v017 polish (#778)
* fix(research, tasks): Make research command and task updates tag-aware * refactor(tasks): Prevent automatic task file generation across other locations This commit refactors several core task management functions to prevent them from automatically regenerating individual task files after modifying the main `tasks.json`. Previously, operations like `add-task`, `clear-subtasks`, `expand-task`, and `update-task-by-id` would immediately trigger `generateTaskFiles`. This could be slow and was often unnecessary. The calls to `generateTaskFiles` have been removed or commented out from the core task functions. Users should now run `task-master generate` explicitly to update their individual task files. Additionally, this commit includes fixes to the `move` command to make it fully tag-aware. * fix: move and clear subtasks mcp commands * chore: fix format * chore: fix unit tests --------- Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
This commit is contained in:
committed by
Ralph Khreish
parent
44eba3f7d1
commit
5119cd2d8e
@@ -1614,7 +1614,7 @@ function registerCommands(programInstance) {
|
||||
.command('research')
|
||||
.description('Perform AI-powered research queries with project context')
|
||||
.argument('[prompt]', 'Research prompt to investigate')
|
||||
.option('--file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||
.option('--file <file>', 'Path to the tasks file')
|
||||
.option(
|
||||
'-i, --id <ids>',
|
||||
'Comma-separated task/subtask IDs to include as context (e.g., "15,16.2")'
|
||||
@@ -1752,7 +1752,8 @@ function registerCommands(programInstance) {
|
||||
const projectRoot = findProjectRoot() || '.';
|
||||
const tag = options.tag || getCurrentTag(projectRoot) || 'master';
|
||||
const tasksPath =
|
||||
options.file || path.join(projectRoot, 'tasks', 'tasks.json');
|
||||
options.file ||
|
||||
path.join(projectRoot, '.taskmaster', 'tasks', 'tasks.json');
|
||||
|
||||
// Show current tag context
|
||||
displayCurrentTagIndicator(tag);
|
||||
@@ -1856,14 +1857,15 @@ function registerCommands(programInstance) {
|
||||
researchOptions,
|
||||
{
|
||||
commandName: 'research',
|
||||
outputType: 'cli'
|
||||
outputType: 'cli',
|
||||
tag: tag
|
||||
},
|
||||
'text',
|
||||
validatedParams.allowFollowUp // Pass follow-up flag
|
||||
);
|
||||
|
||||
// Auto-save to task/subtask if requested
|
||||
if (validatedParams.saveToId) {
|
||||
// Auto-save to task/subtask if requested and no interactive save occurred
|
||||
if (validatedParams.saveToId && !result.interactiveSaveOccurred) {
|
||||
try {
|
||||
const isSubtask = validatedParams.saveToId.includes('.');
|
||||
|
||||
@@ -1892,7 +1894,8 @@ ${result.result}`;
|
||||
{
|
||||
commandName: 'research-save',
|
||||
outputType: 'cli',
|
||||
projectRoot: validatedParams.projectRoot
|
||||
projectRoot: validatedParams.projectRoot,
|
||||
tag: tag
|
||||
},
|
||||
'text'
|
||||
);
|
||||
@@ -1917,7 +1920,8 @@ ${result.result}`;
|
||||
{
|
||||
commandName: 'research-save',
|
||||
outputType: 'cli',
|
||||
projectRoot: validatedParams.projectRoot
|
||||
projectRoot: validatedParams.projectRoot,
|
||||
tag: tag
|
||||
},
|
||||
'text',
|
||||
true // appendMode = true
|
||||
@@ -3540,7 +3544,7 @@ Examples:
|
||||
|
||||
try {
|
||||
// Read tasks data once to validate destination IDs
|
||||
const tasksData = readJSON(tasksPath);
|
||||
const tasksData = readJSON(tasksPath, projectRoot, tag);
|
||||
if (!tasksData || !tasksData.tasks) {
|
||||
console.error(
|
||||
chalk.red(`Error: Invalid or missing tasks file at ${tasksPath}`)
|
||||
|
||||
@@ -558,14 +558,14 @@ async function addTask(
|
||||
report('DEBUG: tasks.json written.', 'debug');
|
||||
|
||||
// Generate markdown task files
|
||||
report('Generating task files...', 'info');
|
||||
report('DEBUG: Calling generateTaskFiles...', 'debug');
|
||||
// Pass mcpLog if available to generateTaskFiles
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||
projectRoot,
|
||||
tag: targetTag
|
||||
});
|
||||
report('DEBUG: generateTaskFiles finished.', 'debug');
|
||||
// report('Generating task files...', 'info');
|
||||
// report('DEBUG: Calling generateTaskFiles...', 'debug');
|
||||
// // Pass mcpLog if available to generateTaskFiles
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||
// projectRoot,
|
||||
// tag: targetTag
|
||||
// });
|
||||
// report('DEBUG: generateTaskFiles finished.', 'debug');
|
||||
|
||||
// Show success message - only for text output (CLI)
|
||||
if (outputFormat === 'text') {
|
||||
|
||||
@@ -5,7 +5,6 @@ import Table from 'cli-table3';
|
||||
|
||||
import { log, readJSON, writeJSON, truncate, isSilentMode } from '../utils.js';
|
||||
import { displayBanner } from '../ui.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
|
||||
/**
|
||||
* Clear subtasks from specified tasks
|
||||
@@ -99,10 +98,6 @@ function clearSubtasks(tasksPath, taskIds, context = {}) {
|
||||
console.log(summaryTable.toString());
|
||||
}
|
||||
|
||||
// Regenerate task files to reflect changes
|
||||
log('info', 'Regenerating task files...');
|
||||
generateTaskFiles(tasksPath, path.dirname(tasksPath), { projectRoot, tag });
|
||||
|
||||
// Success message
|
||||
if (!isSilentMode()) {
|
||||
console.log(
|
||||
|
||||
@@ -669,7 +669,7 @@ async function expandTask(
|
||||
|
||||
data.tasks[taskIndex] = task; // Assign the modified task back
|
||||
writeJSON(tasksPath, data);
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
// Display AI Usage Summary for CLI
|
||||
if (
|
||||
|
||||
@@ -120,7 +120,7 @@ async function moveTask(
|
||||
|
||||
// Always write the data object, never the _rawTaggedData directly
|
||||
// The writeJSON function will filter out _rawTaggedData automatically
|
||||
writeJSON(tasksPath, rawData);
|
||||
writeJSON(tasksPath, rawData, options.projectRoot, currentTag);
|
||||
|
||||
if (generateFiles) {
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
@@ -107,8 +107,13 @@ async function performResearch(
|
||||
let autoDiscoveredIds = [];
|
||||
|
||||
try {
|
||||
const tasksPath = path.join(projectRoot, 'tasks', 'tasks.json');
|
||||
const tasksData = await readJSON(tasksPath);
|
||||
const tasksPath = path.join(
|
||||
projectRoot,
|
||||
'.taskmaster',
|
||||
'tasks',
|
||||
'tasks.json'
|
||||
);
|
||||
const tasksData = await readJSON(tasksPath, projectRoot);
|
||||
|
||||
if (tasksData && tasksData.tasks && tasksData.tasks.length > 0) {
|
||||
// Flatten tasks to include subtasks for fuzzy search
|
||||
@@ -250,6 +255,9 @@ async function performResearch(
|
||||
const tagInfo = aiResult.tagInfo;
|
||||
|
||||
// Format and display results
|
||||
// Initialize interactive save tracking
|
||||
let interactiveSaveInfo = { interactiveSaveOccurred: false };
|
||||
|
||||
if (outputFormat === 'text') {
|
||||
displayResearchResults(
|
||||
researchResult,
|
||||
@@ -265,7 +273,7 @@ async function performResearch(
|
||||
|
||||
// Offer follow-up question option (only for initial CLI queries, not MCP)
|
||||
if (allowFollowUp && !isMCP) {
|
||||
await handleFollowUpQuestions(
|
||||
interactiveSaveInfo = await handleFollowUpQuestions(
|
||||
options,
|
||||
context,
|
||||
outputFormat,
|
||||
@@ -308,7 +316,8 @@ async function performResearch(
|
||||
detailLevel,
|
||||
telemetryData,
|
||||
tagInfo,
|
||||
savedFilePath
|
||||
savedFilePath,
|
||||
interactiveSaveOccurred: false // MCP save-to-file doesn't count as interactive save
|
||||
};
|
||||
}
|
||||
|
||||
@@ -325,7 +334,9 @@ async function performResearch(
|
||||
totalInputTokens,
|
||||
detailLevel,
|
||||
telemetryData,
|
||||
tagInfo
|
||||
tagInfo,
|
||||
interactiveSaveOccurred:
|
||||
interactiveSaveInfo?.interactiveSaveOccurred || false
|
||||
};
|
||||
} catch (error) {
|
||||
logFn.error(`Research query failed: ${error.message}`);
|
||||
@@ -643,6 +654,8 @@ async function handleFollowUpQuestions(
|
||||
initialQuery,
|
||||
initialResult
|
||||
) {
|
||||
let interactiveSaveOccurred = false;
|
||||
|
||||
try {
|
||||
// Import required modules for saving
|
||||
const { readJSON } = await import('../utils.js');
|
||||
@@ -693,12 +706,15 @@ async function handleFollowUpQuestions(
|
||||
|
||||
if (action === 'save') {
|
||||
// Handle save functionality
|
||||
await handleSaveToTask(
|
||||
const saveResult = await handleSaveToTask(
|
||||
conversationHistory,
|
||||
projectRoot,
|
||||
context,
|
||||
logFn
|
||||
);
|
||||
if (saveResult) {
|
||||
interactiveSaveOccurred = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -762,6 +778,8 @@ async function handleFollowUpQuestions(
|
||||
// silently continue without follow-up functionality
|
||||
logFn.debug(`Follow-up questions not available: ${error.message}`);
|
||||
}
|
||||
|
||||
return { interactiveSaveOccurred };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -828,8 +846,10 @@ async function handleSaveToTask(
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate ID exists
|
||||
const data = readJSON(tasksPath, projectRoot);
|
||||
// Validate ID exists - use tag from context
|
||||
const { getCurrentTag } = await import('../utils.js');
|
||||
const tag = context.tag || getCurrentTag(projectRoot) || 'master';
|
||||
const data = readJSON(tasksPath, projectRoot, tag);
|
||||
if (!data || !data.tasks) {
|
||||
console.log(chalk.red('❌ No valid tasks found.'));
|
||||
return;
|
||||
@@ -863,7 +883,7 @@ async function handleSaveToTask(
|
||||
trimmedTaskId,
|
||||
conversationThread,
|
||||
false, // useResearch = false for simple append
|
||||
context,
|
||||
{ ...context, tag },
|
||||
'text'
|
||||
);
|
||||
|
||||
@@ -890,7 +910,7 @@ async function handleSaveToTask(
|
||||
taskIdNum,
|
||||
conversationThread,
|
||||
false, // useResearch = false for simple append
|
||||
context,
|
||||
{ ...context, tag },
|
||||
'text',
|
||||
true // appendMode = true
|
||||
);
|
||||
@@ -899,9 +919,12 @@ async function handleSaveToTask(
|
||||
chalk.green(`✅ Research conversation saved to task ${trimmedTaskId}`)
|
||||
);
|
||||
}
|
||||
|
||||
return true; // Indicate successful save
|
||||
} catch (error) {
|
||||
console.log(chalk.red(`❌ Error saving conversation: ${error.message}`));
|
||||
logFn.error(`Error saving conversation: ${error.message}`);
|
||||
return false; // Indicate failed save
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ import {
|
||||
truncate,
|
||||
isSilentMode,
|
||||
findProjectRoot,
|
||||
flattenTasksWithSubtasks
|
||||
flattenTasksWithSubtasks,
|
||||
getCurrentTag
|
||||
} from '../utils.js';
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag } from '../config-manager.js';
|
||||
@@ -46,7 +47,7 @@ async function updateSubtaskById(
|
||||
context = {},
|
||||
outputFormat = context.mcpLog ? 'json' : 'text'
|
||||
) {
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot } = context;
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot, tag } = context;
|
||||
const logFn = mcpLog || consoleLog;
|
||||
const isMCP = !!mcpLog;
|
||||
|
||||
@@ -90,7 +91,10 @@ async function updateSubtaskById(
|
||||
throw new Error('Could not determine project root directory');
|
||||
}
|
||||
|
||||
const data = readJSON(tasksPath, projectRoot);
|
||||
// Determine the tag to use
|
||||
const currentTag = tag || getCurrentTag(projectRoot) || 'master';
|
||||
|
||||
const data = readJSON(tasksPath, projectRoot, currentTag);
|
||||
if (!data || !data.tasks) {
|
||||
throw new Error(
|
||||
`No valid tasks found in ${tasksPath}. The file may be corrupted or have an invalid format.`
|
||||
@@ -331,7 +335,7 @@ Output Requirements:
|
||||
if (outputFormat === 'text' && getDebugFlag(session)) {
|
||||
console.log('>>> DEBUG: About to call writeJSON with updated data...');
|
||||
}
|
||||
writeJSON(tasksPath, data, projectRoot);
|
||||
writeJSON(tasksPath, data, projectRoot, currentTag);
|
||||
if (outputFormat === 'text' && getDebugFlag(session)) {
|
||||
console.log('>>> DEBUG: writeJSON call completed.');
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
truncate,
|
||||
isSilentMode,
|
||||
flattenTasksWithSubtasks,
|
||||
findProjectRoot
|
||||
findProjectRoot,
|
||||
getCurrentTag
|
||||
} from '../utils.js';
|
||||
|
||||
import {
|
||||
@@ -23,11 +24,7 @@ import {
|
||||
} from '../ui.js';
|
||||
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import {
|
||||
getDebugFlag,
|
||||
isApiKeySet // Keep this check
|
||||
} from '../config-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { getDebugFlag, isApiKeySet } from '../config-manager.js';
|
||||
import { ContextGatherer } from '../utils/contextGatherer.js';
|
||||
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
|
||||
|
||||
@@ -223,7 +220,7 @@ async function updateTaskById(
|
||||
outputFormat = 'text',
|
||||
appendMode = false
|
||||
) {
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot } = context;
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot, tag } = context;
|
||||
const logFn = mcpLog || consoleLog;
|
||||
const isMCP = !!mcpLog;
|
||||
|
||||
@@ -268,8 +265,11 @@ async function updateTaskById(
|
||||
throw new Error('Could not determine project root directory');
|
||||
}
|
||||
|
||||
// Determine the tag to use
|
||||
const currentTag = tag || getCurrentTag(projectRoot) || 'master';
|
||||
|
||||
// --- Task Loading and Status Check (Keep existing) ---
|
||||
const data = readJSON(tasksPath, projectRoot);
|
||||
const data = readJSON(tasksPath, projectRoot, currentTag);
|
||||
if (!data || !data.tasks)
|
||||
throw new Error(`No valid tasks found in ${tasksPath}.`);
|
||||
const taskIndex = data.tasks.findIndex((task) => task.id === taskId);
|
||||
@@ -510,7 +510,7 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
|
||||
// Write the updated task back to file
|
||||
data.tasks[taskIndex] = taskToUpdate;
|
||||
writeJSON(tasksPath, data);
|
||||
writeJSON(tasksPath, data, projectRoot, currentTag);
|
||||
report('success', `Successfully appended to task ${taskId}`);
|
||||
|
||||
// Display success message for CLI
|
||||
@@ -624,7 +624,7 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
// --- End Update Task Data ---
|
||||
|
||||
// --- Write File and Generate (Unchanged) ---
|
||||
writeJSON(tasksPath, data);
|
||||
writeJSON(tasksPath, data, projectRoot, currentTag);
|
||||
report('success', `Successfully updated task ${taskId}`);
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// --- End Write File ---
|
||||
|
||||
Reference in New Issue
Block a user