fix(update-task): pass projectRoot and adjust parsing

Modified update-task-by-id core, direct function, and tool to pass projectRoot. Reverted parsing logic in core function to prioritize `{...}` extraction, resolving parsing errors. Fixed ReferenceError by correctly destructuring projectRoot.
This commit is contained in:
Eyal Toledano
2025-05-01 17:46:33 -04:00
parent 8536af22ef
commit 48340a76f8
6 changed files with 248 additions and 131 deletions

View File

@@ -6,30 +6,40 @@
import { updateTaskById } from '../../../../scripts/modules/task-manager.js';
import {
enableSilentMode,
disableSilentMode
disableSilentMode,
isSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';
/**
* Direct function wrapper for updateTaskById with error handling.
*
* @param {Object} args - Command arguments containing id, prompt, useResearch and tasksJsonPath.
* @param {Object} args - Command arguments containing id, prompt, useResearch, tasksJsonPath, and projectRoot.
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
* @param {string} args.id - Task ID (or subtask ID like "1.2").
* @param {string} args.prompt - New information/context prompt.
* @param {boolean} [args.research] - Whether to use research role.
* @param {string} [args.projectRoot] - Project root path.
* @param {Object} log - Logger object.
* @param {Object} context - Context object containing session data.
* @returns {Promise<Object>} - Result object with success status and data/error information.
*/
export async function updateTaskByIdDirect(args, log, context = {}) {
const { session } = context; // Only extract session, not reportProgress
// Destructure expected args, including the resolved tasksJsonPath
const { tasksJsonPath, id, prompt, research } = args;
const { session } = context;
// Destructure expected args, including projectRoot
const { tasksJsonPath, id, prompt, research, projectRoot } = args;
const logWrapper = createLogWrapper(log);
try {
log.info(`Updating task with args: ${JSON.stringify(args)}`);
logWrapper.info(
`Updating task by ID via direct function. ID: ${id}, ProjectRoot: ${projectRoot}`
);
// Check if tasksJsonPath was provided
if (!tasksJsonPath) {
const errorMessage = 'tasksJsonPath is required but was not provided.';
log.error(errorMessage);
logWrapper.error(errorMessage);
return {
success: false,
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
@@ -41,7 +51,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
if (!id) {
const errorMessage =
'No task ID specified. Please provide a task ID to update.';
log.error(errorMessage);
logWrapper.error(errorMessage);
return {
success: false,
error: { code: 'MISSING_TASK_ID', message: errorMessage },
@@ -52,7 +62,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
if (!prompt) {
const errorMessage =
'No prompt specified. Please provide a prompt with new information for the task update.';
log.error(errorMessage);
logWrapper.error(errorMessage);
return {
success: false,
error: { code: 'MISSING_PROMPT', message: errorMessage },
@@ -71,7 +81,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
taskId = parseInt(id, 10);
if (isNaN(taskId)) {
const errorMessage = `Invalid task ID: ${id}. Task ID must be a positive integer or subtask ID (e.g., "5.2").`;
log.error(errorMessage);
logWrapper.error(errorMessage);
return {
success: false,
error: { code: 'INVALID_TASK_ID', message: errorMessage },
@@ -89,66 +99,80 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
// Get research flag
const useResearch = research === true;
log.info(
logWrapper.info(
`Updating task with ID ${taskId} with prompt "${prompt}" and research: ${useResearch}`
);
try {
// Enable silent mode to prevent console logs from interfering with JSON response
const wasSilent = isSilentMode();
if (!wasSilent) {
enableSilentMode();
}
// Create the logger wrapper using the utility function
const mcpLog = createLogWrapper(log);
try {
// Execute core updateTaskById function with proper parameters
await updateTaskById(
const updatedTask = await updateTaskById(
tasksPath,
taskId,
prompt,
useResearch,
{
mcpLog, // Pass the wrapped logger
session
mcpLog: logWrapper,
session,
projectRoot
},
'json'
);
// Since updateTaskById doesn't return a value but modifies the tasks file,
// we'll return a success message
// Check if the core function indicated the task wasn't updated (e.g., status was 'done')
if (updatedTask === null) {
// Core function logs the reason, just return success with info
const message = `Task ${taskId} was not updated (likely already completed).`;
logWrapper.info(message);
return {
success: true,
data: { message: message, taskId: taskId, updated: false },
fromCache: false
};
}
// Task was updated successfully
const successMessage = `Successfully updated task with ID ${taskId} based on the prompt`;
logWrapper.success(successMessage);
return {
success: true,
data: {
message: `Successfully updated task with ID ${taskId} based on the prompt`,
taskId,
tasksPath: tasksPath, // Return the used path
useResearch
message: successMessage,
taskId: taskId,
tasksPath: tasksPath,
useResearch: useResearch,
updated: true,
updatedTask: updatedTask
},
fromCache: false // This operation always modifies state and should never be cached
fromCache: false
};
} catch (error) {
log.error(`Error updating task by ID: ${error.message}`);
logWrapper.error(`Error updating task by ID: ${error.message}`);
return {
success: false,
error: {
code: 'UPDATE_TASK_ERROR',
code: 'UPDATE_TASK_CORE_ERROR',
message: error.message || 'Unknown error updating task'
},
fromCache: false
};
} finally {
// Make sure to restore normal logging even if there's an error
disableSilentMode();
if (!wasSilent && isSilentMode()) {
disableSilentMode();
}
}
} catch (error) {
// Ensure silent mode is disabled
disableSilentMode();
log.error(`Error updating task by ID: ${error.message}`);
logWrapper.error(`Setup error in updateTaskByIdDirect: ${error.message}`);
if (isSilentMode()) disableSilentMode();
return {
success: false,
error: {
code: 'UPDATE_TASK_ERROR',
message: error.message || 'Unknown error updating task'
code: 'DIRECT_FUNCTION_SETUP_ERROR',
message: error.message || 'Unknown setup error'
},
fromCache: false
};

View File

@@ -4,6 +4,7 @@
*/
import { z } from 'zod';
import path from 'path'; // Import path
import {
handleApiResult,
createErrorResponse,
@@ -23,7 +24,7 @@ export function registerUpdateTaskTool(server) {
'Updates a single task by ID with new information or context provided in the prompt.',
parameters: z.object({
id: z
.string()
.string() // ID can be number or string like "1.2"
.describe(
"ID of the task (e.g., '15') to update. Subtasks are supported using the update-subtask tool."
),
@@ -40,59 +41,65 @@ export function registerUpdateTaskTool(server) {
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
const toolName = 'update_task';
try {
log.info(`Updating task with args: ${JSON.stringify(args)}`);
log.info(
`Executing ${toolName} tool with args: ${JSON.stringify(args)}`
);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
// Ensure project root was determined
if (!rootFolder) {
// 1. Get Project Root
const rootFolder = args.projectRoot;
if (!rootFolder || !path.isAbsolute(rootFolder)) {
log.error(
`${toolName}: projectRoot is required and must be absolute.`
);
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
'projectRoot is required and must be absolute.'
);
}
log.info(`${toolName}: Project root: ${rootFolder}`);
// Resolve the path to tasks.json
// 2. Resolve Tasks Path
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
{ projectRoot: rootFolder, file: args.file }, // Pass root and optional relative file
log
);
log.info(`${toolName}: Resolved tasks path: ${tasksJsonPath}`);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
`Failed to find tasks.json within project root '${rootFolder}': ${error.message}`
);
}
// 3. Call Direct Function - Include projectRoot
const result = await updateTaskByIdDirect(
{
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
tasksJsonPath: tasksJsonPath, // Pass resolved path
id: args.id,
prompt: args.prompt,
research: args.research
research: args.research,
projectRoot: rootFolder // <<< Pass projectRoot HERE
},
log,
{ session }
{ session } // Pass context with session
);
if (result.success) {
log.info(`Successfully updated task with ID ${args.id}`);
} else {
log.error(
`Failed to update task: ${result.error?.message || 'Unknown error'}`
);
}
// 4. Handle Result
log.info(
`${toolName}: Direct function result: success=${result.success}`
);
// Pass the actual data from the result (contains updated task or message)
return handleApiResult(result, log, 'Error updating task');
} catch (error) {
log.error(`Error in update_task tool: ${error.message}`);
return createErrorResponse(error.message);
log.error(
`Critical error in ${toolName} tool execute: ${error.message}`
);
return createErrorResponse(
`Internal tool error (${toolName}): ${error.message}`
);
}
}
});