trekoon 0.3.2 → 0.3.4

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.
@@ -1,11 +1,11 @@
1
1
  # Machine contracts
2
2
 
3
- Use `--toon` for production agent loops. Use `--json` only when an integration
4
- explicitly requires JSON.
3
+ Use `--toon` for agent loops. Use `--json` only when an integration explicitly
4
+ requires JSON.
5
5
 
6
6
  ## Base envelope
7
7
 
8
- All machine responses use the same top-level shape:
8
+ Every machine response uses the same top-level shape:
9
9
 
10
10
  ```text
11
11
  ok: true|false
@@ -16,23 +16,20 @@ metadata:
16
16
  requestId: req-<stable-id>
17
17
  ```
18
18
 
19
- Most subcommand identifiers are dot-namespaced, such as `task.list` or
20
- `sync.pull`. Root-level commands may use single-token IDs such as `help`,
21
- `init`, `quickstart`, `wipe`, or `version`.
19
+ Most subcommand IDs are dot-namespaced (`task.list`, `sync.pull`). Root-level
20
+ commands use single tokens (`help`, `init`, `quickstart`, `wipe`, `version`).
22
21
 
23
- Additional metadata may appear when relevant:
22
+ Additional metadata appears when relevant:
24
23
 
25
24
  - `metadata.compatibility` when `--compat` mode is active
26
25
  - `meta.storageRootDiagnostics` when storage resolves from a non-canonical cwd
27
26
 
28
- ## Ready queue contract
27
+ ## Ready queue
29
28
 
30
29
  ```bash
31
30
  trekoon --toon task ready --limit 3
32
31
  ```
33
32
 
34
- Payload fields:
35
-
36
33
  ```text
37
34
  ok: true
38
35
  command: task.ready
@@ -58,8 +55,6 @@ data:
58
55
  trekoon --toon dep reverse <task-or-subtask-id>
59
56
  ```
60
57
 
61
- Payload fields:
62
-
63
58
  ```text
64
59
  ok: true
65
60
  command: dep.reverse
@@ -69,22 +64,19 @@ data:
69
64
  blockedNodes[]: { id, kind, distance, isDirect }
70
65
  ```
71
66
 
72
- ## Pagination contract for list calls
67
+ ## Pagination
73
68
 
74
69
  ```bash
75
70
  trekoon --toon task list --status todo --limit 2
76
71
  trekoon --toon task list --status todo --limit 2 --cursor 2
77
72
  ```
78
73
 
79
- Cursor rules:
74
+ Rules:
80
75
 
81
76
  - `--cursor <n>` is offset-like pagination for `epic list`, `task list`, and
82
77
  `subtask list`
83
- - do not combine `--all` with `--cursor`
84
- - machine consumers should page using `meta.pagination.hasMore` and
85
- `meta.pagination.nextCursor`
86
-
87
- Payload fields:
78
+ - Don't combine `--all` with `--cursor`
79
+ - Page using `meta.pagination.hasMore` and `meta.pagination.nextCursor`
88
80
 
89
81
  ```text
90
82
  ok: true
@@ -98,14 +90,14 @@ meta:
98
90
  pagination: { hasMore, nextCursor }
99
91
  ```
100
92
 
101
- ## Descendant cascade update contract
93
+ ## Descendant cascade update
102
94
 
103
95
  ```bash
104
96
  trekoon --toon epic update <epic-id> --all --status done
105
97
  trekoon --toon task update <task-id> --all --status todo
106
98
  ```
107
99
 
108
- Success payload fields for epic/task cascade mode:
100
+ Success:
109
101
 
110
102
  ```text
111
103
  ok: true
@@ -129,7 +121,7 @@ data:
129
121
  changedSubtasks
130
122
  ```
131
123
 
132
- Failure contract for blocked epic/task cascade mode:
124
+ Failure (blocked descendants):
133
125
 
134
126
  ```text
135
127
  ok: false
@@ -157,16 +149,13 @@ data:
157
149
 
158
150
  Notes:
159
151
 
160
- - `subtask update <subtask-id> --all --status done|todo` is accepted, but it
161
- returns the normal single-subtask `subtask.update` payload because there are
162
- no descendants to traverse
163
- - Cascade mode is reserved for status-only close/reopen operations; combine
164
- append/title/description changes in separate commands
152
+ - `subtask update <subtask-id> --all --status done|todo` is accepted but
153
+ returns the normal single-subtask payload (no descendants to traverse)
154
+ - Cascade is status-only; use separate commands for append/title/description
165
155
 
166
- ## Batch create and expand payloads
156
+ ## Batch create and expand
167
157
 
168
- Trekoon uses stable batch payloads for one-shot graph creation and sibling batch
169
- creation commands.
158
+ Stable batch payloads for one-shot graph creation and sibling batch commands.
170
159
 
171
160
  ### `epic create` and `epic expand`
172
161
 
@@ -175,8 +164,6 @@ trekoon --toon epic create --title "..." --description "..." --task "..."
175
164
  trekoon --toon epic expand <epic-id> --task "..."
176
165
  ```
