fix: handle subtasks in getTask method (#1254)

Co-authored-by: Ralph Khreish <Crunchyman-ralph@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
This commit is contained in:
Ralph Khreish
2025-09-26 20:58:15 +02:00
committed by GitHub
parent b7f32eac5a
commit af53525cbc
3 changed files with 82 additions and 6 deletions

View File

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

View File

@@ -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<Task | null> { async getTask(taskId: string, tag?: string): Promise<Task | null> {
const result = await this.getTaskList({ // Use provided tag or get active tag
tag, const activeTag = tag || this.getActiveTag();
includeSubtasks: true
});
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
);
}
} }
/** /**

View File

@@ -105,9 +105,65 @@ export class FileStorage implements IStorage {
/** /**
* Load a single task by ID from the tasks.json file * 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<Task | null> { async loadTask(taskId: string, tag?: string): Promise<Task | null> {
const tasks = await this.loadTasks(tag); 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; return tasks.find((task) => String(task.id) === String(taskId)) || null;
} }