159 lines
4.2 KiB
JavaScript
Raw Normal View History

import fs from 'fs';
import path from 'path';
import { log, readJSON, writeJSON } from '../utils.js';
import generateTaskFiles from './generate-task-files.js';
import taskExists from './task-exists.js';
/**
* Removes a task or subtask from the tasks file
* @param {string} tasksPath - Path to the tasks file
* @param {string|number} taskId - ID of task or subtask to remove (e.g., '5' or '5.2')
* @returns {Object} Result object with success message and removed task info
*/
async function removeTask(tasksPath, taskId) {
try {
// Read the tasks file
const data = readJSON(tasksPath);
if (!data || !data.tasks) {
throw new Error(`No valid tasks found in ${tasksPath}`);
}
// Check if the task ID exists
if (!taskExists(data.tasks, taskId)) {
throw new Error(`Task with ID ${taskId} not found`);
}
// Handle subtask removal (e.g., '5.2')
if (typeof taskId === 'string' && taskId.includes('.')) {
const [parentTaskId, subtaskId] = taskId
.split('.')
.map((id) => parseInt(id, 10));
// Find the parent task
const parentTask = data.tasks.find((t) => t.id === parentTaskId);
if (!parentTask || !parentTask.subtasks) {
throw new Error(
`Parent task with ID ${parentTaskId} or its subtasks not found`
);
}
// Find the subtask to remove
const subtaskIndex = parentTask.subtasks.findIndex(
(st) => st.id === subtaskId
);
if (subtaskIndex === -1) {
throw new Error(
`Subtask with ID ${subtaskId} not found in parent task ${parentTaskId}`
);
}
// Store the subtask info before removal for the result
const removedSubtask = parentTask.subtasks[subtaskIndex];
// Remove the subtask
parentTask.subtasks.splice(subtaskIndex, 1);
// Remove references to this subtask in other subtasks' dependencies
if (parentTask.subtasks && parentTask.subtasks.length > 0) {
parentTask.subtasks.forEach((subtask) => {
if (
subtask.dependencies &&
subtask.dependencies.includes(subtaskId)
) {
subtask.dependencies = subtask.dependencies.filter(
(depId) => depId !== subtaskId
);
}
});
}
// Save the updated tasks
writeJSON(tasksPath, data);
// Generate updated task files
try {
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
} catch (genError) {
log(
'warn',
`Successfully removed subtask but failed to regenerate task files: ${genError.message}`
);
}
return {
success: true,
message: `Successfully removed subtask ${subtaskId} from task ${parentTaskId}`,
removedTask: removedSubtask,
parentTaskId: parentTaskId
};
}
// Handle main task removal
const taskIdNum = parseInt(taskId, 10);
const taskIndex = data.tasks.findIndex((t) => t.id === taskIdNum);
if (taskIndex === -1) {
throw new Error(`Task with ID ${taskId} not found`);
}
// Store the task info before removal for the result
const removedTask = data.tasks[taskIndex];
// Remove the task
data.tasks.splice(taskIndex, 1);
// Remove references to this task in other tasks' dependencies
data.tasks.forEach((task) => {
if (task.dependencies && task.dependencies.includes(taskIdNum)) {
task.dependencies = task.dependencies.filter(
(depId) => depId !== taskIdNum
);
}
});
// Save the updated tasks
writeJSON(tasksPath, data);
// Delete the task file if it exists
const taskFileName = path.join(
path.dirname(tasksPath),
`task_${taskIdNum.toString().padStart(3, '0')}.txt`
);
if (fs.existsSync(taskFileName)) {
try {
fs.unlinkSync(taskFileName);
} catch (unlinkError) {
log(
'warn',
`Successfully removed task from tasks.json but failed to delete task file: ${unlinkError.message}`
);
}
}
// Generate updated task files
try {
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
} catch (genError) {
log(
'warn',
`Successfully removed task but failed to regenerate task files: ${genError.message}`
);
}
return {
success: true,
message: `Successfully removed task ${taskId}`,
removedTask: removedTask
};
} catch (error) {
log('error', `Error removing task: ${error.message}`);
throw {
code: 'REMOVE_TASK_ERROR',
message: error.message,
details: error.stack
};
}
}
export default removeTask;