Compare commits
3 Commits
d897b0cc47
...
0c6f47ce82
| Author | SHA1 | Date |
|---|---|---|
|
|
0c6f47ce82 | |
|
|
e9b75bd3cb | |
|
|
1af87338ae |
|
|
@ -166,8 +166,66 @@ async function buildAllAgents(projectDir, force = false) {
|
||||||
let builtCount = 0;
|
let builtCount = 0;
|
||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
|
|
||||||
// First, build standalone agents in bmad/agents/
|
// Detect .bmad folder name (could be .bmad or bmad)
|
||||||
const standaloneAgentsDir = path.join(projectDir, 'bmad', 'agents');
|
const bmadFolder = (await fs.pathExists(path.join(projectDir, '.bmad'))) ? '.bmad' : 'bmad';
|
||||||
|
const bmadDir = path.join(projectDir, bmadFolder);
|
||||||
|
|
||||||
|
// Build agents from ALL module directories in .bmad/ (including custom, hde, etc.)
|
||||||
|
if (await fs.pathExists(bmadDir)) {
|
||||||
|
console.log(chalk.cyan('\nScanning all modules in .bmad/...'));
|
||||||
|
const moduleEntries = await fs.readdir(bmadDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const moduleEntry of moduleEntries) {
|
||||||
|
// Skip special directories
|
||||||
|
if (!moduleEntry.isDirectory() || moduleEntry.name === '_cfg' || moduleEntry.name === 'docs') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulePath = path.join(bmadDir, moduleEntry.name);
|
||||||
|
const agentsPath = path.join(modulePath, 'agents');
|
||||||
|
|
||||||
|
// Check if this module has an agents/ directory
|
||||||
|
if (!(await fs.pathExists(agentsPath))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.cyan(`\nBuilding agents in ${moduleEntry.name} module...`));
|
||||||
|
const agentFiles = await fs.readdir(agentsPath);
|
||||||
|
|
||||||
|
for (const file of agentFiles) {
|
||||||
|
if (!file.endsWith('.agent.yaml')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const agentName = file.replace('.agent.yaml', '');
|
||||||
|
const agentYamlPath = path.join(agentsPath, file);
|
||||||
|
const outputPath = path.join(agentsPath, `${agentName}.md`);
|
||||||
|
|
||||||
|
// Check if rebuild needed
|
||||||
|
if (!force && (await fs.pathExists(outputPath))) {
|
||||||
|
const needsRebuild = await checkIfNeedsRebuild(agentYamlPath, outputPath, projectDir, agentName);
|
||||||
|
if (!needsRebuild) {
|
||||||
|
console.log(chalk.dim(` ${agentName}: up to date`));
|
||||||
|
skippedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.cyan(` Building ${agentName}...`));
|
||||||
|
|
||||||
|
const customizePath = path.join(bmadDir, '_cfg', 'agents', `${moduleEntry.name}-${agentName}.customize.yaml`);
|
||||||
|
const customizeExists = await fs.pathExists(customizePath);
|
||||||
|
|
||||||
|
await builder.buildAgent(agentYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });
|
||||||
|
|
||||||
|
console.log(chalk.green(` ✓ ${agentName} (${moduleEntry.name})`));
|
||||||
|
builtCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also build standalone agents in bmad/agents/ (top-level, for backward compatibility)
|
||||||
|
const standaloneAgentsDir = path.join(projectDir, bmadFolder, 'agents');
|
||||||
if (await fs.pathExists(standaloneAgentsDir)) {
|
if (await fs.pathExists(standaloneAgentsDir)) {
|
||||||
console.log(chalk.cyan('\nBuilding standalone agents...'));
|
console.log(chalk.cyan('\nBuilding standalone agents...'));
|
||||||
const agentDirs = await fs.readdir(standaloneAgentsDir);
|
const agentDirs = await fs.readdir(standaloneAgentsDir);
|
||||||
|
|
@ -205,7 +263,7 @@ async function buildAllAgents(projectDir, force = false) {
|
||||||
|
|
||||||
console.log(chalk.cyan(` Building standalone agent ${agentName}...`));
|
console.log(chalk.cyan(` Building standalone agent ${agentName}...`));
|
||||||
|
|
||||||
const customizePath = path.join(projectDir, 'bmad', '_cfg', 'agents', `${agentName}.customize.yaml`);
|
const customizePath = path.join(projectDir, bmadFolder, '_cfg', 'agents', `${agentName}.customize.yaml`);
|
||||||
const customizeExists = await fs.pathExists(customizePath);
|
const customizeExists = await fs.pathExists(customizePath);
|
||||||
|
|
||||||
await builder.buildAgent(agentYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });
|
await builder.buildAgent(agentYamlPath, customizeExists ? customizePath : null, outputPath, { includeMetadata: true });
|
||||||
|
|
@ -275,8 +333,52 @@ async function checkBuildStatus(projectDir) {
|
||||||
const needsRebuild = [];
|
const needsRebuild = [];
|
||||||
const upToDate = [];
|
const upToDate = [];
|
||||||
|
|
||||||
// Check standalone agents in bmad/agents/
|
// Detect .bmad folder name (could be .bmad or bmad)
|
||||||
const standaloneAgentsDir = path.join(projectDir, 'bmad', 'agents');
|
const bmadFolder = (await fs.pathExists(path.join(projectDir, '.bmad'))) ? '.bmad' : 'bmad';
|
||||||
|
const bmadDir = path.join(projectDir, bmadFolder);
|
||||||
|
|
||||||
|
// Check agents in ALL module directories in .bmad/
|
||||||
|
if (await fs.pathExists(bmadDir)) {
|
||||||
|
const moduleEntries = await fs.readdir(bmadDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const moduleEntry of moduleEntries) {
|
||||||
|
// Skip special directories
|
||||||
|
if (!moduleEntry.isDirectory() || moduleEntry.name === '_cfg' || moduleEntry.name === 'docs') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulePath = path.join(bmadDir, moduleEntry.name);
|
||||||
|
const agentsPath = path.join(modulePath, 'agents');
|
||||||
|
|
||||||
|
// Check if this module has an agents/ directory
|
||||||
|
if (!(await fs.pathExists(agentsPath))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const agentFiles = await fs.readdir(agentsPath);
|
||||||
|
|
||||||
|
for (const file of agentFiles) {
|
||||||
|
if (!file.endsWith('.agent.yaml')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const agentName = file.replace('.agent.yaml', '');
|
||||||
|
const agentYamlPath = path.join(agentsPath, file);
|
||||||
|
const outputPath = path.join(agentsPath, `${agentName}.md`);
|
||||||
|
|
||||||
|
if (!(await fs.pathExists(outputPath))) {
|
||||||
|
needsRebuild.push(`${agentName} (${moduleEntry.name})`);
|
||||||
|
} else if (await checkIfNeedsRebuild(agentYamlPath, outputPath, projectDir, agentName)) {
|
||||||
|
needsRebuild.push(`${agentName} (${moduleEntry.name})`);
|
||||||
|
} else {
|
||||||
|
upToDate.push(`${agentName} (${moduleEntry.name})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check standalone agents in bmad/agents/ (top-level)
|
||||||
|
const standaloneAgentsDir = path.join(projectDir, bmadFolder, 'agents');
|
||||||
if (await fs.pathExists(standaloneAgentsDir)) {
|
if (await fs.pathExists(standaloneAgentsDir)) {
|
||||||
const agentDirs = await fs.readdir(standaloneAgentsDir);
|
const agentDirs = await fs.readdir(standaloneAgentsDir);
|
||||||
|
|
||||||
|
|
@ -406,8 +508,42 @@ async function checkIfNeedsRebuild(yamlPath, outputPath, projectDir, agentName)
|
||||||
* List available agents
|
* List available agents
|
||||||
*/
|
*/
|
||||||
async function listAvailableAgents(projectDir) {
|
async function listAvailableAgents(projectDir) {
|
||||||
// List standalone agents first
|
// Detect .bmad folder name (could be .bmad or bmad)
|
||||||
const standaloneAgentsDir = path.join(projectDir, 'bmad', 'agents');
|
const bmadFolder = (await fs.pathExists(path.join(projectDir, '.bmad'))) ? '.bmad' : 'bmad';
|
||||||
|
const bmadDir = path.join(projectDir, bmadFolder);
|
||||||
|
|
||||||
|
// List agents from ALL module directories in .bmad/
|
||||||
|
if (await fs.pathExists(bmadDir)) {
|
||||||
|
console.log(chalk.dim(' Module agents:'));
|
||||||
|
const moduleEntries = await fs.readdir(bmadDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const moduleEntry of moduleEntries) {
|
||||||
|
// Skip special directories
|
||||||
|
if (!moduleEntry.isDirectory() || moduleEntry.name === '_cfg' || moduleEntry.name === 'docs') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulePath = path.join(bmadDir, moduleEntry.name);
|
||||||
|
const agentsPath = path.join(modulePath, 'agents');
|
||||||
|
|
||||||
|
// Check if this module has an agents/ directory
|
||||||
|
if (!(await fs.pathExists(agentsPath))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const agentFiles = await fs.readdir(agentsPath);
|
||||||
|
|
||||||
|
for (const file of agentFiles) {
|
||||||
|
if (file.endsWith('.agent.yaml')) {
|
||||||
|
const agentName = file.replace('.agent.yaml', '');
|
||||||
|
console.log(chalk.dim(` - ${agentName} (${moduleEntry.name})`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List standalone agents
|
||||||
|
const standaloneAgentsDir = path.join(projectDir, bmadFolder, 'agents');
|
||||||
if (await fs.pathExists(standaloneAgentsDir)) {
|
if (await fs.pathExists(standaloneAgentsDir)) {
|
||||||
console.log(chalk.dim(' Standalone agents:'));
|
console.log(chalk.dim(' Standalone agents:'));
|
||||||
const agentDirs = await fs.readdir(standaloneAgentsDir);
|
const agentDirs = await fs.readdir(standaloneAgentsDir);
|
||||||
|
|
|
||||||
|
|
@ -1787,7 +1787,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
// Rebuild module agents from installer source
|
// Rebuild module agents from installer source
|
||||||
const agentsPath = path.join(modulePath, 'agents');
|
const agentsPath = path.join(modulePath, 'agents');
|
||||||
if (await fs.pathExists(agentsPath)) {
|
if (await fs.pathExists(agentsPath)) {
|
||||||
|
// Check if this module has source in the installer
|
||||||
|
const sourceAgentsPath =
|
||||||
|
entry.name === 'core'
|
||||||
|
? path.join(getModulePath('core'), 'agents')
|
||||||
|
: path.join(getSourcePath(`modules/${entry.name}`), 'agents');
|
||||||
|
|
||||||
|
// Only rebuild if source exists in installer, otherwise skip (for custom modules)
|
||||||
|
if (await fs.pathExists(sourceAgentsPath)) {
|
||||||
await this.rebuildAgentFiles(modulePath, entry.name);
|
await this.rebuildAgentFiles(modulePath, entry.name);
|
||||||
|
}
|
||||||
|
|
||||||
const agentFiles = await fs.readdir(agentsPath);
|
const agentFiles = await fs.readdir(agentsPath);
|
||||||
agentCount += agentFiles.filter((f) => f.endsWith('.md')).length;
|
agentCount += agentFiles.filter((f) => f.endsWith('.md')).length;
|
||||||
}
|
}
|
||||||
|
|
@ -1812,9 +1822,16 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
spinner.succeed('No custom agents found to rebuild');
|
spinner.succeed('No custom agents found to rebuild');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip full manifest regeneration during compileAgents to preserve custom agents
|
// Detect installed modules for manifest regeneration and IDE configuration
|
||||||
// Custom agents are already added to manifests during individual installation
|
spinner.start('Regenerating manifests...');
|
||||||
// Only regenerate YAML manifest for IDE updates if needed
|
const existingInstall = await this.detector.detect(bmadDir);
|
||||||
|
const installedModules = existingInstall.modules.map((m) => m.id);
|
||||||
|
|
||||||
|
// Regenerate manifests to include all discovered content (including custom)
|
||||||
|
const { ManifestGenerator } = require('./manifest-generator');
|
||||||
|
const manifestGen = new ManifestGenerator();
|
||||||
|
|
||||||
|
// Get existing IDE list from current manifest
|
||||||
const existingManifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
const existingManifestPath = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
||||||
let existingIdes = [];
|
let existingIdes = [];
|
||||||
if (await fs.pathExists(existingManifestPath)) {
|
if (await fs.pathExists(existingManifestPath)) {
|
||||||
|
|
@ -1824,6 +1841,12 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
existingIdes = manifest.ides || [];
|
existingIdes = manifest.ides || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await manifestGen.generateManifests(bmadDir, installedModules, [], {
|
||||||
|
ides: existingIdes,
|
||||||
|
preservedModules: [],
|
||||||
|
});
|
||||||
|
spinner.succeed('Manifests regenerated');
|
||||||
|
|
||||||
// Update IDE configurations using the existing IDE list from manifest
|
// Update IDE configurations using the existing IDE list from manifest
|
||||||
if (existingIdes && existingIdes.length > 0) {
|
if (existingIdes && existingIdes.length > 0) {
|
||||||
spinner.start('Updating IDE configurations...');
|
spinner.start('Updating IDE configurations...');
|
||||||
|
|
|
||||||
|
|
@ -87,20 +87,24 @@ class ManifestGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all workflows from core and selected modules
|
* Collect all workflows from ALL directories in bmad installation
|
||||||
* Scans the INSTALLED bmad directory, not the source
|
* Scans the INSTALLED bmad directory, not the source
|
||||||
*/
|
*/
|
||||||
async collectWorkflows(selectedModules) {
|
async collectWorkflows(selectedModules) {
|
||||||
this.workflows = [];
|
this.workflows = [];
|
||||||
|
|
||||||
// Use updatedModules which already includes deduplicated 'core' + selectedModules
|
// Scan all directories under bmad installation
|
||||||
for (const moduleName of this.updatedModules) {
|
const entries = await fs.readdir(this.bmadDir, { withFileTypes: true });
|
||||||
const modulePath = path.join(this.bmadDir, moduleName);
|
|
||||||
|
|
||||||
if (await fs.pathExists(modulePath)) {
|
for (const entry of entries) {
|
||||||
const moduleWorkflows = await this.getWorkflowsFromPath(modulePath, moduleName);
|
// Skip special directories that don't contain modules
|
||||||
this.workflows.push(...moduleWorkflows);
|
if (!entry.isDirectory() || entry.name === '_cfg' || entry.name === 'docs') {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modulePath = path.join(this.bmadDir, entry.name);
|
||||||
|
const moduleWorkflows = await this.getWorkflowsFromPath(modulePath, entry.name);
|
||||||
|
this.workflows.push(...moduleWorkflows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,23 +179,32 @@ class ManifestGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all agents from core and selected modules
|
* Collect all agents from ALL directories in bmad installation
|
||||||
* Scans the INSTALLED bmad directory, not the source
|
* Scans the INSTALLED bmad directory, not the source
|
||||||
*/
|
*/
|
||||||
async collectAgents(selectedModules) {
|
async collectAgents(selectedModules) {
|
||||||
this.agents = [];
|
this.agents = [];
|
||||||
|
|
||||||
// Use updatedModules which already includes deduplicated 'core' + selectedModules
|
// Scan all directories under bmad installation
|
||||||
for (const moduleName of this.updatedModules) {
|
const entries = await fs.readdir(this.bmadDir, { withFileTypes: true });
|
||||||
const agentsPath = path.join(this.bmadDir, moduleName, 'agents');
|
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
// Skip special directories that don't contain modules
|
||||||
|
if (!entry.isDirectory() || entry.name === '_cfg' || entry.name === 'docs') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulePath = path.join(this.bmadDir, entry.name);
|
||||||
|
|
||||||
|
// Check for agents/ subdirectory in this module
|
||||||
|
const agentsPath = path.join(modulePath, 'agents');
|
||||||
if (await fs.pathExists(agentsPath)) {
|
if (await fs.pathExists(agentsPath)) {
|
||||||
const moduleAgents = await this.getAgentsFromDir(agentsPath, moduleName);
|
const moduleAgents = await this.getAgentsFromDir(agentsPath, entry.name);
|
||||||
this.agents.push(...moduleAgents);
|
this.agents.push(...moduleAgents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get standalone agents from bmad/agents/ directory
|
// Also check for standalone agents in bmad/agents/ directory (top-level)
|
||||||
const standaloneAgentsDir = path.join(this.bmadDir, 'agents');
|
const standaloneAgentsDir = path.join(this.bmadDir, 'agents');
|
||||||
if (await fs.pathExists(standaloneAgentsDir)) {
|
if (await fs.pathExists(standaloneAgentsDir)) {
|
||||||
const agentDirs = await fs.readdir(standaloneAgentsDir, { withFileTypes: true });
|
const agentDirs = await fs.readdir(standaloneAgentsDir, { withFileTypes: true });
|
||||||
|
|
@ -283,18 +296,27 @@ class ManifestGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all tasks from core and selected modules
|
* Collect all tasks from ALL directories in bmad installation
|
||||||
* Scans the INSTALLED bmad directory, not the source
|
* Scans the INSTALLED bmad directory, not the source
|
||||||
*/
|
*/
|
||||||
async collectTasks(selectedModules) {
|
async collectTasks(selectedModules) {
|
||||||
this.tasks = [];
|
this.tasks = [];
|
||||||
|
|
||||||
// Use updatedModules which already includes deduplicated 'core' + selectedModules
|
// Scan all directories under bmad installation
|
||||||
for (const moduleName of this.updatedModules) {
|
const entries = await fs.readdir(this.bmadDir, { withFileTypes: true });
|
||||||
const tasksPath = path.join(this.bmadDir, moduleName, 'tasks');
|
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
// Skip special directories that don't contain modules
|
||||||
|
if (!entry.isDirectory() || entry.name === '_cfg' || entry.name === 'docs') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulePath = path.join(this.bmadDir, entry.name);
|
||||||
|
|
||||||
|
// Check for tasks/ subdirectory in this module
|
||||||
|
const tasksPath = path.join(modulePath, 'tasks');
|
||||||
if (await fs.pathExists(tasksPath)) {
|
if (await fs.pathExists(tasksPath)) {
|
||||||
const moduleTasks = await this.getTasksFromDir(tasksPath, moduleName);
|
const moduleTasks = await this.getTasksFromDir(tasksPath, entry.name);
|
||||||
this.tasks.push(...moduleTasks);
|
this.tasks.push(...moduleTasks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue