import path from "path"; import chalk from "chalk"; import boxen from "boxen"; import { log, readJSON, writeJSON, findTaskById } from "../utils.js"; import { displayBanner } from "../ui.js"; import { validateTaskDependencies } from "../dependency-manager.js"; import { getDebugFlag } from "../config-manager.js"; import updateSingleTaskStatus from "./update-single-task-status.js"; import generateTaskFiles from "./generate-task-files.js"; import { isValidTaskStatus, TASK_STATUS_OPTIONS, } from "../../../src/constants/task-status.js"; /** * Set the status of a task * @param {string} tasksPath - Path to the tasks.json file * @param {string} taskIdInput - Task ID(s) to update * @param {string} newStatus - New status * @param {Object} options - Additional options (mcpLog for MCP mode) * @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode */ async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) { try { if (!isValidTaskStatus(newStatus)) { throw new Error( `Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(", ")}` ); } // Determine if we're in MCP mode by checking for mcpLog const isMcpMode = !!options?.mcpLog; // Only display UI elements if not in MCP mode if (!isMcpMode) { console.log( boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), { padding: 1, borderColor: "blue", borderStyle: "round", }) ); } log("info", `Reading tasks from ${tasksPath}...`); const data = readJSON(tasksPath); if (!data || !data.tasks) { throw new Error(`No valid tasks found in ${tasksPath}`); } // Handle multiple task IDs (comma-separated) const taskIds = taskIdInput.split(",").map((id) => id.trim()); const updatedTasks = []; // Update each task for (const id of taskIds) { await updateSingleTaskStatus(tasksPath, id, newStatus, data, !isMcpMode); updatedTasks.push(id); } // Write the updated tasks to the file writeJSON(tasksPath, data); // Validate dependencies after status update log("info", "Validating dependencies after status update..."); validateTaskDependencies(data.tasks); // Generate individual task files log("info", "Regenerating task files..."); await generateTaskFiles(tasksPath, path.dirname(tasksPath), { mcpLog: options.mcpLog, }); // Display success message - only in CLI mode if (!isMcpMode) { for (const id of updatedTasks) { const task = findTaskById(data.tasks, id); const taskName = task ? task.title : id; console.log( boxen( chalk.white.bold(`Successfully updated task ${id} status:`) + "\n" + `From: ${chalk.yellow(task ? task.status : "unknown")}\n` + `To: ${chalk.green(newStatus)}`, { padding: 1, borderColor: "green", borderStyle: "round" } ) ); } } // Return success value for programmatic use return { success: true, updatedTasks: updatedTasks.map((id) => ({ id, status: newStatus, })), }; } catch (error) { log("error", `Error setting task status: ${error.message}`); // Only show error UI in CLI mode if (!options?.mcpLog) { console.error(chalk.red(`Error: ${error.message}`)); // Pass session to getDebugFlag if (getDebugFlag(options?.session)) { // Use getter console.error(error); } process.exit(1); } else { // In MCP mode, throw the error for the caller to handle throw error; } } } export default setTaskStatus;