@pmelab/gtd 6.7.0 → 6.9.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.
@@ -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
- /** Exactly one of script/prompt/message/commit, each a string, file-refs auto-inlined. */
189887
- const compileContent = (raw, name, configDir, errors) => {
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. Throws a single
190023
- * `Error` (message: `"workflow config:\n - ..."`, one line per finding) on
190024
- * ANY config-shape problem or `validateDefinition` finding — never partially
190025
- * succeeds.
190026
- */
190027
- const compileWorkflowConfig = (raw, configDir, rcModes) => {
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
@@ -190089,7 +190146,7 @@ var Cwd = class Cwd extends Tag("Cwd")() {
190089
190146
  * schema.ts` (run via `jiti`, a plain TS-via-Babel loader with no bundler-
190090
190147
  * style pluggable per-extension loaders) can import JUST the schema without
190091
190148
  * risking a transitive pull into the bundled workflow templates
190092
- * (`./workflows/templates.js`, which imports `simple.yaml`/`advanced.yaml` as
190149
+ * (`./workflows/templates.js`, which imports `unified.yaml` as
190093
190150
  * raw text via tsdown's/vitest's `.yaml`-as-text loader — something `jiti` has
190094
190151
  * no equivalent for and doesn't need here): the schema shape never depends on
190095
190152
  * any workflow's content.
@@ -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
- const home = homedir();
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
  });
@@ -190393,21 +190478,22 @@ const compileRcVars = (raw) => {
190393
190478
  * there is nothing to fall back to — the user must scaffold one with
190394
190479
  * `gtd init`. Exported so the CLI layer and tests can assert on it verbatim.
190395
190480
  */
190396
- const NO_WORKFLOW_MESSAGE = "gtd: no workflow configured — run `gtd init <simple|advanced>` to create .gtdrc.json";
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`. `root` is used as the
190400
- * workflow compiler's `configDir` — the directory a custom workflow's
190401
- * `./`-relative content references resolve against. Throws `NO_WORKFLOW_MESSAGE`
190402
- * when no `workflow:` key is present (there is no bundled default to fall back
190403
- * to — see `gtd init`), or (via `compileWorkflowConfig`/`compileRcVars`) on any
190404
- * invalid workflow/vars.
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,
@@ -191385,11 +191471,8 @@ var WorktreeReader = class WorktreeReader extends Tag("WorktreeReader")() {
191385
191471
  }));
191386
191472
  };
191387
191473
  //#endregion
