@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/README.md +20 -1
- package/bin/gtd-loop +33 -0
- package/dist/gtd.bundle.mjs +318 -40
- package/docs/cli.md +77 -13
- package/docs/configuration.md +32 -0
- package/docs/loop.md +40 -3
- package/package.json +1 -1
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 (
|
|
500
|
-
|
|
501
|
-
|
|
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
|
|
510
|
-
`/workflow.json` (the model the page renders)
|
|
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
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
|
525
|
-
|
|
526
|
-
a
|
|
527
|
-
|
|
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
|
|
package/docs/configuration.md
CHANGED
|
@@ -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`
|
|
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.
|