@pmelab/gtd 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@ merge-base with the default branch (whole-history fallback when there is no
17
17
  default branch, when HEAD equals the merge-base, or when there is no merge-base
18
18
  — i.e. budgets engage on the default branch too) plus the working tree, turns
19
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
20
+ through the machine. The fold lands on exactly **one** of 18 states, which
21
21
  selects the prompt. A single run resolves to a single state.
22
22
 
23
23
  `resolve()` returns that state plus an optional **`EdgeAction`** (a commit,
@@ -239,7 +239,9 @@ flowchart TD
239
239
  P5 -->|no| P6{"TODO.md?"}
240
240
  P6 -->|"open markers"| GrillStop["Grilling — gtd: grilling, STOP for answers"]:::gate
241
241
  P6 -->|"dirty, no markers"| GrillIter["Grilling — gtd: grilling, agent iterates"]:::agent
242
- P6 -->|"clean, no markers"| Grilled["Grilled — gtd: grilled, decompose"]:::agent
242
+ P6 -->|"clean, no markers"| GrilledReview["Grilled review — gtd: grilled, human review"]:::gate
243
+ GrilledReview -->|"re-run gtd clean"| Grilled["Grilled — gtd: grilled, decompose"]:::agent
244
+ GrilledReview -->|"edits present"| GrillIter
243
245
  P6 -->|absent| P7{"clean + boundary/package-done HEAD,<br/>reviewable diff?"}
244
246
  P7 -->|yes| CleanState["Clean — write REVIEW.md"]:::agent
245
247
  P7 -->|no| Idle
@@ -290,7 +292,7 @@ back into the working tree uncommitted, and re-derives state from scratch. If
290
292
  the transport commit is the repository's root commit (no parent), `gtd` fails
291
293
  immediately with a clear error instead of looping.
292
294
 
293
- ## The 17 states
295
+ ## The 18 states
294
296
 
295
297
  Each state has a **condition** (when it wins), a deterministic **action**, the
296
298
  **commit(s)** it produces, and where it **advances**. States marked
@@ -313,8 +315,9 @@ action and re-resolves silently.
313
315
  | **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
316
  | **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
317
  | **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
+ | **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 review |
319
+ | **Grilled review** | STOP | TODO.md present, no markers, clean tree, HEAD `gtd: grilled` (converged — not yet decomposed) | none | edits Grilling; clean re-run Grilled |
320
+ | **Grilled** | agent, auto | TODO.md present, no markers, clean tree, HEAD `gtd: grilled` (returning from Grilled review with no new edits) | commit pending `gtd: grilled` | decompose into `.gtd/` → Planning |
318
321
  | **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
322
  | **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
323
 
@@ -422,9 +425,11 @@ loop before the next one starts.
422
425
  your answer, and run `gtd` again. The agent integrates answers, moves them to
423
426
  `## Resolved`, and raises fresh questions — repeat until none remain (it
424
427
  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
+ 4. **Converge.** A clean tree with no markers resolves to **Grilled review**
429
+ (`gtd: grilled`) a human STOP. Review the plan; edit and re-run `gtd` to
430
+ re-enter Grilling, or re-run `gtd` with no changes to advance. A clean re-run
431
+ triggers **Grilled** (auto-advance), which then **Planning** decomposes
432
+ `TODO.md` into ordered `.gtd/` work packages (`gtd: planning`).
428
433
  5. **Build.** Run `gtd` — **Building** first deletes `TODO.md` (when HEAD is
429
434
  `gtd: planning` and it is still present, committed under the same
430
435
  `gtd: planning` prefix — fires once). It then names the single next package
@@ -442,9 +447,9 @@ loop before the next one starts.
442
447
  (`gtd: done`) → **Squashing** → **Idle**. The Squashing agent authors a
443
448
  conventional-commits message from the full process diff and squashes all
444
449
  intermediate `gtd: *` commits into one with `git reset --soft <base>` +
445
- <<<<<<< HEAD `git commit`, then **gtd STOPs**. The base is the parent of the
446
- current cycle's start marker **nearest to HEAD** (the last `gtd: new task`;
447
- for legacy cycles the last contiguous `gtd: grilling` run), and on a feature
450
+ `git commit`, then **gtd STOPs**. The base is the parent of the current
451
+ cycle's start marker **nearest to HEAD** (the last `gtd: new task`; for
452
+ legacy cycles the last contiguous `gtd: grilling` run), and on a feature
448
453
  branch it never reaches below the merge-base with the default branch — stray
