@pmelab/gtd 7.1.1 → 7.3.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/docs/cli.md CHANGED
@@ -30,6 +30,12 @@ Commands:
30
30
  the commit the process started from, keeping everything it
31
31
  produced as uncommitted changes. A no-op when no process is
32
32
  underway
33
+ restore Hard-reset HEAD back to the pre-squash tip retained by the
34
+ last squash/abandon (refs/worktree/gtd/history), undoing a
35
+ squash or bringing back an abandoned process's turns.
36
+ Refuses on a dirty working tree, when there is no retained
37
+ history, or when HEAD has advanced past the squash with
38
+ commits that would be lost
33
39
  next Print the resolved rest's rendered script/prompt/message
34
40
  (no mutation)
35
41
  status Print the resolved rest's state/actor and which declared
@@ -295,6 +301,47 @@ hash HEAD now points at:
295
301
 
296
302
  The no-op reports `{"state": "idle", "abandoned": false}` and still exits 0.
297
303
 
304
+ ## `gtd restore [--json]`
305
+
306
+ Undoes the last squash or `gtd abandon` by hard-resetting HEAD back to the
307
+ pre-squash tip that either one retains, before acting, on the per-worktree
308
+ `refs/worktree/gtd/history` ref
309
+ ([STATES.md §8](../STATES.md#8-the-squash-lifecycle)):
310
+
311
+ ```
312
+ restored the retained history — HEAD is back at 1a2b3c4 ("gtd(agent): drafting
313
+ → working"), resting at "await-review". Resume with the loop, or `git reset`
314
+ to any earlier turn to restart from there.
315
+ ```
316
+
317
+ Refuses on a dirty working tree — commit, stash, or discard your changes first.
318
+ Refuses when there is no retained history to restore (nothing has been squashed
319
+ or abandoned yet, or a previous `restore` already consumed it). Refuses when
320
+ HEAD has advanced past the retained tip with commits that would be discarded by
321
+ resetting:
322
+
323
+ ```
324
+ gtd restore: HEAD has advanced past the squash — restoring would discard
325
+ commits built on top of it — HEAD 4d5e6f7 is ahead of the retained tip
326
+ 1a2b3c4.
327
+ ```
328
+
329
+ Takes no positional argument (extra arguments are a usage error).
330
+
331
+ `--json` emits `{state, restored, to, from}` — `state` is the resolved rest
332
+ after the reset, `restored` always `true` (a refusal exits non-zero instead),
333
+ `to` the full hash HEAD was reset to, and `from` the state the machine rested at
334
+ before the reset:
335
+
336
+ ```json
337
+ {
338
+ "state": "await-review",
339
+ "restored": true,
340
+ "to": "1a2b3c4…",
341
+ "from": "idle"
342
+ }
343
+ ```
344
+
298
345
  ## `gtd next [--json]`
299
346
 
300
347
  Pure emitter of the resolved rest's rendered content — it **never mutates** the
@@ -496,9 +543,17 @@ cleanly.
496
543
  ## `gtd visualize [--port=<n>] [--no-open] [--json]`
497
544
 
498
545
  Serve an interactive diagram of the ACTIVE workflow on a local web server: the
499
- main flow as a graph (one box per sub-machine), and a click-through inspector
500
- with each state's actor, content kind, model/memory, steering file+mode, retry,
501
- flags, and outgoing/incoming edges. This is the replacement for the removed
546
+ main flow as a graph (each sub-machine invocation collapsed into a single opaque
547
+ black-box node — click it to jump to that sub-machine's own diagram, rendered
548
+ separately below with its true member states/shapes/colours and a muted ghost
549
+ node for any edge leaving the group; every diagram supports scroll-to-zoom,
550
+ drag-to-pan, and a corner `+`/`−`/fit control cluster), a click-through
551
+ inspector with each state's actor, content kind, its own raw
552
+ prompt/message/script text, model/memory, steering file+mode, retry, flags, and
553
+ outgoing/incoming edges, and — read ONCE at page load, never polled — a "Current
554
+ state" panel showing where the active process rests, its pending changes, and
555
+ which `on` pattern (or retry redirect) currently leads where, with the resting
556
+ node highlighted in the diagrams. This is the replacement for the removed
502
557
  `gtd mermaid` — a live viewer instead of a static diagram dump.
503
558
 
504
559
  ```
@@ -506,25 +561,34 @@ $ gtd visualize
506
561
  gtd visualize running at http://127.0.0.1:53017 — Ctrl-C to stop
507
562
  ```
508
563
 
509
- The server serves two routes: `/` (the self-contained HTML page) and
510
- `/workflow.json` (the model the page renders). It runs until interrupted
564
+ The server serves three routes: `/` (the self-contained HTML page),
565
+ `/workflow.json` (the model the page renders), and `/state.json` (the current
566
+ process's resting state, or `{}` when there isn't one — not a repo, no commits,
567
+ or an older server; the browser tolerates either). It runs until interrupted
511
568
  (Ctrl-C), then closes cleanly. Options (orthogonal, `gtd visualize` only):
512
569
 
513
570
  - `--port=<n>` (or `--port <n>`) — serve on a specific port (0–65535); the
514
571
  default is a free ephemeral port, printed on start.
515
572
  - `--no-open` — do not open the default browser (the URL is always printed).
516
573
  - `--json` — print the workflow model to stdout and exit WITHOUT starting a
517
- server. The model is `{ states, initial, groups, vars }`; each state carries
518
- its `actor`/`kind`/`model`/`memory`/`file`/`mode`/`retry`/`flags`, its `on`
519
- edges, its computed `incoming` edges, and its sub-machine `group`. `groups`
520
- lists each sub-machine invocation and the concrete states it produced.
574
+ server (unchanged shape — live state is a server-only concern). The model is
575
+ `{ states, initial, groups, vars }`; each state carries its
576
+ `actor`/`kind`/`content`/`model`/`memory`/`file`/`mode`/`retry`/`flags`
577
+ (`content` is the state's own raw script/prompt/message text, omitted for a
578
+ commit state), its `on` edges, its computed `incoming` edges, and its
579
+ sub-machine `group`. `groups` lists each sub-machine invocation and the
580
+ concrete states it produced.
521
581
 
522
582
  Dispatched before the repository-root guard and the config-reading path's review
523
583
  window — it reads the active workflow (the built-in default when none is
524
- configured) but touches no git/HEAD/review-window state. The diagram is rendered
525
- with Mermaid loaded from a CDN, so the graph needs network access the first time
526
- a browser loads the page; the inspector works offline regardless. Rejects
527
- unknown options and unexpected positional arguments.
584
+ configured) but touches no git/HEAD/review-window state ITSELF; the
585
+ `/state.json` route best-effort reads git state per request instead (any failure
586
+ — not a repo, no commits — serves `{}`), preferring the review checkout window's
587
+ saved head over HEAD so a request landing mid-window still reports the state the
588
+ process actually rests at. The diagram is rendered with Mermaid loaded from a
589
+ CDN, so the graph needs network access the first time a browser loads the page;
590
+ the inspector and current-state panel work offline regardless. Rejects unknown
591
+ options and unexpected positional arguments.
528
592
 
529
593
  ## Error envelope
530
594
 
@@ -261,6 +261,38 @@ value) only when the resolved state declares one — **omitted entirely**, never
261
261
  `null`, when unset. Plain `gtd status` prints a `Memory:` line (right after
262
262
  `Model:`, when present).
