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.

467 lines (466 loc) 20 kB
import { executeTool } from '../../services/routing/toolRegistry.js'; import logger from '../../logger.js'; export class MultiToolWorkflowEngine { config; activeWorkflows = new Map(); workflowPatterns = { 'research-to-documentation': { triggers: ['research', 'document', 'create prd', 'write specification'], tools: ['research-manager', 'prd-generator', 'user-stories-generator'], type: 'sequential', estimatedDuration: 180000 }, 'full-project-setup': { triggers: ['create project', 'setup project', 'new application', 'full stack'], tools: ['fullstack-starter-kit-generator', 'rules-generator', 'vibe-task-manager'], type: 'sequential', estimatedDuration: 300000 }, 'analyze-and-plan': { triggers: ['analyze', 'plan', 'break down', 'understand codebase'], tools: ['map-codebase', 'curate-context', 'task-list-generator'], type: 'sequential', estimatedDuration: 240000 }, 'development-workflow': { triggers: ['implement', 'develop', 'build feature', 'create functionality'], tools: ['user-stories-generator', 'task-list-generator', 'curate-context', 'vibe-task-manager'], type: 'sequential', estimatedDuration: 360000 }, 'research-implement': { triggers: ['research and implement', 'learn and build', 'study and develop'], tools: ['research-manager', 'curate-context', 'task-list-generator'], type: 'parallel', estimatedDuration: 420000 } }; constructor(config) { this.config = config; } async analyzeWorkflowPotential(intent, params, selectedTool, context) { try { const input = intent.originalInput.toLowerCase(); for (const [workflowName, pattern] of Object.entries(this.workflowPatterns)) { const matches = pattern.triggers.some(trigger => input.includes(trigger.toLowerCase()) || this.semanticMatch(input, trigger) > 0.7); if (matches) { return { shouldTriggerWorkflow: true, workflowName, workflowType: pattern.type, estimatedDuration: pattern.estimatedDuration, requiredTools: pattern.tools, nextSteps: await this.generateNextSteps(workflowName, selectedTool, params) }; } } const compoundDetection = await this.detectCompoundRequest(input, selectedTool); if (compoundDetection.shouldTriggerWorkflow) { return compoundDetection; } const sequentialDetection = await this.detectSequentialOpportunity(selectedTool, context, params); return sequentialDetection; } catch (error) { logger.error({ err: error, intent: intent.intent }, 'Workflow analysis failed'); return { shouldTriggerWorkflow: false, workflowType: 'simple' }; } } async executeMultiToolWorkflow(workflowName, params, context) { const sessionId = context.sessionId; const startTime = Date.now(); try { logger.info({ workflowName, sessionId }, 'Starting multi-tool workflow execution'); this.registerActiveWorkflow(sessionId, workflowName); const workflowPattern = this.workflowPatterns[workflowName]; if (!workflowPattern) { throw new Error(`Unknown workflow pattern: ${workflowName}`); } let result; if (workflowPattern.type === 'sequential') { result = await this.executeSequentialWorkflow(workflowPattern, params, context); } else if (workflowPattern.type === 'parallel') { result = await this.executeParallelWorkflow(workflowPattern, params, context); } else { result = await this.executeSequentialWorkflow(workflowPattern, params, context); } this.updateContextWithResults(context, result, workflowName); const processingTime = Date.now() - startTime; logger.info({ workflowName, sessionId, processingTime, toolsUsed: result.toolsUsed.length, success: result.success }, 'Multi-tool workflow completed'); return result; } catch (error) { logger.error({ err: error, workflowName, sessionId }, 'Multi-tool workflow execution failed'); return { success: false, message: 'Workflow execution failed', outputs: undefined, error: { message: error instanceof Error ? error.message : 'Workflow execution failed' }, triggeredWorkflows: [], parallelExecutions: 0, toolsUsed: [], crossToolContext: {} }; } finally { this.unregisterActiveWorkflow(sessionId); } } async checkToolCompatibility(toolName, workflowName, context) { try { const workflowPattern = this.workflowPatterns[workflowName]; if (!workflowPattern) { return { compatible: false, confidence: 0, suggestions: [`Unknown workflow: ${workflowName}`] }; } const isDirectMatch = workflowPattern.tools.includes(toolName); if (isDirectMatch) { return { compatible: true, confidence: 1.0, suggestions: [`Tool ${toolName} is part of ${workflowName} workflow`] }; } const compatibility = await this.calculateToolCompatibility(toolName, workflowPattern.tools, context); return { compatible: compatibility.score > 0.6, confidence: compatibility.score, suggestions: compatibility.suggestions, alternativeTools: compatibility.alternatives }; } catch (error) { logger.error({ err: error, toolName, workflowName }, 'Tool compatibility check failed'); return { compatible: false, confidence: 0, suggestions: ['Compatibility check failed'] }; } } async executeSequentialWorkflow(pattern, params, context) { const toolsUsed = []; const crossToolContext = { ...params }; let lastResult = null; for (let i = 0; i < pattern.tools.length; i++) { const toolName = pattern.tools[i]; try { const executionContext = { sessionId: context.sessionId, transportType: 'cli', metadata: { workflowStep: i + 1, totalSteps: pattern.tools.length, previousTool: i > 0 ? pattern.tools[i - 1] : undefined, crossToolContext: { ...crossToolContext } } }; const toolParams = await this.prepareToolParams(toolName, crossToolContext, lastResult, context); logger.debug({ toolName, step: i + 1, totalSteps: pattern.tools.length, params: Object.keys(toolParams) }, 'Executing sequential workflow step'); const result = await executeTool(toolName, toolParams, this.config, executionContext); toolsUsed.push(toolName); lastResult = result; crossToolContext[`${toolName}_result`] = result; if (result && typeof result === 'object') { Object.assign(crossToolContext, this.extractReusableData(toolName, result)); } } catch (error) { logger.error({ err: error, toolName, step: i + 1 }, 'Sequential workflow step failed'); return { success: false, message: `Sequential workflow failed at step ${i + 1}`, outputs: undefined, error: { stepId: `${i + 1}`, toolName, message: `Step ${i + 1} (${toolName}) failed: ${error instanceof Error ? error.message : 'Unknown error'}` }, triggeredWorkflows: ['sequential'], parallelExecutions: 0, toolsUsed, crossToolContext }; } } return { success: true, message: 'Sequential workflow completed successfully', outputs: lastResult, error: undefined, triggeredWorkflows: ['sequential'], parallelExecutions: 0, toolsUsed, crossToolContext }; } async executeParallelWorkflow(pattern, params, context) { const toolPromises = []; for (const toolName of pattern.tools) { const toolPromise = this.executeToolWithErrorHandling(toolName, params, context); toolPromises.push(toolPromise); } try { const results = await Promise.all(toolPromises); const toolsUsed = []; const crossToolContext = { ...params }; const errors = []; const combinedResult = {}; for (const { tool, result, error } of results) { toolsUsed.push(tool); if (error) { errors.push(`${tool}: ${error}`); } else { crossToolContext[`${tool}_result`] = result; if (result && typeof result === 'object') { Object.assign(combinedResult, this.extractReusableData(tool, result)); } } } const success = errors.length === 0; return { success, message: success ? 'Parallel workflow completed successfully' : 'Some parallel workflow steps failed', outputs: combinedResult, error: errors.length > 0 ? { message: errors.join('; ') } : undefined, triggeredWorkflows: ['parallel'], parallelExecutions: pattern.tools.length, toolsUsed, crossToolContext }; } catch (error) { logger.error({ err: error, tools: pattern.tools }, 'Parallel workflow execution failed'); return { success: false, message: 'Parallel workflow execution failed', outputs: undefined, error: { message: error instanceof Error ? error.message : 'Parallel execution failed' }, triggeredWorkflows: ['parallel'], parallelExecutions: 0, toolsUsed: [], crossToolContext: {} }; } } async executeCompoundWorkflow(pattern, params, context) { return this.executeSequentialWorkflow(pattern, params, context); } async executeToolWithErrorHandling(toolName, params, context) { try { const executionContext = { sessionId: context.sessionId, transportType: 'cli', metadata: { parallelExecution: true, toolName } }; const result = await executeTool(toolName, params, this.config, executionContext); return { tool: toolName, result }; } catch (error) { return { tool: toolName, result: null, error: error instanceof Error ? error.message : 'Tool execution failed' }; } } async prepareToolParams(toolName, crossToolContext, _previousResult, _context) { const baseParams = { ...crossToolContext }; switch (toolName) { case 'prd-generator': if (crossToolContext.research_manager_result) { baseParams.research_context = crossToolContext.research_manager_result; } break; case 'user-stories-generator': if (crossToolContext.prd_generator_result) { baseParams.prd_context = crossToolContext.prd_generator_result; } break; case 'task-list-generator': if (crossToolContext.user_stories_generator_result) { baseParams.user_stories = crossToolContext.user_stories_generator_result; } break; case 'curate-context': if (crossToolContext.map_codebase_result) { baseParams.codebase_analysis = crossToolContext.map_codebase_result; } break; } return baseParams; } extractReusableData(toolName, result) { if (!result || typeof result !== 'object') { return {}; } const extracted = {}; if ('content' in result && Array.isArray(result.content)) { for (const item of result.content) { if (item.type === 'text' && item.text) { extracted[`${toolName}_output`] = item.text; break; } } } const resultObj = result; switch (toolName) { case 'research-manager': extracted.research_topic = resultObj.topic || resultObj.query; break; case 'prd-generator': extracted.product_name = resultObj.product_name; extracted.feature_list = resultObj.features; break; case 'map-codebase': extracted.project_structure = resultObj.structure; extracted.main_files = resultObj.important_files; break; } return extracted; } async detectCompoundRequest(input, _primaryTool) { const compoundIndicators = [ 'and then', 'followed by', 'after that', 'also', 'plus', 'additionally', 'as well as', 'along with', 'including' ]; const hasCompoundIndicators = compoundIndicators.some(indicator => input.includes(indicator)); if (!hasCompoundIndicators) { return { shouldTriggerWorkflow: false, workflowType: 'simple' }; } const compoundPatterns = [ { pattern: /research.*(?:and|then).*(?:create|generate|document)/, workflow: 'research-to-documentation' }, { pattern: /(?:create|setup).*project.*(?:and|with).*(?:rules|standards)/, workflow: 'full-project-setup' }, { pattern: /(?:analyze|map).*(?:and|then).*(?:plan|organize)/, workflow: 'analyze-and-plan' } ]; for (const { pattern, workflow } of compoundPatterns) { if (pattern.test(input)) { return { shouldTriggerWorkflow: true, workflowName: workflow, workflowType: 'compound', estimatedDuration: this.workflowPatterns[workflow]?.estimatedDuration, nextSteps: ['Executing compound workflow with multiple tools'] }; } } return { shouldTriggerWorkflow: false, workflowType: 'simple' }; } async detectSequentialOpportunity(selectedTool, context, _params) { const recentTools = context.toolHistory.slice(-3).map(h => h.tool); const sequentialPatterns = [ ['research-manager', 'prd-generator'], ['prd-generator', 'user-stories-generator'], ['user-stories-generator', 'task-list-generator'], ['map-codebase', 'curate-context'] ]; for (const pattern of sequentialPatterns) { const patternIndex = pattern.indexOf(selectedTool); if (patternIndex > 0) { const previousTool = pattern[patternIndex - 1]; if (recentTools.includes(previousTool)) { return { shouldTriggerWorkflow: true, workflowType: 'sequential', nextSteps: [`Continue with ${pattern.slice(patternIndex + 1).join(' → ')}`], estimatedDuration: 120000 }; } } } return { shouldTriggerWorkflow: false, workflowType: 'simple' }; } async calculateToolCompatibility(toolName, workflowTools, _context) { const toolRelationships = { 'research-manager': ['prd-generator', 'user-stories-generator'], 'prd-generator': ['user-stories-generator', 'task-list-generator'], 'user-stories-generator': ['task-list-generator', 'vibe-task-manager'], 'map-codebase': ['curate-context', 'rules-generator'], 'curate-context': ['task-list-generator', 'vibe-task-manager'] }; const relatedTools = toolRelationships[toolName] || []; const compatibility = relatedTools.filter(tool => workflowTools.includes(tool)); const score = compatibility.length / Math.max(workflowTools.length, 1); return { score, suggestions: compatibility.map(tool => `Works well with ${tool}`), alternatives: relatedTools.filter(tool => !workflowTools.includes(tool)) }; } semanticMatch(input, trigger) { const inputWords = input.toLowerCase().split(/\s+/); const triggerWords = trigger.toLowerCase().split(/\s+/); const matches = triggerWords.filter(word => inputWords.some(inputWord => inputWord.includes(word) || word.includes(inputWord))); return matches.length / triggerWords.length; } async generateNextSteps(workflowName, currentTool, _params) { const pattern = this.workflowPatterns[workflowName]; if (!pattern) return []; const currentIndex = pattern.tools.indexOf(currentTool); const remainingTools = pattern.tools.slice(currentIndex + 1); return remainingTools.map(tool => `Execute ${tool} with enriched context`); } registerActiveWorkflow(sessionId, workflowName) { const pattern = this.workflowPatterns[workflowName]; this.activeWorkflows.set(sessionId, { sessionId, workflowName, startTime: new Date(), toolsUsed: [], currentStep: 0, totalSteps: pattern?.tools.length || 1 }); } unregisterActiveWorkflow(sessionId) { this.activeWorkflows.delete(sessionId); } updateContextWithResults(context, result, workflowName) { if (result.success) { context.workflowStack.push(workflowName); } for (const tool of result.toolsUsed) { const currentPreference = context.preferredTools[tool] || 0; context.preferredTools[tool] = result.success ? currentPreference + 0.1 : Math.max(currentPreference - 0.05, 0); } } }