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
JavaScript
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