From 94f67000b25e0abbdea6d1dc3cec5ce5cb080421 Mon Sep 17 00:00:00 2001 From: Daniel Dabrowski <106607+rodpl@users.noreply.github.com> Date: Sat, 6 Sep 2025 20:14:24 +0200 Subject: [PATCH 1/7] feat: enhance file exclusion capabilities with .bmad-flattenignore support (#531) - Added support for a new optional `.bmad-flattenignore` file to allow users to specify additional files to exclude from the flattened XML output. - Updated README and documentation to reflect the new feature and provide examples for usage. - Modified ignore rules to incorporate patterns from the `.bmad-flattenignore` file after applying `.gitignore` rules. Benefits: - Greater flexibility in managing file exclusions for AI model consumption. --- README.md | 14 +++++++++++++- docs/working-in-the-brownfield.md | 8 +++++++- tools/flattener/ignoreRules.js | 2 ++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 135a2937..b5048bfc 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ The BMAD-METHOD™ includes a powerful codebase flattener tool designed to prepa ### Features - **AI-Optimized Output**: Generates clean XML format specifically designed for AI model consumption -- **Smart Filtering**: Automatically respects `.gitignore` patterns to exclude unnecessary files +- **Smart Filtering**: Automatically respects `.gitignore` patterns to exclude unnecessary files, plus optional project-level `.bmad-flattenignore` for additional exclusions - **Binary File Detection**: Intelligently identifies and excludes binary files, focusing on source code - **Progress Tracking**: Real-time progress indicators and comprehensive completion statistics - **Flexible Output**: Customizable output file location and naming @@ -170,6 +170,18 @@ The generated XML file contains your project's text-based source files in a stru - File discovery and ignoring - Uses `git ls-files` when inside a git repository for speed and correctness; otherwise falls back to a glob-based scan. - Applies your `.gitignore` plus a curated set of default ignore patterns (e.g., `node_modules`, build outputs, caches, logs, IDE folders, lockfiles, large media/binaries, `.env*`, and previously generated XML outputs). + - Supports an optional `.bmad-flattenignore` file at the project root for additional ignore patterns (gitignore-style). If present, its rules are applied after `.gitignore` and the defaults. + +##### `.bmad-flattenignore` example + +Create a `.bmad-flattenignore` file in the root of your project to exclude files that must remain in git but should not be included in the flattened XML: + +```text +seeds/** +scripts/private/** +**/*.snap +``` + - Binary handling - Binary files are detected and excluded from the XML content. They are counted in the final summary but not embedded in the output. - XML format and safety diff --git a/docs/working-in-the-brownfield.md b/docs/working-in-the-brownfield.md index aafea7ae..0a979357 100644 --- a/docs/working-in-the-brownfield.md +++ b/docs/working-in-the-brownfield.md @@ -5,7 +5,13 @@ > Gemini Web's 1M+ token context window or Gemini CLI (when it's working) can analyze your ENTIRE codebase, or critical sections of it, all at once (obviously within reason): > > - Upload via GitHub URL or use gemini cli in the project folder -> - If working in the web: use `npx bmad-method flatten` to flatten your project into a single file, then upload that file to your web agent. +> - If working in the web: use `npx bmad-method flatten` to flatten your project into a single file, then upload that file to your web agent. To exclude additional files that must remain in git but shouldn't be sent to the AI, add a `.bmad-flattenignore` file at the project root (gitignore-style), e.g.: +> +> ```text +> seeds/** +> scripts/private/** +> **/*.snap +> ``` ## What is Brownfield Development? diff --git a/tools/flattener/ignoreRules.js b/tools/flattener/ignoreRules.js index bb3a3135..2784458b 100644 --- a/tools/flattener/ignoreRules.js +++ b/tools/flattener/ignoreRules.js @@ -154,9 +154,11 @@ async function parseGitignore(gitignorePath) { async function loadIgnore(rootDir, extraPatterns = []) { const ig = ignore(); const gitignorePath = path.join(rootDir, '.gitignore'); + const flattenIgnorePath = path.join(rootDir, '.bmad-flattenignore'); const patterns = [ ...(await readIgnoreFile(gitignorePath)), ...DEFAULT_PATTERNS, + ...(await readIgnoreFile(flattenIgnorePath)), ...extraPatterns, ]; // De-duplicate From ac7f2437f8dc5b730ffbf5912efcefe824f8d4b2 Mon Sep 17 00:00:00 2001 From: Yuewen Ma Date: Sat, 6 Sep 2025 19:16:10 +0100 Subject: [PATCH 2/7] Fixed: "glob" is not defined (#504) Co-authored-by: Brian --- tools/installer/lib/ide-setup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js index a0ff58c7..3cbaffe3 100644 --- a/tools/installer/lib/ide-setup.js +++ b/tools/installer/lib/ide-setup.js @@ -690,6 +690,7 @@ class IdeSetup extends BaseIdeSetup { async getCoreTaskIds(installDir) { const allTaskIds = []; + const glob = require('glob'); // Check core tasks in .bmad-core or root only let tasksDir = path.join(installDir, '.bmad-core', 'tasks'); @@ -698,7 +699,6 @@ class IdeSetup extends BaseIdeSetup { } if (await fileManager.pathExists(tasksDir)) { - const glob = require('glob'); const taskFiles = glob.sync('*.md', { cwd: tasksDir }); allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md'))); } From 5ae4c5188311ff5ad5e90e601e1628fe19f98ab7 Mon Sep 17 00:00:00 2001 From: Yoav Gal <45267791+yoav0gal@users.noreply.github.com> Date: Sat, 6 Sep 2025 21:19:47 +0300 Subject: [PATCH 3/7] added gemini cli custom commands! (#549) * added gemini cli custom commands! * improvements and changes post review * updated bmad to BMad * removed gemini-extension.json --------- Co-authored-by: Brian --- tools/installer/config/install.config.yaml | 14 +- tools/installer/lib/ide-setup.js | 148 +++++++++------------ 2 files changed, 71 insertions(+), 91 deletions(-) diff --git a/tools/installer/config/install.config.yaml b/tools/installer/config/install.config.yaml index 0444813a..4b1ff8fb 100644 --- a/tools/installer/config/install.config.yaml +++ b/tools/installer/config/install.config.yaml @@ -78,15 +78,15 @@ ide-configurations: # 4. Rules are stored in .clinerules/ directory in your project gemini: name: Gemini CLI - rule-dir: .gemini/bmad-method/ - format: single-file - command-suffix: .md + rule-dir: .gemini/commands/BMad/ + format: multi-file + command-suffix: .toml instructions: | # To use BMad agents with the Gemini CLI: - # 1. The installer creates a .gemini/bmad-method/ directory in your project. - # 2. It concatenates all agent files into a single GEMINI.md file. - # 3. Simply mention the agent in your prompt (e.g., "As *dev, ..."). - # 4. The Gemini CLI will automatically have the context for that agent. + # 1. The installer creates a `BMad` folder in `.gemini/commands`. + # 2. This adds custom commands for each agent and task. + # 3. Type /BMad:agents: (e.g., "/BMad:agents:dev", "/BMad:agents:pm") or /BMad:tasks: (e.g., "/BMad:tasks:create-doc"). + # 4. The agent will adopt that persona for the conversation or preform the task. github-copilot: name: Github Copilot rule-dir: .github/chatmodes/ diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js index 3cbaffe3..09fa46ad 100644 --- a/tools/installer/lib/ide-setup.js +++ b/tools/installer/lib/ide-setup.js @@ -1208,97 +1208,77 @@ class IdeSetup extends BaseIdeSetup { return true; } - async setupGeminiCli(installDir) { - const geminiDir = path.join(installDir, '.gemini'); - const bmadMethodDir = path.join(geminiDir, 'bmad-method'); - await fileManager.ensureDirectory(bmadMethodDir); + async setupGeminiCli(installDir, selectedAgent) { + const ideConfig = await configLoader.getIdeConfiguration('gemini'); + const bmadCommandsDir = path.join(installDir, ideConfig['rule-dir']); - // Update logic for existing settings.json - const settingsPath = path.join(geminiDir, 'settings.json'); - if (await fileManager.pathExists(settingsPath)) { - try { - const settingsContent = await fileManager.readFile(settingsPath); - const settings = JSON.parse(settingsContent); - let updated = false; - - // Handle contextFileName property - if (settings.contextFileName && Array.isArray(settings.contextFileName)) { - const originalLength = settings.contextFileName.length; - settings.contextFileName = settings.contextFileName.filter( - (fileName) => !fileName.startsWith('agents/'), - ); - if (settings.contextFileName.length !== originalLength) { - updated = true; - } - } - - if (updated) { - await fileManager.writeFile(settingsPath, JSON.stringify(settings, null, 2)); - console.log( - chalk.green('✓ Updated .gemini/settings.json - removed agent file references'), - ); - } - } catch (error) { - console.warn(chalk.yellow('Could not update .gemini/settings.json'), error); - } - } - - // Remove old agents directory - const agentsDir = path.join(geminiDir, 'agents'); - if (await fileManager.pathExists(agentsDir)) { - await fileManager.removeDirectory(agentsDir); - console.log(chalk.green('✓ Removed old .gemini/agents directory')); - } - - // Get all available agents - const agents = await this.getAllAgentIds(installDir); - let concatenatedContent = ''; + const agentCommandsDir = path.join(bmadCommandsDir, 'agents'); + const taskCommandsDir = path.join(bmadCommandsDir, 'tasks'); + await fileManager.ensureDirectory(agentCommandsDir); + await fileManager.ensureDirectory(taskCommandsDir); + // Process Agents + const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir); for (const agentId of agents) { - // Find the source agent file const agentPath = await this.findAgentPath(agentId, installDir); - - if (agentPath) { - const agentContent = await fileManager.readFile(agentPath); - - // Create properly formatted agent rule content (similar to trae) - let agentRuleContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`; - agentRuleContent += `This rule is triggered when the user types \`*${agentId}\` and activates the ${await this.getAgentTitle( - agentId, - installDir, - )} agent persona.\n\n`; - agentRuleContent += '## Agent Activation\n\n'; - agentRuleContent += - 'CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n'; - agentRuleContent += '```yaml\n'; - // Extract just the YAML content from the agent file - const yamlContent = extractYamlFromAgent(agentContent); - if (yamlContent) { - agentRuleContent += yamlContent; - } else { - // If no YAML found, include the whole content minus the header - agentRuleContent += agentContent.replace(/^#.*$/m, '').trim(); - } - agentRuleContent += '\n```\n\n'; - agentRuleContent += '## File Reference\n\n'; - const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/'); - agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`; - agentRuleContent += '## Usage\n\n'; - agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle( - agentId, - installDir, - )} persona and follow all instructions defined in the YAML configuration above.\n`; - - // Add to concatenated content with separator - concatenatedContent += agentRuleContent + '\n\n---\n\n'; - console.log(chalk.green(`✓ Added context for @${agentId}`)); + if (!agentPath) { + console.log(chalk.yellow(`✗ Agent file not found for ${agentId}, skipping.`)); + continue; } + + const agentTitle = await this.getAgentTitle(agentId, installDir); + const commandPath = path.join(agentCommandsDir, `${agentId}.toml`); + + // Get relative path from installDir to agent file for @{file} reference + const relativeAgentPath = path.relative(installDir, agentPath).replaceAll('\\', '/'); + + const tomlContent = `description = "Activates the ${agentTitle} agent from the BMad Method." +prompt = """ +CRITICAL: You are now the BMad '${agentTitle}' agent. Adopt its persona, follow its instructions, and use its capabilities. The full agent definition is below. + +@{${relativeAgentPath}} +"""`; + + await fileManager.writeFile(commandPath, tomlContent); + console.log(chalk.green(`✓ Created agent command: /bmad:agents:${agentId}`)); } - // Write the concatenated content to GEMINI.md - const geminiMdPath = path.join(bmadMethodDir, 'GEMINI.md'); - await fileManager.writeFile(geminiMdPath, concatenatedContent); - console.log(chalk.green(`\n✓ Created GEMINI.md in ${bmadMethodDir}`)); + // Process Tasks + const tasks = await this.getAllTaskIds(installDir); + for (const taskId of tasks) { + const taskPath = await this.findTaskPath(taskId, installDir); + if (!taskPath) { + console.log(chalk.yellow(`✗ Task file not found for ${taskId}, skipping.`)); + continue; + } + + const taskTitle = taskId + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + const commandPath = path.join(taskCommandsDir, `${taskId}.toml`); + + // Get relative path from installDir to task file for @{file} reference + const relativeTaskPath = path.relative(installDir, taskPath).replaceAll('\\', '/'); + + const tomlContent = `description = "Executes the BMad Task: ${taskTitle}" +prompt = """ +CRITICAL: You are to execute the BMad Task defined below. + +@{${relativeTaskPath}} +"""`; + + await fileManager.writeFile(commandPath, tomlContent); + console.log(chalk.green(`✓ Created task command: /bmad:tasks:${taskId}`)); + } + + console.log( + chalk.green(` +✓ Created Gemini CLI extension in ${bmadCommandsDir}`), + ); + console.log( + chalk.dim('You can now use commands like /bmad:agents:dev or /bmad:tasks:create-doc.'), + ); return true; } From af368646253c5ea1acb1dcdae83e9d8cb2e492eb Mon Sep 17 00:00:00 2001 From: VansonLeung Date: Sun, 7 Sep 2025 02:38:29 +0800 Subject: [PATCH 4/7] Update ide-setup.js - add missing glob require (#514) Co-authored-by: Brian --- tools/installer/lib/ide-setup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js index 09fa46ad..638b4c93 100644 --- a/tools/installer/lib/ide-setup.js +++ b/tools/installer/lib/ide-setup.js @@ -706,6 +706,7 @@ class IdeSetup extends BaseIdeSetup { // Check common tasks const commonTasksDir = path.join(installDir, 'common', 'tasks'); if (await fileManager.pathExists(commonTasksDir)) { + const glob = require('glob'); const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir }); allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md'))); } From a05b05cec0f78f0f4539c636203a9ecac59521a1 Mon Sep 17 00:00:00 2001 From: Drilmo <31286436+Drilmo@users.noreply.github.com> Date: Sat, 6 Sep 2025 20:40:30 +0200 Subject: [PATCH 5/7] fix: Template file extension in validation next story steps (#523) Co-authored-by: Brian --- bmad-core/tasks/validate-next-story.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bmad-core/tasks/validate-next-story.md b/bmad-core/tasks/validate-next-story.md index fb7688d9..a7e7643b 100644 --- a/bmad-core/tasks/validate-next-story.md +++ b/bmad-core/tasks/validate-next-story.md @@ -21,7 +21,7 @@ To comprehensively validate a story draft before implementation begins, ensuring ### 1. Template Completeness Validation -- Load `bmad-core/templates/story-tmpl.md` and extract all section headings from the template +- Load `.bmad-core/templates/story-tmpl.yaml` and extract all section headings from the template - **Missing sections check**: Compare story sections against template sections to verify all required sections are present - **Placeholder validation**: Ensure no template placeholders remain unfilled (e.g., `{{EpicNum}}`, `{{role}}`, `_TBD_`) - **Agent section verification**: Confirm all sections from template exist for future agent use From 87d68d31fd7d236cfa88aea8ab453fa304a5bc68 Mon Sep 17 00:00:00 2001 From: Armel BOBDA <132626034+armelhbobdad@users.noreply.github.com> Date: Sat, 6 Sep 2025 22:43:05 +0400 Subject: [PATCH 6/7] fix: update .gitignore to correct cursor file entry (#485) This change modifies the .gitignore file to ensure the cursor file is properly ignored by removing the incorrect entry and adding the correct one. This helps maintain a cleaner repository by preventing unnecessary files from being tracked. Co-authored-by: Brian --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a76e85f6..dcfdb613 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,8 @@ Thumbs.db # IDE and editor configs .windsurf/ .trae/ -.bmad*/.cursor/ +.bmad*/ +.cursor/ # AI assistant files CLAUDE.md From 076c104b2cb41fdd814c4d72a4d08b61c8b073cc Mon Sep 17 00:00:00 2001 From: liyuyun-lyy Date: Sun, 7 Sep 2025 02:44:48 +0800 Subject: [PATCH 7/7] feat: add iflow cli support to bmad installer. (#510) * feat: add iflow cli support * chore: update project dependencies and development tooling (#508) - Update various direct and transitive project dependencies. - Remove the circular `bmad-method` self-dependency. - Upgrade ESLint, Prettier, Jest, and other dev tools. - Update semantic-release and related GitHub packages. Co-authored-by: Kayvan Sylvan * refactor: refactor by format --------- Co-authored-by: Kayvan Sylvan Co-authored-by: Kayvan Sylvan Co-authored-by: PinkyD Co-authored-by: Brian --- .gitignore | 1 + tools/installer/bin/bmad.js | 3 +- tools/installer/config/install.config.yaml | 9 ++ tools/installer/lib/ide-setup.js | 131 +++++++++++++++++++++ tools/upgraders/v3-to-v4-upgrader.js | 1 + 5 files changed, 144 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dcfdb613..f700eb24 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ CLAUDE.md .ai/* .claude .gemini +.iflow # Project-specific .bmad-core diff --git a/tools/installer/bin/bmad.js b/tools/installer/bin/bmad.js index f121417b..39816407 100755 --- a/tools/installer/bin/bmad.js +++ b/tools/installer/bin/bmad.js @@ -49,7 +49,7 @@ program .option('-d, --directory ', 'Installation directory') .option( '-i, --ide ', - 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, codex, codex-web, auggie-cli, other)', + 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, codex, codex-web, auggie-cli, iflow-cli, other)', ) .option( '-e, --expansion-packs ', @@ -397,6 +397,7 @@ async function promptInstallation() { choices: [ { name: 'Cursor', value: 'cursor' }, { name: 'Claude Code', value: 'claude-code' }, + { name: 'iFlow CLI', value: 'iflow-cli' }, { name: 'Windsurf', value: 'windsurf' }, { name: 'Trae', value: 'trae' }, // { name: 'Trae', value: 'trae'} { name: 'Roo Code', value: 'roo' }, diff --git a/tools/installer/config/install.config.yaml b/tools/installer/config/install.config.yaml index 4b1ff8fb..f7010838 100644 --- a/tools/installer/config/install.config.yaml +++ b/tools/installer/config/install.config.yaml @@ -28,6 +28,15 @@ ide-configurations: # To use BMad agents in Claude Code: # 1. Type /agent-name (e.g., "/dev", "/pm", "/architect") # 2. Claude will switch to that agent's persona + iflow-cli: + name: iFlow CLI + rule-dir: .iflow/commands/BMad/ + format: multi-file + command-suffix: .md + instructions: | + # To use BMad agents in iFlow CLI: + # 1. Type /agent-name (e.g., "/dev", "/pm", "/architect") + # 2. iFlow will switch to that agent's persona crush: name: Crush rule-dir: .crush/commands/BMad/ diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js index 638b4c93..4de928bd 100644 --- a/tools/installer/lib/ide-setup.js +++ b/tools/installer/lib/ide-setup.js @@ -47,6 +47,9 @@ class IdeSetup extends BaseIdeSetup { case 'claude-code': { return this.setupClaudeCode(installDir, selectedAgent); } + case 'iflow-cli': { + return this.setupIFlowCli(installDir, selectedAgent); + } case 'crush': { return this.setupCrush(installDir, selectedAgent); } @@ -453,6 +456,134 @@ class IdeSetup extends BaseIdeSetup { console.log(chalk.dim(` - Tasks in: ${tasksDir}`)); } + async setupIFlowCli(installDir, selectedAgent) { + // Setup bmad-core commands + const coreSlashPrefix = await this.getCoreSlashPrefix(installDir); + const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir); + const coreTasks = await this.getCoreTaskIds(installDir); + await this.setupIFlowCliForPackage( + installDir, + 'core', + coreSlashPrefix, + coreAgents, + coreTasks, + '.bmad-core', + ); + + // Setup expansion pack commands + const expansionPacks = await this.getInstalledExpansionPacks(installDir); + for (const packInfo of expansionPacks) { + const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path); + const packAgents = await this.getExpansionPackAgents(packInfo.path); + const packTasks = await this.getExpansionPackTasks(packInfo.path); + + if (packAgents.length > 0 || packTasks.length > 0) { + // Use the actual directory name where the expansion pack is installed + const rootPath = path.relative(installDir, packInfo.path); + await this.setupIFlowCliForPackage( + installDir, + packInfo.name, + packSlashPrefix, + packAgents, + packTasks, + rootPath, + ); + } + } + + return true; + } + + async setupIFlowCliForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) { + const commandsBaseDir = path.join(installDir, '.iflow', 'commands', slashPrefix); + const agentsDir = path.join(commandsBaseDir, 'agents'); + const tasksDir = path.join(commandsBaseDir, 'tasks'); + + // Ensure directories exist + await fileManager.ensureDirectory(agentsDir); + await fileManager.ensureDirectory(tasksDir); + + // Setup agents + for (const agentId of agentIds) { + // Find the agent file - for expansion packs, prefer the expansion pack version + let agentPath; + if (packageName === 'core') { + // For core, use the normal search + agentPath = await this.findAgentPath(agentId, installDir); + } else { + // For expansion packs, first try to find the agent in the expansion pack directory + const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`); + if (await fileManager.pathExists(expansionPackPath)) { + agentPath = expansionPackPath; + } else { + // Fall back to core if not found in expansion pack + agentPath = await this.findAgentPath(agentId, installDir); + } + } + + const commandPath = path.join(agentsDir, `${agentId}.md`); + + if (agentPath) { + // Create command file with agent content + let agentContent = await fileManager.readFile(agentPath); + + // Replace {root} placeholder with the appropriate root path for this context + agentContent = agentContent.replaceAll('{root}', rootPath); + + // Add command header + let commandContent = `# /${agentId} Command\n\n`; + commandContent += `When this command is used, adopt the following agent persona:\n\n`; + commandContent += agentContent; + + await fileManager.writeFile(commandPath, commandContent); + console.log(chalk.green(`✓ Created agent command: /${agentId}`)); + } + } + + // Setup tasks + for (const taskId of taskIds) { + // Find the task file - for expansion packs, prefer the expansion pack version + let taskPath; + if (packageName === 'core') { + // For core, use the normal search + taskPath = await this.findTaskPath(taskId, installDir); + } else { + // For expansion packs, first try to find the task in the expansion pack directory + const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`); + if (await fileManager.pathExists(expansionPackPath)) { + taskPath = expansionPackPath; + } else { + // Fall back to core if not found in expansion pack + taskPath = await this.findTaskPath(taskId, installDir); + } + } + + const commandPath = path.join(tasksDir, `${taskId}.md`); + + if (taskPath) { + // Create command file with task content + let taskContent = await fileManager.readFile(taskPath); + + // Replace {root} placeholder with the appropriate root path for this context + taskContent = taskContent.replaceAll('{root}', rootPath); + + // Add command header + let commandContent = `# /${taskId} Task\n\n`; + commandContent += `When this command is used, execute the following task:\n\n`; + commandContent += taskContent; + + await fileManager.writeFile(commandPath, commandContent); + console.log(chalk.green(`✓ Created task command: /${taskId}`)); + } + } + + console.log( + chalk.green(`\n✓ Created iFlow CLI commands for ${packageName} in ${commandsBaseDir}`), + ); + console.log(chalk.dim(` - Agents in: ${agentsDir}`)); + console.log(chalk.dim(` - Tasks in: ${tasksDir}`)); + } + async setupCrushForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) { const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix); const agentsDir = path.join(commandsBaseDir, 'agents'); diff --git a/tools/upgraders/v3-to-v4-upgrader.js b/tools/upgraders/v3-to-v4-upgrader.js index 006afbc4..94c72563 100644 --- a/tools/upgraders/v3-to-v4-upgrader.js +++ b/tools/upgraders/v3-to-v4-upgrader.js @@ -492,6 +492,7 @@ class V3ToV4Upgrader { const ideMessages = { cursor: 'Rules created in .cursor/rules/bmad/', 'claude-code': 'Commands created in .claude/commands/BMad/', + 'iflow-cli': 'Commands created in .iflow/commands/BMad/', windsurf: 'Rules created in .windsurf/workflows/', trae: 'Rules created in.trae/rules/', roo: 'Custom modes created in .roomodes',