@pmelab/gtd 1.4.3

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +800 -0
  3. package/dist/gtd.bundle.mjs +388717 -0
  4. package/package.json +82 -0
package/README.md ADDED
@@ -0,0 +1,800 @@
1
+ # gi[t]hings.**done**
2
+
3
+ > [!WARNING] This project is an experiment in unapologetic vibe coding. Code
4
+ > might be terrible, I don't even know 🤷‍♂️ But otherwise I wouldn't have built it
5
+ > in the first place. Now I have something that actually helps me.
6
+
7
+ A git-aware CLI that emits the next prompt for an autonomous coding agent based
8
+ on the current repository state — capture an idea, grill it into a plan,
9
+ decompose it into work packages, execute with parallel subagents, test,
10
+ agentically review each package, and finally walk a human through a review.
11
+
12
+ Internally, gtd is a **pure fold** over git history. The decision core
13
+ (`src/Machine.ts`) is a single IO-free function, `resolve(events)` — **no
14
+ xstate, no actor, no Effect**. The Effect "edge" (`src/Events.ts`) does all the
15
+ git/filesystem IO: it reads the **first-parent** commit subjects since the
16
+ merge-base with the default branch (whole-history fallback when there is no
17
+ default branch, when HEAD equals the merge-base, or when there is no merge-base
18
+ — i.e. budgets engage on the default branch too) plus the working tree, turns
19
+ them into a `COMMIT[]` + single terminal `RESOLVE` event stream, and folds them
20
+ through the machine. The fold lands on exactly **one** of 17 states, which
21
+ selects the prompt. A single run resolves to a single state.
22
+
23
+ `resolve()` returns that state plus an optional **`EdgeAction`** (a commit,
24
+ revert, test run, or file write). The driver loop (`src/main.ts`) performs the
25
+ action, then re-gathers and re-resolves — auto-advancing through the
26
+ deterministic chain **within one invocation** until it reaches a prompt-bearing
27
+ or STOP state. The agent never runs `git commit` itself: every agent leaves its
28
+ output **uncommitted**, and the edge commits it with the right flat
29
+ `gtd: <phase>` subject on the next hop. The machine stays pure — it only decides
30
+ _which_ action; the semantics live in the edge.
31
+
32
+ `gtd` is an npm CLI — install it, run `gtd` in a repo, and it prints the next
33
+ prompt to stdout; a human or any agent reads and follows it.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ npm install -g @pmelab/gtd
39
+ ```
40
+
41
+ Or run without installing:
42
+
43
+ ```bash
44
+ npx @pmelab/gtd
45
+ ```
46
+
47
+ No config file, no setup subcommand.
48
+
49
+ ## Usage
50
+
51
+ Run `gtd` from your repository's working directory — it prints the next prompt
52
+ to stdout. It takes **no ref argument** — the review base is always
53
+ auto-computed.
54
+
55
+ ## JSON output mode
56
+
57
+ Pass `--json` to the default `gtd` invocation to receive machine-readable output
58
+ instead of a plain prompt:
59
+
60
+ ```bash
61
+ gtd --json
62
+ ```
63
+
64
+ `--json` applies only to the default command. Passing it to `gtd format` is
65
+ rejected with exit code 1 and an error on stderr. It is orthogonal to all other
66
+ flags (`--verbose`, `--debug`, etc.) — each controls exactly one concern.
67
+
68
+ ### Output shape
69
+
70
+ In `--json` mode gtd emits a **single-line JSON object** to stdout:
71
+
72
+ ```json
73
+ { "state": "building", "autoAdvance": true, "prompt": "..." }
74
+ ```
75
+
76
+ - **`state`** — the resolved prompt-bearing `GtdState` (e.g. `"grilling"`,
77
+ `"building"`, `"fixing"`, `"clean"`).
78
+ - **`autoAdvance`** — the same boolean that selects the loop-tail in plain mode.
79
+ `true` means the workflow advances automatically after the agent acts; `false`
80
+ means a STOP state was reached and human input is expected.
81
+ - **`prompt`** — the full markdown prompt, but with **both loop-control tails
82
+ omitted**. In their place, the prompt ends with:
83
+ `Complete the steps above, then end your turn — the harness decides what happens next.`
84
+ The caller is responsible for reading `autoAdvance` and deciding whether to
85
+ run another cycle.
86
+
87
+ ### Loop-ownership division of labor
88
+
89
+ In **plain mode** the in-prompt tails own the loop — the prompt instructs the
90
+ agent to re-run `gtd` when `autoAdvance` is true.
91
+
92
+ In **`--json` mode** the **caller owns the loop** — the tails are stripped and
93
+ the caller reads `autoAdvance` from the JSON object to decide whether to
94
+ iterate.
95
+
96
+ Example driver script:
97
+
98
+ ```bash
99
+ #!/usr/bin/env bash
100
+ set -euo pipefail
101
+
102
+ while true; do
103
+ out="$(gtd --json)"
104
+ prompt="$(jq -r .prompt <<<"$out")"
105
+ claude -p "$prompt" --dangerously-skip-permissions
106
+ jq -e .autoAdvance <<<"$out" >/dev/null || break
107
+ done
108
+ ```
109
+
110
+ ### Error behavior
111
+
112
+ Errors are reported **inside** the JSON object rather than as unstructured text:
113
+
114
+ ```json
115
+ { "state": "error", "autoAdvance": false, "prompt": "<message>" }
116
+ ```
117
+
118
+ The process still exits with code 1. Exit codes are otherwise unchanged: 0 on
119
+ success, 1 on error.
120
+
121
+ ## Steering files
122
+
123
+ `gtd` writes and commits temporary steering files that carry workflow state
124
+ across runs:
125
+
126
+ - **TODO.md** — the current plan, under development during grilling.
127
+ - **REVIEW.md** — a guided human review spanning a commit diff. Format:
128
+ - `# Review: <short-hash>` heading + `<!-- base: <full-hash> -->` marker
129
+ identifying the review base commit
130
+ - Per-hunk `- [ ]` checkboxes: ticking them (`- [ ]` → `- [x]`) is the
131
+ **approval signal** — checkbox-only edits route to Done; _unchecked_ boxes
132
+ never gate the workflow
133
+ - Open questions at the top, resolved/addressed items at the bottom
134
+ (consistent with TODO.md grilling convention)
135
+ - **FEEDBACK.md** — test-failure output, **or** agentic-review findings, to be
136
+ fixed. An **empty** FEEDBACK.md from a clean agentic review signals
137
+ **approval** (→ Close package).
138
+ - **ERRORS.md** — the escalation gate: persistent test-failure output that stops
139
+ the loop for a human (written instead of FEEDBACK.md once the fix-attempt cap
140
+ is hit; never auto-consumed).
141
+ - **.gtd/** — ordered work packages (one numbered directory each) of
142
+ parallelizable subtasks.
143
+
144
+ Steering files are **authoritative**: while any exist, `gtd` resumes that
145
+ workflow regardless of the last commit (even a non-gtd one). They are **never
146
+ garbage-collected automatically** — a stale steering file from an abandoned
147
+ branch is resumed exactly like a live one, so you must `rm` files from a
148
+ workflow you have abandoned.
149
+
150
+ "**Code changes**" below means pending working-tree changes (tracked or
151
+ untracked, respecting `.gitignore`) **outside** the steering set. Changes to
152
+ steering files are detected separately.
153
+
154
+ ## Detection model
155
+
156
+ Every run derives the state in **three layers**:
157
+
158
+ 1. **Transport pre-pass** — if HEAD is `gtd: transport`, short-circuit to the
159
+ Transport state (mixed-reset) before anything else is considered.
160
+ 2. **Steering-file precedence** — the presence of `ERRORS.md` / `FEEDBACK.md` /
161
+ `.gtd/` / `REVIEW.md` drives the decision, authoritative regardless of HEAD.
162
+ 3. **HEAD bucket** — with no steering files in play, the last-commit bucket plus
163
+ working-tree cleanliness selects New Feature / Grilling / Clean / Idle.
164
+
165
+ Within layers 2 and 3 the HEAD subject further disambiguates states the
166
+ filesystem alone cannot separate (e.g. inside the `.gtd/` lifecycle, HEAD
167
+ `gtd: planning` vs `gtd: building` vs `gtd: package done`).
168
+
169
+ ### Commit taxonomy
170
+
171
+ `gtd` writes a single **flat** `gtd: <phase>` subject for every workflow commit.
172
+ The complete set:
173
+
174
+ `gtd: new task` · `gtd: grilling` · `gtd: grilled` · `gtd: planning` ·
175
+ `gtd: building` · `gtd: errors` · `gtd: feedback` · `gtd: fixing` ·
176
+ `gtd: package done` · `gtd: awaiting review` · `gtd: done` — plus the hand-made
177
+ `gtd: transport` (see below).
178
+
179
+ The last commit subject is bucketed two ways:
180
+
181
+ - **Boundary** — a non-`gtd:` commit, or exactly `gtd: done`. Marks a cold
182
+ start: no workflow in progress.
183
+ - **Mid-phase** — any other `gtd: <phase>` subject. Identifies the exact phase
184
+ of an in-progress workflow.
185
+
186
+ ### Precedence ladder (first match wins)
187
+
188
+ 0. **HEAD `gtd: transport`** → Transport.
189
+ 1. **ERRORS.md present** → Escalate (human gate; STOP).
190
+ 2. **FEEDBACK.md present** → non-empty → Fixing; **empty** (clean agentic review
191
+ = approval) → Close package.
192
+ 3. **.gtd present** → build lifecycle, routed by tree + HEAD:
193
+ - `.gtd` modified (package files added/edited) → Planning
194
+ - code changes present → Testing
195
+ - clean tree + HEAD `gtd: fixing` (no-op fixer) → Testing (re-test)
196
+ - else clean, by HEAD: `gtd: planning` / `gtd: package done` → Building;
197
+ `gtd: building` → Agentic Review (or Close package, if force-approved)
198
+ 4. **REVIEW.md present** → review lifecycle, routed by committed-ness + tree:
199
+ committed + clean → Done; committed + checkbox-only edits (only `[ ]`↔`[x]`
200
+ flips in REVIEW.md) → Done; committed + non-checkbox pending edits → Accept
201
+ Review; uncommitted → Await Review (commits REVIEW.md and auto-advances to
202
+ Done). 4a. **HEAD `gtd: done` + `squash` enabled + squash base present + no
203
+ unrelated code dirty** (a lone untracked `SQUASH_MSG.md` is allowed) →
204
+ Squashing; unrelated code dirty → New Feature.
205
+ 5. **Boundary HEAD + pending changes** (and no `.gtd`/REVIEW/FEEDBACK), or HEAD
206
+ `gtd: new task` + clean tree (regenerate a lost seed) → New Feature.
207
+ 6. **TODO.md present** → Grilling / Grilled.
208
+ 7. **Boundary or `gtd: package done` HEAD + clean tree** → Clean (review the
209
+ work) or Idle (nothing to review).
210
+
211
+ Anything matching no rule is corruption — `gtd` **hard-errors** rather than
212
+ guess.
213
+
214
+ ```mermaid
215
+ flowchart TD
216
+ Start([Run gtd]) --> P0{"HEAD = gtd: transport?"}
217
+ P0 -->|yes| Transport["Transport — mixed-reset HEAD, re-derive"]:::edge
218
+ Transport -.->|re-resolve| Start
219
+ P0 -->|no| P1{"ERRORS.md?"}
220
+ P1 -->|yes| Escalate["Escalate — STOP, human gate"]:::gate
221
+ P1 -->|no| P2{"FEEDBACK.md?"}
222
+ P2 -->|"empty = approval"| Close["Close package — rm pkg dir, gtd: package done"]:::edge
223
+ P2 -->|"non-empty"| Fixing["Fixing — rm FEEDBACK, fixer agent"]:::agent
224
+ P2 -->|absent| P3{".gtd/?"}
225
+ P3 -->|"modified"| Planning["Planning — gtd: planning"]:::agent
226
+ P3 -->|"code dirty / resume / no-op fixer"| Testing["Testing — gtd: building, run tests"]:::edge
227
+ P3 -->|"clean, HEAD planning/package done"| Building["Building — pick & build one package"]:::agent
228
+ P3 -->|"clean, HEAD building"| Review["Agentic Review — write FEEDBACK.md"]:::agent
229
+ P3 -->|absent| P4{"REVIEW.md?"}
230
+ P4 -->|"committed + clean or checkbox-only edits"| Done["Done — rm REVIEW, gtd: done"]:::edge
231
+ P4 -->|"committed + non-checkbox edits"| Accept["Accept Review — seed TODO, checkout, rm REVIEW"]:::edge
232
+ P4 -->|"uncommitted"| Await["Await Review — commit gtd: awaiting review"]:::edge
233
+ Await -.->|"re-resolve"| Done
234
+ P4 -->|absent| P5{"boundary HEAD + dirty,<br/>or gtd: new task + clean?"}
235
+ Done -->|"squash enabled"| Squashing["Squashing — reset --soft base, squash commit"]:::agent
236
+ Done -->|"squash disabled"| Idle
237
+ Squashing --> Idle["Idle — nothing to do (STOP; review on next manual gtd run)"]:::gate
238
+ P5 -->|yes| NewFeature["New Feature — gtd: new task, revert, seed TODO"]:::edge
239
+ P5 -->|no| P6{"TODO.md?"}
240
+ P6 -->|"open markers"| GrillStop["Grilling — gtd: grilling, STOP for answers"]:::gate
241
+ P6 -->|"dirty, no markers"| GrillIter["Grilling — gtd: grilling, agent iterates"]:::agent
242
+ P6 -->|"clean, no markers"| Grilled["Grilled — gtd: grilled, decompose"]:::agent
243
+ P6 -->|absent| P7{"clean + boundary/package-done HEAD,<br/>reviewable diff?"}
244
+ P7 -->|yes| CleanState["Clean — write REVIEW.md"]:::agent
245
+ P7 -->|no| Idle
246
+ classDef edge fill:#1a4a6b,color:#fff
247
+ classDef agent fill:#2d6a4f,color:#fff
248
+ classDef gate fill:#7a3b1d,color:#fff
249
+ ```
250
+
251
+ > Blue = **edge-only** (the edge performs IO; no prompt rendered). Green =
252
+ > **agent** (a prompt is emitted; the agent acts, then re-runs gtd). Brown =
253
+ > **gate** (STOP for the human, or nothing to do).
254
+
255
+ ### Illegal combinations
256
+
257
+ These never arise in normal flow; if seen, `gtd` hard-errors rather than
258
+ guessing:
259
+
260
+ - REVIEW.md + .gtd
261
+ - REVIEW.md + TODO.md
262
+ - FEEDBACK.md + REVIEW.md
263
+ - FEEDBACK.md without .gtd
264
+ - ERRORS.md + FEEDBACK.md
265
+ - ERRORS.md without .gtd
266
+
267
+ Legal coexistence: `.gtd` + TODO.md (plan kept alongside packages during
268
+ **Planning** only — TODO.md is deleted at the first Building turn);
269
+ FEEDBACK.md + `.gtd` (a fix during build).
270
+
271
+ ### Single writer, linear branch
272
+
273
+ State is folded from **first-parent** history: gtd assumes a **single writer on
274
+ a linear branch**. A merge commit at HEAD is unsupported — it breaks the counter
275
+ folds, the review base, and last-commit detection (documented, not handled).
276
+
277
+ Distribute work by **sequential handoff** (one active machine at a time) over
278
+ **rebase / fast-forward**, not by merging parallel branches. The primitive for
279
+ carrying _uncommitted_ work across machines or branches is `gtd: transport`:
280
+
281
+ ```bash
282
+ git add -A && git commit -m "gtd: transport" # on the source machine
283
+ git push # … then pull on the far side
284
+ ```
285
+
286
+ There is **no `gtd transport` subcommand** — you make this commit by hand. The
287
+ **Transport** state consumes it: on the far side, the next `gtd` run sees the
288
+ `gtd: transport` HEAD, mixed-resets it (`git reset HEAD~1`) to drop the work
289
+ back into the working tree uncommitted, and re-derives state from scratch. If
290
+ the transport commit is the repository's root commit (no parent), `gtd` fails
291
+ immediately with a clear error instead of looping.
292
+
293
+ ## The 17 states
294
+
295
+ Each state has a **condition** (when it wins), a deterministic **action**, the
296
+ **commit(s)** it produces, and where it **advances**. States marked
297
+ **auto-advance** re-run `gtd` themselves; **STOP** states hand control to a
298
+ human; **edge-only** states render no prompt at all — the driver performs their
299
+ action and re-resolves silently.
300
+
301
+ | State | Kind | Wins when | Action & commit | Advances to |
302
+ | ------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
303
+ | **Transport** | edge-only, auto | HEAD `gtd: transport` (hand-made handoff commit) | mixed-reset HEAD (`git reset HEAD~1`), keep work in tree; **no commit** | re-derive from the restored tree |
304
+ | **Escalate** | STOP | ERRORS.md present | none | held until the human deletes ERRORS.md |
305
+ | **Fixing** | agent, auto | non-empty FEEDBACK.md present | inline FEEDBACK into the prompt, remove FEEDBACK.md; commit its removal `gtd: fixing` (FEEDBACK was committed by Testing) or `gtd: feedback` (uncommitted, written by Agentic Review) | fixer edits → Testing |
306
+ | **Close package** | edge-only, auto | empty FEEDBACK.md present (clean review); also reached from Agentic Review force-approve | rm FEEDBACK.md, rm the first (finished) package dir (+ the now-empty `.gtd/`); commit `gtd: package done` | more packages → Building; `.gtd` gone → Clean |
307
+ | **Planning** | agent, auto | `.gtd` present **and modified**; HEAD `gtd: grilled` or `gtd: planning` | commit the `.gtd/` changes `gtd: planning` | continue decomposing, else → Building |
308
+ | **Testing** | edge-only, auto | `.gtd` present, no FEEDBACK/ERRORS, and a reason to test: code changes, a pending ERRORS.md deletion (human resume), or a clean tree under HEAD `gtd: fixing` (no-op fixer) | commit pending tree `gtd: building`, run `testCommand`; green → proceed; red → write FEEDBACK (below cap) or ERRORS (at cap), commit `gtd: errors`; if captured output is empty/whitespace, a sentinel string is written so the file is never empty (empty FEEDBACK remains reserved for agentic-review approval) | green → Agentic Review; FEEDBACK → Fixing; ERRORS → Escalate |
309
+ | **Building** | agent, auto | `.gtd` present and clean, clean tree; HEAD `gtd: planning` or `gtd: package done` | if HEAD `gtd: planning` and TODO.md present, delete TODO.md and commit (prefix unchanged, fires once); select the first package, inline its tasks; agent leaves work **uncommitted** | Testing |
310
+ | **Agentic Review** | agent, auto | `.gtd` present and clean, clean tree; HEAD `gtd: building` | reviewer writes FEEDBACK.md (empty = approval), uncommitted — **unless** force-approved (kill-switch off or review-fix threshold hit), which routes straight to Close package | empty FEEDBACK → Close package; non-empty → Fixing |
311
+ | **Done** | edge-only, auto | REVIEW.md committed + clean tree, **or** committed + checkbox-only edits (only `- [ ]`→`- [x]` flips in REVIEW.md = approval) | rm REVIEW.md, commit `gtd: done` | Squashing (if enabled) or Idle |
312
+ | **Squashing** | agent, auto | no steering files, HEAD `gtd: done`, `squash` enabled, squash base present, no unrelated code dirty (a lone untracked `SQUASH_MSG.md` is allowed) | agent authors a conventional-commits message from the full `<base>..HEAD` diff, then runs `git reset --soft <base>` + `git commit` — collapses all intermediate `gtd: *` commits (including any interleaved non-gtd commits) into one; **gtd then STOPs** — post-squash review fires only on the next manual `gtd` run | Idle (STOP) |
313
+ | **Accept Review** | edge-only, auto | REVIEW.md committed + pending **non-checkbox** edits (human annotated REVIEW.md with comments / edited code) | seed TODO.md from the changeset, `git checkout` to discard the code edits, rm REVIEW.md; **all uncommitted** | Grilling |
314
+ | **Await Review** | edge-only, auto | REVIEW.md present and **uncommitted** (freshly written by Clean) | commit REVIEW.md `gtd: awaiting review` | Done (auto, same run) |
315
+ | **New Feature** | edge-only, auto | boundary HEAD + pending changes (code and/or a new uncommitted TODO.md), **or** HEAD `gtd: new task` + clean tree (lost-seed regen) | commit the raw input verbatim `gtd: new task` (unless already there), `git revert --no-commit` it back to a clean baseline, seed TODO.md from that diff — revert + seed left **uncommitted** | Grilling |
316
+ | **Grilling** | agent (iterate) / STOP (answers) | TODO.md present, not New Feature | commit pending edits `gtd: grilling`. Open-question markers present → STOP for the human to answer inline; no markers but dirty → grilling agent iterates | converge (no markers, clean tree) → Grilled |
317
+ | **Grilled** | agent, auto | TODO.md present, no markers, clean tree | commit pending `gtd: grilled` | decompose into `.gtd/` → Planning |
318
+ | **Clean** | agent | no steering files, clean tree, boundary or `gtd: package done` HEAD, and the review base yields a **non-empty** diff | compute the review base (four rules — see below); agent writes REVIEW.md **uncommitted** with `# Review: <short-hash>` heading, `<!-- base: <full-hash> -->` marker, and per-hunk `- [ ]` checkboxes (ticking them signals approval → Done) | Await Review |
319
+ | **Idle** | STOP | no steering files, clean tree, and nothing to review (HEAD `gtd: done` with `squash` disabled or after Squashing, or no reviewable diff) | none | — |
320
+
321
+ Every prompt also embeds the current `git diff HEAD` (untracked files included)
322
+ inline, plus the last commit subject and working-tree status, so the agent has
323
+ full context.
324
+
325
+ ### Review base — four rules
326
+
327
+ The review base (the commit whose diff to HEAD forms the REVIEW.md) is chosen by
328
+ four rules evaluated in priority order:
329
+
330
+ 1. **Within a process, first review** — a `gtd: grilling` commit exists after
331
+ the last `gtd: done` (or task start), but no `gtd: awaiting review` yet →
332
+ base = first `gtd: grilling` of the current task cycle; `refDiff` spans the
333
+ whole task.
334
+ 2. **Within a process, incremental** — `gtd: awaiting review` also present in
335
+ the current cycle (takes precedence over rule 1) → base = last
336
+ `gtd: awaiting review`; `refDiff` spans only the post-review changes.
337
+ 3. **Outside a process, feature branch** — no `gtd: grilling` after the last
338
+ `gtd: done`, and HEAD is not on the default branch → base = merge-base with
339
+ the default branch; `refDiff` spans the whole branch.
340
+ 4. **Outside a process, default branch** — no process active and HEAD is on the
341
+ default branch → skip review; `reviewBase`/`refDiff` unset → Idle.
342
+
343
+ In all cases, if the diff from the chosen base to HEAD is empty,
344
+ `reviewBase`/`refDiff` are left unset and the machine settles in Idle.
345
+
346
+ ## The fix loops & counter folds
347
+
348
+ Two derived counters drive the budgeted loops. Both are **folded in the
349
+ machine** from flags on the `COMMIT[]` stream — never recomputed at the edge:
350
+
351
+ - **`testFixCount`** — `gtd: errors` commits (test-fix attempts) since the
352
+ **most recent of** {a package start (`gtd: planning` / `gtd: package done`), a
353
+ `gtd: feedback` (start of a review-fix), or a commit that **removed
354
+ ERRORS.md** (a human resume)}. So each test-fix sub-loop, each review-fix
355
+ round, and every human resume starts a **fresh budget**.
356
+ - **`reviewFixCount`** — `gtd: feedback` commits (review-fix rounds) since the
357
+ most recent package start.
358
+
359
+ ### Test-fix loop (`fixAttemptCap`, default 3)
360
+
361
+ When Testing's run is red, it writes the captured output and commits
362
+ `gtd: errors`, incrementing `testFixCount`. If the captured output is empty or
363
+ whitespace-only (e.g. a command that exits non-zero with no output), a sentinel
364
+ string is written instead — so FEEDBACK/ERRORS is never empty. Empty FEEDBACK
365
+ remains reserved exclusively for Agentic Review's deliberate approval signal.
366
+
367
+ ```
368
+ Building → Testing(red) → Fixing → Testing(red) → … → Testing(green)
369
+ │ │
370
+ └── below cap: FEEDBACK.md, gtd: errors ───┘
371
+ └── at/over the cap: ERRORS.md, gtd: errors → Escalate
372
+ ```
373
+
374
+ Below the cap, the failure goes to **FEEDBACK.md** and Fixing applies a fix. At
375
+ or over the cap (`testFixCount >= fixAttemptCap`), it goes to **ERRORS.md**
376
+ instead and the loop **stops** at Escalate. The human investigates, then deletes
377
+ ERRORS.md — which **resets the fix-attempt budget** (the next run re-tests and
378
+ grants a fresh `cap` attempts before escalating again). While ERRORS.md exists,
379
+ every run resolves straight back to Escalate.
380
+
381
+ ### Review-fix loop & agentic review (`reviewThreshold`, default 3)
382
+
383
+ After a green test run, **Agentic Review** reviews the package's accumulated
384
+ diff against its task specs and **always writes FEEDBACK.md**:
385
+
386
+ ```
387
+ Testing(green) → Agentic Review → empty FEEDBACK → Close package → next package
388
+
389
+ └─ findings → Fixing(gtd: feedback) → Testing → Agentic Review → …
390
+ ```
391
+
392
+ An **empty FEEDBACK.md is approval** — Close package removes the finished
393
+ package directory and commits `gtd: package done`. Findings route to Fixing
394
+ (committed `gtd: feedback`, incrementing `reviewFixCount`), which loops back
395
+ through the test gate and re-reviews. Once `reviewFixCount >= reviewThreshold`,
396
+ Agentic Review **force-approves** (skips the review, closes the package
397
+ directly) so a package can never review-loop forever. Setting
398
+ **`agenticReview: false`** is a kill-switch: every package force-approves
399
+ immediately and the branch proceeds straight to human review.
400
+
401
+ ### Per-package close
402
+
403
+ Close package operates on **one** package at a time: it deletes the first
404
+ (finished) numbered directory under `.gtd/` — plus the now-empty `.gtd/` itself
405
+ if it was the last — and commits `gtd: package done`, which sends Building to
406
+ the next package (or Clean once `.gtd/` is gone). Each package thus runs the
407
+ full
408
+ `Building → Testing → Agentic Review → (Fixing → Testing → Agentic Review)* → Close`
409
+ loop before the next one starts.
410
+
411
+ ## A typical feature
412
+
413
+ 1. **Capture.** Leave a sketch in `TODO.md` (or just some pending code changes),
414
+ then run `gtd`. **New Feature** commits the raw input `gtd: new task`,
415
+ reverts it back to a clean baseline, and seeds an uncommitted `TODO.md` from
416
+ the diff.
417
+ 2. **Grill.** Run `gtd` — the **Grilling** agent (planning model) develops the
418
+ plan, appends open questions each marked with a `<!-- user answers here -->`
419
+ line, and leaves `TODO.md` uncommitted; the edge commits `gtd: grilling`.
420
+ While any marker is present, gtd **STOPs** for you.
421
+ 3. **Answer.** Open `TODO.md`, replace each `<!-- user answers here -->` with
422
+ your answer, and run `gtd` again. The agent integrates answers, moves them to
423
+ `## Resolved`, and raises fresh questions — repeat until none remain (it
424
+ writes `no open questions — run gtd to plan` with no markers).
425
+ 4. **Converge.** A clean tree with no markers resolves to **Grilled**
426
+ (`gtd: grilled`), then **Planning** decomposes `TODO.md` into ordered `.gtd/`
427
+ work packages (`gtd: planning`).
428
+ 5. **Build.** Run `gtd` — **Building** first deletes `TODO.md` (when HEAD is
429
+ `gtd: planning` and it is still present, committed under the same
430
+ `gtd: planning` prefix — fires once). It then names the single next package
431
+ and inlines its task files; the agent spawns one parallel subagent per task
432
+ (execution model + TDD) and leaves the work **uncommitted**. The next run is
433
+ **Testing**: the edge commits `gtd: building`, then runs `testCommand`.
434
+ 6. **Review each package.** On green, **Agentic Review** writes FEEDBACK.md.
435
+ Empty → **Close package** (`gtd: package done`) and on to the next package;
436
+ findings → **Fixing** → back through the test gate. A red test run drives the
437
+ test-fix loop until green or Escalate.
438
+ 7. **Human review.** When `.gtd/` is gone, **Clean** writes a `REVIEW.md` for
439
+ the diff since the review base (uncommitted); **Await Review** (edge-only)
440
+ commits it `gtd: awaiting review` and auto-advances to Done in the same run.
441
+ 8. **Approve or revise.** Re-run `gtd` with **no** changes to approve → **Done**
442
+ (`gtd: done`) → **Squashing** → **Idle**. The Squashing agent authors a
443
+ conventional-commits message from the full process diff and squashes all
444
+ intermediate `gtd: *` commits into one with `git reset --soft <base>` +
445
+ `git commit`, then **gtd STOPs**. Post-squash review does not fire
446
+ automatically — it fires only on the next manual `gtd` run (when the squash
447
+ commit is the boundary HEAD and a reviewable diff exists). Squashing fires
448
+ when the tree has no unrelated code dirty — a lone untracked `SQUASH_MSG.md`
449
+ is tolerated and deleted before the squash commit. If unrelated code is dirty
450
+ at `gtd: done`, gtd routes to **New Feature** instead. Set `squash: false` in
451
+ `.gtdrc` to skip squashing and go straight to Idle. Checking off REVIEW.md
452
+ checkboxes (`- [ ]` → `- [x]`) also counts as approval and routes to **Done**
453
+ — they are navigation aids, not feedback. Only **non-checkbox** edits (code
454
+ changes, inline comments, textual annotations in REVIEW.md) trigger **Accept
455
+ Review**, which seeds a fresh `TODO.md` from your feedback, discards your
456
+ code edits, removes `REVIEW.md`, and re-enters Grilling — the loop starts
457
+ over.
458
+
459
+ ## Configuration
460
+
461
+ gtd reads an optional `.gtdrc` config file via
462
+ [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig). With no config, the
463
+ built-in defaults apply. Supported filenames (searched in this order):
464
+
465
+ - `.gtdrc`
466
+ - `.gtdrc.json`
467
+ - `.gtdrc.yaml`
468
+ - `.gtdrc.yml`
469
+ - `gtd.config.json`
470
+ - `gtd.config.yaml`
471
+
472
+ ### Schema
473
+
474
+ - **`testCommand`** (string, default `npm run test`) — the command the edge runs
475
+ in the Testing state to verify a built package.
476
+ - **`fixAttemptCap`** (non-negative integer, default `3`) — the test-fix budget:
477
+ how many `gtd: errors` attempts are allowed per sub-loop before the failure is
478
+ escalated to ERRORS.md (Escalate). `0` disables the cap (escalates immediately
479
+ on the first red run).
480
+ - **`reviewThreshold`** (integer ≥ 1, default `3`) — the review-fix budget: how
481
+ many `gtd: feedback` rounds are allowed per package before Agentic Review
482
+ force-approves.
483
+ - **`agenticReview`** (boolean, default `true`) — kill-switch for the
484
+ per-package Agentic Review gate. Set to `false` to skip agentic review
485
+ entirely; every package force-approves and the branch proceeds directly to
486
+ human review.
487
+ - **`squash`** (boolean, default `true`) — after `gtd: done`, collapse all
488
+ intermediate `gtd: *` commits into a single conventional-commits commit via
489
+ `git reset --soft <base>` + `git commit`. Set `false` to keep the granular
490
+ history.
491
+ - **`models`** — model selection for the subagent-spawning states:
492
+ - `planning` — high-reasoning tier (default `claude-opus-4-8`), used by
493
+ `decompose`, `grilling`, `agentic-review`, and `clean`.
494
+ - `execution` — everyday tier (default `claude-sonnet-4-8`), used by
495
+ `building` and `fixing`.
496
+ - `states.*` — per-state overrides keyed by the six agent states: `decompose`
497
+ (shared by the Grilled and Planning states), `grilling`, `building`,
498
+ `fixing`, `agentic-review`, `clean`. Unknown `states` keys are **rejected**.
499
+
500
+ ### Validation and errors
501
+
502
+ If a config file fails to load or is invalid, gtd **exits with code 1** and
503
+ writes a human-readable error to **stderr** (never stdout):
504
+
505
+ - **Parse errors** (malformed YAML/JSON) — message includes the offending
506
+ filename, e.g. `gtd: /path/to/.gtdrc: unexpected token`.
507
+ - **Non-object top-level** — a YAML list or `null` at the root is rejected with
508
+ the filename in the message.
509
+ - **Schema violations** — unknown keys or out-of-range values emit
510
+ `Invalid gtd config: <field>: <reason>`. The message is concise and does not
511
+ dump the full type tree.
512
+ - **Missing test binary** — if `testCommand` names an executable that cannot be
513
+ found (`ENOENT`), gtd exits with code 1 and writes
514
+ `gtd: test command not found: <command>` to **stderr**. No stack trace is
515
+ emitted to stdout. A non-zero test exit is _not_ an error — it drives the
516
+ normal red-path (FEEDBACK → Fixing).
517
+
518
+ ### Lookup and precedence
519
+
520
+ gtd walks from the current working directory **up to your home directory** (or
521
+ to the filesystem root when cwd is outside home), collecting every `.gtdrc` it
522
+ finds along the way. All found levels are **deep-merged**, with the **innermost
523
+ (cwd) config winning** on conflicts.
524
+
525
+ This makes the worktree-parent case easy: drop a single `.gtdrc` in a shared
526
+ parent directory and it cascades to **all** checkouts/worktrees beneath it,
527
+ while any individual checkout can still override settings with its own `.gtdrc`.
528
+
529
+ ### Example
530
+
531
+ ```yaml
532
+ # .gtdrc.yaml
533
+ testCommand: pnpm test
534
+ fixAttemptCap: 3
535
+ reviewThreshold: 3
536
+ agenticReview: true
537
+ squash: true
538
+ models:
539
+ planning: claude-opus-4-8
540
+ execution: claude-sonnet-4-8
541
+ states:
542
+ decompose: claude-opus-4-8
543
+ building: claude-sonnet-4-8
544
+ ```
545
+
546
+ ## Build orchestration
547
+
548
+ When a plan is finalized, gtd enters build mode.
549
+
550
+ ### Decompose
551
+
552
+ The Grilled / Planning states spawn a planning-model subagent that breaks
553
+ `TODO.md` into executable work packages under `.gtd/`:
554
+
555
+ ```
556
+ .gtd/
557
+ 01-auth-module/
558
+ 01-define-types.md
559
+ 02-implement-login.md
560
+ 02-api-endpoints/
561
+ 01-create-routes.md
562
+ 02-add-middleware.md
563
+ ```
564
+
565
+ Rules:
566
+
567
+ - **Packages are sequential, in ordinal dependency order** — `01-`, `02-`, …;
568
+ the set is frozen once written. Package 02 cannot start until 01 is complete.
569
+ - **Each package is green on its own** — the test suite runs after every
570
+ package, so none may leave the tree red for a later package to fix.
571
+ - **Tasks within a package are parallel and file-disjoint** — one subagent per
572
+ task, no isolation; tasks that would touch the same file are merged into one.
573
+ - **Vertical slices, not horizontal** — each package is a thin, end-to-end
574
+ slice; prefer many thin packages over a "set up infrastructure" package.
575
+ - **Task files are self-contained** — description, acceptance-criteria
576
+ checkboxes, relevant file paths, constraints, and edge cases.
577
+
578
+ Packages carry only their task `.md` files; the edge commits each built package
579
+ `gtd: building`.
580
+
581
+ ### Execute
582
+
583
+ Execution is **one package per cycle**. gtd selects the single next package
584
+ itself, names it in the prompt, and inlines its task files' full contents — the
585
+ prompt is self-contained, so the agent never browses `.gtd/` or picks a package.
586
+ A single cycle:
587
+
588
+ 1. Spawn parallel execution-model workers for all tasks in the selected package
589
+ (with the `tdd` skill).
590
+ 2. If a worker fails (crash/timeout, not a test failure): ask the user to
591
+ retry/skip/abort.
592
+ 3. Leave all changes **uncommitted**. Do not commit, do not delete the package
593
+ directory, do not run tests here.
594
+ 4. Re-run gtd — the next cycle's edge (Testing) commits the work `gtd: building`
595
+ and runs `testCommand` to verify it.
596
+
597
+ Verification is deterministic and lives in the edge, not the prompt: gtd runs
598
+ the configured `testCommand` itself, captures stdout + stderr + the exit code,
599
+ and the **machine** branches on it (green → Agentic Review; red below cap →
600
+ Fixing; red at/over cap → Escalate).
601
+
602
+ ## Q&A format inside TODO.md
603
+
604
+ The agent never asks the user clarifying questions directly — it records
605
+ uncertainty in `TODO.md` under `## Open Questions` instead. The grilling phase
606
+ is gated by a single **convergence marker**: every open question carries a
607
+ `<!-- user answers here -->` line directly beneath it. While _any_ marker is
608
+ present, gtd **STOPs** and waits for you to answer inline.
609
+
610
+ The `## Open Questions` section lives at the TOP of TODO.md (before the plan
611
+ body). Each question looks like this:
612
+
613
+ ```markdown
614
+ ### What should pagination default to?
615
+
616
+ **Recommendation:** 25 per page — matches the admin tables elsewhere.
617
+
618
+ <!-- user answers here -->
619
+ ```
620
+
621
+ To answer, replace the comment with your response:
622
+
623
+ ```markdown
624
+ ### What should pagination default to?
625
+
626
+ **Recommendation:** 25 per page — matches the admin tables elsewhere.
627
+
628
+ 50 — these tables get long and 25 wastes a click for most users.
629
+ ```
630
+
631
+ On the next run the agent integrates the answer into the plan body and moves the
632
+ question to the `## Resolved` graveyard at the bottom:
633
+
634
+ ```markdown
635
+ ## Resolved
636
+
637
+ ### What should pagination default to?
638
+
639
+ **Recommendation:** 25 per page — matches the admin tables elsewhere.
640
+
641
+ **Answer:** 50 — these tables get long and 25 wastes a click for most users.
642
+ ```
643
+
644
+ When there are genuinely no open questions left, the agent writes the sentinel
645
+ line `no open questions — run gtd to plan` and leaves **no** markers — a clean
646
+ tree with no markers is what advances the plan to **Grilled** and decomposition.
647
+
648
+ ## Formatting
649
+
650
+ gtd ships a `format` subcommand — the **only** subcommand — that formats a
651
+ markdown file in place:
652
+
653
+ ```bash
654
+ gtd format <file>
655
+ ```
656
+
657
+ It uses a bundled prettier with a fixed, gtd-owned config (`parser: "markdown"`,
658
+ `printWidth: 80`, `proseWrap: "always"`). The host repo's `.prettierrc` is
659
+ **intentionally ignored** — determinism across consumer repos matters more than
660
+ local style preferences.
661
+
662
+ The grilling and clean prompts instruct the agent to run this command after
663
+ every edit to `TODO.md` or `REVIEW.md`, so those files stay consistently
664
+ formatted regardless of the host project's toolchain.
665
+
666
+ ### Error handling
667
+
668
+ All errors exit with **code 1** and write a message to **stderr**:
669
+
670
+ - **Missing path** — `gtd format` with no argument:
671
+ `gtd format: missing file path argument`
672
+ - **Extra arguments** — `gtd format a.md b.md`:
673
+ `gtd format: too many arguments — expected one path, got: …`
674
+ - **Non-markdown file** — any extension other than `.md` or `.markdown`
675
+ (case-insensitive):
676
+ `gtd format: <file> is not a markdown file (expected .md or .markdown)`
677
+ - **File not found** — the path does not exist:
678
+ `gtd: skipped formatting <file>: not found`
679
+
680
+ > [!NOTE] Upgrading gtd may reflow existing `TODO.md` files if the bundled
681
+ > prettier major version changes.
682
+
683
+ ## Development
684
+
685
+ ```bash
686
+ npm install
687
+ npm run dev # run from source, no build (node dev/run.mjs)
688
+ npm run build # tsup → dist/gtd.bundle.mjs
689
+ npm test # vitest unit tests (the pure resolver) — --project unit
690
+ npm run test:e2e # gherkin e2e via vitest + quickpickle — --project e2e
691
+ npm run test:mutation # StrykerJS mutation testing
692
+ npm run typecheck
693
+ npm run lint
694
+ ```
695
+
696
+ ### Pre-commit hook
697
+
698
+ A pre-commit hook is installed automatically via the `prepare` script when you
699
+ run `npm install` on a fresh clone — no manual setup needed.
700
+
701
+ The hook runs [lint-staged](https://github.com/lint-staged/lint-staged) with
702
+ [Prettier](https://prettier.io/), formatting every staged file before each
703
+ commit:
704
+
705
+ ```
706
+ prettier --ignore-unknown --write
707
+ ```
708
+
709
+ This mirrors the `format:check` step enforced in CI (`prettier --check .`),
710
+ keeping committed code consistently formatted without requiring a separate
711
+ manual format pass.
712
+
713
+ `npm run dev` runs `src/main.ts` directly via Node's native TypeScript
714
+ type-stripping (requires Node 22.6+). It registers `dev/hooks.mjs`, which fills
715
+ the two gaps the tsup build otherwise covers: resolving `./Foo.js` specifiers to
716
+ the on-disk `./Foo.ts`, and importing `*.md` prompt files as text. Pass CLI args
717
+ after `--`, e.g. `npm run dev -- format <file>`. The helpers live in `dev/`
718
+ rather than `scripts/` because tsup wipes `dist/` (`clean: true`) on build.
719
+
720
+ The decision core (`src/Machine.ts`) is pure and IO-free, so the whole 17-state
721
+ ladder and both counter folds are trivially unit-testable in isolation; all
722
+ git/filesystem IO is confined to the edge (`src/Events.ts`).
723
+
724
+ `npm run build` produces `dist/gtd.bundle.mjs`, which npm exposes as the `gtd`
725
+ binary via the `bin` field in `package.json`.
726
+
727
+ ### Mutation testing
728
+
729
+ Run mutation testing on-demand with `npm run test:mutation` (StrykerJS, ~2 min).
730
+ The single `stryker.config.json` mutates six core files:
731
+
732
+ ```
733
+ src/Machine.ts src/Prompt.ts src/Config.ts
734
+ src/Format.ts src/State.ts src/Events.ts
735
+ ```
736
+
737
+ `src/Git.ts` is excluded: the Cucumber harness stubs git at the Effect boundary,
738
+ so Git.ts mutants have zero in-memory coverage. Measuring its post-refactor
739
+ Live-tier kill rate is a follow-up before re-including it.
740
+
741
+ **`process.chdir()` gotcha (resolved).** `@stryker-mutator/vitest-runner`
742
+ hardcodes `pool: 'threads'` internally, and `process.chdir()` is unsupported in
743
+ worker threads. Before the cwd refactor (package 01), four test files
744
+ (`Events.test.ts`, `Git.test.ts`, `Config.test.ts`, `TestRunner.test.ts`) had to
745
+ be excluded from all Stryker runs. The refactor eliminated those calls, letting
746
+ all four files rejoin the run.
747
+
748
+ Two additional notes: `vitest.related` is disabled for feature-file runs because
749
+ feature files don't import source files directly (Stryker's coverage-based
750
+ filtering would assign zero tests to every mutant). Compile-error mutants are
751
+ counted as kills by the TypeScript checker — they represent real signal, not a
752
+ configuration problem.
753
+
754
+ Run `npm run test:mutation` after making changes to the mutated files to check
755
+ whether surviving mutants increased. The HTML report lands in
756
+ `reports/mutation/mutation.html` (git-ignored).
757
+
758
+ ### Mutation testing
759
+
760
+ Run mutation testing on-demand with `npm run test:mutation` (StrykerJS, ~2 min).
761
+ The single `stryker.config.json` mutates six core files:
762
+
763
+ ```
764
+ src/Machine.ts src/Prompt.ts src/Config.ts
765
+ src/Format.ts src/State.ts src/Events.ts
766
+ ```
767
+
768
+ `src/Git.ts` is excluded: the Cucumber harness stubs git at the Effect boundary,
769
+ so Git.ts mutants have zero in-memory coverage. Measuring its post-refactor
770
+ Live-tier kill rate is a follow-up before re-including it.
771
+
772
+ **`process.chdir()` gotcha (resolved).** `@stryker-mutator/vitest-runner`
773
+ hardcodes `pool: 'threads'` internally, and `process.chdir()` is unsupported in
774
+ worker threads. Before the cwd refactor (package 01), four test files
775
+ (`Events.test.ts`, `Git.test.ts`, `Config.test.ts`, `TestRunner.test.ts`) had to
776
+ be excluded from all Stryker runs. The refactor eliminated those calls, letting
777
+ all four files rejoin the run.
778
+
779
+ Two additional notes: `vitest.related` is disabled for feature-file runs because
780
+ feature files don't import source files directly (Stryker's coverage-based
781
+ filtering would assign zero tests to every mutant). Compile-error mutants are
782
+ counted as kills by the TypeScript checker — they represent real signal, not a
783
+ configuration problem.
784
+
785
+ Run `npm run test:mutation` after making changes to the mutated files to check
786
+ whether surviving mutants increased. The HTML report lands in
787
+ `reports/mutation/mutation.html` (git-ignored).
788
+
789
+ ## Releasing
790
+
791
+ Releases are automatic. Push releasable Conventional Commits (`fix:`, `feat:`,
792
+ or breaking changes) to `main` and the Release workflow runs the tests, then
793
+ `npx semantic-release`. Semantic-release computes the next version, writes it
794
+ into `package.json`, builds the bundle, commits the bump back as
795
+ `chore(release): X.Y.Z [skip ci]`, tags `vX.Y.Z`, and creates the GitHub release
796
+ with `gtd.bundle.mjs` attached.
797
+
798
+ ## License
799
+
800
+ MIT