Skip to main content
Glama
refine.ts14.7 kB
import { Command } from 'commander'; import { getTaskRefinementService, TaskRefinementParams, RedecompositionParams } from '../../services/task-refinement-service.js'; import { CLIUtils } from './index.js'; import { TaskPriority, TaskType } from '../../types/task.js'; import logger from '../../../../logger.js'; /** * Task refinement command */ export const refineCommand = new Command('refine') .description('Refine and re-decompose tasks') .configureOutput({ writeOut: (str) => process.stdout.write(str), writeErr: (str) => process.stderr.write(str) }); /** * Refine task subcommand */ const refineTaskCommand = new Command('task') .description('Refine a task with new parameters') .argument('<taskId>', 'Task ID to refine') .option('-t, --title <title>', 'New task title') .option('-d, --description <description>', 'New task description') .option('--type <type>', 'New task type (development, testing, documentation, research)') .option('--priority <priority>', 'New priority (low, medium, high, critical)') .option('--hours <hours>', 'New estimated hours') .option('--files <files>', 'New file paths (comma-separated)') .option('--criteria <criteria>', 'New acceptance criteria (comma-separated)') .option('--tags <tags>', 'New tags (comma-separated)') .option('--dependencies <deps>', 'New dependencies (comma-separated)') .option('-f, --format <format>', 'Output format (table, json, yaml)', 'table') .action(async (taskId, options) => { try { logger.info({ command: 'refine task', taskId, options }, 'Refining task'); // Prepare refinement parameters const refinements: TaskRefinementParams = {}; if (options.title) refinements.title = options.title; if (options.description) refinements.description = options.description; if (options.type) { const validTypes: TaskType[] = ['development', 'testing', 'documentation', 'research']; if (!validTypes.includes(options.type as TaskType)) { CLIUtils.error(`Invalid type: ${options.type}. Must be one of: ${validTypes.join(', ')}`); } refinements.type = options.type as TaskType; } if (options.priority) { const validPriorities: TaskPriority[] = ['low', 'medium', 'high', 'critical']; if (!validPriorities.includes(options.priority as TaskPriority)) { CLIUtils.error(`Invalid priority: ${options.priority}. Must be one of: ${validPriorities.join(', ')}`); } refinements.priority = options.priority as TaskPriority; } if (options.hours) { const estimatedHours = parseFloat(options.hours); if (isNaN(estimatedHours) || estimatedHours < 0) { CLIUtils.error('Estimated hours must be a non-negative number'); } refinements.estimatedHours = estimatedHours; } if (options.files) refinements.filePaths = CLIUtils.parseTags(options.files); if (options.criteria) refinements.acceptanceCriteria = CLIUtils.parseTags(options.criteria); if (options.tags) refinements.tags = CLIUtils.parseTags(options.tags); if (options.dependencies) refinements.dependencies = CLIUtils.parseTags(options.dependencies); // Check if any refinements provided if (Object.keys(refinements).length === 0) { CLIUtils.error('No refinement parameters provided'); } // Refine task const refinementService = getTaskRefinementService(); const result = await refinementService.refineTask(taskId, refinements, 'cli-user'); if (!result.success) { CLIUtils.error(`Failed to refine task: ${result.error}`); } CLIUtils.success(`Task refined: ${taskId}`); // Display results console.log('\n--- Refinement Summary ---'); const summaryData = { 'Original Task': result.originalTask.id, 'Was Decomposed': result.wasDecomposed ? 'Yes' : 'No', 'Changes Made': result.changes.length, 'Operation': result.metadata.operation, 'Refined By': result.metadata.refinedBy, 'Timestamp': CLIUtils.formatDate(result.metadata.timestamp) }; console.log(CLIUtils.formatOutput(summaryData, options.format as 'table' | 'json' | 'yaml')); // Show changes if (result.changes.length > 0) { console.log('\n--- Changes Made ---'); result.changes.forEach((change, index) => { console.log(`${index + 1}. ${change}`); }); } // Show decomposed tasks if any if (result.wasDecomposed && result.decomposedTasks) { console.log('\n--- Decomposed Tasks ---'); const decomposedData = result.decomposedTasks.map(task => ({ ID: task.id, Title: CLIUtils.truncate(task.title, 40), Type: task.type, Priority: task.priority, 'Est. Hours': task.estimatedHours, 'Files': task.filePaths.length })); console.log(CLIUtils.formatOutput(decomposedData, options.format as 'table' | 'json' | 'yaml')); CLIUtils.info(`Task was decomposed into ${result.decomposedTasks.length} sub-tasks`); } else if (result.refinedTask) { console.log('\n--- Refined Task ---'); const refinedData = { ID: result.refinedTask.id, Title: result.refinedTask.title, Type: result.refinedTask.type, Priority: result.refinedTask.priority, 'Est. Hours': result.refinedTask.estimatedHours, 'Updated At': CLIUtils.formatDate(result.refinedTask.updatedAt) }; console.log(CLIUtils.formatOutput(refinedData, options.format as 'table' | 'json' | 'yaml')); } } catch (error) { logger.error({ err: error, taskId }, 'Failed to refine task'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); /** * Re-decompose task subcommand */ const redecomposeCommand = new Command('decompose') .description('Force re-decomposition of a task') .argument('<taskId>', 'Task ID to re-decompose') .requiredOption('-r, --reason <reason>', 'Reason for re-decomposition') .option('--requirements <requirements>', 'New requirements description') .option('--changes <changes>', 'Context changes (comma-separated)') .option('--force', 'Force decomposition even if task seems atomic') .option('-f, --format <format>', 'Output format (table, json, yaml)', 'table') .action(async (taskId, options) => { try { logger.info({ command: 'refine decompose', taskId, options }, 'Re-decomposing task'); // Prepare re-decomposition parameters const params: RedecompositionParams = { reason: options.reason, newRequirements: options.requirements, contextChanges: CLIUtils.parseTags(options.changes), forceDecomposition: options.force }; // Re-decompose task const refinementService = getTaskRefinementService(); const result = await refinementService.redecomposeTask(taskId, params, 'cli-user'); if (!result.success) { CLIUtils.error(`Failed to re-decompose task: ${result.error}`); } CLIUtils.success(`Task re-decomposed: ${taskId}`); // Display results console.log('\n--- Re-decomposition Summary ---'); const summaryData = { 'Original Task': result.originalTask.id, 'Was Decomposed': result.wasDecomposed ? 'Yes' : 'No', 'Reason': params.reason, 'Sub-tasks Created': result.decomposedTasks?.length || 0, 'Operation': result.metadata.operation, 'Refined By': result.metadata.refinedBy, 'Timestamp': CLIUtils.formatDate(result.metadata.timestamp) }; console.log(CLIUtils.formatOutput(summaryData, options.format as 'table' | 'json' | 'yaml')); // Show decomposed tasks if (result.decomposedTasks && result.decomposedTasks.length > 0) { console.log('\n--- Decomposed Tasks ---'); const decomposedData = result.decomposedTasks.map(task => ({ ID: task.id, Title: CLIUtils.truncate(task.title, 40), Type: task.type, Priority: task.priority, 'Est. Hours': task.estimatedHours, 'Files': task.filePaths.length, 'Criteria': task.acceptanceCriteria.length })); console.log(CLIUtils.formatOutput(decomposedData, options.format as 'table' | 'json' | 'yaml')); CLIUtils.info(`Created ${result.decomposedTasks.length} atomic sub-tasks`); } else { CLIUtils.warning('No sub-tasks were created. Task may already be atomic.'); } } catch (error) { logger.error({ err: error, taskId }, 'Failed to re-decompose task'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); /** * Analyze task complexity subcommand */ const analyzeCommand = new Command('analyze') .description('Analyze task complexity and get refinement recommendations') .argument('<taskId>', 'Task ID to analyze') .option('-f, --format <format>', 'Output format (table, json, yaml)', 'table') .action(async (taskId, options) => { try { logger.info({ command: 'refine analyze', taskId, options }, 'Analyzing task complexity'); // Analyze task complexity const refinementService = getTaskRefinementService(); const result = await refinementService.analyzeTaskComplexity(taskId); if (!result.success) { CLIUtils.error(`Failed to analyze task: ${result.error}`); } const analysis = result.data!; // Display analysis results console.log('--- Task Complexity Analysis ---'); const analysisData = { 'Task ID': taskId, 'Complexity Level': analysis.complexity.toUpperCase(), 'Should Decompose': analysis.shouldDecompose ? 'Yes' : 'No', 'Estimated Sub-tasks': analysis.estimatedSubTasks, 'Recommendations Count': analysis.recommendations.length }; console.log(CLIUtils.formatOutput(analysisData, options.format as 'table' | 'json' | 'yaml')); // Show recommendations if (analysis.recommendations.length > 0) { console.log('\n--- Recommendations ---'); analysis.recommendations.forEach((recommendation, index) => { console.log(`${index + 1}. ${recommendation}`); }); } // Provide action suggestions console.log('\n--- Suggested Actions ---'); if (analysis.shouldDecompose) { CLIUtils.warning(`This task has ${analysis.complexity} complexity and should be decomposed.`); console.log(`Run: vibe-tasks refine decompose ${taskId} -r "High complexity task"`); } else { CLIUtils.success(`This task has ${analysis.complexity} complexity and appears to be appropriately sized.`); } } catch (error) { logger.error({ err: error, taskId }, 'Failed to analyze task'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); /** * Bulk refine subcommand */ const bulkRefineCommand = new Command('bulk') .description('Bulk refine multiple tasks') .argument('<taskIds>', 'Comma-separated task IDs to refine') .option('-t, --title <title>', 'New task title (applied to all)') .option('--type <type>', 'New task type (applied to all)') .option('--priority <priority>', 'New priority (applied to all)') .option('--tags <tags>', 'New tags (applied to all, comma-separated)') .option('-f, --format <format>', 'Output format (table, json, yaml)', 'table') .action(async (taskIdsString, options) => { try { const taskIds = CLIUtils.parseTags(taskIdsString); if (taskIds.length === 0) { CLIUtils.error('No task IDs provided'); } logger.info({ command: 'refine bulk', taskIds, options }, 'Bulk refining tasks'); // Prepare refinement parameters const refinements: TaskRefinementParams = {}; if (options.title) refinements.title = options.title; if (options.type) refinements.type = options.type as TaskType; if (options.priority) refinements.priority = options.priority as TaskPriority; if (options.tags) refinements.tags = CLIUtils.parseTags(options.tags); // Check if any refinements provided if (Object.keys(refinements).length === 0) { CLIUtils.error('No refinement parameters provided'); } // Confirmation prompt const confirmed = await CLIUtils.confirm( `Are you sure you want to apply these refinements to ${taskIds.length} tasks?`, false ); if (!confirmed) { CLIUtils.info('Bulk refinement cancelled'); return; } // Bulk refine tasks const refinementService = getTaskRefinementService(); const results = await refinementService.bulkRefineTask(taskIds, refinements, 'cli-user'); // Display results summary const successCount = results.filter(r => r.success).length; const failureCount = results.length - successCount; const decomposedCount = results.filter(r => r.wasDecomposed).length; console.log('\n--- Bulk Refinement Summary ---'); const summaryData = { 'Total Tasks': results.length, 'Successful': successCount, 'Failed': failureCount, 'Decomposed': decomposedCount, 'Success Rate': `${Math.round((successCount / results.length) * 100)}%` }; console.log(CLIUtils.formatOutput(summaryData, options.format as 'table' | 'json' | 'yaml')); // Show detailed results console.log('\n--- Detailed Results ---'); const detailedData = results.map(result => ({ 'Task ID': result.originalTask.id || 'Unknown', 'Status': result.success ? 'Success' : 'Failed', 'Decomposed': result.wasDecomposed ? 'Yes' : 'No', 'Changes': result.changes.length, 'Error': result.error ? CLIUtils.truncate(result.error, 30) : 'None' })); console.log(CLIUtils.formatOutput(detailedData, options.format as 'table' | 'json' | 'yaml')); if (successCount > 0) { CLIUtils.success(`Successfully refined ${successCount} task(s)`); } if (failureCount > 0) { CLIUtils.warning(`Failed to refine ${failureCount} task(s)`); } } catch (error) { logger.error({ err: error, taskIdsString }, 'Failed to bulk refine tasks'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); // Add subcommands to refine command refineCommand.addCommand(refineTaskCommand); refineCommand.addCommand(redecomposeCommand); refineCommand.addCommand(analyzeCommand); refineCommand.addCommand(bulkRefineCommand);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshtechbro/vibe-coder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server