From c18944f3ff169c1a58afa34414dd2d2c15b9c7a8 Mon Sep 17 00:00:00 2001 From: wladimiiir Date: Tue, 3 Feb 2026 23:03:29 +0100 Subject: [PATCH] feat: add non-interactive installation support Add command-line flags to support non-interactive installation for CI/CD pipelines and automated deployments: - --directory: Installation directory - --modules: Comma-separated module IDs - --tools: Tool/IDE IDs (use "none" to skip) - --custom-content: Custom module paths - --action: Action type for existing installations - --user-name, --communication-language, --document-output-language, --output-folder: Core config - -y, --yes: Accept all defaults When flags are provided, prompts are skipped. Missing values gracefully fall back to interactive prompts. --- README.md | 8 + docs/non-interactive-installation.md | 314 +++++++++++++++++++++++++ tools/cli/commands/install.js | 19 +- tools/cli/lib/ui.js | 333 +++++++++++++++++++++++---- 4 files changed, 622 insertions(+), 52 deletions(-) create mode 100644 docs/non-interactive-installation.md diff --git a/README.md b/README.md index 25828f39..6e1f3a9b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,14 @@ npx bmad-method install Follow the installer prompts, then open your AI IDE (Claude Code, Cursor, Windsurf, etc.) in the project folder. +**Non-Interactive Installation**: For CI/CD pipelines or automated deployments, use command-line flags: + +```bash +npx bmad-method install --directory /path/to/project --modules bmm --tools claude-code --yes +``` + +See [Non-Interactive Installation Guide](docs/non-interactive-installation.md) for all available options. + > **Not sure what to do?** Run `/bmad-help` — it tells you exactly what's next and what's optional. You can also ask it questions like: - `/bmad-help How should I build a web app for my TShirt Business that can scale to millions?` diff --git a/docs/non-interactive-installation.md b/docs/non-interactive-installation.md new file mode 100644 index 00000000..7541ecc2 --- /dev/null +++ b/docs/non-interactive-installation.md @@ -0,0 +1,314 @@ +--- +title: Non-Interactive Installation +description: Install BMAD using command-line flags for CI/CD pipelines and automated deployments +--- + +# Non-Interactive Installation + +BMAD now supports non-interactive installation through command-line flags. This is particularly useful for: + +- Automated deployments and CI/CD pipelines +- Scripted installations +- Batch installations across multiple projects +- Quick installations with known configurations + +## Installation Modes + +### 1. Fully Interactive (Default) + +Run without any flags to use the traditional interactive prompts: + +```bash +npx bmad-method install +``` + +### 2. Fully Non-Interactive + +Provide all required flags to skip all prompts: + +```bash +npx bmad-method install \ + --directory /path/to/project \ + --modules bmm,bmb \ + --tools claude-code,cursor \ + --user-name "John Doe" \ + --communication-language English \ + --document-output-language English \ + --output-folder _bmad-output +``` + +### 3. Semi-Interactive (Graceful Fallback) + +Provide some flags and let BMAD prompt for the rest: + +```bash +npx bmad-method install \ + --directory /path/to/project \ + --modules bmm +``` + +In this case, BMAD will: +- Use the provided directory and modules +- Prompt for tool selection +- Prompt for core configuration + +### 4. Quick Install with Defaults + +Use the `-y` or `--yes` flag to accept all defaults: + +```bash +npx bmad-method install --yes +``` + +This will: +- Install to the current directory +- Skip custom content prompts +- Use default values for all configuration +- Use previously configured tools (or skip tool configuration if none exist) + +### 5. Install Without Tools + +To skip tool/IDE configuration entirely: + +**Option 1: Use --tools none** +```bash +npx bmad-method install --directory ~/myapp --modules bmm --tools none +``` + +**Option 2: Use --yes flag (if no tools were previously configured)** +```bash +npx bmad-method install --yes +``` + +**Option 3: Omit --tools and select "None" in the interactive prompt** +```bash +npx bmad-method install --directory ~/myapp --modules bmm +# Then select "⚠ None - I am not installing any tools" when prompted +``` + +## Available Flags + +### Installation Options + +| Flag | Description | Example | +|------|-------------|---------| +| `--directory ` | Installation directory | `--directory ~/projects/myapp` | +| `--modules ` | Comma-separated module IDs | `--modules bmm,bmb` | +| `--tools ` | Comma-separated tool/IDE IDs (use "none" to skip) | `--tools claude-code,cursor` or `--tools none` | +| `--custom-content ` | Comma-separated paths to custom modules | `--custom-content ~/my-module,~/another-module` | +| `--action ` | Action for existing installations | `--action quick-update` | + +### Core Configuration + +| Flag | Description | Default | +|------|-------------|---------| +| `--user-name ` | Name for agents to use | System username | +| `--communication-language ` | Agent communication language | English | +| `--document-output-language ` | Document output language | English | +| `--output-folder ` | Output folder path | _bmad-output | + +### Other Options + +| Flag | Description | +|------|-------------| +| `-y, --yes` | Accept all defaults and skip prompts | +| `-d, --debug` | Enable debug output for manifest generation | + +## Action Types + +When working with existing installations, use the `--action` flag: + +- `install` - Fresh installation (default for new directories) +- `update` - Modify existing installation (change modules/config) +- `quick-update` - Refresh installation without changing configuration +- `compile-agents` - Recompile agents with customizations only + +Example: + +```bash +npx bmad-method install --action quick-update +``` + +## Module IDs + +Available module IDs for the `--modules` flag: + +### Core Modules +- `bmm` - BMad Method Master +- `bmb` - BMad Builder + +### External Modules +Check the [BMad registry](https://github.com/bmad-code-org) for available external modules. + +## Tool/IDE IDs + +Available tool IDs for the `--tools` flag: + +- `claude-code` - Claude Code CLI +- `cursor` - Cursor IDE +- `windsurf` - Windsurf IDE +- `vscode` - Visual Studio Code +- `jetbrains` - JetBrains IDEs +- And more... + +Run the interactive installer once to see all available tools. + +## Examples + +### Basic Installation + +Install BMM module with Claude Code: + +```bash +npx bmad-method install \ + --directory ~/projects/myapp \ + --modules bmm \ + --tools claude-code \ + --user-name "Development Team" +``` + +### Installation Without Tools + +Install without configuring any tools/IDEs: + +```bash +npx bmad-method install \ + --directory ~/projects/myapp \ + --modules bmm \ + --tools none \ + --user-name "Development Team" +``` + +### Full Installation with Multiple Modules + +```bash +npx bmad-method install \ + --directory ~/projects/myapp \ + --modules bmm,bmb \ + --tools claude-code,cursor \ + --user-name "John Doe" \ + --communication-language English \ + --document-output-language English \ + --output-folder _output +``` + +### Update Existing Installation + +```bash +npx bmad-method install \ + --directory ~/projects/myapp \ + --action update \ + --modules bmm,bmb,custom-module +``` + +### Quick Update (Preserve Settings) + +```bash +npx bmad-method install \ + --directory ~/projects/myapp \ + --action quick-update +``` + +### Installation with Custom Content + +```bash +npx bmad-method install \ + --directory ~/projects/myapp \ + --modules bmm \ + --custom-content ~/my-custom-module,~/another-module \ + --tools claude-code +``` + +### CI/CD Pipeline Installation + +```bash +#!/bin/bash +# install-bmad.sh + +npx bmad-method install \ + --directory "${GITHUB_WORKSPACE}" \ + --modules bmm \ + --tools claude-code \ + --user-name "CI Bot" \ + --communication-language English \ + --document-output-language English \ + --output-folder _bmad-output \ + --yes +``` + +## Environment-Specific Installations + +### Development Environment + +```bash +npx bmad-method install \ + --directory . \ + --modules bmm,bmb \ + --tools claude-code,cursor \ + --user-name "${USER}" +``` + +### Production Environment + +```bash +npx bmad-method install \ + --directory /opt/app \ + --modules bmm \ + --tools claude-code \ + --user-name "Production Team" \ + --output-folder /var/bmad-output +``` + +## Validation and Error Handling + +BMAD validates all provided flags: + +- **Directory**: Must be a valid path with write permissions +- **Modules**: Will warn about invalid module IDs (but won't fail) +- **Tools**: Will warn about invalid tool IDs (but won't fail) +- **Custom Content**: Each path must contain a valid `module.yaml` file +- **Action**: Must be one of: install, update, quick-update, compile-agents + +Invalid values will either: +1. Show an error and exit (for critical options like directory) +2. Show a warning and skip (for optional items like custom content) +3. Fall back to interactive prompts (for missing required values) + +## Tips and Best Practices + +1. **Use absolute paths** for `--directory` to avoid ambiguity +2. **Test flags locally** before using in CI/CD pipelines +3. **Combine with `-y`** for truly unattended installations +4. **Check module availability** by running the interactive installer once +5. **Use `--debug`** flag if you encounter issues during installation +6. **Skip tool configuration** with `--tools none` for server/CI environments where IDEs aren't needed +7. **Partial flags are OK** - Omit flags and let BMAD prompt for missing values interactively + +## Troubleshooting + +### Installation fails with "Invalid directory" + +Check that: +- The directory path exists or its parent exists +- You have write permissions +- The path is absolute or correctly relative to current directory + +### Module not found + +- Verify the module ID is correct (check available modules in interactive mode) +- External modules may need to be available in the registry + +### Custom content path invalid + +Ensure each custom content path: +- Points to a directory +- Contains a `module.yaml` file in the root +- Has a `code` field in the `module.yaml` + +## Feedback and Issues + +If you encounter any issues with non-interactive installation: + +1. Run with `--debug` flag for detailed output +2. Try the interactive mode to verify the issue +3. Report issues on GitHub: diff --git a/tools/cli/commands/install.js b/tools/cli/commands/install.js index ede133a8..6a6622d1 100644 --- a/tools/cli/commands/install.js +++ b/tools/cli/commands/install.js @@ -9,7 +9,22 @@ const ui = new UI(); module.exports = { command: 'install', description: 'Install BMAD Core agents and tools', - options: [['-d, --debug', 'Enable debug output for manifest generation']], + options: [ + ['-d, --debug', 'Enable debug output for manifest generation'], + ['--directory ', 'Installation directory (default: current directory)'], + ['--modules ', 'Comma-separated list of module IDs to install (e.g., "bmm,bmb")'], + [ + '--tools ', + 'Comma-separated list of tool/IDE IDs to configure (e.g., "claude-code,cursor"). Use "none" to skip tool configuration.', + ], + ['--custom-content ', 'Comma-separated list of paths to custom modules/agents/workflows'], + ['--action ', 'Action type for existing installations: install, update, quick-update, or compile-agents'], + ['--user-name ', 'Name for agents to use (default: system username)'], + ['--communication-language ', 'Language for agent communication (default: English)'], + ['--document-output-language ', 'Language for document output (default: English)'], + ['--output-folder ', 'Output folder path relative to project root (default: _bmad-output)'], + ['-y, --yes', 'Accept all defaults and skip prompts where possible'], + ], action: async (options) => { try { // Set debug flag as environment variable for all components @@ -18,7 +33,7 @@ module.exports = { console.log(chalk.cyan('Debug mode enabled\n')); } - const config = await ui.promptInstall(); + const config = await ui.promptInstall(options); // Handle cancel if (config.actionType === 'cancel') { diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index da5420cb..0187930d 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -26,9 +26,10 @@ const choiceUtils = { Separator }; class UI { /** * Prompt for installation configuration + * @param {Object} options - Command-line options from install command * @returns {Object} Installation configuration */ - async promptInstall() { + async promptInstall(options = {}) { CLIUtils.displayLogo(); // Display version-specific start message from install-messages.yaml @@ -36,7 +37,20 @@ class UI { const messageLoader = new MessageLoader(); messageLoader.displayStartMessage(); - const confirmedDirectory = await this.getConfirmedDirectory(); + // Get directory from options or prompt + let confirmedDirectory; + if (options.directory) { + // Use provided directory from command-line + const expandedDir = this.expandUserPath(options.directory); + const validation = this.validateDirectorySync(expandedDir); + if (validation !== true) { + throw new Error(`Invalid directory: ${validation}`); + } + confirmedDirectory = expandedDir; + console.log(chalk.cyan('Using directory from command-line:'), chalk.bold(confirmedDirectory)); + } else { + confirmedDirectory = await this.getConfirmedDirectory(); + } // Preflight: Check for legacy BMAD v4 footprints immediately after getting directory const { Detector } = require('../installers/lib/core/detector'); @@ -218,11 +232,21 @@ class UI { // Common actions choices.push({ name: 'Modify BMAD Installation', value: 'update' }); - actionType = await prompts.select({ - message: 'How would you like to proceed?', - choices: choices, - default: choices[0].value, - }); + // Check if action is provided via command-line + if (options.action) { + const validActions = choices.map((c) => c.value); + if (!validActions.includes(options.action)) { + throw new Error(`Invalid action: ${options.action}. Valid actions: ${validActions.join(', ')}`); + } + actionType = options.action; + console.log(chalk.cyan('Using action from command-line:'), chalk.bold(actionType)); + } else { + actionType = await prompts.select({ + message: 'How would you like to proceed?', + choices: choices, + default: choices[0].value, + }); + } // Handle quick update separately if (actionType === 'quick-update') { @@ -253,30 +277,94 @@ class UI { console.log(chalk.dim(` Found existing modules: ${[...installedModuleIds].join(', ')}`)); // Unified module selection - all modules in one grouped multiselect - let selectedModules = await this.selectAllModules(installedModuleIds); + let selectedModules; + if (options.modules) { + // Use modules from command-line + selectedModules = options.modules + .split(',') + .map((m) => m.trim()) + .filter(Boolean); + console.log(chalk.cyan('Using modules from command-line:'), chalk.bold(selectedModules.join(', '))); + } else { + selectedModules = await this.selectAllModules(installedModuleIds); + } // After module selection, ask about custom modules console.log(''); - const changeCustomModules = await prompts.confirm({ - message: 'Modify custom modules, agents, or workflows?', - default: false, - }); - let customModuleResult = { selectedCustomModules: [], customContentConfig: { hasCustomContent: false } }; - if (changeCustomModules) { - customModuleResult = await this.handleCustomModulesInModifyFlow(confirmedDirectory, selectedModules); - } else { - // Preserve existing custom modules if user doesn't want to modify them - const { Installer } = require('../installers/lib/core/installer'); - const installer = new Installer(); - const { bmadDir } = await installer.findBmadDir(confirmedDirectory); - 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); + if (options.customContent) { + // Use custom content from command-line + const paths = options.customContent + .split(',') + .map((p) => p.trim()) + .filter(Boolean); + console.log(chalk.cyan('Using custom content from command-line:'), chalk.bold(paths.join(', '))); + + // Build custom content config similar to promptCustomContentSource + const customPaths = []; + const selectedModuleIds = []; + + for (const customPath of paths) { + const expandedPath = this.expandUserPath(customPath); + const validation = this.validateCustomContentPathSync(expandedPath); + if (validation !== true) { + console.log(chalk.yellow(`⚠️ Skipping invalid custom content path: ${customPath} - ${validation}`)); + continue; + } + + // Read module metadata + let moduleMeta; + try { + const moduleYamlPath = path.join(expandedPath, 'module.yaml'); + const moduleYaml = await fs.readFile(moduleYamlPath, 'utf-8'); + const yaml = require('yaml'); + moduleMeta = yaml.parse(moduleYaml); + } catch (error) { + console.log(chalk.yellow(`⚠️ Skipping custom content path: ${customPath} - failed to read module.yaml: ${error.message}`)); + continue; + } + + if (!moduleMeta.code) { + console.log(chalk.yellow(`⚠️ Skipping custom content path: ${customPath} - module.yaml missing 'code' field`)); + continue; + } + + customPaths.push(expandedPath); + selectedModuleIds.push(moduleMeta.code); + } + + if (customPaths.length > 0) { + customModuleResult = { + selectedCustomModules: selectedModuleIds, + customContentConfig: { + hasCustomContent: true, + paths: customPaths, + selectedModuleIds: selectedModuleIds, + }, + }; + } + } else { + const changeCustomModules = await prompts.confirm({ + message: 'Modify custom modules, agents, or workflows?', + default: false, + }); + + if (changeCustomModules) { + customModuleResult = await this.handleCustomModulesInModifyFlow(confirmedDirectory, selectedModules); + } else { + // Preserve existing custom modules if user doesn't want to modify them + const { Installer } = require('../installers/lib/core/installer'); + const installer = new Installer(); + const { bmadDir } = await installer.findBmadDir(confirmedDirectory); + + 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); + } } } } @@ -288,9 +376,9 @@ class UI { } // Get tool selection - const toolSelection = await this.promptToolSelection(confirmedDirectory); + const toolSelection = await this.promptToolSelection(confirmedDirectory, options); - const coreConfig = await this.collectCoreConfig(confirmedDirectory); + const coreConfig = await this.collectCoreConfig(confirmedDirectory, options); return { actionType: 'update', @@ -309,16 +397,76 @@ class UI { const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory); // Unified module selection - all modules in one grouped multiselect - let selectedModules = await this.selectAllModules(installedModuleIds); + let selectedModules; + if (options.modules) { + // Use modules from command-line + selectedModules = options.modules + .split(',') + .map((m) => m.trim()) + .filter(Boolean); + console.log(chalk.cyan('Using modules from command-line:'), chalk.bold(selectedModules.join(', '))); + } else { + selectedModules = await this.selectAllModules(installedModuleIds); + } // Ask about custom content (local modules/agents/workflows) - const wantsCustomContent = await prompts.confirm({ - message: 'Add custom modules, agents, or workflows from your computer?', - default: false, - }); + if (options.customContent) { + // Use custom content from command-line + const paths = options.customContent + .split(',') + .map((p) => p.trim()) + .filter(Boolean); + console.log(chalk.cyan('Using custom content from command-line:'), chalk.bold(paths.join(', '))); - if (wantsCustomContent) { - customContentConfig = await this.promptCustomContentSource(); + // Build custom content config similar to promptCustomContentSource + const customPaths = []; + const selectedModuleIds = []; + + for (const customPath of paths) { + const expandedPath = this.expandUserPath(customPath); + const validation = this.validateCustomContentPathSync(expandedPath); + if (validation !== true) { + console.log(chalk.yellow(`⚠️ Skipping invalid custom content path: ${customPath} - ${validation}`)); + continue; + } + + // Read module metadata + let moduleMeta; + try { + const moduleYamlPath = path.join(expandedPath, 'module.yaml'); + const moduleYaml = await fs.readFile(moduleYamlPath, 'utf-8'); + const yaml = require('yaml'); + moduleMeta = yaml.parse(moduleYaml); + } catch (error) { + console.log(chalk.yellow(`⚠️ Skipping custom content path: ${customPath} - failed to read module.yaml: ${error.message}`)); + continue; + } + + if (!moduleMeta.code) { + console.log(chalk.yellow(`⚠️ Skipping custom content path: ${customPath} - module.yaml missing 'code' field`)); + continue; + } + + customPaths.push(expandedPath); + selectedModuleIds.push(moduleMeta.code); + } + + if (customPaths.length > 0) { + customContentConfig = { + hasCustomContent: true, + paths: customPaths, + selectedModuleIds: selectedModuleIds, + }; + } + } else if (!options.yes) { + const wantsCustomContent = await prompts.confirm({ + message: 'Add custom modules, agents, or workflows from your computer?', + default: false, + }); + + if (wantsCustomContent) { + customContentConfig = await this.promptCustomContentSource(); + } } // Add custom content modules if any were selected @@ -327,8 +475,8 @@ class UI { } selectedModules = selectedModules.filter((m) => m !== 'core'); - let toolSelection = await this.promptToolSelection(confirmedDirectory); - const coreConfig = await this.collectCoreConfig(confirmedDirectory); + let toolSelection = await this.promptToolSelection(confirmedDirectory, options); + const coreConfig = await this.collectCoreConfig(confirmedDirectory, options); return { actionType: 'install', @@ -345,9 +493,10 @@ class UI { /** * Prompt for tool/IDE selection (called after module configuration) * @param {string} projectDir - Project directory to check for existing IDEs + * @param {Object} options - Command-line options * @returns {Object} Tool configuration */ - async promptToolSelection(projectDir) { + async promptToolSelection(projectDir, options = {}) { // Check for existing configured IDEs - use findBmadDir to detect custom folder names const { Detector } = require('../installers/lib/core/detector'); const { Installer } = require('../installers/lib/core/installer'); @@ -433,13 +582,37 @@ class UI { let selectedIdes = []; - selectedIdes = await prompts.groupMultiselect({ - message: `Select tools to configure ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`, - options: groupedOptions, - initialValues: initialValues.length > 0 ? initialValues : undefined, - required: true, - selectableGroups: false, - }); + // Check if tools are provided via command-line + if (options.tools) { + // Check for explicit "none" value to skip tool installation + if (options.tools.toLowerCase() === 'none') { + console.log(chalk.cyan('Skipping tool configuration (--tools none)')); + selectedIdes = []; + } else { + selectedIdes = options.tools + .split(',') + .map((t) => t.trim()) + .filter(Boolean); + console.log(chalk.cyan('Using tools from command-line:'), chalk.bold(selectedIdes.join(', '))); + } + } else if (options.yes) { + // If --yes flag is set, skip tool prompt and use previously configured tools or empty + if (initialValues.length > 0) { + selectedIdes = initialValues; + console.log(chalk.cyan('Using previously configured tools (--yes flag):'), chalk.bold(selectedIdes.join(', '))); + } else { + console.log(chalk.cyan('Skipping tool configuration (--yes flag, no previous tools)')); + selectedIdes = []; + } + } else { + selectedIdes = await prompts.groupMultiselect({ + message: `Select tools to configure ${chalk.dim('(↑/↓ navigates, SPACE toggles, ENTER to confirm)')}:`, + options: groupedOptions, + initialValues: initialValues.length > 0 ? initialValues : undefined, + required: true, + selectableGroups: false, + }); + } // If user selected both "__NONE__" and other tools, honor the "None" choice if (selectedIdes && selectedIdes.includes('__NONE__') && selectedIdes.length > 1) { @@ -542,15 +715,75 @@ class UI { /** * Collect core configuration * @param {string} directory - Installation directory + * @param {Object} options - Command-line options * @returns {Object} Core configuration */ - async collectCoreConfig(directory) { + async collectCoreConfig(directory, options = {}) { const { ConfigCollector } = require('../installers/lib/core/config-collector'); const configCollector = new ConfigCollector(); - // Load existing configs first if they exist - await configCollector.loadExistingConfig(directory); - // Now collect with existing values as defaults (false = don't skip loading, true = skip completion message) - await configCollector.collectModuleConfig('core', directory, false, true); + + // If options are provided, set them directly + if (options.userName || options.communicationLanguage || options.documentOutputLanguage || options.outputFolder) { + const coreConfig = {}; + if (options.userName) { + coreConfig.user_name = options.userName; + console.log(chalk.cyan('Using user name from command-line:'), chalk.bold(options.userName)); + } + if (options.communicationLanguage) { + coreConfig.communication_language = options.communicationLanguage; + console.log(chalk.cyan('Using communication language from command-line:'), chalk.bold(options.communicationLanguage)); + } + if (options.documentOutputLanguage) { + coreConfig.document_output_language = options.documentOutputLanguage; + console.log(chalk.cyan('Using document output language from command-line:'), chalk.bold(options.documentOutputLanguage)); + } + if (options.outputFolder) { + coreConfig.output_folder = options.outputFolder; + console.log(chalk.cyan('Using output folder from command-line:'), chalk.bold(options.outputFolder)); + } + + // Load existing config to merge with provided options + await configCollector.loadExistingConfig(directory); + + // Merge provided options with existing config (or defaults) + const existingConfig = configCollector.collectedConfig.core || {}; + configCollector.collectedConfig.core = { ...existingConfig, ...coreConfig }; + + // If not all options are provided, collect the missing ones interactively (unless --yes flag) + if ( + !options.yes && + (!options.userName || !options.communicationLanguage || !options.documentOutputLanguage || !options.outputFolder) + ) { + await configCollector.collectModuleConfig('core', directory, false, true); + } + } else if (options.yes) { + // Use all defaults when --yes flag is set + await configCollector.loadExistingConfig(directory); + const existingConfig = configCollector.collectedConfig.core || {}; + + // If no existing config, use defaults + if (Object.keys(existingConfig).length === 0) { + let safeUsername; + try { + safeUsername = os.userInfo().username; + } catch { + safeUsername = process.env.USER || process.env.USERNAME || 'User'; + } + const defaultUsername = safeUsername.charAt(0).toUpperCase() + safeUsername.slice(1); + configCollector.collectedConfig.core = { + user_name: defaultUsername, + communication_language: 'English', + document_output_language: 'English', + output_folder: '_bmad-output', + }; + console.log(chalk.cyan('Using default configuration (--yes flag)')); + } + } else { + // Load existing configs first if they exist + await configCollector.loadExistingConfig(directory); + // Now collect with existing values as defaults (false = don't skip loading, true = skip completion message) + await configCollector.collectModuleConfig('core', directory, false, true); + } const coreConfig = configCollector.collectedConfig.core; // Ensure we always have a core config object, even if empty