@pmelab/gtd 9.0.0 → 9.1.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
@@ -79,25 +79,27 @@ carrying one piece of content (a script, a prompt, a message, or a squash commit
79
79
  template), with an ordered set of change-patterns routing to the next state.
80
80
 
81
81
  The loop is one beat, repeated: run `gtd next --json` and dispatch on `kind` —
82
- `"message"` means it's a human's move (stop and hand off); `"capture"` means a
83
- human gate the human already acted on (land it immediately); `"script"` means
84
- the driver runs `content` itself, then lands it; `"prompt"` means feed `content`
85
- to your agent — using the accompanying `session.id`/`session.resume` to continue
86
- or start that agent conversation (see "Driving the loop" below) — then run
87
- `gtd land` once it's done. gtd itself never executes anything — the driver owns
88
- running scripts. Every `gtd next` call is strictly mutation-free, safe to poll
89
- or peek at any time: `session.id`/`session.resume` are DERIVED, never stored, so
90
- looking is free — nothing distinguishes a peek from a dispatch, and there is no
91
- separate claiming form at all. A `prompt` beat whose turn changes nothing still
92
- lands: `gtd land` commits an EMPTY `gtd(<actor>): <state>` attempt instead of
93
- silently doing nothing, so the fruitless dispatch is visible in history rather
94
- than invisible. `"kind": "stalled"` is derived from that history — HEAD is an
95
- empty attempt at the resting state and the tree is clean — so every
96
- `gtd next --json` call reports it, and it stays reported on a repeat (there's no
97
- marker to consume) until something actually changes. The fix for a repeated
98
- stall is either a better prompt, a `retry:` cap on the state that redirects to
99
- an escalation state after N fruitless attempts, or — if the state can
100
- legitimately finish with nothing to change — declaring a `C` (clean-tree)
82
+ `"message"` means it's a human's move (stop and hand off, unless it's the run's
83
+ opening beat, which the human triggered by re-running the driver: land that one,
84
+ since changing nothing is itself a declared outcome at some gates); `"capture"`
85
+ means a human gate the human already acted on (land it immediately); `"script"`
86
+ means the driver runs `content` itself, then lands it; `"prompt"` means feed
87
+ `content` to your agent — using the accompanying `session.id`/`session.resume`
88
+ to continue or start that agent conversation (see "Driving the loop" below) —
89
+ then run `gtd land` once it's done. gtd itself never executes anything — the
90
+ driver owns running scripts. Every `gtd next` call is strictly mutation-free,
91
+ safe to poll or peek at any time: `session.id`/`session.resume` are DERIVED,
92
+ never stored, so looking is free — nothing distinguishes a peek from a dispatch,
93
+ and there is no separate claiming form at all. A `prompt` beat whose turn
94
+ changes nothing still lands: `gtd land` commits an EMPTY `gtd(<actor>): <state>`
95
+ attempt instead of silently doing nothing, so the fruitless dispatch is visible
96
+ in history rather than invisible. `"kind": "stalled"` is derived from that
97
+ history — HEAD is an empty attempt at the resting state and the tree is clean —
98
+ so every `gtd next --json` call reports it, and it stays reported on a repeat
99
+ (there's no marker to consume) until something actually changes. The fix for a
100
+ repeated stall is either a better prompt, a `retry:` cap on the state that
101
+ redirects to an escalation state after N fruitless attempts, or — if the state
102
+ can legitimately finish with nothing to change — declaring a `C` (clean-tree)
101
103
  pattern on it.
102
104
 
103
105
  An `on` edge may also carry a short imperative `action` (e.g. `Accept plan`)
@@ -367,8 +369,19 @@ autonomous states (agent turns, check runs) and stops at the first
367
369
  non-autonomous one: reaching a human gate it prints the gate's message and
368
370
  exits. You act by editing files (answer a plan question, tick a review box, fix
369
371
  code) and re-running it — your pending edit arrives as the loop's first beat
370
- (`kind: "capture"`, landed immediately), so you never run `gtd land` by hand; a
371
- mid-process restart simply resumes driving from whatever beat is actually next.
372
+ (`kind: "capture"`, landed immediately), so you never run `gtd land` by hand.
373
+
374
+ Some gates accept by INACTION instead: the bundled template's `plan.await-plan`
375
+ routes its `"C"` (clean-tree) pattern onward, so changing nothing there means
376
+ "accept the plan". That reaches the driver as `kind: "message"`, which no
377
+ inspection can tell apart from a gate you have not read yet — so the driver
378
+ treats its OPENING beat as yours either way and lands it: you re-ran it while
379
+ resting there, and that invocation IS the decision. Every later beat halts as
380
+ usual, because a gate the driver produced mid-run is one you have not seen.
381
+ Landing an opening beat at a gate with no `"C"` pattern is harmless — a benign
382
+ no-op (see `gtd land`'s exit codes below) — and the gate then prints on the next
383
+ beat. A mid-process restart simply resumes driving from whatever beat is
384
+ actually next.
372
385
 
373
386
  Anything richer at that boundary — opening your editor, desktop notifications,
374
387
  terminal-multiplexer status — is the job of an outer wrapper around the driver,
@@ -642,13 +655,17 @@ gtd_land() {
642
655
  return 0
643
656
  }
644
657
 
658
+ beat=1
645
659
  while :; do
646
660
  next="$(gtd next --json)" || exit 1
647
661
  kind="$(jq -r .kind <<<"$next")"
648
662
  log="$(jq -r .log <<<"$next")"
649
663
  case "$kind" in
650
664
  stalled) jq -r .content <<<"$next" >&2; exit 1 ;;
651
- message) jq -r .content <<<"$next"; exit 0 ;;
665
+ # you re-ran us resting here: you either edited something or accepted by
666
+ # editing nothing, so land the opening beat either way. Later beats are
667
+ # gates we just produced and you have not read yet — hand off.
668
+ message) [ "$beat" = 1 ] || { jq -r .content <<<"$next"; exit 0; } ;;
652
669
  capture) ;; # the human already acted — just land it
653
670
  script) bash -c "$(jq -r .content <<<"$next")" >>"$log" 2>&1 || true ;;
654
671
  prompt)
@@ -668,6 +685,7 @@ while :; do
668
685
  done ;;
669
686
  esac
670
687
  gtd_land || exit 1
688
+ beat=$((beat + 1))
671
689
  done
672
690
  ```
673
691
 
@@ -675,11 +693,15 @@ Line by line it is the protocol described above: no opening move at all — a
675
693
  human's pending edit arrives as the `capture` beat, and a stray `gtd land`
676
694
  outside a beat you acted on would author an empty attempt at a clean prompt rest
677
695
  on purpose; one `gtd next --json` beat document read per loop (`kind: "stalled"`
678
- guarding against a spinning agent); `message` halts, `capture` lands a human's
679
- already-made edit outright, `script` runs in the driver, `prompt` goes to the
680
- agent with the document's own `session.id`/`session.resume` mapped onto the
681
- agent's session flags — trying `resume`'s hinted flag first and falling back to
682
- the other on failure, since `session.id` is derived, not remembered (see
696
+ guarding against a spinning agent); `message` halts unless it is the opening
697
+ beat, which the human's own re-invocation authored and which therefore lands
698
+ like any other decision (see [Driving the loop](#driving-the-loop) above — this
699
+ is the one place the driver, not gtd, decides, because "has the human read this
700
+ gate" is run-scoped knowledge gtd deliberately does not keep); `capture` lands a
701
+ human's already-made edit outright, `script` runs in the driver, `prompt` goes
702
+ to the agent with the document's own `session.id`/`session.resume` mapped onto
703
+ the agent's session flags — trying `resume`'s hinted flag first and falling back
704
+ to the other on failure, since `session.id` is derived, not remembered (see
683
705
  [Driving the loop](#driving-the-loop) above) — and its embedded `.validate`
684
706
  script's output re-prompted verbatim on failure (the driver owns only the retry
685
707
  cap); and every landed turn executed — and reported — by the emitted scripts
@@ -207175,13 +207175,17 @@ gtd_land() {
207175
207175
  return 0
207176
207176
  }
207177
207177
 
207178
+ beat=1
207178
207179
  while :; do
207179
207180
  next="$(gtd next --json)" || exit 1
207180
207181
  kind="$(jq -r .kind <<<"$next")"
207181
207182
  log="$(jq -r .log <<<"$next")"
207182
207183
  case "$kind" in
207183
207184
  stalled) jq -r .content <<<"$next" >&2; exit 1 ;;
207184
- message) jq -r .content <<<"$next"; exit 0 ;;
207185
+ # you re-ran us resting here: you either edited something or accepted by
207186
+ # editing nothing, so land the opening beat either way. Later beats are
207187
+ # gates we just produced and you have not read yet — hand off.
207188
+ message) [ "$beat" = 1 ] || { jq -r .content <<<"$next"; exit 0; } ;;
207185
207189
  capture) ;; # the human already acted — just land it
207186
207190
  script) bash -c "$(jq -r .content <<<"$next")" >>"$log" 2>&1 || true ;;
207187
207191
  prompt)
@@ -207201,6 +207205,7 @@ while :; do
207201
207205
  done ;;
207202
207206
  esac
207203
207207
  gtd_land || exit 1
207208
+ beat=$((beat + 1))
207204
207209
  done`;
207205
207210
  const HEADER = () => `gtd ${GTD_VERSION$1} — driver protocol\n\ngtd decides and prints; the driver executes. gtd never runs git, never runs\nyour agent, and never runs a check script. The one subprocess gtd ever spawns\nitself is a steering mode's own format:/validate: command, during a\nland-capture guard.\n`;
207206
207211
  const BEAT_PROTOCOL = `
@@ -207222,7 +207227,12 @@ The \`kind\` field selects what to do:
207222
207227
 
207223
207228
  - \`stalled\` — print \`.content\` (a diagnosis) to stderr and exit non-zero.
207224
207229
  Terminal: another dispatch would just repeat the same fruitless turn.
207225
- - \`message\` — print \`.content\` and exit 0. This is a human gate.
207230
+ - \`message\` — print \`.content\` and exit 0. This is a human gate. EXCEPT on
207231
+ the run's opening beat: the human invoked you while resting there, so land
207232
+ it instead of printing. Some gates route a \`"C"\` (clean-tree) pattern
207233
+ onward, making "change nothing" a real decision that looks identical to a
207234
+ gate nobody has read yet; landing an opening beat at a gate without one is
207235
+ a benign no-op, and it prints on the next beat.
207226
207236
  - \`capture\` — a human gate the human already acted on (the tree is dirty).
207227
207237
  Land it immediately, no display needed.
207228
207238
  - \`script\` — run \`bash -c .content\`, appending its output to \`.log\`, and
@@ -207289,7 +207299,9 @@ const DRIVER_OBLIGATIONS = `
207289
207299
  no opening move: a human's pending edit arrives as a \`kind: "capture"\`
207290
207300
  beat, which you land immediately without executing anything.
207291
207301
  2. A \`kind: "stalled"\` beat halts the driver with a non-zero exit; a
207292
- \`kind: "message"\` beat prints \`content\` and exits 0.
207302
+ \`kind: "message"\` beat prints \`content\` and exits 0, unless it is the
207303
+ run's opening beat — land that one, since the human's re-invocation while
207304
+ resting there is itself their decision.
207293
207305
  3. Run scripts with their output appended to \`.log\` — gtd never creates or
207294
207306
  truncates that file itself; truncate it once per run. \`$GTD_LOOP_LOG\`
207295
207307
  overrides its path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "9.0.0",
3
+ "version": "9.1.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": {