191388
- //#region src/workflows/advanced.yaml
191389
- var advanced_default = "# The \"advanced\" gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init advanced`. See\n# docs/examples/advanced-workflow.md for the full state-by-state walkthrough\n# and STATES.md §10 for how it relates to the \"simple\" template.\n#\n# This is the fuller machine: two-phase human-in-the-loop planning\n# (grilling ⇄ grilling-answer for the product plan, architecting ⇄\n# architecting-answer for the technical plan), task decomposition\n# (decompose → .gtd/tasks/), a deterministic per-task builder queue (picking →\n# building → checking/fixing), agent-prepared review (reviewing → await-review),\n# and a squash finale (squashing → done, a `commit:` state that collapses the\n# whole cycle into one commit whose message the agent authored in\n# .gtd/COMMIT_MSG.md).\n#\n# Every content string below is INLINE (no `./`-relative file references): the\n# bundled templates ship inside a single-file build (`dist/gtd.bundle.mjs`), so\n# their content must already be resolved at BUILD time rather than read from\n# disk at RUNTIME. `advanced.yaml` is imported as raw text (tsdown's `.yaml`\n# text loader / the vitest rawMd transform), so this module never touches the\n# filesystem.\n#\n# `gtd init advanced` DERIVES the scaffolded `.gtdrc.json` from this inline\n# source (src/workflows/templates.ts `renderInitScaffold`): each agent state's\n# `prompt:` below is written out as an editable `gtd-prompts/<state>.md` file\n# and the config value is rewritten to a `./gtd-prompts/<state>.md` reference\n# `compileWorkflowConfig` inlines back at load time. Human `message:`/check\n# `script:` bodies and the `done` `commit:` template stay inline in the config.\n# This YAML stays fully inline regardless — the split happens only at scaffold\n# time, on real disk.\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n architectureFile: .gtd/ARCHITECTURE.md\n reviewFile: .gtd/REVIEW.md\n feedbackFile: .gtd/FEEDBACK.md\n # The two model tiers every agent state draws from (see the model comment at\n # `grilling` below). Both are opaque harness hints — repoint them here (or\n # via a top-level `.gtdrc` `vars:` key / a `GTD_VAR_plannerModel` env var) to\n # change every state on that tier at once.\n plannerModel: smart\n coderModel: base\n\nstates:\n idle:\n actor: human\n initial: true\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 — then run `gtd step human`.\n on:\n \"* **\": 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 provides.\n # Every agent state draws its tier from a `vars` model entry (see `vars:`\n # above) so the tiers repoint in one place: `plannerModel` (default\n # \"smart\") names the heavier tier for the one-shot planning/architecting/\n # reviewing turns, `coderModel` (default \"base\") the tier for the\n # decompose/build/fix/squash turns.\n model: <%= it.vars.plannerModel %>\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state (plans, task specs, review records) — never create, edit, or\n delete anything under `.gtd/` except `<%= it.vars.todoFile %>`, which\n this prompt tells you to write.\n\n `<%= it.vars.todoFile %>` holds the plan under development. Read it,\n then develop it into a concrete, product-level plan in this one turn —\n explore the codebase before asking anything, so every open question is\n one the codebase genuinely cannot answer.\n\n Scope: product and user-facing decisions only. Leave implementation\n details (file/module structure, data models, tech-stack choices) for\n the next phase (`.gtd/ARCHITECTURE.md`).\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\n your suggested answer in plain prose (always suggest one — the human\n accepts it by leaving it untouched, or overrides it by editing it). Omit\n the section entirely once there are none.\n\n If `.gtd/TODO.md` already carries a `## Open Questions` section from a\n previous lap, the human has now responded to that whole batch: fold every\n one of those questions into the plan — the human's edit where they gave\n one, your own suggestion where they left it untouched (an untouched\n suggestion IS acceptance) — and MOVE each resolved `### <question>` block\n down into a `## Answered Questions` section at the bottom. Raise any\n genuinely new questions under `## Open Questions` at the top.\n\n Leave `.gtd/TODO.md` uncommitted and finish your turn — do not commit,\n and do not run `gtd step agent` yourself; the harness does 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.\n\n To answer a question, edit its body in place, then run\n `gtd step human`; leave a suggestion untouched to accept it as-is. On the\n next lap the agent folds every answered question into the plan and moves\n it down to `## Answered Questions`.\n\n To accept all suggested answers as-is and move on to technical\n architecting, run `gtd step human` with no edits.\n on:\n \"C\": architecting\n \"* **\": grilling\n\n architecting:\n actor: agent\n file: <%= it.vars.architectureFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\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.todoFile %>`, which\n this prompt tells you to write and delete respectively.\n\n Read `<%= it.vars.todoFile %>` (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 from\n `<%= it.vars.todoFile %>`; treat them as settled.\n\n For every remaining open question, add it under a `## Open Questions`\n heading the same way as the grilling phase (one `### <question>`\n sub-heading, its body your suggested answer in plain prose). Omit the\n section once there are none. On a return lap, fold the whole answered\n batch into the plan and move each resolved `### <question>` block down\n into a `## Answered Questions` section at the bottom, exactly as grilling\n does.\n\n Once `<%= it.vars.architectureFile %>` is written, delete\n `<%= it.vars.todoFile %>` — its content has been folded in and it must\n not linger.\n\n 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`.\n\n To answer a question, edit its body in place, then run `gtd step human`;\n leave a suggestion untouched to accept it as-is.\n\n To accept all suggested answers as-is and move on to decomposing the\n work, run `gtd step human` with no edits.\n on:\n \"C\": decompose\n \"* **\": architecting\n\n decompose:\n actor: agent\n model: <%= it.vars.coderModel %>\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 task-spec files this prompt tells you to write (under `.gtd/tasks/`),\n and `.gtd/ARCHITECTURE.md`, which it tells you to delete.\n\n Read `.gtd/ARCHITECTURE.md` (the converged technical plan) and\n decompose it into an ordered set of self-contained task files under\n `.gtd/tasks/` (e.g. `.gtd/tasks/01-short-name.md`,\n `.gtd/tasks/02-short-name.md`, ...). Order them so the test suite can\n pass again after each one lands; each file is self-contained\n (description, acceptance criteria as `- [ ]` checkboxes, relevant\n paths) since a building turn receives only that one file as context.\n\n No task file may reference any other `.gtd/` file, or a requirement to\n preserve/update workflow state — `.gtd/` is machine-managed and gets\n cleaned up as the cycle proceeds.\n\n Once the task files are written, delete `.gtd/ARCHITECTURE.md`. Leave\n everything uncommitted and finish your turn.\n on:\n \"* .gtd/tasks/**\": picking\n\n picking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the queue arbiter) — the loop driver executes this\n # verbatim, then steps the check actor. Mechanics only: take the\n # first task file (by name) under .gtd/tasks/ into .gtd/NEXT.md, or\n # remove .gtd/NEXT.md when the queue is empty. What NEXT.md's\n # presence/absence MEANS (another task to build vs. the queue closing\n # out to review) is decided by this state's own `on` rules at capture\n # time — never here.\n set +e\n next=$(ls .gtd/tasks/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > .gtd/NEXT.md\n else\n rm -f .gtd/NEXT.md\n fi\n on:\n \"D .gtd/NEXT.md\": reviewing\n \"* .gtd/NEXT.md\": building\n \"C\": reviewing\n\n building:\n actor: agent\n model: <%= it.vars.coderModel %>\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 the\n one task file this prompt tells you to delete; never touch\n `.gtd/NEXT.md` yourself — the `picking` state owns it.\n\n The task file to implement is: <%~ it.read(\".gtd/NEXT.md\") %>\n\n Read that file and implement EXACTLY the one task it describes — no\n more, no less; leave every other task file under `.gtd/tasks/`\n untouched. Use TDD discipline: one test, then the implementation that\n passes it, then the next — never write all the tests first.\n\n Delete the task file once its work is complete and verified. Leave\n 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 steps\n # 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 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 .gtd/FEEDBACK.md\n else\n rm -f .gtd/.check-output\n printf 'npm test failed with exit code %s and produced no output.' \"$code\" > .gtd/FEEDBACK.md\n fi\n else\n rm -f .gtd/.check-output\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"C\": picking\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\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 Investigate and fix it yourself (editing code and/or\n `<%= it.vars.feedbackFile %>`), then run `gtd step human` to try the\n check again.\n on:\n \"* **\": checking\n\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: <%= it.vars.plannerModel %>\n # A direct entry point: `gtd review <commitish>` starts a fresh review\n # process here over `<commitish>..HEAD` (e.g. a colleague's PR branch).\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 cycle's\n full diff (inlined below): group hunks semantically (same\n feature/refactor/fix, even across files), and for each group write a\n short heading, an explanation of what changed and why, and\n `- [ ] ./path/to/file.ts#42` style file pointers (checkboxes are for\n the human to tick, not you).\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 \"* .gtd/REVIEW.md\": 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.\n\n To approve: delete `<%= it.vars.reviewFile %>` and run\n `gtd step human`.\n\n To request changes: edit `<%= it.vars.reviewFile %>` (or the code)\n with what's wrong — including just ticking its checkboxes — then run\n `gtd step human`; this sends the cycle back through grilling with your\n feedback as the new input.\n on:\n \"D .gtd/REVIEW.md\": squashing\n \"* **\": grilling\n\n squashing:\n actor: agent\n model: <%= it.vars.coderModel %>\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 `.gtd/COMMIT_MSG.md`, which this prompt\n tells you to write.\n\n Write `.gtd/COMMIT_MSG.md` with ONE conventional-commits message for\n the entire cycle (subject line `type(scope): subject`, imperative mood,\n <= 72 characters; a body explaining the why, trade-offs, and key\n decisions). Plain text, no markdown wrapper.\n\n Leave `.gtd/COMMIT_MSG.md` uncommitted and finish your turn — entering\n this file squashes the whole cycle into one commit using its content as\n 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(\".gtd/COMMIT_MSG.md\") %>\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n";
191390
- //#endregion
191391
- //#region src/workflows/simple.yaml
191392
- var simple_default = "# The \"simple\" gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init simple`. gtd ships no default\n# workflow: a repo picks one of the bundled templates 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 walkthrough and docs/examples/advanced-workflow.md\n# for the fuller `advanced` template.\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).\n#\n# Every content string below is INLINE (no `./`-relative file references):\n# the bundled templates ship inside a single-file build\n# (`dist/gtd.bundle.mjs`), so their content must already be resolved at BUILD\n# time rather than read from disk at RUNTIME. `simple.yaml` itself is imported\n# as raw text (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# `gtd init simple` DERIVES the scaffolded `.gtdrc.json` from this inline\n# source (src/workflows/templates.ts `renderInitScaffold`): each agent state's\n# `prompt:` below is written out as an editable `gtd-prompts/<state>.md` file\n# and the config value is rewritten to a `./gtd-prompts/<state>.md` reference\n# `compileWorkflowConfig` inlines back at load time. Human `message:`/check\n# `script:` bodies stay inline in the config. This YAML stays fully inline\n# regardless — the split happens only at scaffold time, on real disk.\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): answer a question by\n# editing its body in place, or leave a suggestion untouched to accept it;\n# a clean step accepts every remaining suggestion -> building; any edit\n# loops back to grilling (which folds the whole answered batch into the\n# plan and moves it down to `## Answered Questions`).\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. `reviewing`\n# ALSO declares `reviewEntry: true` — a second way in: `gtd review\n# <commitish>` (a clean tree resting at idle) writes an empty\n# `gtd(human): reviewing` entry commit carrying <commitish>'s hash as a\n# `Gtd-Review-Base:` trailer, starting a brand new process that reviews\n# <commitish>..HEAD (e.g. a colleague's PR branch) through this exact same\n# reviewing/await-review/feedback-lap machinery.\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>): <from> → idle` commit — entering the workflow's own initial\n# state — 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# Every agent state draws its `model` from a `vars` tier entry (see `vars:`\n# below and the comment at `grilling`) rather than a hardcoded string, so both\n# tiers repoint in one place: `plannerModel` (default \"smart\") for the heavier\n# one-shot planning/reviewing turns (`grilling`, `reviewing`), `coderModel`\n# (default \"base\") for the coding turns (`building`, `fixing`). Both are opaque\n# hints the driving loop maps onto its harness.\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 # The two model tiers every agent state draws from (see the model comment at\n # `grilling` below). Both are opaque harness hints — repoint them here (or\n # via a top-level `.gtdrc` `vars:` key / a `GTD_VAR_plannerModel` env var) to\n # change every state on that tier at once.\n plannerModel: smart\n coderModel: base\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 provides.\n # Every agent state draws its tier from a `vars` model entry (see `vars:`\n # above) so the tiers repoint in one place: `plannerModel` (default\n # \"smart\") names the heavier tier for the one-shot planning/reviewing\n # turns, `coderModel` (default \"base\") the tier for the coding turns.\n model: <%= it.vars.plannerModel %>\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 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 no open\n questions.\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 — using the\n human's edit where they gave one, your own suggested answer where they\n left it untouched (an untouched suggestion IS acceptance) — and MOVE each\n resolved `### <question>` block down into a `## Answered Questions`\n section at the very bottom, keeping it as a record. Raise any genuinely\n new questions this lap under `## Open Questions` at the top.\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 as-is. On the next lap the agent folds\n every answered question into the plan and moves it down to\n `## Answered Questions`.\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 # 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 model: <%= it.vars.coderModel %>\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: <%= it.vars.plannerModel %>\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 # `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\n # existing reviewing/await-review/feedback-lap machinery unmodified over\n # <commitish>..HEAD (see STATES.md §11).\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 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";
191474
+ //#region src/workflows/unified.yaml
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 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";
191393
191476
  //#endregion
