@pmelab/gtd 1.9.0 → 1.9.1

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
@@ -241,8 +241,8 @@ flowchart TD
241
241
  P2 -->|"empty = approval"| Close["Close package — rm pkg dir, gtd: package done"]:::edge
242
242
  P2 -->|"non-empty"| Fixing["Fixing — rm FEEDBACK, fixer agent"]:::agent
243
243
  P2 -->|absent| P2b{"HEALTH.md?"}
244
- P2b -->|"present"| HealthFix["Health Fixing — rm HEALTH.md, gtd: health-fix, fixer agent"]:::agent
245
- HealthFix -.->|"re-resolve"| HealthCheck
244
+ P2b -->|"present"| HealthFix["Health Fixing — rm HEALTH.md, gtd: health-check, fixer agent (leaves edits uncommitted)"]:::agent
245
+ HealthFix -.->|"next gtd run: dirty health HEAD → commit gtd: health-fix, re-resolve"| HealthCheck
246
246
  P2b -->|absent| P3{".gtd/?"}
247
247
  P3 -->|"modified"| Planning["Planning — gtd: planning"]:::agent
248
248
  P3 -->|"code dirty / resume / no-op fixer"| Testing["Testing — gtd: building, run tests"]:::edge
@@ -330,27 +330,27 @@ Each state has a **condition** (when it wins), a deterministic **action**, the
330
330
  human; **edge-only** states render no prompt at all — the driver performs their
331
331
  action and re-resolves silently.
332
332
 
