diff --git a/tools/cli/installers/lib/core/config-collector.js b/tools/cli/installers/lib/core/config-collector.js index 743c1954..ae9f8074 100644 --- a/tools/cli/installers/lib/core/config-collector.js +++ b/tools/cli/installers/lib/core/config-collector.js @@ -132,8 +132,12 @@ class ConfigCollector { * Collect configuration for all modules * @param {Array} modules - List of modules to configure (including 'core') * @param {string} projectDir - Target project directory + * @param {Object} options - Additional options + * @param {Map} options.customModulePaths - Map of module ID to source path for custom modules */ - async collectAllConfigurations(modules, projectDir) { + async collectAllConfigurations(modules, projectDir, options = {}) { + // Store custom module paths for use in collectModuleConfig + this.customModulePaths = options.customModulePaths || new Map(); await this.loadExistingConfig(projectDir); // Check if core was already collected (e.g., in early collection phase) @@ -451,11 +455,21 @@ class ConfigCollector { this.allAnswers = {}; } // Load module's config - // First, try the standard src/modules location - let installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'module.yaml'); - let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml'); + // First, check if we have a custom module path for this module + let installerConfigPath = null; + let moduleConfigPath = null; - // If not found in src/modules, we need to find it by searching the project + if (this.customModulePaths && this.customModulePaths.has(moduleName)) { + const customPath = this.customModulePaths.get(moduleName); + installerConfigPath = path.join(customPath, '_module-installer', 'module.yaml'); + moduleConfigPath = path.join(customPath, 'module.yaml'); + } else { + // Try the standard src/modules location + installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'module.yaml'); + moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml'); + } + + // If not found in src/modules or custom paths, search the project if (!(await fs.pathExists(installerConfigPath)) && !(await fs.pathExists(moduleConfigPath))) { // Use the module manager to find the module source const { ModuleManager } = require('../modules/manager'); diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index d1ae8131..fb670d43 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -435,8 +435,53 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: // Quick update already collected all configs, use them directly moduleConfigs = this.configCollector.collectedConfig; } else { + // Build custom module paths map from customContent + const customModulePaths = new Map(); + + // Handle selectedFiles (from existing install path or manual directory input) + if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) { + const { CustomHandler } = require('../custom/handler'); + const customHandler = new CustomHandler(); + for (const customFile of config.customContent.selectedFiles) { + const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory)); + if (customInfo && customInfo.id) { + customModulePaths.set(customInfo.id, customInfo.path); + } + } + } + + // Handle cachedModules (from new install path where modules are cached) + // Only include modules that were actually selected for installation + if (config.customContent && config.customContent.cachedModules) { + // Get selected cached module IDs (if available) + const selectedCachedIds = config.customContent.selectedCachedModules || []; + // If no selection info, include all cached modules (for backward compatibility) + const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected; + + for (const cachedModule of config.customContent.cachedModules) { + // For cached modules, the path is the cachePath which contains the module.yaml + if ( + cachedModule.id && + cachedModule.cachePath && // Include if selected or if we should include all + (shouldIncludeAll || selectedCachedIds.includes(cachedModule.id)) + ) { + customModulePaths.set(cachedModule.id, cachedModule.cachePath); + } + } + } + + // Get list of all modules including custom modules + const allModulesForConfig = [...(config.modules || [])]; + for (const [moduleId] of customModulePaths) { + if (!allModulesForConfig.includes(moduleId)) { + allModulesForConfig.push(moduleId); + } + } + // Regular install - collect configurations (core was already collected in UI.promptInstall if interactive) - moduleConfigs = await this.configCollector.collectAllConfigurations(config.modules || [], path.resolve(config.directory)); + moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), { + customModulePaths, + }); } // Get bmad_folder from config (default to 'bmad' for backwards compatibility) @@ -905,10 +950,13 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: const moduleTargetPath = path.join(bmadDir, moduleName); await fs.ensureDir(moduleTargetPath); + // Get collected config for this custom module (from module.yaml prompts) + const collectedModuleConfig = moduleConfigs[moduleName] || {}; + const result = await customHandler.install( customInfo.path, path.join(bmadDir, 'temp-custom'), - { ...config.coreConfig, ...customInfo.config, _bmadDir: bmadDir }, + { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig, _bmadDir: bmadDir }, (filePath) => { // Track installed files with correct path const relativePath = path.relative(path.join(bmadDir, 'temp-custom'), filePath); @@ -939,8 +987,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice: await fs.remove(tempCustomPath); } - // Create module config - await this.generateModuleConfigs(bmadDir, { [moduleName]: { ...config.coreConfig, ...customInfo.config } }); + // Create module config (include collected config from module.yaml prompts) + await this.generateModuleConfigs(bmadDir, { + [moduleName]: { ...config.coreConfig, ...customInfo.config, ...collectedModuleConfig }, + }); // Store custom module info for later manifest update if (!config._customModulesToTrack) {