191394
191477
  //#region src/workflows/templates.ts
191395
191478
  /**
@@ -191409,7 +191492,7 @@ const PROMPTS_DIR = "gtd-prompts";
191409
191492
  /**
191410
191493
  * The top-level `modes:` block `gtd init` SEEDS into the scaffolded
191411
191494
  * `.gtdrc.json` as a ready-to-edit suggestion: a `format:` command for each
191412
- * built-in steering-file mode the bundled templates use (`qa` for the plan /
191495
+ * built-in steering-file mode the bundled template uses (`qa` for the plan /
191413
191496
  * open-questions files, `review` for REVIEW.md), so a fresh project's steering
191414
191497
  * files are auto-formatted with Prettier before gtd validates them. Only
191415
191498
  * `format:` is declared — gtd's built-in `qa`/`review` validators still do the
@@ -191420,40 +191503,36 @@ const PROMPTS_DIR = "gtd-prompts";
191420
191503
  *
191421
191504
  * Seeded only by `renderInitScaffold` (the real `gtd init` write path), NOT by
191422
191505
  * `renderInitConfig`: the latter is reused as a hermetic test fixture (the
191423
- * `Given the "…" workflow` step) that must not depend on Prettier being
191424
- * installed or spawn a subprocess at every steering-file gate.
191506
+ * `Given the workflow` step) that must not depend on Prettier being installed
191507
+ * or spawn a subprocess at every steering-file gate.
191425
191508
  */
