feat(cli): enhance task list display, CLI usability, responsive table, colored deps status, help output, expand cmd clarity, init instructions, version bump to 0.9.18
This commit is contained in:
@@ -41,7 +41,29 @@ const program = new Command();
|
|||||||
program
|
program
|
||||||
.name('task-master')
|
.name('task-master')
|
||||||
.description('Claude Task Master CLI')
|
.description('Claude Task Master CLI')
|
||||||
.version(version);
|
.version(version)
|
||||||
|
.addHelpText('afterAll', () => {
|
||||||
|
// Add the same help output that dev.js uses
|
||||||
|
const child = spawn('node', [devScriptPath, '--help'], {
|
||||||
|
stdio: ['inherit', 'pipe', 'inherit'],
|
||||||
|
cwd: process.cwd()
|
||||||
|
});
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
output += data.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', () => {
|
||||||
|
// Only display the custom help text part, not the commander-generated part
|
||||||
|
const customHelpStart = output.indexOf('Task Master CLI');
|
||||||
|
if (customHelpStart > -1) {
|
||||||
|
console.log('\n' + output.substring(customHelpStart));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return ''; // Return empty string to prevent immediate display
|
||||||
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('init')
|
.command('init')
|
||||||
@@ -164,12 +186,12 @@ program
|
|||||||
|
|
||||||
program
|
program
|
||||||
.command('expand')
|
.command('expand')
|
||||||
.description('Expand tasks with subtasks')
|
.description('Break down tasks into detailed subtasks')
|
||||||
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||||
.option('-i, --id <id>', 'Task ID to expand')
|
.option('-i, --id <id>', 'Task ID to expand')
|
||||||
.option('-a, --all', 'Expand all tasks')
|
.option('-a, --all', 'Expand all tasks')
|
||||||
.option('-n, --num <number>', 'Number of subtasks to generate')
|
.option('-n, --num <number>', 'Number of subtasks to generate')
|
||||||
.option('-r, --no-research', 'Disable Perplexity AI for research-backed subtask generation')
|
.option('--research', 'Enable Perplexity AI for research-backed subtask generation')
|
||||||
.option('-p, --prompt <text>', 'Additional context to guide subtask generation')
|
.option('-p, --prompt <text>', 'Additional context to guide subtask generation')
|
||||||
.option('--force', 'Force regeneration of subtasks for tasks that already have them')
|
.option('--force', 'Force regeneration of subtasks for tasks that already have them')
|
||||||
.action((options) => {
|
.action((options) => {
|
||||||
@@ -178,7 +200,7 @@ program
|
|||||||
if (options.id) args.push('--id', options.id);
|
if (options.id) args.push('--id', options.id);
|
||||||
if (options.all) args.push('--all');
|
if (options.all) args.push('--all');
|
||||||
if (options.num) args.push('--num', options.num);
|
if (options.num) args.push('--num', options.num);
|
||||||
if (!options.research) args.push('--no-research');
|
if (options.research) args.push('--research');
|
||||||
if (options.prompt) args.push('--prompt', options.prompt);
|
if (options.prompt) args.push('--prompt', options.prompt);
|
||||||
if (options.force) args.push('--force');
|
if (options.force) args.push('--force');
|
||||||
runDevScript(args);
|
runDevScript(args);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "task-master-ai",
|
"name": "task-master-ai",
|
||||||
"version": "0.9.16",
|
"version": "0.9.18",
|
||||||
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -72,4 +72,4 @@
|
|||||||
"mock-fs": "^5.5.0",
|
"mock-fs": "^5.5.0",
|
||||||
"supertest": "^7.1.0"
|
"supertest": "^7.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,7 +542,7 @@ function createProjectStructure(projectName, projectDescription, projectVersion,
|
|||||||
chalk.white('1. ') + chalk.yellow('Rename .env.example to .env and add your ANTHROPIC_API_KEY and PERPLEXITY_API_KEY') + '\n' +
|
chalk.white('1. ') + chalk.yellow('Rename .env.example to .env and add your ANTHROPIC_API_KEY and PERPLEXITY_API_KEY') + '\n' +
|
||||||
chalk.white('2. ') + chalk.yellow('Discuss your idea with AI, and once ready ask for a PRD using the example_prd.txt file, and save what you get to scripts/PRD.txt') + '\n' +
|
chalk.white('2. ') + chalk.yellow('Discuss your idea with AI, and once ready ask for a PRD using the example_prd.txt file, and save what you get to scripts/PRD.txt') + '\n' +
|
||||||
chalk.white('3. ') + chalk.yellow('Ask Cursor Agent to parse your PRD.txt and generate tasks') + '\n' +
|
chalk.white('3. ') + chalk.yellow('Ask Cursor Agent to parse your PRD.txt and generate tasks') + '\n' +
|
||||||
chalk.white(' └─ ') + chalk.dim('You can also run ') + chalk.cyan('npm run parse-prd -- --input=<your-prd-file.txt>') + '\n' +
|
chalk.white(' └─ ') + chalk.dim('You can also run ') + chalk.cyan('task-master parse-prd <your-prd-file.txt>') + '\n' +
|
||||||
chalk.white('4. ') + chalk.yellow('Ask Cursor to analyze the complexity of your tasks') + '\n' +
|
chalk.white('4. ') + chalk.yellow('Ask Cursor to analyze the complexity of your tasks') + '\n' +
|
||||||
chalk.white('5. ') + chalk.yellow('Ask Cursor which task is next to determine where to start') + '\n' +
|
chalk.white('5. ') + chalk.yellow('Ask Cursor which task is next to determine where to start') + '\n' +
|
||||||
chalk.white('6. ') + chalk.yellow('Ask Cursor to expand any complex tasks that are too large or complex.') + '\n' +
|
chalk.white('6. ') + chalk.yellow('Ask Cursor to expand any complex tasks that are too large or complex.') + '\n' +
|
||||||
|
|||||||
@@ -379,7 +379,17 @@ function generateTaskFiles(tasksPath, outputDir) {
|
|||||||
// Handle numeric dependencies to other subtasks
|
// Handle numeric dependencies to other subtasks
|
||||||
const foundSubtask = task.subtasks.find(st => st.id === depId);
|
const foundSubtask = task.subtasks.find(st => st.id === depId);
|
||||||
if (foundSubtask) {
|
if (foundSubtask) {
|
||||||
return `${depId} (${foundSubtask.status || 'pending'})`;
|
const isDone = foundSubtask.status === 'done' || foundSubtask.status === 'completed';
|
||||||
|
const isInProgress = foundSubtask.status === 'in-progress';
|
||||||
|
|
||||||
|
// Use consistent color formatting instead of emojis
|
||||||
|
if (isDone) {
|
||||||
|
return chalk.green.bold(`${task.id}.${depId}`);
|
||||||
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(`${task.id}.${depId}`);
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(`${task.id}.${depId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return depId.toString();
|
return depId.toString();
|
||||||
@@ -675,8 +685,20 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
`Priority: ${chalk.white(nextTask.priority || 'medium')} Dependencies: ${formatDependenciesWithStatus(nextTask.dependencies, data.tasks, true)}` :
|
`Priority: ${chalk.white(nextTask.priority || 'medium')} Dependencies: ${formatDependenciesWithStatus(nextTask.dependencies, data.tasks, true)}` :
|
||||||
chalk.yellow('No eligible tasks found. All tasks are either completed or have unsatisfied dependencies.');
|
chalk.yellow('No eligible tasks found. All tasks are either completed or have unsatisfied dependencies.');
|
||||||
|
|
||||||
// Get terminal width
|
// Get terminal width - more reliable method
|
||||||
const terminalWidth = process.stdout.columns || 80;
|
let terminalWidth;
|
||||||
|
try {
|
||||||
|
// Try to get the actual terminal columns
|
||||||
|
terminalWidth = process.stdout.columns;
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback if columns cannot be determined
|
||||||
|
log('debug', 'Could not determine terminal width, using default');
|
||||||
|
}
|
||||||
|
// Ensure we have a reasonable default if detection fails
|
||||||
|
terminalWidth = terminalWidth || 80;
|
||||||
|
|
||||||
|
// Ensure terminal width is at least a minimum value to prevent layout issues
|
||||||
|
terminalWidth = Math.max(terminalWidth, 80);
|
||||||
|
|
||||||
// Create dashboard content
|
// Create dashboard content
|
||||||
const projectDashboardContent =
|
const projectDashboardContent =
|
||||||
@@ -794,30 +816,17 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the previously defined terminalWidth for responsive table
|
// COMPLETELY REVISED TABLE APPROACH
|
||||||
|
// Define fixed column widths based on terminal size
|
||||||
|
const idWidth = 10;
|
||||||
|
const statusWidth = 20;
|
||||||
|
const priorityWidth = 10;
|
||||||
|
const depsWidth = 25;
|
||||||
|
|
||||||
// Define column widths based on percentage of available space
|
// Calculate title width from available space
|
||||||
// Reserve minimum widths for ID, Status, Priority and Dependencies
|
const titleWidth = terminalWidth - idWidth - statusWidth - priorityWidth - depsWidth - 10; // 10 for borders and padding
|
||||||
const minIdWidth = 4;
|
|
||||||
const minStatusWidth = 12;
|
|
||||||
const minPriorityWidth = 8;
|
|
||||||
const minDepsWidth = 15;
|
|
||||||
|
|
||||||
// Calculate available space for the title column
|
// Create a table with correct borders and spacing
|
||||||
const minFixedColumnsWidth = minIdWidth + minStatusWidth + minPriorityWidth + minDepsWidth;
|
|
||||||
const tableMargin = 10; // Account for table borders and padding
|
|
||||||
const availableTitleWidth = Math.max(30, terminalWidth - minFixedColumnsWidth - tableMargin);
|
|
||||||
|
|
||||||
// Scale column widths proportionally
|
|
||||||
const colWidths = [
|
|
||||||
minIdWidth,
|
|
||||||
availableTitleWidth,
|
|
||||||
minStatusWidth,
|
|
||||||
minPriorityWidth,
|
|
||||||
minDepsWidth
|
|
||||||
];
|
|
||||||
|
|
||||||
// Create a table for tasks
|
|
||||||
const table = new Table({
|
const table = new Table({
|
||||||
head: [
|
head: [
|
||||||
chalk.cyan.bold('ID'),
|
chalk.cyan.bold('ID'),
|
||||||
@@ -826,61 +835,118 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
chalk.cyan.bold('Priority'),
|
chalk.cyan.bold('Priority'),
|
||||||
chalk.cyan.bold('Dependencies')
|
chalk.cyan.bold('Dependencies')
|
||||||
],
|
],
|
||||||
colWidths: colWidths,
|
colWidths: [idWidth, titleWidth, statusWidth, priorityWidth, depsWidth],
|
||||||
wordWrap: true
|
style: {
|
||||||
|
head: [], // No special styling for header
|
||||||
|
border: [], // No special styling for border
|
||||||
|
compact: false // Use default spacing
|
||||||
|
},
|
||||||
|
wordWrap: true,
|
||||||
|
wrapOnWordBoundary: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add tasks to the table
|
// Process tasks for the table
|
||||||
filteredTasks.forEach(task => {
|
filteredTasks.forEach(task => {
|
||||||
// Get a list of task dependencies
|
// Format dependencies with status indicators (colored)
|
||||||
const formattedDeps = formatDependenciesWithStatus(task.dependencies, data.tasks, true);
|
let depText = 'None';
|
||||||
|
if (task.dependencies && task.dependencies.length > 0) {
|
||||||
|
// Use the proper formatDependenciesWithStatus function for colored status
|
||||||
|
depText = formatDependenciesWithStatus(task.dependencies, data.tasks, true);
|
||||||
|
} else {
|
||||||
|
depText = chalk.gray('None');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up any ANSI codes or confusing characters
|
||||||
|
const cleanTitle = task.title.replace(/\n/g, ' ');
|
||||||
|
|
||||||
|
// Get priority color
|
||||||
|
const priorityColor = {
|
||||||
|
'high': chalk.red,
|
||||||
|
'medium': chalk.yellow,
|
||||||
|
'low': chalk.gray
|
||||||
|
}[task.priority || 'medium'] || chalk.white;
|
||||||
|
|
||||||
|
// Format status
|
||||||
|
const status = getStatusWithColor(task.status, true);
|
||||||
|
|
||||||
|
// Add the row without truncating dependencies
|
||||||
table.push([
|
table.push([
|
||||||
task.id,
|
task.id.toString(),
|
||||||
truncate(task.title, availableTitleWidth - 3), // -3 for table cell padding
|
truncate(cleanTitle, titleWidth - 3),
|
||||||
getStatusWithColor(task.status),
|
status,
|
||||||
chalk.white(task.priority || 'medium'),
|
priorityColor(truncate(task.priority || 'medium', priorityWidth - 2)),
|
||||||
formattedDeps
|
depText // No truncation for dependencies
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Add subtasks if requested
|
// Add subtasks if requested
|
||||||
if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
|
if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
|
||||||
task.subtasks.forEach(subtask => {
|
task.subtasks.forEach(subtask => {
|
||||||
// Format subtask dependencies
|
// Format subtask dependencies with status indicators
|
||||||
let subtaskDeps = '';
|
let subtaskDepText = 'None';
|
||||||
|
|
||||||
if (subtask.dependencies && subtask.dependencies.length > 0) {
|
if (subtask.dependencies && subtask.dependencies.length > 0) {
|
||||||
subtaskDeps = subtask.dependencies.map(depId => {
|
// Handle both subtask-to-subtask and subtask-to-task dependencies
|
||||||
|
const formattedDeps = subtask.dependencies.map(depId => {
|
||||||
// Check if it's a dependency on another subtask
|
// Check if it's a dependency on another subtask
|
||||||
const foundSubtask = task.subtasks.find(st => st.id === depId);
|
if (typeof depId === 'number' && depId < 100) {
|
||||||
|
const foundSubtask = task.subtasks.find(st => st.id === depId);
|
||||||
if (foundSubtask) {
|
if (foundSubtask) {
|
||||||
const isDone = foundSubtask.status === 'done' || foundSubtask.status === 'completed';
|
const isDone = foundSubtask.status === 'done' || foundSubtask.status === 'completed';
|
||||||
const statusIcon = isDone ?
|
const isInProgress = foundSubtask.status === 'in-progress';
|
||||||
chalk.green('✅') :
|
|
||||||
chalk.yellow('⏱️');
|
// Use consistent color formatting instead of emojis
|
||||||
|
if (isDone) {
|
||||||
return `${statusIcon} ${chalk.cyan(`${task.id}.${depId}`)}`;
|
return chalk.green.bold(`${task.id}.${depId}`);
|
||||||
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(`${task.id}.${depId}`);
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(`${task.id}.${depId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Default to regular task dependency
|
||||||
|
const depTask = data.tasks.find(t => t.id === depId);
|
||||||
|
if (depTask) {
|
||||||
|
const isDone = depTask.status === 'done' || depTask.status === 'completed';
|
||||||
|
const isInProgress = depTask.status === 'in-progress';
|
||||||
|
// Use the same color scheme as in formatDependenciesWithStatus
|
||||||
|
if (isDone) {
|
||||||
|
return chalk.green.bold(`${depId}`);
|
||||||
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(`${depId}`);
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(`${depId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chalk.cyan(depId.toString());
|
return chalk.cyan(depId.toString());
|
||||||
}).join(', ');
|
}).join(', ');
|
||||||
} else {
|
|
||||||
subtaskDeps = chalk.gray('None');
|
subtaskDepText = formattedDeps || chalk.gray('None');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the subtask row without truncating dependencies
|
||||||
table.push([
|
table.push([
|
||||||
`${task.id}.${subtask.id}`,
|
`${task.id}.${subtask.id}`,
|
||||||
chalk.dim(`└─ ${truncate(subtask.title, availableTitleWidth - 5)}`), // -5 for the "└─ " prefix
|
chalk.dim(`└─ ${truncate(subtask.title, titleWidth - 5)}`),
|
||||||
getStatusWithColor(subtask.status),
|
getStatusWithColor(subtask.status, true),
|
||||||
chalk.dim('-'),
|
chalk.dim('-'),
|
||||||
subtaskDeps
|
subtaskDepText // No truncation for dependencies
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(table.toString());
|
// Ensure we output the table even if it had to wrap
|
||||||
|
try {
|
||||||
|
console.log(table.toString());
|
||||||
|
} catch (err) {
|
||||||
|
log('error', `Error rendering table: ${err.message}`);
|
||||||
|
|
||||||
|
// Fall back to simpler output
|
||||||
|
console.log(chalk.yellow('\nFalling back to simple task list due to terminal width constraints:'));
|
||||||
|
filteredTasks.forEach(task => {
|
||||||
|
console.log(`${chalk.cyan(task.id)}: ${chalk.white(task.title)} - ${getStatusWithColor(task.status)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Show filter info if applied
|
// Show filter info if applied
|
||||||
if (statusFilter) {
|
if (statusFilter) {
|
||||||
@@ -891,8 +957,8 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
// Define priority colors
|
// Define priority colors
|
||||||
const priorityColors = {
|
const priorityColors = {
|
||||||
'high': chalk.red.bold,
|
'high': chalk.red.bold,
|
||||||
'medium': chalk.yellow,
|
'medium': chalk.yellow,
|
||||||
'low': chalk.gray
|
'low': chalk.gray
|
||||||
};
|
};
|
||||||
|
|
||||||
// Show next task box in a prominent color
|
// Show next task box in a prominent color
|
||||||
@@ -902,25 +968,35 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
if (nextTask.subtasks && nextTask.subtasks.length > 0) {
|
if (nextTask.subtasks && nextTask.subtasks.length > 0) {
|
||||||
subtasksSection = `\n\n${chalk.white.bold('Subtasks:')}\n`;
|
subtasksSection = `\n\n${chalk.white.bold('Subtasks:')}\n`;
|
||||||
subtasksSection += nextTask.subtasks.map(subtask => {
|
subtasksSection += nextTask.subtasks.map(subtask => {
|
||||||
const subtaskStatus = getStatusWithColor(subtask.status || 'pending');
|
// Using a more simplified format for subtask status display
|
||||||
return `${chalk.cyan(`${nextTask.id}.${subtask.id}`)} ${subtaskStatus} ${subtask.title}`;
|
const status = subtask.status || 'pending';
|
||||||
|
const statusColors = {
|
||||||
|
'done': chalk.green,
|
||||||
|
'completed': chalk.green,
|
||||||
|
'pending': chalk.yellow,
|
||||||
|
'in-progress': chalk.blue,
|
||||||
|
'deferred': chalk.gray,
|
||||||
|
'blocked': chalk.red
|
||||||
|
};
|
||||||
|
const statusColor = statusColors[status.toLowerCase()] || chalk.white;
|
||||||
|
return `${chalk.cyan(`${nextTask.id}.${subtask.id}`)} [${statusColor(status)}] ${subtask.title}`;
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(boxen(
|
console.log(boxen(
|
||||||
chalk.hex('#FF8800').bold(`🔥 Next Task to Work On: #${nextTask.id} - ${nextTask.title}`) + '\n\n' +
|
chalk.hex('#FF8800').bold(`🔥 Next Task to Work On: #${nextTask.id} - ${nextTask.title}`) + '\n\n' +
|
||||||
`${chalk.white('Priority:')} ${priorityColors[nextTask.priority || 'medium'](nextTask.priority || 'medium')} ${chalk.white('Status:')} ${getStatusWithColor(nextTask.status)}\n` +
|
`${chalk.white('Priority:')} ${priorityColors[nextTask.priority || 'medium'](nextTask.priority || 'medium')} ${chalk.white('Status:')} ${getStatusWithColor(nextTask.status, true)}\n` +
|
||||||
`${chalk.white('Dependencies:')} ${formatDependenciesWithStatus(nextTask.dependencies, data.tasks, true)}\n\n` +
|
`${chalk.white('Dependencies:')} ${nextTask.dependencies && nextTask.dependencies.length > 0 ? formatDependenciesWithStatus(nextTask.dependencies, data.tasks, true) : chalk.gray('None')}\n\n` +
|
||||||
`${chalk.white('Description:')} ${nextTask.description}` +
|
`${chalk.white('Description:')} ${nextTask.description}` +
|
||||||
subtasksSection + '\n\n' +
|
subtasksSection + '\n\n' +
|
||||||
`${chalk.cyan('Start working:')} ${chalk.yellow(`task-master set-status --id=${nextTask.id} --status=in-progress`)}\n` +
|
`${chalk.cyan('Start working:')} ${chalk.yellow(`task-master set-status --id=${nextTask.id} --status=in-progress`)}\n` +
|
||||||
`${chalk.cyan('View details:')} ${chalk.yellow(`task-master show ${nextTask.id}`)}`,
|
`${chalk.cyan('View details:')} ${chalk.yellow(`task-master show ${nextTask.id}`)}`,
|
||||||
{
|
{
|
||||||
padding: 1,
|
padding: { left: 2, right: 2, top: 1, bottom: 1 },
|
||||||
borderColor: '#FF8800',
|
borderColor: '#FF8800',
|
||||||
borderStyle: 'round',
|
borderStyle: 'round',
|
||||||
margin: { top: 1, bottom: 1 },
|
margin: { top: 1, bottom: 1 },
|
||||||
title: '⚡ RECOMMENDED NEXT ACTION ⚡',
|
title: '⚡ RECOMMENDED NEXT TASK ⚡',
|
||||||
titleAlignment: 'center',
|
titleAlignment: 'center',
|
||||||
width: terminalWidth - 4, // Use full terminal width minus a small margin
|
width: terminalWidth - 4, // Use full terminal width minus a small margin
|
||||||
fullscreen: false // Keep it expandable but not literally fullscreen
|
fullscreen: false // Keep it expandable but not literally fullscreen
|
||||||
@@ -935,7 +1011,7 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
borderColor: '#FF8800',
|
borderColor: '#FF8800',
|
||||||
borderStyle: 'round',
|
borderStyle: 'round',
|
||||||
margin: { top: 1, bottom: 1 },
|
margin: { top: 1, bottom: 1 },
|
||||||
title: '⚡ NEXT ACTION ⚡',
|
title: '⚡ NEXT TASK ⚡',
|
||||||
titleAlignment: 'center',
|
titleAlignment: 'center',
|
||||||
width: terminalWidth - 4, // Use full terminal width minus a small margin
|
width: terminalWidth - 4, // Use full terminal width minus a small margin
|
||||||
}
|
}
|
||||||
@@ -962,6 +1038,23 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely apply chalk coloring, stripping ANSI codes when calculating string length
|
||||||
|
* @param {string} text - Original text
|
||||||
|
* @param {Function} colorFn - Chalk color function
|
||||||
|
* @param {number} maxLength - Maximum allowed length
|
||||||
|
* @returns {string} Colored text that won't break table layout
|
||||||
|
*/
|
||||||
|
function safeColor(text, colorFn, maxLength = 0) {
|
||||||
|
if (!text) return '';
|
||||||
|
|
||||||
|
// If maxLength is provided, truncate the text first
|
||||||
|
const baseText = maxLength > 0 ? truncate(text, maxLength) : text;
|
||||||
|
|
||||||
|
// Apply color function if provided, otherwise return as is
|
||||||
|
return colorFn ? colorFn(baseText) : baseText;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand a task with subtasks
|
* Expand a task with subtasks
|
||||||
* @param {number} taskId - Task ID to expand
|
* @param {number} taskId - Task ID to expand
|
||||||
@@ -1087,7 +1180,7 @@ async function expandTask(taskId, numSubtasks = CONFIG.defaultSubtasks, useResea
|
|||||||
`${taskId}.${subtask.id}`,
|
`${taskId}.${subtask.id}`,
|
||||||
truncate(subtask.title, 47),
|
truncate(subtask.title, 47),
|
||||||
deps,
|
deps,
|
||||||
getStatusWithColor(subtask.status)
|
getStatusWithColor(subtask.status, true)
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -97,24 +97,42 @@ function createProgressBar(percent, length = 30) {
|
|||||||
/**
|
/**
|
||||||
* Get a colored status string based on the status value
|
* Get a colored status string based on the status value
|
||||||
* @param {string} status - Task status (e.g., "done", "pending", "in-progress")
|
* @param {string} status - Task status (e.g., "done", "pending", "in-progress")
|
||||||
|
* @param {boolean} forTable - Whether the status is being displayed in a table
|
||||||
* @returns {string} Colored status string
|
* @returns {string} Colored status string
|
||||||
*/
|
*/
|
||||||
function getStatusWithColor(status) {
|
function getStatusWithColor(status, forTable = false) {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
return chalk.gray('❓ unknown');
|
return chalk.gray('❓ unknown');
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusConfig = {
|
const statusConfig = {
|
||||||
'done': { color: chalk.green, icon: '✅' },
|
'done': { color: chalk.green, icon: '✅', tableIcon: '✓' },
|
||||||
'completed': { color: chalk.green, icon: '✅' },
|
'completed': { color: chalk.green, icon: '✅', tableIcon: '✓' },
|
||||||
'pending': { color: chalk.yellow, icon: '⏱️' },
|
'pending': { color: chalk.yellow, icon: '⏱️', tableIcon: '⏱' },
|
||||||
'in-progress': { color: chalk.blue, icon: '🔄' },
|
'in-progress': { color: chalk.hex('#FFA500'), icon: '🔄', tableIcon: '►' },
|
||||||
'deferred': { color: chalk.gray, icon: '⏱️' },
|
'deferred': { color: chalk.gray, icon: '⏱️', tableIcon: '⏱' },
|
||||||
'blocked': { color: chalk.red, icon: '❌' },
|
'blocked': { color: chalk.red, icon: '❌', tableIcon: '✗' },
|
||||||
'review': { color: chalk.magenta, icon: '👀' }
|
'review': { color: chalk.magenta, icon: '👀', tableIcon: '👁' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = statusConfig[status.toLowerCase()] || { color: chalk.red, icon: '❌' };
|
const config = statusConfig[status.toLowerCase()] || { color: chalk.red, icon: '❌', tableIcon: '✗' };
|
||||||
|
|
||||||
|
// Use simpler icons for table display to prevent border issues
|
||||||
|
if (forTable) {
|
||||||
|
// Use ASCII characters instead of Unicode for completely stable display
|
||||||
|
const simpleIcons = {
|
||||||
|
'done': '✓',
|
||||||
|
'completed': '✓',
|
||||||
|
'pending': '○',
|
||||||
|
'in-progress': '►',
|
||||||
|
'deferred': 'x',
|
||||||
|
'blocked': '!', // Using plain x character for better compatibility
|
||||||
|
'review': '?' // Using circled dot symbol
|
||||||
|
};
|
||||||
|
const simpleIcon = simpleIcons[status.toLowerCase()] || 'x';
|
||||||
|
return config.color(`${simpleIcon} ${status}`);
|
||||||
|
}
|
||||||
|
|
||||||
return config.color(`${config.icon} ${status}`);
|
return config.color(`${config.icon} ${status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,27 +149,91 @@ function formatDependenciesWithStatus(dependencies, allTasks, forConsole = false
|
|||||||
}
|
}
|
||||||
|
|
||||||
const formattedDeps = dependencies.map(depId => {
|
const formattedDeps = dependencies.map(depId => {
|
||||||
const depTask = findTaskById(allTasks, depId);
|
const depIdStr = depId.toString(); // Ensure string format for display
|
||||||
|
|
||||||
|
// Check if it's already a fully qualified subtask ID (like "22.1")
|
||||||
|
if (depIdStr.includes('.')) {
|
||||||
|
const [parentId, subtaskId] = depIdStr.split('.').map(id => parseInt(id, 10));
|
||||||
|
|
||||||
|
// Find the parent task
|
||||||
|
const parentTask = allTasks.find(t => t.id === parentId);
|
||||||
|
if (!parentTask || !parentTask.subtasks) {
|
||||||
|
return forConsole ?
|
||||||
|
chalk.red(`${depIdStr} (Not found)`) :
|
||||||
|
`${depIdStr} (Not found)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the subtask
|
||||||
|
const subtask = parentTask.subtasks.find(st => st.id === subtaskId);
|
||||||
|
if (!subtask) {
|
||||||
|
return forConsole ?
|
||||||
|
chalk.red(`${depIdStr} (Not found)`) :
|
||||||
|
`${depIdStr} (Not found)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format with status
|
||||||
|
const status = subtask.status || 'pending';
|
||||||
|
const isDone = status.toLowerCase() === 'done' || status.toLowerCase() === 'completed';
|
||||||
|
const isInProgress = status.toLowerCase() === 'in-progress';
|
||||||
|
|
||||||
|
if (forConsole) {
|
||||||
|
if (isDone) {
|
||||||
|
return chalk.green.bold(depIdStr);
|
||||||
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(depIdStr);
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(depIdStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusIcon = isDone ? '✅' : '⏱️';
|
||||||
|
return `${statusIcon} ${depIdStr} (${status})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If depId is a number less than 100, it's likely a reference to a subtask ID in the current task
|
||||||
|
// This case is typically handled elsewhere (in task-specific code) before calling this function
|
||||||
|
|
||||||
|
// For regular task dependencies (not subtasks)
|
||||||
|
// Convert string depId to number if needed
|
||||||
|
const numericDepId = typeof depId === 'string' ? parseInt(depId, 10) : depId;
|
||||||
|
|
||||||
|
// Look up the task using the numeric ID
|
||||||
|
const depTask = findTaskById(allTasks, numericDepId);
|
||||||
|
|
||||||
if (!depTask) {
|
if (!depTask) {
|
||||||
return forConsole ?
|
return forConsole ?
|
||||||
chalk.red(`${depId} (Not found)`) :
|
chalk.red(`${depIdStr} (Not found)`) :
|
||||||
`${depId} (Not found)`;
|
`${depIdStr} (Not found)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const status = depTask.status || 'pending';
|
const status = depTask.status || 'pending';
|
||||||
const isDone = status.toLowerCase() === 'done' || status.toLowerCase() === 'completed';
|
const isDone = status.toLowerCase() === 'done' || status.toLowerCase() === 'completed';
|
||||||
|
const isInProgress = status.toLowerCase() === 'in-progress';
|
||||||
|
|
||||||
|
// Apply colors for console output with more visible options
|
||||||
if (forConsole) {
|
if (forConsole) {
|
||||||
return isDone ?
|
if (isDone) {
|
||||||
chalk.green(`${depId}`) :
|
return chalk.green.bold(depIdStr); // Make completed dependencies bold green
|
||||||
chalk.red(`${depId}`);
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(depIdStr); // Use bright orange for in-progress (more visible)
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(depIdStr); // Make pending dependencies bold red
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusIcon = isDone ? '✅' : '⏱️';
|
const statusIcon = isDone ? '✅' : '⏱️';
|
||||||
return `${statusIcon} ${depId} (${status})`;
|
return `${statusIcon} ${depIdStr} (${status})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (forConsole) {
|
||||||
|
// Handle both single and multiple dependencies
|
||||||
|
if (dependencies.length === 1) {
|
||||||
|
return formattedDeps[0]; // Return the single colored dependency
|
||||||
|
}
|
||||||
|
// Join multiple dependencies with white commas
|
||||||
|
return formattedDeps.join(chalk.white(', '));
|
||||||
|
}
|
||||||
|
|
||||||
return formattedDeps.join(', ');
|
return formattedDeps.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +424,18 @@ function getComplexityWithColor(score) {
|
|||||||
return chalk.red(`🔴 ${score}`);
|
return chalk.red(`🔴 ${score}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate a string to a maximum length and add ellipsis if needed
|
||||||
|
* @param {string} str - The string to truncate
|
||||||
|
* @param {number} maxLength - Maximum length
|
||||||
|
* @returns {string} Truncated string
|
||||||
|
*/
|
||||||
|
function truncateString(str, maxLength) {
|
||||||
|
if (!str) return '';
|
||||||
|
if (str.length <= maxLength) return str;
|
||||||
|
return str.substring(0, maxLength - 3) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the next task to work on
|
* Display the next task to work on
|
||||||
* @param {string} tasksPath - Path to the tasks.json file
|
* @param {string} tasksPath - Path to the tasks.json file
|
||||||
@@ -386,7 +480,8 @@ async function displayNextTask(tasksPath) {
|
|||||||
chars: {
|
chars: {
|
||||||
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
||||||
},
|
},
|
||||||
colWidths: [15, 75]
|
colWidths: [15, Math.min(75, (process.stdout.columns - 20) || 60)],
|
||||||
|
wordWrap: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Priority with color
|
// Priority with color
|
||||||
@@ -424,15 +519,15 @@ async function displayNextTask(tasksPath) {
|
|||||||
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, margin: { top: 1, bottom: 0 }, borderColor: 'magenta', borderStyle: 'round' }
|
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, margin: { top: 1, bottom: 0 }, borderColor: 'magenta', borderStyle: 'round' }
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create a table for subtasks
|
// Create a table for subtasks with improved handling
|
||||||
const subtaskTable = new Table({
|
const subtaskTable = new Table({
|
||||||
head: [
|
head: [
|
||||||
chalk.magenta.bold('ID'),
|
chalk.magenta.bold('ID'),
|
||||||
chalk.magenta.bold('Status'),
|
chalk.magenta.bold('Status'),
|
||||||
chalk.magenta.bold('Title'),
|
chalk.magenta.bold('Title'),
|
||||||
chalk.magenta.bold('Dependencies')
|
chalk.magenta.bold('Deps')
|
||||||
],
|
],
|
||||||
colWidths: [6, 12, 50, 20],
|
colWidths: [6, 12, Math.min(50, process.stdout.columns - 65 || 30), 30],
|
||||||
style: {
|
style: {
|
||||||
head: [],
|
head: [],
|
||||||
border: [],
|
border: [],
|
||||||
@@ -442,7 +537,8 @@ async function displayNextTask(tasksPath) {
|
|||||||
},
|
},
|
||||||
chars: {
|
chars: {
|
||||||
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
||||||
}
|
},
|
||||||
|
wordWrap: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add subtasks to table
|
// Add subtasks to table
|
||||||
@@ -460,11 +556,29 @@ async function displayNextTask(tasksPath) {
|
|||||||
// Format dependencies with correct notation
|
// Format dependencies with correct notation
|
||||||
const formattedDeps = st.dependencies.map(depId => {
|
const formattedDeps = st.dependencies.map(depId => {
|
||||||
if (typeof depId === 'number' && depId < 100) {
|
if (typeof depId === 'number' && depId < 100) {
|
||||||
return `${nextTask.id}.${depId}`;
|
const foundSubtask = nextTask.subtasks.find(st => st.id === depId);
|
||||||
|
if (foundSubtask) {
|
||||||
|
const isDone = foundSubtask.status === 'done' || foundSubtask.status === 'completed';
|
||||||
|
const isInProgress = foundSubtask.status === 'in-progress';
|
||||||
|
|
||||||
|
// Use consistent color formatting instead of emojis
|
||||||
|
if (isDone) {
|
||||||
|
return chalk.green.bold(`${nextTask.id}.${depId}`);
|
||||||
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(`${nextTask.id}.${depId}`);
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(`${nextTask.id}.${depId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chalk.red(`${nextTask.id}.${depId} (Not found)`);
|
||||||
}
|
}
|
||||||
return depId;
|
return depId;
|
||||||
});
|
});
|
||||||
subtaskDeps = formatDependenciesWithStatus(formattedDeps, data.tasks, true);
|
|
||||||
|
// Join the formatted dependencies directly instead of passing to formatDependenciesWithStatus again
|
||||||
|
subtaskDeps = formattedDeps.length === 1
|
||||||
|
? formattedDeps[0]
|
||||||
|
: formattedDeps.join(chalk.white(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
subtaskTable.push([
|
subtaskTable.push([
|
||||||
@@ -542,7 +656,8 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
chars: {
|
chars: {
|
||||||
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
||||||
},
|
},
|
||||||
colWidths: [15, 75]
|
colWidths: [15, Math.min(75, (process.stdout.columns - 20) || 60)],
|
||||||
|
wordWrap: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add subtask details to table
|
// Add subtask details to table
|
||||||
@@ -550,7 +665,7 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
[chalk.cyan.bold('ID:'), `${task.parentTask.id}.${task.id}`],
|
[chalk.cyan.bold('ID:'), `${task.parentTask.id}.${task.id}`],
|
||||||
[chalk.cyan.bold('Parent Task:'), `#${task.parentTask.id} - ${task.parentTask.title}`],
|
[chalk.cyan.bold('Parent Task:'), `#${task.parentTask.id} - ${task.parentTask.title}`],
|
||||||
[chalk.cyan.bold('Title:'), task.title],
|
[chalk.cyan.bold('Title:'), task.title],
|
||||||
[chalk.cyan.bold('Status:'), getStatusWithColor(task.status || 'pending')],
|
[chalk.cyan.bold('Status:'), getStatusWithColor(task.status || 'pending', true)],
|
||||||
[chalk.cyan.bold('Description:'), task.description || 'No description provided.']
|
[chalk.cyan.bold('Description:'), task.description || 'No description provided.']
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -574,7 +689,7 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderColor: 'blue', borderStyle: 'round', margin: { top: 1, bottom: 0 } }
|
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderColor: 'blue', borderStyle: 'round', margin: { top: 1, bottom: 0 } }
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create a table with task details
|
// Create a table with task details with improved handling
|
||||||
const taskTable = new Table({
|
const taskTable = new Table({
|
||||||
style: {
|
style: {
|
||||||
head: [],
|
head: [],
|
||||||
@@ -586,7 +701,8 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
chars: {
|
chars: {
|
||||||
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
||||||
},
|
},
|
||||||
colWidths: [15, 75]
|
colWidths: [15, Math.min(75, (process.stdout.columns - 20) || 60)],
|
||||||
|
wordWrap: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Priority with color
|
// Priority with color
|
||||||
@@ -601,7 +717,7 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
taskTable.push(
|
taskTable.push(
|
||||||
[chalk.cyan.bold('ID:'), task.id.toString()],
|
[chalk.cyan.bold('ID:'), task.id.toString()],
|
||||||
[chalk.cyan.bold('Title:'), task.title],
|
[chalk.cyan.bold('Title:'), task.title],
|
||||||
[chalk.cyan.bold('Status:'), getStatusWithColor(task.status || 'pending')],
|
[chalk.cyan.bold('Status:'), getStatusWithColor(task.status || 'pending', true)],
|
||||||
[chalk.cyan.bold('Priority:'), priorityColor(task.priority || 'medium')],
|
[chalk.cyan.bold('Priority:'), priorityColor(task.priority || 'medium')],
|
||||||
[chalk.cyan.bold('Dependencies:'), formatDependenciesWithStatus(task.dependencies, data.tasks, true)],
|
[chalk.cyan.bold('Dependencies:'), formatDependenciesWithStatus(task.dependencies, data.tasks, true)],
|
||||||
[chalk.cyan.bold('Description:'), task.description]
|
[chalk.cyan.bold('Description:'), task.description]
|
||||||
@@ -634,15 +750,15 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, margin: { top: 1, bottom: 0 }, borderColor: 'magenta', borderStyle: 'round' }
|
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, margin: { top: 1, bottom: 0 }, borderColor: 'magenta', borderStyle: 'round' }
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create a table for subtasks
|
// Create a table for subtasks with improved handling
|
||||||
const subtaskTable = new Table({
|
const subtaskTable = new Table({
|
||||||
head: [
|
head: [
|
||||||
chalk.magenta.bold('ID'),
|
chalk.magenta.bold('ID'),
|
||||||
chalk.magenta.bold('Status'),
|
chalk.magenta.bold('Status'),
|
||||||
chalk.magenta.bold('Title'),
|
chalk.magenta.bold('Title'),
|
||||||
chalk.magenta.bold('Dependencies')
|
chalk.magenta.bold('Deps')
|
||||||
],
|
],
|
||||||
colWidths: [6, 12, 50, 20],
|
colWidths: [6, 12, Math.min(50, process.stdout.columns - 65 || 30), 30],
|
||||||
style: {
|
style: {
|
||||||
head: [],
|
head: [],
|
||||||
border: [],
|
border: [],
|
||||||
@@ -652,7 +768,8 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
},
|
},
|
||||||
chars: {
|
chars: {
|
||||||
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
|
||||||
}
|
},
|
||||||
|
wordWrap: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add subtasks to table
|
// Add subtasks to table
|
||||||
@@ -670,11 +787,29 @@ async function displayTaskById(tasksPath, taskId) {
|
|||||||
// Format dependencies with correct notation
|
// Format dependencies with correct notation
|
||||||
const formattedDeps = st.dependencies.map(depId => {
|
const formattedDeps = st.dependencies.map(depId => {
|
||||||
if (typeof depId === 'number' && depId < 100) {
|
if (typeof depId === 'number' && depId < 100) {
|
||||||
return `${task.id}.${depId}`;
|
const foundSubtask = task.subtasks.find(st => st.id === depId);
|
||||||
|
if (foundSubtask) {
|
||||||
|
const isDone = foundSubtask.status === 'done' || foundSubtask.status === 'completed';
|
||||||
|
const isInProgress = foundSubtask.status === 'in-progress';
|
||||||
|
|
||||||
|
// Use consistent color formatting instead of emojis
|
||||||
|
if (isDone) {
|
||||||
|
return chalk.green.bold(`${task.id}.${depId}`);
|
||||||
|
} else if (isInProgress) {
|
||||||
|
return chalk.hex('#FFA500').bold(`${task.id}.${depId}`);
|
||||||
|
} else {
|
||||||
|
return chalk.red.bold(`${task.id}.${depId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chalk.red(`${task.id}.${depId} (Not found)`);
|
||||||
}
|
}
|
||||||
return depId;
|
return depId;
|
||||||
});
|
});
|
||||||
subtaskDeps = formatDependenciesWithStatus(formattedDeps, data.tasks, true);
|
|
||||||
|
// Join the formatted dependencies directly instead of passing to formatDependenciesWithStatus again
|
||||||
|
subtaskDeps = formattedDeps.length === 1
|
||||||
|
? formattedDeps[0]
|
||||||
|
: formattedDeps.join(chalk.white(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
subtaskTable.push([
|
subtaskTable.push([
|
||||||
|
|||||||
@@ -1414,7 +1414,7 @@
|
|||||||
"description": "Extend Task Master to function as an MCP server, allowing it to provide context management services to other applications following the Model Context Protocol specification.",
|
"description": "Extend Task Master to function as an MCP server, allowing it to provide context management services to other applications following the Model Context Protocol specification.",
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"22"
|
22
|
||||||
],
|
],
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"details": "This task involves implementing the Model Context Protocol server capabilities within Task Master. The implementation should:\n\n1. Create a new module `mcp-server.js` that implements the core MCP server functionality\n2. Implement the required MCP endpoints:\n - `/context` - For retrieving and updating context\n - `/models` - For listing available models\n - `/execute` - For executing operations with context\n3. Develop a context management system that can:\n - Store and retrieve context data efficiently\n - Handle context windowing and truncation when limits are reached\n - Support context metadata and tagging\n4. Add authentication and authorization mechanisms for MCP clients\n5. Implement proper error handling and response formatting according to MCP specifications\n6. Create configuration options in Task Master to enable/disable the MCP server functionality\n7. Add documentation for how to use Task Master as an MCP server\n8. Ensure the implementation is compatible with existing MCP clients\n9. Optimize for performance, especially for context retrieval operations\n10. Add logging for MCP server operations\n\nThe implementation should follow RESTful API design principles and should be able to handle concurrent requests from multiple clients.",
|
"details": "This task involves implementing the Model Context Protocol server capabilities within Task Master. The implementation should:\n\n1. Create a new module `mcp-server.js` that implements the core MCP server functionality\n2. Implement the required MCP endpoints:\n - `/context` - For retrieving and updating context\n - `/models` - For listing available models\n - `/execute` - For executing operations with context\n3. Develop a context management system that can:\n - Store and retrieve context data efficiently\n - Handle context windowing and truncation when limits are reached\n - Support context metadata and tagging\n4. Add authentication and authorization mechanisms for MCP clients\n5. Implement proper error handling and response formatting according to MCP specifications\n6. Create configuration options in Task Master to enable/disable the MCP server functionality\n7. Add documentation for how to use Task Master as an MCP server\n8. Ensure the implementation is compatible with existing MCP clients\n9. Optimize for performance, especially for context retrieval operations\n10. Add logging for MCP server operations\n\nThe implementation should follow RESTful API design principles and should be able to handle concurrent requests from multiple clients.",
|
||||||
@@ -1426,7 +1426,7 @@
|
|||||||
"description": "Create a new 'generate-test' command that leverages AI to automatically produce Jest test files for tasks based on their descriptions and subtasks.",
|
"description": "Create a new 'generate-test' command that leverages AI to automatically produce Jest test files for tasks based on their descriptions and subtasks.",
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"22"
|
22
|
||||||
],
|
],
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"details": "Implement a new command in the Task Master CLI that generates comprehensive Jest test files for tasks. The command should be callable as 'task-master generate-test --id=1' and should:\n\n1. Accept a task ID parameter to identify which task to generate tests for\n2. Retrieve the task and its subtasks from the task store\n3. Analyze the task description, details, and subtasks to understand implementation requirements\n4. Construct an appropriate prompt for an AI service (e.g., OpenAI API) that requests generation of Jest tests\n5. Process the AI response to create a well-formatted test file named 'task_XXX.test.js' where XXX is the zero-padded task ID\n6. Include appropriate test cases that cover the main functionality described in the task\n7. Generate mocks for external dependencies identified in the task description\n8. Create assertions that validate the expected behavior\n9. Handle both parent tasks and subtasks appropriately (for subtasks, name the file 'task_XXX_YYY.test.js' where YYY is the subtask ID)\n10. Include error handling for API failures, invalid task IDs, etc.\n11. Add appropriate documentation for the command in the help system\n\nThe implementation should utilize the existing AI service integration in the codebase and maintain consistency with the current command structure and error handling patterns.",
|
"details": "Implement a new command in the Task Master CLI that generates comprehensive Jest test files for tasks. The command should be callable as 'task-master generate-test --id=1' and should:\n\n1. Accept a task ID parameter to identify which task to generate tests for\n2. Retrieve the task and its subtasks from the task store\n3. Analyze the task description, details, and subtasks to understand implementation requirements\n4. Construct an appropriate prompt for an AI service (e.g., OpenAI API) that requests generation of Jest tests\n5. Process the AI response to create a well-formatted test file named 'task_XXX.test.js' where XXX is the zero-padded task ID\n6. Include appropriate test cases that cover the main functionality described in the task\n7. Generate mocks for external dependencies identified in the task description\n8. Create assertions that validate the expected behavior\n9. Handle both parent tasks and subtasks appropriately (for subtasks, name the file 'task_XXX_YYY.test.js' where YYY is the subtask ID)\n10. Include error handling for API failures, invalid task IDs, etc.\n11. Add appropriate documentation for the command in the help system\n\nThe implementation should utilize the existing AI service integration in the codebase and maintain consistency with the current command structure and error handling patterns.",
|
||||||
|
|||||||
Reference in New Issue
Block a user