172 lines
5.3 KiB
JavaScript
172 lines
5.3 KiB
JavaScript
const path = require('node:path');
|
|
const { BaseIdeSetup } = require('./_base-ide');
|
|
const chalk = require('chalk');
|
|
|
|
/**
|
|
* KiloCode IDE setup handler
|
|
* Creates custom modes in .kilocodemodes file (similar to Roo)
|
|
*/
|
|
class KiloSetup extends BaseIdeSetup {
|
|
constructor() {
|
|
super('kilo', 'Kilo Code');
|
|
this.configFile = '.kilocodemodes';
|
|
}
|
|
|
|
/**
|
|
* Setup KiloCode IDE configuration
|
|
* @param {string} projectDir - Project directory
|
|
* @param {string} bmadDir - BMAD installation directory
|
|
* @param {Object} options - Setup options
|
|
*/
|
|
async setup(projectDir, bmadDir, options = {}) {
|
|
console.log(chalk.cyan(`Setting up ${this.name}...`));
|
|
|
|
// Check for existing .kilocodemodes file
|
|
const kiloModesPath = path.join(projectDir, this.configFile);
|
|
let existingModes = [];
|
|
let existingContent = '';
|
|
|
|
if (await this.pathExists(kiloModesPath)) {
|
|
existingContent = await this.readFile(kiloModesPath);
|
|
// Parse existing modes
|
|
const modeMatches = existingContent.matchAll(/- slug: ([\w-]+)/g);
|
|
for (const match of modeMatches) {
|
|
existingModes.push(match[1]);
|
|
}
|
|
console.log(chalk.yellow(`Found existing .kilocodemodes file with ${existingModes.length} modes`));
|
|
}
|
|
|
|
// Get agents
|
|
const agents = await this.getAgents(bmadDir);
|
|
|
|
// Create modes content
|
|
let newModesContent = '';
|
|
let addedCount = 0;
|
|
let skippedCount = 0;
|
|
|
|
for (const agent of agents) {
|
|
const slug = `bmad-${agent.module}-${agent.name}`;
|
|
|
|
// Skip if already exists
|
|
if (existingModes.includes(slug)) {
|
|
console.log(chalk.dim(` Skipping ${slug} - already exists`));
|
|
skippedCount++;
|
|
continue;
|
|
}
|
|
|
|
const content = await this.readFile(agent.path);
|
|
const modeEntry = this.createModeEntry(agent, content, projectDir);
|
|
|
|
newModesContent += modeEntry;
|
|
addedCount++;
|
|
}
|
|
|
|
// Build final content
|
|
let finalContent = '';
|
|
if (existingContent) {
|
|
finalContent = existingContent.trim() + '\n' + newModesContent;
|
|
} else {
|
|
finalContent = 'customModes:\n' + newModesContent;
|
|
}
|
|
|
|
// Write .kilocodemodes file
|
|
await this.writeFile(kiloModesPath, finalContent);
|
|
|
|
console.log(chalk.green(`✓ ${this.name} configured:`));
|
|
console.log(chalk.dim(` - ${addedCount} modes added`));
|
|
if (skippedCount > 0) {
|
|
console.log(chalk.dim(` - ${skippedCount} modes skipped (already exist)`));
|
|
}
|
|
console.log(chalk.dim(` - Configuration file: ${this.configFile}`));
|
|
console.log(chalk.dim('\n Modes will be available when you open this project in KiloCode'));
|
|
|
|
return {
|
|
success: true,
|
|
modes: addedCount,
|
|
skipped: skippedCount,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create a mode entry for an agent
|
|
*/
|
|
createModeEntry(agent, content, projectDir) {
|
|
// Extract metadata
|
|
const titleMatch = content.match(/title="([^"]+)"/);
|
|
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
|
|
|
|
const iconMatch = content.match(/icon="([^"]+)"/);
|
|
const icon = iconMatch ? iconMatch[1] : '🤖';
|
|
|
|
const whenToUseMatch = content.match(/whenToUse="([^"]+)"/);
|
|
const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`;
|
|
|
|
const roleDefinitionMatch = content.match(/roleDefinition="([^"]+)"/);
|
|
const roleDefinition = roleDefinitionMatch
|
|
? roleDefinitionMatch[1]
|
|
: `You are a ${title} specializing in ${title.toLowerCase()} tasks.`;
|
|
|
|
// Get relative path
|
|
const relativePath = path.relative(projectDir, agent.path).replaceAll('\\', '/');
|
|
|
|
// Build mode entry (KiloCode uses same schema as Roo)
|
|
const slug = `bmad-${agent.module}-${agent.name}`;
|
|
let modeEntry = ` - slug: ${slug}\n`;
|
|
modeEntry += ` name: '${icon} ${title}'\n`;
|
|
modeEntry += ` roleDefinition: ${roleDefinition}\n`;
|
|
modeEntry += ` whenToUse: ${whenToUse}\n`;
|
|
modeEntry += ` customInstructions: CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
|
|
modeEntry += ` groups:\n`;
|
|
modeEntry += ` - read\n`;
|
|
modeEntry += ` - edit\n`;
|
|
|
|
return modeEntry;
|
|
}
|
|
|
|
/**
|
|
* Format name as title
|
|
*/
|
|
formatTitle(name) {
|
|
return name
|
|
.split('-')
|
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Cleanup KiloCode configuration
|
|
*/
|
|
async cleanup(projectDir) {
|
|
const fs = require('fs-extra');
|
|
const kiloModesPath = path.join(projectDir, this.configFile);
|
|
|
|
if (await fs.pathExists(kiloModesPath)) {
|
|
const content = await fs.readFile(kiloModesPath, 'utf8');
|
|
|
|
// Remove BMAD modes only
|
|
const lines = content.split('\n');
|
|
const filteredLines = [];
|
|
let skipMode = false;
|
|
let removedCount = 0;
|
|
|
|
for (const line of lines) {
|
|
if (/^\s*- slug: bmad-/.test(line)) {
|
|
skipMode = true;
|
|
removedCount++;
|
|
} else if (skipMode && /^\s*- slug: /.test(line)) {
|
|
skipMode = false;
|
|
}
|
|
|
|
if (!skipMode) {
|
|
filteredLines.push(line);
|
|
}
|
|
}
|
|
|
|
await fs.writeFile(kiloModesPath, filteredLines.join('\n'));
|
|
console.log(chalk.dim(`Removed ${removedCount} BMAD modes from .kilocodemodes`));
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { KiloSetup };
|