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.

989 lines 81.9 kB
import { z } from 'zod'; import { registerTool } from '../../services/routing/toolRegistry.js'; import { getBaseOutputDir, getVibeTaskManagerOutputDir, getVibeTaskManagerConfig } from './utils/config-loader.js'; import { getTimeoutManager, isTimeoutManagerInitialized } from './utils/timeout-manager.js'; import logger from '../../logger.js'; import { AgentOrchestrator } from './services/agent-orchestrator.js'; import { ProjectOperations } from './core/operations/project-operations.js'; import { DecompositionService } from './services/decomposition-service.js'; import { jobManager, JobStatus } from '../../services/job-manager/index.js'; import path from 'path'; import fs from 'fs/promises'; import { getUnifiedSecurityConfig } from './security/unified-security-config.js'; const vibeTaskManagerInputSchema = z.object({ command: z.enum(['create', 'list', 'run', 'status', 'refine', 'decompose']).optional().describe('The command to execute (optional for natural language input)'), projectName: z.string().optional().describe('Name of the project to work with'), taskId: z.string().optional().describe('ID of the task to work with'), description: z.string().optional().describe('Description for project creation or task decomposition'), options: z.record(z.unknown()).optional().describe('Additional options for the command'), input: z.string().optional().describe('Natural language input for command processing') }); const vibeTaskManagerInputSchemaShape = vibeTaskManagerInputSchema.shape; function isNaturalLanguageInput(params) { if (params.input && typeof params.input === 'string') { return true; } if (!params.command && params.description && typeof params.description === 'string') { const desc = params.description.toLowerCase(); const nlPatterns = [ /^(create|make|build|start|begin)/, /^(list|show|display|get)/, /^(run|execute|start|launch)/, /^(check|status|what|how)/, /^(refine|improve|update|modify)/, /^(decompose|break down|split)/ ]; return nlPatterns.some(pattern => pattern.test(desc)); } return false; } async function handleNaturalLanguageInput(input, config, context) { try { const { CommandGateway } = await import('./nl/command-gateway.js'); const gateway = CommandGateway.getInstance(); const result = await gateway.processCommand(input, { sessionId: context?.sessionId || 'default', userId: context?.sessionId || 'anonymous' }); if (!result.success) { return { content: [{ type: "text", text: `❌ Failed to process command: ${result.validationErrors.join(', ')}\n\n` + `Suggestions:\n${result.suggestions.map(s => `• ${s}`).join('\n')}` }], isError: true }; } const { command, projectName, taskId, description, options } = result.toolParams; switch (command) { case 'create': return await handleCreateCommand(projectName, description, options, config, context?.sessionId || 'default'); case 'list': return await handleListCommand(options, context?.sessionId || 'default'); case 'run': return await handleRunCommand(taskId, options, config, context?.sessionId || 'default'); case 'status': return await handleStatusCommand(projectName, taskId, context?.sessionId || 'default'); case 'refine': return await handleRefineCommand(taskId, description, config, context?.sessionId || 'default'); case 'decompose': return await handleDecomposeCommand(taskId || projectName, description, config, context?.sessionId || 'default'); default: return { content: [{ type: "text", text: `❌ Unsupported command '${command}' from natural language processing.\n\n` + `Recognized intent: ${result.intent.intent}\n` + `Confidence: ${Math.round(result.intent.confidence * 100)}%\n\n` + `Please try a different command or contact support.` }], isError: true }; } } catch (error) { logger.error({ err: error, input }, 'Failed to process natural language input'); return { content: [{ type: "text", text: `❌ Failed to process natural language input: ${error instanceof Error ? error.message : 'Unknown error'}\n\n` + `Please try using a structured command instead, such as:\n` + `• "create" - Create a new project\n` + `• "list" - List existing projects\n` + `• "decompose" - Break down a project into tasks` }], isError: true }; } } async function initializeVibeTaskManagerConfig() { try { if (isTimeoutManagerInitialized()) { logger.debug('Vibe Task Manager already initialized, skipping configuration reload'); return; } const config = await getVibeTaskManagerConfig(); if (config?.taskManager) { const timeoutManager = getTimeoutManager(); timeoutManager.initialize(config.taskManager); logger.debug('Vibe Task Manager configuration initialized successfully'); } else { logger.warn('Vibe Task Manager configuration not available, services will use fallback values'); } } catch (error) { logger.warn({ err: error }, 'Failed to initialize Vibe Task Manager configuration, services will use fallback values'); } } export const vibeTaskManagerExecutor = async (params, config, context) => { const sessionId = context?.sessionId || 'unknown-session'; try { logger.info({ sessionId, params }, 'Vibe Task Manager execution started'); await initializeVibeTaskManagerConfig(); await ensureAgentRegistration(sessionId, context); const validatedParams = vibeTaskManagerInputSchema.parse(params); const { command, projectName, taskId, description, options, input } = validatedParams; if (isNaturalLanguageInput(params)) { return await handleNaturalLanguageInput(input || description || '', config, context); } if (!command) { return { content: [{ type: "text", text: "Validation error: command is required. Please specify one of: create, list, run, status, refine, decompose" }], isError: true }; } switch (command) { case 'create': return await handleCreateCommand(projectName, description, options, config, sessionId); case 'list': return await handleListCommand(options, sessionId); case 'run': return await handleRunCommand(taskId, options, config, sessionId); case 'status': return await handleStatusCommand(projectName, taskId, sessionId); case 'refine': return await handleRefineCommand(taskId, description, config, sessionId); case 'decompose': return await handleDecomposeCommand(taskId || projectName, description, config, sessionId); default: return { content: [{ type: "text", text: `Validation error: Unknown command '${command}'. Valid commands are: create, list, run, status, refine, decompose` }], isError: true }; } } catch (error) { logger.error({ err: error, sessionId, params }, 'Vibe Task Manager execution failed'); if (error instanceof z.ZodError) { return { content: [{ type: "text", text: `Validation error: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}` }], isError: true }; } return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }; function inferProjectComplexity(projectContext) { if (!projectContext) return 'medium'; let complexityScore = 0; const languages = projectContext.languages || []; const frameworks = projectContext.frameworks || []; const tools = projectContext.tools || []; complexityScore += languages.length * 0.5; complexityScore += frameworks.length * 1; complexityScore += tools.length * 0.3; const description = (projectContext.description || '').toLowerCase(); const complexityKeywords = [ 'microservice', 'distributed', 'scalable', 'enterprise', 'architecture', 'system', 'api', 'integration', 'performance', 'security', 'database', 'migration' ]; const keywordMatches = complexityKeywords.filter(keyword => description.includes(keyword)).length; complexityScore += keywordMatches * 0.5; if (projectContext.complexity) { const explicitComplexity = (projectContext.complexity || '').toLowerCase(); if (explicitComplexity === 'high' || explicitComplexity === 'complex') return 'high'; if (explicitComplexity === 'low' || explicitComplexity === 'simple') return 'low'; } if (complexityScore >= 4) return 'high'; if (complexityScore >= 2) return 'medium'; return 'low'; } async function waitForDecompositionCompletion(decompositionService, sessionId, maxWaitTime, projectComplexity = 'medium') { if (!maxWaitTime) { const complexityTimeouts = { low: 300000, medium: 600000, high: 900000 }; maxWaitTime = complexityTimeouts[projectComplexity]; } const { AdaptiveTimeoutManager } = await import('./services/adaptive-timeout-manager.js'); const timeoutManager = AdaptiveTimeoutManager.getInstance(); const result = await timeoutManager.executeWithTimeout(`decomposition-${sessionId}`, async (cancellationToken, progressCallback) => { const startTime = Date.now(); let lastProgressUpdate = Date.now(); const tasksFound = 0; while (!cancellationToken.isCancelled) { const session = decompositionService.getSession(sessionId); if (!session) { throw new Error('Decomposition session not found'); } if (session.status === 'completed') { const results = decompositionService.getResults(sessionId); progressCallback({ completed: results.length, total: results.length, stage: 'completed', lastUpdate: new Date(), estimatedTimeRemaining: 0 }); return results; } if (session.status === 'failed') { throw new Error(session.error || 'Decomposition failed'); } const currentTime = Date.now(); if (currentTime - lastProgressUpdate > 2000) { let estimatedProgress = 0; let stage = 'initializing'; if (session.status === 'in_progress') { stage = 'processing'; const elapsedTime = currentTime - startTime; estimatedProgress = Math.min(0.8, elapsedTime / maxWaitTime); } const estimatedTotal = Math.max(1, tasksFound || 1); const estimatedCompleted = Math.floor(estimatedProgress * estimatedTotal); const remainingTime = Math.max(0, maxWaitTime - (currentTime - startTime)); progressCallback({ completed: estimatedCompleted, total: estimatedTotal, stage, lastUpdate: new Date(), estimatedTimeRemaining: remainingTime }); lastProgressUpdate = currentTime; } await new Promise(resolve => setTimeout(resolve, 1000)); } throw new Error('Decomposition cancelled'); }, { baseTimeoutMs: maxWaitTime, maxTimeoutMs: Math.max(maxWaitTime, 300000), progressCheckIntervalMs: 5000, exponentialBackoffFactor: 1.5, maxRetries: 2, partialResultThreshold: 0.5 }, (_currentState) => { try { const session = decompositionService.getSession(sessionId); if (session && session.status === 'in_progress') { const partialResults = decompositionService.getResults(sessionId); return partialResults.length > 0 ? partialResults : undefined; } } catch { } return undefined; }); if (result.success && result.result) { return result.result; } if (result.partialResult && Array.isArray(result.partialResult) && result.partialResult.length > 0) { logger.warn({ sessionId, partialResultCount: result.partialResult.length, totalDuration: result.totalDuration }, 'Using partial decomposition results due to timeout'); return result.partialResult; } throw new Error(result.error || 'Decomposition timeout'); } async function handleCreateCommand(projectName, description, options, config, sessionId) { logger.info({ sessionId, projectName }, 'Creating new project'); if (!projectName) { return { content: [{ type: "text", text: "Error: Project name is required for create command" }], isError: true }; } if (!description) { return { content: [{ type: "text", text: "Error: Project description is required for create command" }], isError: true }; } try { const jobId = jobManager.createJob('vibe-task-manager', { projectName, description, options, sessionId }); setTimeout(async () => { try { jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Starting project creation...'); const projectOps = ProjectOperations.getInstance(); const result = await projectOps.createProject({ name: projectName, description, techStack: options?.techStack, tags: options?.tags, rootPath: options?.rootPath }, sessionId); if (result.success && result.data) { const outputDir = await getBaseOutputDir(); const projectOutputPath = path.join(outputDir, 'projects', result.data.id); jobManager.setJobResult(jobId, { content: [{ type: "text", text: `✅ Project "${projectName}" created successfully!\n\n` + `Project ID: ${result.data.id}\n` + `Description: ${description}\n` + `Status: ${result.data.status}\n` + `Output Directory: ${projectOutputPath}\n\n` + `You can now decompose this project into tasks using:\n` + `"Decompose my project into development tasks"` }] }); } else { jobManager.setJobResult(jobId, { content: [{ type: "text", text: `Error: ${result.error || 'Unknown error during project creation'}` }], isError: true }); } } catch (error) { logger.error({ err: error, jobId, projectName }, 'Project creation failed'); jobManager.setJobResult(jobId, { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : 'Project creation failed'}` }], isError: true }); } }, 100); return { content: [{ type: "text", text: `🚀 Project creation started for "${projectName}"!\n\n` + `Job ID: ${jobId}\n` + `Status: Processing...\n\n` + `Use 'get-job-result' with job ID '${jobId}' to check progress and get the final result.` }], jobId }; } catch (error) { logger.error({ err: error, sessionId, projectName }, 'Failed to start project creation'); return { content: [{ type: "text", text: `Error starting project creation: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } async function handleListCommand(options, sessionId) { logger.info({ sessionId, options }, 'Listing projects'); try { const { getProjectOperations } = await import('./core/operations/project-operations.js'); const projectOps = getProjectOperations(); const queryParams = {}; if (options?.status) queryParams.status = options.status; if (options?.tags) queryParams.tags = options.tags; if (options?.limit) queryParams.limit = options.limit; const listResult = await projectOps.listProjects(queryParams); if (!listResult.success) { return { content: [{ type: "text", text: `❌ Failed to list projects: ${listResult.error}` }], isError: true }; } const projects = listResult.data; if (projects.length === 0) { return { content: [{ type: "text", text: `📋 **No projects found.**\n\n` + `You haven't created any projects yet.\n\n` + `Use the "create" command to get started:\n` + `• \`vibe-task-manager create "My Project" "Project description"\`\n` + `• Or try natural language: "Create a project for building a todo app"` }] }; } const projectList = projects .map((p) => `• **${p.name}** (${p.status}) - ID: ${p.id}\n ${p.description || 'No description'}\n Created: ${p.metadata?.createdAt ? new Date(p.metadata.createdAt).toLocaleDateString() : 'Unknown'}`) .join('\n\n'); return { content: [{ type: "text", text: `📋 **Your Projects:**\n\n${projectList}\n\n` + `Total: ${projects.length} project${projects.length !== 1 ? 's' : ''}\n\n` + `**Next Steps:**\n` + `• Use "decompose" to break down a project into tasks\n` + `• Use "status" to check project details\n` + `• Try natural language: "Show me details for [project name]"` }] }; } catch (error) { logger.error({ err: error, sessionId }, 'Failed to list projects'); return { content: [{ type: "text", text: `❌ Error listing projects: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } async function handleRunCommand(taskId, options, config, sessionId) { logger.info({ sessionId, taskId, options }, 'Running task'); if (!taskId) { return { content: [{ type: "text", text: "Error: Task ID is required for run command" }], isError: true }; } try { const jobId = jobManager.createJob('vibe-task-manager', { taskId, options, sessionId }); setTimeout(async () => { try { jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Starting task execution...'); const { AgentOrchestrator } = await import('./services/agent-orchestrator.js'); const orchestrator = AgentOrchestrator.getInstance(); const { getTaskOperations } = await import('./core/operations/task-operations.js'); const taskOps = getTaskOperations(); const taskResult = await taskOps.getTask(taskId); if (!taskResult.success) { jobManager.setJobResult(jobId, { content: [{ type: "text", text: `❌ **Task Not Found**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `❗ **Error**: ${taskResult.error || 'Task not found in storage'}\n\n` + `**Possible Solutions:**\n` + `• Verify the task ID is correct\n` + `• Use "list" to see available tasks\n` + `• Use "decompose" to create tasks from a project\n` + `• Check if the task was created successfully` }], isError: true }); return; } const task = taskResult.data; const { getProjectOperations } = await import('./core/operations/project-operations.js'); const projectOps = getProjectOperations(); let projectContext; if (task.projectId && task.projectId !== 'unknown') { const projectResult = await projectOps.getProject(task.projectId); if (projectResult.success && projectResult.data) { const project = projectResult.data; const { ProjectAnalyzer } = await import('./utils/project-analyzer.js'); const projectAnalyzer = ProjectAnalyzer.getInstance(); const projectPath = project.rootPath || process.cwd(); let languages; let frameworks; let tools; if (project.techStack?.languages?.length) { languages = project.techStack.languages; } else { try { languages = await projectAnalyzer.detectProjectLanguages(projectPath); } catch (error) { logger.warn({ error, projectId: project.id }, 'Language detection failed, using fallback'); languages = ['typescript']; } } if (project.techStack?.frameworks?.length) { frameworks = project.techStack.frameworks; } else { try { frameworks = await projectAnalyzer.detectProjectFrameworks(projectPath); } catch (error) { logger.warn({ error, projectId: project.id }, 'Framework detection failed, using fallback'); frameworks = ['node.js']; } } if (project.techStack?.tools?.length) { tools = project.techStack.tools; } else { try { tools = await projectAnalyzer.detectProjectTools(projectPath); } catch (error) { logger.warn({ error, projectId: project.id }, 'Tools detection failed, using fallback'); tools = ['npm']; } } projectContext = { projectId: project.id, projectPath, projectName: project.name, description: project.description || 'No description available', languages, frameworks, buildTools: tools, tools: [], configFiles: ['package.json'], entryPoints: ['src/index.ts'], architecturalPatterns: ['mvc'], existingTasks: [], codebaseSize: 'medium', teamSize: 1, complexity: 'medium', codebaseContext: { relevantFiles: [], contextSummary: project.description || 'No description available', gatheringMetrics: { searchTime: 0, readTime: 0, scoringTime: 0, totalTime: 0, cacheHitRate: 0 }, totalContextSize: 0, averageRelevance: 0 }, structure: { sourceDirectories: ['src'], testDirectories: ['tests'], docDirectories: ['docs'], buildDirectories: ['dist'] }, dependencies: { production: [], development: [], external: [] }, metadata: { createdAt: project.metadata.createdAt, updatedAt: project.metadata.updatedAt, version: '1.0.0', source: 'hybrid' } }; } else { logger.warn({ taskId, projectId: task.projectId }, 'Project not found, using dynamic detection'); const securityConfig = getUnifiedSecurityConfig().getConfig(); const projectPath = securityConfig.allowedReadDirectory; projectContext = await createDynamicProjectContext(projectPath); projectContext.projectName = task.projectId; projectContext.description = 'Project context dynamically detected'; } } else { const securityConfig = getUnifiedSecurityConfig().getConfig(); const projectPath = securityConfig.allowedReadDirectory; projectContext = await createDynamicProjectContext(projectPath); } const executionOptions = { timeout: options?.timeout || 300000, maxRetries: options?.maxRetries || 3, enableMonitoring: true, priority: options?.priority || 'medium' }; const result = await orchestrator.executeTask(task, projectContext, executionOptions); if (result.success) { jobManager.setJobResult(jobId, { content: [{ type: "text", text: `✅ **Task Execution Completed Successfully!**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `📊 **Status**: ${result.status}\n` + `⏱️ **Duration**: ${result.metadata?.totalDuration || 'N/A'}ms\n` + `🤖 **Agent**: ${result.assignment?.agentId || 'Unknown'}\n` + `📝 **Result**: ${result.message || 'Task completed successfully'}\n\n` + `**Execution Details:**\n` + `• Start Time: ${result.startTime?.toISOString() || 'N/A'}\n` + `• End Time: ${result.endTime?.toISOString() || 'N/A'}\n` + `• Retry Count: ${result.metadata?.attempts || 0}\n\n` + `**Next Steps:**\n` + `• Review the task results\n` + `• Check for any follow-up tasks\n` + `• Use "status" to monitor progress` }] }); } else { jobManager.setJobResult(jobId, { content: [{ type: "text", text: `❌ **Task Execution Failed**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `📊 **Status**: ${result.status}\n` + `❗ **Error**: ${result.error || 'Unknown error'}\n` + `⏱️ **Duration**: ${result.metadata?.totalDuration || 'N/A'}ms\n` + `🔄 **Retry Count**: ${result.metadata?.attempts || 0}\n\n` + `**Troubleshooting:**\n` + `• Check task requirements and dependencies\n` + `• Verify agent availability\n` + `• Try running with different options\n` + `• Use "refine" to improve task definition` }], isError: true }); } } catch (error) { logger.error({ err: error, jobId, taskId }, 'Task execution failed'); jobManager.setJobResult(jobId, { content: [{ type: "text", text: `❌ **Task Execution Error**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `❗ **Error**: ${error instanceof Error ? error.message : 'Unknown error'}\n\n` + `**Possible Solutions:**\n` + `• Verify the task ID is correct\n` + `• Check if agents are available\n` + `• Try again with different execution options` }], isError: true }); } }, 100); return { content: [{ type: "text", text: `🚀 **Task Execution Started!**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `📋 **Job ID**: ${jobId}\n` + `⏱️ **Status**: Processing...\n\n` + `**Execution Options:**\n` + `• Timeout: ${options?.timeout || 300000}ms\n` + `• Max Retries: ${options?.maxRetries || 3}\n` + `• Priority: ${options?.priority || 'medium'}\n\n` + `Use 'get-job-result' with job ID '${jobId}' to check progress and get the final result.` }], jobId }; } catch (error) { logger.error({ err: error, sessionId, taskId }, 'Failed to start task execution'); return { content: [{ type: "text", text: `❌ Error starting task execution: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } async function handleStatusCommand(projectName, taskId, sessionId) { logger.info({ sessionId, projectName, taskId }, 'Checking status'); try { if (projectName) { const { getProjectOperations } = await import('./core/operations/project-operations.js'); const projectOps = getProjectOperations(); const projectResult = await projectOps.getProject(projectName); if (!projectResult.success) { return { content: [{ type: "text", text: `❌ Project not found: ${projectName}\n\n` + `**Available Commands:**\n` + `• Use "list" to see all projects\n` + `• Use "create" to create a new project` }], isError: true }; } const project = projectResult.data; return { content: [{ type: "text", text: `📊 **Project Status: ${project.name}**\n\n` + `🆔 **ID**: ${project.id}\n` + `📝 **Description**: ${project.description || 'No description'}\n` + `📊 **Status**: ${project.status}\n` + `🏷️ **Tags**: ${project.metadata.tags?.join(', ') || 'None'}\n` + `📅 **Created**: ${project.metadata.createdAt.toLocaleDateString()}\n` + `👤 **Created By**: ${project.metadata.createdBy}\n\n` + `**Tech Stack:**\n` + `• Languages: ${project.techStack?.languages?.join(', ') || 'Not specified'}\n` + `• Frameworks: ${project.techStack?.frameworks?.join(', ') || 'Not specified'}\n` + `• Tools: ${project.techStack?.tools?.join(', ') || 'Not specified'}\n\n` + `**Next Steps:**\n` + `• Use "decompose" to break down into tasks\n` + `• Use "list" to see all projects\n` + `• Try natural language: "Show me tasks for ${project.name}"` }] }; } else if (taskId) { const { AgentOrchestrator } = await import('./services/agent-orchestrator.js'); const orchestrator = AgentOrchestrator.getInstance(); const assignments = orchestrator.getAssignments(); const taskAssignment = assignments.find(a => a.taskId === taskId); if (!taskAssignment) { return { content: [{ type: "text", text: `❌ Task not found or not currently active: ${taskId}\n\n` + `**Possible Reasons:**\n` + `• Task has not been started yet\n` + `• Task has already completed\n` + `• Task ID is incorrect\n\n` + `**Available Commands:**\n` + `• Use "run ${taskId}" to execute the task\n` + `• Use "list" to see available projects\n` + `• Use "decompose" to create tasks from a project` }], isError: true }; } const agents = orchestrator.getAgents(); const agent = agents.find(a => a.id === taskAssignment.agentId); const task = taskAssignment.task; return { content: [{ type: "text", text: `🎯 **Task Status: ${task.title}**\n\n` + `🆔 **Task ID**: ${task.id}\n` + `📝 **Description**: ${task.description}\n` + `📊 **Task Status**: ${task.status}\n` + `🔥 **Priority**: ${task.priority}\n` + `📂 **Type**: ${task.type}\n` + `🏗️ **Project**: ${task.projectId}\n` + `⏱️ **Estimated Hours**: ${task.estimatedHours}h\n` + `⏰ **Actual Hours**: ${task.actualHours}h\n\n` + `**Assignment Details:**\n` + `🤖 **Assigned Agent**: ${agent?.name || agent?.id || 'Unknown'}\n` + `📅 **Assigned At**: ${taskAssignment.assignedAt.toISOString()}\n` + `📊 **Assignment Status**: ${taskAssignment.status}\n` + `⏰ **Expected Completion**: ${taskAssignment.expectedCompletionAt.toISOString()}\n` + `🔄 **Attempts**: ${taskAssignment.attempts}\n` + `📊 **Last Status Update**: ${taskAssignment.lastStatusUpdate.toISOString()}\n\n` + `**Task Progress:**\n` + `• Created: ${task.createdAt.toLocaleDateString()}\n` + `• Last Updated: ${task.updatedAt.toLocaleDateString()}\n` + `• Tags: ${task.tags.join(', ')}\n` + `• Dependencies: ${task.dependencies.length} task(s)\n` + `• Acceptance Criteria: ${task.acceptanceCriteria.length} item(s)\n\n` + `**Agent Details:**\n` + `• Agent ID: ${taskAssignment.agentId}\n` + `• Agent Status: ${agent?.status || 'Unknown'}\n` + `• Agent Capabilities: ${agent?.capabilities?.join(', ') || 'Unknown'}\n` + `• Current Tasks: ${agent?.currentTasks?.length || 0}\n\n` + `**Next Steps:**\n` + `• Monitor task progress\n` + `• Use "refine ${taskId}" to update task requirements\n` + `• Check agent status for more details` }] }; } else { const { getProjectOperations } = await import('./core/operations/project-operations.js'); const projectOps = getProjectOperations(); const { AgentOrchestrator } = await import('./services/agent-orchestrator.js'); const orchestrator = AgentOrchestrator.getInstance(); const projectsResult = await projectOps.listProjects({ limit: 5 }); const projects = projectsResult.success ? projectsResult.data : []; const activeAssignments = orchestrator.getAssignments(); const agents = orchestrator.getAgents(); return { content: [{ type: "text", text: `📊 **Vibe Task Manager Status Overview**\n\n` + `**Projects:**\n` + `• Total Projects: ${projects.length}\n` + `• Recent Projects: ${projects.slice(0, 3).map(p => p.name).join(', ') || 'None'}\n\n` + `**Tasks:**\n` + `• Active Tasks: ${activeAssignments.length}\n` + `• In Progress Tasks: ${activeAssignments.filter(a => a.status === 'in_progress').length}\n` + `• Assigned Tasks: ${activeAssignments.filter(a => a.status === 'assigned').length}\n\n` + `**Agents:**\n` + `• Registered Agents: ${agents.length}\n` + `• Available Agents: ${agents.filter(a => a.status === 'available').length}\n` + `• Busy Agents: ${agents.filter(a => a.status === 'busy').length}\n\n` + `**Quick Actions:**\n` + `• Use "list" to see all projects\n` + `• Use "create [name] [description]" to start a new project\n` + `• Use "decompose [project]" to break down a project\n` + `• Try natural language: "Create a project for building a web app"` }] }; } } catch (error) { logger.error({ err: error, sessionId, projectName, taskId }, 'Failed to check status'); return { content: [{ type: "text", text: `❌ Error checking status: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } async function handleRefineCommand(taskId, description, config, sessionId) { logger.info({ sessionId, taskId, description }, 'Refining task'); if (!taskId) { return { content: [{ type: "text", text: "Error: Task ID is required for refine command" }], isError: true }; } if (!description) { return { content: [{ type: "text", text: "Error: Refinement description is required for refine command" }], isError: true }; } try { const jobId = jobManager.createJob('vibe-task-manager', { taskId, description, sessionId }); setTimeout(async () => { try { jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Starting task refinement...'); const { getTaskRefinementService } = await import('./services/task-refinement-service.js'); const refinementService = getTaskRefinementService(); const refinementParams = { description: description, }; const refinementResult = await refinementService.refineTask(taskId, refinementParams, sessionId); if (refinementResult.success && refinementResult.refinedTask) { const refinedTask = refinementResult.refinedTask; jobManager.setJobResult(jobId, { content: [{ type: "text", text: `✅ **Task Refinement Completed Successfully!**\n\n` + `🎯 **Original Task ID**: ${taskId}\n` + `🔄 **Refinement Type**: ${refinementResult.wasDecomposed ? 'Refine and Decompose' : 'Simple Refinement'}\n` + `📝 **Feedback Applied**: ${description}\n\n` + `**Refined Task Details:**\n` + `• **Title**: ${refinedTask.title}\n` + `• **Description**: ${refinedTask.description}\n` + `• **Priority**: ${refinedTask.priority}\n` + `• **Estimated Hours**: ${refinedTask.estimatedHours}h\n` + `• **Type**: ${refinedTask.type}\n\n` + `**Changes Made:**\n` + `• Updated task description based on feedback\n` + `• Adjusted priority and estimates if needed\n` + `• Enhanced acceptance criteria\n` + `• Improved task clarity and specificity\n\n` + `**Next Steps:**\n` + `• Review the refined task details\n` + `• Use "run ${taskId}" to execute the refined task\n` + `• Use "status ${taskId}" to monitor progress\n` + `• Apply additional refinements if needed` }] }); } else { jobManager.setJobResult(jobId, { content: [{ type: "text", text: `❌ **Task Refinement Failed**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `❗ **Error**: ${refinementResult.error || 'Unknown error during refinement'}\n\n` + `**Possible Solutions:**\n` + `• Check if the task ID is correct\n` + `• Provide more specific refinement feedback\n` + `• Try breaking down the refinement into smaller changes\n` + `• Use "status" to verify task exists` }], isError: true }); } } catch (error) { logger.error({ err: error, jobId, taskId }, 'Task refinement failed'); jobManager.setJobResult(jobId, { content: [{ type: "text", text: `❌ **Task Refinement Error**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `❗ **Error**: ${error instanceof Error ? error.message : 'Unknown error'}\n\n` + `**Troubleshooting:**\n` + `• Verify the task ID exists\n` + `• Check refinement description clarity\n` + `• Try with simpler refinement requests\n` + `• Use "list" to see available tasks` }], isError: true }); } }, 100); return { content: [{ type: "text", text: `🔄 **Task Refinement Started!**\n\n` + `🎯 **Task ID**: ${taskId}\n` + `📋 **Job ID**: ${jobId}\n` + `📝 **Refinement**: ${description}\n` + `⏱️ **Status**: Processing...\n\n` + `**Refinement Process:**\n` + `• Analyzing current task structure\n` + `• Applying your feedback and requirements\n` + `• Optimizing task clarity and specificity\n` + `• Updating estimates and priorities\n\n` + `Use 'get-job-result' with job ID '${jobId}' to check progress and get the refined task.` }], jobId }; } catch (error) { logger.error({ err: error, sessionId, taskId }, 'Failed to start task refinement'); return { content: [{ type: "text", text: `❌ Error starting task refinement: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } async function validateProjectForDecomposition(project) { const errors = []; const warnings = []; const recommendations = []; if (!project.id) { errors.push('Project missing required ID field'); } if (typeof project.name !== 'string' || project.name.trim().length === 0) { errors.push('Project missing required name field'); } if (typeof project.description !== 'string' || project.description.trim().length === 0) { warnings.push('Project missing description - decomposition may be less accurate'); recommendations.push('Add a detailed project description for better task generation'); } if (!project.techStack) { warnings.push('Project missing tech stack information'); recommendations.push('Add tech stack details (languages, frameworks, tools) for more accurate decomposition'); } else { if (!project.techStack.languages || project.techStack.languages.length === 0) { warnings.push('No programming