BMAD-METHOD/tools/cli/lib/kiro-cli-generator.js

151 lines
5.2 KiB
JavaScript

const fs = require('node:fs');
const path = require('node:path');
const csv = require('csv-parse/sync');
const chalk = require('chalk');
class KiroCLIGenerator {
constructor() {}
/**
* Generate Kiro CLI agent files from BMAD agent manifest
* @param {string} projectRoot - Project root directory
* @param {string} outputDir - Output directory for Kiro CLI agents
* @param {boolean} force - Overwrite existing files
* @returns {Object} Generation results
*/
async generateAgents(projectRoot, outputDir = null, force = false) {
const manifestPath = path.join(projectRoot, '.bmad/_cfg/agent-manifest.csv');
const defaultOutputDir = path.join(projectRoot, '.kiro/agents');
const targetOutputDir = outputDir || defaultOutputDir;
// Validate manifest exists
if (!fs.existsSync(manifestPath)) {
throw new Error(`Agent manifest not found at: ${manifestPath}`);
}
// Create output directory
if (!fs.existsSync(targetOutputDir)) {
fs.mkdirSync(targetOutputDir, { recursive: true });
}
// Read and parse agent manifest
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
const agents = csv.parse(manifestContent, {
columns: true,
skip_empty_lines: true,
});
let generated = 0;
let skipped = 0;
for (const agent of agents) {
const agentJsonPath = path.join(targetOutputDir, `${agent.name}.json`);
const agentPromptPath = path.join(targetOutputDir, `${agent.name}-prompt.md`);
// Skip if files exist and not forcing
if (!force && (fs.existsSync(agentJsonPath) || fs.existsSync(agentPromptPath))) {
skipped++;
continue;
}
// Generate Kiro CLI agent JSON
const kiroAgent = {
name: agent.name,
description: `BMAD ${agent.title} (context optimized) - auto-generated from manifest`,
prompt: `file://./${agent.name}-prompt.md`,
tools: ['read', 'write', 'shell', '@docker_mcp_gateway'],
allowedTools: ['read', 'write', 'shell', '@docker_mcp_gateway'],
mcpServers: {},
resources: ['file://.bmad/bmm/config.yaml'],
model: 'claude-sonnet-4',
};
// Generate Kiro CLI agent prompt
const kiroPrompt = `# ${agent.name} (Context Optimized)
**IMPORTANT**: Only load specific workflow/task details when explicitly needed using fs_read.
Load workflows: \`file://.bmad/bmm/workflows/[name]/workflow.yaml\`
You are ${agent.displayName}, a ${agent.title} ${agent.icon}
## Core Identity
- **Role**: ${agent.role}
- **Identity**: ${agent.identity}
- **Communication Style**: ${agent.communicationStyle}
- **Principles**: ${agent.principles}
## Kiro CLI Optimization
- **Lazy Loading**: Load workflow files only when requested using read tool
- **Context Efficiency**: Minimal initial load, expand on demand
- **File Output**: Generate files as specified in workflows
- **Session Continuity**: Maintain conversation context within Kiro CLI
- **MCP Integration**: Inherits MCP servers from agent configuration
## Available Workflows
Load workflows from: \`.bmad/bmm/workflows/\`
## MCP Server Access
You have access to any MCP servers configured in this agent's configuration.
Use MCP capabilities when they provide better functionality than basic tools.
## Interaction Pattern
1. **Greet user** as ${agent.displayName}
2. **Offer capabilities** based on role and available workflows
3. **Load workflows** on demand using read tool when user requests specific functionality
4. **Execute conversationally** following loaded workflow instructions
5. **Generate outputs** to appropriate file paths
6. **Leverage available MCP servers** when they enhance functionality
## Configuration
- **Project Root**: Use current working directory
- **Config**: Load from .bmad/bmm/config.yaml
- **Output**: Use configured output folder from config
Remember: You are ${agent.displayName} - ${agent.identity}`;
// Write files
fs.writeFileSync(agentJsonPath, JSON.stringify(kiroAgent, null, 2));
fs.writeFileSync(agentPromptPath, kiroPrompt);
generated++;
}
return {
total: agents.length,
generated,
skipped,
outputDir: targetOutputDir,
};
}
/**
* Generate Kiro CLI agents with console output
* @param {string} projectRoot - Project root directory
* @param {Object} options - Generation options
*/
async generateWithOutput(projectRoot, options = {}) {
try {
console.log(chalk.cyan('🤖 Generating Kiro CLI agents...'));
const result = await this.generateAgents(projectRoot, options.outputDir, options.force);
if (result.generated > 0) {
console.log(chalk.green(`✅ Generated ${result.generated} Kiro CLI agents`));
if (result.skipped > 0) {
console.log(chalk.yellow(`⏭️ Skipped ${result.skipped} existing agents`));
}
console.log(chalk.dim(`📁 Output: ${result.outputDir}`));
} else {
console.log(chalk.yellow('⏭️ All Kiro CLI agents already exist (use --force to regenerate)'));
}
return result;
} catch (error) {
console.error(chalk.red('❌ Kiro CLI agent generation failed:'), error.message);
throw error;
}
}
name = 'Kiro CLI Agent Generator';
}
module.exports = { KiroCLIGenerator };