@pmelab/gtd 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,6 +39,11 @@ or `REVIEW.md` at the repository root is the project's own file — gtd never
39
39
  reads, consumes, or deletes it. (Corollary: don't gitignore `.gtd/` — the
40
40
  workflow commits its state through it.)
41
41
 
42
+ A squash commit's message can carry a `## Decisions` section — one entry per
43
+ architecture/product question resolved that cycle. Grilling/architecting read
44
+ every past squash commit's `## Decisions` section back as "Prior decisions"
45
+ context, oldest to newest. See `decisionLog` under Configuration.
46
+
42
47
  ## Quick start: the two-beat loop
43
48
 
44
49
  gtd splits what used to be one mutating command into three:
@@ -750,19 +755,20 @@ With `squash: true` (the default), `gtd: done` (or, once learning has run,
750
755
  to `gtd: squash template`, writing and committing a `.gtd/SQUASH_MSG.md`
751
756
  template. `gtd next` then emits the squashing prompt: the agent overwrites
752
757
  `.gtd/SQUASH_MSG.md` with a real conventional-commits message (drawing on
753
- grilling- and architecting-round decisions from history) and finishes its turn.
754
- `gtd step-agent` then performs the squash itself: `git reset --soft <base>` +
755
- `git commit`, collapsing every intermediate `gtd: *` commit of the cycle into
756
- one — including any review-feedback detours, and the learning phase's own
757
- commits if learning ran: the squash base is the cycle's ORIGINAL start (the
758
- first grilling or, via the escape hatch, architecting turn since the previous
759
- `gtd: done` boundary, or the `gtd: reviewing <hash>` anchor for an ad-hoc review
760
- cycle), not the most recent re-grilling round — the collapse folds the whole
761
- cycle into one, using the overwritten message's content verbatim (turn position,
762
- not message content, triggers the squash). Doc edits made during
763
- `learning-apply` survive in the squashed tree, not as their own commit. With
764
- `squash: false`, `gtd: done` (or `gtd: learning applied`) is the resting
765
- boundary and no template is ever written.
758
+ grilling- and architecting-round decisions from history, and, when this cycle
759
+ resolved any open questions, a `## Decisions` section recording them) and
760
+ finishes its turn. `gtd step-agent` then performs the squash itself:
761
+ `git reset --soft <base>` + `git commit`, collapsing every intermediate `gtd: *`
762
+ commit of the cycle into one — including any review-feedback detours, and the
763
+ learning phase's own commits if learning ran: the squash base is the cycle's
764
+ ORIGINAL start (the first grilling or, via the escape hatch, architecting turn
765
+ since the previous `gtd: done` boundary, or the `gtd: reviewing <hash>` anchor
766
+ for an ad-hoc review cycle), not the most recent re-grilling round — the
767
+ collapse folds the whole cycle into one, using the overwritten message's content
768
+ verbatim (turn position, not message content, triggers the squash). Doc edits
769
+ made during `learning-apply` survive in the squashed tree, not as their own
770
+ commit. With `squash: false`, `gtd: done` (or `gtd: learning applied`) is the
771
+ resting boundary and no template is ever written.
766
772
 
767
773
  ### Health check
768
774
 
@@ -849,6 +855,10 @@ built-in defaults apply. Supported filenames (searched in this order):
849
855
  `.gtd/LEARNINGS.md`, have a human review them, then integrate them into the
850
856
  project's own docs before the squash decision runs. Set `false` to skip the
851
857
  phase entirely — independent of `squash`.
858
+ - **`decisionLog`** (boolean, default `true`) — squashing records this cycle's
859
+ resolved open questions as a `## Decisions` section in the squash commit
860
+ message; grilling/architecting inline every past squash commit's section,
861
+ oldest to newest, as prior-decision context. Set `false` to skip both.
852
862
  - **`models`** — model selection for the subagent-spawning states:
853
863
  - `planning` — high-reasoning tier (default `claude-opus-4-8`), used by