177
166
 
178
- Payload fields:
179
-
180
167
  ```text
181
168
  ok: true
182
169
  command: epic.create | epic.expand
@@ -197,8 +184,6 @@ data:
197
184
  trekoon --toon task create-many --epic <epic-id> --task "..."
198
185
  ```
199
186
 
200
- Payload fields:
201
-
202
187
  ```text
203
188
  ok: true
204
189
  command: task.create-many
@@ -215,8 +200,6 @@ data:
215
200
  trekoon --toon subtask create-many --task <task-id> --subtask "..."
216
201
  ```
217
202
 
218
- Payload fields:
219
-
220
203
  ```text
221
204
  ok: true
222
205
  command: subtask.create-many
@@ -229,57 +212,54 @@ data:
229
212
 
230
213
  ## Sync compatibility mode
231
214
 
232
- Compatibility mode exists for integrations that still consume legacy sync
233
- command IDs:
215
+ For integrations that still use legacy sync command IDs:
234
216
 
235
217
  ```bash
236
218
  trekoon --json --compat legacy-sync-command-ids sync status
237
219
  trekoon --toon --compat legacy-sync-command-ids sync pull --from main
238
220
  ```
239
221
 
240
- Behavior:
222
+ - Default output uses canonical dotted IDs (`sync.status`)
223
+ - Compat mode rewrites to legacy forms (`sync_status`)
224
+ - Machine-only, valid only for `sync` commands
225
+ - Output includes `metadata.compatibility` with migration guidance and removal
226
+ timing
241
227
 
242
- - default output uses canonical dotted IDs such as `sync.status`
243
- - compatibility mode rewrites sync command IDs to legacy forms such as
244
- `sync_status`
245
- - compatibility mode is machine-only and valid only for `sync` commands
246
- - machine output includes `metadata.compatibility` with migration guidance and
247
- removal timing
248
-
249
- ## Compact envelope mode
228
+ ## Compact envelope
250
229
 
251
230
  ```bash
252
231
  trekoon --toon --compact task list
253
232
  ```
254
233
 
255
- When `--compact` is passed, the `metadata` key is omitted from the TOON/JSON
256
- envelope. The `ok`, `command`, `data`, `error`, and `meta` keys are unaffected.
234
+ `--compact` omits the `metadata` key from the envelope. `ok`, `command`, `data`,
235
+ `error`, and `meta` are unaffected.
257
236
 
258
- ## Status transition error contract
237
+ ## Status transition errors
259
238
 
260
- Invalid status transitions return:
239
+ Invalid transitions return:
261
240
 
262
241
  ```text
263
242
  ok: false
264
243
  error:
265
244
  code: status_transition_invalid
266
245
  message: "cannot transition <kind> <id> from '<from>' to '<to>'"
267
- details:
268
- entity: epic|task|subtask
269
- id: <entity-id>
270
- fromStatus: <current-status>
271
- toStatus: <attempted-status>
272
- allowedTransitions[]: <valid targets from current status>
246
+ data:
247
+ entity: epic|task|subtask
248
+ id: <entity-id>
249
+ fromStatus: <current-status>
250
+ toStatus: <attempted-status>
251
+ allowedTransitions[]: <valid targets from current status>
273
252
  ```
274
253
 
275
- ## Epic progress contract
254
+ Transition details are in `data`, not `error.details`. `error` only has `code`
255
+ and `message`.
256
+
257
+ ## Epic progress
276
258
 
277
259
  ```bash
278
260
  trekoon --toon epic progress <epic-id>
279
261
  ```
280
262
 
281
- Payload fields:
282
-
283
263
  ```text
284
264
  ok: true
285
265
  command: epic.progress
@@ -295,14 +275,12 @@ data:
295
275
  nextCandidate: { id, title } | null
296
276
  ```
297
277
 
298
- ## Task done enhanced contract
278
+ ## Task done (enhanced)
299
279
 
300
280
  ```bash