449
454
  markers left behind by older squashes can never drag the squash into
450
455
  previously shipped features. Post-squash review does not fire automatically —
@@ -453,20 +458,13 @@ loop before the next one starts.
453
458
  has no unrelated code dirty — a lone untracked `SQUASH_MSG.md` is tolerated
454
459
  and deleted before the squash commit. If unrelated code is dirty at
455
460
  `gtd: done`, gtd routes to **New Feature** instead. Set `squash: false` in
456
- ======= `git commit`, then **gtd STOPs**. Post-squash review does not fire
457
- automatically it fires only on the next manual `gtd` run (when the squash
458
- commit is the boundary HEAD and a reviewable diff exists). Squashing fires
459
- when the tree has no unrelated code dirty — a lone untracked `SQUASH_MSG.md`
460
- is tolerated and deleted before the squash commit. If unrelated code is dirty
461
- at `gtd: done`, gtd routes to **New Feature** instead. Set `squash: false` in
462
- > > > > > > > origin/46-config-schema `.gtdrc` to skip squashing and go
463
- > > > > > > > straight to Idle. Checking off REVIEW.md checkboxes (`- [ ]` →
464
- > > > > > > > `- [x]`) also counts as approval and routes to **Done** — they
465
- > > > > > > > are navigation aids, not feedback. Only **non-checkbox** edits
466
- > > > > > > > (code changes, inline comments, textual annotations in
467
- > > > > > > > REVIEW.md) trigger **Accept Review**, which seeds a fresh
468
- > > > > > > > `TODO.md` from your feedback, discards your code edits, removes
469
- > > > > > > > `REVIEW.md`, and re-enters Grilling — the loop starts over.
461
+ `.gtdrc` to skip squashing and go straight to Idle. Checking off REVIEW.md
462
+ checkboxes (`- [ ]` `- [x]`) also counts as approval and routes to **Done**
463
+ they are navigation aids, not feedback. Only **non-checkbox** edits (code
464
+ changes, inline comments, textual annotations in REVIEW.md) trigger **Accept
465
+ Review**, which seeds a fresh `TODO.md` from your feedback, discards your
466
+ code edits, removes `REVIEW.md`, and re-enters Grilling the loop starts
467
+ over.
470
468
 
471
469
  ## Configuration
472
470
 
@@ -814,37 +812,6 @@ Run `npm run test:mutation` after making changes to the mutated files to check
814
812
  whether surviving mutants increased. The HTML report lands in
815
813
  `reports/mutation/mutation.html` (git-ignored).
816
814
 
817
- ### Mutation testing
818
-
819
- Run mutation testing on-demand with `npm run test:mutation` (StrykerJS, ~2 min).
820
- The single `stryker.config.json` mutates six core files:
821
-
822
- ```
823
- src/Machine.ts src/Prompt.ts src/Config.ts
824
- src/Format.ts src/State.ts src/Events.ts
825
- ```
826
-
827
- `src/Git.ts` is excluded: the Cucumber harness stubs git at the Effect boundary,
828
- so Git.ts mutants have zero in-memory coverage. Measuring its post-refactor
829
- Live-tier kill rate is a follow-up before re-including it.
830
-
831
- **`process.chdir()` gotcha (resolved).** `@stryker-mutator/vitest-runner`
832
- hardcodes `pool: 'threads'` internally, and `process.chdir()` is unsupported in
833
- worker threads. Before the cwd refactor (package 01), four test files
834
- (`Events.test.ts`, `Git.test.ts`, `Config.test.ts`, `TestRunner.test.ts`) had to
835
- be excluded from all Stryker runs. The refactor eliminated those calls, letting
836
- all four files rejoin the run.
837
-
838
- Two additional notes: `vitest.related` is disabled for feature-file runs because
839
- feature files don't import source files directly (Stryker's coverage-based
840
- filtering would assign zero tests to every mutant). Compile-error mutants are
841
- counted as kills by the TypeScript checker — they represent real signal, not a
842
- configuration problem.
843
-
844
- Run `npm run test:mutation` after making changes to the mutated files to check
845
- whether surviving mutants increased. The HTML report lands in
846
- `reports/mutation/mutation.html` (git-ignored).
847
-
848
815
  ## Releasing
