diff --git a/README-task-master.md b/README-task-master.md index 818e696c..64556c1c 100644 --- a/README-task-master.md +++ b/README-task-master.md @@ -366,6 +366,15 @@ npm run dev -- analyze-complexity --file=custom-tasks.json npm run dev -- analyze-complexity --research ``` +### View Complexity Report +```bash +# Display the task complexity analysis report +npm run dev -- complexity-report + +# View a report at a custom location +npm run dev -- complexity-report --file=my-report.json +``` + ### Managing Task Dependencies ```bash # Add a dependency to a task @@ -398,6 +407,16 @@ The generated report contains: - AI-generated expansion prompts customized for each task - Ready-to-run expansion commands directly within each task analysis +### Viewing Complexity Report + +The `complexity-report` command: +- Displays a formatted, easy-to-read version of the complexity analysis report +- Shows tasks organized by complexity score (highest to lowest) +- Provides complexity distribution statistics (low, medium, high) +- Highlights tasks recommended for expansion based on threshold score +- Includes ready-to-use expansion commands for each complex task +- If no report exists, offers to generate one on the spot + ### Smart Task Expansion The `expand` command automatically checks for and uses the complexity report: @@ -413,7 +432,8 @@ Example workflow: # Generate the complexity analysis report with research capabilities npm run dev -- analyze-complexity --research -# Review the report in scripts/task-complexity-report.json +# Review the report in a readable format +npm run dev -- complexity-report # Expand tasks using the optimized recommendations npm run dev -- expand --id=8 @@ -502,4 +522,9 @@ Please mark it as complete and tell me what I should work on next. ### Analyzing complexity ``` Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further? +``` + +### Viewing complexity report +``` +Can you show me the complexity report in a more readable format? ``` \ No newline at end of file diff --git a/README.md b/README.md index 0fe224af..575be816 100644 --- a/README.md +++ b/README.md @@ -399,6 +399,15 @@ npm run dev -- analyze-complexity --file=custom-tasks.json npm run dev -- analyze-complexity --research ``` +### View Complexity Report +```bash +# Display the task complexity analysis report +npm run dev -- complexity-report + +# View a report at a custom location +npm run dev -- complexity-report --file=my-report.json +``` + ### Managing Task Dependencies ```bash # Add a dependency to a task @@ -431,6 +440,16 @@ The generated report contains: - AI-generated expansion prompts customized for each task - Ready-to-run expansion commands directly within each task analysis +### Viewing Complexity Report + +The `complexity-report` command: +- Displays a formatted, easy-to-read version of the complexity analysis report +- Shows tasks organized by complexity score (highest to lowest) +- Provides complexity distribution statistics (low, medium, high) +- Highlights tasks recommended for expansion based on threshold score +- Includes ready-to-use expansion commands for each complex task +- If no report exists, offers to generate one on the spot + ### Smart Task Expansion The `expand` command automatically checks for and uses the complexity report: @@ -446,7 +465,8 @@ Example workflow: # Generate the complexity analysis report with research capabilities npm run dev -- analyze-complexity --research -# Review the report in scripts/task-complexity-report.json +# Review the report in a readable format +npm run dev -- complexity-report # Expand tasks using the optimized recommendations npm run dev -- expand --id=8 @@ -537,6 +557,11 @@ Please mark it as complete and tell me what I should work on next. Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further? ``` +### Viewing complexity report +``` +Can you show me the complexity report in a more readable format? +``` + ## License -MIT \ No newline at end of file +MIT diff --git a/scripts/dev.js b/scripts/dev.js index c3ae6463..34f6fb37 100755 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -95,6 +95,15 @@ * -> Regenerates task files with corrected dependencies * -> Provides detailed report of all fixes made * + * 15) complexity-report [--file=path] + * -> Displays the task complexity analysis report in a readable format + * -> Shows tasks organized by complexity score with recommended actions + * -> Includes complexity distribution statistics + * -> Provides ready-to-use expansion commands + * -> If no report exists, offers to generate one + * -> Options: + * --file, -f : Specify report file path (default: 'scripts/task-complexity-report.json') + * * Usage examples: * node dev.js parse-prd --input=sample-prd.txt * node dev.js parse-prd --input=sample-prd.txt --tasks=10 @@ -118,6 +127,8 @@ * node dev.js remove-dependency --id=22 --depends-on=21 * node dev.js validate-dependencies * node dev.js fix-dependencies + * node dev.js complexity-report + * node dev.js complexity-report --file=custom-report.json */ import fs from 'fs'; @@ -196,7 +207,7 @@ const warmGradient = gradient(['#fb8b24', '#e36414', '#9a031e']); // Display a fancy banner function displayBanner() { console.clear(); - const bannerText = figlet.textSync('Claude Task Master', { + const bannerText = figlet.textSync('Task Master AI', { font: 'Standard', horizontalLayout: 'default', verticalLayout: 'default' @@ -204,6 +215,9 @@ function displayBanner() { console.log(coolGradient(bannerText)); + // Add creator credit line below the banner + console.log(chalk.dim('by ') + chalk.cyan.underline('https://x.com/eyaltoledano')); + // Read version directly from package.json let version = "1.5.0"; // Default fallback try { @@ -1215,6 +1229,66 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) { ? data.tasks.filter(t => t.status === statusFilter) : data.tasks; + // Count statistics for metrics + const doneCount = data.tasks.filter(t => t.status === 'done' || t.status === 'completed').length; + const pendingCount = data.tasks.filter(t => t.status === 'pending').length; + const inProgressCount = data.tasks.filter(t => t.status === 'in-progress').length; + const deferredCount = data.tasks.filter(t => t.status === 'deferred').length; + const otherCount = data.tasks.length - doneCount - pendingCount - inProgressCount - deferredCount; + + // Count tasks by priority + const highPriorityCount = data.tasks.filter(t => t.priority === 'high').length; + const mediumPriorityCount = data.tasks.filter(t => t.priority === 'medium').length; + const lowPriorityCount = data.tasks.filter(t => t.priority === 'low').length; + + // Calculate progress percentage + const progressPercent = Math.round((doneCount / data.tasks.length) * 100); + const progressBar = createProgressBar(progressPercent, 30); + + // Count blocked tasks (pending with dependencies that aren't done) + let blockedCount = 0; + data.tasks.filter(t => t.status === 'pending').forEach(task => { + if (task.dependencies && task.dependencies.length > 0) { + const hasPendingDeps = task.dependencies.some(depId => { + const depTask = data.tasks.find(t => t.id === depId); + return depTask && depTask.status !== 'done' && depTask.status !== 'completed'; + }); + if (hasPendingDeps) blockedCount++; + } + }); + + // Count subtasks + let totalSubtasks = 0; + let completedSubtasks = 0; + data.tasks.forEach(task => { + if (task.subtasks && Array.isArray(task.subtasks)) { + totalSubtasks += task.subtasks.length; + completedSubtasks += task.subtasks.filter(st => + st.status === 'done' || st.status === 'completed' + ).length; + } + }); + + // Calculate subtask progress + const subtaskProgressPercent = totalSubtasks > 0 + ? Math.round((completedSubtasks / totalSubtasks) * 100) + : 0; + const subtaskProgressBar = createProgressBar(subtaskProgressPercent, 30); + + // Display the dashboard first + console.log(boxen( + chalk.white.bold('Project Dashboard\n') + + `${chalk.bold('Tasks Progress:')} ${progressBar} ${progressPercent}%\n` + + `${chalk.green.bold('Done:')} ${doneCount} ${chalk.blue.bold('In Progress:')} ${inProgressCount} ${chalk.yellow.bold('Pending:')} ${pendingCount} ${chalk.red.bold('Blocked:')} ${blockedCount} ${chalk.gray.bold('Deferred:')} ${deferredCount}\n` + + '\n' + + `${chalk.bold('Subtasks Progress:')} ${subtaskProgressBar} ${subtaskProgressPercent}%\n` + + `${chalk.green.bold('Completed:')} ${completedSubtasks}/${totalSubtasks} ${chalk.yellow.bold('Remaining:')} ${totalSubtasks - completedSubtasks}\n` + + '\n' + + `${chalk.bold('Priority Breakdown:')}\n` + + `${chalk.red.bold('High:')} ${highPriorityCount} ${chalk.yellow.bold('Medium:')} ${mediumPriorityCount} ${chalk.gray.bold('Low:')} ${lowPriorityCount}`, + { padding: 1, borderColor: 'cyan', borderStyle: 'round', margin: { top: 0, bottom: 0 } } + )); + // Get terminal width for dynamic sizing const terminalWidth = process.stdout.columns || 100; @@ -1353,20 +1427,6 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) { })); console.log(table.toString()); - - // Summary box - const doneCount = data.tasks.filter(t => t.status === 'done' || t.status === 'completed').length; - const pendingCount = data.tasks.filter(t => t.status === 'pending').length; - const otherCount = data.tasks.length - doneCount - pendingCount; - - const progressPercent = Math.round((doneCount / data.tasks.length) * 100); - const progressBar = createProgressBar(progressPercent, 30); - - console.log(boxen( - `${chalk.bold('Project Progress:')} ${progressBar} ${progressPercent}%\n` + - `${chalk.green.bold('Done:')} ${doneCount} ${chalk.yellow.bold('Pending:')} ${pendingCount} ${chalk.gray.bold('Other:')} ${otherCount}`, - { padding: 1, borderColor: 'cyan', borderStyle: 'round', margin: { top: 1 } } - )); } } @@ -2597,6 +2657,15 @@ async function main() { } }); + program + .command('complexity-report') + .description('Display the complexity analysis report') + .option('-f, --file ', 'Path to the complexity report file', 'scripts/task-complexity-report.json') + .action(async (options) => { + const reportPath = options.file; + await displayComplexityReport(reportPath); + }); + program .command('*') .description('Handle unknown commands') @@ -2806,7 +2875,7 @@ function displayHelp() { displayBanner(); console.log(boxen( - chalk.white.bold('Claude Task Master CLI'), + chalk.white.bold('Task Master CLI'), { padding: 1, borderColor: 'blue', borderStyle: 'round', margin: { top: 1, bottom: 1 } } )); @@ -2844,6 +2913,8 @@ function displayHelp() { commands: [ { name: 'analyze-complexity', args: '[--research] [--threshold=5]', desc: 'Analyze tasks and generate expansion recommendations' }, + { name: 'complexity-report', args: '[--file=]', + desc: 'Display the complexity analysis report' }, { name: 'expand', args: '--id= [--num=5] [--research] [--prompt=""]', desc: 'Break down tasks into detailed subtasks' }, { name: 'expand --all', args: '[--force] [--research]', @@ -5198,4 +5269,202 @@ function ensureAtLeastOneIndependentSubtask(tasksData) { }); return changesDetected; +} + +// Add the function to display complexity report (around line ~4850, before the main function) +/** + * Display the complexity analysis report in a nice format + * @param {string} reportPath - Path to the complexity report file + */ +async function displayComplexityReport(reportPath) { + displayBanner(); + + // Check if the report exists + if (!fs.existsSync(reportPath)) { + console.log(boxen( + chalk.yellow(`No complexity report found at ${reportPath}\n\n`) + + 'Would you like to generate one now?', + { padding: 1, borderColor: 'yellow', borderStyle: 'round', margin: { top: 1 } } + )); + + const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout + }); + + const answer = await new Promise(resolve => { + readline.question(chalk.cyan('Generate complexity report? (y/n): '), resolve); + }); + readline.close(); + + if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') { + // Call the analyze-complexity command + console.log(chalk.blue('Generating complexity report...')); + await analyzeTaskComplexity({ + output: reportPath, + research: false, // Default to no research for speed + file: 'tasks/tasks.json' + }); + // Read the newly generated report + return displayComplexityReport(reportPath); + } else { + console.log(chalk.yellow('Report generation cancelled.')); + return; + } + } + + // Read the report + let report; + try { + report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); + } catch (error) { + log('error', `Error reading complexity report: ${error.message}`); + return; + } + + // Display report header + console.log(boxen( + chalk.white.bold('Task Complexity Analysis Report'), + { padding: 1, borderColor: 'blue', borderStyle: 'round', margin: { top: 1, bottom: 1 } } + )); + + // Display metadata + const metaTable = new Table({ + style: { + head: [], + border: [], + 'padding-top': 0, + 'padding-bottom': 0, + compact: true + }, + chars: { + 'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' + }, + colWidths: [20, 50] + }); + + metaTable.push( + [chalk.cyan.bold('Generated:'), new Date(report.meta.generatedAt).toLocaleString()], + [chalk.cyan.bold('Tasks Analyzed:'), report.meta.tasksAnalyzed], + [chalk.cyan.bold('Threshold Score:'), report.meta.thresholdScore], + [chalk.cyan.bold('Project:'), report.meta.projectName], + [chalk.cyan.bold('Research-backed:'), report.meta.usedResearch ? 'Yes' : 'No'] + ); + + console.log(metaTable.toString()); + + // Sort tasks by complexity score (highest first) + const sortedTasks = [...report.complexityAnalysis].sort((a, b) => b.complexityScore - a.complexityScore); + + // Determine which tasks need expansion based on threshold + const tasksNeedingExpansion = sortedTasks.filter(task => task.complexityScore >= report.meta.thresholdScore); + const simpleTasks = sortedTasks.filter(task => task.complexityScore < report.meta.thresholdScore); + + // Create progress bar to show complexity distribution + const complexityDistribution = [0, 0, 0]; // Low (0-4), Medium (5-7), High (8-10) + sortedTasks.forEach(task => { + if (task.complexityScore < 5) complexityDistribution[0]++; + else if (task.complexityScore < 8) complexityDistribution[1]++; + else complexityDistribution[2]++; + }); + + const percentLow = Math.round((complexityDistribution[0] / sortedTasks.length) * 100); + const percentMedium = Math.round((complexityDistribution[1] / sortedTasks.length) * 100); + const percentHigh = Math.round((complexityDistribution[2] / sortedTasks.length) * 100); + + console.log(boxen( + chalk.white.bold('Complexity Distribution\n\n') + + `${chalk.green.bold('Low (1-4):')} ${complexityDistribution[0]} tasks (${percentLow}%)\n` + + `${chalk.yellow.bold('Medium (5-7):')} ${complexityDistribution[1]} tasks (${percentMedium}%)\n` + + `${chalk.red.bold('High (8-10):')} ${complexityDistribution[2]} tasks (${percentHigh}%)`, + { padding: 1, borderColor: 'cyan', borderStyle: 'round', margin: { top: 1, bottom: 1 } } + )); + + // Create table for tasks that need expansion + if (tasksNeedingExpansion.length > 0) { + console.log(boxen( + chalk.yellow.bold(`Tasks Recommended for Expansion (${tasksNeedingExpansion.length})`), + { padding: { left: 2, right: 2, top: 0, bottom: 0 }, margin: { top: 1, bottom: 0 }, borderColor: 'yellow', borderStyle: 'round' } + )); + + const complexTable = new Table({ + head: [ + chalk.yellow.bold('ID'), + chalk.yellow.bold('Title'), + chalk.yellow.bold('Score'), + chalk.yellow.bold('Subtasks'), + chalk.yellow.bold('Expansion Command') + ], + colWidths: [5, 40, 8, 10, 45], + style: { head: [], border: [] } + }); + + tasksNeedingExpansion.forEach(task => { + complexTable.push([ + task.taskId, + truncate(task.taskTitle, 37), + getComplexityWithColor(task.complexityScore), + task.recommendedSubtasks, + chalk.cyan(`node scripts/dev.js expand --id=${task.taskId} --num=${task.recommendedSubtasks}`) + ]); + }); + + console.log(complexTable.toString()); + } + + // Create table for simple tasks + if (simpleTasks.length > 0) { + console.log(boxen( + chalk.green.bold(`Simple Tasks (${simpleTasks.length})`), + { padding: { left: 2, right: 2, top: 0, bottom: 0 }, margin: { top: 1, bottom: 0 }, borderColor: 'green', borderStyle: 'round' } + )); + + const simpleTable = new Table({ + head: [ + chalk.green.bold('ID'), + chalk.green.bold('Title'), + chalk.green.bold('Score'), + chalk.green.bold('Reasoning') + ], + colWidths: [5, 40, 8, 50], + style: { head: [], border: [] } + }); + + simpleTasks.forEach(task => { + simpleTable.push([ + task.taskId, + truncate(task.taskTitle, 37), + getComplexityWithColor(task.complexityScore), + truncate(task.reasoning, 47) + ]); + }); + + console.log(simpleTable.toString()); + } + + // Show action suggestions + console.log(boxen( + chalk.white.bold('Suggested Actions:') + '\n\n' + + `${chalk.cyan('1.')} Expand all complex tasks: ${chalk.yellow(`node scripts/dev.js expand --all`)}\n` + + `${chalk.cyan('2.')} Expand a specific task: ${chalk.yellow(`node scripts/dev.js expand --id=`)}\n` + + `${chalk.cyan('3.')} Regenerate with research: ${chalk.yellow(`node scripts/dev.js analyze-complexity --research`)}`, + { padding: 1, borderColor: 'cyan', borderStyle: 'round', margin: { top: 1 } } + )); +} + +// Helper function to get complexity score with appropriate color +function getComplexityWithColor(score) { + if (score >= 8) { + return chalk.red.bold(score); + } else if (score >= 5) { + return chalk.yellow(score); + } else { + return chalk.green(score); + } +} + +// Helper function to truncate text +function truncate(text, maxLength) { + if (!text) return ''; + return text.length > maxLength ? text.substring(0, maxLength - 3) + '...' : text; } \ No newline at end of file diff --git a/scripts/init.js b/scripts/init.js index e78e3a36..3071fd6c 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -40,7 +40,7 @@ const warmGradient = gradient(['#fb8b24', '#e36414', '#9a031e']); // Display a fancy banner function displayBanner() { console.clear(); - const bannerText = figlet.textSync('Claude Task Master', { + const bannerText = figlet.textSync('Task Master AI', { font: 'Standard', horizontalLayout: 'default', verticalLayout: 'default' @@ -48,6 +48,9 @@ function displayBanner() { console.log(coolGradient(bannerText)); + // Add creator credit line below the banner + console.log(chalk.dim('by ') + chalk.cyan.underline('https://x.com/eyaltoledano')); + console.log(boxen(chalk.white(`${chalk.bold('Initializing')} your new project`), { padding: 1, margin: { top: 0, bottom: 1 },