Fix #990: Enable custom module discovery in installer
- Fix 'installedModules is not defined' error in compileAgents() - Add manifest regeneration during compilation to discover custom content - Change manifest generator to scan all directories dynamically - Fix ManifestGenerator import to use destructuring - Handle custom modules without installer source gracefully This enables custom workflows/agents/tasks in .bmad/custom/ to be automatically discovered and generate proper .claude/commands/ files. Changes: - installer.js: Add manifest regeneration, fix imports, handle custom modules - manifest-generator.js: Scan all directories instead of hardcoded list Fixes #990
This commit is contained in:
parent
355ccebca2
commit
1af87338ae
|
|
@ -1787,7 +1787,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|||
// Rebuild module agents from installer source
|
||||
const agentsPath = path.join(modulePath, 'agents');
|
||||
if (await fs.pathExists(agentsPath)) {
|
||||
await this.rebuildAgentFiles(modulePath, entry.name);
|
||||
// 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);
|
||||
}
|
||||
|
||||
const agentFiles = await fs.readdir(agentsPath);
|
||||
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');
|
||||
}
|
||||
|
||||
// Skip full manifest regeneration during compileAgents to preserve custom agents
|
||||
// Custom agents are already added to manifests during individual installation
|
||||
// Only regenerate YAML manifest for IDE updates if needed
|
||||
// Detect installed modules for manifest regeneration and IDE configuration
|
||||
spinner.start('Regenerating manifests...');
|
||||
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');
|
||||
let existingIdes = [];
|
||||
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 || [];
|
||||
}
|
||||
|
||||
await manifestGen.generateManifests(bmadDir, installedModules, [], {
|
||||
ides: existingIdes,
|
||||
preservedModules: [],
|
||||
});
|
||||
spinner.succeed('Manifests regenerated');
|
||||
|
||||
// Update IDE configurations using the existing IDE list from manifest
|
||||
if (existingIdes && existingIdes.length > 0) {
|
||||
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
|
||||
*/
|
||||
async collectWorkflows(selectedModules) {
|
||||
this.workflows = [];
|
||||
|
||||
// Use updatedModules which already includes deduplicated 'core' + selectedModules
|
||||
for (const moduleName of this.updatedModules) {
|
||||
const modulePath = path.join(this.bmadDir, moduleName);
|
||||
// Scan all directories under bmad installation
|
||||
const entries = await fs.readdir(this.bmadDir, { withFileTypes: true });
|
||||
|
||||
if (await fs.pathExists(modulePath)) {
|
||||
const moduleWorkflows = await this.getWorkflowsFromPath(modulePath, moduleName);
|
||||
this.workflows.push(...moduleWorkflows);
|
||||
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);
|
||||
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
|
||||
*/
|
||||
async collectAgents(selectedModules) {
|
||||
this.agents = [];
|
||||
|
||||
// Use updatedModules which already includes deduplicated 'core' + selectedModules
|
||||
for (const moduleName of this.updatedModules) {
|
||||
const agentsPath = path.join(this.bmadDir, moduleName, 'agents');
|
||||
// Scan all directories under bmad installation
|
||||
const entries = await fs.readdir(this.bmadDir, { withFileTypes: true });
|
||||
|
||||
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)) {
|
||||
const moduleAgents = await this.getAgentsFromDir(agentsPath, moduleName);
|
||||
const moduleAgents = await this.getAgentsFromDir(agentsPath, entry.name);
|
||||
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');
|
||||
if (await fs.pathExists(standaloneAgentsDir)) {
|
||||
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
|
||||
*/
|
||||
async collectTasks(selectedModules) {
|
||||
this.tasks = [];
|
||||
|
||||
// Use updatedModules which already includes deduplicated 'core' + selectedModules
|
||||
for (const moduleName of this.updatedModules) {
|
||||
const tasksPath = path.join(this.bmadDir, moduleName, 'tasks');
|
||||
// Scan all directories under bmad installation
|
||||
const entries = await fs.readdir(this.bmadDir, { withFileTypes: true });
|
||||
|
||||
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)) {
|
||||
const moduleTasks = await this.getTasksFromDir(tasksPath, moduleName);
|
||||
const moduleTasks = await this.getTasksFromDir(tasksPath, entry.name);
|
||||
this.tasks.push(...moduleTasks);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue