@pmelab/gtd 8.4.0 → 8.5.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 +79 -55
- package/bin/gtd +1 -1
- package/dist/gtd.bundle.mjs +3701 -2823
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
**Chat is a terrible source of truth. Git isn't.**
|
|
7
7
|
|
|
8
8
|
**gtd** is a git-aware CLI that derives the entire agentic workflow — capture,
|
|
9
|
-
plan, build,
|
|
10
|
-
prompt for whatever agent you point at it. Every
|
|
11
|
-
|
|
12
|
-
homework.
|
|
9
|
+
plan, build, check, review — from your repository state, and prints the next
|
|
10
|
+
prompt for whatever agent you point at it. Every turn is a commit. The test
|
|
11
|
+
command is printed for your driver to run, and the branch is decided by what the
|
|
12
|
+
run leaves in the tree — so the agent never grades its own homework.
|
|
13
13
|
|
|
14
14
|
No chat scrollback. No lost sessions. No infinite fix loops. Just git.
|
|
15
15
|
|
|
@@ -120,31 +120,31 @@ for). The two steering-file entries are chosen by which file you create:
|
|
|
120
120
|
Both flows converge on the same tail: an agent hands you a `.gtd/REVIEW.md`
|
|
121
121
|
checkbox review of the diff — the prompt never inlines the diff itself; it names
|
|
122
122
|
the commit the changes are based at and the agent runs `git diff` to read the
|
|
123
|
-
range before writing the review. While the
|
|
124
|
-
**review checkout window**: HEAD is rewound to the review base with the
|
|
125
|
-
tree untouched, so the whole reviewable change shows up as ordinary
|
|
126
|
-
changes in your editor's normal git integration (and files added
|
|
127
|
-
|
|
128
|
-
window closes on the next gtd invocation. Tick a box as you
|
|
129
|
-
(ticking just records "I read this"), and leave a **comment**
|
|
130
|
-
changes: a note on a line, an inline `// TODO`-style comment in the
|
|
131
|
-
direct code edit. Any comment sends a build + re-review round — an
|
|
132
|
-
turns your comments into an explicit instruction list, then a build
|
|
133
|
-
implements it (a re-review then covers only the follow-through, and a
|
|
134
|
-
is treated as your own fix the agent completes without reverting your
|
|
135
|
-
comment can't be silently dropped — a build turn that addresses nothing
|
|
136
|
-
refused). For the simple flow, the build turn that follows through on
|
|
137
|
-
and the turn that drafts the final squash message both resume the same
|
|
138
|
-
that built the feature in the first place, since the review tail is
|
|
139
|
-
inside that build identity rather than sitting beside it. Ticking every
|
|
140
|
-
no comment is the sign-off, which collapses the whole
|
|
141
|
-
**squash finale** whose message an agent drafts). Stepping with a box
|
|
142
|
-
unticked and no comment is refused (finish reviewing first), as is
|
|
143
|
-
`.gtd/REVIEW.md`.
|
|
123
|
+
range before writing the review. While the process rests at that gate, gtd opens
|
|
124
|
+
a **review checkout window**: HEAD is rewound to the review base with the
|
|
125
|
+
working tree untouched, so the whole reviewable change shows up as ordinary
|
|
126
|
+
uncommitted changes in your editor's normal git integration (and files added
|
|
127
|
+
during the process show up as ordinary untracked files, so discarding one
|
|
128
|
+
deletes it). The window closes on the next gtd invocation. Tick a box as you
|
|
129
|
+
review each hunk (ticking just records "I read this"), and leave a **comment**
|
|
130
|
+
to request changes: a note on a line, an inline `// TODO`-style comment in the
|
|
131
|
+
code, or a direct code edit. Any comment sends a build + re-review round — an
|
|
132
|
+
agent first turns your comments into an explicit instruction list, then a build
|
|
133
|
+
turn implements it (a re-review then covers only the follow-through, and a
|
|
134
|
+
hand-edit is treated as your own fix the agent completes without reverting your
|
|
135
|
+
lines; a comment can't be silently dropped — a build turn that addresses nothing
|
|
136
|
+
is refused). For the simple flow, the build turn that follows through on
|
|
137
|
+
feedback and the turn that drafts the final squash message both resume the same
|
|
138
|
+
session that built the feature in the first place, since the review tail is
|
|
139
|
+
nested inside that build identity rather than sitting beside it. Ticking every
|
|
140
|
+
box with no comment is the sign-off, which collapses the whole process into one
|
|
141
|
+
commit (a **squash finale** whose message an agent drafts). Stepping with a box
|
|
142
|
+
still unticked and no comment is refused (finish reviewing first), as is
|
|
143
|
+
deleting `.gtd/REVIEW.md`.
|
|
144
144
|
|
|
145
145
|
The same review tail also has a direct entry point —
|
|
146
146
|
`gtd --entry review-gate.check --var reviewBase=<commitish>` starts a brand new
|
|
147
|
-
process reviewing `<commitish>..HEAD` with no
|
|
147
|
+
process reviewing `<commitish>..HEAD` with no build of its own, e.g. a
|
|
148
148
|
colleague's PR branch (`review-gate.check`'s `reviewBase:` is a template bound
|
|
149
149
|
to the `reviewBase` var, so supplying it via `--var` fixes the whole process's
|
|
150
150
|
diff base to that commitish). Its squash keeps and describes only the fixes made
|
|
@@ -207,14 +207,12 @@ Commands:
|
|
|
207
207
|
just-finished invocation's token cost and model on the
|
|
208
208
|
turn commit (summed into it.processCost/processCostByModel).
|
|
209
209
|
Pass --entry <state> to start a brand NEW process at
|
|
210
|
-
<state> instead — any declared, non-commit state
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
that new process's fixed it.vars overrides
|
|
210
|
+
<state> instead — any declared, non-commit state — with
|
|
211
|
+
repeatable --var <name>=<value> supplying that new
|
|
212
|
+
process's fixed it.vars overrides
|
|
214
213
|
(no command) --entry <state>
|
|
215
214
|
Short form of 'step human --entry <state>' — starts a new
|
|
216
|
-
process authenticated as human, e.g.
|
|
217
|
-
'gtd --entry review-gate.check'
|
|
215
|
+
process authenticated as human, e.g. 'gtd --entry <state>'
|
|
218
216
|
abandon End the process currently underway without completing it:
|
|
219
217
|
close any open review checkout window, then rewind HEAD to
|
|
220
218
|
the commit the process started from, keeping everything it
|
|
@@ -226,8 +224,7 @@ Commands:
|
|
|
226
224
|
Refuses on a dirty working tree, when there is no retained
|
|
227
225
|
history, or when HEAD has advanced past the squash with
|
|
228
226
|
commits that would be lost
|
|
229
|
-
next Print the resolved rest's rendered script/prompt/message
|
|
230
|
-
(no mutation)
|
|
227
|
+
next Print the resolved rest's rendered script/prompt/message (no mutation)
|
|
231
228
|
status Print the resolved rest's state/actor and which declared
|
|
232
229
|
pattern (if any) each pending change matches (no mutation)
|
|
233
230
|
validate Format and validate the steering file the resolved rest
|
|
@@ -255,9 +252,6 @@ Options:
|
|
|
255
252
|
(with --entry; repeatable) supply a fixed it.vars
|
|
256
253
|
override for the new process; the name must already be
|
|
257
254
|
declared by the workflow's own vars: or the .gtdrc vars:
|
|
258
|
-
--once (bare gtd or gtd loop only) run exactly one loop beat (one
|
|
259
|
-
human-gate capture, one script check+step, or one agent
|
|
260
|
-
prompt+step), then exit
|
|
261
255
|
--version, -v Print version and exit
|
|
262
256
|
--help, -h Print this help and exit
|
|
263
257
|
```
|
|
@@ -312,9 +306,16 @@ envelope on **stdout**, and still exits 1:
|
|
|
312
306
|
{ "state": "error", "prompt": "<message>" }
|
|
313
307
|
```
|
|
314
308
|
|
|
309
|
+
This covers every failure mode, not just a command's own refusal: a **usage
|
|
310
|
+
error** (an unknown flag, a missing argument, `gtd --entry version`'s "not an
|
|
311
|
+
enterable state") and a **defect** (a layer throwing outside the ordinary error
|
|
312
|
+
channel) both get the same envelope — there is no failure path that reaches
|
|
313
|
+
`--json` without one.
|
|
314
|
+
|
|
315
315
|
A human-readable `gtd: <message>` line is still written to **stderr** regardless
|
|
316
316
|
of `--json` — the envelope adds a structured stdout channel, it does not replace
|
|
317
|
-
the plain-text one.
|
|
317
|
+
the plain-text one. Stderr always carries exactly one `gtd: ` prefix: a message
|
|
318
|
+
already authored with its own `gtd:`/`gtd <cmd>:` prefix is never doubled.
|
|
318
319
|
|
|
319
320
|
## Driving the loop
|
|
320
321
|
|
|
@@ -453,7 +454,7 @@ A few things to know before relying on it:
|
|
|
453
454
|
owns a `herdr:claude` session until the process exits; while it does, the
|
|
454
455
|
wrapper's reports are silently ignored and the sidebar stops tracking `gtd`.
|
|
455
456
|
- **`blocked` covers the resting `idle` state too** — gtd's own `idle` is a
|
|
456
|
-
human gate (it waits for you to write a steering file), so a finished
|
|
457
|
+
human gate (it waits for you to write a steering file), so a finished process
|
|
457
458
|
reads as "your turn", which is what it is.
|
|
458
459
|
- The status **persists after the wrapper exits** — that's the point: the
|
|
459
460
|
sidebar keeps showing which worktree is waiting on you. Hand the pane back to
|
|
@@ -527,7 +528,7 @@ workflow:
|
|
|
527
528
|
anyKey: anyScalarValue
|
|
528
529
|
modes: # optional — steering-file modes a state's `mode:` may name
|
|
529
530
|
<name>:
|
|
530
|
-
format: <shell command> #
|
|
531
|
+
format: <shell command> # both optional — {} is the format-only tier
|
|
531
532
|
validate: <shell command>
|
|
532
533
|
entry:
|
|
533
534
|
default: <machine name> # which machine is the ROOT instance
|
|
@@ -553,11 +554,14 @@ workflow:
|
|
|
553
554
|
retry:
|
|
554
555
|
max: <number>
|
|
555
556
|
otherwise: <targetState>
|
|
557
|
+
label: <string> # optional, opaque display name passed through `gtd next --json`/`gtd status --json`
|
|
556
558
|
file: <string> # optional, an Eta template naming the state's steering file
|
|
557
|
-
mode: <modeName> # optional, requires "file" —
|
|
559
|
+
mode: <modeName> # optional, requires "file" — must be declared in `modes:` (qa/review are seeded for you; everything else, including prose, you declare)
|
|
558
560
|
reviewWindow: true # optional — open the review checkout window at rest here
|
|
559
561
|
reviewBase: true # optional — anchor the review window's diff base to this state's most-recent commit
|
|
560
562
|
# reviewBase: <Eta template> # OR a template — rendered (only meaningful entering via --entry) to a commitish that fixes the WHOLE PROCESS's diff base
|
|
563
|
+
requireProgress: true # optional, requires "file" — refuse a turn whose only change deletes this state's own `file:`
|
|
564
|
+
answerGate: true # optional, requires "file" — refuse a turn until every open question in the (qa-mode) `file:` is answered
|
|
561
565
|
entry: true # optional — an EXTRA reachability root (`entries.manual`), enterable via `gtd --entry <this state's qualified name>` — NOT a precondition for `--entry` (any declared, non-commit state is a valid target)
|
|
562
566
|
<local>: { machine: <name>, with: { <param>: <value> } } # a REFERENCE — instantiates <name> as a child, qualified as `<local>.<childLocal>`
|
|
563
567
|
```
|
|
@@ -587,7 +591,7 @@ conversation across both call sites.
|
|
|
587
591
|
|
|
588
592
|
Besides `it.vars` (below), a `script`/`prompt`/`message`/`commit` template sees:
|
|
589
593
|
|
|
590
|
-
- **`it.startCommit`** — the process's diff base (the commit the current
|
|
594
|
+
- **`it.startCommit`** — the process's diff base (the commit the current process
|
|
591
595
|
started from, or the base a `--var reviewBase=<commitish>` entry resolved to).
|
|
592
596
|
- **`it.reviewBase`** — the previous review round's boundary, falling back to
|
|
593
597
|
`it.startCommit` on a first review.
|
|
@@ -607,9 +611,9 @@ model, pattern grammar, load-time rules, and how to verify a change compiles.
|
|
|
607
611
|
|
|
608
612
|
> **Upgrading from a pre-8.2 `workflow:`?** The old flat `states:` shape (with a
|
|
609
613
|
> per-state `initial: true`/`reviewEntry: true`/`fixEntry: true` flag) is no
|
|
610
|
-
> longer accepted — finish or `gtd abandon` any in-flight
|
|
611
|
-
> upgrading, since the old and new shapes aren't compatible mid-
|
|
612
|
-
> states under a single
|
|
614
|
+
> longer accepted — finish or `gtd abandon` any in-flight process before
|
|
615
|
+
> upgrading, since the old and new shapes aren't compatible mid-process. Wrap
|
|
616
|
+
> your states under a single
|
|
613
617
|
> `machines: { <name>: { entry: <initial state>, states: {...} } }` and declare
|
|
614
618
|
> `entry: { default: <name> }` at the top level (moving any
|
|
615
619
|
> `reviewEntry`/`fixEntry` state to a plain per-state `entry: true` flag,
|
|
@@ -785,13 +789,18 @@ own `- [ ]` line — diagnostics for both (live as you edit), and a
|
|
|
785
789
|
|
|
786
790
|
It is config-driven via each state's `file:`/`mode:`, and falls back to basename
|
|
787
791
|
dispatch (`REVIEW.md` → `review`) with no config in sight. `qa` and `review` are
|
|
788
|
-
gtd's built-in steering-file
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
792
|
+
gtd's built-in steering-file FORMATS, each with its own outline/actions/
|
|
793
|
+
go-to-definition and a VALIDATOR gtd itself implements; a mode's `format:` and
|
|
794
|
+
`validate:` are shell commands a workflow (or a project's `.gtdrc`) declares for
|
|
795
|
+
itself, so you bring your own formatter and your own checkers. Overriding one of
|
|
796
|
+
`qa`/`review`'s `validate:` with a shell command does NOT lose its outline/
|
|
797
|
+
actions — those come from the FORMAT (the name), independent of who validates —
|
|
798
|
+
but `gtd lsp` never runs a shell command per keystroke, so live diagnostics
|
|
799
|
+
become one `Information` notice pointing at `gtd validate` instead of the
|
|
800
|
+
built-in findings. Any OTHER mode name — `prose` (used by the simple flow's plan
|
|
801
|
+
file), or a project's own declared name — has no built-in format, so it gets no
|
|
802
|
+
live editor support at all: `gtd validate` and the `gtd step` gate still format
|
|
803
|
+
and validate it like any other mode.
|
|
795
804
|
|
|
796
805
|
## Development
|
|
797
806
|
|
|
@@ -800,8 +809,10 @@ npm install
|
|
|
800
809
|
npm run dev # run from source, no build (node dev/run.mjs)
|
|
801
810
|
npm run build # tsdown → dist/gtd.bundle.mjs
|
|
802
811
|
npm test # format:check, typecheck, lint, unit + e2e tests, fallow
|
|
803
|
-
npm run test:unit
|
|
804
|
-
npm run test:e2e
|
|
812
|
+
npm run test:unit # vitest unit tests (the pure resolver) — --project unit
|
|
813
|
+
npm run test:e2e # gherkin e2e, both tiers — test:e2e:inmem + test:e2e:live
|
|
814
|
+
npm run test:e2e:inmem # @inmem scenarios, in-process, no build — --project e2e-inmem
|
|
815
|
+
npm run test:e2e:live # @live scenarios against the built bundle — --project e2e-live
|
|
805
816
|
npm run test:mutation # StrykerJS mutation testing (manual only, ~10 min)
|
|
806
817
|
npm run typecheck
|
|
807
818
|
npm run lint
|
|
@@ -817,7 +828,20 @@ The decision core is pure and IO-free: the pattern machine's shape (states,
|
|
|
817
828
|
patterns, retry) is a plain-data `WorkflowDefinition` (`src/PatternMachine.ts`),
|
|
818
829
|
and the same module's `resolveState`/`step` are the pure resolver/interpreter
|
|
819
830
|
over it — so the whole engine is trivially unit-testable in isolation. All
|
|
820
|
-
git/filesystem/template IO is confined to the edge (`src/Edge.ts`)
|
|
831
|
+
git/filesystem/template IO is confined to the edge (`src/Edge.ts`), whose
|
|
832
|
+
`currentRest` resolves everything derivable from where the process rests right
|
|
833
|
+
now into a single snapshot, and whose `planStep`/`planEntry` pair separates
|
|
834
|
+
deciding a transition from performing it — so a command's own capture guards run
|
|
835
|
+
between the two. The edge reaches the outside world through two content ports
|
|
836
|
+
(`src/RepoFiles.ts` for working-tree and committed reads, `src/CommandRunner.ts`
|
|
837
|
+
for the one place gtd spawns a subprocess itself), with the `gtd step` capture
|
|
838
|
+
guards (`src/StepGuards.ts`) built on top of them.
|
|
839
|
+
|
|
840
|
+
`src/testing/` holds the in-memory git/config/filesystem test double every
|
|
841
|
+
`@inmem` scenario and unit test runs against instead of a real repo — it never
|
|
842
|
+
ships (a lint rule and a build-time check both enforce that) and is trustworthy
|
|
843
|
+
only because the same `GitOperations` contract suite runs against a real git
|
|
844
|
+
repo too.
|
|
821
845
|
|
|
822
846
|
Releases are automatic: push releasable Conventional Commits (`fix:`, `feat:`,
|
|
823
847
|
or breaking changes) to `main` and semantic-release computes the next version,
|
package/bin/gtd
CHANGED
|
@@ -513,7 +513,7 @@ reported_head="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
|
513
513
|
# We peek with the pure, mutation-free `gtd next --json` and step ONLY when it
|
|
514
514
|
# reports a human rest (`kind == "message"`): at an agent/check rest
|
|
515
515
|
# `gtd step human` would refuse out-of-turn (non-zero, aborting under `set -e`),
|
|
516
|
-
# and a mid-
|
|
516
|
+
# and a mid-process restart must simply resume driving, not fail. A clean gate is
|
|
517
517
|
# a harmless no-op; a genuine refusal there (a malformed steering file, or an
|
|
518
518
|
# edit no `on` pattern accepts) is surfaced and stops the loop rather than being
|
|
519
519
|
# driven past. --once treats a captured commit here as the run's one beat and
|