diff --git a/scripts/modules/commands.js b/scripts/modules/commands.js index 00c5e1ca..daa1fcb0 100644 --- a/scripts/modules/commands.js +++ b/scripts/modules/commands.js @@ -805,7 +805,7 @@ function registerCommands(programInstance) { '-i, --input ', 'Path to the PRD file (alternative to positional argument)' ) - .option('-o, --output ', 'Output file path', TASKMASTER_TASKS_FILE) + .option('-o, --output ', 'Output file path') .option( '-n, --num-tasks ', 'Number of tasks to generate', @@ -825,14 +825,18 @@ function registerCommands(programInstance) { // Initialize TaskMaster let taskMaster; try { - taskMaster = initTaskMaster({ - prdPath: file || options.input || true, - tasksPath: options.output || true - }); + const initOptions = { + prdPath: file || options.input || true + }; + // Only include tasksPath if output is explicitly specified + if (options.output) { + initOptions.tasksPath = options.output; + } + taskMaster = initTaskMaster(initOptions); } catch (error) { 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 -i, --input Path to the PRD file (alternative to positional argument)\n -o, --output Output file path (default: "${TASKMASTER_TASKS_FILE}")\n -n, --num-tasks 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 ${TASKMASTER_DOCS_DIR}/PRD.md 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 [options]\n\n${chalk.cyan('Options:')}\n -i, --input Path to the PRD file (alternative to positional argument)\n -o, --output Output file path (default: .taskmaster/tasks/tasks.json)\n -n, --num-tasks 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 ${TASKMASTER_DOCS_DIR}/PRD.md 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' } ) ); @@ -912,18 +916,17 @@ function registerCommands(programInstance) { } spinner = ora('Parsing PRD and generating tasks...\n').start(); - await parsePRD( - taskMaster.getPrdPath(), - taskMaster.getTasksPath(), - numTasks, - { - append: useAppend, - force: useForce, - research: research, - projectRoot: taskMaster.getProjectRoot(), - tag: tag - } - ); + // Handle case where getTasksPath() returns null + const outputPath = + taskMaster.getTasksPath() || + path.join(taskMaster.getProjectRoot(), TASKMASTER_TASKS_FILE); + await parsePRD(taskMaster.getPrdPath(), outputPath, numTasks, { + append: useAppend, + force: useForce, + research: research, + projectRoot: taskMaster.getProjectRoot(), + tag: tag + }); spinner.succeed('Tasks generated successfully!'); } catch (error) { if (spinner) { @@ -1631,11 +1634,7 @@ function registerCommands(programInstance) { .description( `Analyze tasks and generate expansion recommendations${chalk.reset('')}` ) - .option( - '-o, --output ', - 'Output file path for the report', - COMPLEXITY_REPORT_FILE - ) + .option('-o, --output ', 'Output file path for the report') .option( '-m, --model ', 'LLM model to use for analysis (defaults to configured model)' @@ -1663,10 +1662,14 @@ function registerCommands(programInstance) { .option('--tag ', 'Specify tag context for task operations') .action(async (options) => { // Initialize TaskMaster - const taskMaster = initTaskMaster({ - tasksPath: options.file || true, - complexityReportPath: options.output || true - }); + const initOptions = { + tasksPath: options.file || true // Tasks file is required to analyze + }; + // Only include complexityReportPath if output is explicitly specified + if (options.output) { + initOptions.complexityReportPath = options.output; + } + const taskMaster = initTaskMaster(initOptions); const tag = options.tag; const modelOverride = options.model; @@ -1681,7 +1684,9 @@ function registerCommands(programInstance) { displayCurrentTagIndicator(targetTag); // Tag-aware output file naming: master -> task-complexity-report.json, other tags -> task-complexity-report_tagname.json - const baseOutputPath = taskMaster.getComplexityReportPath(); + const baseOutputPath = + taskMaster.getComplexityReportPath() || + path.join(taskMaster.getProjectRoot(), COMPLEXITY_REPORT_FILE); const outputPath = options.output === COMPLEXITY_REPORT_FILE && targetTag !== 'master' ? baseOutputPath.replace('.json', `_${targetTag}.json`) diff --git a/scripts/modules/task-manager/analyze-task-complexity.js b/scripts/modules/task-manager/analyze-task-complexity.js index bdccb3b0..df5c65c4 100644 --- a/scripts/modules/task-manager/analyze-task-complexity.js +++ b/scripts/modules/task-manager/analyze-task-complexity.js @@ -240,7 +240,7 @@ async function analyzeTaskComplexity(options, context = {}) { tasks: relevantTaskIds, format: 'research' }); - gatheredContext = contextResult; + gatheredContext = contextResult.context || ''; } } catch (contextError) { reportLog( @@ -406,11 +406,10 @@ async function analyzeTaskComplexity(options, context = {}) { useResearch: useResearch }; - const variantKey = useResearch ? 'research' : 'default'; const { systemPrompt, userPrompt: prompt } = await promptManager.loadPrompt( 'analyze-complexity', promptParams, - variantKey + 'default' ); let loadingIndicator = null; diff --git a/scripts/modules/task-manager/expand-task.js b/scripts/modules/task-manager/expand-task.js index cc40f63d..641297eb 100644 --- a/scripts/modules/task-manager/expand-task.js +++ b/scripts/modules/task-manager/expand-task.js @@ -369,7 +369,7 @@ async function expandTask( tasks: finalTaskIds, format: 'research' }); - gatheredContext = contextResult; + gatheredContext = contextResult.context || ''; } } catch (contextError) { logger.warn(`Could not gather context: ${contextError.message}`); diff --git a/scripts/modules/task-manager/update-subtask-by-id.js b/scripts/modules/task-manager/update-subtask-by-id.js index 1648e33d..27dfbed7 100644 --- a/scripts/modules/task-manager/update-subtask-by-id.js +++ b/scripts/modules/task-manager/update-subtask-by-id.js @@ -161,7 +161,7 @@ async function updateSubtaskById( tasks: finalTaskIds, format: 'research' }); - gatheredContext = contextResult; + gatheredContext = contextResult.context || ''; } } catch (contextError) { report('warn', `Could not gather context: ${contextError.message}`); diff --git a/scripts/modules/task-manager/update-task-by-id.js b/scripts/modules/task-manager/update-task-by-id.js index 50ca08c8..b77044fc 100644 --- a/scripts/modules/task-manager/update-task-by-id.js +++ b/scripts/modules/task-manager/update-task-by-id.js @@ -346,7 +346,7 @@ async function updateTaskById( tasks: finalTaskIds, format: 'research' }); - gatheredContext = contextResult; + gatheredContext = contextResult.context || ''; } } catch (contextError) { report('warn', `Could not gather context: ${contextError.message}`); diff --git a/scripts/modules/task-manager/update-tasks.js b/scripts/modules/task-manager/update-tasks.js index 31327e72..fa048037 100644 --- a/scripts/modules/task-manager/update-tasks.js +++ b/scripts/modules/task-manager/update-tasks.js @@ -300,7 +300,7 @@ async function updateTasks( tasks: finalTaskIds, format: 'research' }); - gatheredContext = contextResult; // contextResult is a string + gatheredContext = contextResult.context || ''; } } catch (contextError) { logFn( diff --git a/tests/unit/scripts/modules/task-manager/expand-task.test.js b/tests/unit/scripts/modules/task-manager/expand-task.test.js index fc25b586..e6521648 100644 --- a/tests/unit/scripts/modules/task-manager/expand-task.test.js +++ b/tests/unit/scripts/modules/task-manager/expand-task.test.js @@ -131,7 +131,19 @@ jest.unstable_mockModule( '../../../../../scripts/modules/utils/contextGatherer.js', () => ({ ContextGatherer: jest.fn().mockImplementation(() => ({ - gather: jest.fn().mockResolvedValue('Mock project context from files') + gather: jest.fn().mockResolvedValue({ + context: 'Mock project context from files' + }) + })) + }) +); + +jest.unstable_mockModule( + '../../../../../scripts/modules/utils/fuzzyTaskSearch.js', + () => ({ + FuzzyTaskSearch: jest.fn().mockImplementation(() => ({ + findRelevantTasks: jest.fn().mockReturnValue([]), + getTaskIds: jest.fn().mockReturnValue([]) })) }) );