191426
191509
  const MODES_SUGGESTION = {
191427
191510
  qa: { format: "npx prettier --write <%= it.file %>" },
191428
191511
  review: { format: "npx prettier --write <%= it.file %>" }
191429
191512
  };
191430
191513
  /**
191431
- * The bundled workflow templates `gtd init <name>` can scaffold, keyed by the
191432
- * name the user passes. Each value is the raw YAML text of the template file,
191433
- * imported as a string (tsdown's `.yaml` text loader / the vitest `rawMd`
191434
- * transform — see tsdown.config.ts / tests/vitest.rawMd.ts), so this module
191435
- * never touches the filesystem: it works identically in the dev checkout,
191436
- * under `vitest`, and inside the single-file `dist/gtd.bundle.mjs` build.
191514
+ * The single bundled workflow template `gtd init` scaffolds — the raw YAML text
191515
+ * of `unified.yaml`, imported as a string (tsdown's `.yaml` text loader / the
191516
+ * vitest `rawMd` transform — see tsdown.config.ts / tests/vitest.rawMd.ts), so
191517
+ * this module never touches the filesystem: it works identically in the dev
191518
+ * checkout, under `vitest`, and inside the single-file `dist/gtd.bundle.mjs`
191519
+ * build. gtd ships NO default workflow and no longer offers a choice of
191520
+ * templates — `gtd init` takes no argument.
191437
191521
  */
