@cloudkinetix/bmad-enhanced
Version:
Cloud-Kinetix enhanced fork of BMAD-METHOD - Breakthrough Method of Agile AI-driven Development with robust versioning and unified validation.
244 lines (207 loc) • 7.46 kB
JavaScript
/**
* Cloud Kinetix Enhancer - Thin Layer on top of BMAD
*
* Minimal wrapper that adds CK-specific features without duplicating upstream functionality.
* Uses a hook-based architecture to enhance BMAD commands.
*/
const path = require('path');
const fs = require('fs-extra');
const { spawn } = require('child_process');
class CKEnhancer {
constructor() {
this.hooks = {
pre: {},
post: {}
};
// Register default hooks
this.registerDefaultHooks();
}
/**
* Register default CK enhancement hooks
*/
registerDefaultHooks() {
// Pre-install: Create backup
this.addHook('pre', 'install', async (options) => {
if (options.backup !== false && !options.ckFeatures === false) {
const BMADBackupManager = require('./bmad-backup-manager');
const backupManager = new BMADBackupManager(options.directory || process.cwd());
const result = await backupManager.createBackups();
if (result.success) {
console.log(`✅ Backup created: ${result.message}`);
} else {
console.warn(`⚠️ Backup failed: ${result.message}`);
}
}
});
// Post-install: Add CK features
this.addHook('post', 'install', async (options, installedIDEs) => {
if (options.ckFeatures !== false) {
await this.installCKFeatures(options, installedIDEs);
}
});
// Pre-update: Create backup
this.addHook('pre', 'update', async (options) => {
if (options.backup !== false) {
const BMADBackupManager = require('./bmad-backup-manager');
const backupManager = new BMADBackupManager(options.directory || process.cwd());
const result = await backupManager.createBackups();
if (result.success) {
console.log(`✅ Pre-update backup created: ${result.message}`);
}
}
});
}
/**
* Add a hook for command enhancement
*/
addHook(type, command, handler) {
if (!this.hooks[type][command]) {
this.hooks[type][command] = [];
}
this.hooks[type][command].push(handler);
}
/**
* Run pre-hooks for a command
*/
async runPreHooks(command, options) {
const hooks = this.hooks.pre[command] || [];
for (const hook of hooks) {
await hook(options);
}
}
/**
* Run post-hooks for a command
*/
async runPostHooks(command, options, result) {
const hooks = this.hooks.post[command] || [];
for (const hook of hooks) {
await hook(options, result);
}
}
/**
* Execute upstream BMAD command
*/
async runUpstreamCommand(command, options) {
const args = [command];
// Build arguments based on command
if (command === 'install') {
if (options.full) args.push('--full');
if (options.expansionOnly) args.push('--expansion-only');
if (options.directory) args.push('--directory', path.resolve(options.directory));
// Filter out CK-specific expansion packs for upstream
if (options.expansionPacks && options.expansionPacks.length > 0) {
const upstreamPacks = options.expansionPacks.filter(pack => !pack.startsWith('ck-'));
if (upstreamPacks.length > 0) {
args.push('--expansion-packs', ...upstreamPacks);
}
}
// Use all IDEs - upstream now supports all IDEs including Trae
if (options.ides && options.ides.length > 0) {
const upstreamIDEs = options.ides;
if (upstreamIDEs.length > 0) {
args.push('--ide', ...upstreamIDEs);
}
}
} else if (command === 'update') {
if (options.force) args.push('--force');
if (options.dryRun) args.push('--dry-run');
}
// Execute upstream command
return new Promise((resolve, reject) => {
console.log(`🚀 Running upstream BMAD ${command}...`);
const child = spawn('npx', ['--yes', 'bmad-method@latest', ...args], {
cwd: options.directory || process.cwd(),
stdio: 'inherit'
});
child.on('close', (code) => {
if (code === 0) {
resolve({ success: true, ides: options.ides });
} else {
reject(new Error(`BMAD ${command} failed with exit code ${code}`));
}
});
child.on('error', reject);
});
}
/**
* Install CK-specific features
*/
async installCKFeatures(options, installedIDEs) {
const ckPacks = (options.expansionPacks || []).filter(pack => pack.startsWith('ck-'));
if (ckPacks.length === 0 && !options.full) {
return; // No CK features to install
}
console.log('⭐ Installing Cloud Kinetix enterprise features...');
// Get expansion packs directory
const targetDir = path.resolve(options.directory || process.cwd());
const expansionPacksSource = path.join(__dirname, '../../expansion-packs');
// Install each CK expansion pack
for (const packName of ckPacks) {
const sourceDir = path.join(expansionPacksSource, packName);
const targetPath = path.join(targetDir, `.bmad-${packName}`);
if (await fs.pathExists(sourceDir)) {
console.log(`📦 Installing ${packName}...`);
await fs.copy(sourceDir, targetPath, { overwrite: true });
} else {
console.warn(`⚠️ CK expansion pack not found: ${packName}`);
}
}
// IMPORTANT: We do NOT setup IDE configurations for CK agents here
// because the upstream BMAD installer already handles CK expansion packs
// when we pass them via --expansion-packs. The upstream installer creates
// the proper organized structure:
// - .claude/commands/ck/jira/agents/
// - .claude/commands/ck/ai-agent/agents/
// etc.
//
// Setting up IDE configurations again here would:
// 1. Create duplicate files in flat structure (.claude/commands/jira.md)
// 2. Conflict with the organized structure created by upstream
// 3. Cause confusion with multiple command files for the same agent
console.log('✅ Cloud Kinetix features installed successfully!');
}
/**
* Main enhancement method - wraps upstream commands
*/
async enhance(command, options = {}) {
try {
// Run pre-hooks
await this.runPreHooks(command, options);
// Execute upstream command
const result = await this.runUpstreamCommand(command, options);
// Run post-hooks
await this.runPostHooks(command, options, result);
return result;
} catch (error) {
console.error(`❌ Enhancement failed: ${error.message}`);
throw error;
}
}
/**
* Check if a command should be enhanced or passed through directly
*/
shouldEnhance(command) {
// Only enhance install and update commands
return ['install', 'update'].includes(command);
}
/**
* Direct pass-through to upstream for non-enhanced commands
*/
async passThrough(command, args = []) {
return new Promise((resolve, reject) => {
const child = spawn('npx', ['--yes', 'bmad-method@latest', command, ...args], {
stdio: 'inherit'
});
child.on('close', (code) => {
if (code === 0) {
resolve({ success: true });
} else {
reject(new Error(`BMAD ${command} failed with exit code ${code}`));
}
});
child.on('error', reject);
});
}
}
module.exports = CKEnhancer;