quick update output modified

This commit is contained in:
Brian Madison 2025-12-15 17:30:12 +08:00
parent 08f05cf9a4
commit bbda7171bd
8 changed files with 184 additions and 139 deletions

View File

@ -21,6 +21,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
TRADEMARK NOTICE: TRADEMARK NOTICE:
BMAD™, BMAD-CORE™ and BMAD-METHOD™ are trademarks of BMad Code, LLC. The use of these BMad™ , BMAD-CORE™ and BMAD-METHOD™ are trademarks of BMad Code, LLC. The use of these
trademarks in this software does not grant any rights to use the trademarks trademarks in this software does not grant any rights to use the trademarks
for any other purpose. for any other purpose.

View File

@ -202,7 +202,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for full development guidelines.
MIT License - See [LICENSE](LICENSE) for details. MIT License - See [LICENSE](LICENSE) for details.
**Trademarks:** BMAD™ and BMAD-METHOD™ are trademarks of BMad Code, LLC. **Trademarks:** BMad™ and BMAD-METHOD™ are trademarks of BMad Code, LLC.
--- ---

View File

@ -1,8 +1,8 @@
code: core code: core
name: "BMAD™ Core Module" name: "BMad™ Core Module"
header: "BMAD™ Core Configuration" header: "BMad™ Core Configuration"
subheader: "Configure the core settings for your BMAD™ installation.\nThese settings will be used across all modules and agents." subheader: "Configure the core settings for your BMad™ installation.\nThese settings will be used across all modules and agents."
user_name: user_name:
prompt: "What shall the agents call you (TIP: Use a team name if using with a group)?" prompt: "What shall the agents call you (TIP: Use a team name if using with a group)?"

View File

@ -1,7 +1,7 @@
code: bmb code: bmb
name: "BMB: BMad Builder - Agent, Workflow and Module Builder" name: "BMB: BMad Builder - Agent, Workflow and Module Builder"
header: "BMad Optimized Builder (BoMB) Module Configuration" header: "BMad Optimized Builder (BoMB) Module Configuration"
subheader: "Configure the settings for the BoMB Factory!\nThe agent, workflow and module builder for BMAD™" subheader: "Configure the settings for the BoMB Factory!\nThe agent, workflow and module builder for BMad™ "
default_selected: false # This module will not be selected by default for new installations default_selected: false # This module will not be selected by default for new installations
# Variables from Core Config inserted: # Variables from Core Config inserted:

View File

@ -1,4 +1,3 @@
# Powered by BMAD™ Core
name: bmmcc name: bmmcc
short-title: BMM Claude Code Sub Module short-title: BMM Claude Code Sub Module
author: Brian (BMad) Madison author: Brian (BMad) Madison

View File

@ -127,10 +127,6 @@ class ConfigCollector {
} }
} }
if (foundAny) {
console.log(chalk.cyan('\n📋 Found existing BMAD module configurations'));
}
return foundAny; return foundAny;
} }

View File