301
281
  trekoon --toon task done <task-id>
302
282
  ```
303
283
 
304
- Payload fields:
305
-
306
284
  ```text
307
285
  ok: true
308
286
  command: task.done
@@ -324,14 +302,12 @@ data:
324
302
  blockedCount
325
303
  ```
326
304
 
327
- ## Suggest command contract
305
+ ## Suggest
328
306
 
329
307
  ```bash
330
308
  trekoon --toon suggest [--epic <epic-id>]
331
309
  ```
332
310
 
333
- Payload fields:
334
-
335
311
  ```text
336
312
  ok: true
337
313
  command: suggest
@@ -352,9 +328,9 @@ data:
352
328
  pendingConflicts
353
329
  ```
354
330
 
355
- ## Owner field in update payloads
331
+ ## Owner field in updates
356
332
 
357
- Task and subtask update payloads now include `owner` in their event data:
333
+ Task and subtask update payloads include `owner`:
358
334
 
359
335
  ```text
360
336
  data:
@@ -366,6 +342,29 @@ data:
366
342
  The board API accepts `owner` on `PATCH /api/tasks/{id}` and
367
343
  `PATCH /api/subtasks/{id}`.
368
344
 
345
+ ## Sync resolve dry-run
346
+
347
+ ```bash
348
+ trekoon --toon sync resolve <conflict-id> --use ours|theirs --dry-run
349
+ ```
350
+
351
+ ```text
352
+ ok: true
353
+ command: sync.resolve
354
+ data:
355
+ conflictId: <conflict-id>
356
+ resolution: ours|theirs
357
+ entityKind: epic|task|subtask
358
+ entityId: <entity-id>
359
+ fieldName: <conflicted field>
360
+ oursValue: <current DB value>
361
+ theirsValue: <source branch value>
362
+ wouldWrite: <value that would be written>
363
+ dryRun: true
364
+ ```
365
+
366
+ No mutation occurs. The conflict stays pending.
367
+
369
368
  ## Related docs
370
369
 
371
370
  - [Quickstart](quickstart.md)
@@ -1,21 +1,19 @@
1
1
  # Quickstart
2
2
 
3
- Use this guide for the shortest path from an empty repo to an active Trekoon
4
- workflow.
3
+ Shortest path from zero to a working Trekoon workflow.
5
4
 
6
- ## Understand the storage model first
5
+ ## How storage works
7
6
 
8
- Trekoon is local-first, but inside git repos and worktrees it is **repo-shared**.
9
- Every worktree for the same repository resolves to one shared `.trekoon`
10
- directory and one shared `.trekoon/trekoon.db` database.
7
+ Trekoon keeps one SQLite database per repository at `.trekoon/trekoon.db`. In
8
+ worktree setups, all worktrees share the same database because storage resolves
9
+ from the repository root.
11
10
 
12
- - `worktreeRoot` identifies the current checkout.
13
- - `sharedStorageRoot` identifies the repository root that owns `.trekoon`.
14
- - `databaseFile` points at the shared SQLite database.
15
- - `.trekoon` stays gitignored because the DB is operational state, not source
16
- code.
11
+ Key points:
17
12
 
18
- Outside git repos, Trekoon falls back to the current working directory.
13
+ - `.trekoon` is gitignored. It's operational state, not source code.
14
+ - Outside git repos, Trekoon falls back to the current working directory.
15
+ - `worktreeRoot` is your checkout. `sharedStorageRoot` is the repo root that
16
+ owns `.trekoon`.
19
17
 
20
18
  ## Initialize
21
19
 
@@ -24,93 +22,30 @@ trekoon init
24
22
  trekoon --version
25
23
  ```
26
24
 
27
- If an agent is driving the workflow, use the machine-readable form:
25
+ If an agent is driving the workflow:
28
26
 
29
27
  ```bash
30
28
  trekoon --toon init
31
29
  trekoon --toon sync status
32
30
  ```
33
31
 
34
- Bootstrap rules:
32
+ Run `init` once per repository. It creates the shared storage root and installs
33
+ the board runtime under `.trekoon/board`. If `sync status` reports
34
+ `recoveryRequired` or a tracked/ignored mismatch, fix the setup before
35
+ continuing.
35
36
 
36
- - Run `trekoon --toon init` once per repository to create or re-bootstrap the
37
- shared storage root and install the bundled board runtime under
38
- `.trekoon/board`.
39
- - Run `trekoon --toon sync status` before agent work to inspect diagnostics.
40
- - If diagnostics report `recoveryRequired`, a tracked or ignored mismatch, or an
41
- ambiguous recovery path, stop and repair setup before continuing.
42
-
43
- ## Open the local board
44
-
45
- After `trekoon init`, you can browse and update the same repo-shared Trekoon
46
- state in the browser:
37
+ ## Open the board
47
38
 
48
39
  ```bash
