fix: add --yes flag guards to all unguarded prompts in update/modify path

Guard 5 interactive prompts in ui.js that caused the installer to hang
when --yes flag was used with existing installations. Add skipPrompts
field to 3 return objects that were missing it, ensuring installer.js
downstream guards work correctly for all update paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Davor Racić 2026-02-09 19:36:08 +01:00
parent 3287073530
commit e67d54161d
1 changed files with 60 additions and 2 deletions

View File

@ -189,7 +189,7 @@ class UI {
const installedVersion = existingInstall.version || 'unknown';
// Check if version is pre beta
const shouldProceed = await this.showLegacyVersionWarning(installedVersion, currentVersion, path.basename(bmadDir));
const shouldProceed = await this.showLegacyVersionWarning(installedVersion, currentVersion, path.basename(bmadDir), options);
// If user chose to cancel, exit the installer
if (!shouldProceed) {
@ -227,6 +227,14 @@ class UI {
}
actionType = options.action;
await prompts.log.info(`Using action from command-line: ${actionType}`);
} else if (options.yes) {
// Default to quick-update if available, otherwise first available choice
if (choices.length === 0) {
throw new Error('No valid actions available for this installation');
}
const hasQuickUpdate = choices.some((c) => c.value === 'quick-update');
actionType = hasQuickUpdate ? 'quick-update' : choices[0].value;
await prompts.log.info(`Non-interactive mode (--yes): defaulting to ${actionType}`);
} else {
actionType = await prompts.select({
message: 'How would you like to proceed?',
@ -242,6 +250,7 @@ class UI {
actionType: 'quick-update',
directory: confirmedDirectory,
customContent: { hasCustomContent: false },
skipPrompts: options.yes || false,
};
}
@ -252,6 +261,7 @@ class UI {
actionType: 'compile-agents',
directory: confirmedDirectory,
customContent: { hasCustomContent: false },
skipPrompts: options.yes || false,
};
}
@ -272,6 +282,11 @@ class UI {
.map((m) => m.trim())
.filter(Boolean);
await prompts.log.info(`Using modules from command-line: ${selectedModules.join(', ')}`);
} else if (options.yes) {
selectedModules = await this.getDefaultModules(installedModuleIds);
await prompts.log.info(
`Non-interactive mode (--yes): using default modules (installed + defaults): ${selectedModules.join(', ')}`,
);
} else {
selectedModules = await this.selectAllModules(installedModuleIds);
}
@ -330,6 +345,22 @@ class UI {
},
};
}
} else if (options.yes) {
// Non-interactive mode: preserve existing custom modules (matches default: false)
const cacheDir = path.join(bmadDir, '_config', 'custom');
if (await fs.pathExists(cacheDir)) {
const entries = await fs.readdir(cacheDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
customModuleResult.selectedCustomModules.push(entry.name);
}
}
await prompts.log.info(
`Non-interactive mode (--yes): preserving ${customModuleResult.selectedCustomModules.length} existing custom module(s)`,
);
} else {
await prompts.log.info('Non-interactive mode (--yes): no existing custom modules found');
}
} else {
const changeCustomModules = await prompts.confirm({
message: 'Modify custom modules, agents, or workflows?',
@ -378,6 +409,7 @@ class UI {
skipIde: toolSelection.skipIde,
coreConfig: coreConfig,
customContent: customModuleResult.customContentConfig,
skipPrompts: options.yes || false,
};
}
}
@ -529,6 +561,27 @@ class UI {
if (configuredIdes.length > 0) {
const allTools = [...preferredIdes, ...otherIdes];
// Non-interactive: handle --tools and --yes flags before interactive prompt
if (options.tools) {
if (options.tools.toLowerCase() === 'none') {
await prompts.log.info('Skipping tool configuration (--tools none)');
return { ides: [], skipIde: true };
}
const selectedIdes = options.tools
.split(',')
.map((t) => t.trim())
.filter(Boolean);
await prompts.log.info(`Using tools from command-line: ${selectedIdes.join(', ')}`);
await this.displaySelectedTools(selectedIdes, preferredIdes, allTools);
return { ides: selectedIdes, skipIde: false };
}
if (options.yes) {
await prompts.log.info(`Non-interactive mode (--yes): keeping configured tools: ${configuredIdes.join(', ')}`);
await this.displaySelectedTools(configuredIdes, preferredIdes, allTools);
return { ides: configuredIdes, skipIde: false };
}
// Sort: configured tools first, then preferred, then others
const sortedTools = [
...allTools.filter((ide) => configuredIdes.includes(ide.value)),
@ -1630,7 +1683,7 @@ class UI {
* @param {string} bmadFolderName - Name of the BMAD folder
* @returns {Promise<boolean>} True if user wants to proceed, false if they cancel
*/
async showLegacyVersionWarning(installedVersion, currentVersion, bmadFolderName) {
async showLegacyVersionWarning(installedVersion, currentVersion, bmadFolderName, options = {}) {
if (!this.isLegacyVersion(installedVersion)) {
return true; // Not legacy, proceed
}
@ -1656,6 +1709,11 @@ class UI {
await prompts.log.warn('VERSION WARNING');
await prompts.note(warningContent, 'Version Warning');
if (options.yes) {
await prompts.log.warn('Non-interactive mode (--yes): auto-proceeding with legacy update');
return true;
}
const proceed = await prompts.select({
message: 'How would you like to proceed?',
choices: [