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
JavaScript
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);
}
}
}