191438
- const WORKFLOW_TEMPLATES = {
191439
- simple: simple_default,
191440
- advanced: advanced_default
191441
- };
191442
- /** The template names as a plain array, for usage messages and validation. */
191443
- const WORKFLOW_TEMPLATE_NAMES = Object.keys(WORKFLOW_TEMPLATES);
191444
- const isWorkflowTemplateName = (name) => Object.prototype.hasOwnProperty.call(WORKFLOW_TEMPLATES, name);
191522
+ const UNIFIED_WORKFLOW = unified_default;
191445
191523
  /**
191446
- * Render everything `gtd init <name>` writes. Like `renderInitConfig`, but each
191447
- * agent state's inline `prompt:` is EXTRACTED into a standalone
191524
+ * Render everything `gtd init` writes. Like `renderInitConfig`, but each agent
191525
+ * state's inline `prompt:` is EXTRACTED into a standalone
191448
191526
  * `gtd-prompts/<state>.md` file and the config value is rewritten to a
191449
191527
  * `./gtd-prompts/<state>.md` file reference — `compileWorkflowConfig` inlines
191450
191528
  * it back at load time (`resolveContent`), so the workflow behaves identically
191451
191529
  * to the fully-inline `renderInitConfig` form while the prompts stay editable
191452
191530
  * as ordinary Markdown. Only `prompt:` content is externalized: human
191453
- * `message:` blocks and check `script:` bodies remain inline in the config
191454
- * (they are workflow mechanics, not prompts). The bundled YAML template itself
191455
- * stays fully inline (it ships inside the single-file bundle); this function is
191456
- * the only place the split happens, at scaffold time.
191531
+ * `message:` blocks, check `script:` bodies, and the `done` `commit:` template
191532
+ * remain inline in the config (they are workflow mechanics, not prompts). The
191533
+ * bundled YAML template itself stays fully inline (it ships inside the
191534
+ * single-file bundle); this function is the only place the split happens, at
191535
+ * scaffold time.
191457
191536
  *
191458
191537
  * It also seeds a top-level `modes:` block (`MODES_SUGGESTION`) — a ready-to-
191459
191538
  * edit Prettier `format:` for the built-in `qa`/`review` steering-file modes —
@@ -191461,8 +191540,8 @@ const isWorkflowTemplateName = (name) => Object.prototype.hasOwnProperty.call(WO
191461
191540
  * here rather than in `renderInitConfig` so the latter stays a hermetic test
191462
191541
  * fixture (see `MODES_SUGGESTION`).
191463
191542
  */
