mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2026-01-30 06:12:05 +00:00
fix: improve update-subtask task validation
This commit is contained in:
@@ -47,10 +47,12 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
// Basic validation for ID format (e.g., '5.2')
|
||||
if (!id || typeof id !== 'string' || !id.includes('.')) {
|
||||
const errorMessage =
|
||||
'Invalid subtask ID format. Must be in format "parentId.subtaskId" (e.g., "5.2").';
|
||||
// Basic validation - ID must be present
|
||||
// In API storage, subtask IDs like "HAM-2611" are valid (no dot required)
|
||||
// In file storage, subtask IDs must be in format "parentId.subtaskId"
|
||||
// The core function handles storage-specific validation
|
||||
if (!id || typeof id !== 'string' || !id.trim()) {
|
||||
const errorMessage = 'Subtask ID cannot be empty.';
|
||||
logWrapper.error(errorMessage);
|
||||
return {
|
||||
success: false,
|
||||
@@ -68,26 +70,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
// Validate subtask ID format
|
||||
const subtaskId = id;
|
||||
if (typeof subtaskId !== 'string' && typeof subtaskId !== 'number') {
|
||||
const errorMessage = `Invalid subtask ID type: ${typeof subtaskId}. Subtask ID must be a string or number.`;
|
||||
log.error(errorMessage);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'INVALID_SUBTASK_ID_TYPE', message: errorMessage }
|
||||
};
|
||||
}
|
||||
|
||||
const subtaskIdStr = String(subtaskId);
|
||||
if (!subtaskIdStr.includes('.')) {
|
||||
const errorMessage = `Invalid subtask ID format: ${subtaskIdStr}. Subtask ID must be in format "parentId.subtaskId" (e.g., "5.2").`;
|
||||
log.error(errorMessage);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'INVALID_SUBTASK_ID_FORMAT', message: errorMessage }
|
||||
};
|
||||
}
|
||||
const subtaskIdStr = String(id).trim();
|
||||
|
||||
// Use the provided path
|
||||
const tasksPath = tasksJsonPath;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Tool to append additional information to a specific subtask
|
||||
*/
|
||||
|
||||
import { TaskIdSchemaForMcp } from '@tm/core';
|
||||
import {
|
||||
createErrorResponse,
|
||||
handleApiResult,
|
||||
@@ -23,11 +24,9 @@ export function registerUpdateSubtaskTool(server) {
|
||||
description:
|
||||
'Appends timestamped information to a specific subtask without replacing existing content. If you just want to update the subtask status, use set_task_status instead.',
|
||||
parameters: z.object({
|
||||
id: z
|
||||
.string()
|
||||
.describe(
|
||||
'ID of the subtask to update in format "parentId.subtaskId" (e.g., "5.2"). Parent ID is the ID of the task that contains the subtask.'
|
||||
),
|
||||
id: TaskIdSchemaForMcp.describe(
|
||||
'ID of the subtask to update in format "parentId.subtaskId" (e.g., "5.2"). Parent ID is the ID of the task that contains the subtask.'
|
||||
),
|
||||
prompt: z.string().describe('Information to add to the subtask'),
|
||||
research: z
|
||||
.boolean()
|
||||
|
||||
@@ -1395,10 +1395,7 @@ function registerCommands(programInstance) {
|
||||
'Path to the tasks file',
|
||||
TASKMASTER_TASKS_FILE
|
||||
)
|
||||
.option(
|
||||
'-i, --id <id>',
|
||||
'Subtask ID to update in format "parentId.subtaskId" (required)'
|
||||
)
|
||||
.option('-i, --id <id>', 'Subtask ID to update (required)')
|
||||
.option(
|
||||
'-p, --prompt <text>',
|
||||
'Prompt explaining what information to add (required)'
|
||||
@@ -1431,21 +1428,10 @@ function registerCommands(programInstance) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Validate subtask ID format (should contain a dot)
|
||||
// Get subtask ID - validation happens in the core function
|
||||
// In API storage, subtask IDs like "HAM-2611" are valid
|
||||
// In file storage, subtask IDs must be in format "parentId.subtaskId"
|
||||
const subtaskId = options.id;
|
||||
if (!subtaskId.includes('.')) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
`Error: Invalid subtask ID format: ${subtaskId}. Subtask ID must be in format "parentId.subtaskId"`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Usage example: task-master update-subtask --id=5.2 --prompt="Add more details about the API endpoint"'
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!options.prompt) {
|
||||
console.error(
|
||||
|
||||
@@ -67,14 +67,9 @@ async function updateSubtaskById(
|
||||
try {
|
||||
report('info', `Updating subtask ${subtaskId} with prompt: "${prompt}"`);
|
||||
|
||||
if (
|
||||
!subtaskId ||
|
||||
typeof subtaskId !== 'string' ||
|
||||
!subtaskId.includes('.')
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid subtask ID format: ${subtaskId}. Subtask ID must be in format "parentId.subtaskId"`
|
||||
);
|
||||
// Basic validation - ID must be present
|
||||
if (!subtaskId || typeof subtaskId !== 'string') {
|
||||
throw new Error('Subtask ID cannot be empty.');
|
||||
}
|
||||
|
||||
if (!prompt || typeof prompt !== 'string' || prompt.trim() === '') {
|
||||
@@ -83,17 +78,13 @@ async function updateSubtaskById(
|
||||
);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(tasksPath)) {
|
||||
throw new Error(`Tasks file not found at path: ${tasksPath}`);
|
||||
}
|
||||
|
||||
const projectRoot = providedProjectRoot || findProjectRoot();
|
||||
if (!projectRoot) {
|
||||
throw new Error('Could not determine project root directory');
|
||||
}
|
||||
|
||||
// --- BRIDGE: Try remote update first (API storage) ---
|
||||
// In API storage, subtask IDs like "1.2" or "TAS-49.1" are just regular task IDs
|
||||
// In API storage, subtask IDs like "HAM-2611" are just regular task IDs
|
||||
// So update-subtask and update-task work identically
|
||||
const remoteResult = await tryUpdateViaRemote({
|
||||
taskId: subtaskId,
|
||||
@@ -118,6 +109,17 @@ async function updateSubtaskById(
|
||||
// Otherwise fall through to file-based logic below
|
||||
// --- End BRIDGE ---
|
||||
|
||||
// For file storage, validate the subtask ID format (must contain a dot)
|
||||
if (!subtaskId.includes('.')) {
|
||||
throw new Error(
|
||||
`Invalid subtask ID format: ${subtaskId}. In solo mode, subtask ID must be in format "parentId.subtaskId" (e.g., "5.2").`
|
||||
);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(tasksPath)) {
|
||||
throw new Error(`Tasks file not found at path: ${tasksPath}`);
|
||||
}
|
||||
|
||||
const data = readJSON(tasksPath, projectRoot, tag);
|
||||
if (!data || !data.tasks) {
|
||||
throw new Error(
|
||||
@@ -318,7 +320,7 @@ async function updateSubtaskById(
|
||||
// Check if the string is not empty
|
||||
const timestamp = new Date().toISOString();
|
||||
const formattedBlock = `<info added on ${timestamp}>\n${generatedContentString.trim()}\n</info added on ${timestamp}>`;
|
||||
newlyAddedSnippet = formattedBlock; // <--- ADD THIS LINE: Store for display
|
||||
newlyAddedSnippet = formattedBlock;
|
||||
|
||||
subtask.details =
|
||||
(subtask.details ? subtask.details + '\n' : '') + formattedBlock;
|
||||
|
||||
Reference in New Issue
Block a user