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',