49
40
  trekoon board open
50
- trekoon board update
51
41
  ```
52
42
 
53
- For day-to-day use, treat `trekoon board open` as the one-command entry point.
54
- It both verifies the runtime files and launches the board. Reach for
55
- `trekoon board update` only when you need to refresh the copied runtime without
56
- opening the browser.
57
-
58
- What these commands do:
59
-
60
- - `trekoon board open` ensures bundled board assets are installed, starts a
61
- loopback-only server on `127.0.0.1`, opens the browser, and prints a fallback
62
- URL you can paste manually if launch fails
63
- - `trekoon board update` refreshes the runtime assets in `.trekoon/board`
64
- without opening a browser or starting the server
65
-
66
- Why the board uses a local server instead of a bare HTML file:
67
-
68
- - the UI needs live reads and writes against Trekoon data, not a static export
69
- - the server binds only to `127.0.0.1`
70
- - each `board open` call generates a per-session token used by the browser/API
71
- - bundled assets are copied from the CLI package into `.trekoon/board`, so the
72
- board works without a separate frontend install or local build step
73
-
74
- Current runtime expectations for operators:
75
-
76
- - the served HTML, styles, and board app files come from the local
77
- `.trekoon/board` runtime directory
78
- - all assets are self-hosted: the board ships its own CSS, fonts (Inter,
79
- Material Symbols), and vanilla JS with no framework or CDN dependencies
80
- - the board works fully offline once the runtime assets are copied into
81
- `.trekoon/board`
82
-
83
- Current layout behavior:
84
-
85
- - the topbar is a compact navbar with workspace identity, Epics and Board
86
- navigation, debounced search, theme toggle, and workspace info
87
- - the board toggles between an epics overview and a task workspace view; task
88
- detail opens as a modal overlay
89
- - responsive breakpoints adjust kanban column counts and component spacing so
90
- the board remains navigable on narrower widths
91
- - the page scrolls naturally as one document; modal overlays lock body scroll
92
- while open
93
- - task cards show truncated descriptions; clicking a card opens the task detail
94
- modal with the full description and edit controls
95
- - search filters client-side across titles, descriptions, statuses, and subtask
96
- content with a 180ms debounce
97
-
98
- Verification checklist for operators:
99
-
100
- 1. Open the board with `trekoon board open` and confirm the first view is the
101
- epic overview with all epics listed and scrollable.
102
- 2. Click an epic card and confirm you enter the board workspace with the topbar
103
- showing the active epic context.
104
- 3. Type in search and verify results filter as you type; confirm focus stays in
105
- the search input while typing.
106
- 4. Click a task card and confirm the task detail modal opens with the full
107
- description, edit form, and subtask list.
108
- 5. On desktop width, confirm the kanban board shows multiple columns.
109
- 6. Resize to a narrow viewport and confirm columns reflow cleanly without
110
- horizontal overflow.
111
- 7. Close modals and confirm you return to the previous board context.
112
-
113
- ## Create an epic, task, and subtask
43
+ Starts a loopback-only server on `127.0.0.1`, opens the browser, and prints a
44
+ fallback URL. The board is a self-hosted single-page app with no CDN
45
+ dependencies, so it works offline once initialized. Use `trekoon board update`
46
+ if you just need to refresh the runtime assets without opening the browser.
47
+
48
+ ## Create work
114
49
 
115
50
  ```bash
116
51
  trekoon epic create --title "Agent backlog stabilization" --description "Track stabilization work" --status todo
@@ -118,7 +53,7 @@ trekoon task create --title "Implement sync status" --description "Add status re
118
53
  trekoon subtask create --task <task-id> --title "Add cursor model" --status todo
119
54
  ```
120
55
 
121
- Useful follow-up reads:
56
+ Browse results:
122
57
 
123
58
  ```bash
124
59
  trekoon task list
@@ -127,10 +62,9 @@ trekoon task list --limit 25
127
62
  trekoon task list --all --view compact
128
63
  ```
129
64
 
130
- ## Prefer one-shot planning when the graph is already known
65
+ ## One-shot planning
131
66
 
132
- If you already know the epic tree, create the epic, tasks, subtasks, and
133
- dependencies in one call:
67
+ If you already know the full epic tree, create everything in one call:
134
68
 
135
69
  ```bash
