UNPKG

@sethdouglasford/claude-flow

Version:

Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology

522 lines (505 loc) 24.2 kB
import { Command } from "commander"; import { logger } from "../../core/logger.js"; import path from "path"; import fs from "fs-extra"; import chalk from "chalk"; import { TaskAnalyzer } from "../../analysis/task-analyzer.js"; import { generateId } from "../../utils/helpers.js"; export const analyzeCommand = new Command() .name("analyze") .description("Analyze tasks for complexity and generate intelligent breakdowns") .addHelpText("after", ` ${chalk.yellow("Examples:")} ${chalk.cyan("claude-flow analyze task")} "Implement user authentication" ${chalk.cyan("claude-flow analyze workflow")} examples/02-workflows/claude-workflow.json ${chalk.cyan("claude-flow analyze breakdown")} "Create REST API" --subtasks 5 ${chalk.cyan("claude-flow analyze project")} --tech-stack "Node.js,React,PostgreSQL" ${chalk.yellow("Task Analysis Features:")}${chalk.green("Complexity Scoring")} - AI-powered complexity assessment (1-10 scale) • ${chalk.green("Risk Assessment")} - Identify potential blockers and challenges • ${chalk.green("Skill Requirements")} - Determine required expertise levels • ${chalk.green("Time Estimation")} - Accurate hour estimates based on complexity • ${chalk.green("Breakdown Recommendations")} - Smart subtask generation • ${chalk.green("Dependency Analysis")} - Identify task relationships ${chalk.yellow("Integration:")} • Works with existing workflow files • Enhances swarm task decomposition • Supports project context analysis • Generates actionable subtasks with acceptance criteria `); // Analyze a single task analyzeCommand .command("task") .description("Analyze a single task for complexity and breakdown recommendations") .argument("<description>", "Task description to analyze") .option("--title <title>", "Task title (optional)") .option("--project-type <type>", "Project type context (web, mobile, api, etc.)") .option("--tech-stack <stack>", "Comma-separated technology stack") .option("--team-size <size>", "Team size (number)", parseInt) .option("--timeline <timeline>", "Project timeline context") .option("--output <file>", "Save analysis to file") .option("--format <format>", "Output format (json, markdown, table)", "table") .action(async (description, options) => { try { // Configure logger for clean text output await logger.configure({ level: "warn", format: "text", destination: "console", }); console.log(chalk.blue("🔍 Analyzing task complexity...")); console.log(); const analyzer = new TaskAnalyzer(); const taskId = generateId("task"); const title = options.title || description.slice(0, 50) + (description.length > 50 ? "..." : ""); const context = { projectType: options.projectType, techStack: options.techStack ? options.techStack.split(",").map((s) => s.trim()) : undefined, teamSize: options.teamSize, timeline: options.timeline }; const analysis = await analyzer.analyzeComplexity(taskId, title, description, context); displayTaskAnalysis(analysis, options.format); if (options.output) { await saveAnalysis(analysis, options.output, options.format); console.log(chalk.green(`✅ Analysis saved to ${options.output}`)); } // Show breakdown recommendation if (analysis.breakdownRecommendation !== "none") { console.log(); console.log(chalk.yellow("💡 Recommendation:")); console.log(` This task ${analysis.breakdownRecommendation === "required" ? "should" : "could"} be broken down into ${analysis.recommendedSubtasks} subtasks.`); console.log(` Run: ${chalk.cyan(`claude-flow analyze breakdown "${description}" --subtasks ${analysis.recommendedSubtasks}`)}`); } } catch (error) { logger.error("Error analyzing task:", error); console.error(chalk.red("❌ Failed to analyze task")); process.exit(1); } }); // Break down a task into subtasks analyzeCommand .command("breakdown") .description("Break down a complex task into manageable subtasks") .argument("<description>", "Task description to break down") .option("--title <title>", "Task title (optional)") .option("--subtasks <count>", "Number of subtasks to generate", parseInt) .option("--project-type <type>", "Project type context") .option("--tech-stack <stack>", "Comma-separated technology stack") .option("--constraints <constraints>", "Comma-separated constraints") .option("--preferences <preferences>", "Comma-separated preferences") .option("--output <file>", "Save breakdown to file") .option("--format <format>", "Output format (json, markdown, workflow)", "markdown") .action(async (description, options) => { try { // Configure logger for clean text output await logger.configure({ level: "warn", format: "text", destination: "console", }); console.log(chalk.blue("🔧 Breaking down task into subtasks...")); console.log(); const analyzer = new TaskAnalyzer(); const taskId = generateId("task"); const title = options.title || description.slice(0, 50) + (description.length > 50 ? "..." : ""); const context = { projectType: options.projectType, techStack: options.techStack ? options.techStack.split(",").map((s) => s.trim()) : undefined, constraints: options.constraints ? options.constraints.split(",").map((s) => s.trim()) : undefined, preferences: options.preferences ? options.preferences.split(",").map((s) => s.trim()) : undefined }; const breakdown = await analyzer.breakdownTask(taskId, title, description, options.subtasks, context); displayTaskBreakdown(breakdown, options.format); if (options.output) { await saveBreakdown(breakdown, options.output, options.format); console.log(chalk.green(`✅ Breakdown saved to ${options.output}`)); } } catch (error) { logger.error("Error breaking down task:", error); console.error(chalk.red("❌ Failed to break down task")); process.exit(1); } }); // Analyze workflow file analyzeCommand .command("workflow") .description("Analyze all tasks in a workflow file for complexity") .argument("<file>", "Path to workflow file") .option("--project-type <type>", "Project type context") .option("--tech-stack <stack>", "Comma-separated technology stack") .option("--team-experience <level>", "Team experience level (junior, mid, senior, mixed)", "mixed") .option("--output <file>", "Save analysis to file") .option("--format <format>", "Output format (json, markdown, summary)", "summary") .option("--breakdown", "Generate breakdowns for complex tasks") .action(async (file, options) => { try { // Configure logger for clean text output await logger.configure({ level: "warn", format: "text", destination: "console", }); const workflowPath = path.resolve(file); if (!await fs.pathExists(workflowPath)) { console.error(chalk.red(`❌ Workflow file not found: ${file}`)); process.exit(1); } console.log(chalk.blue(`🔍 Analyzing workflow: ${path.basename(file)}`)); console.log(); const workflow = await fs.readJson(workflowPath); const analyzer = new TaskAnalyzer(); const tasks = workflow.tasks.map(task => ({ id: task.id, title: task.name, description: task.description })); const context = { projectType: options.projectType, techStack: options.techStack ? options.techStack.split(",").map((s) => s.trim()) : undefined, teamExperience: options.teamExperience }; const analysis = await analyzer.analyzeTaskList(tasks, context); displayWorkflowAnalysis(workflow, analysis, options.format); // Generate breakdowns for complex tasks if requested if (options.breakdown) { console.log(); console.log(chalk.blue("🔧 Generating breakdowns for complex tasks...")); for (const taskId of analysis.recommendations.immediate) { const task = tasks.find(t => t.id === taskId); if (task) { console.log(); console.log(chalk.yellow(`Breaking down: ${task.title}`)); const breakdown = await analyzer.breakdownTask(task.id, task.title, task.description, undefined, context); displayTaskBreakdown(breakdown, "markdown"); } } } if (options.output) { await saveWorkflowAnalysis(workflow, analysis, options.output, options.format); console.log(chalk.green(`✅ Analysis saved to ${options.output}`)); } } catch (error) { logger.error("Error analyzing workflow:", error); console.error(chalk.red("❌ Failed to analyze workflow")); process.exit(1); } }); // Analyze entire project context analyzeCommand .command("project") .description("Analyze project context and generate development recommendations") .option("--project-type <type>", "Project type (web, mobile, api, desktop, etc.)") .option("--tech-stack <stack>", "Comma-separated technology stack") .option("--team-size <size>", "Team size (number)", parseInt) .option("--timeline <timeline>", "Project timeline") .option("--scope <scope>", "Project scope description") .option("--output <file>", "Save recommendations to file") .option("--generate-tasks", "Generate initial task breakdown") .action(async (options) => { try { console.log(chalk.blue("🏗️ Analyzing project context...")); console.log(); if (!options.scope) { console.error(chalk.red("❌ Project scope is required. Use --scope to describe your project.")); process.exit(1); } const analyzer = new TaskAnalyzer(); const projectId = generateId("project"); const context = { projectType: options.projectType, techStack: options.techStack ? options.techStack.split(",").map((s) => s.trim()) : undefined, teamSize: options.teamSize, timeline: options.timeline }; // Analyze project scope as a high-level task const analysis = await analyzer.analyzeComplexity(projectId, `${options.projectType || "Software"} Project`, options.scope, context); console.log(chalk.green("📊 Project Analysis Results:")); console.log(); displayTaskAnalysis(analysis, "table"); if (options.generateTasks) { console.log(); console.log(chalk.blue("🔧 Generating initial task breakdown...")); const breakdown = await analyzer.breakdownTask(projectId, `${options.projectType || "Software"} Project`, options.scope, undefined, context); displayTaskBreakdown(breakdown, "workflow"); if (options.output) { await saveBreakdown(breakdown, options.output, "workflow"); console.log(chalk.green(`✅ Task breakdown saved to ${options.output}`)); } } } catch (error) { logger.error("Error analyzing project:", error); console.error(chalk.red("❌ Failed to analyze project")); process.exit(1); } }); // Display functions function displayTaskAnalysis(analysis, format) { switch (format) { case "json": console.log(JSON.stringify(analysis, null, 2)); break; case "markdown": console.log(`# Task Analysis: ${analysis.title}\n`); console.log(`**Description:** ${analysis.description}\n`); console.log(`**Complexity:** ${analysis.complexityLevel} (${analysis.complexityScore}/10)`); console.log(`**Estimated Hours:** ${analysis.estimatedHours}`); console.log(`**Recommended Subtasks:** ${analysis.recommendedSubtasks}`); console.log(`**Breakdown Recommendation:** ${analysis.breakdownRecommendation}\n`); if (analysis.riskFactors.length > 0) { console.log(`**Risk Factors:**`); analysis.riskFactors.forEach(risk => console.log(`- ${risk}`)); console.log(); } if (analysis.skillsRequired.length > 0) { console.log(`**Skills Required:**`); analysis.skillsRequired.forEach(skill => console.log(`- ${skill}`)); console.log(); } console.log(`**Reasoning:** ${analysis.reasoning}`); break; default: // table console.log(chalk.green("📊 Task Analysis Results:")); console.log(); console.log(`${chalk.bold("Task:")} ${analysis.title}`); console.log(`${chalk.bold("Complexity:")} ${getComplexityColor(analysis.complexityLevel)}${analysis.complexityLevel}${chalk.reset()} (${analysis.complexityScore}/10)`); console.log(`${chalk.bold("Estimated Hours:")} ${analysis.estimatedHours}`); console.log(`${chalk.bold("Breakdown Needed:")} ${getRecommendationColor(analysis.breakdownRecommendation)}${analysis.breakdownRecommendation}${chalk.reset()}`); console.log(`${chalk.bold("Recommended Subtasks:")} ${analysis.recommendedSubtasks}`); if (analysis.riskFactors.length > 0) { console.log(`${chalk.bold("Risk Factors:")} ${chalk.yellow(analysis.riskFactors.join(", "))}`); } if (analysis.skillsRequired.length > 0) { console.log(`${chalk.bold("Skills Required:")} ${chalk.cyan(analysis.skillsRequired.join(", "))}`); } console.log(); console.log(chalk.gray(`Reasoning: ${analysis.reasoning}`)); } } function displayTaskBreakdown(breakdown, format) { switch (format) { case "json": console.log(JSON.stringify(breakdown, null, 2)); break; case "workflow": // Generate workflow-compatible JSON const workflow = { name: breakdown.originalTask.title, description: breakdown.originalTask.description, tasks: breakdown.subtasks.map(subtask => ({ id: subtask.id, name: subtask.title, description: subtask.description, type: subtask.type, priority: subtask.priority, dependencies: subtask.dependencies, estimatedHours: subtask.estimatedHours, skillsRequired: subtask.skillsRequired, acceptanceCriteria: subtask.acceptanceCriteria, notes: subtask.notes })) }; console.log(JSON.stringify(workflow, null, 2)); break; default: // markdown console.log(chalk.green("🔧 Task Breakdown Results:")); console.log(); console.log(`${chalk.bold("Original Task:")} ${breakdown.originalTask.title}`); console.log(`${chalk.bold("Total Estimated Hours:")} ${breakdown.totalEstimatedHours}`); console.log(`${chalk.bold("Number of Subtasks:")} ${breakdown.subtasks.length}`); console.log(); breakdown.subtasks.forEach((subtask, index) => { console.log(`${chalk.blue(`${index + 1}. ${subtask.title}`)} ${chalk.gray(`(${subtask.estimatedHours}h)`)}`); console.log(` ${chalk.gray("Type:")} ${getTypeColor(subtask.type)}${subtask.type}${chalk.reset()}`); console.log(` ${chalk.gray("Priority:")} ${getPriorityColor(subtask.priority)}${subtask.priority}${chalk.reset()}`); console.log(` ${chalk.gray("Description:")} ${subtask.description}`); if (subtask.dependencies.length > 0) { console.log(` ${chalk.gray("Dependencies:")} ${subtask.dependencies.join(", ")}`); } if (subtask.acceptanceCriteria.length > 0) { console.log(` ${chalk.gray("Acceptance Criteria:")}`); subtask.acceptanceCriteria.forEach(criteria => { console.log(` • ${criteria}`); }); } console.log(); }); if (breakdown.recommendations.length > 0) { console.log(chalk.yellow("💡 Recommendations:")); breakdown.recommendations.forEach(rec => console.log(` • ${rec}`)); } } } function displayWorkflowAnalysis(workflow, analysis, format) { console.log(chalk.green(`📊 Workflow Analysis: ${workflow.name}`)); console.log(); console.log(`${chalk.bold("Total Tasks:")} ${analysis.analyses.length}`); console.log(`${chalk.bold("Total Estimated Hours:")} ${analysis.totalEstimatedHours}`); console.log(); console.log(chalk.yellow("📋 Task Complexity Summary:")); const complexityGroups = analysis.analyses.reduce((groups, task) => { groups[task.complexityLevel] = (groups[task.complexityLevel] || 0) + 1; return groups; }, {}); Object.entries(complexityGroups).forEach(([level, count]) => { console.log(` ${getComplexityColor(level)}${level}${chalk.reset()}: ${count} tasks`); }); console.log(); console.log(chalk.yellow("🎯 Breakdown Recommendations:")); console.log(` ${chalk.red("Immediate:")} ${analysis.recommendations.immediate.length} tasks`); console.log(` ${chalk.yellow("Optional:")} ${analysis.recommendations.optional.length} tasks`); console.log(` ${chalk.green("Defer:")} ${analysis.recommendations.defer.length} tasks`); if (analysis.recommendations.immediate.length > 0) { console.log(); console.log(chalk.red("⚠️ Tasks requiring immediate breakdown:")); analysis.recommendations.immediate.forEach((taskId) => { const task = analysis.analyses.find((a) => a.taskId === taskId); if (task) { console.log(` • ${task.title} (${task.complexityScore}/10, ${task.estimatedHours}h)`); } }); } } // Helper functions for colored output function getComplexityColor(level) { switch (level) { case "trivial": return chalk.gray(""); case "simple": return chalk.green(""); case "moderate": return chalk.yellow(""); case "complex": return chalk.red(""); case "expert": return chalk.magenta(""); default: return ""; } } function getRecommendationColor(recommendation) { switch (recommendation) { case "none": return chalk.gray(""); case "optional": return chalk.green(""); case "recommended": return chalk.yellow(""); case "required": return chalk.red(""); default: return ""; } } function getTypeColor(type) { switch (type) { case "setup": return chalk.blue(""); case "implementation": return chalk.green(""); case "testing": return chalk.yellow(""); case "documentation": return chalk.cyan(""); case "integration": return chalk.magenta(""); default: return ""; } } function getPriorityColor(priority) { switch (priority) { case "low": return chalk.gray(""); case "medium": return chalk.yellow(""); case "high": return chalk.red(""); case "critical": return chalk.magenta(""); default: return ""; } } // Save functions async function saveAnalysis(analysis, filePath, format) { const outputPath = path.resolve(filePath); switch (format) { case "json": await fs.writeJson(outputPath, analysis, { spaces: 2 }); break; case "markdown": const markdown = `# Task Analysis: ${analysis.title} **Description:** ${analysis.description} ## Complexity Assessment - **Complexity Level:** ${analysis.complexityLevel} - **Complexity Score:** ${analysis.complexityScore}/10 - **Estimated Hours:** ${analysis.estimatedHours} - **Breakdown Recommendation:** ${analysis.breakdownRecommendation} - **Recommended Subtasks:** ${analysis.recommendedSubtasks} ${analysis.riskFactors.length > 0 ? `## Risk Factors ${analysis.riskFactors.map(risk => `- ${risk}`).join('\n')} ` : ''}${analysis.skillsRequired.length > 0 ? `## Skills Required ${analysis.skillsRequired.map(skill => `- ${skill}`).join('\n')} ` : ''}## Analysis Reasoning ${analysis.reasoning} `; await fs.writeFile(outputPath, markdown); break; default: await fs.writeJson(outputPath, analysis, { spaces: 2 }); } } async function saveBreakdown(breakdown, filePath, format) { const outputPath = path.resolve(filePath); switch (format) { case "workflow": const workflow = { name: breakdown.originalTask.title, description: breakdown.originalTask.description, tasks: breakdown.subtasks.map(subtask => ({ id: subtask.id, name: subtask.title, description: subtask.description, type: subtask.type, priority: subtask.priority, dependencies: subtask.dependencies, estimatedHours: subtask.estimatedHours, skillsRequired: subtask.skillsRequired, acceptanceCriteria: subtask.acceptanceCriteria, notes: subtask.notes })) }; await fs.writeJson(outputPath, workflow, { spaces: 2 }); break; case "markdown": const markdown = `# Task Breakdown: ${breakdown.originalTask.title} **Original Description:** ${breakdown.originalTask.description} **Total Estimated Hours:** ${breakdown.totalEstimatedHours} **Number of Subtasks:** ${breakdown.subtasks.length} ## Complexity Analysis - **Complexity Level:** ${breakdown.analysis.complexityLevel} - **Complexity Score:** ${breakdown.analysis.complexityScore}/10 - **Estimated Hours:** ${breakdown.analysis.estimatedHours} ## Subtasks ${breakdown.subtasks.map((subtask, index) => `### ${index + 1}. ${subtask.title} **Type:** ${subtask.type} **Priority:** ${subtask.priority} **Estimated Hours:** ${subtask.estimatedHours} **Dependencies:** ${subtask.dependencies.join(', ') || 'None'} **Description:** ${subtask.description} **Skills Required:** ${subtask.skillsRequired.join(', ')} **Acceptance Criteria:** ${subtask.acceptanceCriteria.map(criteria => `- ${criteria}`).join('\n')} ${subtask.notes ? `**Notes:** ${subtask.notes}` : ''} `).join('\n')} ## Recommendations ${breakdown.recommendations.map(rec => `- ${rec}`).join('\n')} `; await fs.writeFile(outputPath, markdown); break; default: await fs.writeJson(outputPath, breakdown, { spaces: 2 }); } } async function saveWorkflowAnalysis(workflow, analysis, filePath, format) { const outputPath = path.resolve(filePath); const report = { workflow: { name: workflow.name, description: workflow.description, totalTasks: analysis.analyses.length, totalEstimatedHours: analysis.totalEstimatedHours }, complexityDistribution: analysis.analyses.reduce((groups, task) => { groups[task.complexityLevel] = (groups[task.complexityLevel] || 0) + 1; return groups; }, {}), recommendations: analysis.recommendations, taskAnalyses: analysis.analyses }; await fs.writeJson(outputPath, report, { spaces: 2 }); } //# sourceMappingURL=analyze.js.map