@pmelab/gtd 6.8.0 → 6.9.1
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 +13 -7
- package/dist/gtd.bundle.mjs +141 -36
- package/docs/configuration.md +27 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,13 +58,19 @@ gtd init
|
|
|
58
58
|
This writes a `.gtdrc.json` for the bundled unified workflow, with each agent
|
|
59
59
|
state's prompt saved as an editable Markdown file under `gtd-prompts/` and
|
|
60
60
|
referenced from the config (review and commit both). Edit a prompt by editing
|
|
61
|
-
its `gtd-prompts/*.md` file — no config edit needed.
|
|
62
|
-
`
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
its `gtd-prompts/*.md` file — no config edit needed. File references resolve
|
|
62
|
+
relative to **the `.gtdrc` that declares them**, so a config and its
|
|
63
|
+
`gtd-prompts/` can even live in a parent directory shared across repos while gtd
|
|
64
|
+
runs from each repo root — run `gtd init` from that parent directory (it need
|
|
65
|
+
not be a git repo) to scaffold it there (see
|
|
66
|
+
[Configuration](docs/configuration.md#content-values-inline-or-a-file-reference)).
|
|
67
|
+
It also seeds a top-level `modes:` block suggesting **Prettier** as the
|
|
68
|
+
steering-file formatter (`npx prettier --write` for the built-in `qa`/`review`
|
|
69
|
+
modes — format only, so gtd still validates); edit or drop it freely (swap in
|
|
70
|
+
dprint, a script, or delete the key). `gtd init` takes no argument. gtd ships
|
|
71
|
+
**no** default workflow: a state command run before `gtd init` fails, pointing
|
|
72
|
+
you back here. See [Configuration](docs/configuration.md#gtd-init) for the
|
|
73
|
+
template.
|
|
68
74
|
|
|
69
75
|
## How it works
|
|
70
76
|
|
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -189815,6 +189815,49 @@ const resolveContent = (value, configDir, where, errors) => {
|
|
|
189815
189815
|
return;
|
|
189816
189816
|
}
|
|
189817
189817
|
};
|
|
189818
|
+
/**
|
|
189819
|
+
* Inline every `./`/`../` content file reference in ONE raw `workflow:` value
|
|
189820
|
+
* against `configDir` — the directory of the `.gtdrc` that DECLARED it — and
|
|
189821
|
+
* return a copy with those references replaced by the referenced file's text.
|
|
189822
|
+
*
|
|
189823
|
+
* Used by `src/Config.ts`'s `loadMerged` to resolve each config level's
|
|
189824
|
+
* references against its OWN file's directory BEFORE the levels are
|
|
189825
|
+
* deep-merged. The merge collapses every level into one anonymous object, so it
|
|
189826
|
+
* erases which file a given `states.x.prompt` came from; resolving up front,
|
|
189827
|
+
* per level, is the only way `./gtd-prompts/x.md` in a parent `.gtdrc` resolves
|
|
189828
|
+
* against the parent (not the cwd a child repo runs from). `compileWorkflowConfig`
|
|
189829
|
+
* is then invoked with `inlineFileRefs: false` on the merged result.
|
|
189830
|
+
*
|
|
189831
|
+
* Malformed shapes (a non-object workflow, non-object `states`, a non-object
|
|
189832
|
+
* state, a non-string content value) pass through untouched —
|
|
189833
|
+
* `compileWorkflowConfig`/`validateDefinition` own those findings; this pass
|
|
189834
|
+
* only rewrites the strings it recognizes as file references, collecting a load
|
|
189835
|
+
* error (via `resolveContent`) for any that is missing or unreadable.
|
|
189836
|
+
*/
|
|
189837
|
+
const inlineWorkflowFileRefs = (rawWorkflow, configDir, errors) => {
|
|
189838
|
+
if (!isPlainObject$1(rawWorkflow)) return rawWorkflow;
|
|
189839
|
+
const rawStates = rawWorkflow["states"];
|
|
189840
|
+
if (!isPlainObject$1(rawStates)) return rawWorkflow;
|
|
189841
|
+
const states = {};
|
|
189842
|
+
for (const [name, state] of Object.entries(rawStates)) {
|
|
189843
|
+
if (!isPlainObject$1(state)) {
|
|
189844
|
+
states[name] = state;
|
|
189845
|
+
continue;
|
|
189846
|
+
}
|
|
189847
|
+
const next = { ...state };
|
|
189848
|
+
for (const key of CONTENT_KEYS) {
|
|
189849
|
+
const value = state[key];
|
|
189850
|
+
if (typeof value !== "string" || !isFileReference(value)) continue;
|
|
189851
|
+
const resolved = resolveContent(value, configDir, `state "${name}" (${key})`, errors);
|
|
189852
|
+
if (resolved !== void 0) next[key] = resolved;
|
|
189853
|
+
}
|
|
189854
|
+
states[name] = next;
|
|
189855
|
+
}
|
|
189856
|
+
return {
|
|
189857
|
+
...rawWorkflow,
|
|
189858
|
+
states
|
|
189859
|
+
};
|
|
189860
|
+
};
|
|
189818
189861
|
const KNOWN_EDGE_KEYS = /* @__PURE__ */ new Set(["to", "describe"]);
|
|
189819
189862
|
/**
|
|
189820
189863
|
* Compile one `on` row's value into an `OnEdge` (or `undefined`, pushing a
|
|
@@ -189883,8 +189926,15 @@ const compileRetry = (raw, name, errors) => {
|
|
|
189883
189926
|
otherwise
|
|
189884
189927
|
} : void 0;
|
|
189885
189928
|
};
|
|
189886
|
-
/**
|
|
189887
|
-
|
|
189929
|
+
/**
|
|
189930
|
+
* Exactly one of script/prompt/message/commit, each a string, file-refs
|
|
189931
|
+
* auto-inlined. `inlineFileRefs` is `false` only when the caller
|
|
189932
|
+
* (`src/Config.ts`'s `loadMerged`) has ALREADY inlined every reference per
|
|
189933
|
+
* declaring file — the content is then taken verbatim, so a `script:` whose
|
|
189934
|
+
* inlined text happens to begin with `./` is never mistaken for a second file
|
|
189935
|
+
* reference and re-resolved.
|
|
189936
|
+
*/
|
|
189937
|
+
const compileContent = (raw, name, configDir, errors, inlineFileRefs) => {
|
|
189888
189938
|
const content = {};
|
|
189889
189939
|
for (const key of CONTENT_KEYS) {
|
|
189890
189940
|
const rawValue = raw[key];
|
|
@@ -189893,6 +189943,10 @@ const compileContent = (raw, name, configDir, errors) => {
|
|
|
189893
189943
|
errors.push(`state "${name}": "${key}" must be a string`);
|
|
189894
189944
|
continue;
|
|
189895
189945
|
}
|
|
189946
|
+
if (!inlineFileRefs) {
|
|
189947
|
+
content[key] = rawValue;
|
|
189948
|
+
continue;
|
|
189949
|
+
}
|
|
189896
189950
|
const resolved = resolveContent(rawValue, configDir, `state "${name}" (${key})`, errors);
|
|
189897
189951
|
if (resolved !== void 0) content[key] = resolved;
|
|
189898
189952
|
}
|
|
@@ -189991,7 +190045,7 @@ const assembleStateDef = (parts) => ({
|
|
|
189991
190045
|
...assembleContentFields(parts.content)
|
|
189992
190046
|
});
|
|
189993
190047
|
/** One state's full shape: actor, content, `on`, `initial`, `retry`. */
|
|
189994
|
-
const compileState = (name, raw, configDir, errors) => {
|
|
190048
|
+
const compileState = (name, raw, configDir, errors, inlineFileRefs) => {
|
|
189995
190049
|
if (!isPlainObject$1(raw)) {
|
|
189996
190050
|
errors.push(`state "${name}": must be an object, got ${describeType(raw)}`);
|
|
189997
190051
|
return {};
|
|
@@ -190000,7 +190054,7 @@ const compileState = (name, raw, configDir, errors) => {
|
|
|
190000
190054
|
if (unknownKeys.length > 0) errors.push(`state "${name}": unknown key(s) ${unknownKeys.join(", ")}`);
|
|
190001
190055
|
return assembleStateDef({
|
|
190002
190056
|
actor: compileActor(raw, name, errors),
|
|
190003
|
-
content: compileContent(raw, name, configDir, errors),
|
|
190057
|
+
content: compileContent(raw, name, configDir, errors, inlineFileRefs),
|
|
190004
190058
|
on: compileOn(raw.on, name, errors),
|
|
190005
190059
|
initial: compileInitial(raw, name, errors),
|
|
190006
190060
|
retry: compileRetry(raw.retry, name, errors),
|
|
@@ -190019,12 +190073,15 @@ const compileState = (name, raw, configDir, errors) => {
|
|
|
190019
190073
|
* file's own directory, used to resolve `./`/`../` file references. `rcModes`
|
|
190020
190074
|
* is the already-compiled top-level `.gtdrc` `modes:` key (`src/Config.ts`),
|
|
190021
190075
|
* layered over the workflow's own `modes:` per half BEFORE validation — so a
|
|
190022
|
-
* state may name a mode either layer declares.
|
|
190023
|
-
* `
|
|
190024
|
-
*
|
|
190025
|
-
*
|
|
190026
|
-
|
|
190027
|
-
|
|
190076
|
+
* state may name a mode either layer declares. `inlineFileRefs` (default
|
|
190077
|
+
* `true`) inlines content file references against `configDir`; the real config
|
|
190078
|
+
* path (`src/Config.ts`'s `loadMerged`) passes `false` because it has already
|
|
190079
|
+
* inlined every reference per declaring file across the merge chain, so
|
|
190080
|
+
* `configDir` is then irrelevant. Throws a single `Error` (message:
|
|
190081
|
+
* `"workflow config:\n - ..."`, one line per finding) on ANY config-shape
|
|
190082
|
+
* problem or `validateDefinition` finding — never partially succeeds.
|
|
190083
|
+
*/
|
|
190084
|
+
const compileWorkflowConfig = (raw, configDir, rcModes, inlineFileRefs = true) => {
|
|
190028
190085
|
if (!isPlainObject$1(raw)) throw new Error(`workflow config: must be an object, got ${describeType(raw)}`);
|
|
190029
190086
|
const errors = [];
|
|
190030
190087
|
const unknownTopKeys = Object.keys(raw).filter((k) => !KNOWN_TOP_KEYS.has(k));
|
|
@@ -190037,7 +190094,7 @@ const compileWorkflowConfig = (raw, configDir, rcModes) => {
|
|
|
190037
190094
|
throw new Error(formatErrors(errors));
|
|
190038
190095
|
}
|
|
190039
190096
|
const states = {};
|
|
190040
|
-
for (const [name, s] of Object.entries(rawStates)) states[name] = compileState(name, s, configDir, errors);
|
|
190097
|
+
for (const [name, s] of Object.entries(rawStates)) states[name] = compileState(name, s, configDir, errors, inlineFileRefs);
|
|
190041
190098
|
const definition = modes !== void 0 ? {
|
|
190042
190099
|
states,
|
|
190043
190100
|
modes
|
|
@@ -190327,22 +190384,50 @@ const makeExplorer = () => (0, import_dist.cosmiconfig)("gtd", {
|
|
|
190327
190384
|
/**
|
|
190328
190385
|
* Load and deep-merge every config level from cwd up the directory chain.
|
|
190329
190386
|
* Innermost (cwd) wins. Returns the merged plain object (undecoded).
|
|
190330
|
-
|
|
190387
|
+
*
|
|
190388
|
+
* A `workflow:` value's `./`/`../` content file references are inlined PER
|
|
190389
|
+
* LEVEL against that level's OWN file directory (`dirname(result.filepath)`)
|
|
190390
|
+
* BEFORE merging — because the deep-merge collapses every level into one
|
|
190391
|
+
* anonymous object and erases which file each `states.x.prompt` came from.
|
|
190392
|
+
* Resolving up front is what lets a `.gtdrc` stored in a parent directory
|
|
190393
|
+
* reference `./gtd-prompts/x.md` and have it resolve against the parent, even
|
|
190394
|
+
* though gtd runs from a child repo (a different cwd). Any missing/unreadable
|
|
190395
|
+
* reference is collected and thrown as one aggregated `workflow config:` error,
|
|
190396
|
+
* exactly like the compiler's own resolution. The merged, already-inlined
|
|
190397
|
+
* `workflow:` is later compiled with `inlineFileRefs: false` (see
|
|
190398
|
+
* `toOperations`).
|
|
190399
|
+
*/
|
|
190400
|
+
/**
|
|
190401
|
+
* Normalize one loaded config level: validate it is a plain object, then inline
|
|
190402
|
+
* its `workflow:` file references against its OWN file's directory (collecting
|
|
190403
|
+
* any missing/unreadable reference into `refErrors`). A level with no
|
|
190404
|
+
* `workflow:` key passes through untouched.
|
|
190405
|
+
*/
|
|
190406
|
+
const inlineLevel = (config, filepath, refErrors) => {
|
|
190407
|
+
if (!isPlainObject(config)) throw new Error(`${filepath}: config must be a plain object, got ${Array.isArray(config) ? "array" : String(config)}`);
|
|
190408
|
+
if (config["workflow"] === void 0) return config;
|
|
190409
|
+
return {
|
|
190410
|
+
...config,
|
|
190411
|
+
workflow: inlineWorkflowFileRefs(config["workflow"], dirname(filepath), refErrors)
|
|
190412
|
+
};
|
|
190413
|
+
};
|
|
190414
|
+
/** Read every config level from cwd up the chain, outermost→innermost, each with its file references already inlined per declaring file. Throws on a malformed level or an aggregated set of bad references. */
|
|
190415
|
+
const readConfigLevels = async (root) => {
|
|
190416
|
+
const chain = walkUp(root, homedir());
|
|
190417
|
+
const explorer = makeExplorer();
|
|
190418
|
+
const levels = [];
|
|
190419
|
+
const refErrors = [];
|
|
190420
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
190421
|
+
const result = await explorer.search(chain[i]);
|
|
190422
|
+
if (!result || result.isEmpty) continue;
|
|
190423
|
+
levels.push(inlineLevel(result.config, result.filepath, refErrors));
|
|
190424
|
+
}
|
|
190425
|
+
if (refErrors.length > 0) throw new Error(`workflow config:\n${refErrors.map((e) => ` - ${e}`).join("\n")}`);
|
|
190426
|
+
return levels;
|
|
190427
|
+
};
|
|
190331
190428
|
const loadMerged = (root) => tryPromise({
|
|
190332
190429
|
try: async () => {
|
|
190333
|
-
|
|
190334
|
-
const chain = walkUp(root, home);
|
|
190335
|
-
const explorer = makeExplorer();
|
|
190336
|
-
const levels = [];
|
|
190337
|
-
for (let i = chain.length - 1; i >= 0; i--) {
|
|
190338
|
-
const dir = chain[i];
|
|
190339
|
-
const result = await explorer.search(dir);
|
|
190340
|
-
if (result && !result.isEmpty) {
|
|
190341
|
-
if (!isPlainObject(result.config)) throw new Error(`${result.filepath}: config must be a plain object, got ${Array.isArray(result.config) ? "array" : String(result.config)}`);
|
|
190342
|
-
levels.push(result.config);
|
|
190343
|
-
}
|
|
190344
|
-
}
|
|
190345
|
-
return levels.reduce((acc, level) => deepMerge(acc, level), {});
|
|
190430
|
+
return (await readConfigLevels(root)).reduce((acc, level) => deepMerge(acc, level), {});
|
|
190346
190431
|
},
|
|
190347
190432
|
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
190348
190433
|
});
|
|
@@ -190396,18 +190481,19 @@ const compileRcVars = (raw) => {
|
|
|
190396
190481
|
const NO_WORKFLOW_MESSAGE = "gtd: no workflow configured — run `gtd init` to create .gtdrc.json";
|
|
190397
190482
|
/**
|
|
190398
190483
|
* Compile the decoded config's `workflow:` key plus its top-level
|
|
190399
|
-
* `vars:`/`modes:` keys into `ConfigOperations`.
|
|
190400
|
-
*
|
|
190401
|
-
*
|
|
190402
|
-
*
|
|
190403
|
-
*
|
|
190404
|
-
* invalid
|
|
190484
|
+
* `vars:`/`modes:` keys into `ConfigOperations`. Its `./`/`../` content file
|
|
190485
|
+
* references were already inlined per declaring file by `loadMerged`, so the
|
|
190486
|
+
* compiler is invoked with `inlineFileRefs: false` and `root` is passed only as
|
|
190487
|
+
* an (unused) `configDir` placeholder. Throws `NO_WORKFLOW_MESSAGE` when no
|
|
190488
|
+
* `workflow:` key is present (there is no bundled default to fall back to — see
|
|
190489
|
+
* `gtd init`), or (via `compileWorkflowConfig`/`compileRcVars`) on any invalid
|
|
190490
|
+
* workflow/vars.
|
|
190405
190491
|
*/
|
|
190406
190492
|
const toOperations = (decoded, root) => {
|
|
190407
190493
|
const rcVars = compileRcVars(decoded.vars);
|
|
190408
190494
|
const rcModes = compileRcModes(decoded.modes);
|
|
190409
190495
|
if (decoded.workflow === void 0) throw new Error(NO_WORKFLOW_MESSAGE);
|
|
190410
|
-
const { definition, vars: workflowVars } = compileWorkflowConfig(decoded.workflow, root, rcModes);
|
|
190496
|
+
const { definition, vars: workflowVars } = compileWorkflowConfig(decoded.workflow, root, rcModes, false);
|
|
190411
190497
|
return {
|
|
190412
190498
|
workflow: definition,
|
|
190413
190499
|
workflowVars,
|
|
@@ -191386,7 +191472,7 @@ var WorktreeReader = class WorktreeReader extends Tag("WorktreeReader")() {
|
|
|
191386
191472
|
};
|
|
191387
191473
|
//#endregion
|
|
191388
191474
|
//#region src/workflows/unified.yaml
|
|
191389
|
-
var unified_default = "# The unified gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init` (no argument). gtd ships no\n# default workflow: a repo picks this bundled template with `gtd init`, and a\n# state command run with no workflow configured fails (see src/Config.ts).\n# See STATES.md §10 for the full walkthrough.\n#\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/templates.ts does the compiling). Every content string below\n# is INLINE (no `./`-relative file references): the bundled template ships\n# inside a single-file build (`dist/gtd.bundle.mjs`), so its content must\n# already be resolved at BUILD time. `gtd init` DERIVES the scaffolded\n# `.gtdrc.json` from this inline source (`renderInitScaffold`): each agent\n# state's `prompt:` is written out as an editable `gtd-prompts/<state>.md`\n# file; human `message:`/check `script:`/the `done` `commit:` body stay inline.\n#\n# ── Three entry points, one shared tail ────────────────────────────────────\n#\n# The initial `idle` state FORKS on which steering file the human creates:\n#\n# * .gtd/REQUIREMENTS.md -> the ADVANCED flow (product + technical Q&A,\n# package decomposition, per-package parallel build + agentic spec-review).\n# * anything else (e.g. .gtd/TODO.md) -> the SIMPLE flow (one grilling Q&A\n# loop, a monolithic build, no decomposition, no agentic review).\n#\n# A third entry, `gtd review <commitish>`, jumps straight to `reviewing`\n# (reviewEntry) to review a foreign branch through the same shared tail.\n#\n# All three converge at `reviewing` -> `await-review`. From there:\n# * full sign-off (tick every box, or delete REVIEW.md) -> squashing -> done\n# (the ONLY path to the finale — the whole cycle collapses into one commit).\n# * partial sign-off / notes -> review-deciding extracts the unticked items\n# into REVIEW_FEEDBACK.md -> feedback-building implements them -> checking\n# -> reviewing regenerates an INCREMENTAL REVIEW.md (review-deciding carries\n# `reviewBase: true`, so each feedback round's review + checkout window\n# covers only `last-review..HEAD`) -> await-review, looping until sign-off.\n# * a code-only human edit (REVIEW.md untouched) -> checking -> reviewing.\n#\n# The shared health segment (`checking`/`fixing`/`escalate`) serves the simple\n# build, the feedback loop, and code-only edits — its green edge targets\n# `reviewing`. The advanced per-package loop keeps its OWN health triple\n# (`adv-checking`/`adv-fixing`/`adv-escalate`) because its green edge targets\n# the per-package `spec-review` gate instead.\n#\n# KNOWN LIMITATION: `on` pattern keys are NOT Eta templates — they keep the\n# LITERAL `.gtd/...` paths matching these vars' DEFAULT values. Repointing a\n# file var changes what templates read/write WITHOUT changing what the `on`\n# patterns match, desyncing the machine (see docs/configuration.md).\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n requirementsFile: .gtd/REQUIREMENTS.md\n architectureFile: .gtd/ARCHITECTURE.md\n packagesDir: .gtd/packages\n nextFile: .gtd/NEXT.md\n reviewFile: .gtd/REVIEW.md\n reviewFeedbackFile: .gtd/REVIEW_FEEDBACK.md\n feedbackFile: .gtd/FEEDBACK.md\n specFeedbackFile: .gtd/SPEC_FEEDBACK.md\n commitMsgFile: .gtd/COMMIT_MSG.md\n # The two opaque model tiers every agent state draws from — repoint them here\n # (or via a top-level `.gtdrc` `vars:` key / a `GTD_VAR_plannerModel` env var)\n # to change every state on that tier at once. `plannerModel` (default \"smart\")\n # is the heavier tier for the one-shot planning/architecting/review turns;\n # `coderModel` (default \"base\") is the tier for the build/fix turns.\n plannerModel: smart\n coderModel: base\n\nstates:\n # ── Shared entry ────────────────────────────────────────────────────────\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the same rendered \"What\n # each change does next\" list, built from `it.edges` (the resting state's\n # own `on` edges — each edge's `describe:` is the single source of truth).\n message: |\n No active gtd cycle.\n\n To start one, create a steering file describing what you want built:\n\n - `.gtd/TODO.md` — a short sketch — starts the SIMPLE flow (plan, build,\n review).\n - `.gtd/REQUIREMENTS.md` — the product requirements — starts the ADVANCED\n flow (product + technical Q&A, package decomposition, parallel build,\n per-package agentic review).\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 \"* .gtd/REQUIREMENTS.md\":\n to: adv-grilling\n describe: >-\n Save the product requirements to `.gtd/REQUIREMENTS.md` to start the\n ADVANCED flow — the agent develops them through product then technical\n Q&A, decomposes the work into packages, and builds each in parallel\n (**adv-grilling**).\n \"* **\":\n to: grilling\n describe: >-\n Save a short sketch to `.gtd/TODO.md` (or any other change) to start\n the SIMPLE flow — the agent reads it, explores the codebase, and\n develops it into a concrete implementation plan (**grilling**).\n\n # ── Simple flow: one Q&A loop, monolithic build ───────────────────────────\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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 near the top, one `### <question>`\n sub-heading each, its body your suggested answer in plain prose (always\n suggest one — the human accepts it by leaving it untouched, or overrides\n it by editing it). Omit the section entirely once there are none.\n\n If `<%= it.vars.todoFile %>` already carries a `## Open Questions`\n section from a previous lap, the human has now responded to that whole\n batch: fold every one of those questions into the plan — the human's edit\n where they gave one, your own suggestion where they left it untouched (an\n untouched suggestion IS acceptance) — and MOVE each resolved\n `### <question>` block down into a `## Answered Questions` section at the\n bottom. Raise any genuinely new questions under `## Open Questions`.\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 answer. Answer a question by editing its body in place; leave a\n suggestion untouched to accept it.\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 answer 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 model: <%= it.vars.coderModel %>\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 its\n 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 all\n 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 # ── Advanced flow: product + technical Q&A ────────────────────────────────\n adv-grilling:\n actor: agent\n file: <%= it.vars.requirementsFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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.requirementsFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.requirementsFile %>` holds the product requirements (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete, PRODUCT-level plan in this one turn.\n\n Scope: product and user-facing decisions only. Leave implementation\n details (file/module structure, data models, tech-stack choices) for the\n next phase (`<%= it.vars.architectureFile %>`).\n\n For every remaining open question, add it under a `## Open Questions`\n heading near the top, one `### <question>` sub-heading each, its body your\n suggested answer in plain prose (always suggest one). Omit the section\n once there are none. On a return lap, fold the whole answered batch into\n the plan and move each resolved `### <question>` block down into a\n `## Answered Questions` section at the bottom.\n\n Leave `<%= it.vars.requirementsFile %>` uncommitted and finish your turn.\n on:\n \"* **\": adv-grilling-answer\n\n adv-grilling-answer:\n actor: human\n file: <%= it.vars.requirementsFile %>\n mode: qa\n message: |\n `<%= it.vars.requirementsFile %>` holds the product plan under\n development, with any open questions under `## Open Questions` — each\n carrying a suggested answer. Answer a question by editing its body in\n place; leave a suggestion untouched to accept it.\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: architecting\n describe: >-\n Change nothing to accept the product plan and move on to technical\n architecting (**architecting**).\n \"* **\":\n to: adv-grilling\n describe: >-\n Edit the product plan — answer a question, add detail, or raise a new\n one — to send it back for another round (**adv-grilling**).\n\n architecting:\n actor: agent\n file: <%= it.vars.architectureFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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.architectureFile %>` and `<%= it.vars.requirementsFile %>`,\n which this prompt tells you to write and delete respectively.\n\n Read `<%= it.vars.requirementsFile %>` (the converged product plan) and\n develop `<%= it.vars.architectureFile %>` from it in this one turn:\n file/module structure, data models, library/tech-stack choices,\n error-handling strategy — the *how*, building on the *what* already\n settled. Do not re-open product-level decisions; treat them as settled.\n\n For every remaining open question, add it under a `## Open Questions`\n heading exactly as the grilling phase does (one `### <question>` each, its\n body your suggested answer). Omit the section once there are none. On a\n return lap, fold the whole answered batch in and move each resolved block\n down into a `## Answered Questions` section at the bottom.\n\n Once `<%= it.vars.architectureFile %>` is written, delete\n `<%= it.vars.requirementsFile %>` — its content has been folded in and it\n must not linger. Leave everything uncommitted and finish your turn.\n on:\n \"* **\": architecting-answer\n\n architecting-answer:\n actor: human\n file: <%= it.vars.architectureFile %>\n mode: qa\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development, with any open questions under `## Open Questions`. Answer a\n question by editing its body in place; leave a suggestion untouched to\n accept it.\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: decompose\n describe: >-\n Change nothing to accept the technical plan and move on to decomposing\n the work into packages (**decompose**).\n \"* **\":\n to: architecting\n describe: >-\n Edit the technical plan to send it back for another round\n (**architecting**).\n\n # ── Advanced flow: decomposition + per-package parallel build loop ─────────\n decompose:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\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 the\n package files this prompt tells you to write (under\n `<%= it.vars.packagesDir %>/`), and `<%= it.vars.architectureFile %>`,\n which it tells you to delete.\n\n Read `<%= it.vars.architectureFile %>` (the converged technical plan) and\n decompose it into an ordered set of WORK PACKAGES, one file each under\n `<%= it.vars.packagesDir %>/` (e.g. `<%= it.vars.packagesDir %>/01-name.md`,\n `<%= it.vars.packagesDir %>/02-name.md`, ...). Order the packages so the\n test suite can pass again after each one lands.\n\n Each package file describes a set of INDEPENDENT tasks that can be\n implemented concurrently without ordering constraints between them — a\n single build turn will fan them out to parallel subagents. Give each task\n a clear description, acceptance criteria as `- [ ]` checkboxes, and the\n relevant paths. Also state the package's overall requirement so it can be\n reviewed against its spec. Do NOT put ordering-dependent tasks in the same\n package — split them across ordered packages instead.\n\n No package file may reference any other `.gtd/` file. Once the package\n files are written, delete `<%= it.vars.architectureFile %>`. Leave\n everything uncommitted and finish your turn.\n on:\n \"* .gtd/packages/**\": picking\n\n picking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package queue arbiter) — the loop driver executes\n # this verbatim, then steps the check actor. Mechanics only: take the\n # first package file (by name) under the packages dir into NEXT.md, or\n # remove NEXT.md when the queue is empty. What NEXT.md's presence/absence\n # MEANS (another package to build vs. the queue closing out to review) is\n # decided by this state's own `on` rules at capture time — never here.\n set +e\n next=$(ls <%~ it.vars.packagesDir %>/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > <%~ it.vars.nextFile %>\n else\n rm -f <%~ it.vars.nextFile %>\n fi\n on:\n \"D .gtd/NEXT.md\": reviewing\n \"* .gtd/NEXT.md\": adv-building\n \"C\": reviewing\n\n adv-building:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\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/`; in\n particular do NOT delete the package file (the `spec-review` gate reads it\n after you) and never touch `<%= it.vars.nextFile %>` — the `picking` state\n owns it.\n\n The package to implement is: <%~ it.read(it.vars.nextFile) %>\n\n That file describes a set of INDEPENDENT tasks. Implement ALL of them in\n this one turn, fanning the independent tasks out to parallel subagents\n where your harness supports it — each task is self-contained and can be\n built concurrently. Implement EXACTLY the tasks this package describes — no\n more, no less; leave every other package file untouched. Use TDD\n discipline within each task.\n\n Leave the package file in place and everything else uncommitted, then\n finish your turn.\n on:\n \"* **\": adv-checking\n\n adv-checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\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 'the test command 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\": adv-fixing\n \"M .gtd/FEEDBACK.md\": adv-fixing\n \"D .gtd/FEEDBACK.md\": spec-review\n \"C\": spec-review\n\n adv-fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n retry:\n max: 3\n otherwise: adv-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 the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": adv-checking\n\n adv-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: adv-checking\n describe: >-\n Fix the failure yourself — edit the code and/or\n `.gtd/FEEDBACK.md` — to re-run the check (**adv-checking**).\n\n spec-review:\n actor: agent\n model: <%= it.vars.plannerModel %>\n memory: review\n # Bounded per cycle: once entered `max` times, a transition into it is\n # redirected to `closing` (force-close the package and move on — the\n # unresolved concern is deferred to the shared human review at the tail,\n # which sees the whole diff anyway). NOTE the cap is PROCESS-scoped, so it\n # pools across packages — the same documented limitation the shared\n # `fixing` cap has (see STATES.md / docs/configuration.md).\n retry:\n max: 3\n otherwise: closing\n prompt: |\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec. `.gtd/` holds this workflow's own state — never\n create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.specFeedbackFile %>`, which this prompt tells you to write\n when — and only when — you find problems.\n\n The package spec is: <%~ it.read(it.vars.nextFile) %>\n\n Read that spec, then verify the package's implementation (the diff below)\n against it: are all its tasks done, acceptance criteria met, and the code\n sound and consistent with the codebase?\n\n - If it fully satisfies the spec, write NOTHING and finish your turn — a\n clean turn is your approval.\n - If it does not, write `<%= it.vars.specFeedbackFile %>` listing the\n concrete problems for a fix turn to address. Be specific.\n\n Do not fix anything yourself and do not delete the package file — a later\n step owns that. Finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Diff under review\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"M .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"D .gtd/SPEC_FEEDBACK.md\": closing\n \"C\": closing\n\n spec-fix:\n actor: agent\n file: <%= it.vars.specFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\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.specFeedbackFile %>`, which this prompt tells you to address\n and then delete.\n\n Read `<%= it.vars.specFeedbackFile %>` (the reviewer's concerns about the\n current package) and the package spec (`<%= it.read(it.vars.nextFile) %>`),\n then fix the code to resolve every concern. Keep the change focused.\n\n Delete `<%= it.vars.specFeedbackFile %>` once every concern is addressed.\n Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": adv-checking\n\n closing:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package closer) — remove the just-reviewed package\n # file (its path is in NEXT.md) plus any leftover spec feedback, so\n # `picking` selects the NEXT package (or falls through to review when the\n # queue is empty). Reached on approval (spec-review clean) and on the\n # spec-review retry cap (force-close, deferring the concern to the tail).\n set +e\n pkg=$(cat <%~ it.vars.nextFile %> 2>/dev/null)\n [ -n \"$pkg\" ] && rm -f \"$pkg\"\n rm -f <%~ it.vars.specFeedbackFile %> <%~ it.vars.nextFile %>\n on:\n \"* **\": picking\n\n # ── Shared health segment (simple build, feedback loop, code-only edits) ──\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\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 'the test command 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 model: <%= it.vars.coderModel %>\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 the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\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 # ── Shared tail: agent review -> human review -> squash finale ────────────\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: <%= it.vars.plannerModel %>\n memory: review\n # `gtd review <commitish>` (a plain non-gtd branch, e.g. a colleague's PR)\n # enters here directly, recording the resolved base as a `Gtd-Review-Base:`\n # trailer on its empty entry commit — everything below then reuses the same\n # reviewing/await-review/feedback machinery over <commitish>..HEAD.\n reviewEntry: true\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 diff below in\n 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 of\n what changed and why, then one `- [ ] ./path/to/file.ts#42 — note` file\n pointer per hunk (checkboxes are for the human to tick, not you; every\n path is `./`-relative, with an optional `#line` and an optional\n ` — note`).\n\n The diff below is `it.reviewDiff` — on a feedback round it covers ONLY the\n changes made since the previous review, so you review just what changed,\n not items already signed off. Leave `<%= it.vars.reviewFile %>`\n uncommitted and finish your turn.\n <% if (it.reviewDiff.trim()) { %>\n\n ## Full diff under review\n\n ```diff\n <%~ it.reviewDiff %>\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, gtd opens a \"review checkout window\": raw HEAD\n # is rewound to the review base (the most-recent `review-deciding` commit —\n # so a feedback round shows only `last-review..HEAD` — or the process start\n # on the first review) with the working tree untouched, surfacing the diff\n # as ordinary uncommitted changes. It closes on the next invocation.\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the cycle — one\n `- [ ]` 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: squashing\n describe: >-\n Delete `.gtd/REVIEW.md` outright to sign off on the whole cycle at\n once and collapse it into one commit (**squashing**).\n \"M .gtd/REVIEW.md\":\n to: review-deciding\n describe: >-\n Edit `.gtd/REVIEW.md` — tick a `- [ ]` box to `- [x]` to sign off that\n item, or add notes — to hand it to a deterministic check\n (**review-deciding**) that squashes when every box is ticked, or sends\n the still-unticked items back for another build+review round.\n \"* **\":\n to: checking\n describe: >-\n Change only code, leaving `.gtd/REVIEW.md` untouched, to re-run the\n tests on your manual fix and re-review it (**checking**).\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n # Anchors the INCREMENTAL review base: its most-recent in-process commit is\n # the point the previous review round ended, so the next `reviewing` turn\n # (and the review checkout window) covers only the changes since then. On\n # the first review there is no such commit, so the base is the cycle start.\n reviewBase: true\n script: |\n #!/usr/bin/env bash\n # gtd check turn — if REVIEW.md still has any unticked \"- [ ]\" pointer,\n # extract those pointers (with their notes) into REVIEW_FEEDBACK.md;\n # either way, remove REVIEW.md. What the resulting diff MEANS (feedback\n # round vs. full sign-off) is decided by this state's own `on` rules. The\n # A/M REVIEW_FEEDBACK.md rows are declared BEFORE the D REVIEW.md row so a\n # feedback round — which always also deletes REVIEW.md — is captured as\n # feedback, not mistaken for a sign-off.\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.reviewFeedbackFile %>\n fi\n rm -f <%~ it.vars.reviewFile %>\n on:\n \"A .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"M .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"D .gtd/REVIEW.md\": squashing\n \"C\": await-review\n\n feedback-building:\n actor: agent\n file: <%= it.vars.reviewFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent addressing review feedback. `.gtd/`\n holds this workflow's own state — never create, edit, or delete anything\n under `.gtd/` except `<%= it.vars.reviewFeedbackFile %>`, which this\n prompt tells you to address and then delete.\n\n The review feedback to address is: <%~ it.read(it.vars.reviewFeedbackFile) %>\n\n Implement exactly those changes — no Q&A, no re-planning. Use TDD\n discipline. Delete `<%= it.vars.reviewFeedbackFile %>` once every item is\n addressed. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n squashing:\n actor: agent\n file: <%= it.vars.commitMsgFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. The cycle is approved and done.\n `.gtd/` holds this workflow's own state — never create, edit, or delete\n anything under `.gtd/` except `<%= it.vars.commitMsgFile %>`, which this\n prompt tells you to write.\n\n Write `<%= it.vars.commitMsgFile %>` with ONE conventional-commits message\n for the entire cycle (subject line `type(scope): subject`, imperative\n mood, <= 72 characters; a body explaining the why, trade-offs, and key\n decisions). Plain text, no markdown wrapper.\n\n Leave `<%= it.vars.commitMsgFile %>` uncommitted and finish your turn —\n entering this file squashes the whole cycle into one commit using its\n content as the message.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/COMMIT_MSG.md\": done\n \"M .gtd/COMMIT_MSG.md\": done\n\n done:\n commit: |\n <%~ it.read(it.vars.commitMsgFile) %>\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n";
|
|
191475
|
+
var unified_default = "# The unified gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init` (no argument). gtd ships no\n# default workflow: a repo picks this bundled template with `gtd init`, and a\n# state command run with no workflow configured fails (see src/Config.ts).\n# See STATES.md §10 for the full walkthrough.\n#\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/templates.ts does the compiling). Every content string below\n# is INLINE (no `./`-relative file references): the bundled template ships\n# inside a single-file build (`dist/gtd.bundle.mjs`), so its content must\n# already be resolved at BUILD time. `gtd init` DERIVES the scaffolded\n# `.gtdrc.json` from this inline source (`renderInitScaffold`): each agent\n# state's `prompt:` is written out as an editable `gtd-prompts/<state>.md`\n# file; human `message:`/check `script:`/the `done` `commit:` body stay inline.\n#\n# ── Three entry points, one shared tail ────────────────────────────────────\n#\n# The initial `idle` state FORKS on which steering file the human creates:\n#\n# * .gtd/REQUIREMENTS.md -> the ADVANCED flow (product + technical Q&A,\n# package decomposition, per-package parallel build + agentic spec-review).\n# * anything else (e.g. .gtd/TODO.md) -> the SIMPLE flow (one grilling Q&A\n# loop, a monolithic build, no decomposition, no agentic review).\n#\n# A third entry, `gtd review <commitish>`, jumps straight to `reviewing`\n# (reviewEntry) to review a foreign branch through the same shared tail.\n#\n# All three converge at `reviewing` -> `await-review`. From there:\n# * full sign-off (tick every box, or delete REVIEW.md) -> squashing -> done\n# (the ONLY path to the finale — the whole cycle collapses into one commit).\n# * partial sign-off / notes -> review-deciding extracts the unticked items\n# into REVIEW_FEEDBACK.md -> feedback-building implements them -> checking\n# -> reviewing regenerates an INCREMENTAL REVIEW.md (review-deciding carries\n# `reviewBase: true`, so each feedback round's review + checkout window\n# covers only `last-review..HEAD`) -> await-review, looping until sign-off.\n# * a code-only human edit (REVIEW.md untouched) -> checking -> reviewing.\n#\n# The shared health segment (`checking`/`fixing`/`escalate`) serves the simple\n# build, the feedback loop, and code-only edits — its green edge targets\n# `reviewing`. The advanced per-package loop keeps its OWN health triple\n# (`adv-checking`/`adv-fixing`/`adv-escalate`) because its green edge targets\n# the per-package `spec-review` gate instead.\n#\n# KNOWN LIMITATION: `on` pattern keys are NOT Eta templates — they keep the\n# LITERAL `.gtd/...` paths matching these vars' DEFAULT values. Repointing a\n# file var changes what templates read/write WITHOUT changing what the `on`\n# patterns match, desyncing the machine (see docs/configuration.md).\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n requirementsFile: .gtd/REQUIREMENTS.md\n architectureFile: .gtd/ARCHITECTURE.md\n packagesDir: .gtd/packages\n nextFile: .gtd/NEXT.md\n reviewFile: .gtd/REVIEW.md\n reviewFeedbackFile: .gtd/REVIEW_FEEDBACK.md\n feedbackFile: .gtd/FEEDBACK.md\n specFeedbackFile: .gtd/SPEC_FEEDBACK.md\n commitMsgFile: .gtd/COMMIT_MSG.md\n # The two opaque model tiers every agent state draws from — repoint them here\n # (or via a top-level `.gtdrc` `vars:` key / a `GTD_VAR_plannerModel` env var)\n # to change every state on that tier at once. `plannerModel` (default \"smart\")\n # is the heavier tier for the one-shot planning/architecting/review turns;\n # `coderModel` (default \"base\") is the tier for the build/fix turns.\n plannerModel: smart\n coderModel: base\n\nstates:\n # ── Shared entry ────────────────────────────────────────────────────────\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the same rendered \"What\n # each change does next\" list, built from `it.edges` (the resting state's\n # own `on` edges — each edge's `describe:` is the single source of truth).\n message: |\n No active gtd cycle.\n\n To start one, create a steering file describing what you want built:\n\n - `.gtd/TODO.md` — a short sketch — starts the SIMPLE flow (plan, build,\n review).\n - `.gtd/REQUIREMENTS.md` — the product requirements — starts the ADVANCED\n flow (product + technical Q&A, package decomposition, parallel build,\n per-package agentic review).\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 \"* .gtd/REQUIREMENTS.md\":\n to: adv-grilling\n describe: >-\n Save the product requirements to `.gtd/REQUIREMENTS.md` to start the\n ADVANCED flow — the agent develops them through product then technical\n Q&A, decomposes the work into packages, and builds each in parallel\n (**adv-grilling**).\n \"* **\":\n to: grilling\n describe: >-\n Save a short sketch to `.gtd/TODO.md` (or any other change) to start\n the SIMPLE flow — the agent reads it, explores the codebase, and\n develops it into a concrete implementation plan (**grilling**).\n\n # ── Simple flow: one Q&A loop, monolithic build ───────────────────────────\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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 near the top, one `### <question>`\n sub-heading each, its body your suggested answer in plain prose (always\n suggest one — the human accepts it by leaving it untouched, or overrides\n it by editing it). Omit the section entirely once there are none.\n\n If `<%= it.vars.todoFile %>` already carries a `## Open Questions`\n section from a previous lap, the human has now responded to that whole\n batch: fold every one of those questions into the plan — the human's edit\n where they gave one, your own suggestion where they left it untouched (an\n untouched suggestion IS acceptance) — and MOVE each resolved\n `### <question>` block down into a `## Answered Questions` section at the\n bottom. Raise any genuinely new questions under `## Open Questions`.\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 answer. Answer a question by editing its body in place; leave a\n suggestion untouched to accept it.\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 answer 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 model: <%= it.vars.coderModel %>\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 its\n 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 all\n 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 # ── Advanced flow: product + technical Q&A ────────────────────────────────\n adv-grilling:\n actor: agent\n file: <%= it.vars.requirementsFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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.requirementsFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.requirementsFile %>` holds the product requirements (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete, PRODUCT-level plan in this one turn.\n\n Scope: product and user-facing decisions only. Leave implementation\n details (file/module structure, data models, tech-stack choices) for the\n next phase (`<%= it.vars.architectureFile %>`).\n\n For every remaining open question, add it under a `## Open Questions`\n heading near the top, one `### <question>` sub-heading each, its body your\n suggested answer in plain prose (always suggest one). Omit the section\n once there are none. On a return lap, fold the whole answered batch into\n the plan and move each resolved `### <question>` block down into a\n `## Answered Questions` section at the bottom.\n\n Leave `<%= it.vars.requirementsFile %>` uncommitted and finish your turn.\n on:\n \"* **\": adv-grilling-answer\n\n adv-grilling-answer:\n actor: human\n file: <%= it.vars.requirementsFile %>\n mode: qa\n message: |\n `<%= it.vars.requirementsFile %>` holds the product plan under\n development, with any open questions under `## Open Questions` — each\n carrying a suggested answer. Answer a question by editing its body in\n place; leave a suggestion untouched to accept it.\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: architecting\n describe: >-\n Change nothing to accept the product plan and move on to technical\n architecting (**architecting**).\n \"* **\":\n to: adv-grilling\n describe: >-\n Edit the product plan — answer a question, add detail, or raise a new\n one — to send it back for another round (**adv-grilling**).\n\n architecting:\n actor: agent\n file: <%= it.vars.architectureFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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.architectureFile %>` and `<%= it.vars.requirementsFile %>`,\n which this prompt tells you to write and delete respectively.\n\n Read `<%= it.vars.requirementsFile %>` (the converged product plan) and\n develop `<%= it.vars.architectureFile %>` from it in this one turn:\n file/module structure, data models, library/tech-stack choices,\n error-handling strategy — the *how*, building on the *what* already\n settled. Do not re-open product-level decisions; treat them as settled.\n\n For every remaining open question, add it under a `## Open Questions`\n heading exactly as the grilling phase does (one `### <question>` each, its\n body your suggested answer). Omit the section once there are none. On a\n return lap, fold the whole answered batch in and move each resolved block\n down into a `## Answered Questions` section at the bottom.\n\n Once `<%= it.vars.architectureFile %>` is written, delete\n `<%= it.vars.requirementsFile %>` — its content has been folded in and it\n must not linger. Leave everything uncommitted and finish your turn.\n on:\n \"* **\": architecting-answer\n\n architecting-answer:\n actor: human\n file: <%= it.vars.architectureFile %>\n mode: qa\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development, with any open questions under `## Open Questions`. Answer a\n question by editing its body in place; leave a suggestion untouched to\n accept it.\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: decompose\n describe: >-\n Change nothing to accept the technical plan and move on to decomposing\n the work into packages (**decompose**).\n \"* **\":\n to: architecting\n describe: >-\n Edit the technical plan to send it back for another round\n (**architecting**).\n\n # ── Advanced flow: decomposition + per-package parallel build loop ─────────\n decompose:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\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 the\n package files this prompt tells you to write (under\n `<%= it.vars.packagesDir %>/`), and `<%= it.vars.architectureFile %>`,\n which it tells you to delete.\n\n Read `<%= it.vars.architectureFile %>` (the converged technical plan) and\n decompose it into an ordered set of WORK PACKAGES, one file each under\n `<%= it.vars.packagesDir %>/` (e.g. `<%= it.vars.packagesDir %>/01-name.md`,\n `<%= it.vars.packagesDir %>/02-name.md`, ...). Order the packages so the\n test suite can pass again after each one lands.\n\n Each package file describes a set of INDEPENDENT tasks that can be\n implemented concurrently without ordering constraints between them — a\n single build turn will fan them out to parallel subagents. Give each task\n a clear description, acceptance criteria as `- [ ]` checkboxes, and the\n relevant paths. Also state the package's overall requirement so it can be\n reviewed against its spec. Do NOT put ordering-dependent tasks in the same\n package — split them across ordered packages instead.\n\n No package file may reference any other `.gtd/` file. Once the package\n files are written, delete `<%= it.vars.architectureFile %>`. Leave\n everything uncommitted and finish your turn.\n on:\n \"* .gtd/packages/**\": picking\n\n picking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package queue arbiter) — the loop driver executes\n # this verbatim, then steps the check actor. Mechanics only: take the\n # first package file (by name) under the packages dir into NEXT.md, or\n # remove NEXT.md when the queue is empty. What NEXT.md's presence/absence\n # MEANS (another package to build vs. the queue closing out to review) is\n # decided by this state's own `on` rules at capture time — never here.\n set +e\n # Hoist the NEXT path once, at the TOP: Eta's autoTrim eats the newline\n # after an interpolation tag, so no tag may be the last token on a line —\n # it would glue the next line's `else`/`fi` onto it and break the script.\n # Below this point everything is plain bash.\n nextFile=\"<%~ it.vars.nextFile %>\"\n next=$(ls <%~ it.vars.packagesDir %>/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > \"$nextFile\"\n else\n rm -f \"$nextFile\"\n fi\n on:\n \"D .gtd/NEXT.md\": reviewing\n \"* .gtd/NEXT.md\": adv-building\n \"C\": reviewing\n\n adv-building:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\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/`; in\n particular do NOT delete the package file (the `spec-review` gate reads it\n after you) and never touch `<%= it.vars.nextFile %>` — the `picking` state\n owns it.\n\n The package to implement is: <%~ it.read(it.vars.nextFile) %>\n\n That file describes a set of INDEPENDENT tasks. Implement ALL of them in\n this one turn, fanning the independent tasks out to parallel subagents\n where your harness supports it — each task is self-contained and can be\n built concurrently. Implement EXACTLY the tasks this package describes — no\n more, no less; leave every other package file untouched. Use TDD\n discipline within each task.\n\n Leave the package file in place and everything else uncommitted, then\n finish your turn.\n on:\n \"* **\": adv-checking\n\n adv-checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\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 \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": adv-fixing\n \"M .gtd/FEEDBACK.md\": adv-fixing\n \"D .gtd/FEEDBACK.md\": spec-review\n \"C\": spec-review\n\n adv-fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n retry:\n max: 3\n otherwise: adv-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 the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": adv-checking\n\n adv-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: adv-checking\n describe: >-\n Fix the failure yourself — edit the code and/or\n `.gtd/FEEDBACK.md` — to re-run the check (**adv-checking**).\n\n spec-review:\n actor: agent\n model: <%= it.vars.plannerModel %>\n memory: review\n # Bounded per cycle: once entered `max` times, a transition into it is\n # redirected to `closing` (force-close the package and move on — the\n # unresolved concern is deferred to the shared human review at the tail,\n # which sees the whole diff anyway). NOTE the cap is PROCESS-scoped, so it\n # pools across packages — the same documented limitation the shared\n # `fixing` cap has (see STATES.md / docs/configuration.md).\n retry:\n max: 3\n otherwise: closing\n prompt: |\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec. `.gtd/` holds this workflow's own state — never\n create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.specFeedbackFile %>`, which this prompt tells you to write\n when — and only when — you find problems.\n\n The package spec is: <%~ it.read(it.vars.nextFile) %>\n\n Read that spec, then verify the package's implementation (the diff below)\n against it: are all its tasks done, acceptance criteria met, and the code\n sound and consistent with the codebase?\n\n - If it fully satisfies the spec, write NOTHING and finish your turn — a\n clean turn is your approval.\n - If it does not, write `<%= it.vars.specFeedbackFile %>` listing the\n concrete problems for a fix turn to address. Be specific.\n\n Do not fix anything yourself and do not delete the package file — a later\n step owns that. Finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Diff under review\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"M .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"D .gtd/SPEC_FEEDBACK.md\": closing\n \"C\": closing\n\n spec-fix:\n actor: agent\n file: <%= it.vars.specFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\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.specFeedbackFile %>`, which this prompt tells you to address\n and then delete.\n\n Read `<%= it.vars.specFeedbackFile %>` (the reviewer's concerns about the\n current package) and the package spec (`<%= it.read(it.vars.nextFile) %>`),\n then fix the code to resolve every concern. Keep the change focused.\n\n Delete `<%= it.vars.specFeedbackFile %>` once every concern is addressed.\n Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": adv-checking\n\n closing:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package closer) — remove the just-reviewed package\n # file (its path is in NEXT.md) plus any leftover spec feedback, so\n # `picking` selects the NEXT package (or falls through to review when the\n # queue is empty). Reached on approval (spec-review clean) and on the\n # spec-review retry cap (force-close, deferring the concern to the tail).\n set +e\n pkg=$(cat <%~ it.vars.nextFile %> 2>/dev/null)\n [ -n \"$pkg\" ] && rm -f \"$pkg\"\n rm -f <%~ it.vars.specFeedbackFile %> <%~ it.vars.nextFile %>\n on:\n \"* **\": picking\n\n # ── Shared health segment (simple build, feedback loop, code-only edits) ──\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\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 \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\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 model: <%= it.vars.coderModel %>\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 the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\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 # ── Shared tail: agent review -> human review -> squash finale ────────────\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: <%= it.vars.plannerModel %>\n memory: review\n # `gtd review <commitish>` (a plain non-gtd branch, e.g. a colleague's PR)\n # enters here directly, recording the resolved base as a `Gtd-Review-Base:`\n # trailer on its empty entry commit — everything below then reuses the same\n # reviewing/await-review/feedback machinery over <commitish>..HEAD.\n reviewEntry: true\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 diff below in\n 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 of\n what changed and why, then one `- [ ] ./path/to/file.ts#42 — note` file\n pointer per hunk (checkboxes are for the human to tick, not you; every\n path is `./`-relative, with an optional `#line` and an optional\n ` — note`).\n\n The diff below is `it.reviewDiff` — on a feedback round it covers ONLY the\n changes made since the previous review, so you review just what changed,\n not items already signed off. Leave `<%= it.vars.reviewFile %>`\n uncommitted and finish your turn.\n <% if (it.reviewDiff.trim()) { %>\n\n ## Full diff under review\n\n ```diff\n <%~ it.reviewDiff %>\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, gtd opens a \"review checkout window\": raw HEAD\n # is rewound to the review base (the most-recent `review-deciding` commit —\n # so a feedback round shows only `last-review..HEAD` — or the process start\n # on the first review) with the working tree untouched, surfacing the diff\n # as ordinary uncommitted changes. It closes on the next invocation.\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the cycle — one\n `- [ ]` 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: squashing\n describe: >-\n Delete `.gtd/REVIEW.md` outright to sign off on the whole cycle at\n once and collapse it into one commit (**squashing**).\n \"M .gtd/REVIEW.md\":\n to: review-deciding\n describe: >-\n Edit `.gtd/REVIEW.md` — tick a `- [ ]` box to `- [x]` to sign off that\n item, or add notes — to hand it to a deterministic check\n (**review-deciding**) that squashes when every box is ticked, or sends\n the still-unticked items back for another build+review round.\n \"* **\":\n to: checking\n describe: >-\n Change only code, leaving `.gtd/REVIEW.md` untouched, to re-run the\n tests on your manual fix and re-review it (**checking**).\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n # Anchors the INCREMENTAL review base: its most-recent in-process commit is\n # the point the previous review round ended, so the next `reviewing` turn\n # (and the review checkout window) covers only the changes since then. On\n # the first review there is no such commit, so the base is the cycle start.\n reviewBase: true\n script: |\n #!/usr/bin/env bash\n # gtd check turn — if REVIEW.md still has any unticked \"- [ ]\" pointer,\n # extract those pointers (with their notes) into REVIEW_FEEDBACK.md;\n # either way, remove REVIEW.md. What the resulting diff MEANS (feedback\n # round vs. full sign-off) is decided by this state's own `on` rules. The\n # A/M REVIEW_FEEDBACK.md rows are declared BEFORE the D REVIEW.md row so a\n # feedback round — which always also deletes REVIEW.md — is captured as\n # feedback, not mistaken for a sign-off.\n set +e\n mkdir -p .gtd\n # Hoist the review paths once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on a\n # line — it would glue the next line onto it and break the script. Below\n # this point everything is plain bash.\n reviewFile=\"<%~ it.vars.reviewFile %>\"\n reviewFeedbackFile=\"<%~ it.vars.reviewFeedbackFile %>\"\n if grep -qE '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' \"$reviewFile\" 2>/dev/null; then\n {\n echo \"Feedback from review — address these before continuing:\"\n echo\n grep -E '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' \"$reviewFile\"\n } > \"$reviewFeedbackFile\"\n fi\n rm -f \"$reviewFile\"\n on:\n \"A .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"M .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"D .gtd/REVIEW.md\": squashing\n \"C\": await-review\n\n feedback-building:\n actor: agent\n file: <%= it.vars.reviewFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent addressing review feedback. `.gtd/`\n holds this workflow's own state — never create, edit, or delete anything\n under `.gtd/` except `<%= it.vars.reviewFeedbackFile %>`, which this\n prompt tells you to address and then delete.\n\n The review feedback to address is: <%~ it.read(it.vars.reviewFeedbackFile) %>\n\n Implement exactly those changes — no Q&A, no re-planning. Use TDD\n discipline. Delete `<%= it.vars.reviewFeedbackFile %>` once every item is\n addressed. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n squashing:\n actor: agent\n file: <%= it.vars.commitMsgFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. The cycle is approved and done.\n `.gtd/` holds this workflow's own state — never create, edit, or delete\n anything under `.gtd/` except `<%= it.vars.commitMsgFile %>`, which this\n prompt tells you to write.\n\n Write `<%= it.vars.commitMsgFile %>` with ONE conventional-commits message\n for the entire cycle (subject line `type(scope): subject`, imperative\n mood, <= 72 characters; a body explaining the why, trade-offs, and key\n decisions). Plain text, no markdown wrapper.\n\n Leave `<%= it.vars.commitMsgFile %>` uncommitted and finish your turn —\n entering this file squashes the whole cycle into one commit using its\n content as the message.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/COMMIT_MSG.md\": done\n \"M .gtd/COMMIT_MSG.md\": done\n\n done:\n commit: |\n <%~ it.read(it.vars.commitMsgFile) %>\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n";
|
|
191390
191476
|
//#endregion
|
|
191391
191477
|
//#region src/workflows/templates.ts
|
|
191392
191478
|
/**
|
|
@@ -204404,7 +204490,7 @@ const runInitCommand = (argv, json, write) => gen(function* () {
|
|
|
204404
204490
|
if (args.length > 0) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: too many arguments — init takes no argument, got: ${args.join(", ")}`));
|
|
204405
204491
|
const git = yield* GitService;
|
|
204406
204492
|
const fs = yield* FileSystem;
|
|
204407
|
-
yield*
|
|
204493
|
+
const inRepo = yield* assertInitLocation(git, fs);
|
|
204408
204494
|
const { root } = yield* Cwd;
|
|
204409
204495
|
if (yield* configPresentAt(root)) return yield* fail$2(/* @__PURE__ */ new Error("gtd init: a gtd config already exists — remove it before re-initializing"));
|
|
204410
204496
|
const toError = (e) => e instanceof Error ? e : new Error(String(e));
|
|
@@ -204418,11 +204504,12 @@ const runInitCommand = (argv, json, write) => gen(function* () {
|
|
|
204418
204504
|
if (json) write(JSON.stringify({
|
|
204419
204505
|
written: ".gtdrc.json",
|
|
204420
204506
|
workflow: "unified",
|
|
204421
|
-
prompts: scaffold.prompts.map((p) => p.path)
|
|
204507
|
+
prompts: scaffold.prompts.map((p) => p.path),
|
|
204508
|
+
inRepo
|
|
204422
204509
|
}) + "\n");
|
|
204423
204510
|
else {
|
|
204424
204511
|
const promptCount = scaffold.prompts.length;
|
|
204425
|
-
write(`Wrote .gtdrc.json with the bundled unified workflow, and its ${promptCount} agent prompt${promptCount === 1 ? "" : "s"} as editable Markdown under ${PROMPTS_DIR}/ (referenced from the config).\n\
|
|
204512
|
+
write(`Wrote .gtdrc.json with the bundled unified workflow, and its ${promptCount} agent prompt${promptCount === 1 ? "" : "s"} as editable Markdown under ${PROMPTS_DIR}/ (referenced from the config).\n\n` + (inRepo ? "Review and commit them before starting: an uncommitted .gtdrc.json (or prompt\nfile) counts as a pending change, so the initial state would capture it on the\nfirst step. Once committed, run `gtd step human` to begin.\n" : "This directory is not a git repository, so there is nothing to commit here. The\nconfig applies to any gtd repository nested below it — gtd discovers it by\nwalking up from the repository root. Run `gtd step human` from such a repo to\nbegin.\n"));
|
|
204426
204513
|
}
|
|
204427
204514
|
});
|
|
204428
204515
|
/**
|
|
@@ -204812,6 +204899,24 @@ const assertRunningFromRepoRoot = (git, fs) => gen(function* () {
|
|
|
204812
204899
|
const topLevel = yield* git.topLevel();
|
|
204813
204900
|
if ((yield* fs.realPath(topLevel)) !== (yield* fs.realPath(process.cwd()))) return yield* fail$2(/* @__PURE__ */ new Error(`gtd must be run from the repository root (${topLevel}); the current directory is ${process.cwd()}`));
|
|
204814
204901
|
});
|
|
204902
|
+
/**
|
|
204903
|
+
* `gtd init` writes only a `.gtdrc.json` (+ prompt files) — it derives no state,
|
|
204904
|
+
* so unlike the state commands it need not sit in a git repository at all. It
|
|
204905
|
+
* may run EITHER at a repository root OR in a directory outside any repository —
|
|
204906
|
+
* the latter scaffolds a shared config a nested repo picks up by walking up the
|
|
204907
|
+
* cwd→home chain (see the parent-dir layout in docs/configuration.md). The one
|
|
204908
|
+
* placement it must refuse is a repository SUBDIRECTORY: gtd runs from the repo
|
|
204909
|
+
* root and discovers config by walking UP, so a config written below the root
|
|
204910
|
+
* would silently never be found. Returns whether cwd is inside a repository (at
|
|
204911
|
+
* its root), so the caller can tailor the "commit before starting" guidance —
|
|
204912
|
+
* there is nothing to commit outside a repo.
|
|
204913
|
+
*/
|
|
204914
|
+
const assertInitLocation = (git, fs) => gen(function* () {
|
|
204915
|
+
const topLevel = yield* either(git.topLevel());
|
|
204916
|
+
if (topLevel._tag === "Left") return false;
|
|
204917
|
+
if ((yield* fs.realPath(topLevel.right)) !== (yield* fs.realPath(process.cwd()))) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init must be run from the repository root (${topLevel.right}) or from a directory outside any git repository; the current directory is a repository subdirectory: ${process.cwd()}`));
|
|
204918
|
+
return true;
|
|
204919
|
+
});
|
|
204815
204920
|
/** Dispatches to the named `run*Command` handler for every known subcommand. */
|
|
204816
204921
|
const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
|
|
204817
204922
|
switch (sub) {
|
package/docs/configuration.md
CHANGED
|
@@ -92,11 +92,23 @@ engine, and [§3](../STATES.md#3-pattern-grammar) for the pattern grammar.
|
|
|
92
92
|
### Content values: inline or a file reference
|
|
93
93
|
|
|
94
94
|
A `script`/`prompt`/`message`/`commit` value starting with `./` or `../` is a
|
|
95
|
-
**file reference** — resolved relative to the
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
**file reference** — resolved relative to **the directory of the `.gtdrc` that
|
|
96
|
+
declared it** (not the directory `gtd` runs from) and read at load time. A
|
|
97
|
+
missing or unreadable file reference is a **load error**, collected and thrown
|
|
98
|
+
with every other config problem — never silently treated as inline text. Any
|
|
99
|
+
other string (including one that merely contains a `/`, or an absolute path) is
|
|
100
|
+
inline Eta template source, used verbatim.
|
|
101
|
+
|
|
102
|
+
Because references anchor to the declaring file, a `.gtdrc` and its
|
|
103
|
+
`gtd-prompts/` (or `prompts/`) directory can live **outside** the repo — e.g. in
|
|
104
|
+
a parent directory shared across several repos. gtd still runs from the repo
|
|
105
|
+
root, discovers the parent `.gtdrc` by walking up the directory chain (see
|
|
106
|
+
["Lookup and precedence"](#lookup-and-precedence) — cwd→home), and inlines each
|
|
107
|
+
level's `./`-relative references against that level's own directory. When
|
|
108
|
+
workflows are layered across levels, every reference resolves against the file
|
|
109
|
+
that wrote it, so a parent's `./gtd-prompts/x.md` and a child's never collide.
|
|
110
|
+
You can scaffold such a shared config by running [`gtd init`](#gtd-init) from
|
|
111
|
+
that parent directory — it need not be a git repository.
|
|
100
112
|
|
|
101
113
|
```yaml
|
|
102
114
|
workflow:
|
|
@@ -677,6 +689,16 @@ load time, so a prompt is editable Markdown and editing it changes the workflow
|
|
|
677
689
|
with no config edit. Human `message:` blocks and check `script:` bodies stay
|
|
678
690
|
inline in the config (they are workflow mechanics, not prompts).
|
|
679
691
|
|
|
692
|
+
`gtd init` writes only config — it derives no git state — so it need not run in
|
|
693
|
+
a git repository. It runs at a **repository root** or in a directory **outside
|
|
694
|
+
any repository** (scaffolding a shared config a nested repo picks up by walking
|
|
695
|
+
up — see the parent-dir note under
|
|
696
|
+
[file references](#content-values-inline-or-a-file-reference)); its guidance
|
|
697
|
+
adjusts to say there is nothing to commit when it is not in a repo. It refuses
|
|
698
|
+
only to write into a repository **subdirectory**, where the upward config walk
|
|
699
|
+
would never find the file. Either way, it will not overwrite an existing config
|
|
700
|
+
at that directory (remove it first to re-scaffold).
|
|
701
|
+
|
|
680
702
|
It also seeds a top-level [`modes:`](#modes--pluggable-steering-file-modes)
|
|
681
703
|
block as a ready-to-edit suggestion — a Prettier `format:` for each built-in
|
|
682
704
|
steering-file mode the template uses:
|