fix(commands): Add missing context parameters to dependency and remove-subtask commands
- Add projectRoot and tag context to all dependency commands - Add projectRoot and tag context to remove-subtask command - Add --tag option to remove-subtask command - Fixes critical bug where remove-subtask was deleting other tags due to missing context - All dependency and subtask commands now properly handle tagged task lists
This commit is contained in:
@@ -1972,10 +1972,18 @@ ${result.result}
|
||||
'Path to the tasks file',
|
||||
TASKMASTER_TASKS_FILE
|
||||
)
|
||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||
.action(async (options) => {
|
||||
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
|
||||
const taskId = options.id;
|
||||
const dependencyId = options.dependsOn;
|
||||
const tag = options.tag;
|
||||
|
||||
const projectRoot = findProjectRoot();
|
||||
if (!projectRoot) {
|
||||
console.error(chalk.red('Error: Could not find project root.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!taskId || !dependencyId) {
|
||||
console.error(
|
||||
@@ -1993,7 +2001,10 @@ ${result.result}
|
||||
? dependencyId
|
||||
: parseInt(dependencyId, 10);
|
||||
|
||||
await addDependency(tasksPath, formattedTaskId, formattedDependencyId);
|
||||
await addDependency(tasksPath, formattedTaskId, formattedDependencyId, {
|
||||
projectRoot,
|
||||
tag
|
||||
});
|
||||
});
|
||||
|
||||
// remove-dependency command
|
||||
@@ -2007,10 +2018,18 @@ ${result.result}
|
||||
'Path to the tasks file',
|
||||
TASKMASTER_TASKS_FILE
|
||||
)
|
||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||
.action(async (options) => {
|
||||
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
|
||||
const taskId = options.id;
|
||||
const dependencyId = options.dependsOn;
|
||||
const tag = options.tag;
|
||||
|
||||
const projectRoot = findProjectRoot();
|
||||
if (!projectRoot) {
|
||||
console.error(chalk.red('Error: Could not find project root.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!taskId || !dependencyId) {
|
||||
console.error(
|
||||
@@ -2028,7 +2047,15 @@ ${result.result}
|
||||
? dependencyId
|
||||
: parseInt(dependencyId, 10);
|
||||
|
||||
await removeDependency(tasksPath, formattedTaskId, formattedDependencyId);
|
||||
await removeDependency(
|
||||
tasksPath,
|
||||
formattedTaskId,
|
||||
formattedDependencyId,
|
||||
{
|
||||
projectRoot,
|
||||
tag
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// validate-dependencies command
|
||||
@@ -2042,8 +2069,18 @@ ${result.result}
|
||||
'Path to the tasks file',
|
||||
TASKMASTER_TASKS_FILE
|
||||
)
|
||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||
.action(async (options) => {
|
||||
await validateDependenciesCommand(options.file || TASKMASTER_TASKS_FILE);
|
||||
const tag = options.tag;
|
||||
const projectRoot = findProjectRoot();
|
||||
if (!projectRoot) {
|
||||
console.error(chalk.red('Error: Could not find project root.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await validateDependenciesCommand(options.file || TASKMASTER_TASKS_FILE, {
|
||||
context: { projectRoot, tag }
|
||||
});
|
||||
});
|
||||
|
||||
// fix-dependencies command
|
||||
@@ -2055,8 +2092,18 @@ ${result.result}
|
||||
'Path to the tasks file',
|
||||
TASKMASTER_TASKS_FILE
|
||||
)
|
||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||
.action(async (options) => {
|
||||
await fixDependenciesCommand(options.file || TASKMASTER_TASKS_FILE);
|
||||
const tag = options.tag;
|
||||
const projectRoot = findProjectRoot();
|
||||
if (!projectRoot) {
|
||||
console.error(chalk.red('Error: Could not find project root.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await fixDependenciesCommand(options.file || TASKMASTER_TASKS_FILE, {
|
||||
context: { projectRoot, tag }
|
||||
});
|
||||
});
|
||||
|
||||
// complexity-report command
|
||||
@@ -2275,11 +2322,19 @@ ${result.result}
|
||||
'Convert the subtask to a standalone task instead of deleting it'
|
||||
)
|
||||
.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 subtaskIds = options.id;
|
||||
const convertToTask = options.convert || false;
|
||||
const generateFiles = !options.skipGenerate;
|
||||
const tag = options.tag;
|
||||
|
||||
const projectRoot = findProjectRoot();
|
||||
if (!projectRoot) {
|
||||
console.error(chalk.red('Error: Could not find project root.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!subtaskIds) {
|
||||
console.error(
|
||||
@@ -2318,7 +2373,8 @@ ${result.result}
|
||||
tasksPath,
|
||||
subtaskId,
|
||||
convertToTask,
|
||||
generateFiles
|
||||
generateFiles,
|
||||
{ projectRoot, tag }
|
||||
);
|
||||
|
||||
if (convertToTask && result) {
|
||||
|
||||
@@ -26,11 +26,12 @@ import { generateTaskFiles } from './task-manager.js';
|
||||
* @param {string} tasksPath - Path to the tasks.json file
|
||||
* @param {number|string} taskId - ID of the task to add dependency to
|
||||
* @param {number|string} dependencyId - ID of the task to add as dependency
|
||||
* @param {Object} context - Context object containing projectRoot and tag information
|
||||
*/
|
||||
async function addDependency(tasksPath, taskId, dependencyId) {
|
||||
async function addDependency(tasksPath, taskId, dependencyId, context = {}) {
|
||||
log('info', `Adding dependency ${dependencyId} to task ${taskId}...`);
|
||||
|
||||
const data = readJSON(tasksPath);
|
||||
const data = readJSON(tasksPath, context.projectRoot, context.tag);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', 'No valid tasks found in tasks.json');
|
||||
process.exit(1);
|
||||
@@ -172,7 +173,7 @@ async function addDependency(tasksPath, taskId, dependencyId) {
|
||||
});
|
||||
|
||||
// Save changes
|
||||
writeJSON(tasksPath, data);
|
||||
writeJSON(tasksPath, data, context.projectRoot, context.tag);
|
||||
log(
|
||||
'success',
|
||||
`Added dependency ${formattedDependencyId} to task ${formattedTaskId}`
|
||||
@@ -195,7 +196,7 @@ async function addDependency(tasksPath, taskId, dependencyId) {
|
||||
}
|
||||
|
||||
// Generate updated task files
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
log('info', 'Task files regenerated with updated dependencies.');
|
||||
} else {
|
||||
@@ -212,12 +213,13 @@ async function addDependency(tasksPath, taskId, dependencyId) {
|
||||
* @param {string} tasksPath - Path to the tasks.json file
|
||||
* @param {number|string} taskId - ID of the task to remove dependency from
|
||||
* @param {number|string} dependencyId - ID of the task to remove as dependency
|
||||
* @param {Object} context - Context object containing projectRoot and tag information
|
||||
*/
|
||||
async function removeDependency(tasksPath, taskId, dependencyId) {
|
||||
async function removeDependency(tasksPath, taskId, dependencyId, context = {}) {
|
||||
log('info', `Removing dependency ${dependencyId} from task ${taskId}...`);
|
||||
|
||||
// Read tasks file
|
||||
const data = readJSON(tasksPath);
|
||||
const data = readJSON(tasksPath, context.projectRoot, context.tag);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', 'No valid tasks found.');
|
||||
process.exit(1);
|
||||
@@ -309,7 +311,7 @@ async function removeDependency(tasksPath, taskId, dependencyId) {
|
||||
targetTask.dependencies.splice(dependencyIndex, 1);
|
||||
|
||||
// Save the updated tasks
|
||||
writeJSON(tasksPath, data);
|
||||
writeJSON(tasksPath, data, context.projectRoot, context.tag);
|
||||
|
||||
// Success message
|
||||
log(
|
||||
@@ -334,7 +336,7 @@ async function removeDependency(tasksPath, taskId, dependencyId) {
|
||||
}
|
||||
|
||||
// Regenerate task files
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,12 +563,14 @@ function cleanupSubtaskDependencies(tasksData) {
|
||||
/**
|
||||
* Validate dependencies in task files
|
||||
* @param {string} tasksPath - Path to tasks.json
|
||||
* @param {Object} options - Options object, including context
|
||||
*/
|
||||
async function validateDependenciesCommand(tasksPath, options = {}) {
|
||||
const { context = {} } = options;
|
||||
log('info', 'Checking for invalid dependencies in task files...');
|
||||
|
||||
// Read tasks data
|
||||
const data = readJSON(tasksPath);
|
||||
const data = readJSON(tasksPath, context.projectRoot, context.tag);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', 'No valid tasks found in tasks.json');
|
||||
process.exit(1);
|
||||
@@ -683,14 +687,15 @@ function countAllDependencies(tasks) {
|
||||
/**
|
||||
* Fixes invalid dependencies in tasks.json
|
||||
* @param {string} tasksPath - Path to tasks.json
|
||||
* @param {Object} options - Options object
|
||||
* @param {Object} options - Options object, including context
|
||||
*/
|
||||
async function fixDependenciesCommand(tasksPath, options = {}) {
|
||||
const { context = {} } = options;
|
||||
log('info', 'Checking for and fixing invalid dependencies in tasks.json...');
|
||||
|
||||
try {
|
||||
// Read tasks data
|
||||
const data = readJSON(tasksPath);
|
||||
const data = readJSON(tasksPath, context.projectRoot, context.tag);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', 'No valid tasks found in tasks.json');
|
||||
process.exit(1);
|
||||
@@ -1004,12 +1009,12 @@ async function fixDependenciesCommand(tasksPath, options = {}) {
|
||||
|
||||
if (dataChanged) {
|
||||
// Save the changes
|
||||
writeJSON(tasksPath, data);
|
||||
writeJSON(tasksPath, data, context.projectRoot, context.tag);
|
||||
log('success', 'Fixed dependency issues in tasks.json');
|
||||
|
||||
// Regenerate task files
|
||||
log('info', 'Regenerating task files to reflect dependency changes...');
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
} else {
|
||||
log('info', 'No changes needed to fix dependencies');
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ async function addSubtask(
|
||||
// Generate task files if requested
|
||||
if (generateFiles) {
|
||||
log('info', 'Regenerating task files...');
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), context);
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), context);
|
||||
}
|
||||
|
||||
return newSubtask;
|
||||
|
||||
@@ -575,7 +575,7 @@ async function addTask(
|
||||
report('Generating task files...', 'info');
|
||||
report('DEBUG: Calling generateTaskFiles...', 'debug');
|
||||
// Pass mcpLog if available to generateTaskFiles
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), { mcpLog });
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), { mcpLog });
|
||||
report('DEBUG: generateTaskFiles finished.', 'debug');
|
||||
|
||||
// Show success message - only for text output (CLI)
|
||||
|
||||
@@ -24,7 +24,7 @@ async function moveTask(
|
||||
tasksPath,
|
||||
sourceId,
|
||||
destinationId,
|
||||
generateFiles = true,
|
||||
generateFiles = false,
|
||||
options = {}
|
||||
) {
|
||||
// Check if we have comma-separated IDs (batch move)
|
||||
|
||||
@@ -339,7 +339,7 @@ Guidelines:
|
||||
);
|
||||
|
||||
// Generate markdown task files after writing tasks.json
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), { mcpLog });
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), { mcpLog });
|
||||
|
||||
// Handle CLI output (e.g., success message)
|
||||
if (outputFormat === 'text') {
|
||||
|
||||
@@ -8,19 +8,21 @@ import generateTaskFiles from './generate-task-files.js';
|
||||
* @param {string} subtaskId - ID of the subtask to remove in format "parentId.subtaskId"
|
||||
* @param {boolean} convertToTask - Whether to convert the subtask to a standalone task
|
||||
* @param {boolean} generateFiles - Whether to regenerate task files after removing the subtask
|
||||
* @param {Object} context - Context object containing projectRoot and tag information
|
||||
* @returns {Object|null} The removed subtask if convertToTask is true, otherwise null
|
||||
*/
|
||||
async function removeSubtask(
|
||||
tasksPath,
|
||||
subtaskId,
|
||||
convertToTask = false,
|
||||
generateFiles = true
|
||||
generateFiles = true,
|
||||
context = {}
|
||||
) {
|
||||
try {
|
||||
log('info', `Removing subtask ${subtaskId}...`);
|
||||
|
||||
// Read the existing tasks
|
||||
const data = readJSON(tasksPath);
|
||||
// Read the existing tasks with proper context
|
||||
const data = readJSON(tasksPath, context.projectRoot, context.tag);
|
||||
if (!data || !data.tasks) {
|
||||
throw new Error(`Invalid or missing tasks file at ${tasksPath}`);
|
||||
}
|
||||
@@ -63,7 +65,7 @@ async function removeSubtask(
|
||||
|
||||
// If parent has no more subtasks, remove the subtasks array
|
||||
if (parentTask.subtasks.length === 0) {
|
||||
delete parentTask.subtasks;
|
||||
parentTask.subtasks = undefined;
|
||||
}
|
||||
|
||||
let convertedTask = null;
|
||||
@@ -100,13 +102,13 @@ async function removeSubtask(
|
||||
log('info', `Subtask ${subtaskId} deleted`);
|
||||
}
|
||||
|
||||
// Write the updated tasks back to the file
|
||||
writeJSON(tasksPath, data);
|
||||
// Write the updated tasks back to the file with proper context
|
||||
writeJSON(tasksPath, data, context.projectRoot, context.tag);
|
||||
|
||||
// Generate task files if requested
|
||||
if (generateFiles) {
|
||||
log('info', 'Regenerating task files...');
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), context);
|
||||
}
|
||||
|
||||
return convertedTask;
|
||||
|
||||
@@ -195,10 +195,10 @@ async function removeTask(tasksPath, taskIds, context = {}) {
|
||||
|
||||
// Generate updated task files ONCE, with context
|
||||
try {
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||
projectRoot,
|
||||
tag: currentTag
|
||||
});
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||
// projectRoot,
|
||||
// tag: currentTag
|
||||
// });
|
||||
results.messages.push('Task files regenerated successfully.');
|
||||
} catch (genError) {
|
||||
const genErrMsg = `Failed to regenerate task files: ${genError.message}`;
|
||||
|
||||
@@ -119,10 +119,10 @@ async function setTaskStatus(
|
||||
validateTaskDependencies(data.tasks);
|
||||
|
||||
// Generate individual task files
|
||||
log('info', 'Regenerating task files...');
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||
mcpLog: options.mcpLog
|
||||
});
|
||||
// log('info', 'Regenerating task files...');
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||
// mcpLog: options.mcpLog
|
||||
// });
|
||||
|
||||
// Display success message - only in CLI mode
|
||||
if (!isMcpMode) {
|
||||
|
||||
@@ -337,7 +337,7 @@ Output Requirements:
|
||||
}
|
||||
|
||||
report('success', `Successfully updated subtask ${subtaskId}`);
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
if (outputFormat === 'text') {
|
||||
if (loadingIndicator) {
|
||||
|
||||
@@ -524,7 +524,7 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
// --- Write File and Generate (Unchanged) ---
|
||||
writeJSON(tasksPath, data);
|
||||
report('success', `Successfully updated task ${taskId}`);
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// --- End Write File ---
|
||||
|
||||
// --- Display CLI Telemetry ---
|
||||
|
||||
@@ -477,7 +477,7 @@ The changes described in the prompt should be applied to ALL tasks in the list.`
|
||||
'success',
|
||||
`Successfully updated ${actualUpdateCount} tasks in ${tasksPath}`
|
||||
);
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
if (outputFormat === 'text' && aiServiceResponse.telemetryData) {
|
||||
displayAiUsageSummary(aiServiceResponse.telemetryData, 'cli');
|
||||
|
||||
Reference in New Issue
Block a user