trekoon 0.1.4 → 0.1.5
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/.agents/skills/trekoon/SKILL.md +7 -3
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/commands/help.ts +1 -1
- package/src/commands/quickstart.ts +1 -1
- package/src/commands/subtask.ts +105 -2
|
@@ -61,7 +61,11 @@ Use long flags (`--status`, `--description`, etc.) and ALWAYS append `--toon` to
|
|
|
61
61
|
|
|
62
62
|
## 1) Status Management
|
|
63
63
|
|
|
64
|
-
###
|
|
64
|
+
### Status values
|
|
65
|
+
|
|
66
|
+
Trekoon accepts any non-empty status string.
|
|
67
|
+
|
|
68
|
+
Recommended statuses for consistent workflows:
|
|
65
69
|
|
|
66
70
|
| Status | Meaning |
|
|
67
71
|
|--------|---------|
|
|
@@ -69,7 +73,7 @@ Use long flags (`--status`, `--description`, etc.) and ALWAYS append `--toon` to
|
|
|
69
73
|
| `in_progress` | Actively being worked on |
|
|
70
74
|
| `done` | Completed successfully |
|
|
71
75
|
|
|
72
|
-
Note: `in-progress` (hyphenated) is
|
|
76
|
+
Note: `in-progress` (hyphenated) is treated the same as `in_progress` for default list ordering/filtering.
|
|
73
77
|
|
|
74
78
|
### When to Change Status
|
|
75
79
|
|
|
@@ -165,7 +169,7 @@ trekoon epic show <id> --all --toon
|
|
|
165
169
|
trekoon task show <id> --all --toon
|
|
166
170
|
```
|
|
167
171
|
|
|
168
|
-
- `epic list` / `task list` defaults:
|
|
172
|
+
- `epic list` / `task list` / `subtask list` defaults:
|
|
169
173
|
- open work only (`in_progress`, `in-progress`, `todo`)
|
|
170
174
|
- prioritized as `in_progress`/`in-progress` first, then `todo`
|
|
171
175
|
- default limit `10`
|
package/README.md
CHANGED
|
@@ -77,10 +77,10 @@ Human view options:
|
|
|
77
77
|
|
|
78
78
|
- List and show commands default to table output in human mode.
|
|
79
79
|
- Use `--view compact` to restore compact pipe output.
|
|
80
|
-
- `epic list` and `
|
|
80
|
+
- `epic list`, `task list`, and `subtask list` support `--view table|compact`.
|
|
81
81
|
- `epic show` and `task show` support `--view table|compact|tree|detail`.
|
|
82
82
|
|
|
83
|
-
List defaults and filters (`epic list`, `task list`):
|
|
83
|
+
List defaults and filters (`epic list`, `task list`, `subtask list`):
|
|
84
84
|
|
|
85
85
|
- Default scope: open work only (`in_progress`, `in-progress`, `todo`)
|
|
86
86
|
- Default limit: `10`
|
package/package.json
CHANGED
package/src/commands/help.ts
CHANGED
|
@@ -36,7 +36,7 @@ const COMMAND_HELP: Record<string, string> = {
|
|
|
36
36
|
task:
|
|
37
37
|
"Usage: trekoon task <subcommand> [options] (list defaults: open statuses + limit 10; list flags: --status <csv> | --limit <n> | --all | --view table|compact; show: compact=task summary, tree=hierarchy, detail=descriptions, and --all defaults to detail in machine modes; update bulk flags: --all | --ids <csv> with --append <text> and/or --status <status>)",
|
|
38
38
|
subtask:
|
|
39
|
-
"Usage: trekoon subtask <subcommand> [options] (list
|
|
39
|
+
"Usage: trekoon subtask <subcommand> [options] (list defaults: open statuses + limit 10; list flags: --task <id> | --status <csv> | --limit <n> | --all | --view table|compact; update bulk flags: --all | --ids <csv> with --append <text> and/or --status <status>)",
|
|
40
40
|
dep: "Usage: trekoon dep <subcommand> [options]",
|
|
41
41
|
events: "Usage: trekoon events prune [--dry-run] [--archive] [--retention-days <n>]",
|
|
42
42
|
migrate: "Usage: trekoon migrate <status|rollback> [--to-version <n>]",
|
|
@@ -17,7 +17,7 @@ const QUICKSTART_TEXT = [
|
|
|
17
17
|
"3) Task details and description",
|
|
18
18
|
"- Human list and show views default to table format.",
|
|
19
19
|
"- Alternate list view: add --view compact.",
|
|
20
|
-
"- task/epic list defaults: open work only (in_progress/in-progress, todo), max 10.",
|
|
20
|
+
"- task/epic/subtask list defaults: open work only (in_progress/in-progress, todo), max 10.",
|
|
21
21
|
"- Filter list by status: --status in_progress,todo (CSV).",
|
|
22
22
|
"- Change page size: --limit <n>. Show all statuses and all rows with --all.",
|
|
23
23
|
"- --all cannot be combined with --status or --limit.",
|
package/src/commands/subtask.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasFlag, parseArgs, readEnumOption, readMissingOptionValue, readOption } from "./arg-parser";
|
|
1
|
+
import { hasFlag, parseArgs, parseStrictPositiveInt, readEnumOption, readMissingOptionValue, readOption } from "./arg-parser";
|
|
2
2
|
|
|
3
3
|
import { DomainError, type SubtaskRecord } from "../domain/types";
|
|
4
4
|
import { TrackerDomain } from "../domain/tracker-domain";
|
|
@@ -12,6 +12,8 @@ function formatSubtask(subtask: SubtaskRecord): string {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const VIEW_MODES = ["table", "compact"] as const;
|
|
15
|
+
const DEFAULT_SUBTASK_LIST_LIMIT = 10;
|
|
16
|
+
const DEFAULT_OPEN_SUBTASK_STATUSES = ["in_progress", "in-progress", "todo"] as const;
|
|
15
17
|
|
|
16
18
|
function parseIdsOption(rawIds: string | undefined): string[] {
|
|
17
19
|
if (rawIds === undefined) {
|
|
@@ -24,6 +26,45 @@ function parseIdsOption(rawIds: string | undefined): string[] {
|
|
|
24
26
|
.filter((value) => value.length > 0);
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
function parseStatusCsv(rawStatuses: string | undefined): string[] | undefined {
|
|
30
|
+
if (rawStatuses === undefined) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return rawStatuses
|
|
35
|
+
.split(",")
|
|
36
|
+
.map((value) => value.trim())
|
|
37
|
+
.filter((value) => value.length > 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function subtaskStatusPriority(status: string): number {
|
|
41
|
+
if (status === "in_progress" || status === "in-progress") {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (status === "todo") {
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return 2;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function filterSortAndLimitSubtasks(
|
|
53
|
+
subtasks: readonly SubtaskRecord[],
|
|
54
|
+
statuses: readonly string[] | undefined,
|
|
55
|
+
limit: number | undefined,
|
|
56
|
+
): SubtaskRecord[] {
|
|
57
|
+
const allowedStatuses = statuses === undefined ? undefined : new Set(statuses);
|
|
58
|
+
const filtered = allowedStatuses === undefined ? [...subtasks] : subtasks.filter((subtask) => allowedStatuses.has(subtask.status));
|
|
59
|
+
const sorted = [...filtered].sort((left, right) => subtaskStatusPriority(left.status) - subtaskStatusPriority(right.status));
|
|
60
|
+
|
|
61
|
+
if (limit === undefined) {
|
|
62
|
+
return sorted;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return sorted.slice(0, limit);
|
|
66
|
+
}
|
|
67
|
+
|
|
27
68
|
function appendLine(existing: string, line: string): string {
|
|
28
69
|
return existing.length > 0 ? `${existing}\n${line}` : line;
|
|
29
70
|
}
|
|
@@ -116,6 +157,8 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
116
157
|
case "list": {
|
|
117
158
|
const missingListOption =
|
|
118
159
|
readMissingOptionValue(parsed.missingOptionValues, "view") ??
|
|
160
|
+
readMissingOptionValue(parsed.missingOptionValues, "status", "s") ??
|
|
161
|
+
readMissingOptionValue(parsed.missingOptionValues, "limit", "l") ??
|
|
119
162
|
readMissingOptionValue(parsed.missingOptionValues, "task", "t");
|
|
120
163
|
if (missingListOption !== undefined) {
|
|
121
164
|
return failMissingOptionValue("subtask.list", missingListOption);
|
|
@@ -123,6 +166,10 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
123
166
|
|
|
124
167
|
const rawView: string | undefined = readOption(parsed.options, "view");
|
|
125
168
|
const view = readEnumOption(parsed.options, VIEW_MODES, "view");
|
|
169
|
+
const includeAll = hasFlag(parsed.flags, "all");
|
|
170
|
+
const rawStatuses = readOption(parsed.options, "status", "s");
|
|
171
|
+
const rawLimit = readOption(parsed.options, "limit", "l");
|
|
172
|
+
|
|
126
173
|
if (rawView !== undefined && view === undefined) {
|
|
127
174
|
return failResult({
|
|
128
175
|
command: "subtask.list",
|
|
@@ -135,8 +182,64 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
135
182
|
});
|
|
136
183
|
}
|
|
137
184
|
|
|
185
|
+
if (includeAll && rawStatuses !== undefined) {
|
|
186
|
+
return failResult({
|
|
187
|
+
command: "subtask.list",
|
|
188
|
+
human: "Use either --all or --status, not both.",
|
|
189
|
+
data: { code: "invalid_input", flags: ["all", "status"] },
|
|
190
|
+
error: {
|
|
191
|
+
code: "invalid_input",
|
|
192
|
+
message: "--all and --status are mutually exclusive",
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (includeAll && rawLimit !== undefined) {
|
|
198
|
+
return failResult({
|
|
199
|
+
command: "subtask.list",
|
|
200
|
+
human: "Use either --all or --limit, not both.",
|
|
201
|
+
data: { code: "invalid_input", flags: ["all", "limit"] },
|
|
202
|
+
error: {
|
|
203
|
+
code: "invalid_input",
|
|
204
|
+
message: "--all and --limit are mutually exclusive",
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const statuses = parseStatusCsv(rawStatuses);
|
|
210
|
+
if (rawStatuses !== undefined && statuses !== undefined && statuses.length === 0) {
|
|
211
|
+
return failResult({
|
|
212
|
+
command: "subtask.list",
|
|
213
|
+
human: "Provide at least one status with --status.",
|
|
214
|
+
data: { code: "invalid_input", status: rawStatuses },
|
|
215
|
+
error: {
|
|
216
|
+
code: "invalid_input",
|
|
217
|
+
message: "Invalid --status value",
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const parsedLimit = parseStrictPositiveInt(rawLimit);
|
|
223
|
+
if (Number.isNaN(parsedLimit)) {
|
|
224
|
+
return failResult({
|
|
225
|
+
command: "subtask.list",
|
|
226
|
+
human: "Invalid --limit value. Use an integer >= 1.",
|
|
227
|
+
data: { code: "invalid_input", limit: rawLimit },
|
|
228
|
+
error: {
|
|
229
|
+
code: "invalid_input",
|
|
230
|
+
message: "Invalid --limit value",
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
138
235
|
const taskId: string | undefined = readOption(parsed.options, "task", "t") ?? parsed.positional[1];
|
|
139
|
-
const
|
|
236
|
+
const selectedStatuses = includeAll
|
|
237
|
+
? undefined
|
|
238
|
+
: statuses ?? [...DEFAULT_OPEN_SUBTASK_STATUSES];
|
|
239
|
+
const selectedLimit = includeAll
|
|
240
|
+
? undefined
|
|
241
|
+
: parsedLimit ?? DEFAULT_SUBTASK_LIST_LIMIT;
|
|
242
|
+
const subtasks = filterSortAndLimitSubtasks(domain.listSubtasks(taskId), selectedStatuses, selectedLimit);
|
|
140
243
|
const listView = view ?? "table";
|
|
141
244
|
const human =
|
|
142
245
|
subtasks.length === 0
|