@pmelab/gtd 8.4.0 → 8.6.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 +212 -58
- package/bin/gtd +184 -21
- package/dist/gtd.bundle.mjs +4436 -2966
- 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,17 +224,29 @@ 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
|
-
validate
|
|
234
|
-
|
|
235
|
-
|
|
230
|
+
validate Print the script that formats (when declared) then
|
|
231
|
+
validates the resolved rest's steering file, using its
|
|
232
|
+
mode's commands (its file:/mode:), instead of running it —
|
|
233
|
+
a driver runs the script and reads the findings from its
|
|
234
|
+
own exit code/output. Always exits 0; --json emits
|
|
235
|
+
{state, file?, mode?, script} (script is "" when there is
|
|
236
|
+
nothing to validate; plain text prints "nothing to
|
|
237
|
+
validate" in that case)
|
|
236
238
|
lsp Start the LSP server for .gtd/ steering files (stdio)
|
|
237
239
|
visualize Serve an interactive diagram of the active workflow on a
|
|
238
240
|
local web server (--port <n>, --no-open; --json prints the
|
|
239
241
|
model and exits)
|
|
242
|
+
check <mode> <file>
|
|
243
|
+
Read <file> and run the built-in steering format named
|
|
244
|
+
<mode> (see `gtd validate`'s modes: qa, review) over its
|
|
245
|
+
contents, printing each finding one per line and exiting
|
|
246
|
+
non-zero when there are any. Resolves no workflow state and
|
|
247
|
+
reads no config — standalone, runnable from any directory
|
|
248
|
+
with <mode>/<file> given explicitly. This is what a
|
|
249
|
+
workflow's emitted validation script invokes as a leaf step
|
|
240
250
|
version Print version and exit
|
|
241
251
|
help Print this help and exit
|
|
242
252
|
|
|
@@ -255,9 +265,6 @@ Options:
|
|
|
255
265
|
(with --entry; repeatable) supply a fixed it.vars
|
|
256
266
|
override for the new process; the name must already be
|
|
257
267
|
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
268
|
--version, -v Print version and exit
|
|
262
269
|
--help, -h Print this help and exit
|
|
263
270
|
```
|
|
@@ -312,9 +319,16 @@ envelope on **stdout**, and still exits 1:
|
|
|
312
319
|
{ "state": "error", "prompt": "<message>" }
|
|
313
320
|
```
|
|
314
321
|
|
|
322
|
+
This covers every failure mode, not just a command's own refusal: a **usage
|
|
323
|
+
error** (an unknown flag, a missing argument, `gtd --entry version`'s "not an
|
|
324
|
+
enterable state") and a **defect** (a layer throwing outside the ordinary error
|
|
325
|
+
channel) both get the same envelope — there is no failure path that reaches
|
|
326
|
+
`--json` without one.
|
|
327
|
+
|
|
315
328
|
A human-readable `gtd: <message>` line is still written to **stderr** regardless
|
|
316
329
|
of `--json` — the envelope adds a structured stdout channel, it does not replace
|
|
317
|
-
the plain-text one.
|
|
330
|
+
the plain-text one. Stderr always carries exactly one `gtd: ` prefix: a message
|
|
331
|
+
already authored with its own `gtd:`/`gtd <cmd>:` prefix is never doubled.
|
|
318
332
|
|
|
319
333
|
## Driving the loop
|
|
320
334
|
|
|
@@ -453,7 +467,7 @@ A few things to know before relying on it:
|
|
|
453
467
|
owns a `herdr:claude` session until the process exits; while it does, the
|
|
454
468
|
wrapper's reports are silently ignored and the sidebar stops tracking `gtd`.
|
|
455
469
|
- **`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
|
|
470
|
+
human gate (it waits for you to write a steering file), so a finished process
|
|
457
471
|
reads as "your turn", which is what it is.
|
|
458
472
|
- The status **persists after the wrapper exits** — that's the point: the
|
|
459
473
|
sidebar keeps showing which worktree is waiting on you. Hand the pane back to
|
|
@@ -468,6 +482,123 @@ Optionally,
|
|
|
468
482
|
sets a value renderable as `$summary` in an Agent sidebar row, if you want more
|
|
469
483
|
than the state itself.
|
|
470
484
|
|
|
485
|
+
## Writing your own driver
|
|
486
|
+
|
|
487
|
+
`bin/gtd` is gtd's own reference driver, not a privileged one — the engine
|
|
488
|
+
itself is a supported public surface, and anything below holds for any driver
|
|
489
|
+
you write against it. gtd decides and prints; it never touches git itself. The
|
|
490
|
+
four commands that change anything — `gtd step [<actor>]` (including
|
|
491
|
+
`--entry <state>`), `gtd abandon`, and `gtd restore` — perform no git write when
|
|
492
|
+
run. Instead, under `--json`, each one's output object carries two extra string
|
|
493
|
+
fields, `required` and `optional`: bash scripts for YOU to execute. (Plain-text
|
|
494
|
+
output, without `--json`, never shows these — it prints only the human-facing
|
|
495
|
+
result line, e.g. `committed: <subject>`, so a plain-text caller never sees a
|
|
496
|
+
script at all.) A driver that only prints gtd's plain-text output is not driving
|
|
497
|
+
anything; to actually land a turn, run `gtd ... --json`, pull `required`/
|
|
498
|
+
`optional` out with `jq`, and execute them.
|
|
499
|
+
|
|
500
|
+
- **`required`** is everything that decides what lands in git — closing an open
|
|
501
|
+
review checkout window, the resting state's own steering-mode
|
|
502
|
+
`format:`/`validate:` commands, then the commit or squash itself (`gtd step`
|
|
503
|
+
and `gtd --entry <state>`), or the ref update and reset that undo a process
|
|
504
|
+
(`gtd abandon`, `gtd restore`). Run this one. Skipping it means the turn never
|
|
505
|
+
lands.
|
|
506
|
+
- **`optional`** is presentation only: re-opening the review checkout window
|
|
507
|
+
(the `<<<<<<< HEAD` diff view) after a `gtd step` lands at a
|
|
508
|
+
`reviewWindow: true` state, so an editor's diff view has something to show.
|
|
509
|
+
Skip it and you lose nothing but that view — the workflow is still driven
|
|
510
|
+
correctly. `gtd abandon`/`gtd restore` always emit `optional: ""` (there is no
|
|
511
|
+
window to reopen after either).
|
|
512
|
+
|
|
513
|
+
Either field may be the empty string `""`, meaning "nothing to do here" — a
|
|
514
|
+
no-op `gtd step` (a clean tree matching no `on` pattern) emits
|
|
515
|
+
`required: ""`/`optional: ""` and exits zero; run nothing and move on. Both
|
|
516
|
+
scripts are self-contained (each carries its own precondition assert and retry
|
|
517
|
+
helper) and safe to run standalone, in sequence, or not at all — paste either
|
|
518
|
+
into a terminal and it does exactly what it says.
|
|
519
|
+
|
|
520
|
+
### Failure taxonomy and recovery
|
|
521
|
+
|
|
522
|
+
Two different things can go wrong, and they mean different things:
|
|
523
|
+
|
|
524
|
+
- **`gtd` itself exits non-zero.** Nothing was attempted — this is a refusal (a
|
|
525
|
+
guard rejected the turn) or a usage error. No script was ever produced.
|
|
526
|
+
- **An emitted script exits non-zero when YOU run it.** Something may have
|
|
527
|
+
partially happened — e.g. a `gtd_retry`-wrapped git write landed but a later
|
|
528
|
+
step in the same script failed.
|
|
529
|
+
|
|
530
|
+
Recovery is the same in both cases: **re-invoke `gtd`.** It re-reads the real
|
|
531
|
+
repository state fresh every time — never a cached plan — and emits whatever
|
|
532
|
+
still needs to happen from there. This works because every emitted script opens
|
|
533
|
+
by asserting its own precondition
|
|
534
|
+
(`[ "$(git rev-parse --verify --quiet HEAD 2>/dev/null)" = <expected> ] || { ...; exit 1; }`,
|
|
535
|
+
and the same shape for a review window's saved ref — see `src/Emit.ts`'s
|
|
536
|
+
`headAssertion`/`reviewWindowAssertion`), so a script generated against a
|
|
537
|
+
repository state that has since moved refuses loudly instead of corrupting
|
|
538
|
+
anything. In a repository with no commits yet, `<expected>` is the empty string
|
|
539
|
+
— the deciding read's own convention for "no commits yet" — so the first
|
|
540
|
+
workflow commit is allowed to land instead of being blocked by an unborn `HEAD`.
|
|
541
|
+
**Emitted scripts are re-runnable**: this is the single most important property
|
|
542
|
+
for a driver's recovery logic. Re-running a script that already fully applied is
|
|
543
|
+
a no-op (its git writes are `--allow-empty` commits, idempotent ref updates, and
|
|
544
|
+
tolerant staged-restore calls), and re-running one that only partially applied
|
|
545
|
+
resumes correctly, because the precondition either still holds (nothing landed
|
|
546
|
+
yet — safe to retry verbatim) or gtd's next invocation reads the new real state
|
|
547
|
+
and emits a fresh script for what remains. A driver never needs its own
|
|
548
|
+
retry/resume logic beyond "if the script failed, ask gtd again."
|
|
549
|
+
|
|
550
|
+
### Prerequisites
|
|
551
|
+
|
|
552
|
+
- **`jq`** — to pull the `required`/`optional` strings back out of gtd's
|
|
553
|
+
`--json` output.
|
|
554
|
+
- **`gtd` on `PATH`** — a mode's seeded `validate:` command (the one the
|
|
555
|
+
compiler fills in for the built-in `qa`/`review` formats) is literally the
|
|
556
|
+
string `gtd check <mode> '<file>'`, invoked by NAME from inside an emitted
|
|
557
|
+
script, not by absolute path (see `src/SteeringFormats.ts`'s
|
|
558
|
+
`seededValidateCommand`). This is a deliberate trade: a readable, overridable,
|
|
559
|
+
copy-pasteable command in exchange for depending on shell name resolution at
|
|
560
|
+
the moment the script runs. The sharp edge: if the `gtd` binary you invoked to
|
|
561
|
+
GENERATE the script differs from the `gtd` that resolves on `PATH` when the
|
|
562
|
+
script later RUNS (a locally-built dev binary vs. a globally-installed
|
|
563
|
+
release, say), you can get version skew between the two — the command that
|
|
564
|
+
validates may not be the command that decided. Keep the two in sync (one `gtd`
|
|
565
|
+
on `PATH`, consistently) if you care about that gap.
|
|
566
|
+
|
|
567
|
+
### The normalization-only contract on `format:`
|
|
568
|
+
|
|
569
|
+
A mode's `format:` command may reformat a steering file — whitespace, wrapping,
|
|
570
|
+
reordering — but must NEVER change what a step-capture guard would decide. gtd's
|
|
571
|
+
guards (the review sign-off check, the feedback-progress check, the
|
|
572
|
+
answer-completeness check — `src/StepGuards.ts`) decide ONCE, against whichever
|
|
573
|
+
bytes are on disk at the moment `gtd step` runs, which may be before OR after an
|
|
574
|
+
emitted script's own `format:` line has run (the script runs `format:` then
|
|
575
|
+
`validate:` then the commit — see `src/SteeringMode.ts`'s
|
|
576
|
+
`renderSteeringCommands` — but gtd's decision and the driver's script execution
|
|
577
|
+
are different processes at different times, so there's no guaranteed ordering
|
|
578
|
+
between "gtd decided" and "the script formatted"). That's only safe because
|
|
579
|
+
every built-in guard judges content, not incidental formatting (e.g. it
|
|
580
|
+
normalizes `[ ]`/`[x]` checkboxes before comparing). If you plug in your own
|
|
581
|
+
`format:` command, the same rule binds it: a formatter that also changes meaning
|
|
582
|
+
— ticking a box, stripping a paragraph — makes the guard's decision and the
|
|
583
|
+
file's actual content disagree, and gtd will not catch that for you.
|
|
584
|
+
|
|
585
|
+
### Built-in steering formats are ordinary modes
|
|
586
|
+
|
|
587
|
+
`qa` and `review` are gtd's two built-in steering-file formats (parsed and
|
|
588
|
+
validated in-process because `gtd lsp` needs the same parsers for live
|
|
589
|
+
diagnostics), but their `validate:` is not hardcoded or hidden: the compiler
|
|
590
|
+
SEEDS every workflow's `modes:` map with `qa`/`review` entries whose `validate:`
|
|
591
|
+
is the same `gtd check <mode> '<file>'` string described above
|
|
592
|
+
(`src/PatternConfig.ts`, `src/SteeringFormats.ts`'s `seededValidateCommand`).
|
|
593
|
+
That seeded command is visible in `gtd visualize`'s compiled model and in the
|
|
594
|
+
editor JSON schema like any other mode, and it's overridable the same way any
|
|
595
|
+
mode is — declare `modes: { qa: { validate: "your-own-command" } }` (in the
|
|
596
|
+
workflow or in `.gtdrc`) and your command displaces the seed; declaring only a
|
|
597
|
+
`format:` for `qa`/`review` composes with the seeded `validate:` rather than
|
|
598
|
+
replacing it. There is no special-cased built-in behavior a driver needs to know
|
|
599
|
+
about beyond the ordinary mode-resolution rules already documented under
|
|
600
|
+
[The `workflow:` key](#the-workflow-key).
|
|
601
|
+
|
|
471
602
|
## Configuration
|
|
472
603
|
|
|
473
604
|
gtd reads an optional `.gtdrc` config file via
|
|
@@ -527,7 +658,7 @@ workflow:
|
|
|
527
658
|
anyKey: anyScalarValue
|
|
528
659
|
modes: # optional — steering-file modes a state's `mode:` may name
|
|
529
660
|
<name>:
|
|
530
|
-
format: <shell command> #
|
|
661
|
+
format: <shell command> # both optional — {} is the format-only tier
|
|
531
662
|
validate: <shell command>
|
|
532
663
|
entry:
|
|
533
664
|
default: <machine name> # which machine is the ROOT instance
|
|
@@ -553,11 +684,14 @@ workflow:
|
|
|
553
684
|
retry:
|
|
554
685
|
max: <number>
|
|
555
686
|
otherwise: <targetState>
|
|
687
|
+
label: <string> # optional, opaque display name passed through `gtd next --json`/`gtd status --json`
|
|
556
688
|
file: <string> # optional, an Eta template naming the state's steering file
|
|
557
|
-
mode: <modeName> # optional, requires "file" —
|
|
689
|
+
mode: <modeName> # optional, requires "file" — must be declared in `modes:` (qa/review are seeded for you; everything else, including prose, you declare)
|
|
558
690
|
reviewWindow: true # optional — open the review checkout window at rest here
|
|
559
691
|
reviewBase: true # optional — anchor the review window's diff base to this state's most-recent commit
|
|
560
692
|
# reviewBase: <Eta template> # OR a template — rendered (only meaningful entering via --entry) to a commitish that fixes the WHOLE PROCESS's diff base
|
|
693
|
+
requireProgress: true # optional, requires "file" — refuse a turn whose only change deletes this state's own `file:`
|
|
694
|
+
answerGate: true # optional, requires "file" — refuse a turn until every open question in the (qa-mode) `file:` is answered
|
|
561
695
|
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
696
|
<local>: { machine: <name>, with: { <param>: <value> } } # a REFERENCE — instantiates <name> as a child, qualified as `<local>.<childLocal>`
|
|
563
697
|
```
|
|
@@ -587,7 +721,7 @@ conversation across both call sites.
|
|
|
587
721
|
|
|
588
722
|
Besides `it.vars` (below), a `script`/`prompt`/`message`/`commit` template sees:
|
|
589
723
|
|
|
590
|
-
- **`it.startCommit`** — the process's diff base (the commit the current
|
|
724
|
+
- **`it.startCommit`** — the process's diff base (the commit the current process
|
|
591
725
|
started from, or the base a `--var reviewBase=<commitish>` entry resolved to).
|
|
592
726
|
- **`it.reviewBase`** — the previous review round's boundary, falling back to
|
|
593
727
|
`it.startCommit` on a first review.
|
|
@@ -607,9 +741,9 @@ model, pattern grammar, load-time rules, and how to verify a change compiles.
|
|
|
607
741
|
|
|
608
742
|
> **Upgrading from a pre-8.2 `workflow:`?** The old flat `states:` shape (with a
|
|
609
743
|
> 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
|
|
744
|
+
> longer accepted — finish or `gtd abandon` any in-flight process before
|
|
745
|
+
> upgrading, since the old and new shapes aren't compatible mid-process. Wrap
|
|
746
|
+
> your states under a single
|
|
613
747
|
> `machines: { <name>: { entry: <initial state>, states: {...} } }` and declare
|
|
614
748
|
> `entry: { default: <name> }` at the top level (moving any
|
|
615
749
|
> `reviewEntry`/`fixEntry` state to a plain per-state `entry: true` flag,
|
|
@@ -785,13 +919,18 @@ own `- [ ]` line — diagnostics for both (live as you edit), and a
|
|
|
785
919
|
|
|
786
920
|
It is config-driven via each state's `file:`/`mode:`, and falls back to basename
|
|
787
921
|
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
|
-
|
|
922
|
+
gtd's built-in steering-file FORMATS, each with its own outline/actions/
|
|
923
|
+
go-to-definition and a VALIDATOR gtd itself implements; a mode's `format:` and
|
|
924
|
+
`validate:` are shell commands a workflow (or a project's `.gtdrc`) declares for
|
|
925
|
+
itself, so you bring your own formatter and your own checkers. Overriding one of
|
|
926
|
+
`qa`/`review`'s `validate:` with a shell command does NOT lose its outline/
|
|
927
|
+
actions — those come from the FORMAT (the name), independent of who validates —
|
|
928
|
+
but `gtd lsp` never runs a shell command per keystroke, so live diagnostics
|
|
929
|
+
become one `Information` notice pointing at `gtd validate` instead of the
|
|
930
|
+
built-in findings. Any OTHER mode name — `prose` (used by the simple flow's plan
|
|
931
|
+
file), or a project's own declared name — has no built-in format, so it gets no
|
|
932
|
+
live editor support at all: `gtd validate`'s emitted script and the `gtd step`
|
|
933
|
+
gate still format and validate it like any other mode.
|
|
795
934
|
|
|
796
935
|
## Development
|
|
797
936
|
|
|
@@ -800,8 +939,10 @@ npm install
|
|
|
800
939
|
npm run dev # run from source, no build (node dev/run.mjs)
|
|
801
940
|
npm run build # tsdown → dist/gtd.bundle.mjs
|
|
802
941
|
npm test # format:check, typecheck, lint, unit + e2e tests, fallow
|
|
803
|
-
npm run test:unit
|
|
804
|
-
npm run test:e2e
|
|
942
|
+
npm run test:unit # vitest unit tests (the pure resolver) — --project unit
|
|
943
|
+
npm run test:e2e # gherkin e2e, both tiers — test:e2e:inmem + test:e2e:live
|
|
944
|
+
npm run test:e2e:inmem # @inmem scenarios, in-process, no build — --project e2e-inmem
|
|
945
|
+
npm run test:e2e:live # @live scenarios against the built bundle — --project e2e-live
|
|
805
946
|
npm run test:mutation # StrykerJS mutation testing (manual only, ~10 min)
|
|
806
947
|
npm run typecheck
|
|
807
948
|
npm run lint
|
|
@@ -817,7 +958,20 @@ The decision core is pure and IO-free: the pattern machine's shape (states,
|
|
|
817
958
|
patterns, retry) is a plain-data `WorkflowDefinition` (`src/PatternMachine.ts`),
|
|
818
959
|
and the same module's `resolveState`/`step` are the pure resolver/interpreter
|
|
819
960
|
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`)
|
|
961
|
+
git/filesystem/template IO is confined to the edge (`src/Edge.ts`), whose
|
|
962
|
+
`currentRest` resolves everything derivable from where the process rests right
|
|
963
|
+
now into a single snapshot, and whose `planStep`/`planEntry` pair separates
|
|
964
|
+
deciding a transition from performing it — so a command's own capture guards run
|
|
965
|
+
between the two. The edge reaches the outside world through two content ports
|
|
966
|
+
(`src/RepoFiles.ts` for working-tree and committed reads, `src/CommandRunner.ts`
|
|
967
|
+
for the one place gtd spawns a subprocess itself), with the `gtd step` capture
|
|
968
|
+
guards (`src/StepGuards.ts`) built on top of them.
|
|
969
|
+
|
|
970
|
+
`src/testing/` holds the in-memory git/config/filesystem test double every
|
|
971
|
+
`@inmem` scenario and unit test runs against instead of a real repo — it never
|
|
972
|
+
ships (a lint rule and a build-time check both enforce that) and is trustworthy
|
|
973
|
+
only because the same `GitOperations` contract suite runs against a real git
|
|
974
|
+
repo too.
|
|
821
975
|
|
|
822
976
|
Releases are automatic: push releasable Conventional Commits (`fix:`, `feat:`,
|
|
823
977
|
or breaking changes) to `main` and semantic-release computes the next version,
|