fix installer to discover and install local src/modules
- listAvailable() now scans src/modules/ so local modules appear in the UI
- findModuleSource() checks src/modules/{code} before falling back to GitHub
- Handle menu object format (menu.items) alongside legacy flat array in
vendorCrossModuleWorkflows, agent compiler, and agent analyzer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ab8f412c3c
commit
7d6180b6a2
|
|
@ -204,6 +204,21 @@ class ModuleManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scan src/modules/ for locally-defined modules
|
||||||
|
const localModulesPath = getSourcePath('modules');
|
||||||
|
if (await fs.pathExists(localModulesPath)) {
|
||||||
|
const moduleEntries = await fs.readdir(localModulesPath, { withFileTypes: true });
|
||||||
|
for (const entry of moduleEntries) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
const modulePath = path.join(localModulesPath, entry.name);
|
||||||
|
const moduleInfo = await this.getModuleInfo(modulePath, entry.name, 'src/modules');
|
||||||
|
if (moduleInfo && !modules.some((m) => m.id === moduleInfo.id)) {
|
||||||
|
modules.push(moduleInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for cached custom modules in _config/custom/
|
// Check for cached custom modules in _config/custom/
|
||||||
if (this.bmadDir) {
|
if (this.bmadDir) {
|
||||||
const customCacheDir = path.join(this.bmadDir, '_config', 'custom');
|
const customCacheDir = path.join(this.bmadDir, '_config', 'custom');
|
||||||
|
|
@ -308,6 +323,12 @@ class ModuleManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check src/modules/{moduleCode} for locally-defined modules (takes priority over external)
|
||||||
|
const localModulePath = getModulePath(moduleCode);
|
||||||
|
if (await fs.pathExists(localModulePath)) {
|
||||||
|
return localModulePath;
|
||||||
|
}
|
||||||
|
|
||||||
// Check external official modules
|
// Check external official modules
|
||||||
const externalSource = await this.findExternalModuleSource(moduleCode, options);
|
const externalSource = await this.findExternalModuleSource(moduleCode, options);
|
||||||
if (externalSource) {
|
if (externalSource) {
|
||||||
|
|
@ -1150,7 +1171,9 @@ class ModuleManager {
|
||||||
const agentYaml = yaml.parse(await fs.readFile(agentPath, 'utf8'));
|
const agentYaml = yaml.parse(await fs.readFile(agentPath, 'utf8'));
|
||||||
|
|
||||||
// Check if agent has menu items with workflow-install
|
// Check if agent has menu items with workflow-install
|
||||||
const menuItems = agentYaml?.agent?.menu || [];
|
// menu can be an array OR an object with an `items` array
|
||||||
|
const menu = agentYaml?.agent?.menu;
|
||||||
|
const menuItems = Array.isArray(menu) ? menu : menu?.items || [];
|
||||||
const workflowInstallItems = menuItems.filter((item) => item['workflow-install']);
|
const workflowInstallItems = menuItems.filter((item) => item['workflow-install']);
|
||||||
|
|
||||||
if (workflowInstallItems.length === 0) {
|
if (workflowInstallItems.length === 0) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ class AgentAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyze menu items (support both 'menu' and legacy 'commands')
|
// Analyze menu items (support both 'menu' and legacy 'commands')
|
||||||
const menuItems = agentYaml.agent?.menu || agentYaml.agent?.commands || [];
|
// menu can be an array or an object with an `items` array
|
||||||
|
const rawMenu = agentYaml.agent?.menu || agentYaml.agent?.commands;
|
||||||
|
const menuItems = Array.isArray(rawMenu) ? rawMenu : rawMenu?.items || [];
|
||||||
|
|
||||||
for (const item of menuItems) {
|
for (const item of menuItems) {
|
||||||
// Track the menu item
|
// Track the menu item
|
||||||
|
|
|
||||||
|
|
@ -309,8 +309,10 @@ async function compileToXml(agentYaml, agentName = '', targetPath = '') {
|
||||||
xml += buildMemoriesXml(agent.memories);
|
xml += buildMemoriesXml(agent.memories);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Menu section
|
// Menu section — menu can be an array or an object with an `items` array
|
||||||
xml += buildMenuXml(agent.menu || []);
|
const menuData = agent.menu;
|
||||||
|
const menuItems = Array.isArray(menuData) ? menuData : menuData?.items || [];
|
||||||
|
xml += buildMenuXml(menuItems);
|
||||||
|
|
||||||
// Closing agent tag
|
// Closing agent tag
|
||||||
xml += '</agent>\n';
|
xml += '</agent>\n';
|
||||||
|
|
@ -397,7 +399,8 @@ async function compileAgent(yamlContent, answers = {}, agentName = '', targetPat
|
||||||
|
|
||||||
// For menu: append to existing or create new
|
// For menu: append to existing or create new
|
||||||
if (customizations.menu) {
|
if (customizations.menu) {
|
||||||
const existing = agentYaml.agent.menu || [];
|
const existingMenu = agentYaml.agent.menu;
|
||||||
|
const existing = Array.isArray(existingMenu) ? existingMenu : existingMenu?.items || [];
|
||||||
agentYaml.agent.menu = [...existing, ...customizations.menu];
|
agentYaml.agent.menu = [...existing, ...customizations.menu];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue