fix: improve update-subtask task validation

This commit is contained in:
Ralph Khreish
2025-12-11 14:23:08 +01:00
parent 3cf38252af
commit af020f5e8d
4 changed files with 31 additions and 61 deletions

View File

@@ -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;

View File

@@ -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()

View File

@@ -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(

View File

@@ -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;