From fc5ef57a5a971ffcbe8a8f044774124cc508cf14 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Sun, 8 Feb 2026 08:18:28 -0700 Subject: [PATCH] feat: add Kiro IDE support via config-driven installer (#1589) Replace broken kiro-cli.js custom installer with config-driven approach using platform-codes.yaml. Creates Kiro-specific templates with inclusion: manual frontmatter and #[[file:...]] reference syntax. --- docs/how-to/install-bmad.md | 4 +- docs/index.md | 1 + docs/reference/commands.md | 13 +- tools/cli/installers/lib/core/installer.js | 2 +- tools/cli/installers/lib/ide/kiro-cli.js | 326 ------------------ tools/cli/installers/lib/ide/manager.js | 6 +- .../installers/lib/ide/platform-codes.yaml | 12 +- .../lib/ide/templates/combined/kiro-agent.md | 16 + .../lib/ide/templates/combined/kiro-task.md | 9 + .../lib/ide/templates/combined/kiro-tool.md | 9 + .../templates/combined/kiro-workflow-yaml.md | 15 + .../ide/templates/combined/kiro-workflow.md | 7 + tools/platform-codes.yaml | 8 +- 13 files changed, 87 insertions(+), 341 deletions(-) delete mode 100644 tools/cli/installers/lib/ide/kiro-cli.js create mode 100644 tools/cli/installers/lib/ide/templates/combined/kiro-agent.md create mode 100644 tools/cli/installers/lib/ide/templates/combined/kiro-task.md create mode 100644 tools/cli/installers/lib/ide/templates/combined/kiro-tool.md create mode 100644 tools/cli/installers/lib/ide/templates/combined/kiro-workflow-yaml.md create mode 100644 tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md diff --git a/docs/how-to/install-bmad.md b/docs/how-to/install-bmad.md index 0b477905a..4127bd8b2 100644 --- a/docs/how-to/install-bmad.md +++ b/docs/how-to/install-bmad.md @@ -41,6 +41,7 @@ Pick which AI tools you use: - Claude Code - Cursor - Windsurf +- Kiro - Others Each tool has its own way of integrating commands. The installer creates tiny prompt files to activate workflows and agents — it just puts them where your tool expects to find them. @@ -63,7 +64,8 @@ your-project/ │ ├── core/ # Required core module │ └── ... ├── _bmad-output/ # Generated artifacts -└── .claude/ # Claude Code commands (if using Claude Code) +├── .claude/ # Claude Code commands (if using Claude Code) +└── .kiro/ # Kiro steering files (if using Kiro) ``` ## Verify Installation diff --git a/docs/index.md b/docs/index.md index 8b626a63d..ae3be92e5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,6 +36,7 @@ BMad works with any AI coding assistant that supports custom system prompts or p - **[Claude Code](https://code.claude.com)** — Anthropic's CLI tool (recommended) - **[Cursor](https://cursor.sh)** — AI-first code editor - **[Windsurf](https://codeium.com/windsurf)** — Codeium's AI IDE +- **[Kiro](https://kiro.dev)** — Amazon's AI-powered IDE - **[Roo Code](https://roocode.com)** — VS Code extension You should be comfortable with basic software development concepts like version control, project structure, and agile workflows. No prior experience with BMad-style agent systems is required—that's what these docs are for. diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 39b97f637..6a77b1a2c 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -15,11 +15,22 @@ That means the authoritative list lives **in your project**, not in a static doc ## Where Commands Are Generated -The installer writes command files into your project (example paths for Claude Code): +The installer writes command files into your project. The location and format depend on your AI tool: + +| AI Tool | Location | File Reference Syntax | +| --- | --- | --- | +| Claude Code | `.claude/commands/` | `@path` references | +| Kiro | `.kiro/steering/` | `#[[file:path]]` references with `inclusion: manual` frontmatter | +| Cursor | `.cursor/commands/` | `@path` references | +| Windsurf | `.windsurf/workflows/` | `@{project-root}/path` references | + +Example paths for Claude Code: - `.claude/commands/bmad//agents/` - `.claude/commands/bmad//workflows/` +All tools invoke the same underlying `_bmad/` workflows and agents — only the launcher format differs. + These folders are the **canonical, project-specific command list**. ## Common Commands diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 1e161bdc8..659d55193 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -175,7 +175,7 @@ class Installer { } // Check if this IDE handler has a collectConfiguration method - // (custom installers like Codex, Kilo, Kiro-cli may have this) + // (custom installers like Codex, Kilo may have this) if (typeof handler.collectConfiguration === 'function') { await prompts.log.info(`Configuring ${ide}...`); ideConfigurations[ide] = await handler.collectConfiguration({ diff --git a/tools/cli/installers/lib/ide/kiro-cli.js b/tools/cli/installers/lib/ide/kiro-cli.js deleted file mode 100644 index 150dca189..000000000 --- a/tools/cli/installers/lib/ide/kiro-cli.js +++ /dev/null @@ -1,326 +0,0 @@ -const path = require('node:path'); -const { BaseIdeSetup } = require('./_base-ide'); -const fs = require('fs-extra'); -const prompts = require('../../../lib/prompts'); -const yaml = require('yaml'); - -/** - * Kiro CLI setup handler for BMad Method - */ -class KiroCliSetup extends BaseIdeSetup { - constructor() { - super('kiro-cli', 'Kiro CLI', false); - this.configDir = '.kiro'; - this.agentsDir = 'agents'; - } - - /** - * Cleanup old BMAD installation before reinstalling - * @param {string} projectDir - Project directory - */ - async cleanup(projectDir, options = {}) { - const bmadAgentsDir = path.join(projectDir, this.configDir, this.agentsDir); - - if (await fs.pathExists(bmadAgentsDir)) { - // Remove existing BMad agents - const files = await fs.readdir(bmadAgentsDir); - for (const file of files) { - if (file.startsWith('bmad')) { - await fs.remove(path.join(bmadAgentsDir, file)); - } - } - if (!options.silent) await prompts.log.message(` Cleaned old BMAD agents from ${this.name}`); - } - } - - /** - * Setup Kiro CLI configuration with BMad agents - * @param {string} projectDir - Project directory - * @param {string} bmadDir - BMAD installation directory - * @param {Object} options - Setup options - */ - async setup(projectDir, bmadDir, options = {}) { - if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`); - - await this.cleanup(projectDir, options); - - const kiroDir = path.join(projectDir, this.configDir); - const agentsDir = path.join(kiroDir, this.agentsDir); - - await this.ensureDir(agentsDir); - - // Create BMad agents from source YAML files - await this.createBmadAgentsFromSource(agentsDir, projectDir); - - if (!options.silent) await prompts.log.success(`${this.name} configured with BMad agents`); - } - - /** - * Create BMad agent definitions from source YAML files - * @param {string} agentsDir - Agents directory - * @param {string} projectDir - Project directory - */ - async createBmadAgentsFromSource(agentsDir, projectDir) { - const sourceDir = path.join(__dirname, '../../../../../src/modules'); - - // Find all agent YAML files - const agentFiles = await this.findAgentFiles(sourceDir); - - for (const agentFile of agentFiles) { - try { - await this.processAgentFile(agentFile, agentsDir, projectDir); - } catch (error) { - await prompts.log.warn(`Failed to process ${agentFile}: ${error.message}`); - } - } - } - - /** - * Find all agent YAML files in modules and core - * @param {string} sourceDir - Source modules directory - * @returns {Array} Array of agent file paths - */ - async findAgentFiles(sourceDir) { - const agentFiles = []; - - // Check core agents - const coreAgentsDir = path.join(__dirname, '../../../../../src/core/agents'); - if (await fs.pathExists(coreAgentsDir)) { - const files = await fs.readdir(coreAgentsDir); - - for (const file of files) { - if (file.endsWith('.agent.yaml')) { - agentFiles.push(path.join(coreAgentsDir, file)); - } - } - } - - // Check module agents - if (!(await fs.pathExists(sourceDir))) { - return agentFiles; - } - - const modules = await fs.readdir(sourceDir); - - for (const module of modules) { - const moduleAgentsDir = path.join(sourceDir, module, 'agents'); - - if (await fs.pathExists(moduleAgentsDir)) { - const files = await fs.readdir(moduleAgentsDir); - - for (const file of files) { - if (file.endsWith('.agent.yaml')) { - agentFiles.push(path.join(moduleAgentsDir, file)); - } - } - } - } - - return agentFiles; - } - - /** - * Validate BMad Core compliance - * @param {Object} agentData - Agent YAML data - * @returns {boolean} True if compliant - */ - validateBmadCompliance(agentData) { - const requiredFields = ['agent.metadata.id', 'agent.persona.role', 'agent.persona.principles']; - - for (const field of requiredFields) { - const keys = field.split('.'); - let current = agentData; - - for (const key of keys) { - if (!current || !current[key]) { - return false; - } - current = current[key]; - } - } - - return true; - } - - /** - * Process individual agent YAML file - * @param {string} agentFile - Path to agent YAML file - * @param {string} agentsDir - Target agents directory - * @param {string} projectDir - Project directory - */ - async processAgentFile(agentFile, agentsDir, projectDir) { - const yamlContent = await fs.readFile(agentFile, 'utf8'); - const agentData = yaml.parse(yamlContent); - - if (!this.validateBmadCompliance(agentData)) { - return; - } - - // Extract module from file path - const normalizedPath = path.normalize(agentFile); - const pathParts = normalizedPath.split(path.sep); - const basename = path.basename(agentFile, '.agent.yaml'); - - // Find the module name from path - let moduleName = 'unknown'; - if (pathParts.includes('src')) { - const srcIndex = pathParts.indexOf('src'); - if (srcIndex + 3 < pathParts.length) { - const folderAfterSrc = pathParts[srcIndex + 1]; - if (folderAfterSrc === 'core') { - moduleName = 'core'; - } else if (folderAfterSrc === 'bmm') { - moduleName = 'bmm'; - } - } - } - - // Extract the agent name from the ID path in YAML if available - let agentBaseName = basename; - if (agentData.agent && agentData.agent.metadata && agentData.agent.metadata.id) { - const idPath = agentData.agent.metadata.id; - agentBaseName = path.basename(idPath, '.md'); - } - - const agentName = `bmad-${moduleName}-${agentBaseName}`; - const sanitizedAgentName = this.sanitizeAgentName(agentName); - - // Create JSON definition - await this.createAgentDefinitionFromYaml(agentsDir, sanitizedAgentName, agentData); - - // Create prompt file - await this.createAgentPromptFromYaml(agentsDir, sanitizedAgentName, agentData, projectDir); - } - - /** - * Sanitize agent name for file naming - * @param {string} name - Agent name - * @returns {string} Sanitized name - */ - sanitizeAgentName(name) { - return name - .toLowerCase() - .replaceAll(/\s+/g, '-') - .replaceAll(/[^a-z0-9-]/g, ''); - } - - /** - * Create agent JSON definition from YAML data - * @param {string} agentsDir - Agents directory - * @param {string} agentName - Agent name (role-based) - * @param {Object} agentData - Agent YAML data - */ - async createAgentDefinitionFromYaml(agentsDir, agentName, agentData) { - const personName = agentData.agent.metadata.name; - const role = agentData.agent.persona.role; - - const agentConfig = { - name: agentName, - description: `${personName} - ${role}`, - prompt: `file://./${agentName}-prompt.md`, - tools: ['*'], - mcpServers: {}, - useLegacyMcpJson: true, - resources: [], - }; - - const agentPath = path.join(agentsDir, `${agentName}.json`); - await fs.writeJson(agentPath, agentConfig, { spaces: 2 }); - } - - /** - * Create agent prompt from YAML data - * @param {string} agentsDir - Agents directory - * @param {string} agentName - Agent name (role-based) - * @param {Object} agentData - Agent YAML data - * @param {string} projectDir - Project directory - */ - async createAgentPromptFromYaml(agentsDir, agentName, agentData, projectDir) { - const promptPath = path.join(agentsDir, `${agentName}-prompt.md`); - - // Generate prompt from YAML data - const prompt = this.generatePromptFromYaml(agentData); - await fs.writeFile(promptPath, prompt); - } - - /** - * Generate prompt content from YAML data - * @param {Object} agentData - Agent YAML data - * @returns {string} Generated prompt - */ - generatePromptFromYaml(agentData) { - const agent = agentData.agent; - const name = agent.metadata.name; - const icon = agent.metadata.icon || '🤖'; - const role = agent.persona.role; - const identity = agent.persona.identity; - const style = agent.persona.communication_style; - const principles = agent.persona.principles; - - let prompt = `# ${name} ${icon}\n\n`; - prompt += `## Role\n${role}\n\n`; - - if (identity) { - prompt += `## Identity\n${identity}\n\n`; - } - - if (style) { - prompt += `## Communication Style\n${style}\n\n`; - } - - if (principles) { - prompt += `## Principles\n`; - if (typeof principles === 'string') { - // Handle multi-line string principles - prompt += principles + '\n\n'; - } else if (Array.isArray(principles)) { - // Handle array principles - for (const principle of principles) { - prompt += `- ${principle}\n`; - } - prompt += '\n'; - } - } - - // Add menu items if available - if (agent.menu && agent.menu.length > 0) { - prompt += `## Available Workflows\n`; - for (let i = 0; i < agent.menu.length; i++) { - const item = agent.menu[i]; - prompt += `${i + 1}. **${item.trigger}**: ${item.description}\n`; - } - prompt += '\n'; - } - - prompt += `## Instructions\nYou are ${name}, part of the BMad Method. Follow your role and principles while assisting users with their development needs.\n`; - - return prompt; - } - - /** - * Check if Kiro CLI is available - * @returns {Promise} True if available - */ - async isAvailable() { - try { - const { execSync } = require('node:child_process'); - execSync('kiro-cli --version', { stdio: 'ignore' }); - return true; - } catch { - return false; - } - } - - /** - * Get installation instructions - * @returns {string} Installation instructions - */ - getInstallInstructions() { - return `Install Kiro CLI: - curl -fsSL https://github.com/aws/kiro-cli/releases/latest/download/install.sh | bash - - Or visit: https://github.com/aws/kiro-cli`; - } -} - -module.exports = { KiroCliSetup }; diff --git a/tools/cli/installers/lib/ide/manager.js b/tools/cli/installers/lib/ide/manager.js index ad3352502..c68527f6a 100644 --- a/tools/cli/installers/lib/ide/manager.js +++ b/tools/cli/installers/lib/ide/manager.js @@ -8,7 +8,7 @@ const prompts = require('../../../lib/prompts'); * Dynamically discovers and loads IDE handlers * * Loading strategy: - * 1. Custom installer files (codex.js, kilo.js, kiro-cli.js) - for platforms with unique installation logic + * 1. Custom installer files (codex.js, kilo.js) - for platforms with unique installation logic * 2. Config-driven handlers (from platform-codes.yaml) - for standard IDE installation patterns */ class IdeManager { @@ -44,7 +44,7 @@ class IdeManager { /** * Dynamically load all IDE handlers - * 1. Load custom installer files first (codex.js, kilo.js, kiro-cli.js) + * 1. Load custom installer files first (codex.js, kilo.js) * 2. Load config-driven handlers from platform-codes.yaml */ async loadHandlers() { @@ -61,7 +61,7 @@ class IdeManager { */ async loadCustomInstallerFiles() { const ideDir = __dirname; - const customFiles = ['codex.js', 'kilo.js', 'kiro-cli.js']; + const customFiles = ['codex.js', 'kilo.js']; for (const file of customFiles) { const filePath = path.join(ideDir, file); diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index b329d283c..f65143616 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -111,12 +111,14 @@ platforms: description: "AI coding platform" # No installer config - uses custom kilo.js (creates .kilocodemodes file) - kiro-cli: - name: "Kiro CLI" + kiro: + name: "Kiro" preferred: false - category: cli - description: "Kiro command-line interface" - # No installer config - uses custom kiro-cli.js (YAML→JSON conversion) + category: ide + description: "Amazon's AI-powered IDE" + installer: + target_dir: .kiro/steering + template_type: kiro opencode: name: "OpenCode" diff --git a/tools/cli/installers/lib/ide/templates/combined/kiro-agent.md b/tools/cli/installers/lib/ide/templates/combined/kiro-agent.md new file mode 100644 index 000000000..e2c2a83fa --- /dev/null +++ b/tools/cli/installers/lib/ide/templates/combined/kiro-agent.md @@ -0,0 +1,16 @@ +--- +inclusion: manual +--- + +# {{name}} + +You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. + + +1. LOAD the FULL agent file from #[[file:{{bmadFolderName}}/{{path}}]] +2. READ its entire contents - this contains the complete agent persona, menu, and instructions +3. FOLLOW every step in the section precisely +4. DISPLAY the welcome/greeting as instructed +5. PRESENT the numbered menu +6. WAIT for user input before proceeding + diff --git a/tools/cli/installers/lib/ide/templates/combined/kiro-task.md b/tools/cli/installers/lib/ide/templates/combined/kiro-task.md new file mode 100644 index 000000000..8952e5ee2 --- /dev/null +++ b/tools/cli/installers/lib/ide/templates/combined/kiro-task.md @@ -0,0 +1,9 @@ +--- +inclusion: manual +--- + +# {{name}} + +Read the entire task file at: #[[file:{{bmadFolderName}}/{{path}}]] + +Follow all instructions in the task file exactly as written. diff --git a/tools/cli/installers/lib/ide/templates/combined/kiro-tool.md b/tools/cli/installers/lib/ide/templates/combined/kiro-tool.md new file mode 100644 index 000000000..cd903217a --- /dev/null +++ b/tools/cli/installers/lib/ide/templates/combined/kiro-tool.md @@ -0,0 +1,9 @@ +--- +inclusion: manual +--- + +# {{name}} + +Read the entire tool file at: #[[file:{{bmadFolderName}}/{{path}}]] + +Follow all instructions in the tool file exactly as written. diff --git a/tools/cli/installers/lib/ide/templates/combined/kiro-workflow-yaml.md b/tools/cli/installers/lib/ide/templates/combined/kiro-workflow-yaml.md new file mode 100644 index 000000000..4ee4e0824 --- /dev/null +++ b/tools/cli/installers/lib/ide/templates/combined/kiro-workflow-yaml.md @@ -0,0 +1,15 @@ +--- +inclusion: manual +--- + +# {{name}} + +IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded: + + +1. Always LOAD the FULL #[[file:{{bmadFolderName}}/core/tasks/workflow.xml]] +2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config #[[file:{{bmadFolderName}}/{{path}}]] +3. Pass the yaml path {{bmadFolderName}}/{{path}} as 'workflow-config' parameter to the workflow.xml instructions +4. Follow workflow.xml instructions EXACTLY as written to process and follow the specific workflow config and its instructions +5. Save outputs after EACH section when generating any documents from templates + diff --git a/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md b/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md new file mode 100644 index 000000000..e1847f414 --- /dev/null +++ b/tools/cli/installers/lib/ide/templates/combined/kiro-workflow.md @@ -0,0 +1,7 @@ +--- +inclusion: manual +--- + +# {{name}} + +IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL #[[file:{{bmadFolderName}}/{{path}}]], READ its entire contents and follow its directions exactly! diff --git a/tools/platform-codes.yaml b/tools/platform-codes.yaml index 04c4a45f2..d75e31fee 100644 --- a/tools/platform-codes.yaml +++ b/tools/platform-codes.yaml @@ -67,11 +67,11 @@ platforms: category: ide description: "Atlassian's Rovo development environment" - kiro-cli: - name: "Kiro CLI" + kiro: + name: "Kiro" preferred: false - category: cli - description: "Kiro command-line interface" + category: ide + description: "Amazon's AI-powered IDE" github-copilot: name: "GitHub Copilot"