From b3d6e61e6b2fe512d262ae446e1bcd0ebfc497ba Mon Sep 17 00:00:00 2001 From: Eyal Toledano Date: Tue, 25 Mar 2025 00:42:59 -0400 Subject: [PATCH] fix: ensure CLI correctly handles kebab-case options - Fixed CLI wrapper to convert camelCase options to kebab-case when passing to dev.js - Added explicit support for --input option in parse-prd command - Updated commands.mdc to clarify Commander.js camelCase/kebab-case behavior --- .cursor/rules/commands.mdc | 2 ++ .cursor/rules/new_features.mdc | 2 +- bin/task-master.js | 23 ++++++++++++++++++++--- package.json | 8 ++++---- scripts/modules/commands.js | 18 ++++++++++++------ scripts/sample-prd.txt | 3 +++ 6 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 scripts/sample-prd.txt diff --git a/.cursor/rules/commands.mdc b/.cursor/rules/commands.mdc index 534a44b9..4f80ac09 100644 --- a/.cursor/rules/commands.mdc +++ b/.cursor/rules/commands.mdc @@ -50,6 +50,8 @@ alwaysApply: false .option('-p, --path ', 'Output directory') // Should be --output ``` + > **Note**: Although options are defined with kebab-case (`--num-tasks`), Commander.js stores them internally as camelCase properties. Access them in code as `options.numTasks`, not `options['num-tasks']`. + ## Input Validation - **Required Parameters**: diff --git a/.cursor/rules/new_features.mdc b/.cursor/rules/new_features.mdc index 2a5bfe89..65287305 100644 --- a/.cursor/rules/new_features.mdc +++ b/.cursor/rules/new_features.mdc @@ -133,7 +133,7 @@ For features requiring components in multiple modules: - **Naming Conventions**: - Use kebab-case for command names (`analyze-complexity`, not `analyzeComplexity`) - - Use camelCase for option names (`--outputFormat`, not `--output-format`) + - Use kebab-case for option names (`--output-format`, not `--outputFormat`) - Use the same option names across commands when they represent the same concept - **Command Structure**: diff --git a/bin/task-master.js b/bin/task-master.js index 03fe1728..2685f587 100755 --- a/bin/task-master.js +++ b/bin/task-master.js @@ -143,6 +143,22 @@ function createDevScriptAction(commandName) { // Add Commander-provided defaults for options not specified by user Object.entries(options).forEach(([key, value]) => { + // Debug output to see what keys we're getting + if (process.env.DEBUG === '1') { + console.error(`DEBUG - Processing option: ${key} = ${value}`); + } + + // Special case for numTasks > num-tasks (a known problem case) + if (key === 'numTasks') { + if (process.env.DEBUG === '1') { + console.error('DEBUG - Converting numTasks to num-tasks'); + } + if (!userOptions.has('num-tasks') && !userOptions.has('numTasks')) { + args.push(`--num-tasks=${value}`); + } + return; + } + // Skip built-in Commander properties and options the user provided if (['parent', 'commands', 'options', 'rawArgs'].includes(key) || userOptions.has(key)) { return; @@ -154,16 +170,17 @@ function createDevScriptAction(commandName) { return; } - // Add default values + // Add default values, using kebab-case for the parameter name if (value !== undefined) { if (typeof value === 'boolean') { if (value === true) { - args.push(`--${key}`); + args.push(`--${kebabKey}`); } else if (value === false && key === 'generate') { args.push('--no-generate'); } } else { - args.push(`--${key}=${value}`); + // Always use kebab-case for option names + args.push(`--${kebabKey}=${value}`); } } }); diff --git a/package.json b/package.json index 44cc005e..0f2e2cb8 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "task-master-ai", - "version": "0.9.26", + "version": "0.9.27", "description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.", "main": "index.js", "type": "module", "bin": { - "task-master": "./bin/task-master.js", - "task-master-init": "./bin/task-master-init.js" + "task-master": "bin/task-master.js", + "task-master-init": "bin/task-master-init.js" }, "scripts": { "test": "node --experimental-vm-modules node_modules/.bin/jest", @@ -72,4 +72,4 @@ "mock-fs": "^5.5.0", "supertest": "^7.1.0" } -} \ No newline at end of file +} diff --git a/scripts/modules/commands.js b/scripts/modules/commands.js index 51dc3bd5..cadd7d51 100644 --- a/scripts/modules/commands.js +++ b/scripts/modules/commands.js @@ -56,20 +56,26 @@ function registerCommands(programInstance) { .command('parse-prd') .description('Parse a PRD file and generate tasks') .argument('[file]', 'Path to the PRD file') + .option('-i, --input ', 'Path to the PRD file (alternative to positional argument)') .option('-o, --output ', 'Output file path', 'tasks/tasks.json') .option('-n, --num-tasks ', 'Number of tasks to generate', '10') .action(async (file, options) => { - if (!file) { + // Use input option if file argument not provided + const inputFile = file || options.input; + + if (!inputFile) { console.log(chalk.yellow('No PRD file specified.')); console.log(boxen( chalk.white.bold('Parse PRD Help') + '\n\n' + chalk.cyan('Usage:') + '\n' + ` task-master parse-prd [options]\n\n` + chalk.cyan('Options:') + '\n' + - ' -o, --output Output file path (default: "tasks/tasks.json")\n' + - ' -n, --num-tasks Number of tasks to generate (default: 10)\n\n' + + ' -i, --input Path to the PRD file (alternative to positional argument)\n' + + ' -o, --output Output file path (default: "tasks/tasks.json")\n' + + ' -n, --num-tasks Number of tasks to generate (default: 10)\n\n' + chalk.cyan('Example:') + '\n' + - ' task-master parse-prd requirements.txt --num-tasks 15\n\n' + + ' task-master parse-prd requirements.txt --num-tasks 15\n' + + ' task-master parse-prd --input=requirements.txt\n\n' + chalk.yellow('Note: This command will generate tasks from a PRD document and will overwrite any existing tasks.json file.'), { padding: 1, borderColor: 'blue', borderStyle: 'round' } )); @@ -79,10 +85,10 @@ function registerCommands(programInstance) { const numTasks = parseInt(options.numTasks, 10); const outputPath = options.output; - console.log(chalk.blue(`Parsing PRD file: ${file}`)); + console.log(chalk.blue(`Parsing PRD file: ${inputFile}`)); console.log(chalk.blue(`Generating ${numTasks} tasks...`)); - await parsePRD(file, outputPath, numTasks); + await parsePRD(inputFile, outputPath, numTasks); }); // update command diff --git a/scripts/sample-prd.txt b/scripts/sample-prd.txt new file mode 100644 index 00000000..7049575c --- /dev/null +++ b/scripts/sample-prd.txt @@ -0,0 +1,3 @@ +Task Master PRD + +Create a CLI tool for task management