trekoon 0.1.7 → 0.1.8
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 +39 -15
- package/README.md +110 -9
- package/package.json +1 -1
- package/src/commands/arg-parser.ts +13 -0
- package/src/commands/dep.ts +20 -1
- package/src/commands/epic.ts +72 -7
- package/src/commands/help.ts +255 -17
- package/src/commands/quickstart.ts +88 -24
- package/src/commands/subtask.ts +76 -6
- package/src/commands/task.ts +299 -7
- package/src/domain/tracker-domain.ts +113 -7
- package/src/domain/types.ts +7 -0
- package/src/runtime/cli-shell.ts +1 -2
- package/src/runtime/version.ts +20 -0
package/src/commands/help.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { okResult } from "../io/output";
|
|
2
2
|
import { type CliContext, type CliResult } from "../runtime/command-types";
|
|
3
|
+
import { CLI_VERSION } from "../runtime/version";
|
|
3
4
|
|
|
4
5
|
const ROOT_HELP = [
|
|
5
6
|
"Trekoon - AI-first local issue tracker",
|
|
7
|
+
`Version: ${CLI_VERSION}`,
|
|
6
8
|
"",
|
|
7
9
|
"Usage:",
|
|
8
10
|
" trekoon [global-options] <command> [command-options]",
|
|
@@ -15,7 +17,7 @@ const ROOT_HELP = [
|
|
|
15
17
|
"",
|
|
16
18
|
"Commands:",
|
|
17
19
|
" init Initialize .trekoon storage and local DB",
|
|
18
|
-
" quickstart Show
|
|
20
|
+
" quickstart Show AI execution loop + task detail workflow",
|
|
19
21
|
" wipe Remove local Trekoon state (requires --yes)",
|
|
20
22
|
" epic Epic lifecycle commands",
|
|
21
23
|
" task Task lifecycle commands",
|
|
@@ -24,25 +26,260 @@ const ROOT_HELP = [
|
|
|
24
26
|
" events Event retention and cleanup commands",
|
|
25
27
|
" migrate Migration status and rollback commands",
|
|
26
28
|
" sync Cross-branch sync commands",
|
|
27
|
-
" skills Project-local skill install/link
|
|
29
|
+
" skills Project-local skill install/update/link",
|
|
30
|
+
].join("\n");
|
|
31
|
+
|
|
32
|
+
const INIT_HELP = [
|
|
33
|
+
"Usage: trekoon init [--json|--toon]",
|
|
34
|
+
"",
|
|
35
|
+
"Purpose:",
|
|
36
|
+
" Initialize local Trekoon storage (.trekoon) and database.",
|
|
37
|
+
"",
|
|
38
|
+
"Examples:",
|
|
39
|
+
" trekoon init",
|
|
40
|
+
" trekoon --json init",
|
|
41
|
+
].join("\n");
|
|
42
|
+
|
|
43
|
+
const QUICKSTART_HELP = [
|
|
44
|
+
"Usage: trekoon quickstart [--json|--toon]",
|
|
45
|
+
"",
|
|
46
|
+
"Purpose:",
|
|
47
|
+
" Show the canonical Trekoon AI execution loop and task-detail workflow.",
|
|
48
|
+
"",
|
|
49
|
+
"Flow:",
|
|
50
|
+
" 1) trekoon --toon sync status",
|
|
51
|
+
" 2) trekoon --toon task ready --limit 5",
|
|
52
|
+
" 3) trekoon --toon task next",
|
|
53
|
+
" 4) trekoon --toon dep reverse <task-or-subtask-id>",
|
|
54
|
+
" 5) trekoon --toon task update <task-id> --status in_progress",
|
|
55
|
+
"",
|
|
56
|
+
"Examples:",
|
|
57
|
+
" trekoon quickstart",
|
|
58
|
+
" trekoon --toon quickstart",
|
|
59
|
+
].join("\n");
|
|
60
|
+
|
|
61
|
+
const WIPE_HELP = [
|
|
62
|
+
"Usage: trekoon wipe --yes [--json|--toon]",
|
|
63
|
+
"",
|
|
64
|
+
"Purpose:",
|
|
65
|
+
" Remove local Trekoon state for the current repository.",
|
|
66
|
+
"",
|
|
67
|
+
"Options:",
|
|
68
|
+
" --yes Required safety confirmation.",
|
|
69
|
+
"",
|
|
70
|
+
"Examples:",
|
|
71
|
+
" trekoon wipe --yes",
|
|
72
|
+
].join("\n");
|
|
73
|
+
|
|
74
|
+
const EPIC_HELP = [
|
|
75
|
+
"Usage: trekoon epic <create|list|show|update|delete> [options]",
|
|
76
|
+
"",
|
|
77
|
+
"List behavior:",
|
|
78
|
+
" Defaults:",
|
|
79
|
+
" - Open statuses only: in_progress, in-progress, todo",
|
|
80
|
+
" - Limit: 10",
|
|
81
|
+
" Flags:",
|
|
82
|
+
" --status <csv> | --limit <n> | --cursor <n> | --all | --view table|compact",
|
|
83
|
+
" Pagination:",
|
|
84
|
+
" - --cursor is offset-like",
|
|
85
|
+
" - Machine modes expose meta.pagination.hasMore / nextCursor",
|
|
86
|
+
" Constraints:",
|
|
87
|
+
" - --all is mutually exclusive with --status, --limit, and --cursor",
|
|
88
|
+
"",
|
|
89
|
+
"Show behavior:",
|
|
90
|
+
" Views:",
|
|
91
|
+
" - compact: epic summary",
|
|
92
|
+
" - tree: hierarchy",
|
|
93
|
+
" - detail: descriptions",
|
|
94
|
+
" Machine default:",
|
|
95
|
+
" - With --all, machine modes default to detail",
|
|
96
|
+
"",
|
|
97
|
+
"Update behavior:",
|
|
98
|
+
" Bulk target flags:",
|
|
99
|
+
" --all | --ids <csv>",
|
|
100
|
+
" Bulk fields:",
|
|
101
|
+
" --append <text> and/or --status <status>",
|
|
102
|
+
].join("\n");
|
|
103
|
+
|
|
104
|
+
const TASK_HELP = [
|
|
105
|
+
"Usage: trekoon task <create|list|show|ready|next|update|delete> [options]",
|
|
106
|
+
"",
|
|
107
|
+
"List behavior:",
|
|
108
|
+
" Defaults:",
|
|
109
|
+
" - Open statuses only: in_progress, in-progress, todo",
|
|
110
|
+
" - Limit: 10",
|
|
111
|
+
" Flags:",
|
|
112
|
+
" --status <csv> | --limit <n> | --cursor <n> | --all | --view table|compact",
|
|
113
|
+
" Pagination:",
|
|
114
|
+
" - --cursor is offset-like",
|
|
115
|
+
" - Machine modes expose meta.pagination.hasMore / nextCursor",
|
|
116
|
+
" Constraints:",
|
|
117
|
+
" - --all is mutually exclusive with --status, --limit, and --cursor",
|
|
118
|
+
"",
|
|
119
|
+
"Show behavior:",
|
|
120
|
+
" Views:",
|
|
121
|
+
" - compact: task summary",
|
|
122
|
+
" - tree: hierarchy",
|
|
123
|
+
" - detail: descriptions",
|
|
124
|
+
" Machine default:",
|
|
125
|
+
" - With --all, machine modes default to detail",
|
|
126
|
+
"",
|
|
127
|
+
"Ready/Next behavior:",
|
|
128
|
+
" ready:",
|
|
129
|
+
" - Returns deterministic unblocked candidates",
|
|
130
|
+
" - Sort order: status, blockers, createdAt, id",
|
|
131
|
+
" - Options: --limit <n>, --epic <id>",
|
|
132
|
+
" next:",
|
|
133
|
+
" - Returns top ready candidate",
|
|
134
|
+
" - Option: --epic <id>",
|
|
135
|
+
"",
|
|
136
|
+
"Update behavior:",
|
|
137
|
+
" Bulk target flags:",
|
|
138
|
+
" --all | --ids <csv>",
|
|
139
|
+
" Bulk fields:",
|
|
140
|
+
" --append <text> and/or --status <status>",
|
|
141
|
+
].join("\n");
|
|
142
|
+
|
|
143
|
+
const SUBTASK_HELP = [
|
|
144
|
+
"Usage: trekoon subtask <create|list|update|delete> [options]",
|
|
145
|
+
"",
|
|
146
|
+
"List behavior:",
|
|
147
|
+
" Defaults:",
|
|
148
|
+
" - Open statuses only: in_progress, in-progress, todo",
|
|
149
|
+
" - Limit: 10",
|
|
150
|
+
" Flags:",
|
|
151
|
+
" --task <id> | --status <csv> | --limit <n> | --cursor <n> | --all | --view table|compact",
|
|
152
|
+
" Pagination:",
|
|
153
|
+
" - --cursor is offset-like",
|
|
154
|
+
" - Machine modes expose meta.pagination.hasMore / nextCursor",
|
|
155
|
+
" Constraints:",
|
|
156
|
+
" - --all is mutually exclusive with --status, --limit, and --cursor",
|
|
157
|
+
"",
|
|
158
|
+
"Update behavior:",
|
|
159
|
+
" Bulk target flags:",
|
|
160
|
+
" --all | --ids <csv>",
|
|
161
|
+
" Bulk fields:",
|
|
162
|
+
" --append <text> and/or --status <status>",
|
|
163
|
+
].join("\n");
|
|
164
|
+
|
|
165
|
+
const DEP_HELP = [
|
|
166
|
+
"Usage: trekoon dep <add|remove|list|reverse> [options]",
|
|
167
|
+
"",
|
|
168
|
+
"Subcommands:",
|
|
169
|
+
" add <source-id> <depends-on-id>",
|
|
170
|
+
" Create dependency edge: source depends on depends-on.",
|
|
171
|
+
" remove <source-id> <depends-on-id>",
|
|
172
|
+
" Remove one dependency edge if it exists.",
|
|
173
|
+
" list <source-id>",
|
|
174
|
+
" Show direct dependencies for a node.",
|
|
175
|
+
" reverse <target-id>",
|
|
176
|
+
" Show downstream nodes blocked by target (with distance).",
|
|
177
|
+
"",
|
|
178
|
+
"Examples:",
|
|
179
|
+
" trekoon dep add <task-a> <task-b>",
|
|
180
|
+
" trekoon dep remove <task-a> <task-b>",
|
|
181
|
+
" trekoon dep list <task-a>",
|
|
182
|
+
" trekoon dep reverse <task-b>",
|
|
183
|
+
].join("\n");
|
|
184
|
+
|
|
185
|
+
const EVENTS_HELP = [
|
|
186
|
+
"Usage: trekoon events prune [--dry-run] [--archive] [--retention-days <n>]",
|
|
187
|
+
"",
|
|
188
|
+
"Purpose:",
|
|
189
|
+
" Manage retention for internal sync event log rows.",
|
|
190
|
+
"",
|
|
191
|
+
"Options:",
|
|
192
|
+
" --dry-run Preview candidate/archive/delete counts only.",
|
|
193
|
+
" --archive Copy pruned rows to event_archive before delete.",
|
|
194
|
+
" --retention-days <n> Keep last n days (positive integer, default 90).",
|
|
195
|
+
"",
|
|
196
|
+
"Examples:",
|
|
197
|
+
" trekoon events prune --dry-run",
|
|
198
|
+
" trekoon events prune --retention-days 30",
|
|
199
|
+
" trekoon events prune --archive",
|
|
200
|
+
].join("\n");
|
|
201
|
+
|
|
202
|
+
const MIGRATE_HELP = [
|
|
203
|
+
"Usage: trekoon migrate <status|rollback> [--to-version <n>]",
|
|
204
|
+
"",
|
|
205
|
+
"Subcommands:",
|
|
206
|
+
" status",
|
|
207
|
+
" Show current schema version, latest version, and pending count.",
|
|
208
|
+
" rollback [--to-version <n>]",
|
|
209
|
+
" Roll back migrations; default target is one version back.",
|
|
210
|
+
"",
|
|
211
|
+
"Examples:",
|
|
212
|
+
" trekoon migrate status",
|
|
213
|
+
" trekoon migrate rollback",
|
|
214
|
+
" trekoon migrate rollback --to-version 1",
|
|
215
|
+
].join("\n");
|
|
216
|
+
|
|
217
|
+
const SYNC_HELP = [
|
|
218
|
+
"Usage: trekoon sync <status|pull|resolve|conflicts> [options]",
|
|
219
|
+
"",
|
|
220
|
+
"Subcommands:",
|
|
221
|
+
" status [--from <branch>]",
|
|
222
|
+
" Show ahead/behind counts and pending conflicts vs source branch (default: main).",
|
|
223
|
+
" pull --from <branch>",
|
|
224
|
+
" Pull and apply upstream tracker events from a source branch.",
|
|
225
|
+
" conflicts list [--mode pending|all]",
|
|
226
|
+
" List sync conflicts (default mode: pending).",
|
|
227
|
+
" conflicts show <conflict-id>",
|
|
228
|
+
" Show full details for one conflict.",
|
|
229
|
+
" resolve <conflict-id> --use ours|theirs",
|
|
230
|
+
" Resolve a pending conflict by selecting ours or theirs.",
|
|
231
|
+
"",
|
|
232
|
+
"Examples:",
|
|
233
|
+
" trekoon sync status",
|
|
234
|
+
" trekoon sync status --from main",
|
|
235
|
+
" trekoon sync pull --from main",
|
|
236
|
+
" trekoon sync conflicts list",
|
|
237
|
+
" trekoon sync conflicts list --mode all",
|
|
238
|
+
" trekoon sync conflicts show <conflict-id>",
|
|
239
|
+
" trekoon sync resolve <conflict-id> --use ours",
|
|
240
|
+
].join("\n");
|
|
241
|
+
|
|
242
|
+
const SKILLS_HELP = [
|
|
243
|
+
"Usage:",
|
|
244
|
+
" trekoon skills install [--link --editor opencode|claude|pi] [--to <path>] [--allow-outside-repo]",
|
|
245
|
+
" trekoon skills update",
|
|
246
|
+
"",
|
|
247
|
+
"Purpose:",
|
|
248
|
+
" Install or refresh the project-local Trekoon skill asset.",
|
|
249
|
+
"",
|
|
250
|
+
"Install behavior:",
|
|
251
|
+
" - Always installs canonical file to:",
|
|
252
|
+
" <cwd>/.agents/skills/trekoon/SKILL.md",
|
|
253
|
+
" - Use --link to also create an editor symlink named 'trekoon'.",
|
|
254
|
+
" - --editor is required when --link is used (opencode|claude|pi).",
|
|
255
|
+
" - --to overrides the symlink root for --link only.",
|
|
256
|
+
" - Without --allow-outside-repo, link targets must resolve inside repo.",
|
|
257
|
+
" - --allow-outside-repo requires --link and disables that boundary check.",
|
|
258
|
+
"",
|
|
259
|
+
"Update behavior:",
|
|
260
|
+
" - Refreshes canonical SKILL file in the install path above.",
|
|
261
|
+
" - Reports default link states for opencode/claude/pi.",
|
|
262
|
+
"",
|
|
263
|
+
"Examples:",
|
|
264
|
+
" trekoon skills install",
|
|
265
|
+
" trekoon skills install --link --editor opencode",
|
|
266
|
+
" trekoon skills install --link --editor claude --to .claude/skills",
|
|
267
|
+
" trekoon skills install --link --editor pi --to ../shared/skills --allow-outside-repo",
|
|
268
|
+
" trekoon skills update",
|
|
28
269
|
].join("\n");
|
|
29
270
|
|
|
30
271
|
const COMMAND_HELP: Record<string, string> = {
|
|
31
|
-
init:
|
|
32
|
-
quickstart:
|
|
33
|
-
wipe:
|
|
34
|
-
epic:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
migrate: "Usage: trekoon migrate <status|rollback> [--to-version <n>]",
|
|
43
|
-
sync: "Usage: trekoon sync <subcommand> [options]",
|
|
44
|
-
skills:
|
|
45
|
-
"Usage: trekoon skills install [--link --editor opencode|claude|pi] [--to <path>] [--allow-outside-repo] | trekoon skills update (--to sets symlink root for --link only; install path always <cwd>/.agents/skills/trekoon/SKILL.md; links must resolve inside repo unless --allow-outside-repo is set; update refreshes canonical SKILL and reports default link states)",
|
|
272
|
+
init: INIT_HELP,
|
|
273
|
+
quickstart: QUICKSTART_HELP,
|
|
274
|
+
wipe: WIPE_HELP,
|
|
275
|
+
epic: EPIC_HELP,
|
|
276
|
+
task: TASK_HELP,
|
|
277
|
+
subtask: SUBTASK_HELP,
|
|
278
|
+
dep: DEP_HELP,
|
|
279
|
+
events: EVENTS_HELP,
|
|
280
|
+
migrate: MIGRATE_HELP,
|
|
281
|
+
sync: SYNC_HELP,
|
|
282
|
+
skills: SKILLS_HELP,
|
|
46
283
|
help: "Usage: trekoon help [command] [--json|--toon]",
|
|
47
284
|
};
|
|
48
285
|
|
|
@@ -64,6 +301,7 @@ export async function runHelp(context: CliContext): Promise<CliResult> {
|
|
|
64
301
|
data: {
|
|
65
302
|
topic,
|
|
66
303
|
text,
|
|
304
|
+
version: CLI_VERSION,
|
|
67
305
|
},
|
|
68
306
|
});
|
|
69
307
|
}
|
|
@@ -4,34 +4,69 @@ import { type CliContext, type CliResult } from "../runtime/command-types";
|
|
|
4
4
|
const QUICKSTART_TEXT = [
|
|
5
5
|
"Trekoon quickstart",
|
|
6
6
|
"",
|
|
7
|
+
"This quickstart is aligned with .agents/skills/trekoon/SKILL.md.",
|
|
8
|
+
"For agents: always use --toon for every command.",
|
|
9
|
+
"",
|
|
7
10
|
"1) Local DB and worktree model",
|
|
8
11
|
"- Every worktree stores tracker state at .trekoon/trekoon.db.",
|
|
9
12
|
"- This DB stays local; it is not merged by Git automatically.",
|
|
10
13
|
"",
|
|
11
|
-
"2)
|
|
12
|
-
"-
|
|
13
|
-
"-
|
|
14
|
-
"-
|
|
15
|
-
"-
|
|
14
|
+
"2) Agent session-start checklist (verify setup + current state)",
|
|
15
|
+
"- 1. Verify tracker exists (or initialize once): trekoon --toon init",
|
|
16
|
+
"- 2. Check sync baseline: trekoon --toon sync status",
|
|
17
|
+
"- 3. Load active context: trekoon --toon epic list",
|
|
18
|
+
"- 4. Load active tasks: trekoon --toon task list",
|
|
19
|
+
"- 5. Load deterministic candidates: trekoon --toon task ready --limit 5",
|
|
20
|
+
"- 6. If blocked, inspect downstream impact: trekoon --toon dep reverse <task-or-subtask-id>",
|
|
21
|
+
"",
|
|
22
|
+
"3) AI execution loop (deterministic, dependency-aware)",
|
|
23
|
+
"- 1. Sync branch/worktree state: trekoon --toon sync status",
|
|
24
|
+
"- 2. Select ready work: trekoon --toon task ready --limit 5",
|
|
25
|
+
"- 3. Pick top candidate when needed: trekoon --toon task next",
|
|
26
|
+
"- 4. Check downstream blockers: trekoon --toon dep reverse <task-or-subtask-id>",
|
|
27
|
+
"- 5. Claim work and update status: trekoon --toon task update <task-id> --status in_progress",
|
|
28
|
+
"- 6. Complete with context: trekoon --toon task update <task-id> --append \"Completed implementation\" --status done",
|
|
29
|
+
"- 7. Or report block: trekoon --toon task update <task-id> --append \"Blocked by <reason>\" --status blocked",
|
|
16
30
|
"",
|
|
17
|
-
"
|
|
31
|
+
"4) Power-user command patterns (aligned with skill)",
|
|
32
|
+
"- Inspect full epic tree: trekoon --toon epic show <epic-id> --all",
|
|
33
|
+
"- Inspect full task payload: trekoon --toon task show <task-id> --all",
|
|
34
|
+
"- Check direct dependencies before starting: trekoon --toon dep list <task-id>",
|
|
35
|
+
"- Find what this item unblocks: trekoon --toon dep reverse <task-or-subtask-id>",
|
|
36
|
+
"- Filter list explicitly: trekoon --toon task list --status in_progress,todo --limit 20",
|
|
37
|
+
"- Paginate deterministically: trekoon --toon task list --cursor <n>",
|
|
38
|
+
"- Bulk append/status update: trekoon --toon task update --ids id1,id2 --append \"...\" --status in_progress",
|
|
39
|
+
"",
|
|
40
|
+
"5) Task details and description",
|
|
18
41
|
"- Human list and show views default to table format.",
|
|
19
42
|
"- Alternate list view: add --view compact.",
|
|
20
43
|
"- task/epic/subtask list defaults: open work only (in_progress/in-progress, todo), max 10.",
|
|
21
44
|
"- Filter list by status: --status in_progress,todo (CSV).",
|
|
22
45
|
"- Change page size: --limit <n>. Show all statuses and all rows with --all.",
|
|
23
|
-
"-
|
|
46
|
+
"- Continue pagination with --cursor <n> (offset-like list position).",
|
|
47
|
+
"- --all cannot be combined with --status, --limit, or --cursor.",
|
|
24
48
|
"- Bulk update: use --all or --ids <csv> with --append and/or --status.",
|
|
25
49
|
"- Bulk update rejects positional id, and --all/--ids cannot be combined.",
|
|
26
|
-
"-
|
|
27
|
-
"-
|
|
28
|
-
"
|
|
29
|
-
"",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"- trekoon
|
|
34
|
-
"- trekoon sync
|
|
50
|
+
"- Ready queue: trekoon task ready [--limit <n>] [--epic <id>] (deterministic order).",
|
|
51
|
+
"- Next execution candidate: trekoon task next [--epic <id>]",
|
|
52
|
+
"- Full tree + descriptions (canonical): trekoon --toon epic show <epic-id> --all",
|
|
53
|
+
"- Full task payload (including description): trekoon --toon task show <task-id> --all",
|
|
54
|
+
"- Optional integration format: trekoon --json task show <task-id> --all",
|
|
55
|
+
"",
|
|
56
|
+
"6) Pre-merge sync flow",
|
|
57
|
+
"- Run: trekoon --toon sync status",
|
|
58
|
+
"- Pull upstream tracker events: trekoon --toon sync pull --from main",
|
|
59
|
+
"- Resolve conflicts if needed: trekoon --toon sync resolve <id> --use ours",
|
|
60
|
+
"- Run sync status again before opening or merging a PR.",
|
|
61
|
+
"",
|
|
62
|
+
"7) Machine output examples",
|
|
63
|
+
"- trekoon --toon quickstart",
|
|
64
|
+
"- trekoon --toon task show <task-id> --all",
|
|
65
|
+
"- trekoon --toon epic show <epic-id> --all",
|
|
66
|
+
"- trekoon --toon sync status",
|
|
67
|
+
"- trekoon --toon task ready --limit 5",
|
|
68
|
+
"- trekoon --toon task next",
|
|
69
|
+
"- trekoon --toon dep reverse <task-or-subtask-id>",
|
|
35
70
|
].join("\n");
|
|
36
71
|
|
|
37
72
|
export async function runQuickstart(_: CliContext): Promise<CliResult> {
|
|
@@ -44,17 +79,46 @@ export async function runQuickstart(_: CliContext): Promise<CliResult> {
|
|
|
44
79
|
databaseFile: ".trekoon/trekoon.db",
|
|
45
80
|
mergeBehavior: "manual-sync",
|
|
46
81
|
},
|
|
82
|
+
alignedSkill: ".agents/skills/trekoon/SKILL.md",
|
|
83
|
+
requiresToonForAgents: true,
|
|
84
|
+
agentStartupChecklist: [
|
|
85
|
+
"trekoon --toon init",
|
|
86
|
+
"trekoon --toon sync status",
|
|
87
|
+
"trekoon --toon epic list",
|
|
88
|
+
"trekoon --toon task list",
|
|
89
|
+
"trekoon --toon task ready --limit 5",
|
|
90
|
+
"trekoon --toon dep reverse <task-or-subtask-id>",
|
|
91
|
+
],
|
|
47
92
|
preMergeFlow: [
|
|
48
|
-
"trekoon sync status",
|
|
49
|
-
"trekoon sync pull --from main",
|
|
50
|
-
"trekoon sync resolve <id> --use ours",
|
|
51
|
-
"trekoon sync status",
|
|
93
|
+
"trekoon --toon sync status",
|
|
94
|
+
"trekoon --toon sync pull --from main",
|
|
95
|
+
"trekoon --toon sync resolve <id> --use ours",
|
|
96
|
+
"trekoon --toon sync status",
|
|
97
|
+
],
|
|
98
|
+
executionLoop: [
|
|
99
|
+
"trekoon --toon sync status",
|
|
100
|
+
"trekoon --toon task ready --limit 5",
|
|
101
|
+
"trekoon --toon task next",
|
|
102
|
+
"trekoon --toon dep reverse <task-or-subtask-id>",
|
|
103
|
+
"trekoon --toon task update <task-id> --status in_progress",
|
|
104
|
+
],
|
|
105
|
+
powerUserCommands: [
|
|
106
|
+
"trekoon --toon epic show <epic-id> --all",
|
|
107
|
+
"trekoon --toon task show <task-id> --all",
|
|
108
|
+
"trekoon --toon dep list <task-id>",
|
|
109
|
+
"trekoon --toon dep reverse <task-or-subtask-id>",
|
|
110
|
+
"trekoon --toon task list --status in_progress,todo --limit 20",
|
|
111
|
+
"trekoon --toon task list --cursor <n>",
|
|
112
|
+
"trekoon --toon task update --ids id1,id2 --append \"...\" --status in_progress",
|
|
52
113
|
],
|
|
53
114
|
machineExamples: [
|
|
54
|
-
"trekoon quickstart
|
|
55
|
-
"trekoon task show <task-id> --all
|
|
56
|
-
"trekoon epic show <epic-id> --all
|
|
57
|
-
"trekoon sync status
|
|
115
|
+
"trekoon --toon quickstart",
|
|
116
|
+
"trekoon --toon task show <task-id> --all",
|
|
117
|
+
"trekoon --toon epic show <epic-id> --all",
|
|
118
|
+
"trekoon --toon sync status",
|
|
119
|
+
"trekoon --toon task ready --limit 5",
|
|
120
|
+
"trekoon --toon task next",
|
|
121
|
+
"trekoon --toon dep reverse <task-or-subtask-id>",
|
|
58
122
|
],
|
|
59
123
|
},
|
|
60
124
|
});
|
package/src/commands/subtask.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
hasFlag,
|
|
3
|
+
parseArgs,
|
|
4
|
+
parseStrictNonNegativeInt,
|
|
5
|
+
parseStrictPositiveInt,
|
|
6
|
+
readEnumOption,
|
|
7
|
+
readMissingOptionValue,
|
|
8
|
+
readOption,
|
|
9
|
+
} from "./arg-parser";
|
|
2
10
|
|
|
3
11
|
import { MutationService } from "../domain/mutation-service";
|
|
4
12
|
import { TrackerDomain } from "../domain/tracker-domain";
|
|
@@ -54,16 +62,44 @@ function filterSortAndLimitSubtasks(
|
|
|
54
62
|
subtasks: readonly SubtaskRecord[],
|
|
55
63
|
statuses: readonly string[] | undefined,
|
|
56
64
|
limit: number | undefined,
|
|
57
|
-
|
|
65
|
+
cursor: number,
|
|
66
|
+
): { subtasks: SubtaskRecord[]; pagination: { hasMore: boolean; nextCursor: string | null } } {
|
|
58
67
|
const allowedStatuses = statuses === undefined ? undefined : new Set(statuses);
|
|
59
68
|
const filtered = allowedStatuses === undefined ? [...subtasks] : subtasks.filter((subtask) => allowedStatuses.has(subtask.status));
|
|
60
|
-
const sorted = [...filtered].sort((left, right) =>
|
|
69
|
+
const sorted = [...filtered].sort((left, right) => {
|
|
70
|
+
const byStatus = subtaskStatusPriority(left.status) - subtaskStatusPriority(right.status);
|
|
71
|
+
if (byStatus !== 0) {
|
|
72
|
+
return byStatus;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const byCreatedAt = left.createdAt - right.createdAt;
|
|
76
|
+
if (byCreatedAt !== 0) {
|
|
77
|
+
return byCreatedAt;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return left.id.localeCompare(right.id);
|
|
81
|
+
});
|
|
61
82
|
|
|
62
83
|
if (limit === undefined) {
|
|
63
|
-
return
|
|
84
|
+
return {
|
|
85
|
+
subtasks: sorted,
|
|
86
|
+
pagination: {
|
|
87
|
+
hasMore: false,
|
|
88
|
+
nextCursor: null,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
|
|
93
|
+
const pagedSubtasks = sorted.slice(cursor, cursor + limit);
|
|
94
|
+
const nextIndex = cursor + pagedSubtasks.length;
|
|
95
|
+
const hasMore = nextIndex < sorted.length;
|
|
96
|
+
return {
|
|
97
|
+
subtasks: pagedSubtasks,
|
|
98
|
+
pagination: {
|
|
99
|
+
hasMore,
|
|
100
|
+
nextCursor: hasMore ? `${nextIndex}` : null,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
67
103
|
}
|
|
68
104
|
|
|
69
105
|
function appendLine(existing: string, line: string): string {
|
|
@@ -161,6 +197,7 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
161
197
|
readMissingOptionValue(parsed.missingOptionValues, "view") ??
|
|
162
198
|
readMissingOptionValue(parsed.missingOptionValues, "status", "s") ??
|
|
163
199
|
readMissingOptionValue(parsed.missingOptionValues, "limit", "l") ??
|
|
200
|
+
readMissingOptionValue(parsed.missingOptionValues, "cursor") ??
|
|
164
201
|
readMissingOptionValue(parsed.missingOptionValues, "task", "t");
|
|
165
202
|
if (missingListOption !== undefined) {
|
|
166
203
|
return failMissingOptionValue("subtask.list", missingListOption);
|
|
@@ -171,6 +208,7 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
171
208
|
const includeAll = hasFlag(parsed.flags, "all");
|
|
172
209
|
const rawStatuses = readOption(parsed.options, "status", "s");
|
|
173
210
|
const rawLimit = readOption(parsed.options, "limit", "l");
|
|
211
|
+
const rawCursor = readOption(parsed.options, "cursor");
|
|
174
212
|
|
|
175
213
|
if (rawView !== undefined && view === undefined) {
|
|
176
214
|
return failResult({
|
|
@@ -208,6 +246,18 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
208
246
|
});
|
|
209
247
|
}
|
|
210
248
|
|
|
249
|
+
if (includeAll && rawCursor !== undefined) {
|
|
250
|
+
return failResult({
|
|
251
|
+
command: "subtask.list",
|
|
252
|
+
human: "Use either --all or --cursor, not both.",
|
|
253
|
+
data: { code: "invalid_input", flags: ["all", "cursor"] },
|
|
254
|
+
error: {
|
|
255
|
+
code: "invalid_input",
|
|
256
|
+
message: "--all and --cursor are mutually exclusive",
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
211
261
|
const statuses = parseStatusCsv(rawStatuses);
|
|
212
262
|
if (rawStatuses !== undefined && statuses !== undefined && statuses.length === 0) {
|
|
213
263
|
return failResult({
|
|
@@ -234,6 +284,19 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
234
284
|
});
|
|
235
285
|
}
|
|
236
286
|
|
|
287
|
+
const parsedCursor = parseStrictNonNegativeInt(rawCursor);
|
|
288
|
+
if (Number.isNaN(parsedCursor)) {
|
|
289
|
+
return failResult({
|
|
290
|
+
command: "subtask.list",
|
|
291
|
+
human: "Invalid --cursor value. Use an integer >= 0.",
|
|
292
|
+
data: { code: "invalid_input", cursor: rawCursor },
|
|
293
|
+
error: {
|
|
294
|
+
code: "invalid_input",
|
|
295
|
+
message: "Invalid --cursor value",
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
237
300
|
const taskId: string | undefined = readOption(parsed.options, "task", "t") ?? parsed.positional[1];
|
|
238
301
|
const selectedStatuses = includeAll
|
|
239
302
|
? undefined
|
|
@@ -241,7 +304,13 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
241
304
|
const selectedLimit = includeAll
|
|
242
305
|
? undefined
|
|
243
306
|
: parsedLimit ?? DEFAULT_SUBTASK_LIST_LIMIT;
|
|
244
|
-
const
|
|
307
|
+
const listed = filterSortAndLimitSubtasks(
|
|
308
|
+
domain.listSubtasks(taskId),
|
|
309
|
+
selectedStatuses,
|
|
310
|
+
selectedLimit,
|
|
311
|
+
parsedCursor ?? 0,
|
|
312
|
+
);
|
|
313
|
+
const subtasks = listed.subtasks;
|
|
245
314
|
const listView = view ?? "table";
|
|
246
315
|
const human =
|
|
247
316
|
subtasks.length === 0
|
|
@@ -254,6 +323,7 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
|
|
|
254
323
|
command: "subtask.list",
|
|
255
324
|
human,
|
|
256
325
|
data: { subtasks },
|
|
326
|
+
...(context.mode === "human" ? {} : { meta: { pagination: listed.pagination } }),
|
|
257
327
|
});
|
|
258
328
|
}
|
|
259
329
|
case "update": {
|