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:
@@ -3727,10 +3727,7 @@ Examples:
|
||||
const taskMaster = initTaskMaster({});
|
||||
const projectRoot = taskMaster.getProjectRoot(); // Find project root for context
|
||||
const { response, setup } = options;
|
||||
console.log(
|
||||
chalk.blue('Response language set to:', JSON.stringify(options))
|
||||
);
|
||||
let responseLanguage = response || 'English';
|
||||
let responseLanguage = response !== undefined ? response : 'English';
|
||||
if (setup) {
|
||||
console.log(
|
||||
chalk.blue('Starting interactive response language setup...')
|
||||
@@ -3772,6 +3769,7 @@ Examples:
|
||||
`❌ Error setting response language: ${result.error.message}`
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -40,8 +40,10 @@ const subtaskSchema = z
|
||||
.min(10)
|
||||
.describe('Detailed description of the subtask'),
|
||||
dependencies: z
|
||||
.array(z.number().int())
|
||||
.describe('IDs of prerequisite subtasks within this expansion'),
|
||||
.array(z.string())
|
||||
.describe(
|
||||
'Array of subtask dependencies within the same parent task. Use format ["parentTaskId.1", "parentTaskId.2"]. Subtasks can only depend on siblings, not external tasks.'
|
||||
),
|
||||
details: z.string().min(20).describe('Implementation details and guidance'),
|
||||
status: z
|
||||
.string()
|
||||
@@ -235,12 +237,10 @@ function parseSubtasksFromText(
|
||||
...rawSubtask,
|
||||
id: currentId,
|
||||
dependencies: Array.isArray(rawSubtask.dependencies)
|
||||
? rawSubtask.dependencies
|
||||
.map((dep) => (typeof dep === 'string' ? parseInt(dep, 10) : dep))
|
||||
.filter(
|
||||
(depId) =>
|
||||
!Number.isNaN(depId) && depId >= startId && depId < currentId
|
||||
)
|
||||
? rawSubtask.dependencies.filter(
|
||||
(dep) =>
|
||||
typeof dep === 'string' && dep.startsWith(`${parentTaskId}.`)
|
||||
)
|
||||
: [],
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
@@ -25,6 +25,10 @@ import { findConfigPath } from '../../../src/utils/path-utils.js';
|
||||
import { log } from '../utils.js';
|
||||
import { CUSTOM_PROVIDERS } from '../../../src/constants/providers.js';
|
||||
|
||||
// Constants
|
||||
const CONFIG_MISSING_ERROR =
|
||||
'The configuration file is missing. Run "task-master init" to create it.';
|
||||
|
||||
/**
|
||||
* Fetches the list of models from OpenRouter API.
|
||||
* @returns {Promise<Array|null>} A promise that resolves with the list of model IDs or null if fetch fails.
|
||||
@@ -168,9 +172,7 @@ async function getModelConfiguration(options = {}) {
|
||||
);
|
||||
|
||||
if (!configExists) {
|
||||
throw new Error(
|
||||
'The configuration file is missing. Run "task-master models --setup" to create it.'
|
||||
);
|
||||
throw new Error(CONFIG_MISSING_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -298,9 +300,7 @@ async function getAvailableModelsList(options = {}) {
|
||||
);
|
||||
|
||||
if (!configExists) {
|
||||
throw new Error(
|
||||
'The configuration file is missing. Run "task-master models --setup" to create it.'
|
||||
);
|
||||
throw new Error(CONFIG_MISSING_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -391,9 +391,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
);
|
||||
|
||||
if (!configExists) {
|
||||
throw new Error(
|
||||
'The configuration file is missing. Run "task-master models --setup" to create it.'
|
||||
);
|
||||
throw new Error(CONFIG_MISSING_ERROR);
|
||||
}
|
||||
|
||||
// Validate role
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
import { generateObjectService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { displayAiUsageSummary } from '../ui.js';
|
||||
|
||||
// Define the Zod schema for a SINGLE task object
|
||||
|
||||
@@ -34,7 +34,7 @@ function setResponseLanguage(lang, options = {}) {
|
||||
error: {
|
||||
code: 'CONFIG_MISSING',
|
||||
message:
|
||||
'The configuration file is missing. Run "task-master models --setup" to create it.'
|
||||
'The configuration file is missing. Run "task-master init" to create it.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user