263
263
 
264
+ ### `label:` — the opaque display name, template-rendered
265
+
266
+ A state may declare `label: <string>` — an OPAQUE display name gtd never
267
+ interprets; it is only passed through so a driver/viewer can show something
268
+ friendlier than the raw state name. Unset means the state has no display name.
269
+ Forbidden on a commit state (never at rest, emits nothing):
270
+
271
+ ```yaml
272
+ workflow:
273
+ states:
274
+ working:
275
+ actor: agent
276
+ label: Implement the feature
277
+ prompt: do the thing
278
+ on:
279
+ "* **": done
280
+ ```
281
+
282
+ Like `model`/`memory`, `label:` is rendered as an Eta template through the exact
283
+ same context as the state's content — a plain string with no Eta tags
284
+ (`Implement the feature` above) passes through unchanged, but
285
+ `label: "<%= it.vars.stateLabel %>"` resolves against the merged `it.vars` (see
286
+ ["Variables"](#variables)). A render failure behaves exactly like a content
287
+ render failure at the same call site: `gtd next`/`gtd status` error out, nothing
288
+ committed.
289
+
290
+ `gtd next --json` and `gtd status --json` include a `"label"` key (the RENDERED
291
+ value) only when the resolved state declares one — it is **omitted entirely**,
292
+ never emitted as `null`, when unset. A driver/viewer choosing to fall back to
293
+ the raw state name when `label` is absent is that consumer's own fallback, not
294
+ something gtd itself does.
295
+
264
296
  ### `file:`/`mode:` — the steering-file association
265
297
 
266
298
  A state may additionally declare `file:` — an Eta template naming THE steering
package/docs/loop.md CHANGED
@@ -18,13 +18,18 @@ DRIVER runs the emitted `content` and then steps that state's own actor.
18
18
  A loop is a simple cycle:
19
19
 
20
20
  1. Run `gtd next --json` — see [`cli.md`](cli.md#gtd-next---json) for the exact
21
- `{state, actor, kind, content, model?, memory?}` field shape. `kind` is the
22
- dispatch key: `"message"` → halt (a human rest); `"script"` → execute
21
+ `{state, actor, kind, content, model?, memory?, label?}` field shape. `kind`
22
+ is the dispatch key: `"message"` → halt (a human rest); `"script"` → execute
23
23
  `content` verbatim (ignoring its exit code), then `gtd step <actor>` to
24
24
  capture the outcome; `"prompt"` → feed `content` to the agent, then
25
25
  `gtd step <actor>` yourself once it's done acting. The optional `model` is an
26
26
  opaque string the workflow author chose, which you map onto your own
27
- harness's model selection if you use one.
27
+ harness's model selection if you use one. The optional `label` is a
28
+ human-readable display name for the resolved state, present only when that
29
+ state declares `label:` in the workflow; when it's absent, a driver that
30
+ wants to show something falls back to `state` itself (that fallback lives in
31
+ the driver, not gtd core — see `bin/gtd-loop`'s
32
+ `label="$(jq -r '.label // .state' <<<"$next_json")"`).
28
33
  2. Repeat until a `"message"` rest halts the loop, or a zero-commit script step
29
34
  at idle settles it (the green terminal signal).
30
35
 
@@ -162,3 +167,35 @@ example, to drive a different agent CLI:
162
167
  ```bash
163
168
  GTD_LOOP_AGENT_CMD='my-agent-cli --prompt "$GTD_LOOP_PROMPT"' gtd-loop
164
169
  ```
170
+
171
+ ## Herdr integration (optional)
172
+
173
+ `bin/gtd-loop` optionally reports its lifecycle to [Herdr](https://herdr.dev) (a
174
+ terminal multiplexer for coding agents) via a `herdr` CLI binary, entirely at
175
+ the driver's edge — gtd core never talks to Herdr. Every call is best-effort
176
+ (guarded, output discarded, `|| true`), so a missing/failing `herdr` binary
177
+ never blocks, slows, or fails the loop.
178
+
179
+ The reporting is a no-op unless all three guard conditions hold: `HERDR_ENV=1`,
180
+ a non-empty `$HERDR_PANE_ID`, and `herdr` on `$PATH`. When they do, `gtd-loop`
181
+ makes three kinds of calls:
182
+
183
+ - `herdr pane report-agent --source herdr:gtd --agent gtd --state <state> --message <label> "$HERDR_PANE_ID"`
184
+ - `herdr pane release-agent --source herdr:gtd --agent gtd "$HERDR_PANE_ID"`
185
+ - `herdr notification show <title> --body <label> --sound request`
186
+
187
+ mapped onto the loop's states like this:
188
+
189
+ | Loop moment | Call(s) |
190
+ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
191
+ | Top of every non-`"message"` iteration | `report-agent --state working` |
192
+ | Right before halting at a `"message"` (human) gate | `report-agent --state blocked` + `notification show` |
193
+ | Right before a clean settle (a script rest, zero new commits) | `report-agent --state idle` + `release-agent` |
194
+ | Any non-zero exit (stall guard, validate-cap, `gtd next` failure, unhandled failure) | `report-agent --state blocked` + `notification show`, via an `EXIT` trap |
195
+
196
+ `working`/`blocked` reports use the resolved rest's `label` (falling back to
197
+ `state`, per the field above); the trap falls back to `$GTD_LAST_LABEL`, the
198
+ label tracked from the last completed iteration, when it fires outside the
199
+ normal per-iteration flow. `report-agent` claims display authority for the
200
+ `herdr:gtd` source, so `working` is re-reported every lap rather than once, to
201
+ stay fresh against Herdr's own heuristic detection of the inner agent process.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "7.1.1",
3
+ "version": "7.3.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": {