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:
@@ -70,29 +70,80 @@ function parseUpdatedTaskFromText(text, expectedTaskId, logFn, isMCP) {
|
||||
|
||||
let cleanedResponse = text.trim();
|
||||
const originalResponseForDebug = cleanedResponse;
|
||||
let parseMethodUsed = 'raw'; // Keep track of which method worked
|
||||
|
||||
// Extract from Markdown code block first
|
||||
const codeBlockMatch = cleanedResponse.match(
|
||||
/```(?:json)?\s*([\s\S]*?)\s*```/
|
||||
);
|
||||
if (codeBlockMatch) {
|
||||
cleanedResponse = codeBlockMatch[1].trim();
|
||||
report('info', 'Extracted JSON content from Markdown code block.');
|
||||
} else {
|
||||
// If no code block, find first '{' and last '}' for the object
|
||||
const firstBrace = cleanedResponse.indexOf('{');
|
||||
const lastBrace = cleanedResponse.lastIndexOf('}');
|
||||
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
||||
cleanedResponse = cleanedResponse.substring(firstBrace, lastBrace + 1);
|
||||
report('info', 'Extracted content between first { and last }.');
|
||||
} else {
|
||||
report(
|
||||
'warn',
|
||||
'Response does not appear to contain a JSON object structure. Parsing raw response.'
|
||||
);
|
||||
// --- NEW Step 1: Try extracting between {} first ---
|
||||
const firstBraceIndex = cleanedResponse.indexOf('{');
|
||||
const lastBraceIndex = cleanedResponse.lastIndexOf('}');
|
||||
let potentialJsonFromBraces = null;
|
||||
|
||||
if (firstBraceIndex !== -1 && lastBraceIndex > firstBraceIndex) {
|
||||
potentialJsonFromBraces = cleanedResponse.substring(
|
||||
firstBraceIndex,
|
||||
lastBraceIndex + 1
|
||||
);
|
||||
if (potentialJsonFromBraces.length <= 2) {
|
||||
potentialJsonFromBraces = null; // Ignore empty braces {}
|
||||
}
|
||||
}
|
||||
|
||||
// If {} extraction yielded something, try parsing it immediately
|
||||
if (potentialJsonFromBraces) {
|
||||
try {
|
||||
const testParse = JSON.parse(potentialJsonFromBraces);
|
||||
// It worked! Use this as the primary cleaned response.
|
||||
cleanedResponse = potentialJsonFromBraces;
|
||||
parseMethodUsed = 'braces';
|
||||
report(
|
||||
'info',
|
||||
'Successfully parsed JSON content extracted between first { and last }.'
|
||||
);
|
||||
} catch (e) {
|
||||
report(
|
||||
'info',
|
||||
'Content between {} looked promising but failed initial parse. Proceeding to other methods.'
|
||||
);
|
||||
// Reset cleanedResponse to original if brace parsing failed
|
||||
cleanedResponse = originalResponseForDebug;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Step 2: If brace parsing didn't work or wasn't applicable, try code block extraction ---
|
||||
if (parseMethodUsed === 'raw') {
|
||||
const codeBlockMatch = cleanedResponse.match(
|
||||
/```(?:json|javascript)?\s*([\s\S]*?)\s*```/i
|
||||
);
|
||||
if (codeBlockMatch) {
|
||||
cleanedResponse = codeBlockMatch[1].trim();
|
||||
parseMethodUsed = 'codeblock';
|
||||
report('info', 'Extracted JSON content from Markdown code block.');
|
||||
} else {
|
||||
// --- Step 3: If code block failed, try stripping prefixes ---
|
||||
const commonPrefixes = [
|
||||
'json\n',
|
||||
'javascript\n'
|
||||
// ... other prefixes ...
|
||||
];
|
||||
let prefixFound = false;
|
||||
for (const prefix of commonPrefixes) {
|
||||
if (cleanedResponse.toLowerCase().startsWith(prefix)) {
|
||||
cleanedResponse = cleanedResponse.substring(prefix.length).trim();
|
||||
parseMethodUsed = 'prefix';
|
||||
report('info', `Stripped prefix: "${prefix.trim()}"`);
|
||||
prefixFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!prefixFound) {
|
||||
report(
|
||||
'warn',
|
||||
'Response does not appear to contain {}, code block, or known prefix. Attempting raw parse.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Step 4: Attempt final parse ---
|
||||
let parsedTask;
|
||||
try {
|
||||
parsedTask = JSON.parse(cleanedResponse);
|
||||
@@ -168,7 +219,7 @@ async function updateTaskById(
|
||||
context = {},
|
||||
outputFormat = 'text'
|
||||
) {
|
||||
const { session, mcpLog } = context;
|
||||
const { session, mcpLog, projectRoot } = context;
|
||||
const logFn = mcpLog || consoleLog;
|
||||
const isMCP = !!mcpLog;
|
||||
|
||||
@@ -343,7 +394,8 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
prompt: userPrompt,
|
||||
systemPrompt: systemPrompt,
|
||||
role,
|
||||
session
|
||||
session,
|
||||
projectRoot
|
||||
});
|
||||
report('success', 'Successfully received text response from AI service');
|
||||
// --- End AI Service Call ---
|
||||
|
||||
@@ -43,13 +43,12 @@ const updatedTaskArraySchema = z.array(updatedTaskSchema);
|
||||
* Parses an array of task objects from AI's text response.
|
||||
* @param {string} text - Response text from AI.
|
||||
* @param {number} expectedCount - Expected number of tasks.
|
||||
* @param {Function | Object} logFn - The logging function (consoleLog) or MCP log object.
|
||||
* @param {Function | Object} logFn - The logging function or MCP log object.
|
||||
* @param {boolean} isMCP - Flag indicating if logFn is MCP logger.
|
||||
* @returns {Array} Parsed and validated tasks array.
|
||||
* @throws {Error} If parsing or validation fails.
|
||||
*/
|
||||
function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
|
||||
// Helper for consistent logging inside parser
|
||||
const report = (level, ...args) => {
|
||||
if (isMCP) {
|
||||
if (typeof logFn[level] === 'function') logFn[level](...args);
|
||||
@@ -70,32 +69,70 @@ function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
|
||||
let cleanedResponse = text.trim();
|
||||
const originalResponseForDebug = cleanedResponse;
|
||||
|
||||
// Extract from Markdown code block first
|
||||
// Step 1: Attempt to extract from Markdown code block first
|
||||
const codeBlockMatch = cleanedResponse.match(
|
||||
/```(?:json)?\s*([\s\S]*?)\s*```/
|
||||
/```(?:json|javascript)?\s*([\s\S]*?)\s*```/i // Made case-insensitive, allow js
|
||||
);
|
||||
if (codeBlockMatch) {
|
||||
cleanedResponse = codeBlockMatch[1].trim();
|
||||
report('info', 'Extracted JSON content from Markdown code block.');
|
||||
report('info', 'Extracted content from Markdown code block.');
|
||||
} else {
|
||||
// If no code block, find first '[' and last ']' for the array
|
||||
// Step 2 (if no code block): Attempt to strip common language identifiers/intro text
|
||||
// List common prefixes AI might add before JSON
|
||||
const commonPrefixes = [
|
||||
'json\n',
|
||||
'javascript\n',
|
||||
'python\n', // Language identifiers
|
||||
'here are the updated tasks:',
|
||||
'here is the updated json:', // Common intro phrases
|
||||
'updated tasks:',
|
||||
'updated json:',
|
||||
'response:',
|
||||
'output:'
|
||||
];
|
||||
let prefixFound = false;
|
||||
for (const prefix of commonPrefixes) {
|
||||
if (cleanedResponse.toLowerCase().startsWith(prefix)) {
|
||||
cleanedResponse = cleanedResponse.substring(prefix.length).trim();
|
||||
report('info', `Stripped prefix: "${prefix.trim()}"`);
|
||||
prefixFound = true;
|
||||
break; // Stop after finding the first matching prefix
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3 (if no code block and no prefix stripped, or after stripping): Find first '[' and last ']'
|
||||
// This helps if there's still leading/trailing text around the array
|
||||
const firstBracket = cleanedResponse.indexOf('[');
|
||||
const lastBracket = cleanedResponse.lastIndexOf(']');
|
||||
if (firstBracket !== -1 && lastBracket > firstBracket) {
|
||||
cleanedResponse = cleanedResponse.substring(
|
||||
const extractedArray = cleanedResponse.substring(
|
||||
firstBracket,
|
||||
lastBracket + 1
|
||||
);
|
||||
report('info', 'Extracted content between first [ and last ].');
|
||||
} else {
|
||||
// Basic check to see if the extraction looks like JSON
|
||||
if (extractedArray.length > 2) {
|
||||
// More than just '[]'
|
||||
cleanedResponse = extractedArray; // Use the extracted array content
|
||||
if (!codeBlockMatch && !prefixFound) {
|
||||
// Only log if we didn't already log extraction/stripping
|
||||
report('info', 'Extracted content between first [ and last ].');
|
||||
}
|
||||
} else if (!codeBlockMatch && !prefixFound) {
|
||||
report(
|
||||
'warn',
|
||||
'Found brackets "[]" but content seems empty or invalid. Proceeding with original cleaned response.'
|
||||
);
|
||||
}
|
||||
} else if (!codeBlockMatch && !prefixFound) {
|
||||
// Only warn if no other extraction method worked
|
||||
report(
|
||||
'warn',
|
||||
'Response does not appear to contain a JSON array structure. Parsing raw response.'
|
||||
'Response does not appear to contain a JSON code block, known prefix, or clear array structure ([...]). Attempting to parse raw response.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to parse the array
|
||||
// Step 4: Attempt to parse the (hopefully) cleaned JSON array
|
||||
let parsedTasks;
|
||||
try {
|
||||
parsedTasks = JSON.parse(cleanedResponse);
|
||||
@@ -114,7 +151,7 @@ function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
|
||||
);
|
||||
}
|
||||
|
||||
// Validate Array structure
|
||||
// Step 5: Validate Array structure
|
||||
if (!Array.isArray(parsedTasks)) {
|
||||
report(
|
||||
'error',
|
||||
@@ -135,7 +172,7 @@ function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
|
||||
);
|
||||
}
|
||||
|
||||
// Validate each task object using Zod
|
||||
// Step 6: Validate each task object using Zod
|
||||
const validationResult = updatedTaskArraySchema.safeParse(parsedTasks);
|
||||
if (!validationResult.success) {
|
||||
report('error', 'Parsed task array failed Zod validation.');
|
||||
|
||||
@@ -510,8 +510,6 @@ function detectCamelCaseFlags(args) {
|
||||
|
||||
// Export all utility functions and configuration
|
||||
export {
|
||||
// CONFIG, <-- Already Removed
|
||||
// getConfig <-- Removing now
|
||||
LOG_LEVELS,
|
||||
log,
|
||||
readJSON,
|
||||
@@ -532,5 +530,4 @@ export {
|
||||
resolveEnvVariable,
|
||||
getTaskManager,
|
||||
findProjectRoot
|
||||
// getConfig <-- Removed
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user