191464
- const renderInitScaffold = (name) => {
191465
- const workflow = (0, import_dist$1.parse)(WORKFLOW_TEMPLATES[name]);
191543
+ const renderInitScaffold = () => {
191544
+ const workflow = (0, import_dist$1.parse)(UNIFIED_WORKFLOW);
191466
191545
  const prompts = [];
191467
191546
  for (const [stateName, state] of Object.entries(workflow.states ?? {})) if (typeof state.prompt === "string") {
191468
191547
  const path = `${PROMPTS_DIR}/${stateName}.md`;
@@ -192174,11 +192253,24 @@ const toTemplateEdges = (edges) => (edges ?? []).map(([pattern, target, describe
192174
192253
  * breakdown including the squashing step) — `0`/absent for the pure emitters
192175
192254
  * (`gtd next`/`gtd status`), where no step is being performed.
192176
192255
  */
192177
- const buildTemplateContext = (git, read, state, actor, run, vars, edges, currentCost = 0, currentModel) => gen(function* () {
192256
+ /** Join a committed diff and the pending working-tree diff, dropping empties. */
192257
+ const joinDiffs = (committed, pending) => [committed, pending].filter((d) => d.trim().length > 0).join("\n\n");
192258
+ /**
192259
+ * The committed half of `it.reviewDiff`: the diff from `reviewDiffBase` (the
192260
+ * previous review round's boundary) when it is a distinct base, else the
192261
+ * already-computed process-wide `committedDiff` (first review — reviewDiff
192262
+ * collapses to processDiff).
192263
+ */
192264
+ const committedReviewDiffOf = (git, processDiffBase, reviewDiffBase, committedDiff) => reviewDiffBase !== void 0 && reviewDiffBase !== processDiffBase ? git.diffRef(reviewDiffBase).pipe(catchAll(() => succeed$3(""))) : succeed$3(committedDiff);
192265
+ const buildTemplateContext = (git, read, state, actor, run, vars, edges, currentCost = 0, currentModel, reviewDiffBase) => gen(function* () {
192178
192266
  const hasCommits = yield* git.hasCommits();
192179
192267
  const currentCommit = hasCommits ? yield* git.resolveRef("HEAD") : "";
192180
192268
  const previousCommit = hasCommits ? yield* git.resolveRef("HEAD~1").pipe(catchAll(() => succeed$3(run.startParentHash))) : "";
192181
- const processDiff = [yield* git.diffRef(run.diffBase).pipe(catchAll(() => succeed$3(""))), yield* git.diffHead().pipe(catchAll(() => succeed$3("")))].filter((d) => d.trim().length > 0).join("\n\n");
192269
+ const committedDiff = yield* git.diffRef(run.diffBase).pipe(catchAll(() => succeed$3("")));
192270
+ const pendingDiff = yield* git.diffHead().pipe(catchAll(() => succeed$3("")));
192271
+ const processDiff = joinDiffs(committedDiff, pendingDiff);
192272
+ const committedReviewDiff = yield* committedReviewDiffOf(git, run.diffBase, reviewDiffBase, committedDiff);
192273
+ const reviewDiff = joinDiffs(committedReviewDiff, pendingDiff);
192182
192274
  const lastDiff = run.trace.length > 0 ? yield* git.commitDiff(currentCommit).pipe(catchAll(() => succeed$3(""))) : "";
192183
192275
  const stepEntry = currentCost > 0 || currentModel !== void 0 ? [{
192184
192276
  cost: currentCost,
@@ -192192,6 +192284,7 @@ const buildTemplateContext = (git, read, state, actor, run, vars, edges, current
192192
192284
  state,
192193
192285
  actor,
192194
192286
  processDiff,
192287
+ reviewDiff,
192195
192288
  lastDiff,
192196
192289
  processCost: totalCostOf(allCostEntries),
192197
192290
  processCostByModel: costByModel(allCostEntries),
@@ -203881,6 +203974,7 @@ const buildFileModeMap = (def, vars, root) => {
203881
203974
  state: name,
203882
203975
  actor: "",
203883
203976
  processDiff: "",
203977
+ reviewDiff: "",
203884
203978
  lastDiff: "",
203885
203979
  processCost: 0,
203886
203980
  processCostByModel: [],
@@ -204245,12 +204339,12 @@ const GTD_VERSION = createRequire(import.meta.url)("../package.json").version;
204245
204339
  const HELP_TEXT = `Usage: gtd [command] [options]
204246
204340
 
204247
204341
  Commands:
204248
- init <workflow> Scaffold a .gtdrc.json for this repo with the chosen
204249
- bundled workflow (one of: simple, advanced); its agent
204250
- prompts are written as editable Markdown under gtd-prompts/
204251
- and referenced from the config. Run once per repo; refuses
204252
- if a gtd config already exists. Leaves the files uncommitted
204253
- for you to review and commit
204342
+ init Scaffold a .gtdrc.json for this repo with the bundled
204343
+ unified workflow; its agent prompts are written as editable
204344
+ Markdown under gtd-prompts/ and referenced from the config.
204345
+ Takes no argument. Run once per repo; refuses if a gtd
204346
+ config already exists. Leaves the files uncommitted for you
204347
+ to review and commit
204254
204348
  step <actor> Authenticate as <actor>, match the resolved rest's
204255
204349
  declared patterns against the pending changes, and commit
204256
204350
  (or squash) the one resulting transition. Pass
@@ -204292,6 +204386,17 @@ Options:
204292
204386
  const ENVELOPED = Symbol.for("gtd/enveloped");
204293
204387
  const markEnveloped = (error) => Object.assign(error, { [ENVELOPED]: true });
204294
204388
  const isEnveloped = (error) => typeof error === "object" && error !== null && error[ENVELOPED] === true;
204389
+ /**
204390
+ * The stderr line for a CLI error (see `main.ts`): a `gtd: ` prefix UNLESS the
204391
+ * message already carries one. Most gtd errors are authored with a
204392
+ * `gtd:`/`gtd <cmd>:` prefix of their own (e.g. `gtd init: …`,
204393
+ * `gtd: no workflow configured …`), so a blind prepend produced a doubled
204394
+ * `gtd: gtd: …`.
204395
+ */
204396
+ const cliErrorLine = (error) => {
204397
+ const message = error instanceof Error ? error.message : String(error);
204398
+ return /^gtd[: ]/.test(message) ? message : `gtd: ${message}`;
204399
+ };
204295
204400
  /** Non-flag positional arguments past the subcommand name (argv[3..]). */
204296
204401
  const commandArgs = (argv) => argv.slice(3).filter((a) => a.length > 0 && !a.startsWith("--"));
204297
204402
  const COST_FLAG = "--cost=";
@@ -204364,13 +204469,12 @@ const runLspCommand = (argv, json) => gen(function* () {
204364
204469
  yield* rejectExtraArgs("lsp", argv);
204365
204470
  yield* startLspServer();
204366
204471
  });
204367
- /** The `choose one of: ...` suffix shared by every `gtd init` usage error. */
204368
- const initWorkflowChoices = () => `choose one of: ${WORKFLOW_TEMPLATE_NAMES.join(", ")}`;
204369
204472
  /**
204370
- * `gtd init <workflow>`: scaffold a `.gtdrc.json` carrying one of the bundled
204371
- * workflow templates inline (`simple`/`advanced`). Dispatched like `lsp` —
204372
- * BEFORE the closeReviewWindow/dispatch/openReviewWindow block — because it is
204373
- * the ONE command that must run with no workflow configured yet: it needs no
204473
+ * `gtd init`: scaffold a `.gtdrc.json` carrying the bundled unified workflow
204474
+ * template inline. Takes NO argument — gtd ships a single template and no
204475
+ * longer offers a choice. Dispatched like `lsp` — BEFORE the
204476
+ * closeReviewWindow/dispatch/openReviewWindow block — because it is the ONE
204477
+ * command that must run with no workflow configured yet: it needs no
204374
204478
  * `ConfigService` (which would throw the "no workflow" error pre-init) and no
204375
204479
  * review window. It still runs the repo-root guard (it writes `.gtdrc.json` at
204376
204480
  * the root) and refuses to clobber an existing config. It also writes each
@@ -204383,17 +204487,14 @@ const initWorkflowChoices = () => `choose one of: ${WORKFLOW_TEMPLATE_NAMES.join
204383
204487
  */
204384
204488
  const runInitCommand = (argv, json, write) => gen(function* () {
204385
204489
  const args = commandArgs(argv);
204386
- if (args.length === 0) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: missing workflow — ${initWorkflowChoices()}`));
204387
- if (args.length > 1) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: too many arguments — expected one workflow, got: ${args.join(", ")}`));
204388
- const name = args[0];
204389
- if (!isWorkflowTemplateName(name)) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: unknown workflow '${name}' — ${initWorkflowChoices()}`));
204490
+ if (args.length > 0) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: too many arguments — init takes no argument, got: ${args.join(", ")}`));
204390
204491
  const git = yield* GitService;
204391
204492
  const fs = yield* FileSystem;
204392
- yield* assertRunningFromRepoRoot(git, fs);
204493
+ const inRepo = yield* assertInitLocation(git, fs);
204393
204494
  const { root } = yield* Cwd;
204394
204495
  if (yield* configPresentAt(root)) return yield* fail$2(/* @__PURE__ */ new Error("gtd init: a gtd config already exists — remove it before re-initializing"));
204395
204496
  const toError = (e) => e instanceof Error ? e : new Error(String(e));
204396
- const scaffold = renderInitScaffold(name);
204497
+ const scaffold = renderInitScaffold();
204397
204498
  yield* fs.writeFileString(join(root, ".gtdrc.json"), scaffold.config).pipe(mapError$1(toError));
204398
204499
  for (const prompt of scaffold.prompts) {
204399
204500
  const full = join(root, prompt.path);
@@ -204402,12 +204503,13 @@ const runInitCommand = (argv, json, write) => gen(function* () {
204402
204503
  }
204403
204504
  if (json) write(JSON.stringify({
204404
204505
  written: ".gtdrc.json",
204405
- workflow: name,
204406
- prompts: scaffold.prompts.map((p) => p.path)
204506
+ workflow: "unified",
204507
+ prompts: scaffold.prompts.map((p) => p.path),
204508
+ inRepo
204407
204509
  }) + "\n");
204408
204510
  else {
204409
204511
  const promptCount = scaffold.prompts.length;
204410
- write(`Wrote .gtdrc.json with the "${name}" workflow, and its ${promptCount} agent prompt${promptCount === 1 ? "" : "s"} as editable Markdown under ${PROMPTS_DIR}/ (referenced from the config).\n\nReview 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`);
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"));
204411
204513
  }
204412
204514
  });
204413
204515
  /**
@@ -204425,10 +204527,11 @@ const resolveRestContext = (git) => gen(function* () {
204425
204527
  const rest = yield* resolveRest();
204426
204528
  const run = yield* computeProcessRun(git, rest.def);
204427
204529
  const vars = resolveVars(config.workflowVars, config.rcVars, envVars.all);
204530
+ const reviewBase = yield* reviewBaseHash(git, rest.def, run);
204428
204531
  return {
204429
204532
  rest,
204430
204533
  run,
204431
- context: yield* buildTemplateContext(git, worktree.read, rest.state, rest.actor, run, vars, rest.stateDef.on)
204534
+ context: yield* buildTemplateContext(git, worktree.read, rest.state, rest.actor, run, vars, rest.stateDef.on, 0, void 0, reviewBase)
204432
204535
  };
204433
204536
  });
204434
204537
  /** The user-facing message for a `step` refusal — out-of-turn names the awaited actor, no-match names every declared pattern. */
@@ -204796,6 +204899,24 @@ const assertRunningFromRepoRoot = (git, fs) => gen(function* () {
204796
204899
  const topLevel = yield* git.topLevel();
204797
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()}`));
204798
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
+ });
204799
204920
  /** Dispatches to the named `run*Command` handler for every known subcommand. */
204800
204921
  const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
204801
204922
  switch (sub) {
@@ -204817,7 +204938,7 @@ const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
204817
204938
  *
204818
204939
  * v3 command surface: `step <actor>` / `next` / `status` / `validate` /
204819
204940
  * `mermaid` (see `src/Edge.ts` and `docs/design/pattern-machine-plan.md` §3),
204820
- * plus `lsp` and `init <workflow>` — both dispatched before the config-reading
204941
+ * plus `lsp` and `init` — both dispatched before the config-reading
204821
204942
  * path since they must work with no workflow configured yet. Bare `gtd` or an
204822
204943
  * unknown subcommand is a usage error. Shared setup (argv parsing, the
204823
204944
  * repo-root guard) lives here; each subcommand's own logic is a named
@@ -204857,7 +204978,7 @@ makeProgram().pipe(provide$1(GitService.Live), provide$1(ConfigService.Live), pr
204857
204978
  state: "error",
204858
204979
  prompt: error.message
204859
204980
  }) + "\n");
204860
- process.stderr.write(`gtd: ${error.message ?? String(error)}\n`);
204981
+ process.stderr.write(cliErrorLine(error) + "\n");
204861
204982
  process.exit(1);
204862
204983
  })), runMain);
204863
204984
  //#endregion