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