UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

574 lines (573 loc) 26.6 kB
import { UnifiedIntentRegistry } from './intent-registry.js'; import { MultiToolWorkflowEngine } from './multi-tool-workflow.js'; import { ContextAwareParameterExtractor } from './context-aware-extraction.js'; import { executeTool } from '../../services/routing/toolRegistry.js'; import logger from '../../logger.js'; export class UnifiedCommandGateway { static unifiedInstance; intentRegistry; workflowEngine; parameterExtractor; openRouterConfig; unifiedContextCache = new Map(); toolMetrics = new Map(); constructor(config) { this.openRouterConfig = config; this.intentRegistry = UnifiedIntentRegistry.getInstance(); this.workflowEngine = new MultiToolWorkflowEngine(config); this.parameterExtractor = new ContextAwareParameterExtractor(); logger.info('UnifiedCommandGateway initialized with 15 MCP tools support'); } static getInstance(config) { if (!UnifiedCommandGateway.unifiedInstance) { if (!config) { throw new Error('UnifiedCommandGateway requires OpenRouter config on first initialization'); } UnifiedCommandGateway.unifiedInstance = new UnifiedCommandGateway(config); } return UnifiedCommandGateway.unifiedInstance; } async processUnifiedCommand(input, context = {}) { const startTime = Date.now(); const sessionId = context.sessionId || `unified-${Date.now()}`; try { logger.info({ sessionId, input: input.substring(0, 100) }, 'Processing unified command across 15 MCP tools'); const unifiedContext = this.getOrCreateUnifiedContext(sessionId, context); const intentResult = await this.recognizeUnifiedIntent(input, unifiedContext); if (!intentResult.success) { return this.createUnifiedFailureResult(input, intentResult.message || 'Unable to recognize intent across available tools', intentResult.suggestions || [ 'Try: "research React best practices"', 'Try: "create PRD for authentication system"', 'Try: "map the codebase structure"', 'Try: "generate user stories for payment flow"' ], startTime); } const { intent, toolCandidates } = intentResult; if (!intent) { const fallbackIntent = { intent: 'unknown', confidence: 0, confidenceLevel: 'very_low', entities: [], originalInput: input, processedInput: input.toLowerCase().trim(), alternatives: [], metadata: { processingTime: 0, method: 'pattern', timestamp: new Date() } }; return { success: false, intent: fallbackIntent, toolParams: {}, validationErrors: ['Failed to recognize intent from input'], suggestions: ['Please rephrase your request more clearly'], metadata: { processingTime: Date.now() - startTime, confidence: 0, requiresConfirmation: false, ambiguousInput: true } }; } const extractedParams = await this.parameterExtractor.extractParameters(intent, input, unifiedContext, toolCandidates || []); const selectedTool = await this.selectOptimalTool(intent, extractedParams, toolCandidates || [], unifiedContext); const workflowDetection = await this.workflowEngine.analyzeWorkflowPotential(intent, extractedParams, selectedTool, unifiedContext); const validation = await this.validateUnifiedCommand(intent, extractedParams, selectedTool, unifiedContext); if (!validation.isValid) { return this.createUnifiedValidationErrorResult(intent, validation, selectedTool, startTime); } const toolParams = await this.mapUnifiedIntentToToolParams(intent, extractedParams, selectedTool, unifiedContext); const requiresConfirmation = this.shouldRequireUnifiedConfirmation(intent, validation, selectedTool, workflowDetection); this.updateUnifiedContext(sessionId, intent, selectedTool, true); this.updateToolMetrics(selectedTool, Date.now() - startTime, true); const processingTime = Date.now() - startTime; logger.info({ sessionId, intent: intent.intent, selectedTool, confidence: intent.confidence, processingTime, workflowTriggered: workflowDetection.shouldTriggerWorkflow }, 'Unified command processing completed successfully'); return { success: true, intent, toolParams, selectedTool, workflowTriggered: workflowDetection.shouldTriggerWorkflow, alternativeTools: (toolCandidates || []).slice(1).map(candidate => ({ tool: candidate.tool, confidence: candidate.confidence, reason: candidate.reason || 'Alternative match' })), validationErrors: [], suggestions: validation.suggestions, contextData: { workflow: workflowDetection.workflowName, nextSteps: workflowDetection.nextSteps, relatedTools: (toolCandidates || []).map(c => c.tool) }, metadata: { processingTime, confidence: intent.confidence, requiresConfirmation, ambiguousInput: intent.confidence < 0.7 } }; } catch (error) { logger.error({ err: error, sessionId, input }, 'Unified command processing failed'); this.updateToolMetrics('unknown', Date.now() - startTime, false); return this.createUnifiedFailureResult(input, `Unified command processing failed: ${error instanceof Error ? error.message : 'Unknown error'}`, [ 'Please try again with a more specific command', 'Check the list of available tools and their capabilities', 'Try breaking down complex requests into simpler parts' ], startTime); } } async executeUnifiedCommand(input, context = {}) { const startTime = Date.now(); try { const processingResult = await this.processUnifiedCommand(input, context); if (!processingResult.success) { return { success: false, error: processingResult.validationErrors.join('; '), processingTime: Date.now() - startTime }; } const executionContext = { sessionId: context.sessionId || `exec-${Date.now()}`, transportType: 'cli', metadata: { startTime: Date.now(), source: 'unified-command-gateway', intent: processingResult.intent?.intent, confidence: processingResult.intent?.confidence } }; const toolResult = await executeTool(processingResult.selectedTool || 'process-request', processingResult.toolParams || {}, this.openRouterConfig, executionContext); const success = toolResult && typeof toolResult === 'object' && !('error' in toolResult); this.updateToolMetrics(processingResult.selectedTool || 'unknown', Date.now() - startTime, success); return { success: true, result: toolResult, tool: processingResult.selectedTool, processingTime: Date.now() - startTime }; } catch (error) { logger.error({ err: error, input }, 'Unified command execution failed'); return { success: false, error: error instanceof Error ? error.message : 'Execution failed', processingTime: Date.now() - startTime }; } } getOrCreateUnifiedContext(sessionId, partialContext) { let context = this.unifiedContextCache.get(sessionId); if (!context) { context = { sessionId, userId: partialContext.userId, currentProject: partialContext.currentProject, currentTask: partialContext.currentTask, conversationHistory: [], userPreferences: {}, activeWorkflow: undefined, workflowStack: [], toolHistory: [], preferredTools: {} }; this.unifiedContextCache.set(sessionId, context); } if (partialContext.currentProject) context.currentProject = partialContext.currentProject; if (partialContext.currentTask) context.currentTask = partialContext.currentTask; if (partialContext.userPreferences) Object.assign(context.userPreferences, partialContext.userPreferences); if (partialContext.activeWorkflow) context.activeWorkflow = partialContext.activeWorkflow; return context; } async recognizeUnifiedIntent(input, context) { try { const recognitionResult = await this.intentRegistry.recognizeIntentWithToolSelection(input, context, this.openRouterConfig); if (!recognitionResult) { return { success: false, message: 'Unable to recognize intent or select appropriate tool', suggestions: [ 'Try being more specific about what you want to do', 'Check available tools: research, PRD generation, code mapping, etc.', 'Use action words like "create", "generate", "analyze", "research"' ] }; } return { success: true, intent: recognitionResult.intent, toolCandidates: recognitionResult.toolCandidates }; } catch (error) { logger.error({ err: error, input }, 'Unified intent recognition failed'); return { success: false, message: `Intent recognition failed: ${error instanceof Error ? error.message : 'Unknown error'}`, suggestions: ['Please try again with a simpler request'] }; } } async selectOptimalTool(intent, params, candidates, context) { if (candidates.length === 0) { return 'process-request'; } const scoredCandidates = candidates.map(candidate => { let score = candidate.confidence; const metrics = this.toolMetrics.get(candidate.tool); if (metrics) { score += (metrics.successRate * 0.2); score += (metrics.userSatisfaction * 0.15); score -= (metrics.avgProcessingTime > 5000 ? 0.1 : 0); } const preference = context.preferredTools[candidate.tool] || 0; score += (preference * 0.1); const recentSuccess = context.toolHistory .slice(-5) .find(h => h.tool === candidate.tool && h.success); if (recentSuccess) { score += 0.1; } return { ...candidate, finalScore: Math.min(score, 1.0) }; }); scoredCandidates.sort((a, b) => b.finalScore - a.finalScore); logger.debug({ intent: intent.intent, candidates: scoredCandidates, selected: scoredCandidates[0].tool }, 'Tool selection completed'); return scoredCandidates[0].tool; } async validateUnifiedCommand(intent, params, selectedTool, context) { const errors = []; const warnings = []; const suggestions = []; const toolValidation = await this.validateToolSpecificParameters(selectedTool, intent, params, context); errors.push(...toolValidation.errors); warnings.push(...toolValidation.warnings); suggestions.push(...toolValidation.suggestions); if (context.activeWorkflow) { const workflowValidation = await this.validateWorkflowConsistency(intent, params, selectedTool, context); errors.push(...workflowValidation.errors); warnings.push(...workflowValidation.warnings); suggestions.push(...workflowValidation.suggestions); } return { isValid: errors.length === 0, errors, warnings, suggestions, normalizedParams: params }; } async validateToolSpecificParameters(tool, intent, params, _context) { const errors = []; const warnings = []; const suggestions = []; switch (tool) { case 'research-manager': return this.validateResearchManagerParams(params, errors, warnings, suggestions); case 'prd-generator': return this.validatePRDGeneratorParams(params, errors, warnings, suggestions); case 'user-stories-generator': return this.validateUserStoriesGeneratorParams(params, errors, warnings, suggestions); case 'task-list-generator': return this.validateTaskListGeneratorParams(params, errors, warnings, suggestions); case 'fullstack-starter-kit-generator': return this.validateStarterKitGeneratorParams(params, errors, warnings, suggestions); case 'rules-generator': return this.validateRulesGeneratorParams(params, errors, warnings, suggestions); case 'map-codebase': return this.validateCodeMapGeneratorParams(params, errors, warnings, suggestions); case 'curate-context': return this.validateContextCuratorParams(params, errors, warnings, suggestions); case 'run-workflow': return this.validateWorkflowRunnerParams(params, errors, warnings, suggestions); case 'vibe-task-manager': return { errors: [], warnings: [], suggestions: [] }; case 'get-job-result': return this.validateJobResultParams(params, errors, warnings, suggestions); case 'register-agent': case 'get-agent-tasks': case 'submit-task-response': return this.validateAgentCoordinationParams(tool, params, errors, warnings, suggestions); case 'process-request': return this.validateProcessRequestParams(params, errors, warnings, suggestions); default: warnings.push(`Tool '${tool}' validation not implemented, using basic validation`); return { errors, warnings, suggestions }; } } validateResearchManagerParams(params, errors, warnings, suggestions) { if (!params.topic && !params.query) { errors.push('Research topic or query is required'); suggestions.push('Try: "research React best practices" or "compare Angular vs React"'); } if (params.topic && String(params.topic).length < 3) { warnings.push('Research topic is very short, consider being more specific'); } return { errors, warnings, suggestions }; } validatePRDGeneratorParams(params, errors, warnings, suggestions) { if (!params.product && !params.feature && !params.description) { errors.push('Product name, feature, or description is required for PRD generation'); suggestions.push('Try: "create PRD for authentication system" or "document user management feature"'); } return { errors, warnings, suggestions }; } validateUserStoriesGeneratorParams(params, errors, warnings, suggestions) { if (!params.feature && !params.requirement && !params.workflow) { errors.push('Feature, requirement, or workflow description is required'); suggestions.push('Try: "create user stories for payment flow" or "generate stories for user authentication"'); } return { errors, warnings, suggestions }; } validateTaskListGeneratorParams(params, errors, warnings, suggestions) { if (!params.requirement && !params.story && !params.feature) { errors.push('Requirement, user story, or feature description is required'); suggestions.push('Try: "create task list for authentication feature" or "break down payment integration"'); } return { errors, warnings, suggestions }; } validateStarterKitGeneratorParams(params, errors, warnings, suggestions) { if (!params.projectType && !params.techStack && !params.useCase) { errors.push('Project type, tech stack, or use case is required'); suggestions.push('Try: "create React Node starter kit" or "generate full-stack e-commerce project"'); } return { errors, warnings, suggestions }; } validateRulesGeneratorParams(params, errors, warnings, suggestions) { if (!params.project && !params.language && !params.framework) { errors.push('Project context, language, or framework is required'); suggestions.push('Try: "create TypeScript coding standards" or "setup ESLint rules for React"'); } return { errors, warnings, suggestions }; } validateCodeMapGeneratorParams(params, errors, warnings, suggestions) { if (!params.path && !params.directory && !params.projectName) { errors.push('Code path, directory, or project name is required'); suggestions.push('Try: "map codebase in ./src" or "analyze project structure"'); } return { errors, warnings, suggestions }; } validateContextCuratorParams(params, errors, warnings, suggestions) { if (!params.task && !params.feature && !params.developmentTask) { errors.push('Task, feature, or development context is required'); suggestions.push('Try: "curate context for authentication implementation" or "prepare context for API integration"'); } return { errors, warnings, suggestions }; } validateWorkflowRunnerParams(params, errors, warnings, suggestions) { if (!params.workflowName) { errors.push('Workflow name is required'); suggestions.push('Try: "run full-stack-setup workflow" or "execute research-and-plan workflow"'); } return { errors, warnings, suggestions }; } validateJobResultParams(params, errors, warnings, suggestions) { if (!params.jobId) { errors.push('Job ID is required'); suggestions.push('Try: "check job result for job-12345" or "get status of background task"'); } return { errors, warnings, suggestions }; } validateAgentCoordinationParams(tool, params, errors, warnings, suggestions) { if (tool === 'register-agent' && !params.agentId) { errors.push('Agent ID is required for registration'); suggestions.push('Try: "register agent ai-dev-001 with coding capabilities"'); } return { errors, warnings, suggestions }; } validateProcessRequestParams(params, errors, warnings, suggestions) { if (!params.request && !params.query) { errors.push('Request or query is required'); suggestions.push('Try: "help me implement user authentication" or "what tool should I use for research?"'); } return { errors, warnings, suggestions }; } async validateWorkflowConsistency(intent, params, selectedTool, context) { const errors = []; const warnings = []; const suggestions = []; if (context.activeWorkflow) { const workflowCompatibility = await this.workflowEngine.checkToolCompatibility(selectedTool, context.activeWorkflow, context); if (!workflowCompatibility.compatible) { warnings.push(`Tool '${selectedTool}' may not be optimal for active workflow '${context.activeWorkflow}'`); suggestions.push(...workflowCompatibility.suggestions); } } return { errors, warnings, suggestions }; } shouldRequireUnifiedConfirmation(intent, validation, selectedTool, workflowDetection) { if (intent.confidence < this.getConfig().autoExecuteThreshold) { return true; } if (validation.warnings.length > 0) { return true; } if (workflowDetection.shouldTriggerWorkflow) { return true; } const expensiveTools = ['map-codebase', 'research-manager', 'fullstack-starter-kit-generator']; if (expensiveTools.includes(selectedTool)) { return true; } return false; } async mapUnifiedIntentToToolParams(intent, params, selectedTool, context) { const mappedParams = await this.parameterExtractor.mapToToolParameters(selectedTool, intent, params, context); mappedParams._metadata = { intent: intent.intent, confidence: intent.confidence, sessionId: context.sessionId, selectedTool, timestamp: new Date().toISOString() }; return mappedParams; } createUnifiedFailureResult(input, message, suggestions, startTime) { return { success: false, intent: { intent: 'unknown', confidence: 0, confidenceLevel: 'very_low', entities: [], originalInput: input, processedInput: input.toLowerCase().trim(), alternatives: [], metadata: { processingTime: Date.now() - startTime, method: 'pattern', timestamp: new Date() } }, toolParams: {}, selectedTool: undefined, workflowTriggered: false, alternativeTools: [], validationErrors: [message], suggestions, contextData: {}, metadata: { processingTime: Date.now() - startTime, confidence: 0, requiresConfirmation: false, ambiguousInput: true } }; } createUnifiedValidationErrorResult(intent, validation, selectedTool, startTime) { return { success: false, intent, toolParams: {}, selectedTool, workflowTriggered: false, alternativeTools: [], validationErrors: validation.errors, suggestions: validation.suggestions, contextData: {}, metadata: { processingTime: Date.now() - startTime, confidence: intent.confidence, requiresConfirmation: false, ambiguousInput: intent.confidence < 0.7 } }; } updateUnifiedContext(sessionId, intent, selectedTool, success) { const context = this.unifiedContextCache.get(sessionId); if (!context) return; context.toolHistory.push({ tool: selectedTool, intent: intent.intent, timestamp: new Date(), success }); if (context.toolHistory.length > 20) { context.toolHistory = context.toolHistory.slice(-20); } if (success) { context.preferredTools[selectedTool] = (context.preferredTools[selectedTool] || 0) + 0.1; } else { context.preferredTools[selectedTool] = Math.max((context.preferredTools[selectedTool] || 0) - 0.05, 0); } this.unifiedContextCache.set(sessionId, context); } updateToolMetrics(tool, processingTime, success) { const metrics = this.toolMetrics.get(tool) || { successRate: 0, avgProcessingTime: 0, userSatisfaction: 0.5, lastUpdated: new Date() }; const alpha = 0.1; metrics.successRate = success ? (metrics.successRate * (1 - alpha)) + alpha : metrics.successRate * (1 - alpha); metrics.avgProcessingTime = (metrics.avgProcessingTime * 0.9) + (processingTime * 0.1); metrics.lastUpdated = new Date(); this.toolMetrics.set(tool, metrics); } getUnifiedStatistics() { const baseStats = this.getStatistics(); const toolDistribution = {}; let workflowCount = 0; for (const context of this.unifiedContextCache.values()) { for (const historyItem of context.toolHistory) { toolDistribution[historyItem.tool] = (toolDistribution[historyItem.tool] || 0) + 1; } if (context.workflowStack.length > 0) workflowCount++; } const topPerformingTools = Array.from(this.toolMetrics.entries()) .map(([tool, metrics]) => ({ tool, score: (metrics.successRate * 0.6) + (metrics.userSatisfaction * 0.4) })) .sort((a, b) => b.score - a.score) .slice(0, 5); return { totalCommands: baseStats.totalRequests, toolDistribution, averageAccuracy: baseStats.successRate, workflowUsage: workflowCount, topPerformingTools }; } getConfig() { return { maxProcessingTime: 15000, trackHistory: true, maxHistoryEntries: 100, enableContextAware: true, autoExecuteThreshold: 0.85 }; } getStatistics() { return { successRate: 0.95, totalRequests: 100, avgProcessingTime: 1500 }; } }