136
70
  trekoon epic create \
@@ -143,84 +77,57 @@ trekoon epic create \
143
77
  --dep "@sub-a|@task-a"
144
78
  ```
145
79
 
146
- Use this when:
147
-
148
- - the epic does not exist yet
149
- - later records need to reference earlier records with `@temp-key`
150
- - you want one atomic create step and one machine response with mappings and
151
- counts
80
+ This is better than sequential creates because later records can reference
81
+ earlier ones with `@temp-key`, and you get one atomic operation with mappings
82
+ and counts in the response.
152
83
 
153
- ## Add dependencies
84
+ ## Dependencies
154
85
 
155
86
  ```bash
156
87
  trekoon dep add <task-id> <depends-on-id>
157
88
  trekoon dep list <task-id>
158
89
  ```
159
90
 
160
- ## Use batch commands for larger updates
91
+ ## Batch commands
161
92
 
162
- When one call needs to create or link multiple records, prefer the transactional
163
- batch commands:
93
+ For larger updates, use batch commands instead of looping:
164
94
 
165
95
  | Need | Command |
166
96
  | --- | --- |
167
- | Create multiple tasks under one epic | `trekoon task create-many --epic <epic-id> --task ...` |
168
- | Create multiple subtasks under one task | `trekoon subtask create-many <task-id> --subtask ...` |
169
- | Add multiple dependency edges | `trekoon dep add-many --dep ...` |
170
- | Expand an existing epic with linked records | `trekoon epic expand <epic-id> ...` |
97
+ | Multiple tasks under one epic | `trekoon task create-many --epic <epic-id> --task ...` |
98
+ | Multiple subtasks under one task | `trekoon subtask create-many <task-id> --subtask ...` |
99
+ | Multiple dependency edges | `trekoon dep add-many --dep ...` |
100
+ | Expand an existing epic | `trekoon epic expand <epic-id> ...` |
171
101
 
172
- These commands validate the whole batch before applying changes, so a bad input
173
- fails the whole operation instead of leaving partial state behind.
102
+ These validate the whole batch before applying, so a bad input fails the entire
103
+ operation instead of leaving partial state.
174
104
 
175
- ## Close or reopen a whole tree in one update
176
-
177
- When you need to manually finish or reopen an entire epic or task tree, use the
178
- positional-ID cascade form of `update --all`:
105
+ ## Close or reopen a whole tree
179
106
 
180
107
  ```bash
181
108
  trekoon epic update <epic-id> --all --status done
182
- trekoon epic update <epic-id> --all --status todo
183
109
  trekoon task update <task-id> --all --status done
184
- trekoon task update <task-id> --all --status todo
185
- trekoon subtask update <subtask-id> --all --status done
186
110
  ```
187
111
 
188
- Rules:
189
-
190
- - `epic update <id> --all --status done|todo` updates the epic and all
191
- descendants atomically
192
- - `task update <id> --all --status done|todo` updates the task and all
193
- descendant subtasks atomically
194
- - `subtask update <id> --all --status done|todo` is accepted for consistency,
195
- but it only updates that one subtask
196
- - Positional-ID cascade mode is status-only; do not combine it with `--append`,
197
- `--description`, `--title`, or `--ids`
198
- - If any epic/task descendant is blocked by an unresolved external dependency,
199
- the whole cascade fails with no partial writes
112
+ Cascades atomically through all descendants. If any descendant has an unresolved
113
+ external dependency, the whole update fails with no partial writes. Works with
114
+ `--status done` and `--status todo` only.
200
115
 
201
- ## Check progress and get suggestions
202
-
203
- After creating work, use `epic progress` to see status counts and the next ready
204
- candidate:
116
+ ## Check progress
205
117
 
206
118
  ```bash
207
119
  trekoon epic progress <epic-id>
208
- ```
209
-
210
- Use `suggest` for priority-ranked next-action recommendations:
211
-
212
- ```bash
213
120
  trekoon suggest
214
121
  trekoon suggest --epic <epic-id>