849
816
 
850
817
  Releases are automatic. Push releasable Conventional Commits (`fix:`, `feat:`,
@@ -368486,6 +368486,9 @@ var squashing_default = '<%~ include("@header") %>\n\n<%~ include("@context", {
368486
368486
  // src/prompts/escalate.md
368487
368487
  var escalate_default = '<%~ include("@header") %>\n\n<%~ include("@context", { context: it.context, fenceFor: it.fenceFor }) %>\nThe agent was not able to fix all errors on its own. The last error report is\nstored in `ERRORS.md` for a human to investigate.\n\nNext steps for the human developer:\n\n1. Investigate and fix errors reported in `ERRORS.md`\n2. Delete `ERRORS.md`\n3. Continue the process when you are ready\n\n<%~ include(it.tail) %>\n';
368488
368488
 
368489
+ // src/prompts/grilled-review.md
368490
+ var grilled_review_default = '<%~ include("@header") %>\n\n<%~ include("@context", { context: it.context, fenceFor: it.fenceFor }) %> The\nplan in `TODO.md` has converged \u2014 no open questions remain \u2014 and has been\ncommitted as `gtd: grilled`.\n\n### Human review gate\n\nThis is a human review point. Tell the user to open `TODO.md` and review the\nfinalized plan:\n\n- To **proceed**, continue with a clean working tree \u2014 the plan will be\n decomposed into `.gtd/` work packages.\n- To **revise**, edit `TODO.md` (or sketch code) and continue \u2014 the changes\n re-enter the grilling loop for another convergence round.\n\n<%~ include(it.tail) %>\n';
368491
+
368489
368492
  // src/prompts/idle.md
368490
368493
  var idle_default = '<%~ include("@header") %>\n\n<%~ include("@context", { context: it.context, fenceFor: it.fenceFor }) %>\nReport that the repository is idle \u2014 nothing to do.\n\n<%~ include(it.tail) %>\n';
368491
368494
 
@@ -368533,11 +368536,13 @@ eta.loadTemplate("@agentic-review", agentic_review_default);
368533
368536
  eta.loadTemplate("@clean", clean_default);
368534
368537
  eta.loadTemplate("@squashing", squashing_default);
368535
368538
  eta.loadTemplate("@escalate", escalate_default);
368539
+ eta.loadTemplate("@grilled-review", grilled_review_default);
368536
368540
  eta.loadTemplate("@idle", idle_default);
368537
368541
  eta.readFile = null;
368538
368542
  eta.resolvePath = null;
368539
368543
  var STATE_TEMPLATE = {
368540
368544
  grilled: "@decompose",
368545
+ "grilled-review": "@grilled-review",
368541
368546
  planning: "@decompose",
368542
368547
  building: "@building",
368543
368548
  fixing: "@fixing",
@@ -388987,9 +388992,16 @@ var resolveGrilling = (p4, counters, head7) => {
388987
388992
  context: buildContext(p4, counters, "iterate")
388988
388993
  };
388989
388994
  }
388995
+ if (head7 === "gtd: grilled") {
388996
+ return {
388997
+ state: "grilled",
388998
+ autoAdvance: true,
388999
+ context: buildContext(p4, counters)
389000
+ };
389001
+ }
388990
389002
  return {
388991
- state: "grilled",
388992
- autoAdvance: true,
389003
+ state: "grilled-review",
389004
+ autoAdvance: false,
388993
389005
  edgeAction: { kind: "commitPending", prefix: "gtd: grilled" },
388994
389006
  context: buildContext(p4, counters)
388995
389007
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {