agkan 3.15.0 → 3.15.2
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 +33 -1
- package/dist/board/boardRoutes.js.map +1 -1
- package/dist/board/client/main.js +103 -183
- 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 +195 -162
- 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/db/adapters/sqlite-storage-backend.js +4 -4
- package/dist/db/adapters/sqlite-storage-backend.js.map +1 -1
- package/dist/hooks/hook-stop.mjs +107 -42
- package/dist/services/ExportImportService.d.ts +11 -0
- package/dist/services/ExportImportService.d.ts.map +1 -1
- package/dist/services/ExportImportService.js +44 -30
- package/dist/services/ExportImportService.js.map +1 -1
- 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 +24 -18
- 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/terminal/ensureSpawnHelperExecutable.d.ts +31 -0
- package/dist/terminal/ensureSpawnHelperExecutable.d.ts.map +1 -0
- package/dist/terminal/ensureSpawnHelperExecutable.js +89 -0
- package/dist/terminal/ensureSpawnHelperExecutable.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 +3 -2
- 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.
|
|
21
23
|
*/
|
|
22
|
-
function
|
|
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.
|
|
73
|
+
*/
|
|
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,50 +87,65 @@ 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
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Determine the root tasks to render for tree view output. A task is a root
|
|
116
|
+
* if none of its ancestors (walked via the full, unfiltered taskById map) are
|
|
117
|
+
* present in the filtered result set (pseudo-root). Without this, a filter
|
|
118
|
+
* (e.g. `-s in_progress`) that only matches child tasks would report a
|
|
119
|
+
* non-zero count while rendering nothing, since the real root would be
|
|
120
|
+
* filtered out.
|
|
121
|
+
*
|
|
122
|
+
* Checking the entire ancestor chain (not just the immediate parent) avoids
|
|
123
|
+
* rendering a task twice: if some ancestor is in the display set, that
|
|
124
|
+
* ancestor will itself become a root (or a pseudo-root) and its full,
|
|
125
|
+
* unfiltered subtree — which already includes this task — gets printed.
|
|
126
|
+
*/
|
|
127
|
+
function getTreeRootTasks(displayTasks, taskById) {
|
|
128
|
+
const displayTaskIds = new Set(displayTasks.map((task) => task.id));
|
|
129
|
+
return displayTasks.filter((task) => {
|
|
130
|
+
let current = task;
|
|
131
|
+
while (current.parent_id != null) {
|
|
132
|
+
if (displayTaskIds.has(current.parent_id)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
const parent = taskById.get(current.parent_id);
|
|
136
|
+
if (!parent) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
current = parent;
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
71
144
|
/**
|
|
72
145
|
* Build the JSON output object for the tree view.
|
|
73
146
|
*/
|
|
74
|
-
function buildTreeJsonOutput(displayTasks, options, tagIds,
|
|
75
|
-
const rootTasks = displayTasks
|
|
147
|
+
function buildTreeJsonOutput(displayTasks, options, tagIds, childrenByParentId, taskById, allTaskTags, allTasksMetadata) {
|
|
148
|
+
const rootTasks = getTreeRootTasks(displayTasks, taskById);
|
|
76
149
|
return {
|
|
77
150
|
totalCount: displayTasks.length,
|
|
78
151
|
viewMode: 'tree',
|
|
@@ -87,14 +160,14 @@ function buildTreeJsonOutput(displayTasks, options, tagIds, taskService, allTask
|
|
|
87
160
|
},
|
|
88
161
|
sort: options.sort || 'created_at',
|
|
89
162
|
order: options.order || 'desc',
|
|
90
|
-
tasks: rootTasks.map((task) => buildTreeNode(task,
|
|
163
|
+
tasks: rootTasks.map((task) => buildTreeNode(task, childrenByParentId, allTaskTags, allTasksMetadata)),
|
|
91
164
|
};
|
|
92
165
|
}
|
|
93
166
|
/**
|
|
94
167
|
* Build the JSON output object for the normal (flat) list view.
|
|
95
168
|
*/
|
|
96
|
-
function buildListJsonOutput(displayTasks, options, tagIds,
|
|
97
|
-
const tasksWithRelations = displayTasks.map((task) => buildTaskWithRelations(task,
|
|
169
|
+
function buildListJsonOutput(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata) {
|
|
170
|
+
const tasksWithRelations = displayTasks.map((task) => buildTaskWithRelations(task, taskById, allTaskTags, allTasksMetadata));
|
|
98
171
|
return {
|
|
99
172
|
totalCount: displayTasks.length,
|
|
100
173
|
filters: {
|
|
@@ -113,24 +186,11 @@ function buildListJsonOutput(displayTasks, options, tagIds, taskService, allTask
|
|
|
113
186
|
/**
|
|
114
187
|
* Build a single task object with parent, tags and metadata relations.
|
|
115
188
|
*/
|
|
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;
|
|
189
|
+
function buildTaskWithRelations(task, taskById, allTaskTags, allTasksMetadata) {
|
|
190
|
+
const parent = task.parent_id ? (taskById.get(task.parent_id) ?? null) : null;
|
|
120
191
|
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,
|
|
192
|
+
...mapTaskFields(task, allTaskTags, allTasksMetadata),
|
|
131
193
|
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
194
|
};
|
|
135
195
|
}
|
|
136
196
|
/**
|
|
@@ -158,65 +218,35 @@ function collectAllBlockedIds(blockMap) {
|
|
|
158
218
|
}
|
|
159
219
|
return allBlockedIds;
|
|
160
220
|
}
|
|
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
221
|
/**
|
|
181
222
|
* Recursively display dependency tree (blocker -> blocked only).
|
|
182
223
|
*/
|
|
183
|
-
function displayDependencyTree(task,
|
|
184
|
-
|
|
224
|
+
function displayDependencyTree(task, taskById, blockMap, allTasksMetadata, prefix, isLast, visited, relationshipLabel) {
|
|
225
|
+
printTreeNode(task, allTasksMetadata, prefix, isLast, relationshipLabel);
|
|
185
226
|
if (visited.has(task.id)) {
|
|
186
227
|
return;
|
|
187
228
|
}
|
|
188
229
|
visited.add(task.id);
|
|
189
230
|
const blockedIds = blockMap.get(task.id) || [];
|
|
190
|
-
const newPrefix = prefix + (isLast ? ' ' : '
|
|
231
|
+
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
191
232
|
blockedIds.forEach((blockedId, index) => {
|
|
192
|
-
const childTask =
|
|
233
|
+
const childTask = taskById.get(blockedId);
|
|
193
234
|
if (childTask && !visited.has(blockedId)) {
|
|
194
235
|
const isChildLast = index === blockedIds.length - 1;
|
|
195
|
-
displayDependencyTree(childTask,
|
|
236
|
+
displayDependencyTree(childTask, taskById, blockMap, allTasksMetadata, newPrefix, isChildLast, new Set(visited), '[blocks]');
|
|
196
237
|
}
|
|
197
238
|
});
|
|
198
239
|
}
|
|
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
240
|
/**
|
|
211
241
|
* Build blocked task nodes recursively.
|
|
212
242
|
*/
|
|
213
|
-
function buildBlockedNodes(blockedIds,
|
|
243
|
+
function buildBlockedNodes(blockedIds, taskById, blockMap, allTaskTags, allTasksMetadata, visited) {
|
|
214
244
|
const blocks = [];
|
|
215
245
|
for (const blockedId of blockedIds) {
|
|
216
246
|
if (!visited.has(blockedId)) {
|
|
217
|
-
const blockedTask =
|
|
247
|
+
const blockedTask = taskById.get(blockedId);
|
|
218
248
|
if (blockedTask) {
|
|
219
|
-
blocks.push(buildDepTreeNode(blockedTask,
|
|
249
|
+
blocks.push(buildDepTreeNode(blockedTask, taskById, blockMap, allTaskTags, allTasksMetadata, new Set(visited)));
|
|
220
250
|
}
|
|
221
251
|
}
|
|
222
252
|
}
|
|
@@ -225,31 +255,19 @@ function buildBlockedNodes(blockedIds, taskService, blockMap, allTaskTags, allTa
|
|
|
225
255
|
/**
|
|
226
256
|
* Build a dependency tree node recursively (blocking relationships only).
|
|
227
257
|
*/
|
|
228
|
-
function buildDepTreeNode(task,
|
|
258
|
+
function buildDepTreeNode(task, taskById, blockMap, allTaskTags, allTasksMetadata, visited) {
|
|
229
259
|
visited.add(task.id);
|
|
230
260
|
const blockedIds = blockMap.get(task.id) || [];
|
|
231
|
-
const
|
|
232
|
-
const blocks = buildBlockedNodes(blockedIds, taskService, blockMap, allTaskTags, allTasksMetadata, visited);
|
|
261
|
+
const blocks = buildBlockedNodes(blockedIds, taskById, blockMap, allTaskTags, allTasksMetadata, visited);
|
|
233
262
|
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,
|
|
263
|
+
...mapTaskFields(task, allTaskTags, allTasksMetadata),
|
|
246
264
|
blocks,
|
|
247
265
|
};
|
|
248
266
|
}
|
|
249
267
|
/**
|
|
250
268
|
* Build the JSON output for dep-tree view.
|
|
251
269
|
*/
|
|
252
|
-
function buildDepTreeJsonOutput(displayTasks, options, tagIds,
|
|
270
|
+
function buildDepTreeJsonOutput(displayTasks, options, tagIds, taskById, blockMap, allTaskTags, allTasksMetadata) {
|
|
253
271
|
const allBlockedIds = collectAllBlockedIds(blockMap);
|
|
254
272
|
const rootTasks = displayTasks.filter((task) => !allBlockedIds.has(task.id));
|
|
255
273
|
return {
|
|
@@ -263,7 +281,7 @@ function buildDepTreeJsonOutput(displayTasks, options, tagIds, taskService, bloc
|
|
|
263
281
|
all: options.all || false,
|
|
264
282
|
priority: options.priority || null,
|
|
265
283
|
},
|
|
266
|
-
tasks: rootTasks.map((task) => buildDepTreeNode(task,
|
|
284
|
+
tasks: rootTasks.map((task) => buildDepTreeNode(task, taskById, blockMap, allTaskTags, allTasksMetadata, new Set())),
|
|
267
285
|
};
|
|
268
286
|
}
|
|
269
287
|
/**
|
|
@@ -307,10 +325,10 @@ function printTaskPersonInfo(task) {
|
|
|
307
325
|
/**
|
|
308
326
|
* Print task parent reference.
|
|
309
327
|
*/
|
|
310
|
-
function printTaskParent(task,
|
|
328
|
+
function printTaskParent(task, taskById) {
|
|
311
329
|
if (!task.parent_id)
|
|
312
330
|
return;
|
|
313
|
-
const parentTask =
|
|
331
|
+
const parentTask = taskById.get(task.parent_id);
|
|
314
332
|
if (parentTask) {
|
|
315
333
|
console.log(` ${chalk_1.default.bold('Parent:')} ${chalk_1.default.cyan(`[${parentTask.id}]`)} ${parentTask.title}`);
|
|
316
334
|
}
|
|
@@ -360,10 +378,10 @@ function printTaskSeparator(isLast) {
|
|
|
360
378
|
/**
|
|
361
379
|
* Print a single task row in the human-readable list view.
|
|
362
380
|
*/
|
|
363
|
-
function printTaskRow(task,
|
|
381
|
+
function printTaskRow(task, taskById, allTaskTags, allTasksMetadata, isLast) {
|
|
364
382
|
printTaskHeader(task);
|
|
365
383
|
printTaskPersonInfo(task);
|
|
366
|
-
printTaskParent(task,
|
|
384
|
+
printTaskParent(task, taskById);
|
|
367
385
|
printTaskPriority(task);
|
|
368
386
|
printTaskTags(task.id, allTaskTags);
|
|
369
387
|
printTaskMetadata(task.id, allTasksMetadata);
|
|
@@ -379,17 +397,31 @@ function parseStatusFilter(statusStr) {
|
|
|
379
397
|
.map((s) => s.trim())
|
|
380
398
|
.filter((s) => s !== '');
|
|
381
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Validation error for task list command options.
|
|
402
|
+
* Carries the exact text-mode rendering previously passed to formatter.error,
|
|
403
|
+
* so the action's catch (setupTaskListCommand) can reproduce identical output
|
|
404
|
+
* while owning the single process.exit(1) call site.
|
|
405
|
+
*/
|
|
406
|
+
class TaskListValidationError extends errors_1.ValidationError {
|
|
407
|
+
render;
|
|
408
|
+
constructor(message, render) {
|
|
409
|
+
super(message);
|
|
410
|
+
this.render = render;
|
|
411
|
+
this.name = 'TaskListValidationError';
|
|
412
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
382
415
|
/**
|
|
383
416
|
* Validate status values.
|
|
384
417
|
*/
|
|
385
|
-
function validateStatuses(statuses
|
|
418
|
+
function validateStatuses(statuses) {
|
|
386
419
|
for (const s of statuses) {
|
|
387
420
|
if (!(0, validators_1.validateTaskStatus)(s)) {
|
|
388
|
-
|
|
389
|
-
console.
|
|
390
|
-
console.
|
|
421
|
+
throw new TaskListValidationError(`Invalid status: ${s}. Valid statuses: icebox, backlog, ready, in_progress, review, done, closed`, () => {
|
|
422
|
+
console.error(chalk_1.default.red(`Invalid status: ${s}`));
|
|
423
|
+
console.error('Valid statuses: icebox, backlog, ready, in_progress, review, done, closed');
|
|
391
424
|
});
|
|
392
|
-
process.exit(1);
|
|
393
425
|
}
|
|
394
426
|
}
|
|
395
427
|
}
|
|
@@ -402,25 +434,23 @@ function normalizeStatusFilter(statusParts) {
|
|
|
402
434
|
/**
|
|
403
435
|
* Validate sort field.
|
|
404
436
|
*/
|
|
405
|
-
function validateSortField(sortField
|
|
437
|
+
function validateSortField(sortField) {
|
|
406
438
|
if (!TaskService_1.ALLOWED_SORT_FIELDS.includes(sortField)) {
|
|
407
|
-
|
|
408
|
-
console.
|
|
409
|
-
console.
|
|
439
|
+
throw new TaskListValidationError(`Invalid sort field: ${sortField}. Valid fields: ${TaskService_1.ALLOWED_SORT_FIELDS.join(', ')}`, () => {
|
|
440
|
+
console.error(chalk_1.default.red(`Invalid sort field: ${sortField}`));
|
|
441
|
+
console.error(`Valid fields: ${TaskService_1.ALLOWED_SORT_FIELDS.join(', ')}`);
|
|
410
442
|
});
|
|
411
|
-
process.exit(1);
|
|
412
443
|
}
|
|
413
444
|
}
|
|
414
445
|
/**
|
|
415
446
|
* Validate sort order.
|
|
416
447
|
*/
|
|
417
|
-
function validateSortOrder(sortOrder
|
|
448
|
+
function validateSortOrder(sortOrder) {
|
|
418
449
|
if (!['asc', 'desc'].includes(sortOrder)) {
|
|
419
|
-
|
|
420
|
-
console.
|
|
421
|
-
console.
|
|
450
|
+
throw new TaskListValidationError(`Invalid sort order: ${sortOrder}. Valid orders: asc, desc`, () => {
|
|
451
|
+
console.error(chalk_1.default.red(`Invalid sort order: ${sortOrder}`));
|
|
452
|
+
console.error('Valid orders: asc, desc');
|
|
422
453
|
});
|
|
423
|
-
process.exit(1);
|
|
424
454
|
}
|
|
425
455
|
}
|
|
426
456
|
/**
|
|
@@ -435,14 +465,13 @@ function parsePriorityFilter(priorityStr) {
|
|
|
435
465
|
/**
|
|
436
466
|
* Validate priority values.
|
|
437
467
|
*/
|
|
438
|
-
function validatePriorities(priorities
|
|
468
|
+
function validatePriorities(priorities) {
|
|
439
469
|
for (const p of priorities) {
|
|
440
470
|
if (!(0, models_1.isPriority)(p)) {
|
|
441
|
-
|
|
442
|
-
console.
|
|
443
|
-
console.
|
|
471
|
+
throw new TaskListValidationError(`Invalid priority: ${p}. Valid priorities: ${models_1.PRIORITIES.join(', ')}`, () => {
|
|
472
|
+
console.error(chalk_1.default.red(`Invalid priority: ${p}`));
|
|
473
|
+
console.error(`Valid priorities: ${models_1.PRIORITIES.join(', ')}`);
|
|
444
474
|
});
|
|
445
|
-
process.exit(1);
|
|
446
475
|
}
|
|
447
476
|
}
|
|
448
477
|
}
|
|
@@ -455,9 +484,9 @@ function normalizePriorityFilter(priorityParts) {
|
|
|
455
484
|
/**
|
|
456
485
|
* Resolve tag filter from options to an array of tag IDs.
|
|
457
486
|
* Returns undefined if no tag filter is specified.
|
|
458
|
-
*
|
|
487
|
+
* Throws TaskListValidationError if the tag filter is invalid.
|
|
459
488
|
*/
|
|
460
|
-
function resolveTagIds(tagOption, tagService
|
|
489
|
+
function resolveTagIds(tagOption, tagService) {
|
|
461
490
|
if (!tagOption) {
|
|
462
491
|
return undefined;
|
|
463
492
|
}
|
|
@@ -466,20 +495,18 @@ function resolveTagIds(tagOption, tagService, formatter) {
|
|
|
466
495
|
.map((s) => s.trim())
|
|
467
496
|
.filter((s) => s !== '');
|
|
468
497
|
if (parts.length === 0) {
|
|
469
|
-
|
|
470
|
-
console.
|
|
498
|
+
throw new TaskListValidationError('Invalid tag filter. Provide tag IDs or names.', () => {
|
|
499
|
+
console.error(chalk_1.default.red('\nError: Invalid tag filter. Provide tag IDs or names.\n'));
|
|
471
500
|
});
|
|
472
|
-
process.exit(1);
|
|
473
501
|
}
|
|
474
502
|
const tagIds = [];
|
|
475
503
|
for (const part of parts) {
|
|
476
504
|
const { tag, byId } = (0, tag_resolver_1.resolveTag)(tagService, part);
|
|
477
505
|
if (!tag) {
|
|
478
506
|
const message = byId ? `Tag with ID "${part}" not found` : `Tag with name "${part}" not found`;
|
|
479
|
-
|
|
480
|
-
console.
|
|
507
|
+
throw new TaskListValidationError(message, () => {
|
|
508
|
+
console.error(chalk_1.default.red(`\nError: ${message}\n`));
|
|
481
509
|
});
|
|
482
|
-
process.exit(1);
|
|
483
510
|
}
|
|
484
511
|
tagIds.push(tag.id);
|
|
485
512
|
}
|
|
@@ -488,14 +515,14 @@ function resolveTagIds(tagOption, tagService, formatter) {
|
|
|
488
515
|
/**
|
|
489
516
|
* Handle tree view output.
|
|
490
517
|
*/
|
|
491
|
-
function handleTreeView(displayTasks, options, tagIds,
|
|
492
|
-
const rootTasks = displayTasks
|
|
493
|
-
formatter.output(() => buildTreeJsonOutput(displayTasks, options, tagIds,
|
|
518
|
+
function handleTreeView(displayTasks, options, tagIds, childrenByParentId, taskById, allTaskTags, allTasksMetadata, formatter) {
|
|
519
|
+
const rootTasks = getTreeRootTasks(displayTasks, taskById);
|
|
520
|
+
formatter.output(() => buildTreeJsonOutput(displayTasks, options, tagIds, childrenByParentId, taskById, allTaskTags, allTasksMetadata), () => {
|
|
494
521
|
console.log(chalk_1.default.bold(`\nFound ${displayTasks.length} task(s) in tree view:\n`));
|
|
495
522
|
console.log(chalk_1.default.bold('─'.repeat(80)));
|
|
496
523
|
rootTasks.forEach((task, index) => {
|
|
497
524
|
const isLast = index === rootTasks.length - 1;
|
|
498
|
-
displayTaskTree(
|
|
525
|
+
displayTaskTree(task, childrenByParentId, '', isLast, allTasksMetadata);
|
|
499
526
|
});
|
|
500
527
|
console.log('\n');
|
|
501
528
|
});
|
|
@@ -503,16 +530,16 @@ function handleTreeView(displayTasks, options, tagIds, taskService, allTaskTags,
|
|
|
503
530
|
/**
|
|
504
531
|
* Handle dependency tree view output.
|
|
505
532
|
*/
|
|
506
|
-
function handleDepTreeView(displayTasks, options, tagIds,
|
|
533
|
+
function handleDepTreeView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, taskBlockService, formatter) {
|
|
507
534
|
const blockMap = buildBlockMap(taskBlockService);
|
|
508
535
|
const allBlockedIds = collectAllBlockedIds(blockMap);
|
|
509
536
|
const rootTasks = displayTasks.filter((task) => !allBlockedIds.has(task.id));
|
|
510
|
-
formatter.output(() => buildDepTreeJsonOutput(displayTasks, options, tagIds,
|
|
537
|
+
formatter.output(() => buildDepTreeJsonOutput(displayTasks, options, tagIds, taskById, blockMap, allTaskTags, allTasksMetadata), () => {
|
|
511
538
|
console.log(chalk_1.default.bold(`\nFound ${displayTasks.length} task(s) in dependency tree view:\n`));
|
|
512
|
-
console.log(chalk_1.default.bold('
|
|
539
|
+
console.log(chalk_1.default.bold('─'.repeat(80)));
|
|
513
540
|
rootTasks.forEach((task, index) => {
|
|
514
541
|
const isLast = index === rootTasks.length - 1;
|
|
515
|
-
displayDependencyTree(task,
|
|
542
|
+
displayDependencyTree(task, taskById, blockMap, allTasksMetadata, '', isLast, new Set());
|
|
516
543
|
});
|
|
517
544
|
console.log('\n');
|
|
518
545
|
});
|
|
@@ -520,13 +547,13 @@ function handleDepTreeView(displayTasks, options, tagIds, taskService, allTaskTa
|
|
|
520
547
|
/**
|
|
521
548
|
* Handle normal list view output.
|
|
522
549
|
*/
|
|
523
|
-
function handleListView(displayTasks, options, tagIds,
|
|
524
|
-
formatter.output(() => buildListJsonOutput(displayTasks, options, tagIds,
|
|
550
|
+
function handleListView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, formatter) {
|
|
551
|
+
formatter.output(() => buildListJsonOutput(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata), () => {
|
|
525
552
|
console.log(chalk_1.default.bold(`\nFound ${displayTasks.length} task(s):\n`));
|
|
526
553
|
console.log(chalk_1.default.bold('─'.repeat(80)));
|
|
527
554
|
displayTasks.forEach((task, index) => {
|
|
528
555
|
const isLast = index === displayTasks.length - 1;
|
|
529
|
-
printTaskRow(task,
|
|
556
|
+
printTaskRow(task, taskById, allTaskTags, allTasksMetadata, isLast);
|
|
530
557
|
});
|
|
531
558
|
console.log('\n');
|
|
532
559
|
});
|
|
@@ -570,41 +597,44 @@ function handleEmptyResults(options, tagIds, tasks, formatter) {
|
|
|
570
597
|
/**
|
|
571
598
|
* Resolve all filter parameters from command options.
|
|
572
599
|
*/
|
|
573
|
-
function resolveFilters(options, tagService
|
|
600
|
+
function resolveFilters(options, tagService) {
|
|
574
601
|
// Validate and normalize status filter
|
|
575
602
|
let statusFilter;
|
|
576
603
|
if (options.status) {
|
|
577
604
|
const statusParts = parseStatusFilter(options.status);
|
|
578
|
-
validateStatuses(statusParts
|
|
605
|
+
validateStatuses(statusParts);
|
|
579
606
|
statusFilter = normalizeStatusFilter(statusParts);
|
|
580
607
|
}
|
|
581
608
|
// Validate sort field and order
|
|
582
609
|
if (options.sort) {
|
|
583
|
-
validateSortField(options.sort
|
|
610
|
+
validateSortField(options.sort);
|
|
584
611
|
}
|
|
585
612
|
if (options.order) {
|
|
586
|
-
validateSortOrder(options.order
|
|
613
|
+
validateSortOrder(options.order);
|
|
587
614
|
}
|
|
588
615
|
// Parse and resolve tag filter
|
|
589
|
-
const tagIds = resolveTagIds(options.tag, tagService
|
|
616
|
+
const tagIds = resolveTagIds(options.tag, tagService);
|
|
590
617
|
// Validate and normalize priority filter
|
|
591
618
|
let priorityFilter;
|
|
592
619
|
if (options.priority) {
|
|
593
620
|
const priorityParts = parsePriorityFilter(options.priority);
|
|
594
|
-
validatePriorities(priorityParts
|
|
621
|
+
validatePriorities(priorityParts);
|
|
595
622
|
priorityFilter = normalizePriorityFilter(priorityParts);
|
|
596
623
|
}
|
|
597
624
|
return { statusFilter, tagIds, priorityFilter };
|
|
598
625
|
}
|
|
599
626
|
/**
|
|
600
|
-
* Fetch task data and relations (tags, metadata, blocks).
|
|
627
|
+
* Fetch task data and relations (tags, metadata, blocks, parent/children maps).
|
|
601
628
|
*/
|
|
602
|
-
function fetchTaskRelations() {
|
|
629
|
+
function fetchTaskRelations(taskService) {
|
|
603
630
|
const { taskTagService, metadataService, taskBlockService } = (0, service_container_1.getServiceContainer)();
|
|
631
|
+
const { taskById, childrenByParentId } = buildTaskMaps(taskService);
|
|
604
632
|
return {
|
|
605
633
|
allTaskTags: taskTagService.getAllTaskTags(),
|
|
606
634
|
allTasksMetadata: metadataService.getAllTasksMetadata(),
|
|
607
635
|
taskBlockService,
|
|
636
|
+
taskById,
|
|
637
|
+
childrenByParentId,
|
|
608
638
|
};
|
|
609
639
|
}
|
|
610
640
|
/**
|
|
@@ -627,21 +657,21 @@ function queryAndFilterTasks(taskService, options, statusFilter, tagIds, priorit
|
|
|
627
657
|
*/
|
|
628
658
|
async function executeListAction(options, formatter) {
|
|
629
659
|
const { taskService, tagService } = (0, service_container_1.getServiceContainer)();
|
|
630
|
-
const { statusFilter, tagIds, priorityFilter } = resolveFilters(options, tagService
|
|
660
|
+
const { statusFilter, tagIds, priorityFilter } = resolveFilters(options, tagService);
|
|
631
661
|
const { displayTasks, allTasks } = queryAndFilterTasks(taskService, options, statusFilter, tagIds, priorityFilter);
|
|
632
662
|
if (displayTasks.length === 0) {
|
|
633
663
|
handleEmptyResults(options, tagIds, allTasks, formatter);
|
|
634
664
|
return;
|
|
635
665
|
}
|
|
636
|
-
const { allTaskTags, allTasksMetadata, taskBlockService } = fetchTaskRelations();
|
|
666
|
+
const { allTaskTags, allTasksMetadata, taskBlockService, taskById, childrenByParentId } = fetchTaskRelations(taskService);
|
|
637
667
|
if (options.tree) {
|
|
638
|
-
handleTreeView(displayTasks, options, tagIds,
|
|
668
|
+
handleTreeView(displayTasks, options, tagIds, childrenByParentId, taskById, allTaskTags, allTasksMetadata, formatter);
|
|
639
669
|
}
|
|
640
670
|
else if (options.depTree) {
|
|
641
|
-
handleDepTreeView(displayTasks, options, tagIds,
|
|
671
|
+
handleDepTreeView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, taskBlockService, formatter);
|
|
642
672
|
}
|
|
643
673
|
else {
|
|
644
|
-
handleListView(displayTasks, options, tagIds,
|
|
674
|
+
handleListView(displayTasks, options, tagIds, taskById, allTaskTags, allTasksMetadata, formatter);
|
|
645
675
|
}
|
|
646
676
|
}
|
|
647
677
|
function setupTaskListCommand(program) {
|
|
@@ -671,12 +701,15 @@ function setupTaskListCommand(program) {
|
|
|
671
701
|
await executeListAction(options, formatter);
|
|
672
702
|
}
|
|
673
703
|
catch (error) {
|
|
674
|
-
if (error instanceof
|
|
704
|
+
if (error instanceof TaskListValidationError) {
|
|
705
|
+
formatter.error(error.message, error.render);
|
|
706
|
+
}
|
|
707
|
+
else if (error instanceof Error) {
|
|
675
708
|
(0, error_handler_1.handleError)(error, options);
|
|
676
709
|
}
|
|
677
710
|
else {
|
|
678
711
|
formatter.error('An unknown error occurred', () => {
|
|
679
|
-
console.
|
|
712
|
+
console.error(chalk_1.default.red('\n✗ An unknown error occurred\n'));
|
|
680
713
|
});
|
|
681
714
|
}
|
|
682
715
|
process.exit(1);
|