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.

175 lines (171 loc) 9.19 kB
import { Command } from 'commander'; import { PRDIntegrationService } from '../../integrations/prd-integration.js'; import { TaskListIntegrationService } from '../../integrations/task-list-integration.js'; import { getProjectOperations } from '../../core/operations/project-operations.js'; import { CLIUtils } from './index.js'; import logger from '../../../../logger.js'; export const parseCommand = new Command('parse') .description('Parse existing PRDs and task lists from generators') .configureHelp({ sortSubcommands: true }); const parsePRDCommand = new Command('prd') .description('Parse an existing PRD from prd-generator') .option('-p, --project <name>', 'Project name to filter PRDs') .option('-f, --file <path>', 'Specific PRD file path') .option('--format <format>', 'Output format (table|json|yaml)', 'table') .option('--create-project', 'Create project from PRD after parsing', false) .action(async (options) => { try { logger.info({ options }, 'Parsing PRD via CLI'); const prdService = PRDIntegrationService.getInstance(); let prdInfo; if (options.file) { CLIUtils.info(`Parsing PRD from: ${options.file}`); const result = await prdService.parsePRD(options.file); if (!result.success) { CLIUtils.error(`Failed to parse PRD: ${result.error}`); } prdInfo = result.prdData; } else { CLIUtils.info(`Detecting existing PRD${options.project ? ` for project "${options.project}"` : ''}...`); const detectedPRD = await prdService.detectExistingPRD(options.project); if (!detectedPRD) { CLIUtils.error(`No PRD found${options.project ? ` for project "${options.project}"` : ''}. Please ensure a PRD exists in the VibeCoderOutput/prd-generator/ directory.`); } CLIUtils.info(`Found PRD: ${detectedPRD.fileName}`); const result = await prdService.parsePRD(detectedPRD.filePath); if (!result.success) { CLIUtils.error(`Failed to parse PRD: ${result.error}`); } prdInfo = result.prdData; } CLIUtils.success('PRD parsed successfully!'); const displayData = { 'Project Name': prdInfo.metadata.projectName, 'File Path': prdInfo.metadata.filePath, 'File Size': `${(prdInfo.metadata.fileSize / 1024).toFixed(1)} KB`, 'Created At': CLIUtils.formatDate(prdInfo.metadata.createdAt), 'Features Count': prdInfo.features.length, 'Tech Stack': prdInfo.technical.techStack.slice(0, 3).join(', ') + (prdInfo.technical.techStack.length > 3 ? '...' : ''), 'Business Goals': prdInfo.overview.businessGoals.length, 'Product Goals': prdInfo.overview.productGoals.length }; console.log('\nPRD Details:'); console.log(CLIUtils.formatOutput(displayData, options.format)); if (prdInfo.features.length > 0) { console.log('\nFeatures:'); prdInfo.features.slice(0, 10).forEach((feature, index) => { console.log(` ${index + 1}. ${feature.title} (${feature.priority})`); }); if (prdInfo.features.length > 10) { console.log(` ... and ${prdInfo.features.length - 10} more features`); } } if (options.createProject) { CLIUtils.info('Creating project from PRD...'); const projectOperations = getProjectOperations(); const projectResult = await projectOperations.createProjectFromPRD(prdInfo, 'cli-user'); if (!projectResult.success) { CLIUtils.error(`Failed to create project from PRD: ${projectResult.error}`); } const project = projectResult.data; CLIUtils.success(`Project created: ${project.id} - ${project.name}`); } logger.info({ projectName: prdInfo.metadata.projectName }, 'PRD parsed successfully via CLI'); } catch (error) { logger.error({ err: error, options }, 'Failed to parse PRD via CLI'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); const parseTasksCommand = new Command('tasks') .description('Parse an existing task list from task-list-generator') .option('-p, --project <name>', 'Project name to filter task lists') .option('-f, --file <path>', 'Specific task list file path') .option('--format <format>', 'Output format (table|json|yaml)', 'table') .option('--create-project', 'Create project from task list after parsing', false) .action(async (options) => { try { logger.info({ options }, 'Parsing task list via CLI'); const taskListService = TaskListIntegrationService.getInstance(); let taskListInfo; if (options.file) { CLIUtils.info(`Parsing task list from: ${options.file}`); const result = await taskListService.parseTaskList(options.file); if (!result.success) { CLIUtils.error(`Failed to parse task list: ${result.error}`); } taskListInfo = result.taskListData; } else { CLIUtils.info(`Detecting existing task list${options.project ? ` for project "${options.project}"` : ''}...`); const detectedTaskList = await taskListService.detectExistingTaskList(options.project); if (!detectedTaskList) { CLIUtils.error(`No task list found${options.project ? ` for project "${options.project}"` : ''}. Please ensure a task list exists in the VibeCoderOutput/generated_task_lists/ directory.`); } CLIUtils.info(`Found task list: ${detectedTaskList.fileName}`); const result = await taskListService.parseTaskList(detectedTaskList.filePath); if (!result.success) { CLIUtils.error(`Failed to parse task list: ${result.error}`); } taskListInfo = result.taskListData; } CLIUtils.success('Task list parsed successfully!'); const displayData = { 'Project Name': taskListInfo.metadata.projectName, 'File Path': taskListInfo.metadata.filePath, 'File Size': `${(taskListInfo.metadata.fileSize / 1024).toFixed(1)} KB`, 'Created At': CLIUtils.formatDate(taskListInfo.metadata.createdAt), 'Total Tasks': taskListInfo.metadata.totalTasks, 'Phases': taskListInfo.metadata.phaseCount, 'Estimated Hours': taskListInfo.statistics.totalEstimatedHours, 'List Type': taskListInfo.metadata.listType }; console.log('\nTask List Details:'); console.log(CLIUtils.formatOutput(displayData, options.format)); if (taskListInfo.phases.length > 0) { console.log('\nPhases:'); taskListInfo.phases.forEach((phase, index) => { console.log(` ${index + 1}. ${phase.name} (${phase.tasks.length} tasks)`); }); } if (options.createProject) { CLIUtils.info('Creating project from task list...'); const projectOperations = getProjectOperations(); const projectResult = await projectOperations.createProjectFromTaskList(taskListInfo, 'cli-user'); if (!projectResult.success) { CLIUtils.error(`Failed to create project from task list: ${projectResult.error}`); } const project = projectResult.data; CLIUtils.success(`Project created: ${project.id} - ${project.name}`); const atomicTasks = await taskListService.convertToAtomicTasks(taskListInfo, project.id, 'default-epic', 'cli-user'); CLIUtils.info(`Created ${atomicTasks.length} atomic tasks`); } logger.info({ projectName: taskListInfo.metadata.projectName }, 'Task list parsed successfully via CLI'); } catch (error) { logger.error({ err: error, options }, 'Failed to parse task list via CLI'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); parseCommand.addCommand(parsePRDCommand); parseCommand.addCommand(parseTasksCommand); parseCommand.addHelpText('after', ` Examples: # Parse PRD files $ vibe-tasks parse prd --project "E-commerce Platform" --create-project $ vibe-tasks parse prd --file "/path/to/ecommerce-prd.md" $ vibe-tasks parse prd --project "My Web App" --format json # Parse task list files $ vibe-tasks parse tasks --project "Mobile App" --create-project $ vibe-tasks parse tasks --file "/path/to/mobile-task-list-detailed.md" $ vibe-tasks parse tasks --project "E-commerce Platform" --format yaml # Auto-discovery (searches VibeCoderOutput directories) $ vibe-tasks parse prd --project "My Project" $ vibe-tasks parse tasks --project "My Project" # Import with specific file paths $ vibe-tasks parse prd --file "VibeCoderOutput/prd-generator/ecommerce-prd.md" $ vibe-tasks parse tasks --file "VibeCoderOutput/generated_task_lists/mobile-task-list-detailed.md" `);