854
864
  `decompose` (the `grilled`/`planning` states), `grilling`, `architecting`,
@@ -913,6 +923,7 @@ reviewThreshold: 3
913
923
  agenticReview: true
914
924
  squash: true
915
925
  learning: true
926
+ decisionLog: true
916
927
  models:
917
928
  planning: claude-opus-4-8
918
929
  execution: claude-sonnet-4-8
@@ -189138,6 +189138,7 @@ const DEFAULT_TEST_COMMAND = "npm run test";
189138
189138
  const DEFAULT_AGENTIC_REVIEW = true;
189139
189139
  const DEFAULT_SQUASH = true;
189140
189140
  const DEFAULT_LEARNING = true;
189141
+ const DEFAULT_DECISION_LOG = true;
189141
189142
  const DEFAULT_FIX_ATTEMPT_CAP = 3;
189142
189143
  const DEFAULT_REVIEW_THRESHOLD = 3;
189143
189144
  const ModelStatesSchema = Struct({
@@ -189160,6 +189161,7 @@ const ConfigSchema = Struct({
189160
189161
  agenticReview: optional(Boolean$),
189161
189162
  squash: optional(Boolean$),
189162
189163
  learning: optional(Boolean$),
189164
+ decisionLog: optional(Boolean$),
189163
189165
  fixAttemptCap: optional(Int.pipe(greaterThanOrEqualTo(0))),
189164
189166
  reviewThreshold: optional(Int.pipe(greaterThanOrEqualTo(1)))
189165
189167
  });
@@ -189300,6 +189302,7 @@ const toOperations = (decoded) => {
189300
189302
  agenticReview: decoded.agenticReview ?? DEFAULT_AGENTIC_REVIEW,
189301
189303
  squash: decoded.squash ?? DEFAULT_SQUASH,
189302
189304
  learning: decoded.learning ?? DEFAULT_LEARNING,
189305
+ decisionLog: decoded.decisionLog ?? DEFAULT_DECISION_LOG,
189303
189306
  fixAttemptCap: decoded.fixAttemptCap ?? DEFAULT_FIX_ATTEMPT_CAP,
189304
189307
  reviewThreshold: decoded.reviewThreshold ?? DEFAULT_REVIEW_THRESHOLD
189305
189308
  };
@@ -351972,6 +351975,25 @@ const subjectOf$1 = (message) => (message.split("\n")[0] ?? "").trim();
351972
351975
  * keeps pre-namespaced history classifying identically.
351973
351976
  */
351974
351977
  const touchedFeedback = (touched) => touched.includes(FEEDBACK_FILE) || touched.includes(LEGACY_FEEDBACK_FILE);
351978
+ const DECISIONS_TRAILER_RE = /^Gtd-Decisions:\s*true\s*$/m;
351979
+ const DECISIONS_HEADING = "## Decisions";
351980
+ /**
351981
+ * Extracts one commit message's `## Decisions` section verbatim (heading
351982
+ * through the next `#`/`##` heading or end of message, trailer line
351983
+ * excluded), or `undefined` when the trailer is absent or the heading can't
351984
+ * be found despite it (a malformed historical commit — skipped, not an
351985
+ * error, since this reads commits the current turn doesn't own and can't be
351986
+ * asked to fix). Total, never throws.
351987
+ */
351988
+ const extractDecisionsSection = (message) => {
351989
+ if (!DECISIONS_TRAILER_RE.test(message)) return void 0;
351990
+ const lines = message.split(/\r?\n/);
351991
+ const headingIdx = lines.findIndex((line) => line.trim() === DECISIONS_HEADING);
351992
+ if (headingIdx === -1) return void 0;
351993
+ const endIdx = lines.findIndex((line, i) => i > headingIdx && /^#{1,2}\s+\S/.test(line.trim()));
351994
+ const section = lines.slice(headingIdx, endIdx === -1 ? lines.length : endIdx).filter((line) => !DECISIONS_TRAILER_RE.test(line)).join("\n").trim();
351995
+ return section.length > 0 ? section : void 0;
351996
+ };
351975
351997
  /**
351976
351998
  * Gather ALL git/filesystem facts and produce the typed event stream the pure
351977
351999
  * machine folds: one `COMMIT` per first-parent commit (oldest→newest) followed
@@ -352062,8 +352084,10 @@ const gatherEvents = (invoker) => gen(function* () {
352062
352084
  let refDiff;
352063
352085
  let reviewAnchor;
352064
352086
  let hasCommitsAfterLastDone = true;
352087
+ let decisionLog = "";
352065
352088
  if (hasCommits) {
352066
352089
  const allHistory = isNone(base) ? history : yield* git.commitHistory();
352090
+ if (config.decisionLog) decisionLog = allHistory.map((c) => extractDecisionsSection(c.message)).filter((section) => section !== void 0).join("\n\n");
352067
352091
  const lastDoneIdx = (() => {
352068
352092
  let idx = -1;
352069
352093
  for (let i = 0; i < allHistory.length; i++) if (subjectOf$1(allHistory[i].message) === DONE_SUBJECT) idx = i;
@@ -352230,7 +352254,8 @@ const gatherEvents = (invoker) => gen(function* () {
352230
352254
  ...healthFixBase !== void 0 ? { healthFixBase } : {},
352231
352255
  learningEnabled: config.learning,
352232
352256
  learningMsgPresent,
352233
- learningMsgIsTemplate
352257
+ learningMsgIsTemplate,
352258
+ decisionLog
352234
352259
  }
352235
352260
  };
352236
352261
  return [...commitEvents, resolveEvent];
@@ -352537,7 +352562,8 @@ const DEFAULT_PAYLOAD = {
352537
352562
  healthCommitted: false,
352538
352563
  learningEnabled: false,
352539
352564
  learningMsgPresent: false,
352540
- learningMsgIsTemplate: false
352565
+ learningMsgIsTemplate: false,
352566
+ decisionLog: ""
352541
352567
  };
352542
352568
  /** Build the prompt context from the payload passthrough + the folded counters. */
352543
352569
  const buildContext = (p, counters) => ({
@@ -352549,7 +352575,8 @@ const buildContext = (p, counters) => ({
352549
352575
  ...p.squashBase !== void 0 ? { squashBase: p.squashBase } : {},
352550
352576
  ...p.squashDiff !== void 0 ? { squashDiff: p.squashDiff } : {},
352551
352577
  feedbackContent: p.feedbackContent !== "" ? p.feedbackContent : p.healthContent,
352552
- ...p.headTurnDiff !== "" ? { turnDiff: p.headTurnDiff } : {}
352578
+ ...p.headTurnDiff !== "" ? { turnDiff: p.headTurnDiff } : {},
352579
+ decisionLog: p.decisionLog
352553
352580
  });
352554
352581
  const healthFileConflictRules = [
352555
352582
  {
@@ -353910,17 +353937,20 @@ var feedback_default = "<%\n const body = it.content.replace(/\\n$/, \"\")\n c
353910
353937
  //#region src/prompts/partials/package.md
353911
353938
  var package_default = "<%\n const lines = [\"\", \"### Package: `\" + it.pkg.name + \"/`\", \"\"]\n for (const task of it.pkg.taskContents) {\n const fence = it.fenceFor(task.content)\n lines.push(\"#### `\" + task.name + \"`\", \"\", fence, task.content.replace(/\\n$/, \"\"), fence, \"\")\n }\n%><%~ lines.join(\"\\n\") %>\n";
353912
353939
  //#endregion
353940
+ //#region src/prompts/partials/decision-log.md
353941
+ var decision_log_default = "<%\n const body = it.decisionLog.replace(/\\n$/, \"\")\n const fence = it.fenceFor(body)\n const out = [\n \"\",\n \"### Prior decisions\",\n \"\",\n \"Recorded in past squash commits, oldest to newest, with no deduplication\" +\n \" — if two entries answer the same question differently, the LATER one\" +\n \" (further down) is the current, authoritative answer. Don't re-ask a\" +\n \" settled question unless the codebase now genuinely contradicts it.\",\n \"\",\n fence,\n body,\n fence,\n \"\",\n ].join(\"\\n\")\n%><%~ out %>\n";
353942
+ //#endregion
353913
353943
  //#region src/prompts/partials/agent-turn.md
353914
353944
  var agent_turn_default = "Finish your turn by running `gtd step-agent`. Then run `gtd next` and follow\nits output — repeat this cycle as long as the output is addressed to you (the\nagent); when it awaits the human, stop and hand off.\n";
353915
353945
  //#endregion
353916
353946
  //#region src/prompts/grilling-agent.md
353917
- var grilling_agent_default = "<%~ include(\"@header\") %>\n\n`.gtd/TODO.md` holds the plan under development. Develop it into a concrete,\nproduct-level plan **in this one turn** — use subagents / internal iteration\nto go as deep as needed; there is no further agent-only round after this one.\n\n**Scope: product and user-facing decisions only.** Do not decide\nimplementation details in this file — file/module structure, data models,\nlibrary or tech-stack choices, and other architecture questions belong to the\nnext phase (`.gtd/ARCHITECTURE.md`) and must not be treated as open questions\nhere.\n\n### Develop the plan\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the plan.\nThe subagent works entirely by editing `.gtd/TODO.md`:\n\n1. **Explore the codebase before asking anything** — read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the captured input / seed template with a real product plan** —\n what to build and why, and the user-facing behavior it should have,\n grounded in the codebase. Leave *how* to build it for the next phase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the plan is as complete as it can get without human input. Do not\n leave this to a future round — there isn't one before the human is asked.\n4. For every remaining open question, add it under a `## Open Questions`\n section near the top of the file, one `### <question>` sub-heading per\n question, whose first body line is `Suggested default: <answer>` — your\n best-guess answer, stated plainly, that the human can accept as-is. This\n structure is enforced: a `###` question with no `Suggested default:` line\n blocks your turn — `gtd step-agent` refuses until it's fixed. Omit the\n `## Open Questions` section entirely once there are none.\n5. Leave `.gtd/TODO.md` **uncommitted** — the human's turn (`gtd step`) reads it\n next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include(\"@diff\", { heading: \"Latest human input (answers / sketch / review feedback)\", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions — fold them into the plan and re-implement them\n properly, including test coverage, rather than restoring them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the plan (or the reviewed work) as\n a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353947
+ var grilling_agent_default = "<%~ include(\"@header\") %>\n\n`.gtd/TODO.md` holds the plan under development. Develop it into a concrete,\nproduct-level plan **in this one turn** — use subagents / internal iteration\nto go as deep as needed; there is no further agent-only round after this one.\n\n**Scope: product and user-facing decisions only.** Do not decide\nimplementation details in this file — file/module structure, data models,\nlibrary or tech-stack choices, and other architecture questions belong to the\nnext phase (`.gtd/ARCHITECTURE.md`) and must not be treated as open questions\nhere.\n<% if (it.context.decisionLog && it.context.decisionLog.trim()) { %>\n<%~ include(\"@decision-log\", { decisionLog: it.context.decisionLog, fenceFor: it.fenceFor }) %>\n<% } %>\n\n### Develop the plan\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the plan.\nThe subagent works entirely by editing `.gtd/TODO.md`:\n\n1. **Explore the codebase before asking anything** — read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the captured input / seed template with a real product plan** —\n what to build and why, and the user-facing behavior it should have,\n grounded in the codebase. Leave *how* to build it for the next phase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the plan is as complete as it can get without human input. Do not\n leave this to a future round — there isn't one before the human is asked.\n4. For every remaining open question, add it under a `## Open Questions`\n section near the top of the file, one `### <question>` sub-heading per\n question, whose first body line is `Suggested default: <answer>` — your\n best-guess answer, stated plainly, that the human can accept as-is. This\n structure is enforced: a `###` question with no `Suggested default:` line\n blocks your turn — `gtd step-agent` refuses until it's fixed. Omit the\n `## Open Questions` section entirely once there are none.\n5. Leave `.gtd/TODO.md` **uncommitted** — the human's turn (`gtd step`) reads it\n next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include(\"@diff\", { heading: \"Latest human input (answers / sketch / review feedback)\", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions — fold them into the plan and re-implement them\n properly, including test coverage, rather than restoring them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the plan (or the reviewed work) as\n a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353918
353948
  //#endregion
353919
353949
  //#region src/prompts/grilling-answers.md
353920
353950
  var grilling_answers_default = "<%~ include(\"@header\") %>\n\n`.gtd/TODO.md` holds the plan under development, with open questions under a\n`## Open Questions` section — each `### <question>` carrying a suggested\ndefault.\n\nThis is a human gate; there is nothing for the agent to do.\n\nTell the user:\n\n- To answer a question, edit its `### <question>` entry under `## Open\n Questions` in place — replace the `Suggested default: ...` line with\n `Answer: ...` (or annotate it further), then run `gtd step`.\n- To accept **all** suggested defaults as-is, run `gtd step` with **no\n edits** — this converges the plan and moves on to technical architecting.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353921
353951
  //#endregion
353922
353952
  //#region src/prompts/architecting-agent.md
353923
- var architecting_agent_default = "<%~ include(\"@header\") %>\n\n`.gtd/ARCHITECTURE.md` holds the technical plan under development — seeded\nfrom the converged product plan, or, if this is the very first turn on\nalready-technical input, written directly by the human. Develop it into a concrete,\nimplementation-ready technical plan **in this one turn** — use subagents /\ninternal iteration to go as deep as needed; there is no further agent-only\nround after this one.\n\n**Scope: technical/architectural decisions only.** Decide file/module\nstructure, data models, module boundaries, library or tech-stack choices, and\nerror-handling/concurrency strategy — the *how*, building on the *what*\nalready settled in this file's product content. Do not re-open or re-litigate\nproduct/user-facing decisions from the prior phase; treat them as settled\ncontext.\n\n### Develop the architecture\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the\narchitecture. The subagent works entirely by editing `.gtd/ARCHITECTURE.md`:\n\n1. **Explore the codebase before asking anything** — read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the seeded/captured content with a real architecture** — the\n files to change, module/data-model structure, and why, grounded in the\n codebase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the architecture is as complete as it can get without human input.\n Do not leave this to a future round — there isn't one before the human is\n asked.\n4. For every remaining open question, add it under a `## Open Questions`\n section near the top of the file, one `### <question>` sub-heading per\n question, whose first body line is `Suggested default: <answer>` — your\n best-guess answer, stated plainly, that the human can accept as-is. This\n structure is enforced: a `###` question with no `Suggested default:` line\n blocks your turn — `gtd step-agent` refuses until it's fixed. Omit the\n `## Open Questions` section entirely once there are none.\n5. Leave `.gtd/ARCHITECTURE.md` **uncommitted** — the human's turn (`gtd step`)\n reads it next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include(\"@diff\", { heading: \"Latest human input (answers / sketch / review feedback)\", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions — fold them into the architecture and\n re-implement them properly, including test coverage, rather than restoring\n them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the architecture (or the reviewed\n work) as a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353953
+ var architecting_agent_default = "<%~ include(\"@header\") %>\n\n`.gtd/ARCHITECTURE.md` holds the technical plan under development — seeded\nfrom the converged product plan, or, if this is the very first turn on\nalready-technical input, written directly by the human. Develop it into a concrete,\nimplementation-ready technical plan **in this one turn** — use subagents /\ninternal iteration to go as deep as needed; there is no further agent-only\nround after this one.\n\n**Scope: technical/architectural decisions only.** Decide file/module\nstructure, data models, module boundaries, library or tech-stack choices, and\nerror-handling/concurrency strategy — the *how*, building on the *what*\nalready settled in this file's product content. Do not re-open or re-litigate\nproduct/user-facing decisions from the prior phase; treat them as settled\ncontext.\n<% if (it.context.decisionLog && it.context.decisionLog.trim()) { %>\n<%~ include(\"@decision-log\", { decisionLog: it.context.decisionLog, fenceFor: it.fenceFor }) %>\n<% } %>\n\n### Develop the architecture\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the\narchitecture. The subagent works entirely by editing `.gtd/ARCHITECTURE.md`:\n\n1. **Explore the codebase before asking anything** — read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the seeded/captured content with a real architecture** — the\n files to change, module/data-model structure, and why, grounded in the\n codebase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the architecture is as complete as it can get without human input.\n Do not leave this to a future round — there isn't one before the human is\n asked.\n4. For every remaining open question, add it under a `## Open Questions`\n section near the top of the file, one `### <question>` sub-heading per\n question, whose first body line is `Suggested default: <answer>` — your\n best-guess answer, stated plainly, that the human can accept as-is. This\n structure is enforced: a `###` question with no `Suggested default:` line\n blocks your turn — `gtd step-agent` refuses until it's fixed. Omit the\n `## Open Questions` section entirely once there are none.\n5. Leave `.gtd/ARCHITECTURE.md` **uncommitted** — the human's turn (`gtd step`)\n reads it next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include(\"@diff\", { heading: \"Latest human input (answers / sketch / review feedback)\", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions — fold them into the architecture and\n re-implement them properly, including test coverage, rather than restoring\n them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the architecture (or the reviewed\n work) as a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353924
353954
  //#endregion
353925
353955
  //#region src/prompts/architecting-answers.md
353926
353956
  var architecting_answers_default = "<%~ include(\"@header\") %>\n\n`.gtd/ARCHITECTURE.md` holds the technical plan under development, with open\nquestions under a `## Open Questions` section — each `### <question>`\ncarrying a suggested default.\n\nThis is a human gate; there is nothing for the agent to do.\n\nTell the user:\n\n- To answer a question, edit its `### <question>` entry under `## Open\n Questions` in place — replace the `Suggested default: ...` line with\n `Answer: ...` (or annotate it further), then run `gtd step`.\n- To accept **all** suggested defaults as-is, run `gtd step` with **no\n edits** — this converges the architecture and moves on to decomposition.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
@@ -353947,7 +353977,7 @@ var review_default = "<%~ include(\"@header\") %>\n\nSpawn a **planning-model su
353947
353977
  var await_review_default = "<%~ include(\"@header\") %>\n\n`.gtd/REVIEW.md` holds the review record for the completed work. This is a human\ngate; there is nothing for the agent to do.\n\nTell the user:\n\n- Read `.gtd/REVIEW.md` (and the underlying work it points at).\n- To **approve**: run `gtd step` with no edits, or only tick checkboxes in\n `.gtd/REVIEW.md` — either way ends the review with an approval.\n- To **request changes**: write substantive edits or annotations into\n `.gtd/REVIEW.md` (or the code) describing what's wrong, then run `gtd step` —\n this sends the feedback back for another round.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353948
353978
  //#endregion
353949
353979
  //#region src/prompts/squashing.md
353950
- var squashing_default = "<%~ include(\"@header\") %>\n\nThe process is **approved and done**. `.gtd/SQUASH_MSG.md` has already been\ncommitted with a template — your job is to overwrite it with the real\nconventional-commits squash message and finish your turn.\n\n### Step 1 — Extract decisions from grilling rounds\n\nScan the git history of recent `gtd: ...` / `gtd(agent): ...` /\n`gtd(human): ...` commits. Look for changes to `.gtd/TODO.md` and\n`.gtd/ARCHITECTURE.md` — specifically the `## Captured input` sections and any\nedits to plan/architecture text. Extract **key decisions, trade-offs, and\ndesign choices** made during grilling and architecting rounds. These will\nappear in the commit body so the history is self-documenting.\n\n### Step 2 — Draft the commit message\n\nDraft ONE conventional-commits message:\n\n```\ntype(scope): subject\n\nbody (explain the why — motivation, trade-offs, key decisions from grilling)\n```\n\n- **type**: `feat` / `fix` / `refactor` / `chore` / `docs` / `test`\n- **subject**: imperative mood, ≤ 72 characters, lowercase after the colon\n- **body**: include the important decisions / trade-offs from grilling\n sessions. Omit if there were no meaningful decisions to capture.\n\n### Step 3 — Overwrite .gtd/SQUASH_MSG.md\n\nReplace the **entire content** of `.gtd/SQUASH_MSG.md` (plain text, no markdown\nwrapper, no leftover template scaffolding) with the message from Step 2, then\nleave it uncommitted and finish your turn — `gtd step-agent` performs the\nsquash using this file's content.\n<% if (it.context.squashDiff && it.context.squashDiff.trim()) { %>\n<%= it.context.squashBase !== undefined ? \"\\nSquash base: \" + it.context.squashBase + \"\\n\" : \"\" %>\n<%~ include(\"@diff\", { heading: \"Full-process diff (`git diff \" + (it.context.squashBase !== undefined ? it.context.squashBase : \"<squashBase>\") + \" HEAD`)\", diff: it.context.squashDiff, fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353980
+ var squashing_default = "<%~ include(\"@header\") %>\n\nThe process is **approved and done**. `.gtd/SQUASH_MSG.md` has already been\ncommitted with a template — your job is to overwrite it with the real\nconventional-commits squash message and finish your turn.\n\n### Step 1 — Extract decisions from grilling rounds\n\nScan the git history of recent `gtd: ...` / `gtd(agent): ...` /\n`gtd(human): ...` commits. Look for changes to `.gtd/TODO.md` and\n`.gtd/ARCHITECTURE.md` — specifically the `## Captured input` sections and any\nedits to plan/architecture text. Extract **key decisions, trade-offs, and\ndesign choices** made during grilling and architecting rounds — specifically\nthe `### <question>` entries under `## Open Questions` that got resolved\n(`Answer:`, or an accepted `Suggested default:`) this cycle.\n\n### Step 2 — Draft the commit message\n\nDraft ONE conventional-commits message:\n\n```\ntype(scope): subject\n\nbody (explain the why — motivation, trade-offs, key decisions from grilling)\n\n## Decisions\n\n### <question, verbatim from Step 1>\nAnswer: <the resolved answer> (default accepted | human override: <reason>)\n\nGtd-Decisions: true\n```\n\n- **type**: `feat` / `fix` / `refactor` / `chore` / `docs` / `test`\n- **subject**: imperative mood, ≤ 72 characters, lowercase after the colon\n- **body**: a brief motivation/trade-off summary.\n- **`## Decisions`**: one block per question resolved THIS cycle (from Step\n 1) — omit the whole section (and the `Gtd-Decisions: true` line) entirely\n when this cycle recorded no decisions. Don't restate decisions from earlier\n cycles — this commit only records what changed now; a later grilling round\n reads the full history of these sections back as context, so repeating old\n entries here would just duplicate them forever. If this cycle's answer\n contradicts an earlier cycle's, don't try to edit or annotate the old one —\n just state the new answer plainly; the newer commit naturally reads as the\n current truth.\n\n### Step 3 — Overwrite .gtd/SQUASH_MSG.md\n\nReplace the **entire content** of `.gtd/SQUASH_MSG.md` (plain text, no markdown\nwrapper, no leftover template scaffolding) with the message from Step 2, then\nleave it uncommitted and finish your turn — `gtd step-agent` performs the\nsquash using this file's content.\n<% if (it.context.squashDiff && it.context.squashDiff.trim()) { %>\n<%= it.context.squashBase !== undefined ? \"\\nSquash base: \" + it.context.squashBase + \"\\n\" : \"\" %>\n<%~ include(\"@diff\", { heading: \"Full-process diff (`git diff \" + (it.context.squashBase !== undefined ? it.context.squashBase : \"<squashBase>\") + \" HEAD`)\", diff: it.context.squashDiff, fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
353951
353981
  //#endregion
353952
353982
  //#region src/prompts/learning.md
353953
353983
  var learning_default = "<%~ include(\"@header\") %>\n\nThe process is **approved and done**, and headed for a squash. Before that\nhappens, `.gtd/LEARNINGS.md` has been committed with a template — your job is\nto overwrite it with the real, distilled learnings from this cycle so they\nsurvive the squash even though the granular history won't.\n\n### Step 1 — Walk the cycle's history\n\nScan the git history back to the cycle's start (the full-process diff is\ninlined below, and `git log` over the same range shows every `gtd: ...` /\n`gtd(agent): ...` / `gtd(human): ...` commit). Look specifically for:\n\n- **Test failures and fixes** — each `gtd: errors` round and the fix that\n followed it. What broke, and what actually made it pass?\n- **Review feedback** — human `.gtd/REVIEW.md` notes and `gtd: review\n feedback` detours. What did the human have to correct that the agent got\n wrong on its own?\n- **Health-check rounds** — any `gtd: health-check` / `gtd: health-fix`\n cycles. Environmental or systemic issues surfaced outside normal building?\n- **Grilling decisions** — `.gtd/TODO.md`'s \"## Captured input\" sections and\n plan edits. Trade-offs or conventions the human specified that a future\n agent should already know.\n\n### Step 2 — Distill, don't transcribe\n\nFor each thing you found, ask: is this a **durable, generalizable lesson**\n(a convention, a gotcha, a pattern this project wants followed next time), or\na **one-off detail** specific to this change? Keep only the former. A future\nagent reading this should learn something that changes how it works on this\nproject, not a recap of what happened.\n\n### Step 3 — Overwrite .gtd/LEARNINGS.md\n\nReplace the **entire content** of `.gtd/LEARNINGS.md` with your distilled\nlearnings (plain markdown, no leftover template scaffolding). If nothing\ndurable surfaced this cycle, write a short explicit note saying so — don't\nleave the placeholder text in place. Leave the file uncommitted and finish\nyour turn; a human reviews it next before it's integrated into the project's\nown memory (CLAUDE.md/AGENTS.md/docs).\n<% if (it.context.squashDiff && it.context.squashDiff.trim()) { %>\n<%= it.context.squashBase !== undefined ? \"\\nCycle base: \" + it.context.squashBase + \"\\n\" : \"\" %>\n<%~ include(\"@diff\", { heading: \"Full-process diff (`git diff \" + (it.context.squashBase !== undefined ? it.context.squashBase : \"<squashBase>\") + \" HEAD`)\", diff: it.context.squashDiff, fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
@@ -354034,6 +354064,7 @@ eta.loadTemplate("@header", header_default);
354034
354064
  eta.loadTemplate("@diff", diff_default);
354035
354065
  eta.loadTemplate("@feedback", feedback_default);
354036
354066
  eta.loadTemplate("@package", package_default);
354067
+ eta.loadTemplate("@decision-log", decision_log_default);
354037
354068
  eta.loadTemplate("@agent-turn", agent_turn_default);
354038
354069
  eta.loadTemplate("@grilling-agent", grilling_agent_default);
354039
354070
  eta.loadTemplate("@grilling-answers", grilling_answers_default);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {
package/schema.json CHANGED
@@ -63,6 +63,9 @@
63
63
  "learning": {
64
64
  "type": "boolean"
65
65
  },
66
+ "decisionLog": {
67
+ "type": "boolean"
68
+ },
66
69
  "fixAttemptCap": {
67
70
  "$ref": "#/$defs/Int",
68
71
  "description": "a non-negative number",