agkan 3.15.0 → 3.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/board/boardRoutes.d.ts +1 -0
- package/dist/board/boardRoutes.d.ts.map +1 -1
- package/dist/board/boardRoutes.js +17 -1
- package/dist/board/boardRoutes.js.map +1 -1
- package/dist/board/server.js +1 -1
- package/dist/board/server.js.map +1 -1
- package/dist/cli/commands/block/add.js +10 -10
- package/dist/cli/commands/block/add.js.map +1 -1
- package/dist/cli/commands/block/list.js +4 -4
- package/dist/cli/commands/block/list.js.map +1 -1
- package/dist/cli/commands/block/remove.js +9 -9
- package/dist/cli/commands/block/remove.js.map +1 -1
- package/dist/cli/commands/comment/add.js +2 -2
- package/dist/cli/commands/comment/add.js.map +1 -1
- package/dist/cli/commands/comment/delete.js +2 -2
- package/dist/cli/commands/comment/delete.js.map +1 -1
- package/dist/cli/commands/comment/list.js +2 -2
- package/dist/cli/commands/comment/list.js.map +1 -1
- package/dist/cli/commands/config/get.js +2 -2
- package/dist/cli/commands/config/get.js.map +1 -1
- package/dist/cli/commands/meta/delete.js +3 -3
- package/dist/cli/commands/meta/delete.js.map +1 -1
- package/dist/cli/commands/meta/get.js +3 -3
- package/dist/cli/commands/meta/get.js.map +1 -1
- package/dist/cli/commands/meta/list.js +2 -2
- package/dist/cli/commands/meta/list.js.map +1 -1
- package/dist/cli/commands/meta/set.js +4 -4
- package/dist/cli/commands/meta/set.js.map +1 -1
- package/dist/cli/commands/tag/add.js +7 -7
- package/dist/cli/commands/tag/add.js.map +1 -1
- package/dist/cli/commands/tag/attach.js +7 -7
- package/dist/cli/commands/tag/attach.js.map +1 -1
- package/dist/cli/commands/tag/delete.js +5 -5
- package/dist/cli/commands/tag/delete.js.map +1 -1
- package/dist/cli/commands/tag/detach.js +7 -7
- package/dist/cli/commands/tag/detach.js.map +1 -1
- package/dist/cli/commands/tag/list.js +2 -2
- package/dist/cli/commands/tag/list.js.map +1 -1
- package/dist/cli/commands/tag/rename.js +8 -8
- package/dist/cli/commands/tag/rename.js.map +1 -1
- package/dist/cli/commands/tag/show.js +3 -3
- package/dist/cli/commands/tag/show.js.map +1 -1
- package/dist/cli/commands/task/add.js +11 -11
- package/dist/cli/commands/task/add.js.map +1 -1
- package/dist/cli/commands/task/archive.js +3 -3
- package/dist/cli/commands/task/archive.js.map +1 -1
- package/dist/cli/commands/task/copy.js +5 -5
- package/dist/cli/commands/task/copy.js.map +1 -1
- package/dist/cli/commands/task/count.js +4 -4
- package/dist/cli/commands/task/count.js.map +1 -1
- package/dist/cli/commands/task/delete.js +3 -3
- package/dist/cli/commands/task/delete.js.map +1 -1
- package/dist/cli/commands/task/find.js +2 -2
- package/dist/cli/commands/task/find.js.map +1 -1
- package/dist/cli/commands/task/get.js +3 -3
- package/dist/cli/commands/task/get.js.map +1 -1
- package/dist/cli/commands/task/list.d.ts.map +1 -1
- package/dist/cli/commands/task/list.js +163 -160
- package/dist/cli/commands/task/list.js.map +1 -1
- package/dist/cli/commands/task/purge.js +3 -3
- package/dist/cli/commands/task/purge.js.map +1 -1
- package/dist/cli/commands/task/unarchive.js +3 -3
- package/dist/cli/commands/task/unarchive.js.map +1 -1
- package/dist/cli/commands/task/update-helpers.js +11 -11
- package/dist/cli/commands/task/update-helpers.js.map +1 -1
- package/dist/cli/commands/task/update-parent.js +8 -8
- package/dist/cli/commands/task/update-parent.js.map +1 -1
- package/dist/cli/commands/task/update.js +3 -3
- package/dist/cli/commands/task/update.js.map +1 -1
- package/dist/cli/utils/error-handler.js +4 -4
- package/dist/cli/utils/error-handler.js.map +1 -1
- package/dist/cli/utils/output-formatter.js +2 -2
- package/dist/cli/utils/output-formatter.js.map +1 -1
- package/dist/hooks/hook-stop.mjs +107 -42
- package/dist/services/index.d.ts +1 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -3
- package/dist/services/index.js.map +1 -1
- package/dist/services/types.d.ts +28 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +6 -0
- package/dist/services/types.js.map +1 -0
- package/dist/terminal/PtySessionService.d.ts +1 -1
- package/dist/terminal/PtySessionService.d.ts.map +1 -1
- package/dist/terminal/PtySessionService.js +2 -7
- package/dist/terminal/PtySessionService.js.map +1 -1
- package/dist/terminal/buildHookEnv.d.ts +8 -0
- package/dist/terminal/buildHookEnv.d.ts.map +1 -0
- package/dist/terminal/buildHookEnv.js +24 -0
- package/dist/terminal/buildHookEnv.js.map +1 -0
- package/dist/utils/runTargetStatus.d.ts +9 -0
- package/dist/utils/runTargetStatus.d.ts.map +1 -0
- package/dist/utils/runTargetStatus.js +17 -0
- package/dist/utils/runTargetStatus.js.map +1 -0
- package/package.json +1 -1
- package/dist/services/ClaudeProcessService.d.ts +0 -110
- package/dist/services/ClaudeProcessService.d.ts.map +0 -1
- package/dist/services/ClaudeProcessService.js +0 -312
- package/dist/services/ClaudeProcessService.js.map +0 -1
|
@@ -16,10 +16,68 @@ const validators_1 = require("../../utils/validators");
|
|
|
16
16
|
const tag_resolver_1 = require("../../utils/tag-resolver");
|
|
17
17
|
const format_1 = require("../../../utils/format");
|
|
18
18
|
const output_formatter_1 = require("../../utils/output-formatter");
|
|
19
|
+
const errors_1 = require("../../../errors");
|
|
19
20
|
/**
|
|
20
|
-
*
|
|
21
|
+
* Map the common set of task fields (shared by tree/list/dep-tree node builders)
|
|
22
|
+
* plus resolved tags and metadata for a task.
|
|
23
|
+
*/
|
|
24
|
+
function mapTaskFields(task, allTaskTags, allTasksMetadata) {
|
|
25
|
+
const tags = allTaskTags.get(task.id);
|
|
26
|
+
const metadata = allTasksMetadata.get(task.id);
|
|
27
|
+
return {
|
|
28
|
+
id: task.id,
|
|
29
|
+
title: task.title,
|
|
30
|
+
body: task.body,
|
|
31
|
+
author: task.author,
|
|
32
|
+
assignees: task.assignees,
|
|
33
|
+
status: task.status,
|
|
34
|
+
priority: task.priority,
|
|
35
|
+
parent_id: task.parent_id,
|
|
36
|
+
created_at: task.created_at,
|
|
37
|
+
updated_at: task.updated_at,
|
|
38
|
+
tags: tags ? tags.map((tag) => ({ id: tag.id, name: tag.name })) : [],
|
|
39
|
+
metadata: metadata ? metadata.map((m) => ({ key: m.key, value: m.value })) : [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build a Map of task id -> task (including archived, unfiltered) and a Map of
|
|
44
|
+
* parent id -> non-archived children, both from a single bulk query. This
|
|
45
|
+
* replaces the N+1 getChildTasks()/getTask() calls previously made per node.
|
|
46
|
+
*
|
|
47
|
+
* getChildTasks()/getTask() ignore the list command's status/tag/priority filters
|
|
48
|
+
* and getChildTasks() always excludes archived tasks, so the maps here are built
|
|
49
|
+
* from the full (unfiltered) task set to preserve that exact behavior.
|
|
50
|
+
*/
|
|
51
|
+
function buildTaskMaps(taskService) {
|
|
52
|
+
const allTasks = taskService.listTasks({ includeArchived: true }, 'created_at', 'asc');
|
|
53
|
+
const taskById = new Map();
|
|
54
|
+
const childrenByParentId = new Map();
|
|
55
|
+
for (const task of allTasks) {
|
|
56
|
+
taskById.set(task.id, task);
|
|
57
|
+
if (task.is_archived || task.parent_id == null) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const existing = childrenByParentId.get(task.parent_id);
|
|
61
|
+
if (existing) {
|
|
62
|
+
existing.push(task);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
childrenByParentId.set(task.parent_id, [task]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { taskById, childrenByParentId };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Print a single tree/dep-tree node line (connector, id, title, status),
|
|
72
|
+
* followed by its priority and metadata lines.
|
|
21
73
|
*/
|
|
22
|
-
function
|
|
74
|
+
function printTreeNode(task, allTasksMetadata, prefix, isLast, relationshipLabel) {
|
|
75
|
+
const statusColor = (0, format_1.getStatusColor)(task.status);
|
|
76
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
77
|
+
const labelStr = relationshipLabel ? `${chalk_1.default.gray(relationshipLabel)} ` : '';
|
|
78
|
+
console.log(`${prefix}${connector}${labelStr}${chalk_1.default.bold.cyan(`[${task.id}]`)} ${chalk_1.default.bold(task.title)} ` +
|
|
79
|
+
`${chalk_1.default[statusColor](`(${task.status})`)}`);
|
|
80
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
23
81
|
if (task.priority) {
|
|
24
82
|
console.log(`${childPrefix}${formatPriority(task.priority)}`);
|
|
25
83
|
}
|
|
@@ -29,49 +87,34 @@ function printTreeTaskDetails(task, childPrefix, allTasksMetadata) {
|
|
|
29
87
|
console.log(`${childPrefix}${chalk_1.default.bold('Metadata:')} ${metadataStrings.join(', ')}`);
|
|
30
88
|
}
|
|
31
89
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
printTreeTaskDetails(task, childPrefix, allTasksMetadata);
|
|
39
|
-
const children = taskService.getChildTasks(task.id);
|
|
90
|
+
/**
|
|
91
|
+
* Recursive function to display tasks in tree structure.
|
|
92
|
+
*/
|
|
93
|
+
function displayTaskTree(task, childrenByParentId, prefix, isLast, allTasksMetadata) {
|
|
94
|
+
printTreeNode(task, allTasksMetadata, prefix, isLast);
|
|
95
|
+
const children = childrenByParentId.get(task.id) || [];
|
|
40
96
|
if (children.length > 0) {
|
|
41
97
|
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
42
98
|
children.forEach((child, index) => {
|
|
43
99
|
const isChildLast = index === children.length - 1;
|
|
44
|
-
displayTaskTree(
|
|
100
|
+
displayTaskTree(child, childrenByParentId, newPrefix, isChildLast, allTasksMetadata);
|
|
45
101
|
});
|
|
46
102
|
}
|
|
47
103
|
}
|
|
48
104
|
/**
|
|
49
105
|
* Build a tree node from a task, including children recursively.
|
|
50
106
|
*/
|
|
51
|
-
function buildTreeNode(task,
|
|
52
|
-
const
|
|
53
|
-
const metadata = allTasksMetadata.get(task.id);
|
|
54
|
-
const children = taskService.getChildTasks(task.id);
|
|
107
|
+
function buildTreeNode(task, childrenByParentId, allTaskTags, allTasksMetadata) {
|
|
108
|
+
const children = childrenByParentId.get(task.id) || [];
|
|
55
109
|
return {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
body: task.body,
|
|
59
|
-
author: task.author,
|
|
60
|
-
assignees: task.assignees,
|
|
61
|
-
status: task.status,
|
|
62
|
-
priority: task.priority,
|
|
63
|
-
parent_id: task.parent_id,
|
|
64
|
-
created_at: task.created_at,
|
|
65
|
-
updated_at: task.updated_at,
|
|
66
|
-
tags: tags ? tags.map((tag) => ({ id: tag.id, name: tag.name })) : [],
|
|
67
|
-
metadata: metadata ? metadata.map((m) => ({ key: m.key, value: m.value })) : [],
|
|
68
|
-
children: children.map((child) => buildTreeNode(child, taskService, allTaskTags, allTasksMetadata)),
|
|
110
|
+
...mapTaskFields(task, allTaskTags, allTasksMetadata),
|
|
111
|
+
children: children.map((child) => buildTreeNode(child, childrenByParentId, allTaskTags, allTasksMetadata)),
|
|
69
112
|
};
|
|
70
113
|
}
|
|
71
114
|
/**
|
|
72
115
|
* Build the JSON output object for the tree view.
|
|
73
116
|
*/
|
|
74
|
-
function buildTreeJsonOutput(displayTasks, options, tagIds,
|
|
117
|
+
function buildTreeJsonOutput(displayTasks, options, tagIds, childrenByParentId, allTaskTags, allTasksMetadata) {
|
|
75
118
|
const rootTasks = displayTasks.filter((task) => !task.parent_id);
|
|
76
119
|
return {
|
|
77
120
|
totalCount: displayTasks.length,
|
|
@@ -87,14 +130,14 @@ function buildTreeJsonOutput(displayTasks, options, tagIds, taskService, allTask
|
|
|
87
130
|
},
|
|
88
131
|
sort: options.sort || 'created_at',
|
|
89
132
|
order: options.order || 'desc',
|
|
90
|
-
tasks: rootTasks.map((task) => buildTreeNode(task,
|
|
133
|
+
tasks: rootTasks.map((task) => buildTreeNode(task, childrenByParentId, allTaskTags, allTasksMetadata)),
|
|
91
134
|
};
|
|
92
135
|
}
|
|
93
136
|
/**
|
|
94
137
|
* Build the JSON output object for the normal (flat) list view.
|
|
95
138
|
*/
|
|
96
|
-
function buildListJsonOutput(displayTasks, options, tagIds,
|
|
97
|
-
const tasksWithRelations = displayTasks.map((task) => buildTaskWithRelations(task,
|
|
139
|
+
function buildListJsonOutput(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata) {
|
|
140
|
+
const tasksWithRelations = displayTasks.map((task) => buildTaskWithRelations(task, taskById, allTaskTags, allTasksMetadata));
|
|
98
141
|
return {
|
|
99
142
|
totalCount: displayTasks.length,
|
|
100
143
|
filters: {
|
|
@@ -113,24 +156,11 @@ function buildListJsonOutput(displayTasks, options, tagIds, taskService, allTask
|
|
|
113
156
|
/**
|
|
114
157
|
* Build a single task object with parent, tags and metadata relations.
|
|
115
158
|
*/
|
|
116
|
-
function buildTaskWithRelations(task,
|
|
117
|
-
const
|
|
118
|
-
const metadata = allTasksMetadata.get(task.id);
|
|
119
|
-
const parent = task.parent_id ? taskService.getTask(task.parent_id) : null;
|
|
159
|
+
function buildTaskWithRelations(task, taskById, allTaskTags, allTasksMetadata) {
|
|
160
|
+
const parent = task.parent_id ? (taskById.get(task.parent_id) ?? null) : null;
|
|
120
161
|
return {
|
|
121
|
-
|
|
122
|
-
title: task.title,
|
|
123
|
-
body: task.body,
|
|
124
|
-
author: task.author,
|
|
125
|
-
assignees: task.assignees,
|
|
126
|
-
status: task.status,
|
|
127
|
-
priority: task.priority,
|
|
128
|
-
parent_id: task.parent_id,
|
|
129
|
-
created_at: task.created_at,
|
|
130
|
-
updated_at: task.updated_at,
|
|
162
|
+
...mapTaskFields(task, allTaskTags, allTasksMetadata),
|
|
131
163
|
parent: parent ? { id: parent.id, title: parent.title, status: parent.status } : null,
|
|
132
|
-
tags: tags ? tags.map((tag) => ({ id: tag.id, name: tag.name })) : [],
|
|
133
|
-
metadata: metadata ? metadata.map((m) => ({ key: m.key, value: m.value })) : [],
|
|
134
164
|
};
|
|
135
165
|
}
|
|
136
166
|
/**
|
|
@@ -158,65 +188,35 @@ function collectAllBlockedIds(blockMap) {
|
|
|
158
188
|
}
|
|
159
189
|
return allBlockedIds;
|
|
160
190
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Display a single task node line with optional metadata and relationship label.
|
|
163
|
-
*/
|
|
164
|
-
function printTreeNodeLine(task, allTasksMetadata, prefix, isLast, relationshipLabel) {
|
|
165
|
-
const statusColor = (0, format_1.getStatusColor)(task.status);
|
|
166
|
-
const connector = isLast ? '\u2514\u2500\u2500 ' : '\u251c\u2500\u2500 ';
|
|
167
|
-
const labelStr = relationshipLabel ? `${chalk_1.default.gray(relationshipLabel)} ` : '';
|
|
168
|
-
console.log(`${prefix}${connector}${labelStr}${chalk_1.default.bold.cyan(`[${task.id}]`)} ${chalk_1.default.bold(task.title)} ` +
|
|
169
|
-
`${chalk_1.default[statusColor](`(${task.status})`)}`);
|
|
170
|
-
const childPrefix = prefix + (isLast ? ' ' : '\u2502 ');
|
|
171
|
-
if (task.priority) {
|
|
172
|
-
console.log(`${childPrefix}${formatPriority(task.priority)}`);
|
|
173
|
-
}
|
|
174
|
-
const metadata = allTasksMetadata.get(task.id);
|
|
175
|
-
if (metadata && metadata.length > 0) {
|
|
176
|
-
const metadataStrings = metadata.map(formatMetadataEntry);
|
|
177
|
-
console.log(`${childPrefix}${chalk_1.default.bold('Metadata:')} ${metadataStrings.join(', ')}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
191
|
/**
|
|
181
192
|
* Recursively display dependency tree (blocker -> blocked only).
|
|
182
193
|
*/
|
|
183
|
-
function displayDependencyTree(task,
|
|
184
|
-
|
|
194
|
+
function displayDependencyTree(task, taskById, blockMap, allTasksMetadata, prefix, isLast, visited, relationshipLabel) {
|
|
195
|
+
printTreeNode(task, allTasksMetadata, prefix, isLast, relationshipLabel);
|
|
185
196
|
if (visited.has(task.id)) {
|
|
186
197
|
return;
|
|
187
198
|
}
|
|
188
199
|
visited.add(task.id);
|
|
189
200
|
const blockedIds = blockMap.get(task.id) || [];
|
|
190
|
-
const newPrefix = prefix + (isLast ? ' ' : '
|
|
201
|
+
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
191
202
|
blockedIds.forEach((blockedId, index) => {
|
|
192
|
-
const childTask =
|
|
203
|
+
const childTask = taskById.get(blockedId);
|
|
193
204
|
if (childTask && !visited.has(blockedId)) {
|
|
194
205
|
const isChildLast = index === blockedIds.length - 1;
|
|
195
|
-
displayDependencyTree(childTask,
|
|
206
|
+
displayDependencyTree(childTask, taskById, blockMap, allTasksMetadata, newPrefix, isChildLast, new Set(visited), '[blocks]');
|
|
196
207
|
}
|
|
197
208
|
});
|
|
198
209
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Extract tags and metadata for a task.
|
|
201
|
-
*/
|
|
202
|
-
function extractTaskRelations(task, allTaskTags, allTasksMetadata) {
|
|
203
|
-
const tags = allTaskTags.get(task.id);
|
|
204
|
-
const metadata = allTasksMetadata.get(task.id);
|
|
205
|
-
return {
|
|
206
|
-
tags: tags ? tags.map((tag) => ({ id: tag.id, name: tag.name })) : [],
|
|
207
|
-
metadata: metadata ? metadata.map((m) => ({ key: m.key, value: m.value })) : [],
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
210
|
/**
|
|
211
211
|
* Build blocked task nodes recursively.
|
|
212
212
|
*/
|
|
213
|
-
function buildBlockedNodes(blockedIds,
|
|
213
|
+
function buildBlockedNodes(blockedIds, taskById, blockMap, allTaskTags, allTasksMetadata, visited) {
|
|
214
214
|
const blocks = [];
|
|
215
215
|
for (const blockedId of blockedIds) {
|
|
216
216
|
if (!visited.has(blockedId)) {
|
|
217
|
-
const blockedTask =
|
|
217
|
+
const blockedTask = taskById.get(blockedId);
|
|
218
218
|
if (blockedTask) {
|
|
219
|
-
blocks.push(buildDepTreeNode(blockedTask,
|
|
219
|
+
blocks.push(buildDepTreeNode(blockedTask, taskById, blockMap, allTaskTags, allTasksMetadata, new Set(visited)));
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
}
|
|
@@ -225,31 +225,19 @@ function buildBlockedNodes(blockedIds, taskService, blockMap, allTaskTags, allTa
|
|
|
225
225
|
/**
|
|
226
226
|
* Build a dependency tree node recursively (blocking relationships only).
|
|
227
227
|
*/
|
|
228
|
-
function buildDepTreeNode(task,
|
|
228
|
+
function buildDepTreeNode(task, taskById, blockMap, allTaskTags, allTasksMetadata, visited) {
|
|
229
229
|
visited.add(task.id);
|
|
230
230
|
const blockedIds = blockMap.get(task.id) || [];
|
|
231
|
-
const
|
|
232
|
-
const blocks = buildBlockedNodes(blockedIds, taskService, blockMap, allTaskTags, allTasksMetadata, visited);
|
|
231
|
+
const blocks = buildBlockedNodes(blockedIds, taskById, blockMap, allTaskTags, allTasksMetadata, visited);
|
|
233
232
|
return {
|
|
234
|
-
|
|
235
|
-
title: task.title,
|
|
236
|
-
body: task.body,
|
|
237
|
-
author: task.author,
|
|
238
|
-
assignees: task.assignees,
|
|
239
|
-
status: task.status,
|
|
240
|
-
priority: task.priority,
|
|
241
|
-
parent_id: task.parent_id,
|
|
242
|
-
created_at: task.created_at,
|
|
243
|
-
updated_at: task.updated_at,
|
|
244
|
-
tags,
|
|
245
|
-
metadata,
|
|
233
|
+
...mapTaskFields(task, allTaskTags, allTasksMetadata),
|
|
246
234
|
blocks,
|
|
247
235
|
};
|
|
248
236
|
}
|
|
249
237
|
/**
|
|
250
238
|
* Build the JSON output for dep-tree view.
|
|
251
239
|
*/
|
|
252
|
-
function buildDepTreeJsonOutput(displayTasks, options, tagIds,
|
|
240
|
+
function buildDepTreeJsonOutput(displayTasks, options, tagIds, taskById, blockMap, allTaskTags, allTasksMetadata) {
|
|
253
241
|
const allBlockedIds = collectAllBlockedIds(blockMap);
|
|
254
242
|
const rootTasks = displayTasks.filter((task) => !allBlockedIds.has(task.id));
|
|
255
243
|
return {
|
|
@@ -263,7 +251,7 @@ function buildDepTreeJsonOutput(displayTasks, options, tagIds, taskService, bloc
|
|
|
263
251
|
all: options.all || false,
|
|
264
252
|
priority: options.priority || null,
|
|
265
253
|
},
|
|
266
|
-
tasks: rootTasks.map((task) => buildDepTreeNode(task,
|
|
254
|
+
tasks: rootTasks.map((task) => buildDepTreeNode(task, taskById, blockMap, allTaskTags, allTasksMetadata, new Set())),
|
|
267
255
|
};
|
|
268
256
|
}
|
|
269
257
|
/**
|
|
@@ -307,10 +295,10 @@ function printTaskPersonInfo(task) {
|
|
|
307
295
|
/**
|
|
308
296
|
* Print task parent reference.
|
|
309
297
|
*/
|
|
310
|
-
function printTaskParent(task,
|
|
298
|
+
function printTaskParent(task, taskById) {
|
|
311
299
|
if (!task.parent_id)
|
|
312
300
|
return;
|
|
313
|
-
const parentTask =
|
|
301
|
+
const parentTask = taskById.get(task.parent_id);
|
|
314
302
|
if (parentTask) {
|
|
315
303
|
console.log(` ${chalk_1.default.bold('Parent:')} ${chalk_1.default.cyan(`[${parentTask.id}]`)} ${parentTask.title}`);
|
|
316
304
|
}
|
|
@@ -360,10 +348,10 @@ function printTaskSeparator(isLast) {
|
|
|
360
348
|
/**
|
|
361
349
|
* Print a single task row in the human-readable list view.
|
|
362
350
|
*/
|
|
363
|
-
function printTaskRow(task,
|
|
351
|
+
function printTaskRow(task, taskById, allTaskTags, allTasksMetadata, isLast) {
|
|
364
352
|
printTaskHeader(task);
|
|
365
353
|
printTaskPersonInfo(task);
|
|
366
|
-
printTaskParent(task,
|
|
354
|
+
printTaskParent(task, taskById);
|
|
367
355
|
printTaskPriority(task);
|
|
368
356
|
printTaskTags(task.id, allTaskTags);
|
|
369
357
|
printTaskMetadata(task.id, allTasksMetadata);
|
|
@@ -379,17 +367,31 @@ function parseStatusFilter(statusStr) {
|
|
|
379
367
|
.map((s) => s.trim())
|
|
380
368
|
.filter((s) => s !== '');
|
|
381
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Validation error for task list command options.
|
|
372
|
+
* Carries the exact text-mode rendering previously passed to formatter.error,
|
|
373
|
+
* so the action's catch (setupTaskListCommand) can reproduce identical output
|
|
374
|
+
* while owning the single process.exit(1) call site.
|
|
375
|
+
*/
|
|
376
|
+
class TaskListValidationError extends errors_1.ValidationError {
|
|
377
|
+
render;
|
|
378
|
+
constructor(message, render) {
|
|
379
|
+
super(message);
|
|
380
|
+
this.render = render;
|
|
381
|
+
this.name = 'TaskListValidationError';
|
|
382
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
382
385
|
/**
|
|
383
386
|
* Validate status values.
|
|
384
387
|
*/
|
|
385
|
-
function validateStatuses(statuses
|
|
388
|
+
function validateStatuses(statuses) {
|
|
386
389
|
for (const s of statuses) {
|
|
387
390
|
if (!(0, validators_1.validateTaskStatus)(s)) {
|
|
388
|
-
|
|
389
|
-
console.
|
|
390
|
-
console.
|
|
391
|
+
throw new TaskListValidationError(`Invalid status: ${s}. Valid statuses: icebox, backlog, ready, in_progress, review, done, closed`, () => {
|
|
392
|
+
console.error(chalk_1.default.red(`Invalid status: ${s}`));
|
|
393
|
+
console.error('Valid statuses: icebox, backlog, ready, in_progress, review, done, closed');
|
|
391
394
|
});
|
|
392
|
-
process.exit(1);
|
|
393
395
|
}
|
|
394
396
|
}
|
|
395
397
|
}
|
|
@@ -402,25 +404,23 @@ function normalizeStatusFilter(statusParts) {
|
|
|
402
404
|
/**
|
|
403
405
|
* Validate sort field.
|
|
404
406
|
*/
|
|
405
|
-
function validateSortField(sortField
|
|
407
|
+
function validateSortField(sortField) {
|
|
406
408
|
if (!TaskService_1.ALLOWED_SORT_FIELDS.includes(sortField)) {
|
|
407
|
-
|
|
408
|
-
console.
|
|
409
|
-
console.
|
|
409
|
+
throw new TaskListValidationError(`Invalid sort field: ${sortField}. Valid fields: ${TaskService_1.ALLOWED_SORT_FIELDS.join(', ')}`, () => {
|
|
410
|
+
console.error(chalk_1.default.red(`Invalid sort field: ${sortField}`));
|
|
411
|
+
console.error(`Valid fields: ${TaskService_1.ALLOWED_SORT_FIELDS.join(', ')}`);
|
|
410
412
|
});
|
|
411
|
-
process.exit(1);
|
|
412
413
|
}
|
|
413
414
|
}
|
|
414
415
|
/**
|
|
415
416
|
* Validate sort order.
|
|
416
417
|
*/
|
|
417
|
-
function validateSortOrder(sortOrder
|
|
418
|
+
function validateSortOrder(sortOrder) {
|
|
418
419
|
if (!['asc', 'desc'].includes(sortOrder)) {
|
|
419
|
-
|
|
420
|
-
console.
|
|
421
|
-
console.
|
|
420
|
+
throw new TaskListValidationError(`Invalid sort order: ${sortOrder}. Valid orders: asc, desc`, () => {
|
|
421
|
+
console.error(chalk_1.default.red(`Invalid sort order: ${sortOrder}`));
|
|
422
|
+
console.error('Valid orders: asc, desc');
|
|
422
423
|
});
|
|
423
|
-
process.exit(1);
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
/**
|
|
@@ -435,14 +435,13 @@ function parsePriorityFilter(priorityStr) {
|
|
|
435
435
|
/**
|
|
436
436
|
* Validate priority values.
|
|
437
437
|
*/
|
|
438
|
-
function validatePriorities(priorities
|
|
438
|
+
function validatePriorities(priorities) {
|
|
439
439
|
for (const p of priorities) {
|
|
440
440
|
if (!(0, models_1.isPriority)(p)) {
|
|
441
|
-
|
|
442
|
-
console.
|
|
443
|
-
console.
|
|
441
|
+
throw new TaskListValidationError(`Invalid priority: ${p}. Valid priorities: ${models_1.PRIORITIES.join(', ')}`, () => {
|
|
442
|
+
console.error(chalk_1.default.red(`Invalid priority: ${p}`));
|
|
443
|
+
console.error(`Valid priorities: ${models_1.PRIORITIES.join(', ')}`);
|
|
444
444
|
});
|
|
445
|
-
process.exit(1);
|
|
446
445
|
}
|
|
447
446
|
}
|
|
448
447
|
}
|
|
@@ -455,9 +454,9 @@ function normalizePriorityFilter(priorityParts) {
|
|
|
455
454
|
/**
|
|
456
455
|
* Resolve tag filter from options to an array of tag IDs.
|
|
457
456
|
* Returns undefined if no tag filter is specified.
|
|
458
|
-
*
|
|
457
|
+
* Throws TaskListValidationError if the tag filter is invalid.
|
|
459
458
|
*/
|
|
460
|
-
function resolveTagIds(tagOption, tagService
|
|
459
|
+
function resolveTagIds(tagOption, tagService) {
|
|
461
460
|
if (!tagOption) {
|
|
462
461
|
return undefined;
|
|
463
462
|
}
|
|
@@ -466,20 +465,18 @@ function resolveTagIds(tagOption, tagService, formatter) {
|
|
|
466
465
|
.map((s) => s.trim())
|
|
467
466
|
.filter((s) => s !== '');
|
|
468
467
|
if (parts.length === 0) {
|
|
469
|
-
|
|
470
|
-
console.
|
|
468
|
+
throw new TaskListValidationError('Invalid tag filter. Provide tag IDs or names.', () => {
|
|
469
|
+
console.error(chalk_1.default.red('\nError: Invalid tag filter. Provide tag IDs or names.\n'));
|
|
471
470
|
});
|
|
472
|
-
process.exit(1);
|
|
473
471
|
}
|
|
474
472
|
const tagIds = [];
|
|
475
473
|
for (const part of parts) {
|
|
476
474
|
const { tag, byId } = (0, tag_resolver_1.resolveTag)(tagService, part);
|
|
477
475
|
if (!tag) {
|
|
478
476
|
const message = byId ? `Tag with ID "${part}" not found` : `Tag with name "${part}" not found`;
|
|
479
|
-
|
|
480
|
-
console.
|
|
477
|
+
throw new TaskListValidationError(message, () => {
|
|
478
|
+
console.error(chalk_1.default.red(`\nError: ${message}\n`));
|
|
481
479
|
});
|
|
482
|
-
process.exit(1);
|
|
483
480
|
}
|
|
484
481
|
tagIds.push(tag.id);
|
|
485
482
|
}
|
|
@@ -488,14 +485,14 @@ function resolveTagIds(tagOption, tagService, formatter) {
|
|
|
488
485
|
/**
|
|
489
486
|
* Handle tree view output.
|
|
490
487
|
*/
|
|
491
|
-
function handleTreeView(displayTasks, options, tagIds,
|
|
488
|
+
function handleTreeView(displayTasks, options, tagIds, childrenByParentId, allTaskTags, allTasksMetadata, formatter) {
|
|
492
489
|
const rootTasks = displayTasks.filter((task) => !task.parent_id);
|
|
493
|
-
formatter.output(() => buildTreeJsonOutput(displayTasks, options, tagIds,
|
|
490
|
+
formatter.output(() => buildTreeJsonOutput(displayTasks, options, tagIds, childrenByParentId, allTaskTags, allTasksMetadata), () => {
|
|
494
491
|
console.log(chalk_1.default.bold(`\nFound ${displayTasks.length} task(s) in tree view:\n`));
|
|
495
492
|
console.log(chalk_1.default.bold('─'.repeat(80)));
|
|
496
493
|
rootTasks.forEach((task, index) => {
|
|
497
494
|
const isLast = index === rootTasks.length - 1;
|
|
498
|
-
displayTaskTree(
|
|
495
|
+
displayTaskTree(task, childrenByParentId, '', isLast, allTasksMetadata);
|
|
499
496
|
});
|
|
500
497
|
console.log('\n');
|
|
501
498
|
});
|
|
@@ -503,16 +500,16 @@ function handleTreeView(displayTasks, options, tagIds, taskService, allTaskTags,
|
|
|
503
500
|
/**
|
|
504
501
|
* Handle dependency tree view output.
|
|
505
502
|
*/
|
|
506
|
-
function handleDepTreeView(displayTasks, options, tagIds,
|
|
503
|
+
function handleDepTreeView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, taskBlockService, formatter) {
|
|
507
504
|
const blockMap = buildBlockMap(taskBlockService);
|
|
508
505
|
const allBlockedIds = collectAllBlockedIds(blockMap);
|
|
509
506
|
const rootTasks = displayTasks.filter((task) => !allBlockedIds.has(task.id));
|
|
510
|
-
formatter.output(() => buildDepTreeJsonOutput(displayTasks, options, tagIds,
|
|
507
|
+
formatter.output(() => buildDepTreeJsonOutput(displayTasks, options, tagIds, taskById, blockMap, allTaskTags, allTasksMetadata), () => {
|
|
511
508
|
console.log(chalk_1.default.bold(`\nFound ${displayTasks.length} task(s) in dependency tree view:\n`));
|
|
512
|
-
console.log(chalk_1.default.bold('
|
|
509
|
+
console.log(chalk_1.default.bold('─'.repeat(80)));
|
|
513
510
|
rootTasks.forEach((task, index) => {
|
|
514
511
|
const isLast = index === rootTasks.length - 1;
|
|
515
|
-
displayDependencyTree(task,
|
|
512
|
+
displayDependencyTree(task, taskById, blockMap, allTasksMetadata, '', isLast, new Set());
|
|
516
513
|
});
|
|
517
514
|
console.log('\n');
|
|
518
515
|
});
|
|
@@ -520,13 +517,13 @@ function handleDepTreeView(displayTasks, options, tagIds, taskService, allTaskTa
|
|
|
520
517
|
/**
|
|
521
518
|
* Handle normal list view output.
|
|
522
519
|
*/
|
|
523
|
-
function handleListView(displayTasks, options, tagIds,
|
|
524
|
-
formatter.output(() => buildListJsonOutput(displayTasks, options, tagIds,
|
|
520
|
+
function handleListView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, formatter) {
|
|
521
|
+
formatter.output(() => buildListJsonOutput(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata), () => {
|
|
525
522
|
console.log(chalk_1.default.bold(`\nFound ${displayTasks.length} task(s):\n`));
|
|
526
523
|
console.log(chalk_1.default.bold('─'.repeat(80)));
|
|
527
524
|
displayTasks.forEach((task, index) => {
|
|
528
525
|
const isLast = index === displayTasks.length - 1;
|
|
529
|
-
printTaskRow(task,
|
|
526
|
+
printTaskRow(task, taskById, allTaskTags, allTasksMetadata, isLast);
|
|
530
527
|
});
|
|
531
528
|
console.log('\n');
|
|
532
529
|
});
|
|
@@ -570,41 +567,44 @@ function handleEmptyResults(options, tagIds, tasks, formatter) {
|
|
|
570
567
|
/**
|
|
571
568
|
* Resolve all filter parameters from command options.
|
|
572
569
|
*/
|
|
573
|
-
function resolveFilters(options, tagService
|
|
570
|
+
function resolveFilters(options, tagService) {
|
|
574
571
|
// Validate and normalize status filter
|
|
575
572
|
let statusFilter;
|
|
576
573
|
if (options.status) {
|
|
577
574
|
const statusParts = parseStatusFilter(options.status);
|
|
578
|
-
validateStatuses(statusParts
|
|
575
|
+
validateStatuses(statusParts);
|
|
579
576
|
statusFilter = normalizeStatusFilter(statusParts);
|
|
580
577
|
}
|
|
581
578
|
// Validate sort field and order
|
|
582
579
|
if (options.sort) {
|
|
583
|
-
validateSortField(options.sort
|
|
580
|
+
validateSortField(options.sort);
|
|
584
581
|
}
|
|
585
582
|
if (options.order) {
|
|
586
|
-
validateSortOrder(options.order
|
|
583
|
+
validateSortOrder(options.order);
|
|
587
584
|
}
|
|
588
585
|
// Parse and resolve tag filter
|
|
589
|
-
const tagIds = resolveTagIds(options.tag, tagService
|
|
586
|
+
const tagIds = resolveTagIds(options.tag, tagService);
|
|
590
587
|
// Validate and normalize priority filter
|
|
591
588
|
let priorityFilter;
|
|
592
589
|
if (options.priority) {
|
|
593
590
|
const priorityParts = parsePriorityFilter(options.priority);
|
|
594
|
-
validatePriorities(priorityParts
|
|
591
|
+
validatePriorities(priorityParts);
|
|
595
592
|
priorityFilter = normalizePriorityFilter(priorityParts);
|
|
596
593
|
}
|
|
597
594
|
return { statusFilter, tagIds, priorityFilter };
|
|
598
595
|
}
|
|
599
596
|
/**
|
|
600
|
-
* Fetch task data and relations (tags, metadata, blocks).
|
|
597
|
+
* Fetch task data and relations (tags, metadata, blocks, parent/children maps).
|
|
601
598
|
*/
|
|
602
|
-
function fetchTaskRelations() {
|
|
599
|
+
function fetchTaskRelations(taskService) {
|
|
603
600
|
const { taskTagService, metadataService, taskBlockService } = (0, service_container_1.getServiceContainer)();
|
|
601
|
+
const { taskById, childrenByParentId } = buildTaskMaps(taskService);
|
|
604
602
|
return {
|
|
605
603
|
allTaskTags: taskTagService.getAllTaskTags(),
|
|
606
604
|
allTasksMetadata: metadataService.getAllTasksMetadata(),
|
|
607
605
|
taskBlockService,
|
|
606
|
+
taskById,
|
|
607
|
+
childrenByParentId,
|
|
608
608
|
};
|
|
609
609
|
}
|
|
610
610
|
/**
|
|
@@ -627,21 +627,21 @@ function queryAndFilterTasks(taskService, options, statusFilter, tagIds, priorit
|
|
|
627
627
|
*/
|
|
628
628
|
async function executeListAction(options, formatter) {
|
|
629
629
|
const { taskService, tagService } = (0, service_container_1.getServiceContainer)();
|
|
630
|
-
const { statusFilter, tagIds, priorityFilter } = resolveFilters(options, tagService
|
|
630
|
+
const { statusFilter, tagIds, priorityFilter } = resolveFilters(options, tagService);
|
|
631
631
|
const { displayTasks, allTasks } = queryAndFilterTasks(taskService, options, statusFilter, tagIds, priorityFilter);
|
|
632
632
|
if (displayTasks.length === 0) {
|
|
633
633
|
handleEmptyResults(options, tagIds, allTasks, formatter);
|
|
634
634
|
return;
|
|
635
635
|
}
|
|
636
|
-
const { allTaskTags, allTasksMetadata, taskBlockService } = fetchTaskRelations();
|
|
636
|
+
const { allTaskTags, allTasksMetadata, taskBlockService, taskById, childrenByParentId } = fetchTaskRelations(taskService);
|
|
637
637
|
if (options.tree) {
|
|
638
|
-
handleTreeView(displayTasks, options, tagIds,
|
|
638
|
+
handleTreeView(displayTasks, options, tagIds, childrenByParentId, allTaskTags, allTasksMetadata, formatter);
|
|
639
639
|
}
|
|
640
640
|
else if (options.depTree) {
|
|
641
|
-
handleDepTreeView(displayTasks, options, tagIds,
|
|
641
|
+
handleDepTreeView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, taskBlockService, formatter);
|
|
642
642
|
}
|
|
643
643
|
else {
|
|
644
|
-
handleListView(displayTasks, options, tagIds,
|
|
644
|
+
handleListView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, formatter);
|
|
645
645
|
}
|
|
646
646
|
}
|
|
647
647
|
function setupTaskListCommand(program) {
|
|
@@ -671,12 +671,15 @@ function setupTaskListCommand(program) {
|
|
|
671
671
|
await executeListAction(options, formatter);
|
|
672
672
|
}
|
|
673
673
|
catch (error) {
|
|
674
|
-
if (error instanceof
|
|
674
|
+
if (error instanceof TaskListValidationError) {
|
|
675
|
+
formatter.error(error.message, error.render);
|
|
676
|
+
}
|
|
677
|
+
else if (error instanceof Error) {
|
|
675
678
|
(0, error_handler_1.handleError)(error, options);
|
|
676
679
|
}
|
|
677
680
|
else {
|
|
678
681
|
formatter.error('An unknown error occurred', () => {
|
|
679
|
-
console.
|
|
682
|
+
console.error(chalk_1.default.red('\n✗ An unknown error occurred\n'));
|
|
680
683
|
});
|
|
681
684
|
}
|
|
682
685
|
process.exit(1);
|