feat: update list and find next task
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
import { log } from '../utils.js';
|
||||||
|
import { addComplexityToTask } from '../utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the next work item:
|
* Return the next work item:
|
||||||
* • Prefer an eligible SUBTASK that belongs to any parent task
|
* • Prefer an eligible SUBTASK that belongs to any parent task
|
||||||
@@ -15,9 +18,10 @@
|
|||||||
* ─ parentId → number (present only when it's a subtask)
|
* ─ parentId → number (present only when it's a subtask)
|
||||||
*
|
*
|
||||||
* @param {Object[]} tasks – full array of top-level tasks, each may contain .subtasks[]
|
* @param {Object[]} tasks – full array of top-level tasks, each may contain .subtasks[]
|
||||||
|
* @param {Object} [complexityReport=null] - Optional complexity report object
|
||||||
* @returns {Object|null} – next work item or null if nothing is eligible
|
* @returns {Object|null} – next work item or null if nothing is eligible
|
||||||
*/
|
*/
|
||||||
function findNextTask(tasks) {
|
function findNextTask(tasks, complexityReport = null) {
|
||||||
// ---------- helpers ----------------------------------------------------
|
// ---------- helpers ----------------------------------------------------
|
||||||
const priorityValues = { high: 3, medium: 2, low: 1 };
|
const priorityValues = { high: 3, medium: 2, low: 1 };
|
||||||
|
|
||||||
@@ -91,7 +95,14 @@ function findNextTask(tasks) {
|
|||||||
if (aPar !== bPar) return aPar - bPar;
|
if (aPar !== bPar) return aPar - bPar;
|
||||||
return aSub - bSub;
|
return aSub - bSub;
|
||||||
});
|
});
|
||||||
return candidateSubtasks[0];
|
const nextTask = candidateSubtasks[0];
|
||||||
|
|
||||||
|
// Add complexity to the task before returning
|
||||||
|
if (nextTask && complexityReport) {
|
||||||
|
addComplexityToTask(nextTask, complexityReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 2) fall back to top-level tasks (original logic) ------------
|
// ---------- 2) fall back to top-level tasks (original logic) ------------
|
||||||
@@ -116,6 +127,11 @@ function findNextTask(tasks) {
|
|||||||
return a.id - b.id;
|
return a.id - b.id;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
// Add complexity to the task before returning
|
||||||
|
if (nextTask && complexityReport) {
|
||||||
|
addComplexityToTask(nextTask, complexityReport);
|
||||||
|
}
|
||||||
|
|
||||||
return nextTask;
|
return nextTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,20 @@ import chalk from 'chalk';
|
|||||||
import boxen from 'boxen';
|
import boxen from 'boxen';
|
||||||
import Table from 'cli-table3';
|
import Table from 'cli-table3';
|
||||||
|
|
||||||
import { log, readJSON, truncate } from '../utils.js';
|
import {
|
||||||
|
log,
|
||||||
|
readJSON,
|
||||||
|
truncate,
|
||||||
|
readComplexityReport,
|
||||||
|
addComplexityToTask
|
||||||
|
} from '../utils.js';
|
||||||
import findNextTask from './find-next-task.js';
|
import findNextTask from './find-next-task.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
displayBanner,
|
displayBanner,
|
||||||
getStatusWithColor,
|
getStatusWithColor,
|
||||||
formatDependenciesWithStatus,
|
formatDependenciesWithStatus,
|
||||||
|
getComplexityWithColor,
|
||||||
createProgressBar
|
createProgressBar
|
||||||
} from '../ui.js';
|
} from '../ui.js';
|
||||||
|
|
||||||
@@ -16,6 +23,7 @@ import {
|
|||||||
* List all tasks
|
* List all tasks
|
||||||
* @param {string} tasksPath - Path to the tasks.json file
|
* @param {string} tasksPath - Path to the tasks.json file
|
||||||
* @param {string} statusFilter - Filter by status
|
* @param {string} statusFilter - Filter by status
|
||||||
|
* @param {string} reportPath - Path to the complexity report
|
||||||
* @param {boolean} withSubtasks - Whether to show subtasks
|
* @param {boolean} withSubtasks - Whether to show subtasks
|
||||||
* @param {string} outputFormat - Output format (text or json)
|
* @param {string} outputFormat - Output format (text or json)
|
||||||
* @returns {Object} - Task list result for json format
|
* @returns {Object} - Task list result for json format
|
||||||
@@ -23,6 +31,7 @@ import {
|
|||||||
function listTasks(
|
function listTasks(
|
||||||
tasksPath,
|
tasksPath,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
|
reportPath = null,
|
||||||
withSubtasks = false,
|
withSubtasks = false,
|
||||||
outputFormat = 'text'
|
outputFormat = 'text'
|
||||||
) {
|
) {
|
||||||
@@ -37,6 +46,13 @@ function listTasks(
|
|||||||
throw new Error(`No valid tasks found in ${tasksPath}`);
|
throw new Error(`No valid tasks found in ${tasksPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add complexity scores to tasks if report exists
|
||||||
|
const complexityReport = readComplexityReport(reportPath); // Read report
|
||||||
|
// Apply complexity scores to tasks
|
||||||
|
if (complexityReport && complexityReport.complexityAnalysis) {
|
||||||
|
data.tasks.forEach((task) => addComplexityToTask(task, complexityReport)); // Apply scores using utility
|
||||||
|
}
|
||||||
|
|
||||||
// Filter tasks by status if specified
|
// Filter tasks by status if specified
|
||||||
const filteredTasks =
|
const filteredTasks =
|
||||||
statusFilter && statusFilter.toLowerCase() !== 'all' // <-- Added check for 'all'
|
statusFilter && statusFilter.toLowerCase() !== 'all' // <-- Added check for 'all'
|
||||||
@@ -257,8 +273,8 @@ function listTasks(
|
|||||||
);
|
);
|
||||||
const avgDependenciesPerTask = totalDependencies / data.tasks.length;
|
const avgDependenciesPerTask = totalDependencies / data.tasks.length;
|
||||||
|
|
||||||
// Find next task to work on
|
// Find next task to work on, passing the complexity report
|
||||||
const nextItem = findNextTask(data.tasks);
|
const nextItem = findNextTask(data.tasks, complexityReport); // Pass complexityReport
|
||||||
|
|
||||||
// Get terminal width - more reliable method
|
// Get terminal width - more reliable method
|
||||||
let terminalWidth;
|
let terminalWidth;
|
||||||
@@ -301,8 +317,11 @@ function listTasks(
|
|||||||
`${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${avgDependenciesPerTask.toFixed(1)}\n\n` +
|
`${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${avgDependenciesPerTask.toFixed(1)}\n\n` +
|
||||||
chalk.cyan.bold('Next Task to Work On:') +
|
chalk.cyan.bold('Next Task to Work On:') +
|
||||||
'\n' +
|
'\n' +
|
||||||
`ID: ${chalk.cyan(nextItem ? nextItem.id : 'N/A')} - ${nextItem ? chalk.white.bold(truncate(nextItem.title, 40)) : chalk.yellow('No task available')}\n` +
|
`ID: ${chalk.cyan(nextItem ? nextItem.id : 'N/A')} - ${nextItem ? chalk.white.bold(truncate(nextItem.title, 40)) : chalk.yellow('No task available')}
|
||||||
`Priority: ${nextItem ? chalk.white(nextItem.priority || 'medium') : ''} Dependencies: ${nextItem ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true) : ''}`;
|
` +
|
||||||
|
`Priority: ${nextItem ? chalk.white(nextItem.priority || 'medium') : ''} Dependencies: ${nextItem ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true, complexityReport) : ''}
|
||||||
|
` + // Pass complexityReport
|
||||||
|
`Complexity: ${nextItem && nextItem.complexityScore ? getComplexityWithColor(nextItem.complexityScore) : chalk.gray('N/A')}`; // Added complexity display
|
||||||
|
|
||||||
// Calculate width for side-by-side display
|
// Calculate width for side-by-side display
|
||||||
// Box borders, padding take approximately 4 chars on each side
|
// Box borders, padding take approximately 4 chars on each side
|
||||||
@@ -412,9 +431,16 @@ function listTasks(
|
|||||||
// Make dependencies column smaller as requested (-20%)
|
// Make dependencies column smaller as requested (-20%)
|
||||||
const depsWidthPct = 20;
|
const depsWidthPct = 20;
|
||||||
|
|
||||||
|
const complexityWidthPct = 10; // Added complexity column percentage
|
||||||
|
|
||||||
// Calculate title/description width as remaining space (+20% from dependencies reduction)
|
// Calculate title/description width as remaining space (+20% from dependencies reduction)
|
||||||
const titleWidthPct =
|
const titleWidthPct =
|
||||||
100 - idWidthPct - statusWidthPct - priorityWidthPct - depsWidthPct;
|
100 -
|
||||||
|
idWidthPct -
|
||||||
|
statusWidthPct -
|
||||||
|
priorityWidthPct -
|
||||||
|
depsWidthPct -
|
||||||
|
complexityWidthPct; // Adjusted for complexity column
|
||||||
|
|
||||||
// Allow 10 characters for borders and padding
|
// Allow 10 characters for borders and padding
|
||||||
const availableWidth = terminalWidth - 10;
|
const availableWidth = terminalWidth - 10;
|
||||||
@@ -424,6 +450,9 @@ function listTasks(
|
|||||||
const statusWidth = Math.floor(availableWidth * (statusWidthPct / 100));
|
const statusWidth = Math.floor(availableWidth * (statusWidthPct / 100));
|
||||||
const priorityWidth = Math.floor(availableWidth * (priorityWidthPct / 100));
|
const priorityWidth = Math.floor(availableWidth * (priorityWidthPct / 100));
|
||||||
const depsWidth = Math.floor(availableWidth * (depsWidthPct / 100));
|
const depsWidth = Math.floor(availableWidth * (depsWidthPct / 100));
|
||||||
|
const complexityWidth = Math.floor(
|
||||||
|
availableWidth * (complexityWidthPct / 100)
|
||||||
|
);
|
||||||
const titleWidth = Math.floor(availableWidth * (titleWidthPct / 100));
|
const titleWidth = Math.floor(availableWidth * (titleWidthPct / 100));
|
||||||
|
|
||||||
// Create a table with correct borders and spacing
|
// Create a table with correct borders and spacing
|
||||||
@@ -433,9 +462,17 @@ function listTasks(
|
|||||||
chalk.cyan.bold('Title'),
|
chalk.cyan.bold('Title'),
|
||||||
chalk.cyan.bold('Status'),
|
chalk.cyan.bold('Status'),
|
||||||
chalk.cyan.bold('Priority'),
|
chalk.cyan.bold('Priority'),
|
||||||
chalk.cyan.bold('Dependencies')
|
chalk.cyan.bold('Dependencies'),
|
||||||
|
chalk.cyan.bold('Complexity') // Added Complexity header
|
||||||
|
],
|
||||||
|
colWidths: [
|
||||||
|
idWidth,
|
||||||
|
titleWidth,
|
||||||
|
statusWidth,
|
||||||
|
priorityWidth,
|
||||||
|
depsWidth,
|
||||||
|
complexityWidth // Added complexity column width
|
||||||
],
|
],
|
||||||
colWidths: [idWidth, titleWidth, statusWidth, priorityWidth, depsWidth],
|
|
||||||
style: {
|
style: {
|
||||||
head: [], // No special styling for header
|
head: [], // No special styling for header
|
||||||
border: [], // No special styling for border
|
border: [], // No special styling for border
|
||||||
@@ -454,7 +491,8 @@ function listTasks(
|
|||||||
depText = formatDependenciesWithStatus(
|
depText = formatDependenciesWithStatus(
|
||||||
task.dependencies,
|
task.dependencies,
|
||||||
data.tasks,
|
data.tasks,
|
||||||
true
|
true,
|
||||||
|
complexityReport // Pass complexityReport
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
depText = chalk.gray('None');
|
depText = chalk.gray('None');
|
||||||
@@ -480,7 +518,10 @@ function listTasks(
|
|||||||
truncate(cleanTitle, titleWidth - 3),
|
truncate(cleanTitle, titleWidth - 3),
|
||||||
status,
|
status,
|
||||||
priorityColor(truncate(task.priority || 'medium', priorityWidth - 2)),
|
priorityColor(truncate(task.priority || 'medium', priorityWidth - 2)),
|
||||||
depText // No truncation for dependencies
|
depText, // No truncation for dependencies
|
||||||
|
task.complexityScore // Add complexity score data
|
||||||
|
? getComplexityWithColor(task.complexityScore) // Use color function
|
||||||
|
: chalk.gray('N/A')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Add subtasks if requested
|
// Add subtasks if requested
|
||||||
@@ -516,6 +557,8 @@ function listTasks(
|
|||||||
// Default to regular task dependency
|
// Default to regular task dependency
|
||||||
const depTask = data.tasks.find((t) => t.id === depId);
|
const depTask = data.tasks.find((t) => t.id === depId);
|
||||||
if (depTask) {
|
if (depTask) {
|
||||||
|
// Add complexity to depTask before checking status
|
||||||
|
addComplexityToTask(depTask, complexityReport);
|
||||||
const isDone =
|
const isDone =
|
||||||
depTask.status === 'done' || depTask.status === 'completed';
|
depTask.status === 'done' || depTask.status === 'completed';
|
||||||
const isInProgress = depTask.status === 'in-progress';
|
const isInProgress = depTask.status === 'in-progress';
|
||||||
@@ -541,7 +584,10 @@ function listTasks(
|
|||||||
chalk.dim(`└─ ${truncate(subtask.title, titleWidth - 5)}`),
|
chalk.dim(`└─ ${truncate(subtask.title, titleWidth - 5)}`),
|
||||||
getStatusWithColor(subtask.status, true),
|
getStatusWithColor(subtask.status, true),
|
||||||
chalk.dim('-'),
|
chalk.dim('-'),
|
||||||
subtaskDepText // No truncation for dependencies
|
subtaskDepText, // No truncation for dependencies
|
||||||
|
subtask.complexityScore // Add subtask complexity if available
|
||||||
|
? chalk.gray(`${subtask.complexityScore}`) // Display subtask complexity
|
||||||
|
: chalk.gray('N/A')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -597,6 +643,8 @@ function listTasks(
|
|||||||
subtasksSection = `\n\n${chalk.white.bold('Subtasks:')}\n`;
|
subtasksSection = `\n\n${chalk.white.bold('Subtasks:')}\n`;
|
||||||
subtasksSection += parentTaskForSubtasks.subtasks
|
subtasksSection += parentTaskForSubtasks.subtasks
|
||||||
.map((subtask) => {
|
.map((subtask) => {
|
||||||
|
// Add complexity to subtask before display
|
||||||
|
addComplexityToTask(subtask, complexityReport);
|
||||||
// Using a more simplified format for subtask status display
|
// Using a more simplified format for subtask status display
|
||||||
const status = subtask.status || 'pending';
|
const status = subtask.status || 'pending';
|
||||||
const statusColors = {
|
const statusColors = {
|
||||||
@@ -625,8 +673,8 @@ function listTasks(
|
|||||||
'\n\n' +
|
'\n\n' +
|
||||||
// Use nextItem.priority, nextItem.status, nextItem.dependencies
|
// Use nextItem.priority, nextItem.status, nextItem.dependencies
|
||||||
`${chalk.white('Priority:')} ${priorityColors[nextItem.priority || 'medium'](nextItem.priority || 'medium')} ${chalk.white('Status:')} ${getStatusWithColor(nextItem.status, true)}\n` +
|
`${chalk.white('Priority:')} ${priorityColors[nextItem.priority || 'medium'](nextItem.priority || 'medium')} ${chalk.white('Status:')} ${getStatusWithColor(nextItem.status, true)}\n` +
|
||||||
`${chalk.white('Dependencies:')} ${nextItem.dependencies && nextItem.dependencies.length > 0 ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true) : chalk.gray('None')}\n\n` +
|
`${chalk.white('Dependencies:')} ${nextItem.dependencies && nextItem.dependencies.length > 0 ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true, complexityReport) : chalk.gray('None')}\n\n` +
|
||||||
// Use nextItem.description (Note: findNextTask doesn't return description, need to fetch original task/subtask for this)
|
// Use nextTask.description (Note: findNextTask doesn't return description, need to fetch original task/subtask for this)
|
||||||
// *** Fetching original item for description and details ***
|
// *** Fetching original item for description and details ***
|
||||||
`${chalk.white('Description:')} ${getWorkItemDescription(nextItem, data.tasks)}` +
|
`${chalk.white('Description:')} ${getWorkItemDescription(nextItem, data.tasks)}` +
|
||||||
subtasksSection + // <-- Subtasks are handled above now
|
subtasksSection + // <-- Subtasks are handled above now
|
||||||
|
|||||||
Reference in New Issue
Block a user