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
This commit is contained in:
Eyal Toledano
2025-03-25 00:42:59 -04:00
parent 3be7920868
commit b3d6e61e6b
6 changed files with 42 additions and 14 deletions

View File

@@ -50,6 +50,8 @@ alwaysApply: false
.option('-p, --path <dir>', 'Output directory') // Should be --output .option('-p, --path <dir>', '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 ## Input Validation
- **Required Parameters**: - **Required Parameters**:

View File

@@ -133,7 +133,7 @@ For features requiring components in multiple modules:
- **Naming Conventions**: - **Naming Conventions**:
- Use kebab-case for command names (`analyze-complexity`, not `analyzeComplexity`) - 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 - Use the same option names across commands when they represent the same concept
- **Command Structure**: - **Command Structure**:

View File

@@ -143,6 +143,22 @@ function createDevScriptAction(commandName) {
// Add Commander-provided defaults for options not specified by user // Add Commander-provided defaults for options not specified by user
Object.entries(options).forEach(([key, value]) => { 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 // Skip built-in Commander properties and options the user provided
if (['parent', 'commands', 'options', 'rawArgs'].includes(key) || userOptions.has(key)) { if (['parent', 'commands', 'options', 'rawArgs'].includes(key) || userOptions.has(key)) {
return; return;
@@ -154,16 +170,17 @@ function createDevScriptAction(commandName) {
return; return;
} }
// Add default values // Add default values, using kebab-case for the parameter name
if (value !== undefined) { if (value !== undefined) {
if (typeof value === 'boolean') { if (typeof value === 'boolean') {
if (value === true) { if (value === true) {
args.push(`--${key}`); args.push(`--${kebabKey}`);
} else if (value === false && key === 'generate') { } else if (value === false && key === 'generate') {
args.push('--no-generate'); args.push('--no-generate');
} }
} else { } else {
args.push(`--${key}=${value}`); // Always use kebab-case for option names
args.push(`--${kebabKey}=${value}`);
} }
} }
}); });

View File

@@ -1,12 +1,12 @@
{ {
"name": "task-master-ai", "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.", "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",
"bin": { "bin": {
"task-master": "./bin/task-master.js", "task-master": "bin/task-master.js",
"task-master-init": "./bin/task-master-init.js" "task-master-init": "bin/task-master-init.js"
}, },
"scripts": { "scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest", "test": "node --experimental-vm-modules node_modules/.bin/jest",
@@ -72,4 +72,4 @@
"mock-fs": "^5.5.0", "mock-fs": "^5.5.0",
"supertest": "^7.1.0" "supertest": "^7.1.0"
} }
} }

View File

@@ -56,20 +56,26 @@ function registerCommands(programInstance) {
.command('parse-prd') .command('parse-prd')
.description('Parse a PRD file and generate tasks') .description('Parse a PRD file and generate tasks')
.argument('[file]', 'Path to the PRD file') .argument('[file]', 'Path to the PRD file')
.option('-i, --input <file>', 'Path to the PRD file (alternative to positional argument)')
.option('-o, --output <file>', 'Output file path', 'tasks/tasks.json') .option('-o, --output <file>', 'Output file path', 'tasks/tasks.json')
.option('-n, --num-tasks <number>', 'Number of tasks to generate', '10') .option('-n, --num-tasks <number>', 'Number of tasks to generate', '10')
.action(async (file, options) => { .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(chalk.yellow('No PRD file specified.'));
console.log(boxen( console.log(boxen(
chalk.white.bold('Parse PRD Help') + '\n\n' + chalk.white.bold('Parse PRD Help') + '\n\n' +
chalk.cyan('Usage:') + '\n' + chalk.cyan('Usage:') + '\n' +
` task-master parse-prd <prd-file.txt> [options]\n\n` + ` task-master parse-prd <prd-file.txt> [options]\n\n` +
chalk.cyan('Options:') + '\n' + chalk.cyan('Options:') + '\n' +
' -o, --output <file> Output file path (default: "tasks/tasks.json")\n' + ' -i, --input <file> Path to the PRD file (alternative to positional argument)\n' +
' -n, --num-tasks <number> Number of tasks to generate (default: 10)\n\n' + ' -o, --output <file> Output file path (default: "tasks/tasks.json")\n' +
' -n, --num-tasks <number> Number of tasks to generate (default: 10)\n\n' +
chalk.cyan('Example:') + '\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.'), 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' } { padding: 1, borderColor: 'blue', borderStyle: 'round' }
)); ));
@@ -79,10 +85,10 @@ function registerCommands(programInstance) {
const numTasks = parseInt(options.numTasks, 10); const numTasks = parseInt(options.numTasks, 10);
const outputPath = options.output; 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...`)); console.log(chalk.blue(`Generating ${numTasks} tasks...`));
await parsePRD(file, outputPath, numTasks); await parsePRD(inputFile, outputPath, numTasks);
}); });
// update command // update command

3
scripts/sample-prd.txt Normal file
View File

@@ -0,0 +1,3 @@
Task Master PRD
Create a CLI tool for task management