333
- | State | Kind | Wins when | Action & commit | Advances to |
334
- | ------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
335
- | **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 |
336
- | **Escalate** | STOP | ERRORS.md present | none | held until the human deletes ERRORS.md |
337
- | **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 |
338
- | **Health Fixing** | agent, auto | HEALTH.md present (no `.gtd`, REVIEW.md, or FEEDBACK.md) | read HEALTH.md into the prompt, commit its removal `gtd: health-fix` | fixer edits → Health check |
339
- | **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 |
340
- | **Planning** | agent, auto | `.gtd` present **and modified**; HEAD `gtd: grilled` or `gtd: planning` | commit the `.gtd/` changes `gtd: planning` | continue decomposing, else → Building |
341
- | **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 |
342
- | **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 |
343
- | **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 |
344
- | **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 |
345
- | **Squashing** | agent, auto | no steering files, HEAD `gtd: done` or green Health check with ≥1 `gtd: health-fix`, `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) |
346
- | **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 |
347
- | **Await Review** | edge-only, auto | REVIEW.md present and **uncommitted** (freshly written by Clean) | commit REVIEW.md `gtd: awaiting review` | Done (auto, same run) |
348
- | **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 |
349
- | **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 |
350
- | **Grilled** | agent, auto | TODO.md present, no markers, clean tree | commit pending `gtd: grilled` | decompose into `.gtd/` → Planning |
351
- | **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 (three 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 |
352
- | **Health check** | edge-only, auto | no steering files, clean tree, outside a process with no reviewable diff (any branch) — the `!reviewable` case from rule 8 | run `testCommand`: green + no prior `gtd: health-fix` → Idle (no commit); red below `fixAttemptCap` → write HEALTH.md, commit `gtd: health-check` → Health Fixing; red at cap → write ERRORS.md, commit `gtd: health-check` → Escalate; green + ≥1 `gtd: health-fix` → Squashing (if `squash`) or Idle | green → Idle or Squashing; red below cap → Health Fixing; red at cap → Escalate |
353
- | **Idle** | STOP | no steering files, clean tree, health check passed with no prior `gtd: health-fix` commits, and nothing to review | none (no commit — the health check edge terminates the driver loop directly) | — |
333
+ | State | Kind | Wins when | Action & commit | Advances to |
334
+ | ------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
335
+ | **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 |
336
+ | **Escalate** | STOP | ERRORS.md present | none | held until the human deletes ERRORS.md |
337
+ | **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 |
338
+ | **Health Fixing** | agent, auto | HEALTH.md present (no `.gtd`, REVIEW.md, or FEEDBACK.md) | read HEALTH.md into the prompt, commit its removal `gtd: health-check` (HEALTH.md removed so next resolve re-enters `resolveCleanOrIdle`); fixer leaves edits **uncommitted** | fixer edits uncommitted next `gtd` invocation commits them `gtd: health-fix` → Health check |
339
+ | **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 |
340
+ | **Planning** | agent, auto | `.gtd` present **and modified**; HEAD `gtd: grilled` or `gtd: planning` | commit the `.gtd/` changes `gtd: planning` | continue decomposing, else → Building |
341
+ | **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 |
342
+ | **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 |
343
+ | **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 |
344
+ | **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 |
345
+ | **Squashing** | agent, auto | no steering files, HEAD `gtd: done` or green Health check with ≥1 `gtd: health-fix`, `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) |
346
+ | **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 |
347
+ | **Await Review** | edge-only, auto | REVIEW.md present and **uncommitted** (freshly written by Clean) | commit REVIEW.md `gtd: awaiting review` | Done (auto, same run) |
348
+ | **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 |
349
+ | **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 |
350
+ | **Grilled** | agent, auto | TODO.md present, no markers, clean tree | commit pending `gtd: grilled` | decompose into `.gtd/` → Planning |
351
+ | **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 (three 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 |
352
+ | **Health check** | edge-only, auto | no steering files, outside a process with no reviewable diff (any branch) — the `!reviewable` case from rule 8. Two entry points: (a) clean tree under a boundary or `gtd: package done` HEAD; (b) **dirty tree under a `gtd: health-check` or `gtd: health-fix` HEAD with `!pendingErrorsDeletion`** (the fixer's uncommitted edits — commits them `gtd: health-fix` and re-runs the health check within the same `gtd` invocation; NOT corruption). | run `testCommand` (entry point a); or commit pending edits `gtd: health-fix`, then run `testCommand` (entry point b). green + no prior `gtd: health-fix` → Idle (no commit); red below `fixAttemptCap` → write HEALTH.md, commit `gtd: health-check` → Health Fixing; red at cap → write ERRORS.md, commit `gtd: health-check` → Escalate; green + ≥1 `gtd: health-fix` → Squashing (if `squash`) or Idle | green → Idle or Squashing; red below cap → Health Fixing; red at cap → Escalate |
353
+ | **Idle** | STOP | no steering files, clean tree, health check passed with no prior `gtd: health-fix` commits, and nothing to review | none (no commit — the health check edge terminates the driver loop directly) | — |
354
354
 
355
355
  Every prompt also embeds the current `git diff HEAD` (untracked files included)
356
356
  inline, plus the last commit subject and working-tree status, so the agent has
@@ -420,17 +420,25 @@ files, `gtd` runs `testCommand` instead of stopping. This reuses `fixAttemptCap`
420
420
  (default 3) and `squash` — no new config keys are introduced.
421
421
 
422
422
  ```
423
- Idle path → Health check(red) → Health Fixing → Health check(red) → … → Health check(green)
424
-
425
- └── below cap: HEALTH.md, gtd: health-check ─────────────────┘
423
+ Idle path → Health check(red) → Health Fixing → [fixer edits, uncommitted]
424
+
425
+ │ next gtd run: commit gtd: health-fix
426
+ │ │
427
+ │ Health check(red) → …
428
+ │ │
429
+ │ Health check(green)
430
+ │ │
431
+ └── below cap: HEALTH.md, gtd: health-check └── green + ≥1 health-fix → Squashing (if squash enabled) → Idle
426
432
  └── at/over the cap: ERRORS.md, gtd: health-check → Escalate
427
- green + ≥1 health-fix → Squashing (if squash enabled) → Idle
428
433
  ```
429
434
 
430
435
  - **green, no prior `gtd: health-fix`** → Idle immediately (no commit).
431
436
  - **red, below `fixAttemptCap`** → write test output to HEALTH.md, commit
432
- `gtd: health-check` → **Health Fixing** agent fixes, commits removal as
433
- `gtd: health-fix`, loops back to Health check.
437
+ `gtd: health-check` → **Health Fixing** agent fixes the code and leaves its
438
+ edits **uncommitted**. The next `gtd` invocation detects the dirty tree under
439
+ the `gtd: health-check` HEAD, commits the fixer's edits as `gtd: health-fix`
440
+ (an edge-only `health-check` state with a `commitPending` action), and
441
+ immediately re-runs the health check within that same invocation.
434
442
  - **red, at or over `fixAttemptCap`** → write test output to ERRORS.md, commit
435
443
  `gtd: health-check` → **Escalate** (human gate). Delete ERRORS.md to reset the
436
444
  budget and resume.
@@ -739,6 +747,27 @@ When there are genuinely no open questions left, the agent writes the sentinel
739
747
  line `no open questions — run gtd to plan` and leaves **no** markers — a clean
740
748
  tree with no markers is what advances the plan to **Grilled** and decomposition.
741
749
 
750
+ ## CLI flags
751
+
752
+ ```
753
+ Usage: gtd [command] [options]
754
+
755
+ Commands:
756
+ (default) Run the gtd driver loop — detect state, emit next prompt
757
+ format <file> Format a markdown file in place
758
+ review <target> Ad-hoc human review against a git ref or branch
759
+
760
+ Options:
761
+ --json Output structured JSON instead of plain text
762
+ --verbose Show verbose output (thinking deltas, tool events)
763
+ --debug Show debug-level internal information
764
+ --version, -v Print version and exit
765
+ --help, -h Print this help and exit
766
+ ```
767
+
768
+ `--version` (`-v`) and `--help` (`-h`) short-circuit before any git or
769
+ repository-state work — they run outside a repo and in any repo state.
770
+
742
771
  ## Subcommands
743
772
 
744
773
  gtd ships two subcommands: `format` and `review`.
@@ -367980,6 +367980,9 @@ var TestRunner = class _TestRunner extends Context_exports.Tag("TestRunner")() {
367980
367980
  );
367981
367981
  };
367982
367982
 
367983
+ // src/program.ts
367984
+ import { createRequire as createRequire2 } from "module";
367985
+
367983
367986
  // src/Events.ts
367984
367987
  import { join as join11 } from "path";
367985
367988
 
@@ -389107,6 +389110,14 @@ var resolveGrilling = (p4, counters, head7) => {
389107
389110
  };
389108
389111
  var resolveCleanOrIdle = (p4, counters, head7) => {
389109
389112
  const isHealthHead = head7 === "gtd: health-check" || head7 === "gtd: health-fix";
389113
+ if (!p4.workingTreeClean && !p4.pendingErrorsDeletion && isHealthHead) {
389114
+ return {
389115
+ state: "health-check",
389116
+ autoAdvance: true,
389117
+ edgeAction: { kind: "commitPending", prefix: "gtd: health-fix" },
389118
+ context: buildContext(p4, counters)
389119
+ };
389120
+ }
389110
389121
  if (!p4.workingTreeClean && !p4.pendingErrorsDeletion) return null;
389111
389122
  if (!p4.workingTreeClean && p4.pendingErrorsDeletion && p4.gtdDirExists) return null;
389112
389123
  if (!p4.workingTreeClean && p4.pendingErrorsDeletion && !isBoundary(head7) && head7 !== "gtd: package done" && !isHealthHead)
@@ -389244,6 +389255,22 @@ var detect = () => Effect_exports.gen(function* () {
389244
389255
  });
389245
389256
 
389246
389257
  // src/program.ts
389258
+ var _require = createRequire2(import.meta.url);
389259
+ var GTD_VERSION = _require("../package.json").version;
389260
+ var HELP_TEXT = `Usage: gtd [command] [options]
389261
+
389262
+ Commands:
389263
+ (default) Run the gtd driver loop \u2014 detect state, emit next prompt
389264
+ format <file> Format a markdown file in place
389265
+ review <target> Ad-hoc human review against a git ref or branch
389266
+
389267
+ Options:
389268
+ --json Output structured JSON instead of plain text
389269
+ --verbose Show verbose output (thinking deltas, tool events)
389270
+ --debug Show debug-level internal information
389271
+ --version, -v Print version and exit
389272
+ --help, -h Print this help and exit
389273
+ `;
389247
389274
  var IDLE_RESULT = {
389248
389275
  state: "idle",
389249
389276
  autoAdvance: false,
@@ -389264,6 +389291,14 @@ function makeProgram(opts = {}) {
389264
389291
  const json2 = argv.includes("--json");
389265
389292
  const positional = argv.slice(2).find((a5) => !a5.startsWith("--"));
389266
389293
  return Effect_exports.gen(function* () {
389294
+ if (argv.includes("--version") || argv.includes("-v")) {
389295
+ write4(GTD_VERSION + "\n");
389296
+ return;
389297
+ }
389298
+ if (argv.includes("--help") || argv.includes("-h")) {
389299
+ write4(HELP_TEXT);
389300
+ return;
389301
+ }
389267
389302
  const sub = positional;
389268
389303
  if (sub === "format") {
389269
389304
  if (json2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
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": {