UNPKG

jay-code

Version:

Streamlined AI CLI orchestration engine with mathematical rigor and enterprise-grade reliability

841 lines (752 loc) â€ĸ 23.6 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, CallToolResult, TextContent, ImageContent, EmbeddedResource, } from '@modelcontextprotocol/sdk/types.js'; import * as fs from 'fs/promises'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { SparcMode, loadSparcModes } from './sparc-modes.js'; // Simple ID generation function generateId(): string { return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); interface SparcContext { memoryKey?: string; parallel?: boolean; timeout?: number; workingDirectory?: string; } interface SwarmAgent { id: string; mode: string; task: string; status: 'pending' | 'active' | 'completed' | 'failed'; result?: any; } interface SwarmExecution { id: string; objective: string; strategy: string; mode: string; agents: SwarmAgent[]; startTime: Date; endTime?: Date; status: 'active' | 'completed' | 'failed'; } export class ClaudeCodeMCPWrapper { private server: Server; private sparcModes: Map<string, SparcMode> = new Map(); private swarmExecutions: Map<string, SwarmExecution> = new Map(); private claudeCodeMCP: any; // Reference to Claude Code MCP client constructor() { this.server = new Server( { name: 'jay-code-wrapper', version: '1.0.0', }, { capabilities: { tools: {}, }, }, ); this.setupHandlers(); this.loadSparcModes(); } private async loadSparcModes() { try { const modes = await loadSparcModes(); modes.forEach((mode) => { this.sparcModes.set(mode.name, mode); }); } catch (error) { console.error('Failed to load SPARC modes:', error); } } private setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: await this.getTools(), })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => this.handleToolCall(request.params.name, request.params.arguments || {}), ); } private async getTools(): Promise<Tool[]> { const tools: Tool[] = []; // Add SPARC mode tools for (const [name, mode] of this.sparcModes) { tools.push({ name: `sparc_${name}`, description: `Execute SPARC ${name} mode: ${mode.description}`, inputSchema: { type: 'object', properties: { task: { type: 'string', description: 'The task description for the SPARC mode to execute', }, context: { type: 'object', description: 'Optional context or parameters for the task', properties: { memoryKey: { type: 'string', description: 'Memory key to store results', }, parallel: { type: 'boolean', description: 'Enable parallel execution', }, }, }, }, required: ['task'], }, }); } // Add meta tools tools.push( { name: 'sparc_list', description: 'List all available SPARC modes', inputSchema: { type: 'object', properties: { verbose: { type: 'boolean', description: 'Include detailed information', }, }, }, }, { name: 'sparc_swarm', description: 'Coordinate multiple SPARC agents in a swarm', inputSchema: { type: 'object', properties: { objective: { type: 'string', description: 'The swarm objective', }, strategy: { type: 'string', enum: [ 'research', 'development', 'analysis', 'testing', 'optimization', 'maintenance', ], description: 'Swarm execution strategy', }, mode: { type: 'string', enum: ['centralized', 'distributed', 'hierarchical', 'mesh', 'hybrid'], description: 'Coordination mode', }, maxAgents: { type: 'number', description: 'Maximum number of agents', default: 5, }, }, required: ['objective', 'strategy'], }, }, { name: 'sparc_swarm_status', description: 'Check status of running swarms and list created files', inputSchema: { type: 'object', properties: { swarmId: { type: 'string', description: 'Optional swarm ID to check specific swarm', }, }, }, }, ); return tools; } private async handleToolCall(toolName: string, args: any): Promise<CallToolResult> { try { if (toolName.startsWith('sparc_')) { return await this.handleSparcTool(toolName, args); } // Pass through to Claude Code MCP return this.forwardToClaudeCode(toolName, args); } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } private async handleSparcTool(toolName: string, args: any): Promise<CallToolResult> { const mode = toolName.replace('sparc_', ''); // Handle special tools if (mode === 'list') { return this.listModes(args.verbose); } if (mode === 'swarm') { return this.handleSwarm(args); } if (mode === 'swarm_status') { return this.getSwarmStatus(args.swarmId); } // Standard SPARC mode execution const sparcMode = this.sparcModes.get(mode); if (!sparcMode) { throw new Error(`Unknown SPARC mode: ${mode}`); } // Execute the SPARC mode directly try { // Import the execution function dynamically to avoid circular dependencies // const { executeSparcMode } = await import('../cli/mcp-stdio-server.js'); // TODO: Implement proper SPARC mode execution or fix import path const executeSparcMode = (mode: string, task: string, tools: any[], context: any) => { throw new Error('SPARC mode execution not yet implemented in wrapper'); }; const result = await executeSparcMode( mode, args.task, sparcMode.tools || [], args.context || {}, ); return { content: [ { type: 'text', text: result.output, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error executing SPARC ${mode}: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } private buildEnhancedPrompt(mode: SparcMode, task: string, context?: SparcContext): string { const parts: string[] = []; // Add SPARC mode header parts.push(`SPARC: ${mode.name}\n`); parts.push(`## Mode Description\n${mode.description}\n`); // Add available tools if (mode.tools && mode.tools.length > 0) { parts.push(`## Available Tools`); mode.tools.forEach((tool) => { parts.push(`- **${tool}**: ${this.getToolDescription(tool)}`); }); parts.push(''); } // Add usage pattern if (mode.usagePattern) { parts.push(`## Usage Pattern\n\`\`\`javascript\n${mode.usagePattern}\n\`\`\`\n`); } // Add best practices if (mode.bestPractices) { parts.push(`## Best Practices`); mode.bestPractices.forEach((practice) => { parts.push(`- ${practice}`); }); parts.push(''); } // Add integration capabilities if (mode.integrationCapabilities) { parts.push(`## Integration Capabilities\nThis mode integrates with:`); mode.integrationCapabilities.forEach((capability) => { parts.push(`- ${capability}`); }); parts.push(''); } // Add instructions if (mode.instructions) { parts.push(`## Instructions\n${mode.instructions}\n`); } // Add the actual task parts.push(`## TASK: ${task}\n`); // Add SPARC methodology parts.push(this.getSparcMethodology(mode.name, task, context)); // Add context if provided if (context) { if (context.memoryKey) { parts.push(`**Memory Key:** \`${context.memoryKey}\``); } if (context.parallel) { parts.push(`**Parallel Execution:** Enabled`); } if (context.workingDirectory) { parts.push(`**Working Directory:** ${context.workingDirectory}`); } } return parts.join('\n'); } private getToolDescription(tool: string): string { const descriptions: Record<string, string> = { TodoWrite: 'Create and manage task coordination', TodoRead: 'Monitor task progress and status', Task: 'Spawn and manage specialized agents', Memory: 'Store and retrieve coordination data', Bash: 'Execute system commands', Read: 'Read file contents', Write: 'Write files', Edit: 'Edit existing files', MultiEdit: 'Make multiple edits to a file', Glob: 'Search for files by pattern', Grep: 'Search file contents', WebSearch: 'Search the web', WebFetch: 'Fetch web content', }; return descriptions[tool] || `${tool} tool`; } private getSparcMethodology(mode: string, task: string, context?: SparcContext): string { return ` # đŸŽ¯ SPARC METHODOLOGY EXECUTION FRAMEWORK You are operating in **SPARC ${mode} mode**. Follow the SPARC Workflow precisely: ## SPARC Workflow Steps ### 1ī¸âƒŖ SPECIFICATION - Clarify goals, scope, constraints **Your Task:** ${task} **Analysis Required:** - Break down into clear, measurable objectives - Identify all requirements and constraints - Define acceptance criteria - Never hard-code environment variables **Use TodoWrite to capture specifications:** \`\`\`javascript TodoWrite([ { id: "specification", content: "Clarify goals, scope, and constraints for: ${task}", status: "pending", priority: "high" }, { id: "acceptance_criteria", content: "Define clear acceptance criteria and success metrics", status: "pending", priority: "high" } ]); \`\`\` ### 2ī¸âƒŖ PSEUDOCODE - High-level logic with TDD anchors **Design Approach:** - Identify core functions and data structures - Create TDD test anchors before implementation - Map out component interactions ### 3ī¸âƒŖ ARCHITECTURE - Design extensible systems **Architecture Requirements:** - Clear service boundaries - Define interfaces between components - Design for extensibility and maintainability - Mode-specific architecture: ${this.getModeSpecificArchitecture(mode)} ### 4ī¸âƒŖ REFINEMENT - Iterate with TDD and security **Refinement Process:** - TDD implementation cycles - Security vulnerability checks (injection, XSS, CSRF) - Performance optimization - Code review and refactoring - All files must be ≤ 500 lines ### 5ī¸âƒŖ COMPLETION - Integrate and verify **Completion Checklist:** - [ ] All acceptance criteria met - [ ] Tests passing (comprehensive test suite) - [ ] Security review completed - [ ] Documentation updated - [ ] Results stored in Memory: \`sparc_${mode}_${Date.now()}\` - [ ] No hard-coded secrets or env vars - [ ] Proper error handling in all code paths ## 🚀 Execution Configuration **Mode:** ${mode} **Strategy:** ${this.getModeStrategy(mode)} **Memory Key:** \`sparc_${mode}_${Date.now()}\` **Batch Operations:** ${context?.parallel ? 'Enabled' : 'Standard operations'} **Primary Tools:** ${this.sparcModes.get(mode)?.tools?.join(', ') || 'Standard tools'} ## 📋 Must Block (Non-negotiable) - Every file ≤ 500 lines - No hard-coded secrets or env vars - All user inputs validated - No security vulnerabilities - Proper error handling in all paths - Each subtask ends with completion check ## đŸŽ¯ IMMEDIATE ACTION REQUIRED **START NOW with SPARC Step 1 - SPECIFICATION:** 1. Create comprehensive TodoWrite task breakdown following SPARC workflow 2. Set "specification" task to "in_progress" 3. Analyze requirements and define acceptance criteria 4. Store initial analysis in Memory: \`sparc_${mode}_${Date.now()}\` **Remember:** You're in **${mode}** mode. Follow the SPARC workflow systematically: Specification → Pseudocode → Architecture → Refinement → Completion Use the appropriate tools for each phase and maintain progress in TodoWrite.`; } private getModeSpecificArchitecture(mode: string): string { const architectures: Record<string, string> = { orchestrator: 'Design for parallel agent coordination with clear task boundaries', coder: 'Focus on clean code architecture with proper abstractions', researcher: 'Structure for data collection and analysis pipelines', tdd: 'Test-first design with comprehensive test coverage', architect: 'System-wide design patterns and component interactions', reviewer: 'Code quality gates and review checkpoints', debugger: 'Diagnostic and monitoring integration points', tester: 'Test framework integration and coverage analysis', }; return architectures[mode] || 'Design for the specific mode requirements'; } private getModeStrategy(mode: string): string { const strategies: Record<string, string> = { orchestrator: 'Parallel coordination', coder: 'Iterative development', researcher: 'Deep analysis', tdd: 'Test-driven cycles', architect: 'System design', reviewer: 'Quality assurance', debugger: 'Systematic debugging', tester: 'Comprehensive validation', }; return strategies[mode] || 'Mode-specific execution'; } private listModes(verbose: boolean): CallToolResult { const modes = Array.from(this.sparcModes.values()); if (verbose) { const content = modes.map((mode) => ({ name: mode.name, description: mode.description, tools: mode.tools, bestPractices: mode.bestPractices, })); return { content: [ { type: 'text', text: JSON.stringify(content, null, 2), }, ], }; } const list = modes.map((m) => `- **${m.name}**: ${m.description}`).join('\n'); return { content: [ { type: 'text', text: `Available SPARC modes:\n\n${list}`, }, ], }; } private async handleSwarm(args: any): Promise<CallToolResult> { const { objective, strategy, mode = 'distributed', maxAgents = 5 } = args; const swarmId = generateId(); // Plan swarm agents const agents = this.planSwarmAgents(objective, strategy, maxAgents); // Create swarm execution record const execution: SwarmExecution = { id: swarmId, objective, strategy, mode, agents, startTime: new Date(), status: 'active', }; this.swarmExecutions.set(swarmId, execution); // Launch agents based on coordination mode if (mode === 'distributed' || mode === 'mesh') { // Parallel execution await Promise.all(agents.map((agent) => this.launchSwarmAgent(agent, execution))); } else { // Sequential execution for (const agent of agents) { await this.launchSwarmAgent(agent, execution); } } execution.status = 'completed'; execution.endTime = new Date(); return { content: [ { type: 'text', text: JSON.stringify( { swarmId, objective, strategy, mode, agentCount: agents.length, status: 'launched', message: 'Swarm coordination initiated', }, null, 2, ), }, ], }; } private planSwarmAgents(objective: string, strategy: string, maxAgents: number): SwarmAgent[] { const agents: SwarmAgent[] = []; // Strategy-based agent planning switch (strategy) { case 'research': agents.push( { id: generateId(), mode: 'researcher', task: `Research: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'analyst', task: `Analyze findings for: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'documenter', task: `Document research results: ${objective}`, status: 'pending', }, ); break; case 'development': agents.push( { id: generateId(), mode: 'architect', task: `Design architecture: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'coder', task: `Implement: ${objective}`, status: 'pending' }, { id: generateId(), mode: 'tester', task: `Test implementation: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'reviewer', task: `Review code: ${objective}`, status: 'pending', }, ); break; case 'analysis': agents.push( { id: generateId(), mode: 'analyst', task: `Analyze: ${objective}`, status: 'pending' }, { id: generateId(), mode: 'optimizer', task: `Optimize based on analysis: ${objective}`, status: 'pending', }, ); break; case 'testing': agents.push( { id: generateId(), mode: 'tester', task: `Create test suite: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'debugger', task: `Debug issues: ${objective}`, status: 'pending', }, ); break; case 'optimization': agents.push( { id: generateId(), mode: 'analyst', task: `Performance analysis: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'optimizer', task: `Optimize: ${objective}`, status: 'pending', }, ); break; case 'maintenance': agents.push( { id: generateId(), mode: 'reviewer', task: `Code review: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'debugger', task: `Fix issues: ${objective}`, status: 'pending', }, { id: generateId(), mode: 'documenter', task: `Update documentation: ${objective}`, status: 'pending', }, ); break; } // Limit to maxAgents return agents.slice(0, maxAgents); } private async launchSwarmAgent(agent: SwarmAgent, execution: SwarmExecution): Promise<void> { agent.status = 'active'; try { // Use the SPARC mode handler const result = await this.handleSparcTool(`sparc_${agent.mode}`, { task: agent.task, context: { memoryKey: `swarm_${execution.id}_${agent.id}`, parallel: execution.mode === 'distributed', }, }); agent.status = 'completed'; agent.result = result; } catch (error) { agent.status = 'failed'; agent.result = { error: error instanceof Error ? error.message : String(error) }; } } private getSwarmStatus(swarmId?: string): CallToolResult { if (swarmId) { const execution = this.swarmExecutions.get(swarmId); if (!execution) { return { content: [ { type: 'text', text: `No swarm found with ID: ${swarmId}`, }, ], }; } return { content: [ { type: 'text', text: JSON.stringify(execution, null, 2), }, ], }; } // Return all swarms const swarms = Array.from(this.swarmExecutions.values()).map((e) => ({ id: e.id, objective: e.objective, status: e.status, agentCount: e.agents.length, startTime: e.startTime, endTime: e.endTime, })); return { content: [ { type: 'text', text: JSON.stringify(swarms, null, 2), }, ], }; } private async forwardToClaudeCode(toolName: string, args: any): Promise<CallToolResult> { // For SPARC tools that were already handled, this shouldn't be called // For other tools, we execute them using the existing logic if (toolName === 'Task') { // This is a SPARC task that's been enhanced with prompts // Extract the mode from the description if possible const modeMatch = args.description?.match(/SPARC (\w+)/); if (modeMatch) { const modeName = modeMatch[1]; const mode = this.sparcModes.get(modeName); if (mode) { // Execute using the existing SPARC execution logic try { const result = await executeSparcMode( modeName, args.prompt || '', mode.tools || [], {}, ); return { content: [ { type: 'text', text: result.output, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error executing SPARC ${modeName}: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } } } // For non-SPARC tools, return a message return { content: [ { type: 'text', text: `Tool ${toolName} is not available in this MCP server.`, }, ], isError: true, }; } async run() { const transport = new StdioServerTransport(); // Log startup message console.error('🚀 Jay-Code MCP Server (Wrapper Mode)'); console.error('đŸ“Ļ Using Claude Code MCP pass-through with SPARC prompt injection'); console.error('🔧 All SPARC tools available with enhanced AI capabilities'); console.error('â„šī¸ To use legacy mode, set JAY_CODE_LEGACY_MCP=true'); console.error(''); await this.server.connect(transport); } } // Run the server if this is the main module if (import.meta.url === `file://${process.argv[1]}`) { const wrapper = new ClaudeCodeMCPWrapper(); wrapper.run().catch(console.error); }