@ -406,7 +406,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
CLIUtils.displayLogo(); CLIUtils.displayLogo();
// Display welcome message // Display welcome message
CLIUtils.displaySection('BMAD™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version); CLIUtils.displaySection('BMad™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
} }
// Note: Legacy V4 detection now happens earlier in UI.promptInstall() // Note: Legacy V4 detection now happens earlier in UI.promptInstall()
@ -834,13 +834,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
moduleManager: tempModuleManager, moduleManager: tempModuleManager,
}); });
if (config.verbose) { spinner.succeed('Dependencies resolved');
spinner.succeed('Dependencies resolved');
} else {
spinner.succeed('Dependencies resolved');
}
// Core is already installed above, skip if included in resolution
// Install modules with their dependencies // Install modules with their dependencies
if (allModules && allModules.length > 0) { if (allModules && allModules.length > 0) {
@ -1217,14 +1211,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
console.log(chalk.green(`✓ Configured: ${validIdes.join(', ')}`)); console.log(chalk.green(`✓ Configured: ${validIdes.join(', ')}`));
} }
} }
// Copy IDE-specific documentation (only for valid IDEs)
const validIdesForDocs = (config.ides || []).filter((ide) => ide && typeof ide === 'string');
if (validIdesForDocs.length > 0) {
spinner.start('Copying IDE documentation...');
await this.copyIdeDocumentation(validIdesForDocs, bmadDir);
spinner.succeed('IDE documentation copied');
}
} }
// Run module-specific installers after IDE setup // Run module-specific installers after IDE setup
@ -1328,20 +1314,20 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
if (customFiles.length > 0) { if (customFiles.length > 0) {
console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`)); console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`));
console.log(chalk.dim('The following custom files were found and restored:\n')); console.log(chalk.dim('The following custom files were found and restored:\n'));
for (const file of customFiles) { for (const customFile of customFiles) {
console.log(chalk.dim(` - ${path.relative(bmadDir, file)}`)); const relativePath = path.relative(projectDir, customFile);
console.log(chalk.dim(`${relativePath}`));
} }
console.log('');
} }
if (modifiedFiles.length > 0) { if (modifiedFiles.length > 0) {
console.log(chalk.yellow(`\n⚠️ Modified files detected: ${modifiedFiles.length}`)); console.log(chalk.yellow(`\n⚠️ User modified files detected: ${modifiedFiles.length}`));
console.log(chalk.dim('The following files were modified and backed up with .bak extension:\n')); console.log(
for (const file of modifiedFiles) { chalk.dim(
console.log(chalk.dim(` - ${file.relativePath}${file.relativePath}.bak`)); '\nThese user modified files have been updated with the new version, search the project for .bak files that had your customizations.',
} ),
console.log(chalk.dim('\nThese files have been updated with the new version.')); );
console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n')); console.log(chalk.dim('Remove these .bak files it no longer needed\n'));
} }
// Display completion message // Display completion message
@ -1906,7 +1892,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml'); const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
if (await fs.pathExists(genericTemplatePath)) { if (await fs.pathExists(genericTemplatePath)) {
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad'); await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath, this.bmadFolderName || 'bmad');
// Only show customize creation in verbose mode
if (process.env.BMAD_VERBOSE_INSTALL === 'true') { if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`)); console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
} }
@ -3056,25 +3041,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
return nodes; return nodes;
} }
/**
* Copy IDE-specific documentation to BMAD docs
* @param {Array} ides - List of selected IDEs
* @param {string} bmadDir - BMAD installation directory
*/
async copyIdeDocumentation(ides, bmadDir) {
const docsDir = path.join(bmadDir, 'docs');
await fs.ensureDir(docsDir);
for (const ide of ides) {
const sourceDocPath = path.join(getProjectRoot(), 'docs', 'ide-info', `${ide}.md`);
const targetDocPath = path.join(docsDir, `${ide}-instructions.md`);
if (await fs.pathExists(sourceDocPath)) {
await this.copyFileWithPlaceholderReplacement(sourceDocPath, targetDocPath, this.bmadFolderName || 'bmad');
}
}
}
/** /**
* Handle missing custom module sources interactively * Handle missing custom module sources interactively
* @param {Map} customModuleSources - Map of custom module ID to info * @param {Map} customModuleSources - Map of custom module ID to info

View File

@ -282,101 +282,133 @@ class UI {
}; };
} }
// If actionType === 'update', continue with normal flow below // If actionType === 'update', handle it with the new flow
} // Return early with modify configuration
if (actionType === 'update') {
// For new installations, ask about content types first // Get existing installation info
if (!hasExistingInstall) {
// Ask about official modules first
const { wantsOfficialModules } = await inquirer.prompt([
{
type: 'confirm',
name: 'wantsOfficialModules',
message: 'Will you be installing any official modules (BMad Method, BMad Builder, Creative Innovation Suite)?',
default: true,
},
]);
let selectedOfficialModules = [];
if (wantsOfficialModules) {
const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory); const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
const moduleChoices = await this.getModuleChoices(installedModuleIds, { hasCustomContent: false });
selectedOfficialModules = await this.selectModules(moduleChoices); console.log(chalk.dim(` Found existing modules: ${[...installedModuleIds].join(', ')}`));
const { changeModuleSelection } = await inquirer.prompt([
{
type: 'confirm',
name: 'changeModuleSelection',
message: 'Change which modules are installed?',
default: false,
},
]);
let selectedModules = [];
if (changeModuleSelection) {
// Show module selection with existing modules pre-selected
const moduleChoices = await this.getModuleChoices(new Set(installedModuleIds), { hasCustomContent: false });
selectedModules = await this.selectModules(moduleChoices, [...installedModuleIds]);
} else {
selectedModules = [...installedModuleIds];
}
// Get tool selection
const toolSelection = await this.promptToolSelection(confirmedDirectory, selectedModules);
// TTS configuration - ask right after tool selection (matches new install flow)
const hasClaudeCode = toolSelection.ides && toolSelection.ides.includes('claude-code');
let enableTts = false;
if (hasClaudeCode) {
const { enableTts: enable } = await inquirer.prompt([
{
type: 'confirm',
name: 'enableTts',
message: 'Claude Code supports TTS (Text-to-Speech). Would you like to enable it?',
default: false,
},
]);
enableTts = enable;
}
// Core config with existing defaults (ask after TTS)
const coreConfig = await this.collectCoreConfig(confirmedDirectory);
return {
actionType: 'update',
directory: confirmedDirectory,
installCore: true,
modules: selectedModules,
ides: toolSelection.ides,
skipIde: toolSelection.skipIde,
coreConfig: coreConfig,
customContent: { hasCustomContent: false },
enableAgentVibes: enableTts,
agentVibesInstalled: false,
};
} }
// Then ask about custom content
const { wantsCustomContent } = await inquirer.prompt([
{
type: 'confirm',
name: 'wantsCustomContent',
message: 'Will you be installing any locally stored custom content?',
default: false,
},
]);
if (wantsCustomContent) {
customContentConfig = await this.promptCustomContentSource();
}
// Store the selected modules for later
customContentConfig._selectedOfficialModules = selectedOfficialModules;
} }
// This section is only for new installations (update returns early above)
const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory); const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
// Collect core configuration first // Ask about official modules for new installations
const { wantsOfficialModules } = await inquirer.prompt([
{
type: 'confirm',
name: 'wantsOfficialModules',
message: 'Will you be installing any official modules (BMad Method, BMad Builder, Creative Innovation Suite)?',
default: true,
},
]);
let selectedOfficialModules = [];
if (wantsOfficialModules) {
const moduleChoices = await this.getModuleChoices(installedModuleIds, { hasCustomContent: false });
selectedOfficialModules = await this.selectModules(moduleChoices);
}
// Ask about custom content
const { wantsCustomContent } = await inquirer.prompt([
{
type: 'confirm',
name: 'wantsCustomContent',
message: 'Will you be installing any locally stored custom content?',
default: false,
},
]);
if (wantsCustomContent) {
customContentConfig = await this.promptCustomContentSource();
}
// Store the selected modules for later
customContentConfig._selectedOfficialModules = selectedOfficialModules;
// Build the final list of selected modules
let selectedModules = customContentConfig._selectedOfficialModules || [];
// Add custom content modules if any were selected
if (customContentConfig && customContentConfig.selectedModuleIds) {
selectedModules = [...selectedModules, ...customContentConfig.selectedModuleIds];
}
// Remove core if it's in the list (it's always installed)
selectedModules = selectedModules.filter((m) => m !== 'core');
// Tool selection (already done for new installs at the beginning)
if (!toolSelection) {
toolSelection = await this.promptToolSelection(confirmedDirectory, selectedModules);
}
// Collect configurations for new installations
const coreConfig = await this.collectCoreConfig(confirmedDirectory); const coreConfig = await this.collectCoreConfig(confirmedDirectory);
// Custom content will be handled during installation phase // TTS already handled at the beginning for new installs
// Store the custom content config for later use
if (customContentConfig._shouldAsk) {
delete customContentConfig._shouldAsk;
}
// Handle module selection
let selectedModules = [];
if (actionType === 'update' || actionType === 'reinstall') {
// Keep all existing installed modules during update/reinstall
selectedModules = [...installedModuleIds];
console.log(chalk.cyan('\n📦 Keeping existing modules: ') + selectedModules.join(', '));
} else if (!hasExistingInstall) {
// For new installs, we've already selected official modules
selectedModules = customContentConfig._selectedOfficialModules || [];
// Add custom content modules if any were selected
if (customContentConfig && customContentConfig.selectedModuleIds) {
selectedModules = [...selectedModules, ...customContentConfig.selectedModuleIds];
}
// Custom modules are already added via selectedModuleIds from customContentConfig
// No need for additional processing here
}
// AgentVibes TTS configuration already collected earlier for new installations
// For existing installations, keep the old behavior
if (hasExistingInstall && !agentVibesConfig.enabled) {
agentVibesConfig = await this.promptAgentVibes(confirmedDirectory);
}
// Tool selection already collected for new installations
// For existing installations, we need to collect it now
if (hasExistingInstall && !toolSelection) {
const modulesForToolSelection = selectedModules;
toolSelection = await this.promptToolSelection(confirmedDirectory, modulesForToolSelection);
}
// No more screen clearing - keep output flowing
return { return {
actionType: actionType || 'update', // Preserve reinstall or update action actionType: 'install',
directory: confirmedDirectory, directory: confirmedDirectory,
installCore: true, // Always install core installCore: true,
modules: selectedModules, modules: selectedModules,
// IDE selection collected after config, will be configured later
ides: toolSelection.ides, ides: toolSelection.ides,
skipIde: toolSelection.skipIde, skipIde: toolSelection.skipIde,
coreConfig: coreConfig, // Pass collected core config to installer coreConfig: coreConfig,
// Custom content configuration
customContent: customContentConfig, customContent: customContentConfig,
enableAgentVibes: agentVibesConfig.enabled, enableAgentVibes: agentVibesConfig.enabled,
agentVibesInstalled: agentVibesConfig.alreadyInstalled, agentVibesInstalled: agentVibesConfig.alreadyInstalled,
@ -760,13 +792,14 @@ class UI {
* @param {Array} moduleChoices - Available module choices * @param {Array} moduleChoices - Available module choices
* @returns {Array} Selected module IDs * @returns {Array} Selected module IDs
*/ */
async selectModules(moduleChoices) { async selectModules(moduleChoices, defaultSelections = []) {
const moduleAnswer = await inquirer.prompt([ const moduleAnswer = await inquirer.prompt([
{ {
type: 'checkbox', type: 'checkbox',
name: 'modules', name: 'modules',
message: 'Select modules to install:', message: 'Select modules to install:',
choices: moduleChoices, choices: moduleChoices,
default: defaultSelections,
}, },
]); ]);
@ -1114,6 +1147,57 @@ class UI {
return (await fs.pathExists(hookPath)) && (await fs.pathExists(playTtsPath)); return (await fs.pathExists(hookPath)) && (await fs.pathExists(playTtsPath));
} }
/**
* Load existing configurations to use as defaults
* @param {string} directory - Installation directory
* @returns {Object} Existing configurations
*/
async loadExistingConfigurations(directory) {
const configs = {
hasCustomContent: false,
coreConfig: {},
ideConfig: { ides: [], skipIde: false },
agentVibesConfig: { enabled: false, alreadyInstalled: false },
};
try {
// Load core config
configs.coreConfig = await this.collectCoreConfig(directory);
// Load IDE configuration
const configuredIdes = await this.getConfiguredIdes(directory);
if (configuredIdes.length > 0) {
configs.ideConfig.ides = configuredIdes;
configs.ideConfig.skipIde = false;
}
// Load AgentVibes configuration
const agentVibesInstalled = await this.checkAgentVibesInstalled(directory);
configs.agentVibesConfig = { enabled: agentVibesInstalled, alreadyInstalled: agentVibesInstalled };
return configs;
} catch {
// If loading fails, return empty configs
console.warn('Warning: Could not load existing configurations');
return configs;
}
}
/**
* Get configured IDEs from existing installation
* @param {string} directory - Installation directory
* @returns {Array} List of configured IDEs
*/
async getConfiguredIdes(directory) {
const { Detector } = require('../installers/lib/core/detector');
const { Installer } = require('../installers/lib/core/installer');
const detector = new Detector();
const installer = new Installer();
const bmadResult = await installer.findBmadDir(directory);
const existingInstall = await detector.detect(bmadResult.bmadDir);
return existingInstall.ides || [];
}
/** /**
* Prompt for custom content for existing installations * Prompt for custom content for existing installations
* @returns {Object} Custom content configuration * @returns {Object} Custom content configuration