@pmelab/gtd 4.0.0 → 5.0.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 +7 -7
- package/bin/gtd-loop +10 -4
- package/dist/gtd.bundle.mjs +9 -40
- package/docs/cli.md +9 -17
- package/docs/configuration.md +7 -5
- package/docs/loop.md +13 -11
- package/package.json +1 -1
- package/schema.json +1 -1
package/README.md
CHANGED
|
@@ -56,22 +56,22 @@ schema stub on first run (see [Configuration](docs/configuration.md#auto-init)).
|
|
|
56
56
|
gtd is a small **pattern machine**: named states, each awaiting one actor and
|
|
57
57
|
carrying one piece of content (a script, a prompt, a message, or a squash commit
|
|
58
58
|
template), with an ordered set of change-patterns routing to the next state.
|
|
59
|
-
|
|
59
|
+
Three commands drive it:
|
|
60
60
|
|
|
61
61
|
- **`gtd step <actor>`** — authenticate as `<actor>` and perform the one
|
|
62
62
|
transition the pending changes match.
|
|
63
63
|
- **`gtd next [--json]`** — print whichever actor is awaited and what they
|
|
64
64
|
should do, without mutating anything.
|
|
65
|
-
- **`gtd run`** — execute the awaited script (the only place gtd itself spawns a
|
|
66
|
-
subprocess), then step its actor.
|
|
67
65
|
- **`gtd status`** — a dry-run report of the resolved state and which pattern
|
|
68
66
|
each pending change matches.
|
|
69
67
|
|
|
70
68
|
The loop is one beat, repeated: run `gtd next --json` and dispatch on `kind` —
|
|
71
|
-
`"message"` means it's a human's move (stop and hand off); `"script"` means
|
|
72
|
-
|
|
73
|
-
`gtd step <actor>` once it's done.
|
|
74
|
-
|
|
69
|
+
`"message"` means it's a human's move (stop and hand off); `"script"` means the
|
|
70
|
+
driver runs `content` itself, then steps its actor; `"prompt"` means feed
|
|
71
|
+
`content` to your agent, then run `gtd step <actor>` once it's done. gtd itself
|
|
72
|
+
never executes anything — the driver owns running scripts. See
|
|
73
|
+
[STATES.md](STATES.md) for the model and [Driving the loop](docs/loop.md) for
|
|
74
|
+
the full protocol.
|
|
75
75
|
|
|
76
76
|
Along the way, the bundled default workflow develops your sketch into an
|
|
77
77
|
implementation plan — asking any open question it can't settle itself via a
|
package/bin/gtd-loop
CHANGED
|
@@ -4,9 +4,10 @@ set -euo pipefail
|
|
|
4
4
|
# Drives the v3 pattern-machine loop (see docs/loop.md): each iteration asks
|
|
5
5
|
# `gtd next --json` who's up and what they should do, dispatching on `kind`:
|
|
6
6
|
# - "message" (a human rest) -> halt, tell the user what gtd is waiting on.
|
|
7
|
-
# - "script" (a check rest) ->
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# - "script" (a check rest) -> execute `content` (the emitted wrapper
|
|
8
|
+
# script) verbatim via bash — exit code deliberately ignored, the outcome
|
|
9
|
+
# lives in the tree — then `gtd step <actor>` to capture it; zero new
|
|
10
|
+
# commits = green with nothing owed (the terminal signal at idle).
|
|
10
11
|
# - "prompt" (an agent rest) -> feed `content` to the agent, run the
|
|
11
12
|
# self-validation gate (`gtd validate`, re-prompting on findings — see the
|
|
12
13
|
# gate's own comment below), then `gtd step <actor>` ourselves — the
|
|
@@ -86,7 +87,12 @@ while true; do
|
|
|
86
87
|
|
|
87
88
|
if [[ "$kind" == "script" ]]; then
|
|
88
89
|
head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
89
|
-
|
|
90
|
+
# Execute the emitted wrapper verbatim; its exit code is deliberately
|
|
91
|
+
# ignored (`|| true` under `set -e`) — the script encodes its outcome in
|
|
92
|
+
# the tree, and `gtd step` captures whatever it left there via the state's
|
|
93
|
+
# own `on` patterns.
|
|
94
|
+
bash -c "$content" || true
|
|
95
|
+
gtd step "$actor" >/dev/null
|
|
90
96
|
head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
91
97
|
if [[ "$head_before" == "$head_after" ]]; then
|
|
92
98
|
echo "--- Settled ($state: check passed, nothing to do) ---"
|
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -190028,7 +190028,7 @@ const compileWorkflowConfig = (raw, configDir, rcModes) => {
|
|
|
190028
190028
|
* default.yaml's content values start with `./`/`../`, so
|
|
190029
190029
|
* `compileWorkflowConfig` never resolves a file reference against it.
|
|
190030
190030
|
*/
|
|
190031
|
-
const compiled = compileWorkflowConfig((0, import_dist$1.parse)("# The bundled default gtd workflow (v3 \"pattern machine\" — see\n# docs/design/pattern-machine-plan.md, and\n# docs/design/steering-file-validation-command.md for the `gtd validate`\n# self-validation model that replaced the old in-machine validation loops).\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/default.ts does the compiling).\n#\n# Every content string below is INLINE (no `./`-relative file references):\n# the bundled default ships inside a single-file build\n# (`dist/gtd.bundle.mjs`), so its content must already be resolved at BUILD\n# time rather than read from disk at RUNTIME — the same reason\n# `src/prompts/*.md` are imported through tsdown's `.md`-as-text loader\n# instead of read via `fs` at runtime. `default.yaml` itself is imported the\n# same way (tsdown/vitest both treat `.yaml` as text — see\n# tsdown.config.ts / tests/vitest.rawMd.ts), so this file never needs a\n# sibling prompts/ directory to exist on disk once installed.\n#\n# States, in cycle order — 10 states, no squash:\n# idle (initial, human, message)\n# -> grilling (agent, prompt): reads .gtd/TODO.md's sketch (or a prior\n# lap's feedback), explores the codebase, and develops it into a concrete\n# implementation plan, stating any open question it can't settle itself\n# under a `## Open Questions` heading rather than guessing silently. Its\n# `file:`/`mode:` (TODO.md/`qa`) makes the plan's format checkable — it\n# self-validates before finishing (see \"Steering-file validation\" below).\n# grilling -> grilling-answer (human, message): tick off suggested\n# defaults by replacing them with `Answer:` lines; a clean step accepts\n# every remaining default -> building; any edit loops back to grilling.\n# grilling-answer -> building (agent, prompt): implements the plan in\n# .gtd/TODO.md directly (no task decomposition, no per-task queue) and\n# deletes .gtd/TODO.md once the work is complete and verified.\n# building -> checking (check, script): runs the test command inline;\n# A/M .gtd/FEEDBACK.md -> fixing (capped, redirects to escalate); D\n# .gtd/FEEDBACK.md (a green run that just cleaned up prior feedback) or C\n# (green, nothing to clean) -> reviewing.\n# fixing (agent, prompt) <-> checking\n# escalate (human, message): manual intervention, then back to checking\n# checking (green) -> reviewing (agent, prompt): writes .gtd/REVIEW.md in\n# the checkbox review format, grouping the cycle's full diff into\n# reviewable chunks; its `file:`/`mode:` (REVIEW.md/`review`) makes that\n# format checkable, so it self-validates before finishing.\n# reviewing -> await-review (human, message): tick a pointer's `- [\n# ]` to `- [x]` to approve that item; deleting .gtd/REVIEW.md outright is\n# the power-user approve-everything shortcut; anything else pending\n# (code edits with REVIEW.md untouched) is feedback straight to grilling.\n# await-review -> review-deciding (check, script), entered by any `M\n# .gtd/REVIEW.md` step: if no unticked `- [ ]` pointer remains, the cycle\n# is approved (rm .gtd/REVIEW.md -> idle); otherwise the still-unticked\n# pointers (with their notes) are extracted into a fresh .gtd/TODO.md and\n# .gtd/REVIEW.md is removed (-> grilling, feedback lap).\n#\n# The cycle ends at human approval, resting back at idle — there is no\n# squash state. The cycle's commits stay in history; whether/how to squash\n# them (amend, interactive rebase, a PR's squash-merge) is entirely the\n# human's business once they're done reviewing — gtd makes no assumption.\n# That squash-flavored finale (a `squashing` prompt state + a `done` commit\n# state) still exists as an ENGINE capability (see STATES.md §8) and is kept\n# as part of the fuller machine at docs/examples/advanced-workflow.md.\n#\n# A `gtd(<actor>): idle` commit — entering the workflow's own initial state —\n# is therefore a PROCESS BOUNDARY (see `computeProcessRun` in src/Edge.ts):\n# the next cycle's `retry` counts, `startCommit`, and diffs never reach back\n# across an approved cycle's own idle-entering commit.\n#\n# HYGIENE INVARIANT (asserted in e2e — see default-workflow.feature): an\n# approved cycle leaves `.gtd/` empty. FEEDBACK.md is cleaned up by a green\n# `checking` run; REVIEW.md by the review-deciding approval branch; TODO.md by\n# `building`.\n#\n# Steering-file validation. `grilling` and `reviewing` each declare a\n# `file:`/`mode:` pair (TODO.md as `qa`, REVIEW.md as `review`), so their\n# output has a checkable format. src/OpenQuestions.ts and src/ReviewDoc.ts are\n# those two formats' EXECUTABLE SPECS (their unit tests are the format spec\n# tests), and `gtd validate` FORMATS the resolved state's rendered `file:` in\n# place then runs the SAME parser over it, exiting non-zero with findings when\n# it violates the format. There is no in-machine validation state and no bash\n# port to keep in sync — the producing agent self-validates before finishing\n# (plain `gtd next` appends the \"run `gtd validate` and fix all violations\"\n# instruction; under `--json` the driving loop runs it after the turn and\n# re-prompts on findings — see skills/loop/SKILL.md), AND `gtd step` runs the\n# same format-and-validate gate on capture, refusing a turn whose steering file\n# is invalid. So the check runs after a human edits too — at the human gates\n# `grilling-answer` (TODO.md) and `await-review` (REVIEW.md) — and a human gate\n# is only ever handed a tidy, well-formed file. The same parsers back the LSP's\n# live diagnostics (src/Lsp.ts).\n#\n# `grilling` and `reviewing` both declare `model: smart` — an opaque harness\n# hint (see the comment at `grilling` below) for their heavier one-shot\n# planning/reviewing turns. Every other state is unset (harness default).\n#\n# The four agent states each declare a `memory:` scope label (`plan`,\n# `build`, `fix`, `review` — see the comment at `grilling` below) — another\n# opaque hint a memory-aware driver reads to decide when an agent turn keeps\n# the previous turn's memory and when it starts fresh: a loop that re-enters\n# one state (grilling around its answer loop, fixing around checking) keeps\n# the same label and retains memory across its laps; crossing to the next\n# phase's differently-labelled state clears it. gtd never interprets the\n# label (see STATES.md §1 and skills/loop/SKILL.md's driver contract).\n#\n# `vars.testCommand` is this workflow's own declared default for `checking`'s\n# script (see it.vars in PatternTemplates.ts) — override it per-repo with a\n# top-level `.gtdrc` `vars: { testCommand: ... }` key, or per-invocation with\n# a `GTD_VAR_testCommand` environment variable (highest precedence).\n#\n# `vars.todoFile`/`reviewFile`/`feedbackFile` are this workflow's ONE source\n# of truth for the three steering-file paths every prompt/script below reads\n# or writes — each appears in templates as `<%~ it.vars.todoFile %>` etc\n# (see docs/design/state-file-association.md §2). Every state that rests on\n# one of these files also declares a `file:`/`mode:` pair (see STATES.md's\n# property table) so `gtd status`/`gtd next --json` and the LSP (`gtd lsp`)\n# know which file a human/editor should look at, and in what format\n# (`qa` for TODO.md's open-questions format, `review` for REVIEW.md's\n# checkbox format) — `fixing`/`escalate` declare `file:` alone (FEEDBACK.md\n# is plain text, no LSP format). KNOWN LIMITATION: `on` pattern keys below are\n# NOT Eta templates — they keep the LITERAL `.gtd/...` paths matching these\n# vars' DEFAULT values. Repointing `todoFile`/`reviewFile`/`feedbackFile` via\n# `.gtdrc`'s top-level `vars:` or a `GTD_VAR_` override changes what `file:`\n# renders to (and what a template reads/writes) WITHOUT changing what the\n# `on` patterns match against — desyncing the machine. See\n# docs/configuration.md's `file:`/`mode:` section for the full caveat.\n#\n# A fuller machine — two-phase Q&A planning, an architecture phase, task\n# decomposition, a deterministic per-task builder loop, agent-prepared\n# review, and the squash finale (`squashing` + `done`) — is preserved as a\n# copy-paste `.gtdrc` example at docs/examples/advanced-workflow.md.\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n reviewFile: .gtd/REVIEW.md\n feedbackFile: .gtd/FEEDBACK.md\n\nstates:\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the SAME rendered block —\n # a \"What each change does next\" list built from `it.edges`, the resting\n # state's own `on` edges (see PatternTemplates.TemplateContext.edges). Each\n # edge's human-readable route lives in its `describe:` (the single source\n # of truth), so the list can never drift from the routing it documents.\n # The block skips edges without a `describe`, and each `<%~ ... \"\\n\" %>`\n # keeps the bullet's own newline (Eta's autoTrim strips the template's own\n # line breaks around `<% %>` tags, not text inside an interpolation).\n message: |\n No active gtd cycle.\n\n To start one, write `.gtd/TODO.md` with a short sketch of what you want\n built — a few sentences is enough.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: grilling\n describe: >-\n Save your sketch to `.gtd/TODO.md` (any working-tree change) to start\n the cycle — the agent reads it, explores the codebase, and develops it\n into a concrete implementation plan (**grilling**).\n\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # `model` is an opaque harness hint — gtd never interprets this string, it\n # only passes it through to `gtd next --json`/`gtd status --json` so the\n # driving loop can map it onto whatever models its agent harness\n # provides. \"smart\" here just names the harness-chosen tier for the\n # heavier planning turn; states without a `model` use the harness's\n # default.\n model: smart\n # `memory` is an opaque memory-scope label — gtd never interprets it, it\n # only passes it through to `gtd next --json`/`gtd status --json` so a\n # memory-aware driving loop can decide when an agent turn continues from\n # the previous turn's memory and when it starts fresh: consecutive agent\n # turns that emit the SAME label share memory, a change (or the first\n # turn) is where the driver starts fresh. `grilling` re-enters itself\n # around the grilling-answer loop, so labelling every lap\n # `plan` keeps the planner's accumulated exploration across the loop; the\n # next phase (`building`) carries a different label, clearing it.\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.todoFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete implementation plan.\n\n For every question you cannot settle yourself, add it under a\n `## Open Questions` heading, one `### <question>` sub-heading each,\n whose body's FIRST non-blank line is exactly\n `Suggested default: <your answer>` — the human can accept it as-is or\n override it. Omit the section entirely once there are no open\n questions. If `<%= it.vars.todoFile %>` already carries\n `## Open Questions` entries from a previous lap, fold any\n `Answer: <text>` line into the plan; every remaining entry's body must\n still start with either `Suggested default: ...` or `Answer: ...` —\n never anything else.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd step agent` yourself; the harness does\n that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n default. Answer a question by editing its entry in place: replace its\n `Suggested default: ...` line with `Answer: ...`, or add further detail.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: building\n describe: >-\n Change nothing to accept every suggested default as-is and move on to\n implementation (**building**).\n \"* **\":\n to: grilling\n describe: >-\n Edit the plan — answer a question, add detail, or raise a new one — to\n send it back for another round of grilling (**grilling**).\n\n building:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # A distinct memory scope from `plan`: implementation starts fresh, with\n # the plan already settled in `.gtd/TODO.md` (which this prompt reads\n # below) rather than carried in the planner's working memory.\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting\n `<%= it.vars.todoFile %>`, which this prompt tells you to delete once\n its work is complete.\n\n The plan to implement is: <%~ it.read(it.vars.todoFile) %>\n\n Read that plan and implement it in full. Use TDD discipline: one test,\n then the implementation that passes it, then the next — never write\n all the tests first.\n\n Delete `<%= it.vars.todoFile %>` once the work it describes is complete\n and verified. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the driver (`gtd run`) executes this verbatim, then\n # steps the check actor. Mechanics only: run the test suite and record a\n # red run's output as .gtd/FEEDBACK.md. What that output MEANS (another\n # fix round vs escalation) is decided by the workflow's own `on`/`retry`\n # rules at capture time — never here.\n #\n # `it.vars.testCommand` defaults to \"npm test\" (this workflow's own\n # `vars:` above) — override it with a top-level `.gtdrc`\n # `vars: { testCommand: ... }` key, or a `GTD_VAR_testCommand`\n # environment variable (highest precedence).\n #\n # A green run also removes any `<%= it.vars.feedbackFile %>` left over\n # from an earlier red run — there is no squash to discard it for us\n # anymore, so the script cleans it up itself before moving on to\n # review.\n set +e\n mkdir -p .gtd\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output <%~ it.vars.feedbackFile %>\n else\n rm -f .gtd/.check-output\n printf 'npm test failed with exit code %s and produced no output.' \"$code\" > <%~ it.vars.feedbackFile %>\n fi\n else\n rm -f .gtd/.check-output\n rm -f <%~ it.vars.feedbackFile %>\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n # Its own memory scope: the fix loop re-enters `fixing` around `checking`\n # (up to the retry cap), so labelling every attempt `fix` lets the agent\n # remember what it already tried across the loop; a green check moves on\n # to the differently-labelled `reviewing`, clearing it.\n memory: fix\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix\n the code so the suite passes. Keep the change focused — do not\n refactor unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue; the\n machine picks the dispute up either way.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: checking\n describe: >-\n Fix the failure yourself — edit the code and/or `.gtd/FEEDBACK.md` —\n to re-run the check (**checking**).\n\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: smart\n # A distinct memory scope again: the reviewer looks at the cycle's full\n # diff (inlined into its prompt) with a fresh perspective, not the\n # builder's or fixer's working memory.\n memory: review\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the cycle's\n full diff (inlined below) in this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.startCommit %> -->`\n - At least one `## <Chunk Title>` heading, grouping hunks semantically\n (same feature/refactor/fix, even across files) — a short explanation\n of what changed and why, then one\n `- [ ] ./path/to/file.ts#42 — note` file pointer per hunk (checkboxes\n are for the human to tick, not you; every path is `./`-relative, with\n an optional `#line` and an optional ` — note`).\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the cycle rests here for human review, gtd opens a \"review checkout\n # window\": HEAD and the index are temporarily rewound to this cycle's start\n # (the process boundary — no `reviewBase` state is declared, so the base\n # defaults to it) with the working tree untouched, so the whole cycle diff\n # surfaces as ordinary uncommitted changes in the editor's git integration.\n # The window closes automatically on the next invocation, once the cycle\n # rests anywhere else (see src/ReviewWindow.ts, STATES.md §11).\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the completed\n cycle — one `- [ ]` checkbox per reviewable item, grouped into chunks.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"D .gtd/REVIEW.md\":\n to: idle\n describe: >-\n Delete `.gtd/REVIEW.md` outright to approve the whole cycle at once\n and rest back at the start (**idle**).\n \"M .gtd/REVIEW.md\":\n to: review-deciding\n describe: >-\n Edit `.gtd/REVIEW.md` — tick a `- [ ]` box to `- [x]` to approve that\n item, or add notes — to hand it to a deterministic check\n (**review-deciding**) that closes the cycle out when every box is\n ticked, or extracts the still-unticked items into a fresh\n `.gtd/TODO.md` and loops back through **grilling** with that feedback.\n \"* **\":\n to: grilling\n describe: >-\n Change only code, leaving `.gtd/REVIEW.md` untouched, to send that as\n feedback straight back to **grilling**.\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the driver (`gtd run`) executes this verbatim, then\n # steps the check actor. Mechanics only: if .gtd/REVIEW.md still has any\n # unticked \"- [ ]\" pointer, extract those pointers (with their notes)\n # into a fresh .gtd/TODO.md; either way, remove .gtd/REVIEW.md. What\n # the resulting diff MEANS (feedback lap vs. full approval) is decided\n # by this state's own `on` rules at capture time — never here. The\n # A/M .gtd/TODO.md row is declared BEFORE the D .gtd/REVIEW.md row (see\n # this workflow's `on` map below) so a feedback lap — which always also\n # deletes .gtd/REVIEW.md — is captured as feedback, not mistaken for an\n # approval.\n set +e\n mkdir -p .gtd\n if grep -qE '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %> 2>/dev/null; then\n {\n echo \"Feedback from review — address these before continuing:\"\n echo\n grep -E '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %>\n } > <%~ it.vars.todoFile %>\n fi\n rm -f <%~ it.vars.reviewFile %>\n on:\n \"A .gtd/TODO.md\": grilling\n \"M .gtd/TODO.md\": grilling\n \"D .gtd/REVIEW.md\": idle\n \"C\": await-review\n"), ".");
|
|
190031
|
+
const compiled = compileWorkflowConfig((0, import_dist$1.parse)("# The bundled default gtd workflow (v3 \"pattern machine\" — see\n# docs/design/pattern-machine-plan.md, and\n# docs/design/steering-file-validation-command.md for the `gtd validate`\n# self-validation model that replaced the old in-machine validation loops).\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/default.ts does the compiling).\n#\n# Every content string below is INLINE (no `./`-relative file references):\n# the bundled default ships inside a single-file build\n# (`dist/gtd.bundle.mjs`), so its content must already be resolved at BUILD\n# time rather than read from disk at RUNTIME — the same reason\n# `src/prompts/*.md` are imported through tsdown's `.md`-as-text loader\n# instead of read via `fs` at runtime. `default.yaml` itself is imported the\n# same way (tsdown/vitest both treat `.yaml` as text — see\n# tsdown.config.ts / tests/vitest.rawMd.ts), so this file never needs a\n# sibling prompts/ directory to exist on disk once installed.\n#\n# States, in cycle order — 10 states, no squash:\n# idle (initial, human, message)\n# -> grilling (agent, prompt): reads .gtd/TODO.md's sketch (or a prior\n# lap's feedback), explores the codebase, and develops it into a concrete\n# implementation plan, stating any open question it can't settle itself\n# under a `## Open Questions` heading rather than guessing silently. Its\n# `file:`/`mode:` (TODO.md/`qa`) makes the plan's format checkable — it\n# self-validates before finishing (see \"Steering-file validation\" below).\n# grilling -> grilling-answer (human, message): tick off suggested\n# defaults by replacing them with `Answer:` lines; a clean step accepts\n# every remaining default -> building; any edit loops back to grilling.\n# grilling-answer -> building (agent, prompt): implements the plan in\n# .gtd/TODO.md directly (no task decomposition, no per-task queue) and\n# deletes .gtd/TODO.md once the work is complete and verified.\n# building -> checking (check, script): runs the test command inline;\n# A/M .gtd/FEEDBACK.md -> fixing (capped, redirects to escalate); D\n# .gtd/FEEDBACK.md (a green run that just cleaned up prior feedback) or C\n# (green, nothing to clean) -> reviewing.\n# fixing (agent, prompt) <-> checking\n# escalate (human, message): manual intervention, then back to checking\n# checking (green) -> reviewing (agent, prompt): writes .gtd/REVIEW.md in\n# the checkbox review format, grouping the cycle's full diff into\n# reviewable chunks; its `file:`/`mode:` (REVIEW.md/`review`) makes that\n# format checkable, so it self-validates before finishing.\n# reviewing -> await-review (human, message): tick a pointer's `- [\n# ]` to `- [x]` to approve that item; deleting .gtd/REVIEW.md outright is\n# the power-user approve-everything shortcut; anything else pending\n# (code edits with REVIEW.md untouched) is feedback straight to grilling.\n# await-review -> review-deciding (check, script), entered by any `M\n# .gtd/REVIEW.md` step: if no unticked `- [ ]` pointer remains, the cycle\n# is approved (rm .gtd/REVIEW.md -> idle); otherwise the still-unticked\n# pointers (with their notes) are extracted into a fresh .gtd/TODO.md and\n# .gtd/REVIEW.md is removed (-> grilling, feedback lap).\n#\n# The cycle ends at human approval, resting back at idle — there is no\n# squash state. The cycle's commits stay in history; whether/how to squash\n# them (amend, interactive rebase, a PR's squash-merge) is entirely the\n# human's business once they're done reviewing — gtd makes no assumption.\n# That squash-flavored finale (a `squashing` prompt state + a `done` commit\n# state) still exists as an ENGINE capability (see STATES.md §8) and is kept\n# as part of the fuller machine at docs/examples/advanced-workflow.md.\n#\n# A `gtd(<actor>): idle` commit — entering the workflow's own initial state —\n# is therefore a PROCESS BOUNDARY (see `computeProcessRun` in src/Edge.ts):\n# the next cycle's `retry` counts, `startCommit`, and diffs never reach back\n# across an approved cycle's own idle-entering commit.\n#\n# HYGIENE INVARIANT (asserted in e2e — see default-workflow.feature): an\n# approved cycle leaves `.gtd/` empty. FEEDBACK.md is cleaned up by a green\n# `checking` run; REVIEW.md by the review-deciding approval branch; TODO.md by\n# `building`.\n#\n# Steering-file validation. `grilling` and `reviewing` each declare a\n# `file:`/`mode:` pair (TODO.md as `qa`, REVIEW.md as `review`), so their\n# output has a checkable format. src/OpenQuestions.ts and src/ReviewDoc.ts are\n# those two formats' EXECUTABLE SPECS (their unit tests are the format spec\n# tests), and `gtd validate` FORMATS the resolved state's rendered `file:` in\n# place then runs the SAME parser over it, exiting non-zero with findings when\n# it violates the format. There is no in-machine validation state and no bash\n# port to keep in sync — the producing agent self-validates before finishing\n# (plain `gtd next` appends the \"run `gtd validate` and fix all violations\"\n# instruction; under `--json` the driving loop runs it after the turn and\n# re-prompts on findings — see skills/loop/SKILL.md), AND `gtd step` runs the\n# same format-and-validate gate on capture, refusing a turn whose steering file\n# is invalid. So the check runs after a human edits too — at the human gates\n# `grilling-answer` (TODO.md) and `await-review` (REVIEW.md) — and a human gate\n# is only ever handed a tidy, well-formed file. The same parsers back the LSP's\n# live diagnostics (src/Lsp.ts).\n#\n# `grilling` and `reviewing` both declare `model: smart` — an opaque harness\n# hint (see the comment at `grilling` below) for their heavier one-shot\n# planning/reviewing turns. Every other state is unset (harness default).\n#\n# The four agent states each declare a `memory:` scope label (`plan`,\n# `build`, `fix`, `review` — see the comment at `grilling` below) — another\n# opaque hint a memory-aware driver reads to decide when an agent turn keeps\n# the previous turn's memory and when it starts fresh: a loop that re-enters\n# one state (grilling around its answer loop, fixing around checking) keeps\n# the same label and retains memory across its laps; crossing to the next\n# phase's differently-labelled state clears it. gtd never interprets the\n# label (see STATES.md §1 and skills/loop/SKILL.md's driver contract).\n#\n# `vars.testCommand` is this workflow's own declared default for `checking`'s\n# script (see it.vars in PatternTemplates.ts) — override it per-repo with a\n# top-level `.gtdrc` `vars: { testCommand: ... }` key, or per-invocation with\n# a `GTD_VAR_testCommand` environment variable (highest precedence).\n#\n# `vars.todoFile`/`reviewFile`/`feedbackFile` are this workflow's ONE source\n# of truth for the three steering-file paths every prompt/script below reads\n# or writes — each appears in templates as `<%~ it.vars.todoFile %>` etc\n# (see docs/design/state-file-association.md §2). Every state that rests on\n# one of these files also declares a `file:`/`mode:` pair (see STATES.md's\n# property table) so `gtd status`/`gtd next --json` and the LSP (`gtd lsp`)\n# know which file a human/editor should look at, and in what format\n# (`qa` for TODO.md's open-questions format, `review` for REVIEW.md's\n# checkbox format) — `fixing`/`escalate` declare `file:` alone (FEEDBACK.md\n# is plain text, no LSP format). KNOWN LIMITATION: `on` pattern keys below are\n# NOT Eta templates — they keep the LITERAL `.gtd/...` paths matching these\n# vars' DEFAULT values. Repointing `todoFile`/`reviewFile`/`feedbackFile` via\n# `.gtdrc`'s top-level `vars:` or a `GTD_VAR_` override changes what `file:`\n# renders to (and what a template reads/writes) WITHOUT changing what the\n# `on` patterns match against — desyncing the machine. See\n# docs/configuration.md's `file:`/`mode:` section for the full caveat.\n#\n# A fuller machine — two-phase Q&A planning, an architecture phase, task\n# decomposition, a deterministic per-task builder loop, agent-prepared\n# review, and the squash finale (`squashing` + `done`) — is preserved as a\n# copy-paste `.gtdrc` example at docs/examples/advanced-workflow.md.\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n reviewFile: .gtd/REVIEW.md\n feedbackFile: .gtd/FEEDBACK.md\n\nstates:\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the SAME rendered block —\n # a \"What each change does next\" list built from `it.edges`, the resting\n # state's own `on` edges (see PatternTemplates.TemplateContext.edges). Each\n # edge's human-readable route lives in its `describe:` (the single source\n # of truth), so the list can never drift from the routing it documents.\n # The block skips edges without a `describe`, and each `<%~ ... \"\\n\" %>`\n # keeps the bullet's own newline (Eta's autoTrim strips the template's own\n # line breaks around `<% %>` tags, not text inside an interpolation).\n message: |\n No active gtd cycle.\n\n To start one, write `.gtd/TODO.md` with a short sketch of what you want\n built — a few sentences is enough.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: grilling\n describe: >-\n Save your sketch to `.gtd/TODO.md` (any working-tree change) to start\n the cycle — the agent reads it, explores the codebase, and develops it\n into a concrete implementation plan (**grilling**).\n\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # `model` is an opaque harness hint — gtd never interprets this string, it\n # only passes it through to `gtd next --json`/`gtd status --json` so the\n # driving loop can map it onto whatever models its agent harness\n # provides. \"smart\" here just names the harness-chosen tier for the\n # heavier planning turn; states without a `model` use the harness's\n # default.\n model: smart\n # `memory` is an opaque memory-scope label — gtd never interprets it, it\n # only passes it through to `gtd next --json`/`gtd status --json` so a\n # memory-aware driving loop can decide when an agent turn continues from\n # the previous turn's memory and when it starts fresh: consecutive agent\n # turns that emit the SAME label share memory, a change (or the first\n # turn) is where the driver starts fresh. `grilling` re-enters itself\n # around the grilling-answer loop, so labelling every lap\n # `plan` keeps the planner's accumulated exploration across the loop; the\n # next phase (`building`) carries a different label, clearing it.\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.todoFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete implementation plan.\n\n For every question you cannot settle yourself, add it under a\n `## Open Questions` heading, one `### <question>` sub-heading each,\n whose body's FIRST non-blank line is exactly\n `Suggested default: <your answer>` — the human can accept it as-is or\n override it. Omit the section entirely once there are no open\n questions. If `<%= it.vars.todoFile %>` already carries\n `## Open Questions` entries from a previous lap, fold any\n `Answer: <text>` line into the plan; every remaining entry's body must\n still start with either `Suggested default: ...` or `Answer: ...` —\n never anything else.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd step agent` yourself; the harness does\n that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n default. Answer a question by editing its entry in place: replace its\n `Suggested default: ...` line with `Answer: ...`, or add further detail.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: building\n describe: >-\n Change nothing to accept every suggested default as-is and move on to\n implementation (**building**).\n \"* **\":\n to: grilling\n describe: >-\n Edit the plan — answer a question, add detail, or raise a new one — to\n send it back for another round of grilling (**grilling**).\n\n building:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # A distinct memory scope from `plan`: implementation starts fresh, with\n # the plan already settled in `.gtd/TODO.md` (which this prompt reads\n # below) rather than carried in the planner's working memory.\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting\n `<%= it.vars.todoFile %>`, which this prompt tells you to delete once\n its work is complete.\n\n The plan to implement is: <%~ it.read(it.vars.todoFile) %>\n\n Read that plan and implement it in full. Use TDD discipline: one test,\n then the implementation that passes it, then the next — never write\n all the tests first.\n\n Delete `<%= it.vars.todoFile %>` once the work it describes is complete\n and verified. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then\n # steps the check actor. Mechanics only: run the test suite and record a\n # red run's output as .gtd/FEEDBACK.md. What that output MEANS (another\n # fix round vs escalation) is decided by the workflow's own `on`/`retry`\n # rules at capture time — never here.\n #\n # `it.vars.testCommand` defaults to \"npm test\" (this workflow's own\n # `vars:` above) — override it with a top-level `.gtdrc`\n # `vars: { testCommand: ... }` key, or a `GTD_VAR_testCommand`\n # environment variable (highest precedence).\n #\n # A green run also removes any `<%= it.vars.feedbackFile %>` left over\n # from an earlier red run — there is no squash to discard it for us\n # anymore, so the script cleans it up itself before moving on to\n # review.\n set +e\n mkdir -p .gtd\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output <%~ it.vars.feedbackFile %>\n else\n rm -f .gtd/.check-output\n printf 'npm test failed with exit code %s and produced no output.' \"$code\" > <%~ it.vars.feedbackFile %>\n fi\n else\n rm -f .gtd/.check-output\n rm -f <%~ it.vars.feedbackFile %>\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n # Its own memory scope: the fix loop re-enters `fixing` around `checking`\n # (up to the retry cap), so labelling every attempt `fix` lets the agent\n # remember what it already tried across the loop; a green check moves on\n # to the differently-labelled `reviewing`, clearing it.\n memory: fix\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix\n the code so the suite passes. Keep the change focused — do not\n refactor unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue; the\n machine picks the dispute up either way.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: checking\n describe: >-\n Fix the failure yourself — edit the code and/or `.gtd/FEEDBACK.md` —\n to re-run the check (**checking**).\n\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: smart\n # A distinct memory scope again: the reviewer looks at the cycle's full\n # diff (inlined into its prompt) with a fresh perspective, not the\n # builder's or fixer's working memory.\n memory: review\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the cycle's\n full diff (inlined below) in this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.startCommit %> -->`\n - At least one `## <Chunk Title>` heading, grouping hunks semantically\n (same feature/refactor/fix, even across files) — a short explanation\n of what changed and why, then one\n `- [ ] ./path/to/file.ts#42 — note` file pointer per hunk (checkboxes\n are for the human to tick, not you; every path is `./`-relative, with\n an optional `#line` and an optional ` — note`).\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the cycle rests here for human review, gtd opens a \"review checkout\n # window\": HEAD and the index are temporarily rewound to this cycle's start\n # (the process boundary — no `reviewBase` state is declared, so the base\n # defaults to it) with the working tree untouched, so the whole cycle diff\n # surfaces as ordinary uncommitted changes in the editor's git integration.\n # The window closes automatically on the next invocation, once the cycle\n # rests anywhere else (see src/ReviewWindow.ts, STATES.md §11).\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the completed\n cycle — one `- [ ]` checkbox per reviewable item, grouped into chunks.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"D .gtd/REVIEW.md\":\n to: idle\n describe: >-\n Delete `.gtd/REVIEW.md` outright to approve the whole cycle at once\n and rest back at the start (**idle**).\n \"M .gtd/REVIEW.md\":\n to: review-deciding\n describe: >-\n Edit `.gtd/REVIEW.md` — tick a `- [ ]` box to `- [x]` to approve that\n item, or add notes — to hand it to a deterministic check\n (**review-deciding**) that closes the cycle out when every box is\n ticked, or extracts the still-unticked items into a fresh\n `.gtd/TODO.md` and loops back through **grilling** with that feedback.\n \"* **\":\n to: grilling\n describe: >-\n Change only code, leaving `.gtd/REVIEW.md` untouched, to send that as\n feedback straight back to **grilling**.\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then\n # steps the check actor. Mechanics only: if .gtd/REVIEW.md still has any\n # unticked \"- [ ]\" pointer, extract those pointers (with their notes)\n # into a fresh .gtd/TODO.md; either way, remove .gtd/REVIEW.md. What\n # the resulting diff MEANS (feedback lap vs. full approval) is decided\n # by this state's own `on` rules at capture time — never here. The\n # A/M .gtd/TODO.md row is declared BEFORE the D .gtd/REVIEW.md row (see\n # this workflow's `on` map below) so a feedback lap — which always also\n # deletes .gtd/REVIEW.md — is captured as feedback, not mistaken for an\n # approval.\n set +e\n mkdir -p .gtd\n if grep -qE '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %> 2>/dev/null; then\n {\n echo \"Feedback from review — address these before continuing:\"\n echo\n grep -E '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %>\n } > <%~ it.vars.todoFile %>\n fi\n rm -f <%~ it.vars.reviewFile %>\n on:\n \"A .gtd/TODO.md\": grilling\n \"M .gtd/TODO.md\": grilling\n \"D .gtd/REVIEW.md\": idle\n \"C\": await-review\n"), ".");
|
|
190032
190032
|
const defaultWorkflowDefinition = compiled.definition;
|
|
190033
190033
|
/** The default workflow's own declared `vars:` defaults — `{ testCommand: "npm test" }`, overridable via a top-level `.gtdrc` `vars:` key or a `GTD_VAR_testCommand` environment variable (see `src/Edge.ts`'s `resolveVars`). */
|
|
190034
190034
|
const defaultWorkflowVars = compiled.vars;
|
|
@@ -190130,7 +190130,7 @@ const workflowJsonSchema = {
|
|
|
190130
190130
|
},
|
|
190131
190131
|
script: {
|
|
190132
190132
|
type: "string",
|
|
190133
|
-
description: "Content kind: a shell script (Eta template).
|
|
190133
|
+
description: "Content kind: a shell script (Eta template). The loop driver executes it verbatim via bash, then steps this state's actor."
|
|
190134
190134
|
},
|
|
190135
190135
|
prompt: {
|
|
190136
190136
|
type: "string",
|
|
@@ -204046,8 +204046,9 @@ const resolveSteeringMode = (def, mode) => {
|
|
|
204046
204046
|
const unknownModeMessage = (def, state, mode) => `state "${state}": mode "${mode}" is not defined by the active workflow (known modes: ${knownModes(def).join(", ")})`;
|
|
204047
204047
|
/**
|
|
204048
204048
|
* Run one rendered mode command through `bash -c` in `cwd`, capturing output
|
|
204049
|
-
* instead of inheriting the terminal (unlike
|
|
204050
|
-
* output IS the point): here the output is
|
|
204049
|
+
* instead of inheriting the terminal (unlike a workflow script, which the driver
|
|
204050
|
+
* runs with inherited stdio because its output IS the point): here the output is
|
|
204051
|
+
* data — a validate command's findings
|
|
204051
204052
|
* are reported to the agent that must fix them. A spawn failure (no `bash`, an
|
|
204052
204053
|
* unreadable cwd) fails the Effect; a non-zero EXIT is a value, since that is
|
|
204053
204054
|
* the mode's way of saying "invalid".
|
|
@@ -204130,8 +204131,7 @@ const formatAndValidateSteeringFile = (resolved, file, readContent, context, cwd
|
|
|
204130
204131
|
});
|
|
204131
204132
|
//#endregion
|
|
204132
204133
|
//#region src/program.ts
|
|
204133
|
-
const
|
|
204134
|
-
const GTD_VERSION = _require("../package.json").version;
|
|
204134
|
+
const GTD_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
204135
204135
|
const HELP_TEXT = `Usage: gtd [command] [options]
|
|
204136
204136
|
|
|
204137
204137
|
Commands:
|
|
@@ -204143,8 +204143,6 @@ Commands:
|
|
|
204143
204143
|
turn commit (summed into it.processCost/processCostByModel)
|
|
204144
204144
|
next Print the resolved rest's rendered script/prompt/message
|
|
204145
204145
|
(no mutation)
|
|
204146
|
-
run Execute the resolved rest's emitted script, then step its
|
|
204147
|
-
actor (the built-in script driver)
|
|
204148
204146
|
status Print the resolved rest's state/actor and which declared
|
|
204149
204147
|
pattern (if any) each pending change matches (no mutation)
|
|
204150
204148
|
validate Format and validate the steering file the resolved rest
|
|
@@ -204247,7 +204245,7 @@ const runLspCommand = (argv, json) => gen(function* () {
|
|
|
204247
204245
|
/**
|
|
204248
204246
|
* Resolve HEAD's rest, the current process run, and the template context for
|
|
204249
204247
|
* rendering that rest's OWN state/actor — the common prefix shared by `gtd
|
|
204250
|
-
* next
|
|
204248
|
+
* next` and `gtd status` (each fetches `git` itself first, since
|
|
204251
204249
|
* `gtd status` also needs it for `pendingChanges`; `stepAsActor`'s squash
|
|
204252
204250
|
* path renders a DIFFERENT state, so it builds its own context inline
|
|
204253
204251
|
* instead of sharing this helper).
|
|
@@ -204269,8 +204267,7 @@ const resolveRestContext = (git) => gen(function* () {
|
|
|
204269
204267
|
const formatStepRefusal = (invoker, refusal) => refusal.reason === "out-of-turn" ? `gtd step ${invoker}: out of turn — "${refusal.state}" awaits ${refusal.awaits}` : `gtd step ${invoker}: no declared pattern matches the pending changes at "${refusal.state}" — declared patterns: ${refusal.patterns.length > 0 ? refusal.patterns.join(", ") : "(none)"}`;
|
|
204270
204268
|
/**
|
|
204271
204269
|
* Authenticate `invoker` against the resolved rest and perform the one
|
|
204272
|
-
* resulting transition (commit or squash)
|
|
204273
|
-
* `gtd run` (which steps the script's own actor after executing it).
|
|
204270
|
+
* resulting transition (commit or squash) for `gtd step`.
|
|
204274
204271
|
* Refusals fail the Effect with a formatted message; a no-op returns
|
|
204275
204272
|
* `subject: null` rather than failing (exit zero, per the plan's "clean
|
|
204276
204273
|
* no-op exits zero").
|
|
@@ -204307,7 +204304,7 @@ const stepAsActor = (invoker, cost, model) => gen(function* () {
|
|
|
204307
204304
|
model: model ?? null
|
|
204308
204305
|
};
|
|
204309
204306
|
});
|
|
204310
|
-
/** Renders `stepAsActor`'s result
|
|
204307
|
+
/** Renders `stepAsActor`'s result for `gtd step`. */
|
|
204311
204308
|
const reportStepResult = (result, json, write) => {
|
|
204312
204309
|
if (json) write(JSON.stringify({
|
|
204313
204310
|
state: result.state,
|
|
@@ -204450,32 +204447,6 @@ const runValidateCommand = (argv, json, write) => gen(function* () {
|
|
|
204450
204447
|
}
|
|
204451
204448
|
return yield* fail$2(/* @__PURE__ */ new Error(`${file} is not valid:\n${errors.map((e) => ` - ${e}`).join("\n")}`));
|
|
204452
204449
|
});
|
|
204453
|
-
/**
|
|
204454
|
-
* `gtd run`: the built-in driver for a `script`-content rest. Renders the
|
|
204455
|
-
* resolved rest exactly like `gtd next`, executes its content verbatim via
|
|
204456
|
-
* `bash` (the ONLY place gtd spawns a subprocess), then steps that state's
|
|
204457
|
-
* own actor to capture the outcome. Refuses when the resolved rest isn't a
|
|
204458
|
-
* script (nothing to run).
|
|
204459
|
-
*/
|
|
204460
|
-
const runRunCommand = (argv, json, write) => gen(function* () {
|
|
204461
|
-
yield* rejectExtraArgs("run", argv);
|
|
204462
|
-
const git = yield* GitService;
|
|
204463
|
-
const { rest, context } = yield* resolveRestContext(git);
|
|
204464
|
-
const rendered = yield* renderRest(rest, context);
|
|
204465
|
-
if (rendered.kind !== "script") return yield* fail$2(/* @__PURE__ */ new Error(`gtd run: "${rest.state}" awaits a ${rendered.kind} from "${rest.actor}" — nothing scripted to run`));
|
|
204466
|
-
yield* try_({
|
|
204467
|
-
try: () => {
|
|
204468
|
-
const { spawnSync } = _require("node:child_process");
|
|
204469
|
-
spawnSync("bash", ["-c", rendered.content], {
|
|
204470
|
-
cwd: process.cwd(),
|
|
204471
|
-
stdio: "inherit"
|
|
204472
|
-
});
|
|
204473
|
-
},
|
|
204474
|
-
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
204475
|
-
});
|
|
204476
|
-
const result = yield* stepAsActor(rest.actor);
|
|
204477
|
-
reportStepResult(result, json, write);
|
|
204478
|
-
});
|
|
204479
204450
|
/** Which declared `on` pattern (if any) each pending change matches — the pure computation `gtd status` reports (both plain and `--json`). */
|
|
204480
204451
|
const computeStatusChanges = (onEdges, changes) => changes.map((change) => {
|
|
204481
204452
|
const matchedRow = onEdges.find(([patternStr]) => {
|
|
@@ -204563,7 +204534,6 @@ const runMermaidCommand = (argv, json, write) => gen(function* () {
|
|
|
204563
204534
|
const KNOWN_SUBCOMMANDS = [
|
|
204564
204535
|
"step",
|
|
204565
204536
|
"next",
|
|
204566
|
-
"run",
|
|
204567
204537
|
"status",
|
|
204568
204538
|
"validate",
|
|
204569
204539
|
"mermaid"
|
|
@@ -204614,7 +204584,6 @@ const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
|
|
|
204614
204584
|
switch (sub) {
|
|
204615
204585
|
case "step": return runStepCommand(argv, json, write, cost, model);
|
|
204616
204586
|
case "next": return runNextCommand(json, write);
|
|
204617
|
-
case "run": return runRunCommand(argv, json, write);
|
|
204618
204587
|
case "status": return runStatusCommand(argv, json, write);
|
|
204619
204588
|
case "validate": return runValidateCommand(argv, json, write);
|
|
204620
204589
|
case "mermaid": return runMermaidCommand(argv, json, write);
|
package/docs/cli.md
CHANGED
|
@@ -158,23 +158,15 @@ which are JSON-only. `--json` emits
|
|
|
158
158
|
when the state has no `on` (a commit state); a per-edge `describe` is likewise
|
|
159
159
|
omitted when that edge declares none.
|
|
160
160
|
|
|
161
|
-
## `gtd
|
|
161
|
+
## Running `script` rests (no `gtd` subcommand)
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
gtd run: "<state>" awaits a <kind> from "<actor>" — nothing scripted to run
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
Reports the same `{state, subject}` (or `committed: …` / `nothing to do at "…"`)
|
|
176
|
-
as `gtd step` for the capturing step it performs. Takes no arguments — extra
|
|
177
|
-
positional args are rejected.
|
|
163
|
+
There is no `gtd` subcommand that executes a workflow script — gtd never runs a
|
|
164
|
+
workflow script itself. When `gtd next` resolves to a `script`-content rest, the
|
|
165
|
+
**driver** (`bin/gtd-loop`, or any loop harness) executes the emitted `content`
|
|
166
|
+
verbatim via `bash -c` — foreground, inherited stdio, exit code deliberately
|
|
167
|
+
ignored (a check script encodes its outcome in the tree, e.g. writing a findings
|
|
168
|
+
file, never in its exit status) — then runs `gtd step <actor>` for that state's
|
|
169
|
+
own actor to capture the outcome. See [Driving the loop](loop.md).
|
|
178
170
|
|
|
179
171
|
## `gtd status [--json]`
|
|
180
172
|
|
|
@@ -370,7 +362,7 @@ the plain-text one.
|
|
|
370
362
|
report, a log file — into the working tree, the tree never goes clean after a
|
|
371
363
|
green run, and the check's `"C"` pattern never fires. Gitignore every path
|
|
372
364
|
your scripts write before wiring gtd into a repo.
|
|
373
|
-
- **Repository root invocation.** Every state subcommand (`step`/`next
|
|
365
|
+
- **Repository root invocation.** Every state subcommand (`step`/`next`/
|
|
374
366
|
`status`/`mermaid`) must run from the git repository root — the workflow,
|
|
375
367
|
pending changes, and process history are resolved against the process cwd.
|
|
376
368
|
`--help`/`--version`, `format`, and `lsp` skip this guard entirely (and any
|
package/docs/configuration.md
CHANGED
|
@@ -331,9 +331,9 @@ The contract:
|
|
|
331
331
|
not a malformed file): nothing is validated and nothing is committed.
|
|
332
332
|
- **At least one of the two is required** in any single entry; the half it
|
|
333
333
|
leaves out falls through to the layer beneath (below), or is a no-op.
|
|
334
|
-
- Commands run via `bash -c` from the repository root — the only place
|
|
335
|
-
`script:` state
|
|
336
|
-
project code: only add commands you would run yourself.
|
|
334
|
+
- Commands run via `bash -c` from the repository root — the only place gtd
|
|
335
|
+
itself spawns a subprocess (a `script:` state is run by the loop driver, not
|
|
336
|
+
by gtd). A `.gtdrc` is project code: only add commands you would run yourself.
|
|
337
337
|
|
|
338
338
|
#### The layers, half by half
|
|
339
339
|
|
|
@@ -518,8 +518,10 @@ vars:
|
|
|
518
518
|
```
|
|
519
519
|
|
|
520
520
|
```bash
|
|
521
|
-
# highest precedence — beats both the workflow default and the .gtdrc value above
|
|
522
|
-
|
|
521
|
+
# highest precedence — beats both the workflow default and the .gtdrc value above.
|
|
522
|
+
# The var is read at render time, so it shows up in the script `gtd next` prints
|
|
523
|
+
# (and in what the loop driver then executes).
|
|
524
|
+
GTD_VAR_testCommand="npm run test -- --bail" gtd next
|
|
523
525
|
```
|
|
524
526
|
|
|
525
527
|
A template reads any of it as `it.vars.<name>`:
|
package/docs/loop.md
CHANGED
|
@@ -11,21 +11,22 @@ gtd (v3, the pattern machine) exposes three commands a loop driver combines:
|
|
|
11
11
|
[Configuration: Token cost](configuration.md#token-cost)).
|
|
12
12
|
- **`gtd next`** — print the resolved rest's rendered script/prompt/message,
|
|
13
13
|
without mutating anything.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
|
|
15
|
+
gtd never executes a workflow script itself: at a `script`-content rest the
|
|
16
|
+
DRIVER runs the emitted `content` and then steps that state's own actor.
|
|
17
17
|
|
|
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
21
|
`{state, actor, kind, content, model?, memory?}` field shape. `kind` is the
|
|
22
|
-
dispatch key: `"message"` → halt (a human rest); `"script"` →
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
dispatch key: `"message"` → halt (a human rest); `"script"` → execute
|
|
23
|
+
`content` verbatim (ignoring its exit code), then `gtd step <actor>` to
|
|
24
|
+
capture the outcome; `"prompt"` → feed `content` to the agent, then
|
|
25
|
+
`gtd step <actor>` yourself once it's done acting. The optional `model` is an
|
|
26
|
+
opaque string the workflow author chose, which you map onto your own
|
|
27
|
+
harness's model selection if you use one.
|
|
28
|
+
2. Repeat until a `"message"` rest halts the loop, or a zero-commit script step
|
|
29
|
+
at idle settles it (the green terminal signal).
|
|
29
30
|
|
|
30
31
|
A human acts by editing files (e.g. writing/editing `.gtd/TODO.md`, fixing code)
|
|
31
32
|
and then running `gtd step human` to capture the edit as their turn.
|
|
@@ -75,7 +76,8 @@ while true; do
|
|
|
75
76
|
|
|
76
77
|
if [[ "$kind" == "script" ]]; then
|
|
77
78
|
head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
78
|
-
|
|
79
|
+
bash -c "$content" || true # run the emitted script; exit code ignored
|
|
80
|
+
gtd step "$actor" >/dev/null # capture whatever it left in the tree
|
|
79
81
|
head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
80
82
|
if [[ "$head_before" == "$head_after" ]]; then
|
|
81
83
|
echo "--- Settled ($state: check passed, nothing to do) ---"
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"script": {
|
|
53
53
|
"type": "string",
|
|
54
|
-
"description": "Content kind: a shell script (Eta template).
|
|
54
|
+
"description": "Content kind: a shell script (Eta template). The loop driver executes it verbatim via bash, then steps this state's actor."
|
|
55
55
|
},
|
|
56
56
|
"prompt": {
|
|
57
57
|
"type": "string",
|