From af53525cbc660a595b67d4bb90d906911c71f45d Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:58:15 +0200 Subject: [PATCH] fix: handle subtasks in getTask method (#1254) Co-authored-by: Ralph Khreish Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- .changeset/fix-subtask-getTask.md | 7 +++ packages/tm-core/src/services/task-service.ts | 25 +++++++-- .../src/storage/file-storage/file-storage.ts | 56 +++++++++++++++++++ 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 .changeset/fix-subtask-getTask.md diff --git a/.changeset/fix-subtask-getTask.md b/.changeset/fix-subtask-getTask.md new file mode 100644 index 00000000..ed79e1a6 --- /dev/null +++ b/.changeset/fix-subtask-getTask.md @@ -0,0 +1,7 @@ +--- +"task-master-ai": patch +--- + +Fixed issue where `tm show` command could not find subtasks using dotted notation IDs (e.g., '8.1'). + +- The command now properly searches within parent task subtasks and returns the correct subtask information. \ No newline at end of file diff --git a/packages/tm-core/src/services/task-service.ts b/packages/tm-core/src/services/task-service.ts index 0670dfa1..2b050e29 100644 --- a/packages/tm-core/src/services/task-service.ts +++ b/packages/tm-core/src/services/task-service.ts @@ -135,15 +135,28 @@ export class TaskService { } /** - * Get a single task by ID + * Get a single task by ID - delegates to storage layer */ async getTask(taskId: string, tag?: string): Promise { - const result = await this.getTaskList({ - tag, - includeSubtasks: true - }); + // Use provided tag or get active tag + const activeTag = tag || this.getActiveTag(); - return result.tasks.find((t) => t.id === taskId) || null; + try { + // Delegate to storage layer which handles the specific logic for tasks vs subtasks + return await this.storage.loadTask(String(taskId), activeTag); + } catch (error) { + throw new TaskMasterError( + `Failed to get task ${taskId}`, + ERROR_CODES.STORAGE_ERROR, + { + operation: 'getTask', + resource: 'task', + taskId: String(taskId), + tag: activeTag + }, + error as Error + ); + } } /** diff --git a/packages/tm-core/src/storage/file-storage/file-storage.ts b/packages/tm-core/src/storage/file-storage/file-storage.ts index a0486e41..b4349e4c 100644 --- a/packages/tm-core/src/storage/file-storage/file-storage.ts +++ b/packages/tm-core/src/storage/file-storage/file-storage.ts @@ -105,9 +105,65 @@ export class FileStorage implements IStorage { /** * Load a single task by ID from the tasks.json file + * Handles both regular tasks and subtasks (with dotted notation like "1.2") */ async loadTask(taskId: string, tag?: string): Promise { const tasks = await this.loadTasks(tag); + + // Check if this is a subtask (contains a dot) + if (taskId.includes('.')) { + const [parentId, subtaskId] = taskId.split('.'); + const parentTask = tasks.find((t) => String(t.id) === parentId); + + if (!parentTask || !parentTask.subtasks) { + return null; + } + + const subtask = parentTask.subtasks.find( + (st) => String(st.id) === subtaskId + ); + if (!subtask) { + return null; + } + + const toFullSubId = (maybeDotId: string | number): string => { + const depId = String(maybeDotId); + return depId.includes('.') ? depId : `${parentTask.id}.${depId}`; + }; + const resolvedDependencies = + subtask.dependencies?.map((dep) => toFullSubId(dep)) ?? []; + + // Return a Task-like object for the subtask with the full dotted ID + // Following the same pattern as findTaskById in utils.js + const subtaskResult = { + ...subtask, + id: taskId, // Use the full dotted ID + title: subtask.title || `Subtask ${subtaskId}`, + description: subtask.description || '', + status: subtask.status || 'pending', + priority: subtask.priority || parentTask.priority || 'medium', + dependencies: resolvedDependencies, + details: subtask.details || '', + testStrategy: subtask.testStrategy || '', + subtasks: [], + tags: parentTask.tags || [], + assignee: subtask.assignee || parentTask.assignee, + complexity: subtask.complexity || parentTask.complexity, + createdAt: subtask.createdAt || parentTask.createdAt, + updatedAt: subtask.updatedAt || parentTask.updatedAt, + // Add reference to parent task for context (like utils.js does) + parentTask: { + id: parentTask.id, + title: parentTask.title, + status: parentTask.status + }, + isSubtask: true + }; + + return subtaskResult; + } + + // Handle regular task lookup return tasks.find((task) => String(task.id) === String(taskId)) || null; }