Batch fixes before release (#1011)

* fix: improve projectRoot

* fix: improve task-master lang command

* feat: add documentation to the readme so more people can access it

* fix: expand command subtask dependency validation

* fix: update command more reliable with perplexity and other models

* chore: fix CI

* chore: implement requested changes

* chore: fix CI
This commit is contained in:
Ralph Khreish
2025-07-19 00:57:47 +03:00
parent 858d4a1c54
commit 444aa5ae19
17 changed files with 148 additions and 83 deletions

View File

@@ -42,7 +42,39 @@ const updatedTaskSchema = z
subtasks: z.array(z.any()).nullable() // Keep subtasks flexible for now
})
.strip(); // Allow potential extra fields during parsing if needed, then validate structure
// Preprocessing schema that adds defaults before validation
const preprocessTaskSchema = z.preprocess((task) => {
// Ensure task is an object
if (typeof task !== 'object' || task === null) {
return {};
}
// Return task with defaults for missing fields
return {
...task,
// Add defaults for required fields if missing
id: task.id ?? 0,
title: task.title ?? 'Untitled Task',
description: task.description ?? '',
status: task.status ?? 'pending',
dependencies: Array.isArray(task.dependencies) ? task.dependencies : [],
// Optional fields - preserve undefined/null distinction
priority: task.hasOwnProperty('priority') ? task.priority : null,
details: task.hasOwnProperty('details') ? task.details : null,
testStrategy: task.hasOwnProperty('testStrategy')
? task.testStrategy
: null,
subtasks: Array.isArray(task.subtasks)
? task.subtasks
: task.subtasks === null
? null
: []
};
}, updatedTaskSchema);
const updatedTaskArraySchema = z.array(updatedTaskSchema);
const preprocessedTaskArraySchema = z.array(preprocessTaskSchema);
/**
* Parses an array of task objects from AI's text response.
@@ -195,32 +227,50 @@ function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
);
}
// Preprocess tasks to ensure required fields have proper defaults
const preprocessedTasks = parsedTasks.map((task) => ({
...task,
// Ensure subtasks is always an array (not null or undefined)
subtasks: Array.isArray(task.subtasks) ? task.subtasks : [],
// Ensure status has a default value if missing
status: task.status || 'pending',
// Ensure dependencies is always an array
dependencies: Array.isArray(task.dependencies) ? task.dependencies : []
}));
// Log missing fields for debugging before preprocessing
let hasWarnings = false;
parsedTasks.forEach((task, index) => {
const missingFields = [];
if (!task.hasOwnProperty('id')) missingFields.push('id');
if (!task.hasOwnProperty('status')) missingFields.push('status');
if (!task.hasOwnProperty('dependencies'))
missingFields.push('dependencies');
const validationResult = updatedTaskArraySchema.safeParse(preprocessedTasks);
if (!validationResult.success) {
report('error', 'Parsed task array failed Zod validation.');
validationResult.error.errors.forEach((err) => {
report('error', ` - Path '${err.path.join('.')}': ${err.message}`);
});
throw new Error(
`AI response failed task structure validation: ${validationResult.error.message}`
if (missingFields.length > 0) {
hasWarnings = true;
report(
'warn',
`Task ${index} is missing fields: ${missingFields.join(', ')} - will use defaults`
);
}
});
if (hasWarnings) {
report(
'warn',
'Some tasks were missing required fields. Applying defaults...'
);
}
report('info', 'Successfully validated task structure.');
return validationResult.data.slice(
// Use the preprocessing schema to add defaults and validate
const preprocessResult = preprocessedTaskArraySchema.safeParse(parsedTasks);
if (!preprocessResult.success) {
// This should rarely happen now since preprocessing adds defaults
report('error', 'Failed to validate task array even after preprocessing.');
preprocessResult.error.errors.forEach((err) => {
report('error', ` - Path '${err.path.join('.')}': ${err.message}`);
});
throw new Error(
`AI response failed validation: ${preprocessResult.error.message}`
);
}
report('info', 'Successfully validated and transformed task structure.');
return preprocessResult.data.slice(
0,
expectedCount || validationResult.data.length
expectedCount || preprocessResult.data.length
);
}