default accepted for installer quesitons

This commit is contained in:
Brian Madison 2025-12-15 12:55:57 +08:00
parent 6513c77d1b
commit 60238d2854
9 changed files with 110 additions and 21 deletions

View File

@ -13,5 +13,4 @@ default_selected: false # This module will not be selected by default for new in
creativity_self_assessment:
prompt: "On a scale of 1 to 10, how would you rate your current level of creativity?"
default: 7
result: "{value}"

View File

@ -303,7 +303,7 @@ class ConfigCollector {
}
}
// Show "no config" message for modules with no new questions
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(chalk.dim(`${moduleName.toUpperCase()} module already up to date`));
return false; // No new fields
}
@ -339,7 +339,7 @@ class ConfigCollector {
Object.assign(allAnswers, promptedAnswers);
} else if (newStaticKeys.length > 0) {
// Only static fields, no questions - show no config message
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(chalk.dim(`${moduleName.toUpperCase()} module configuration updated`));
}
// Store all answers for cross-referencing
@ -558,21 +558,57 @@ class ConfigCollector {
// Collect all answers (static + prompted)
let allAnswers = { ...staticAnswers };
// Display appropriate header based on whether there are questions
// If there are questions to ask, prompt for accepting defaults vs customizing
if (questions.length > 0) {
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(); // Line break before questions
const promptedAnswers = await inquirer.prompt(questions);
// Get friendly module name from config or use uppercase module name
const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
// Merge prompted answers with static answers
Object.assign(allAnswers, promptedAnswers);
// Display the module name in color first
console.log(chalk.cyan('?') + ' ' + chalk.magenta(moduleDisplayName));
// Ask user if they want to accept defaults or customize on the next line
const { customize } = await inquirer.prompt([
{
type: 'confirm',
name: 'customize',
message: 'Accept Defaults (no to customize)?',
default: true,
},
]);
if (customize) {
// Accept defaults - only ask questions that have NO default value
const questionsWithoutDefaults = questions.filter((q) => q.default === undefined || q.default === null || q.default === '');
if (questionsWithoutDefaults.length > 0) {
console.log(chalk.dim(`\n Asking required questions for ${moduleName.toUpperCase()}...`));
const promptedAnswers = await inquirer.prompt(questionsWithoutDefaults);
Object.assign(allAnswers, promptedAnswers);
}
// For questions with defaults that weren't asked, we need to process them with their default values
const questionsWithDefaults = questions.filter((q) => q.default !== undefined && q.default !== null && q.default !== '');
for (const question of questionsWithDefaults) {
// Skip function defaults - these are dynamic and will be evaluated later
if (typeof question.default === 'function') {
continue;
}
allAnswers[question.name] = question.default;
}
} else {
// Customize - ask all questions
console.log(chalk.dim(`\n Configuring ${moduleName.toUpperCase()}...`));
const promptedAnswers = await inquirer.prompt(questions);
Object.assign(allAnswers, promptedAnswers);
}
}
// Store all answers for cross-referencing
Object.assign(this.allAnswers, allAnswers);
// Process all answers (both static and prompted)
if (Object.keys(allAnswers).length > 0) {
// Always process if we have any answers or static answers
if (Object.keys(allAnswers).length > 0 || Object.keys(staticAnswers).length > 0) {
const answers = allAnswers;
// Process answers and build result values
@ -672,7 +708,32 @@ class ConfigCollector {
// No longer display completion boxes - keep output clean
} else {
// No questions for this module - show completion message
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(chalk.dim(`${moduleName.toUpperCase()} module configured`));
}
// If we have no collected config for this module, but we have a module schema,
// ensure we have at least an empty object
if (!this.collectedConfig[moduleName]) {
this.collectedConfig[moduleName] = {};
// If we accepted defaults and have no answers, we still need to check
// if there are any static values in the schema that should be applied
if (moduleConfig) {
for (const key of Object.keys(moduleConfig)) {
if (key !== 'prompt' && moduleConfig[key] && typeof moduleConfig[key] === 'object') {
const item = moduleConfig[key];
// For static items (no prompt, just result), apply the result
if (!item.prompt && item.result) {
// Apply any placeholder replacements to the result
let result = item.result;
if (typeof result === 'string') {
result = this.replacePlaceholders(result, moduleName, moduleConfig);
}
this.collectedConfig[moduleName][key] = result;
}
}
}
}
}
}

View File

@ -40,7 +40,10 @@ class CustomModuleCache {
*/
async updateCacheManifest(manifest) {
const yaml = require('yaml');
const content = yaml.stringify(manifest, {
// Clean the manifest to remove any non-serializable values
const cleanManifest = structuredClone(manifest);
const content = yaml.stringify(cleanManifest, {
indent: 2,
lineWidth: 0,
sortKeys: false,

View File

@ -61,7 +61,10 @@ class IdeConfigManager {
configuration: configuration || {},
};
const yamlContent = yaml.stringify(configData, {
// Clean the config to remove any non-serializable values (like functions)
const cleanConfig = structuredClone(configData);
const yamlContent = yaml.stringify(cleanConfig, {
indent: 2,
lineWidth: 0,
sortKeys: false,

View File

@ -394,8 +394,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
// Clone config to avoid mutating the caller's object
const config = { ...originalConfig };
// Check if core config was already collected in UI
const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
// Only display logo if core config wasn't already collected (meaning we're not continuing from UI)
if (!config.coreConfig) {
if (hasCoreConfig) {
// Core config was already collected in UI, show smooth continuation
// Don't clear screen, just continue flow
} else {
// Display BMAD logo
CLIUtils.displayLogo();
@ -409,7 +415,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
const projectDir = path.resolve(config.directory);
// If core config was pre-collected (from interactive mode), use it
if (config.coreConfig) {
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
this.configCollector.collectedConfig.core = config.coreConfig;
// Also store in allAnswers for cross-referencing
this.configCollector.allAnswers = {};
@ -1583,8 +1589,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
coreSection = '\n# Core Configuration Values\n';
}
// Clean the config to remove any non-serializable values (like functions)
const cleanConfig = structuredClone(finalConfig);
// Convert config to YAML
let yamlContent = yaml.stringify(finalConfig, {
let yamlContent = yaml.stringify(cleanConfig, {
indent: 2,
lineWidth: 0,
minContentWidth: 0,

View File

@ -486,7 +486,10 @@ class ManifestGenerator {
ides: this.selectedIdes,
};
const yamlStr = yaml.stringify(manifest, {
// Clean the manifest to remove any non-serializable values
const cleanManifest = structuredClone(manifest);
const yamlStr = yaml.stringify(cleanManifest, {
indent: 2,
lineWidth: 0,
sortKeys: false,

View File

@ -28,7 +28,10 @@ class Manifest {
};
// Write YAML manifest
const yamlContent = yaml.stringify(manifestData, {
// Clean the manifest data to remove any non-serializable values
const cleanManifestData = structuredClone(manifestData);
const yamlContent = yaml.stringify(cleanManifestData, {
indent: 2,
lineWidth: 0,
sortKeys: false,
@ -100,7 +103,10 @@ class Manifest {
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
await fs.ensureDir(path.dirname(manifestPath));
const yamlContent = yaml.stringify(manifestData, {
// Clean the manifest data to remove any non-serializable values
const cleanManifestData = structuredClone(manifestData);
const yamlContent = yaml.stringify(cleanManifestData, {
indent: 2,
lineWidth: 0,
sortKeys: false,

View File

@ -859,7 +859,10 @@ class ModuleManager {
// Write back to manifest
const yaml = require('yaml');
const updatedContent = yaml.stringify(manifestData, {
// Clean the manifest data to remove any non-serializable values
const cleanManifestData = structuredClone(manifestData);
const updatedContent = yaml.stringify(cleanManifestData, {
indent: 2,
lineWidth: 0,
});

View File

@ -703,7 +703,9 @@ class UI {
// Now collect with existing values as defaults (false = don't skip loading, true = skip completion message)
await configCollector.collectModuleConfig('core', directory, false, true);
return configCollector.collectedConfig.core;
const coreConfig = configCollector.collectedConfig.core;
// Ensure we always have a core config object, even if empty
return coreConfig || {};
}
/**