Merge branch 'next' of github.com:eyaltoledano/claude-task-master into v017-adds

This commit is contained in:
Eyal Toledano
2025-06-13 23:26:08 -04:00
18 changed files with 6599 additions and 35 deletions

View File

@@ -42,6 +42,8 @@ import {
taskExists,
moveTask,
migrateProject
moveTask,
migrateProject
} from './task-manager.js';
import {
@@ -111,6 +113,8 @@ import {
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
import { syncTasksToReadme } from './sync-readme.js';
import { syncTasksToReadme } from './sync-readme.js';
/**
* Runs the interactive setup process for model configuration.
* @param {string|null} projectRoot - The resolved project root directory.
@@ -327,6 +331,7 @@ async function runInteractiveSetup(projectRoot) {
commonPrefix.push(customOllamaOption);
commonPrefix.push(customBedrockOption);
const prefixLength = commonPrefix.length; // Initial prefix length
const prefixLength = commonPrefix.length; // Initial prefix length
if (allowNone) {
@@ -521,6 +526,7 @@ async function runInteractiveSetup(projectRoot) {
console.error(
chalk.red(
'Error: AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY environment variables are missing. Please set them before using custom Bedrock models.'
'Error: AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY environment variables are missing. Please set them before using custom Bedrock models.'
)
);
setupSuccess = false;
@@ -679,6 +685,7 @@ function registerCommands(programInstance) {
'Path to the PRD file (alternative to positional argument)'
)
.option('-o, --output <file>', 'Output file path', TASKMASTER_TASKS_FILE)
.option('-o, --output <file>', 'Output file path', TASKMASTER_TASKS_FILE)
.option('-n, --num-tasks <number>', 'Number of tasks to generate', '10')
.option('-f, --force', 'Skip confirmation when overwriting existing tasks')
.option(
@@ -694,6 +701,7 @@ function registerCommands(programInstance) {
// Use input option if file argument not provided
const inputFile = file || options.input;
const defaultPrdPath = PRD_FILE;
const defaultPrdPath = PRD_FILE;
const numTasks = parseInt(options.numTasks, 10);
const outputPath = options.output;
const force = options.force || false;
@@ -778,10 +786,12 @@ function registerCommands(programInstance) {
console.log(
chalk.yellow(
`No PRD file specified and default PRD file not found at ${PRD_FILE}.`
`No PRD file specified and default PRD file not found at ${PRD_FILE}.`
)
);
console.log(
boxen(
`${chalk.white.bold('Parse PRD Help')}\n\n${chalk.cyan('Usage:')}\n task-master parse-prd <prd-file.txt> [options]\n\n${chalk.cyan('Options:')}\n -i, --input <file> Path to the PRD file (alternative to positional argument)\n -o, --output <file> Output file path (default: "${TASKMASTER_TASKS_FILE}")\n -n, --num-tasks <number> Number of tasks to generate (default: 10)\n -f, --force Skip confirmation when overwriting existing tasks\n --append Append new tasks to existing tasks.json instead of overwriting\n -r, --research Use Perplexity AI for research-backed task generation\n\n${chalk.cyan('Example:')}\n task-master parse-prd requirements.txt --num-tasks 15\n task-master parse-prd --input=requirements.txt\n task-master parse-prd --force\n task-master parse-prd requirements_v2.txt --append\n task-master parse-prd requirements.txt --research\n\n${chalk.yellow('Note: This command will:')}\n 1. Look for a PRD file at ${PRD_FILE} by default\n 2. Use the file specified by --input or positional argument if provided\n 3. Generate tasks from the PRD and either:\n - Overwrite any existing tasks.json file (default)\n - Append to existing tasks.json if --append is used`,
`${chalk.white.bold('Parse PRD Help')}\n\n${chalk.cyan('Usage:')}\n task-master parse-prd <prd-file.txt> [options]\n\n${chalk.cyan('Options:')}\n -i, --input <file> Path to the PRD file (alternative to positional argument)\n -o, --output <file> Output file path (default: "${TASKMASTER_TASKS_FILE}")\n -n, --num-tasks <number> Number of tasks to generate (default: 10)\n -f, --force Skip confirmation when overwriting existing tasks\n --append Append new tasks to existing tasks.json instead of overwriting\n -r, --research Use Perplexity AI for research-backed task generation\n\n${chalk.cyan('Example:')}\n task-master parse-prd requirements.txt --num-tasks 15\n task-master parse-prd --input=requirements.txt\n task-master parse-prd --force\n task-master parse-prd requirements_v2.txt --append\n task-master parse-prd requirements.txt --research\n\n${chalk.yellow('Note: This command will:')}\n 1. Look for a PRD file at ${PRD_FILE} by default\n 2. Use the file specified by --input or positional argument if provided\n 3. Generate tasks from the PRD and either:\n - Overwrite any existing tasks.json file (default)\n - Append to existing tasks.json if --append is used`,
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
)
@@ -841,6 +851,11 @@ function registerCommands(programInstance) {
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'--from <id>',
'Task ID to start updating from (tasks with ID >= this value will be updated)',
@@ -856,6 +871,7 @@ function registerCommands(programInstance) {
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const fromId = parseInt(options.from, 10); // Validation happens here
const prompt = options.prompt;
@@ -939,6 +955,11 @@ function registerCommands(programInstance) {
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option('-i, --id <id>', 'Task ID to update (required)')
.option(
'-p, --prompt <text>',
@@ -982,6 +1003,7 @@ function registerCommands(programInstance) {
// Parse the task ID and validate it's a number
const taskId = parseInt(options.id, 10);
if (Number.isNaN(taskId) || taskId <= 0) {
if (Number.isNaN(taskId) || taskId <= 0) {
console.error(
chalk.red(
@@ -1018,6 +1040,7 @@ function registerCommands(programInstance) {
console.error(
chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
);
if (tasksPath === TASKMASTER_TASKS_FILE) {
if (tasksPath === TASKMASTER_TASKS_FILE) {
console.log(
chalk.yellow(
@@ -1116,6 +1139,11 @@ function registerCommands(programInstance) {
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-i, --id <id>',
'Subtask ID to update in format "parentId.subtaskId" (required)'
@@ -1191,6 +1219,7 @@ function registerCommands(programInstance) {
console.error(
chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
);
if (tasksPath === TASKMASTER_TASKS_FILE) {
if (tasksPath === TASKMASTER_TASKS_FILE) {
console.log(
chalk.yellow(
@@ -1295,6 +1324,7 @@ function registerCommands(programInstance) {
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const outputDir = options.output;
const tag = options.tag;
@@ -1332,6 +1362,7 @@ function registerCommands(programInstance) {
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const taskId = options.id;
const status = options.status;
@@ -1379,10 +1410,16 @@ function registerCommands(programInstance) {
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-r, --report <report>',
'Path to the complexity report file',
COMPLEXITY_REPORT_FILE
COMPLEXITY_REPORT_FILE
)
.option('-s, --status <status>', 'Filter by status')
.option('--with-subtasks', 'Show subtasks for each task')
@@ -1446,6 +1483,7 @@ function registerCommands(programInstance) {
'--file <file>',
'Path to the tasks file (relative to project root)',
TASKMASTER_TASKS_FILE // Allow file override
TASKMASTER_TASKS_FILE // Allow file override
) // Allow file override
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
@@ -1526,6 +1564,7 @@ function registerCommands(programInstance) {
'-o, --output <file>',
'Output file path for the report',
COMPLEXITY_REPORT_FILE
COMPLEXITY_REPORT_FILE
)
.option(
'-m, --model <model>',
@@ -1541,6 +1580,11 @@ function registerCommands(programInstance) {
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-r, --research',
'Use Perplexity AI for research-backed complexity analysis'
@@ -1969,6 +2013,11 @@ ${result.result}
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-i, --id <ids>',
'Task IDs (comma-separated) to clear subtasks from'
@@ -1976,6 +2025,7 @@ ${result.result}
.option('--all', 'Clear subtasks from all tasks')
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const taskIds = options.id;
const all = options.all;
@@ -2022,6 +2072,11 @@ ${result.result}
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-p, --prompt <prompt>',
'Description of the task to add (required if not using manual fields)'
@@ -2064,6 +2119,14 @@ ${result.result}
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
if (!fs.existsSync(tasksPath)) {
console.error(
`❌ No tasks.json file found. Please run "task-master init" or create a tasks.json file at ${TASKMASTER_TASKS_FILE}`
);
process.exit(1);
}
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
if (!fs.existsSync(tasksPath)) {
console.error(
`❌ No tasks.json file found. Please run "task-master init" or create a tasks.json file at ${TASKMASTER_TASKS_FILE}`
@@ -2156,13 +2219,20 @@ ${result.result}
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-r, --report <report>',
'Path to the complexity report file',
COMPLEXITY_REPORT_FILE
COMPLEXITY_REPORT_FILE
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const reportPath = options.report;
const tag = options.tag;
@@ -2200,6 +2270,7 @@ ${result.result}
'-r, --report <report>',
'Path to the complexity report file',
COMPLEXITY_REPORT_FILE
COMPLEXITY_REPORT_FILE
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (taskId, options) => {
@@ -2221,6 +2292,7 @@ ${result.result}
process.exit(1);
}
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const reportPath = options.report;
@@ -2265,6 +2337,7 @@ ${result.result}
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const taskId = options.id;
const dependencyId = options.dependsOn;
@@ -2316,6 +2389,7 @@ ${result.result}
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const taskId = options.id;
const dependencyId = options.dependsOn;
@@ -2425,6 +2499,7 @@ ${result.result}
'-f, --file <file>',
'Path to the report file',
COMPLEXITY_REPORT_FILE
COMPLEXITY_REPORT_FILE
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
@@ -2458,6 +2533,11 @@ ${result.result}
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option('-p, --parent <id>', 'Parent task ID (required)')
.option('-i, --task-id <id>', 'Existing task ID to convert to subtask')
.option(
@@ -2633,6 +2713,7 @@ ${result.result}
function showAddSubtaskHelp() {
console.log(
boxen(
`${chalk.white.bold('Add Subtask Command Help')}\n\n${chalk.cyan('Usage:')}\n task-master add-subtask --parent=<id> [options]\n\n${chalk.cyan('Options:')}\n -p, --parent <id> Parent task ID (required)\n -i, --task-id <id> Existing task ID to convert to subtask\n -t, --title <title> Title for the new subtask\n -d, --description <text> Description for the new subtask\n --details <text> Implementation details for the new subtask\n --dependencies <ids> Comma-separated list of dependency IDs\n -s, --status <status> Status for the new subtask (default: "pending")\n -f, --file <file> Path to the tasks file (default: "${TASKMASTER_TASKS_FILE}")\n --skip-generate Skip regenerating task files\n\n${chalk.cyan('Examples:')}\n task-master add-subtask --parent=5 --task-id=8\n task-master add-subtask -p 5 -t "Implement login UI" -d "Create the login form"`,
`${chalk.white.bold('Add Subtask Command Help')}\n\n${chalk.cyan('Usage:')}\n task-master add-subtask --parent=<id> [options]\n\n${chalk.cyan('Options:')}\n -p, --parent <id> Parent task ID (required)\n -i, --task-id <id> Existing task ID to convert to subtask\n -t, --title <title> Title for the new subtask\n -d, --description <text> Description for the new subtask\n --details <text> Implementation details for the new subtask\n --dependencies <ids> Comma-separated list of dependency IDs\n -s, --status <status> Status for the new subtask (default: "pending")\n -f, --file <file> Path to the tasks file (default: "${TASKMASTER_TASKS_FILE}")\n --skip-generate Skip regenerating task files\n\n${chalk.cyan('Examples:')}\n task-master add-subtask --parent=5 --task-id=8\n task-master add-subtask -p 5 -t "Implement login UI" -d "Create the login form"`,
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
)
@@ -2648,6 +2729,11 @@ ${result.result}
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-i, --id <id>',
'Subtask ID(s) to remove in format "parentId.subtaskId" (can be comma-separated for multiple subtasks)'
@@ -2659,6 +2745,7 @@ ${result.result}
.option('--skip-generate', 'Skip regenerating task files')
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const subtaskIds = options.id;
const convertToTask = options.convert || false;
@@ -2790,6 +2877,9 @@ ${result.result}
' -f, --file <file> Path to the tasks file (default: "' +
TASKMASTER_TASKS_FILE +
'")\n' +
' -f, --file <file> Path to the tasks file (default: "' +
TASKMASTER_TASKS_FILE +
'")\n' +
' --skip-generate Skip regenerating task files\n\n' +
chalk.cyan('Examples:') +
'\n' +
@@ -2959,9 +3049,15 @@ ${result.result}
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option('-y, --yes', 'Skip confirmation prompt', false)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const taskIdsString = options.id;
@@ -3479,6 +3575,11 @@ Examples:
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'--from <id>',
'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")'
@@ -3489,6 +3590,7 @@ Examples:
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const sourceId = options.from;
const destinationId = options.to;
@@ -4289,6 +4391,7 @@ async function runCLI(argv = process.argv) {
// Setup and parse
// NOTE: getConfig() might be called during setupCLI->registerCommands if commands need config
// This means the ConfigurationError might be thrown here if configuration file is missing.
// This means the ConfigurationError might be thrown here if configuration file is missing.
const programInstance = setupCLI();
await programInstance.parseAsync(argv);
@@ -4348,7 +4451,10 @@ async function runCLI(argv = process.argv) {
'\n\n' +
chalk.white('Taskmaster now uses a ') +
chalk.yellow.bold('configuration file') +
chalk.white('Taskmaster now uses a ') +
chalk.yellow.bold('configuration file') +
chalk.white(
' in your project for AI model choices and settings.\n\n' +
' in your project for AI model choices and settings.\n\n' +
'This file appears to be '
) +
@@ -4362,6 +4468,7 @@ async function runCLI(argv = process.argv) {
'\n' +
chalk.white('* ') +
chalk.yellow.bold('Configuration file') +
chalk.yellow.bold('Configuration file') +
chalk.white(
': Stores your AI model settings (do not manually edit)\n'
) +