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 <kayvan@meanwhile.bm> * refactor: refactor by format --------- Co-authored-by: Kayvan Sylvan <kayvan@sylvan.com> Co-authored-by: Kayvan Sylvan <kayvan@meanwhile.bm> Co-authored-by: PinkyD <paulbeanjr@gmail.com> Co-authored-by: Brian <bmadcode@gmail.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -37,6 +37,7 @@ CLAUDE.md
|
|||||||
.ai/*
|
.ai/*
|
||||||
.claude
|
.claude
|
||||||
.gemini
|
.gemini
|
||||||
|
.iflow
|
||||||
|
|
||||||
# Project-specific
|
# Project-specific
|
||||||
.bmad-core
|
.bmad-core
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ program
|
|||||||
.option('-d, --directory <path>', 'Installation directory')
|
.option('-d, --directory <path>', 'Installation directory')
|
||||||
.option(
|
.option(
|
||||||
'-i, --ide <ide...>',
|
'-i, --ide <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(
|
.option(
|
||||||
'-e, --expansion-packs <packs...>',
|
'-e, --expansion-packs <packs...>',
|
||||||
@@ -397,6 +397,7 @@ async function promptInstallation() {
|
|||||||
choices: [
|
choices: [
|
||||||
{ name: 'Cursor', value: 'cursor' },
|
{ name: 'Cursor', value: 'cursor' },
|
||||||
{ name: 'Claude Code', value: 'claude-code' },
|
{ name: 'Claude Code', value: 'claude-code' },
|
||||||
|
{ name: 'iFlow CLI', value: 'iflow-cli' },
|
||||||
{ name: 'Windsurf', value: 'windsurf' },
|
{ name: 'Windsurf', value: 'windsurf' },
|
||||||
{ name: 'Trae', value: 'trae' }, // { name: 'Trae', value: 'trae'}
|
{ name: 'Trae', value: 'trae' }, // { name: 'Trae', value: 'trae'}
|
||||||
{ name: 'Roo Code', value: 'roo' },
|
{ name: 'Roo Code', value: 'roo' },
|
||||||
|
|||||||
@@ -28,6 +28,15 @@ ide-configurations:
|
|||||||
# To use BMad agents in Claude Code:
|
# To use BMad agents in Claude Code:
|
||||||
# 1. Type /agent-name (e.g., "/dev", "/pm", "/architect")
|
# 1. Type /agent-name (e.g., "/dev", "/pm", "/architect")
|
||||||
# 2. Claude will switch to that agent's persona
|
# 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:
|
crush:
|
||||||
name: Crush
|
name: Crush
|
||||||
rule-dir: .crush/commands/BMad/
|
rule-dir: .crush/commands/BMad/
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class IdeSetup extends BaseIdeSetup {
|
|||||||
case 'claude-code': {
|
case 'claude-code': {
|
||||||
return this.setupClaudeCode(installDir, selectedAgent);
|
return this.setupClaudeCode(installDir, selectedAgent);
|
||||||
}
|
}
|
||||||
|
case 'iflow-cli': {
|
||||||
|
return this.setupIFlowCli(installDir, selectedAgent);
|
||||||
|
}
|
||||||
case 'crush': {
|
case 'crush': {
|
||||||
return this.setupCrush(installDir, selectedAgent);
|
return this.setupCrush(installDir, selectedAgent);
|
||||||
}
|
}
|
||||||
@@ -453,6 +456,134 @@ class IdeSetup extends BaseIdeSetup {
|
|||||||
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
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) {
|
async setupCrushForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
|
||||||
const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix);
|
const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix);
|
||||||
const agentsDir = path.join(commandsBaseDir, 'agents');
|
const agentsDir = path.join(commandsBaseDir, 'agents');
|
||||||
|
|||||||
@@ -492,6 +492,7 @@ class V3ToV4Upgrader {
|
|||||||
const ideMessages = {
|
const ideMessages = {
|
||||||
cursor: 'Rules created in .cursor/rules/bmad/',
|
cursor: 'Rules created in .cursor/rules/bmad/',
|
||||||
'claude-code': 'Commands created in .claude/commands/BMad/',
|
'claude-code': 'Commands created in .claude/commands/BMad/',
|
||||||
|
'iflow-cli': 'Commands created in .iflow/commands/BMad/',
|
||||||
windsurf: 'Rules created in .windsurf/workflows/',
|
windsurf: 'Rules created in .windsurf/workflows/',
|
||||||
trae: 'Rules created in.trae/rules/',
|
trae: 'Rules created in.trae/rules/',
|
||||||
roo: 'Custom modes created in .roomodes',
|
roo: 'Custom modes created in .roomodes',
|
||||||
|
|||||||
Reference in New Issue
Block a user