UNPKG

claude-flow

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

532 lines (446 loc) 18 kB
import { getErrorMessage } from '../../utils/error-handler.js'; import { success, error, warning, info } from "../cli-core.js"; import type { CommandContext } from "../cli-core.js"; import chalk from "chalk"; const { blue, yellow, green, magenta, cyan } = chalk; interface SparcMode { slug: string; name: string; roleDefinition: string; customInstructions: string; groups: string[]; source: string; } interface SparcConfig { customModes: SparcMode[]; } let sparcConfig: SparcConfig | null = null; async function loadSparcConfig(): Promise<SparcConfig> { if (sparcConfig) { return sparcConfig; } try { const configPath = ".roomodes"; const { readFile } = await import("fs/promises"); const content = await readFile(configPath, "utf-8"); sparcConfig = JSON.parse(content); return sparcConfig!; } catch (error) { throw new Error(`Failed to load SPARC configuration: ${(error instanceof Error ? error.message : String(error))}`); } } export async function sparcAction(ctx: CommandContext): Promise<void> { const subcommand = ctx.args[0]; switch (subcommand) { case "modes": await listSparcModes(ctx); break; case "info": await showModeInfo(ctx); break; case "run": await runSparcMode(ctx); break; case "tdd": await runTddFlow(ctx); break; case "workflow": await runSparcWorkflow(ctx); break; default: await showSparcHelp(); break; } } async function listSparcModes(ctx: CommandContext): Promise<void> { try { const config = await loadSparcConfig(); const verbose = ctx.flags.verbose as boolean; success("Available SPARC Modes:"); console.log(); for (const mode of config.customModes) { console.log(`${cyan("•")} ${green(mode.name)} ${blue(`(${mode.slug})`)}`); if (verbose) { console.log(` ${mode.roleDefinition}`); console.log(` Tools: ${mode.groups.join(", ")}`); console.log(); } } if (!verbose) { console.log(); info("Use --verbose for detailed descriptions"); } } catch (err) { error(`Failed to list SPARC modes: ${(err as Error).message}`); } } async function showModeInfo(ctx: CommandContext): Promise<void> { const modeSlug = ctx.args[1]; if (!modeSlug) { error("Usage: sparc info <mode-slug>"); return; } try { const config = await loadSparcConfig(); const mode = config.customModes.find(m => m.slug === modeSlug); if (!mode) { error(`Mode not found: ${modeSlug}`); console.log("Available modes:"); for (const m of config.customModes) { console.log(` ${m.slug} - ${m.name}`); } return; } success(`SPARC Mode: ${mode.name}`); console.log(); console.log(blue("Role Definition:")); console.log(mode.roleDefinition); console.log(); console.log(blue("Custom Instructions:")); console.log(mode.customInstructions); console.log(); console.log(blue("Tool Groups:")); console.log(mode.groups.join(", ")); console.log(); console.log(blue("Source:")); console.log(mode.source); } catch (err) { error(`Failed to show mode info: ${(err as Error).message}`); } } async function runSparcMode(ctx: CommandContext): Promise<void> { const modeSlug = ctx.args[1]; const taskDescription = ctx.args.slice(2).join(" "); if (!modeSlug || !taskDescription) { error("Usage: sparc run <mode-slug> <task-description>"); return; } try { const config = await loadSparcConfig(); const mode = config.customModes.find(m => m.slug === modeSlug); if (!mode) { error(`Mode not found: ${modeSlug}`); return; } // Build the enhanced task prompt using SPARC methodology const enhancedTask = buildSparcPrompt(mode, taskDescription, ctx.flags); const instanceId = `sparc-${modeSlug}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; // Build tools based on mode groups const tools = buildToolsFromGroups(mode.groups); if (ctx.flags.dryRun || ctx.flags["dry-run"]) { warning("DRY RUN - SPARC Mode Configuration:"); console.log(`Mode: ${mode.name} (${mode.slug})`); console.log(`Instance ID: ${instanceId}`); console.log(`Tools: ${tools}`); console.log(`Task: ${taskDescription}`); console.log(); console.log("Enhanced prompt preview:"); console.log(enhancedTask.substring(0, 300) + "..."); return; } success(`Starting SPARC mode: ${mode.name}`); console.log(`📝 Instance ID: ${instanceId}`); console.log(`🎯 Mode: ${mode.slug}`); console.log(`🔧 Tools: ${tools}`); console.log(`📋 Task: ${taskDescription}`); console.log(); // Execute Claude with SPARC configuration await executeClaudeWithSparc(enhancedTask, tools, instanceId, ctx.flags); } catch (err) { error(`Failed to run SPARC mode: ${(err as Error).message}`); } } async function runTddFlow(ctx: CommandContext): Promise<void> { const taskDescription = ctx.args.slice(1).join(" "); if (!taskDescription) { error("Usage: sparc tdd <task-description>"); return; } try { const config = await loadSparcConfig(); // Build TDD workflow using SPARC methodology const workflow = [ { mode: "spec-pseudocode", phase: "Specification", description: `Create detailed spec and pseudocode for: ${taskDescription}` }, { mode: "tdd", phase: "Red", description: `Write failing tests for: ${taskDescription}` }, { mode: "code", phase: "Green", description: `Implement minimal code to pass tests for: ${taskDescription}` }, { mode: "refinement-optimization-mode", phase: "Refactor", description: `Refactor and optimize implementation for: ${taskDescription}` }, { mode: "integration", phase: "Integration", description: `Integrate and verify complete solution for: ${taskDescription}` } ]; if (ctx.flags.dryRun || ctx.flags["dry-run"]) { warning("DRY RUN - TDD Workflow:"); for (const step of workflow) { console.log(`${cyan(step.phase)}: ${step.mode} - ${step.description}`); } return; } success("Starting SPARC TDD Workflow"); console.log("Following Test-Driven Development with SPARC methodology"); console.log(); for (let i = 0; i < workflow.length; i++) { const step = workflow[i]; const mode = config.customModes.find(m => m.slug === step.mode); if (!mode) { warning(`Mode not found: ${step.mode}, skipping step`); continue; } info(`Phase ${i + 1}/5: ${step.phase} (${mode.name})`); console.log(`📋 ${step.description}`); console.log(); const enhancedTask = buildSparcPrompt(mode, step.description, { ...ctx.flags, tddPhase: step.phase, workflowStep: i + 1, totalSteps: workflow.length }); const tools = buildToolsFromGroups(mode.groups); const instanceId = `sparc-tdd-${step.phase.toLowerCase()}-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`; await executeClaudeWithSparc(enhancedTask, tools, instanceId, ctx.flags); // Store phase completion in memory for next step if (ctx.flags.sequential !== false) { console.log("Phase completed. Press Enter to continue to next phase, or Ctrl+C to stop..."); await new Promise<void>(async (resolve) => { const readline = await import("readline"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question("", () => { rl.close(); resolve(); }); }); } } success("SPARC TDD Workflow completed!"); } catch (err) { error(`Failed to run TDD flow: ${(err as Error).message}`); } } async function runSparcWorkflow(ctx: CommandContext): Promise<void> { const workflowFile = ctx.args[1]; if (!workflowFile) { error("Usage: sparc workflow <workflow-file.json>"); return; } try { const { readFile } = await import("fs/promises"); const workflowContent = await readFile(workflowFile, "utf-8"); const workflow = JSON.parse(workflowContent); if (!workflow.steps || !Array.isArray(workflow.steps)) { error("Invalid workflow file: missing 'steps' array"); return; } const config = await loadSparcConfig(); success(`Loading SPARC workflow: ${workflow.name || "Unnamed"}`); console.log(`📋 Steps: ${workflow.steps.length}`); console.log(`📝 Description: ${workflow.description || "No description"}`); console.log(); if (ctx.flags.dryRun || ctx.flags["dry-run"]) { warning("DRY RUN - Workflow Steps:"); for (let i = 0; i < workflow.steps.length; i++) { const step = workflow.steps[i]; console.log(`${i + 1}. ${cyan(step.mode)} - ${step.description || step.task}`); } return; } for (let i = 0; i < workflow.steps.length; i++) { const step = workflow.steps[i]; const mode = config.customModes.find(m => m.slug === step.mode); if (!mode) { warning(`Mode not found: ${step.mode}, skipping step ${i + 1}`); continue; } info(`Step ${i + 1}/${workflow.steps.length}: ${mode.name}`); console.log(`📋 ${step.description || step.task}`); console.log(); const enhancedTask = buildSparcPrompt(mode, step.description || step.task, { ...ctx.flags, workflowStep: i + 1, totalSteps: workflow.steps.length, workflowName: workflow.name }); const tools = buildToolsFromGroups(mode.groups); const instanceId = `sparc-workflow-${i + 1}-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`; await executeClaudeWithSparc(enhancedTask, tools, instanceId, ctx.flags); if (workflow.sequential !== false && i < workflow.steps.length - 1) { console.log("Step completed. Press Enter to continue, or Ctrl+C to stop..."); await new Promise<void>((resolve) => { const readline = require("readline"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question("", () => { rl.close(); resolve(); }); }); } } success("SPARC workflow completed!"); } catch (err) { error(`Failed to run workflow: ${(err as Error).message}`); } } function buildSparcPrompt(mode: SparcMode, taskDescription: string, flags: any): string { const memoryNamespace = flags.namespace || mode.slug || "default"; return `# SPARC Development Mode: ${mode.name} ## Your Role ${mode.roleDefinition} ## Your Task ${taskDescription} ## Mode-Specific Instructions ${mode.customInstructions} ## SPARC Development Environment You are working within the SPARC (Specification, Pseudocode, Architecture, Refinement, Completion) methodology using claude-flow orchestration features. ### Available Development Tools - **Memory Persistence**: Use \`npx claude-flow memory store <key> "<value>"\` to save progress and findings - **Memory Retrieval**: Use \`npx claude-flow memory query <search>\` to access previous work - **Namespace**: Your work is stored in the "${memoryNamespace}" namespace ### SPARC Methodology Integration ${flags.tddPhase ? ` **Current TDD Phase**: ${flags.tddPhase} - Follow the Red-Green-Refactor cycle - Store test results and refactoring notes in memory ` : ''} ${flags.workflowStep ? ` **Workflow Progress**: Step ${flags.workflowStep} of ${flags.totalSteps} - Review previous steps: \`npx claude-flow memory query previous_steps\` - Store this step's output: \`npx claude-flow memory store step_${flags.workflowStep}_output "<results>"\` ` : ''} ### Best Practices 1. **Modular Development**: Keep all files under 500 lines 2. **Environment Safety**: Never hardcode secrets or environment values 3. **Memory Usage**: Store key findings and decisions in memory for future reference 4. **Tool Integration**: Use \`new_task\` for subtasks and \`attempt_completion\` when finished ### Memory Commands Examples \`\`\`bash # Store your progress npx claude-flow memory store ${memoryNamespace}_progress "Current status and findings" # Check for previous work npx claude-flow memory query ${memoryNamespace} # Store phase-specific results npx claude-flow memory store ${memoryNamespace}_${flags.tddPhase || 'results'} "Phase output and decisions" \`\`\` ### Integration with Other SPARC Modes When working with other SPARC modes, use memory to: - Share findings with spec-pseudocode mode - Pass requirements to architect mode - Coordinate with code and tdd modes - Communicate results to integration mode Now proceed with your task following the SPARC methodology and your specific role instructions.`; } function buildToolsFromGroups(groups: string[]): string { const toolMappings: Record<string, string[]> = { read: ["View", "LS", "GlobTool", "GrepTool"], edit: ["Edit", "Replace", "MultiEdit", "Write"], browser: ["WebFetch"], mcp: ["mcp_tools"], command: ["Bash", "Terminal"] }; const tools = new Set<string>(); // Always include basic tools tools.add("View"); tools.add("Edit"); tools.add("Bash"); for (const group of groups) { if (Array.isArray(group)) { // Handle nested group definitions const groupName = group[0]; if (toolMappings[groupName]) { toolMappings[groupName].forEach(tool => tools.add(tool)); } } else if (toolMappings[group]) { toolMappings[group].forEach(tool => tools.add(tool)); } } return Array.from(tools).join(","); } async function executeClaudeWithSparc( enhancedTask: string, tools: string, instanceId: string, flags: any ): Promise<void> { const claudeArgs = [enhancedTask]; claudeArgs.push("--allowedTools", tools); if (flags.noPermissions || flags["no-permissions"]) { claudeArgs.push("--dangerously-skip-permissions"); } if (flags.config) { claudeArgs.push("--mcp-config", flags.config); } if (flags.verbose) { claudeArgs.push("--verbose"); } try { const { spawn } = await import("child_process"); const child = spawn("claude", claudeArgs, { env: { ...process.env, CLAUDE_INSTANCE_ID: instanceId, CLAUDE_SPARC_MODE: "true", CLAUDE_FLOW_MEMORY_ENABLED: "true", CLAUDE_FLOW_MEMORY_NAMESPACE: flags.namespace || "sparc", }, stdio: "inherit", }); const status = await new Promise<{ success: boolean; code: number | null }>((resolve) => { child.on("close", (code) => { resolve({ success: code === 0, code }); }); }); if ((status as any).success) { success(`SPARC instance ${instanceId} completed successfully`); } else { error(`SPARC instance ${instanceId} exited with code ${(status as any).code}`); } } catch (err) { error(`Failed to execute Claude: ${(err as Error).message}`); } } async function showSparcHelp(): Promise<void> { console.log(`${cyan("SPARC")} - ${green("Specification, Pseudocode, Architecture, Refinement, Completion")}`); console.log(); console.log("SPARC development methodology with TDD and multi-agent coordination."); console.log(); console.log(blue("Commands:")); console.log(" modes List all available SPARC modes"); console.log(" info <mode> Show detailed information about a mode"); console.log(" run <mode> <task> Execute a task using a specific SPARC mode"); console.log(" tdd <task> Run full TDD workflow using SPARC methodology"); console.log(" workflow <file> Execute a custom SPARC workflow from JSON file"); console.log(); console.log(blue("Common Modes:")); console.log(" spec-pseudocode Create specifications and pseudocode"); console.log(" architect Design system architecture"); console.log(" code Implement code solutions"); console.log(" tdd Test-driven development"); console.log(" debug Debug and troubleshoot issues"); console.log(" security-review Security analysis and review"); console.log(" docs-writer Documentation creation"); console.log(" integration System integration and testing"); console.log(); console.log(blue("Options:")); console.log(" --namespace <ns> Memory namespace for this session"); console.log(" --no-permissions Skip permission prompts"); console.log(" --config <file> MCP configuration file"); console.log(" --verbose Enable verbose output"); console.log(" --dry-run Preview what would be executed"); console.log(" --sequential Wait between workflow steps (default: true)"); console.log(); console.log(blue("Examples:")); console.log(` ${yellow("claude-flow sparc modes")} # List all modes`); console.log(` ${yellow("claude-flow sparc run code")} "implement user auth" # Run specific mode`); console.log(` ${yellow("claude-flow sparc tdd")} "payment processing system" # Full TDD workflow`); console.log(` ${yellow("claude-flow sparc workflow")} project-workflow.json # Custom workflow`); console.log(); console.log(blue("TDD Workflow:")); console.log(" 1. Specification - Define requirements and create pseudocode"); console.log(" 2. Red Phase - Write failing tests"); console.log(" 3. Green Phase - Implement minimal code to pass tests"); console.log(" 4. Refactor Phase - Optimize and clean up code"); console.log(" 5. Integration - Verify complete solution"); console.log(); console.log("For more information: https://github.com/ruvnet/claude-code-flow/docs/sparc.md"); }