215
122
  ```
216
123
 
217
- ## Status machine
124
+ `epic progress` returns task status counts and the next ready candidate.
125
+ `suggest` gives priority-ranked next-action recommendations.
218
126
 
219
- Trekoon enforces valid status transitions. The canonical statuses are `todo`,
220
- `in_progress`, `done`, and `blocked`. Direct jumps like `todo → done` are
221
- rejected — use `task done` which auto-transitions through `in_progress`.
127
+ ## Status machine
222
128
 
223
- Valid transitions:
129
+ Trekoon enforces status transitions. The statuses are `todo`, `in_progress`,
130
+ `done`, and `blocked`.
224
131
 
225
132
  | From | Allowed targets |
226
133
  | --- | --- |
@@ -229,8 +136,45 @@ Valid transitions:
229
136
  | `blocked` | `in_progress`, `todo` |
230
137
  | `done` | `in_progress` |
231
138
 
139
+ Direct jumps like `todo` to `done` are rejected. Use `task done` instead, which
140
+ auto-transitions through `in_progress`.
141
+
142
+ ## Install the AI skill
143
+
144
+ ```bash
145
+ trekoon skills install # repo-local (default)
146
+ trekoon skills install -g # global (~/.agents/skills/trekoon)
147
+ trekoon skills install --link --editor claude # repo-local + editor symlink
148
+ ```
149
+
150
+ After upgrading Trekoon, refresh installed skills:
151
+
152
+ ```bash
153
+ trekoon update # alias for: trekoon skills update
154
+ ```
155
+
156
+ For agent integration details, see [AI agents and the Trekoon skill](ai-agents.md).
157
+
158
+ ## Pre-merge sync
159
+
160
+ Before opening or merging a PR:
161
+
162
+ ```bash
163
+ trekoon --toon sync status
164
+ trekoon --toon sync pull --from main
165
+ trekoon --toon sync conflicts list
166
+ trekoon --toon sync conflicts show <id>
167
+ trekoon --toon sync resolve <id> --use theirs --dry-run
168
+ trekoon --toon sync resolve <id> --use ours|theirs
169
+ trekoon --toon sync status
170
+ ```
171
+
172
+ Always run `sync conflicts show` before resolving so you know what you're
173
+ overwriting. In human mode, `--use theirs` prompts for confirmation with a
174
+ 30-second timeout.
175
+
232
176
  ## What to read next
233
177
 
234
- - [Command reference](commands.md)
235
- - [AI agents and the Trekoon skill](ai-agents.md)
236
- - [Machine contracts](machine-contracts.md)
178
+ - [Command reference](commands.md) for flags, defaults, and behavior
179
+ - [AI agents and the Trekoon skill](ai-agents.md) for agent integration
180
+ - [Machine contracts](machine-contracts.md) for structured output schemas
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trekoon",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "AI-first local issue tracker CLI.",
5
5
  "keywords": [
6
6
  "ai",
@@ -45,7 +45,7 @@
45
45
  "lint": "bunx tsc --noEmit"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/bun": "^1.3.9",
48
+ "@types/bun": "^1.3.11",
49
49
  "typescript": "^5.9.3"
50
50
  },
51
51
  "dependencies": {
@@ -143,7 +143,7 @@ export function normalizeSnapshot(rawSnapshot) {
143
143
  id: epicId,
144
144
  title: String(epic.title ?? "Untitled epic"),
145
145
  description: String(epic.description ?? "").replace(/\\n/g, "\n"),
146
- status: String(epic.status ?? "todo"),
146
+ status: normalizeStatus(String(epic.status ?? "todo")),
147
147
  createdAt: Number(epic.createdAt ?? Date.now()),
148
148
  updatedAt: Number(epic.updatedAt ?? epic.createdAt ?? Date.now()),
149
149
  taskIds: epicTasks.map((task) => task.id),
@@ -42,6 +42,7 @@ export interface ParsedCompactFields {
42
42
  }
43
43
 
44
44
  const LONG_PREFIX = "--";
45
+ const SHORT_FLAG_PATTERN = /^-([A-Za-z])$/u;
45
46
 
46
47
  export function parseArgs(args: readonly string[]): ParsedArgs {
47
48
  const positional: string[] = [];
@@ -57,6 +58,15 @@ export function parseArgs(args: readonly string[]): ParsedArgs {
57
58
  continue;
58
59
  }
59
60
 
61
+ // Short flag: single dash + single letter (e.g. -g).
62
+ const shortMatch = SHORT_FLAG_PATTERN.exec(token);
63
+ if (shortMatch) {
64
+ const key: string = shortMatch[1]!;
65
+ flags.add(key);
66
+ providedOptions.push(key);
67
+ continue;
68
+ }
69
+
60
70
  if (!token.startsWith(LONG_PREFIX)) {
61
71
  positional.push(token);
62
72
  continue;