@pmelab/gtd 58.2.0 → 59.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +115 -390
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
  4. package/dist/gtd.bundle.mjs +76 -59
  5. package/dist/testkit/gherkin.mjs +1 -1
  6. package/dist/testkit.mjs +1 -1
  7. package/package.json +3 -42
  8. package/skills/authoring/SKILL.md +31 -25
  9. package/src/cli/Cli.test.ts +15 -19
  10. package/src/cli/Cli.ts +2 -4
  11. package/src/cli/Program.test.ts +60 -29
  12. package/src/cli/Program.ts +5 -7
  13. package/src/edge/Edge.ts +3 -3
  14. package/src/flags/index.test.ts +0 -13
  15. package/src/flags/index.ts +1 -3
  16. package/src/install/Install.test.ts +14 -11
  17. package/src/install/Install.ts +38 -15
  18. package/src/install/index.ts +1 -2
  19. package/src/replay/Replay.test.ts +1 -1
  20. package/src/replay/index.ts +1 -1
  21. package/src/step/planStart.ts +6 -1
  22. package/src/twins/FlowScripts.ts +1 -1
  23. package/src/workflow/index.ts +1 -0
  24. package/src/workflow/load.test.ts +138 -113
  25. package/src/workflow/load.ts +1 -1
  26. package/src/workflow/vars.test.ts +30 -3
  27. package/src/workflow/vars.ts +34 -2
  28. package/src/workflow-kit/index.ts +1 -1
  29. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  30. package/dist/chunks/review-Bfaror38.mjs +0 -427
  31. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  32. package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
  33. package/dist/workflows/cleanup.mjs +0 -36
  34. package/dist/workflows/feature.mjs +0 -1018
  35. package/dist/workflows/fix.mjs +0 -41
  36. package/dist/workflows/review.mjs +0 -45
  37. package/dist/workflows/rollout.mjs +0 -48
  38. package/dist/workflows.mjs +0 -40651
  39. package/src/install/ConfigScaffold.ts +0 -22
  40. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  41. package/src/workflows/cleanup/cleanup.feature +0 -315
  42. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  43. package/src/workflows/cleanup/cleanup.ts +0 -52
  44. package/src/workflows/cleanup/index.ts +0 -6
  45. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  46. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  47. package/src/workflows/feature/criteria-coverage.feature +0 -102
  48. package/src/workflows/feature/criteria.test.ts +0 -147
  49. package/src/workflows/feature/criteria.ts +0 -267
  50. package/src/workflows/feature/fast-path.feature +0 -93
  51. package/src/workflows/feature/feature.test.ts +0 -338
  52. package/src/workflows/feature/feature.ts +0 -156
  53. package/src/workflows/feature/green.ts +0 -19
  54. package/src/workflows/feature/index.ts +0 -18
  55. package/src/workflows/feature/lens-models.feature +0 -69
  56. package/src/workflows/feature/lens-review.feature +0 -566
  57. package/src/workflows/feature/naive-acceptance.feature +0 -147
  58. package/src/workflows/feature/naive-test.feature +0 -263
  59. package/src/workflows/feature/one-step-land.feature +0 -93
  60. package/src/workflows/feature/planning.ts +0 -41
  61. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  62. package/src/workflows/feature/refactor-first.feature +0 -158
  63. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  64. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  65. package/src/workflows/feature/refactor-review.feature +0 -1528
  66. package/src/workflows/feature/refactor.test.ts +0 -1220
  67. package/src/workflows/feature/refactor.ts +0 -368
  68. package/src/workflows/feature/release-flag.feature +0 -167
  69. package/src/workflows/feature/release-flag.test.ts +0 -49
  70. package/src/workflows/feature/release-flag.ts +0 -46
  71. package/src/workflows/feature/review-gate.feature +0 -241
  72. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  73. package/src/workflows/feature/risk-tier.feature +0 -351
  74. package/src/workflows/feature/seam-rejection.feature +0 -146
  75. package/src/workflows/feature/seam.feature +0 -297
  76. package/src/workflows/feature/seam.test.ts +0 -527
  77. package/src/workflows/feature/seam.ts +0 -426
  78. package/src/workflows/feature/standards.feature +0 -354
  79. package/src/workflows/feature/steps.test.ts +0 -65
  80. package/src/workflows/feature/steps.ts +0 -45
  81. package/src/workflows/feature/tier-pin.feature +0 -66
  82. package/src/workflows/feature/tier.test.ts +0 -166
  83. package/src/workflows/feature/tier.ts +0 -56
  84. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  85. package/src/workflows/fix/fix-quality.feature +0 -212
  86. package/src/workflows/fix/fix.feature +0 -79
  87. package/src/workflows/fix/fix.test.ts +0 -74
  88. package/src/workflows/fix/fix.ts +0 -52
  89. package/src/workflows/fix/index.ts +0 -6
  90. package/src/workflows/index.ts +0 -47
  91. package/src/workflows/review/index.ts +0 -6
  92. package/src/workflows/review/review-door.feature +0 -199
  93. package/src/workflows/review/review.test.ts +0 -27
  94. package/src/workflows/review/review.ts +0 -60
  95. package/src/workflows/rollout/index.ts +0 -6
  96. package/src/workflows/rollout/rollout.feature +0 -510
  97. package/src/workflows/rollout/rollout.test.ts +0 -263
  98. package/src/workflows/rollout/rollout.ts +0 -82
  99. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  100. package/src/workflows/shared/access.test.ts +0 -62
  101. package/src/workflows/shared/access.ts +0 -24
  102. package/src/workflows/shared/config.fixture.ts +0 -12
  103. package/src/workflows/shared/fences.ts +0 -24
  104. package/src/workflows/shared/health.test.ts +0 -148
  105. package/src/workflows/shared/health.ts +0 -191
  106. package/src/workflows/shared/learn.test.ts +0 -140
  107. package/src/workflows/shared/learn.ts +0 -201
  108. package/src/workflows/shared/lenses.test.ts +0 -39
  109. package/src/workflows/shared/lenses.ts +0 -294
  110. package/src/workflows/shared/options.test.ts +0 -157
  111. package/src/workflows/shared/options.ts +0 -92
  112. package/src/workflows/shared/progress.feature +0 -124
  113. package/src/workflows/shared/prose.ts +0 -263
  114. package/src/workflows/shared/recheck.feature +0 -60
  115. package/src/workflows/shared/registry.test.ts +0 -345
  116. package/src/workflows/shared/registry.ts +0 -183
  117. package/src/workflows/shared/reserved.ts +0 -11
  118. package/src/workflows/shared/review.test.ts +0 -1062
  119. package/src/workflows/shared/review.ts +0 -446
  120. package/src/workflows/shared/skills.test.ts +0 -121
  121. package/src/workflows/shared/skills.ts +0 -49
  122. package/src/workflows/shared/standards.test.ts +0 -90
  123. package/src/workflows/shared/standards.ts +0 -156
  124. package/src/workflows/shared/steps.test.ts +0 -219
  125. package/src/workflows/shared/steps.ts +0 -201
  126. package/src/workflows/shared/tail.test.ts +0 -454
  127. package/src/workflows/shared/tail.ts +0 -264
  128. package/src/workflows/shared/text.fixture.ts +0 -128
  129. package/src/workflows/shared/text.test.ts +0 -1430
  130. package/src/workflows/shared/text.ts +0 -1700
  131. package/src/workflows/shared/threads.test.ts +0 -129
  132. package/src/workflows/shared/vars.ts +0 -47
  133. package/src/workflows/shared/workflows.steps.ts +0 -54
@@ -20888,12 +20888,7 @@ const restorability = (git, headHash, tipHash) => gen(function* () {
20888
20888
  });
20889
20889
  //#endregion
20890
20890
  //#region src/flags/index.ts
20891
- const FLAGS$1 = [{
20892
- name: "risk_tier",
20893
- owner: "pmelab",
20894
- expiry: "2027-01-15",
20895
- kind: "release"
20896
- }];
20891
+ const FLAGS$1 = [];
20897
20892
  const BOOL = {
20898
20893
  "0": false,
20899
20894
  false: false,
@@ -20935,6 +20930,7 @@ const findPackageJson = (fromUrl) => {
20935
20930
  const GTD_VERSION = _require(findPackageJson(import.meta.url)).version;
20936
20931
  //#endregion
20937
20932
  //#region src/install/Install.ts
20933
+ const WORKFLOWS_REPO = "https://github.com/pmelab/gtd-workflows";
20938
20934
  const MINIMAL_DRIVER = `#!/usr/bin/env sh
20939
20935
  set -eu
20940
20936
 
@@ -21016,7 +21012,7 @@ while :; do
21016
21012
  system="$(gtd next --json=system)"
21017
21013
  validate="$(gtd next --json=validate)"
21018
21014
  log="$(gtd next --json=log)"
21019
- # \`model\` is a hint, not a model name — the bundled workflow ships
21015
+ # \`model\` is a hint, not a model name — gtd-workflows' default ships
21020
21016
  # \`smart\` and \`base\`, which \`claude --model\` rejects. Resolve them
21021
21017
  # from your environment; unset, the hint is dropped and the agent runs
21022
21018
  # its own default. Any other value is a real model name.
@@ -21338,14 +21334,37 @@ follow in the same numbered list.
21338
21334
  existing config, the project's own README/docs) and ask the user what
21339
21335
  they're trying to accomplish with gtd here — don't assume a fresh
21340
21336
  install or silently start a process on their behalf.
21341
- 2. **Make sure a \`gtd.config.ts\` governs the repository.** Run at the
21342
- repository root, \`gtd install\` writes one importing gtd's shipped
21343
- workflows when none exists yet (its own message says so). Repository
21344
- settings — steering-file modes, \`gtd ui\`, per-scope skills and access —
21345
- go in its \`config\` export; workflow settings are options of the
21346
- workflow factory it exports.
21347
- 3. **Commit the config before the first drive.** \`gtd install\` leaves the
21348
- file it writes uncommitted on purpose — an uncommitted config is a
21337
+ 2. **Make sure a \`gtd.config.ts\` governs the repository.** gtd ships no
21338
+ workflow: the config imports the workflows it runs. Ask the user which
21339
+ way they want:
21340
+ - **A workflow from ${WORKFLOWS_REPO}.** Its \`default\` is a feature →
21341
+ review → fix workflow. It is used from a checkout pinned on this
21342
+ machine:
21343
+
21344
+ \`\`\`sh
21345
+ git clone ${WORKFLOWS_REPO} ~/.local/share/gtd/workflows
21346
+ ~/.local/share/gtd/workflows/bin/gtd-wf pin origin/main # or a tag
21347
+ ~/.local/share/gtd/workflows/bin/gtd-wf config default > gtd.config.ts
21348
+ \`\`\`
21349
+
21350
+ \`pin\` checks the ref out and bundles each workflow; \`config\` prints a
21351
+ config importing the bundle by its ABSOLUTE path. Tell the user what
21352
+ that means: the committed config works only where the checkout sits at
21353
+ that same path, so a teammate or CI needs their own checkout there (or
21354
+ their own config). If a checkout exists already, \`gtd-wf status\`
21355
+ shows its pin — reuse it rather than cloning again. Read the
21356
+ repository's README for the workflows it offers and their options.
21357
+ - **Their own workflow.** A workflow is TypeScript the config exports,
21358
+ written against \`@pmelab/gtd/flows\` — see
21359
+ https://github.com/pmelab/gtd/blob/main/docs/configuration.md, and
21360
+ https://github.com/pmelab/gtd/blob/main/docs/testing.md to test it. A
21361
+ copy of a gtd-workflows workflow (\`gtd-wf new <name> --from default\`
21362
+ in a clone of it) is a good start.
21363
+
21364
+ Repository settings — steering-file modes, \`gtd ui\`, per-scope skills
21365
+ and access — go in the config's \`config\` export; workflow settings are
21366
+ options of the workflow factory it exports.
21367
+ 3. **Commit the config before the first drive.** An uncommitted config is a
21349
21368
  pending change, so starting a process without committing it first turns
21350
21369
  the config into the very first thing the process plans against. Commit
21351
21370
  it, THEN start driving.
@@ -21371,7 +21390,8 @@ follow in the same numbered list.
21371
21390
  from every prompt line and writing no model exports, so every turn runs
21372
21391
  on the chosen CLI's own default tier.
21373
21392
  6. **If model hints are honored, resolve them to real model identifiers.**
21374
- The bundled workflow ships \`plannerModel: smart\` and \`coderModel: base\`.
21393
+ A workflow may declare model hints — gtd-workflows' \`default\` ships
21394
+ \`plannerModel: smart\` and \`coderModel: base\`.
21375
21395
  **Those are opaque hints, not model names** — gtd never interprets them,
21376
21396
  and passing \`--model smart\` to a real CLI fails on the first prompt
21377
21397
  beat. Derive the chosen CLI's own available models at install time — its
@@ -21468,11 +21488,8 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
21468
21488
  rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
21469
21489
  straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
21470
21490
  \`gtd doors\` lists the doors a repository offers (those of the workflows its
21471
- config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
21472
- \`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
21473
- (remove it), which a config adds by exporting \`rollout\` from
21474
- \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
21475
- \`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
21491
+ config exports — gtd-workflows' \`default\` brings \`fix\` and
21492
+ \`review [base]\` — plus any the config declares). Refuses with
21476
21493
  \`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
21477
21494
  missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
21478
21495
 
@@ -21587,28 +21604,6 @@ Any runtime works: the contract is \`--json\`/\`--json=<path>\` in, subprocesses
21587
21604
  out.
21588
21605
  `;
21589
21606
  const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + DRIVER_OBLIGATIONS + RECOVERY + commandSuite() + EDITOR_INTEGRATION + PREREQUISITES;
21590
- //#endregion
21591
- //#region src/install/ConfigScaffold.ts
21592
- const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped feature, review and fix.
21593
- // Every exported function is startable with \`gtd --workflow <name>\`; the
21594
- // default export is what a change in the tree starts. Each workflow brings
21595
- // its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
21596
- // from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
21597
- // \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
21598
- // \`gtd door cleanup <flag>\`.
21599
- export {
21600
- feature as default,
21601
- feature,
21602
- defaults,
21603
- envDefaults,
21604
- summary,
21605
- steering,
21606
- skills,
21607
- access,
21608
- } from "@pmelab/gtd/workflows/feature"
21609
- export { review } from "@pmelab/gtd/workflows/review"
21610
- export { fix } from "@pmelab/gtd/workflows/fix"
21611
- `;
21612
21607
  var FlowsProtocolError = class extends Error {
21613
21608
  constructor(engine, flows) {
21614
21609
  super(`gtd: the workflow speaks flows protocol ${flows}, but the engine installed protocol ${engine} — ${engine < flows ? "upgrade the gtd that runs it" : "upgrade the @pmelab/gtd the workflow imports"}`);
@@ -38953,13 +38948,35 @@ const resolveVars = (workflowVars, entryVars, env) => {
38953
38948
  }
38954
38949
  return merged;
38955
38950
  };
38951
+ const FLAG_VALUES = {
38952
+ "0": "0",
38953
+ false: "0",
38954
+ "1": "1",
38955
+ true: "1"
38956
+ };
38957
+ const flagVariable = (setting) => `GTD_FLAG_${setting.slice(5).toUpperCase()}`;
38958
+ /** The `flag_<name>` settings a workflow declares that gtd's own flags lack: the workflow's own release flags. */
38959
+ const ownFlags = (workflowVars, flags) => Object.keys(workflowVars).filter((name) => name.startsWith("flag_") && !Object.hasOwn(flags, name));
38960
+ /** Why a workflow's own release flag cannot start, if one is set to anything but 1, 0, true or false. */
38961
+ const ownFlagRefusal = (workflowVars, flags, env) => {
38962
+ const bad = ownFlags(workflowVars, flags).find((name) => {
38963
+ const raw = env[flagVariable(name)] ?? "";
38964
+ return raw !== "" && !Object.hasOwn(FLAG_VALUES, raw.toLowerCase());
38965
+ });
38966
+ return bad === void 0 ? void 0 : `${flagVariable(bad)} must be 1, 0, true or false`;
38967
+ };
38956
38968
  /**
38957
38969
  * `resolveVars` for a process being STARTED: `flags` land between the workflow
38958
- * defaults and `--var`, for already-declared names only. A running process never
38959
- * calls this — it replays its pinned record.
38970
+ * defaults and `--var`, for already-declared names only, and so does
38971
+ * `GTD_FLAG_<NAME>` for a `flag_<name>` the workflow declares itself. A running
38972
+ * process never calls this — it replays its pinned record.
38960
38973
  */
38961
38974
  const resolveStartVars = (workflowVars, flags, entryVars, env) => {
38962
38975
  const declared = Object.fromEntries(Object.entries(flags).filter(([name]) => Object.hasOwn(workflowVars, name)));
38976
+ for (const name of ownFlags(workflowVars, flags)) {
38977
+ const value = FLAG_VALUES[(env[flagVariable(name)] ?? "").toLowerCase()];
38978
+ if (value !== void 0) declared[name] = value;
38979
+ }
38963
38980
  return resolveVars({
38964
38981
  ...workflowVars,
38965
38982
  ...declared
@@ -39163,7 +39180,7 @@ const load = gen(function* () {
39163
39180
  const missing = {
39164
39181
  severity: "error",
39165
39182
  path: [],
39166
- message: `no workflows configured: no ${WORKFLOW_MODULE} here or above — run \`gtd install\` to scaffold one`,
39183
+ message: `no workflows configured: no ${WORKFLOW_MODULE} here or above — gtd ships no workflow; \`gtd install\` says where to get one`,
39167
39184
  origin: host.root
39168
39185
  };
39169
39186
  return yield* fail$7(configError([missing]));
@@ -39919,7 +39936,11 @@ const planStart = (current, actor, start) => gen(function* () {
39919
39936
  const overrideRefusal = yield* checkOverrides(commandLabel, varOverrides, Object.keys(workflow.vars), Object.keys(workflow.env));
39920
39937
  if (overrideRefusal !== void 0) return overrideRefusal;
39921
39938
  const baseOf = workflow.def.base;
39922
- const vars = resolveStartVars(workflow.vars, yield* StartFlags, varOverrides, (yield* Host).env);
39939
+ const flags = yield* StartFlags;
39940
+ const hostEnv = (yield* Host).env;
39941
+ const badFlag = ownFlagRefusal(workflow.vars, flags, hostEnv);
39942
+ if (badFlag !== void 0) return refusal(`${commandLabel}: ${badFlag}`);
39943
+ const vars = resolveStartVars(workflow.vars, flags, varOverrides, hostEnv);
39923
39944
  const multiline = multilineSetting(vars);
39924
39945
  if (multiline !== void 0) return refusal(`${commandLabel}: process setting "${multiline}" spans several lines — a process setting is pinned in a commit trailer and must be a single line`);
39925
39946
  const template = yield* try_({
@@ -40782,11 +40803,11 @@ const historySteps = (run) => {
40782
40803
  };
40783
40804
  });
40784
40805
  };
40785
- const versionWarningFor = (recorded) => recorded === GTD_VERSION ? void 0 : `gtd: warning: this history was recorded by ${recorded === void 0 ? "an unknown gtd version" : `gtd ${recorded}`} and is replayed by gtd ${GTD_VERSION} — a workflow imported from @pmelab/gtd/workflows may have changed since`;
40806
+ const versionWarningFor = (recorded) => recorded === GTD_VERSION ? void 0 : `gtd: warning: this history was recorded by ${recorded === void 0 ? "an unknown gtd version" : `gtd ${recorded}`} and is replayed by gtd ${GTD_VERSION} — the engine the workflow runs on may have changed since`;
40786
40807
  /**
40787
40808
  * Replays the closed run at `tip` under today's config. The workflow is not
40788
- * fingerprinted for a version change: one imported from `@pmelab/gtd/workflows`
40789
- * moves with every upgrade, and the version warning is the only signal.
40809
+ * fingerprinted for a version change: the engine under it moves with every
40810
+ * upgrade, and the version warning is the only signal.
40790
40811
  */
40791
40812
  const historyTrail = (tip) => gen(function* () {
40792
40813
  const git = yield* GitService;
@@ -42015,9 +42036,8 @@ const selfGtd = () => {
42015
42036
  const runLspCommand = () => flatMap$4(ToolCommands, (tools) => tools.lsp(selfGtd()));
42016
42037
  /**
42017
42038
  * Prints the briefing, emitted by the binary itself so it's always current.
42018
- * At a repository root no `gtd.config.ts` governs yet, it also writes one
42019
- * importing gtd's shipped workflows, left uncommitted.
42020
- * Anywhere else it writes nothing.
42039
+ * gtd ships no workflow, so where no `gtd.config.ts` governs it says where to
42040
+ * get one; it writes nothing.
42021
42041
  */
42022
42042
  const runInstallCommand = (out) => gen(function* () {
42023
42043
  const briefing = renderBriefing();
@@ -42027,8 +42047,7 @@ const runInstallCommand = (out) => gen(function* () {
42027
42047
  if (topLevel._tag === "Left") return;
42028
42048
  if ((yield* host.realPath(topLevel.right)) !== (yield* host.realPath(host.root))) return;
42029
42049
  if ((yield* (yield* ConfigDiscovery).workflowModule(host.root, host.home)) !== void 0) return;
42030
- yield* (yield* Workspace).write(WORKFLOW_MODULE, CONFIG_SCAFFOLD);
42031
- yield* (yield* Narrator).warn(`gtd install: wrote ${WORKFLOW_MODULE}, importing gtd's shipped workflows — commit it before your first \`gtd land\``);
42050
+ yield* (yield* Narrator).warn(`gtd install: no ${WORKFLOW_MODULE} governs here, and gtd ships no workflow — get one from ${WORKFLOWS_REPO} or write your own (step 2 of the briefing above)`);
42032
42051
  });
42033
42052
  /**
42034
42053
  * Exactly one trailing newline — duplicated from `Cli.ts`'s own
@@ -43086,10 +43105,8 @@ const COMMAND_ROWS = [
43086
43105
  "agent (or a human) to build a gtd driver in any shell or",
43087
43106
  "runtime — the self-serve version of",
43088
43107
  "https://github.com/pmelab/gtd/blob/main/docs/driver.md's",
43089
- "'Writing your own driver'. At a repository root no",
43090
- "gtd.config.ts governs yet, it also writes one importing each",
43091
- "shipped workflow from @pmelab/gtd/workflows/<name>; anywhere",
43092
- "else it writes nothing."
43108
+ "'Writing your own driver', and how to get a workflow: gtd",
43109
+ "ships none. It writes nothing."
43093
43110
  ]
43094
43111
  },
43095
43112
  {
@@ -1,4 +1,4 @@
1
- import { n as testRepo, t as runsItsScript } from "../chunks/testkit-JDtpNKyi.mjs";
1
+ import { n as testRepo, t as runsItsScript } from "../chunks/testkit-CMBAAyRK.mjs";
2
2
  import { resolve } from "node:path";
3
3
  import assert from "node:assert";
4
4
  import { pathToFileURL } from "node:url";
package/dist/testkit.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as testRepo, r as InMemRepo, t as runsItsScript } from "./chunks/testkit-JDtpNKyi.mjs";
1
+ import { n as testRepo, r as InMemRepo, t as runsItsScript } from "./chunks/testkit-CMBAAyRK.mjs";
2
2
  export { InMemRepo, runsItsScript, testRepo };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "58.2.0",
3
+ "version": "59.0.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": {
@@ -27,41 +27,6 @@
27
27
  "source": "./src/workflow-kit/index.ts",
28
28
  "default": "./dist/workflow-kit.mjs"
29
29
  },
30
- "./workflows": {
31
- "types": "./src/workflows/index.ts",
32
- "source": "./src/workflows/index.ts",
33
- "default": "./dist/workflows.mjs"
34
- },
35
- "./workflows/feature": {
36
- "types": "./src/workflows/feature/index.ts",
37
- "source": "./src/workflows/feature/index.ts",
38
- "default": "./dist/workflows/feature.mjs"
39
- },
40
- "./workflows/review": {
41
- "types": "./src/workflows/review/index.ts",
42
- "source": "./src/workflows/review/index.ts",
43
- "default": "./dist/workflows/review.mjs"
44
- },
45
- "./workflows/fix": {
46
- "types": "./src/workflows/fix/index.ts",
47
- "source": "./src/workflows/fix/index.ts",
48
- "default": "./dist/workflows/fix.mjs"
49
- },
50
- "./workflow": {
51
- "types": "./src/workflows/index.ts",
52
- "source": "./src/workflows/index.ts",
53
- "default": "./dist/workflows.mjs"
54
- },
55
- "./workflows/rollout": {
56
- "types": "./src/workflows/rollout/index.ts",
57
- "source": "./src/workflows/rollout/index.ts",
58
- "default": "./dist/workflows/rollout.mjs"
59
- },
60
- "./workflows/cleanup": {
61
- "types": "./src/workflows/cleanup/index.ts",
62
- "source": "./src/workflows/cleanup/index.ts",
63
- "default": "./dist/workflows/cleanup.mjs"
64
- },
65
30
  "./testkit": {
66
31
  "types": "./src/testkit/index.ts",
67
32
  "source": "./src/testkit/index.ts",
@@ -76,7 +41,6 @@
76
41
  "files": [
77
42
  "dist/",
78
43
  "src/flows/",
79
- "src/workflows/",
80
44
  "src/workflow-kit/",
81
45
  "src/wire/",
82
46
  "src/steering/",
@@ -119,7 +83,7 @@
119
83
  "scripts": {
120
84
  "prepare": "husky",
121
85
  "dev": "node dev/run.mjs",
122
- "build": "tsdown --filter web && npx @tailwindcss/cli -i src/web/styles.css -o dist/web/main.css --minify && node scripts/inline-web-client.mjs && tsdown --filter gtd && tsdown --filter workflows && tsdown --filter entrypoints",
86
+ "build": "tsdown --filter web && npx @tailwindcss/cli -i src/web/styles.css -o dist/web/main.css --minify && node scripts/inline-web-client.mjs && tsdown --filter gtd && tsdown --filter entrypoints",
123
87
  "postbuild": "node scripts/assert-no-test-doubles.mjs",
124
88
  "watch": "tsdown --watch",
125
89
  "bench:replay": "node scripts/bench-replay.mjs",
@@ -141,9 +105,7 @@
141
105
  "analyze": "fallow --summary --quiet",
142
106
  "test:web": "vitest run --project storybook",
143
107
  "storybook": "storybook dev --host 0.0.0.0",
144
- "coverage": "vitest run --project unit --coverage",
145
- "eval": ": \"${GTD_EVALS_URL:?GTD_EVALS_URL is required — the OpenAI-compatible gateway every graded turn and the judge run through}\" && : \"${GTD_EVALS_KEY:?GTD_EVALS_KEY is required — the gateway bearer key}\" && npx turbo run build && node evals/eval.mjs",
146
- "eval:baseline": "node evals/compare-baseline.mjs --record"
108
+ "coverage": "vitest run --project unit --coverage"
147
109
  },
148
110
  "lint-staged": {
149
111
  "*": "oxfmt --no-error-on-unmatched-pattern --write"
@@ -184,7 +146,6 @@
184
146
  "oxlint": "^1.74.0",
185
147
  "playwright": "^1.62.1",
186
148
  "prettier": "^3.8.1",
187
- "promptfoo": "0.122.1",
188
149
  "quickpickle": "1.11.2",
189
150
  "react": "^19.2.0",
190
151
  "react-dom": "^19.2.0",
@@ -29,20 +29,25 @@ what a driver does.
29
29
  state (`gtd next`, `gtd lsp`, `gtd door` and `gtd doors` included). It is code
30
30
  the user's repository runs; write it with the same care as a build script.
31
31
 
32
- ## Golden rule: start from the shipped workflows, edit incrementally
32
+ ## Golden rule: start from an existing workflow, edit incrementally
33
33
 
34
34
  Do **not** write a workflow from a blank page unless the user wants something
35
- tiny. gtd ships three workflows (`feature`, `review`, `fix`) as named exports of
36
- `@pmelab/gtd/workflows`; `gtd install` scaffolds a `gtd.config.ts` re-exporting
37
- them. Its default export is `feature` (the ordinary start), and every phase and
38
- single step they are built from is a named export too. Start by importing what
39
- you keep and writing only what changes. Import phases by name; **never
35
+ tiny. gtd ships no workflow; https://github.com/pmelab/gtd-workflows does. Its
36
+ `default` (feature, review, fix, rollout, cleanup) is used from a checkout
37
+ pinned on the machine — `bin/gtd-wf pin` there, then `bin/gtd-wf config default`
38
+ prints a `gtd.config.ts` importing its bundle by absolute path. Every phase and
39
+ single step that bundle is built from is a named export too, so a config can
40
+ import what it keeps and write only what changes. Import phases by name; **never
40
41
  `export *` from it** — every exported function of your file would become a
41
42
  startable workflow:
42
43
 
43
44
  ```ts
44
45
  import { start } from "@pmelab/gtd/flows"
45
- import { afterTail, buildTail, feature } from "@pmelab/gtd/workflows"
46
+ import {
47
+ afterTail,
48
+ buildTail,
49
+ feature,
50
+ } from "/home/me/.local/share/gtd/workflows/dist/default.mjs"
46
51
 
47
52
  export {
48
53
  defaults,
@@ -51,9 +56,9 @@ export {
51
56
  base,
52
57
  steering,
53
58
  skills,
54
- } from "@pmelab/gtd/workflows"
59
+ } from "/home/me/.local/share/gtd/workflows/dist/default.mjs"
55
60
 
56
- export default feature
61
+ export default feature()
57
62
 
58
63
  export async function hotfix() {
59
64
  return afterTail(await buildTail(start(), { fixFirst: true }))
@@ -62,21 +67,22 @@ export async function hotfix() {
62
67
  export const doors = { hotfix: { workflow: "hotfix" } }
63
68
  ```
64
69
 
65
- `gtd --workflow hotfix` (or `gtd door hotfix`) starts it.
70
+ `gtd --workflow hotfix` (or `gtd door hotfix`) starts it. Use the checkout's
71
+ real path; `gtd-wf status` shows it.
66
72
 
67
- To change a phase itself, read its source in the npm package
68
- (`node_modules/@pmelab/gtd/src/workflows/`, or under `$(npm root -g)` for a
69
- global install) and write your own version in `gtd.config.ts`, reusing its
70
- single steps.
73
+ To change a phase itself, read its source in the checkout (`workflows/default/`)
74
+ and either write your own version in `gtd.config.ts`, reusing its single steps,
75
+ or copy the whole workflow in a clone of gtd-workflows
76
+ (`bin/gtd-wf new <name> --from default`) and change it there, with its tests.
71
77
 
72
78
  There is no `extends`/merge: the innermost `gtd.config.ts` walking up from the
73
79
  current directory is the whole workflow. If one already exists, read it and edit
74
80
  it in place.
75
81
 
76
- Prefer the bundled workflow's own parts over re-implementing them — `healthy`,
82
+ Prefer the workflow's own parts over re-implementing them — `healthy`,
77
83
  `escalation`, `gate`, `design`, `seamBuild`, `implement`, `qualityLap`,
78
84
  `buildTail`, and single steps like `triage` or `fixCheck`. Their full step names
79
- are versioned API.
85
+ are that workflow's versioned API, not gtd's.
80
86
 
81
87
  Make one small change, **verify it loads** (see "Verify"), then make the next. A
82
88
  workflow that fails to load breaks every gtd command in the repository.
@@ -142,15 +148,15 @@ the pending landing — call it right after the step whose turn you reject.
142
148
  the last dot). Rename a step and every process resting on it diverges.
143
149
  - An episode ends when the flow returns or calls `restart()`; the next starts at
144
150
  the flow's first step on an ordinary start — that step is where a finished
145
- process waits (the bundled one is `human("idle", …)`).
151
+ process waits (gtd-workflows' default uses `human("idle", …)`).
146
152
  - `gtd --workflow <name>` starts a process on the exported workflow `<name>` (an
147
153
  unknown name is a usage error); `gtd door <name> [args]` starts one through a
148
154
  `doors` entry `{ workflow, args?, vars? }` (`gtd doors` lists them). A
149
- factory's build may return `doors` without `workflow`; exporting the bundled
150
- `fix`, `review`, `rollout` and `cleanup` brings `fix`, `review [base]`,
151
- `rollout <flag>` and `cleanup <flag>`, and yours merge over them. An
152
- `export const base = (workflow, vars) => commitish | undefined` fixes a
153
- started process's diff base (blank = default-branch merge-base).
155
+ factory's build may return `doors` without `workflow`; exporting
156
+ gtd-workflows' `fix`, `review`, `rollout` and `cleanup` brings `fix`,
157
+ `review [base]`, `rollout <flag>` and `cleanup <flag>`, and yours merge over
158
+ them. An `export const base = (workflow, vars) => commitish | undefined` fixes
159
+ a started process's diff base (blank = default-branch merge-base).
154
160
  `--var <name>=<value>` only pins process settings: names the workflow's
155
161
  `defaults` declare — never an environment setting.
156
162
 
@@ -222,7 +228,7 @@ workflow.
222
228
 
223
229
  ## Worked example: add an approval gate before building
224
230
 
225
- In the bundled default, `planAndBuild` runs the design phase, then
231
+ In gtd-workflows' default, `planAndBuild` runs the design phase, then
226
232
  `buildFeature`: the naive pass and its seam judge, then the implementation (the
227
233
  pass itself on `land`), the quality lap and the human review (a review feedback
228
234
  lap re-runs design inside `buildFeature` and does not pass it again). Add a
@@ -260,5 +266,5 @@ process before switching to the edited workflow.
260
266
  - This skill is versioned in the gtd repository, not auto-installed. When gtd is
261
267
  upgraded, re-copy it from the new version's `skills/authoring/SKILL.md`.
262
268
  - Where this file and the code disagree, the code wins: the step API's own doc
263
- comments in `@pmelab/gtd/flows`, and the bundled workflow under
264
- `src/workflows/`.
269
+ comments in `@pmelab/gtd/flows`, and the workflow's source in its own
270
+ repository (gtd-workflows' `workflows/default/`).
@@ -26,7 +26,6 @@ import { EXIT_CODES, EXIT_RUNTIME_ERROR, EXIT_USAGE_ERROR } from "../wire/index.
26
26
  import { Host } from "../platform/index.js"
27
27
  import { type CommandRequirements } from "./Cli.js"
28
28
  import { InMemRepo, makeCapturingCliIo, testLayers } from "../twins/index.js"
29
- import { CONFIG_SCAFFOLD } from "../install/index.js"
30
29
 
31
30
  const FLAG_NAMES = [
32
31
  "--json",
@@ -1291,8 +1290,9 @@ export const side = async () => {
1291
1290
  await agent("working", "w")
1292
1291
  }
1293
1292
 
1294
- export const defaults = { flag_risk_tier: "0" }
1293
+ export const defaults = { flag_beta: "0" }
1295
1294
  `
1295
+ const BETA = { name: "beta", owner: "pmelab", expiry: "2099-01-01", kind: "release" } as const
1296
1296
  const start = async (
1297
1297
  flags?: CliIo["flags"],
1298
1298
  env: Record<string, string> = {},
@@ -1307,27 +1307,23 @@ export const defaults = { flag_risk_tier: "0" }
1307
1307
  return result().stdout
1308
1308
  }
1309
1309
 
1310
- it("pins flag_risk_tier=1 with the flag on", async () => {
1311
- expect(await start(undefined, { GTD_FLAG_RISK_TIER: "1" })).toContain("flag_risk_tier=1")
1310
+ it("pins flag_beta=1 with the flag on", async () => {
1311
+ expect(await start([BETA], { GTD_FLAG_BETA: "1" })).toContain("flag_beta=1")
1312
1312
  })
1313
1313
 
1314
1314
  it("honours defaultOn with GTD_FLAG_* unset", async () => {
1315
- const out = await start(
1316
- [
1317
- {
1318
- name: "risk_tier",
1319
- owner: "pmelab",
1320
- expiry: "2099-01-01",
1321
- kind: "release",
1322
- defaultOn: true,
1323
- },
1324
- ],
1325
- { GTD_FLAG_RISK_TIER: "" },
1326
- )
1327
- expect(out).toContain("flag_risk_tier=1")
1315
+ const out = await start([{ ...BETA, defaultOn: true }], { GTD_FLAG_BETA: "" })
1316
+ expect(out).toContain("flag_beta=1")
1328
1317
  })
1329
1318
  })
1330
1319
 
1320
+ const IDLE_CONFIG = `import { human } from "@pmelab/gtd/flows"
1321
+
1322
+ export default async () => {
1323
+ await human("idle", { message: "hi" })
1324
+ }
1325
+ `
1326
+
1331
1327
  describe("runCli — exit codes", () => {
1332
1328
  it("output plan: exit is never called (success) — --help and --version still exit 0", async () => {
1333
1329
  const { io, captured } = capturingIo(throwingLayers)
@@ -1369,7 +1365,7 @@ describe("runCli — exit codes", () => {
1369
1365
  it("an unknown --json=<path> selector exits EXIT_USAGE_ERROR, not EXIT_RUNTIME_ERROR — Program.ts's SelectorUsageError mapped by report()", async () => {
1370
1366
  const repo = new InMemRepo()
1371
1367
  repo.writeFile("NOTE.md", "a note\n")
1372
- repo.writeFile("gtd.config.ts", CONFIG_SCAFFOLD)
1368
+ repo.writeFile("gtd.config.ts", IDLE_CONFIG)
1373
1369
  repo.seedCommit("gtd: init")
1374
1370
  const { io, captured } = capturingIo(() => testLayers(repo))
1375
1371
  await Effect.runPromise(runCli(["node", "gtd.js", "next", "--json=does.not.exist"], io))
@@ -1534,7 +1530,7 @@ describe("runCli — flush normalizes the artifact's trailing newline", () => {
1534
1530
  it("a plain-text write command's script (no trailing newline) reaches io.stdout with exactly one", async () => {
1535
1531
  const repo = new InMemRepo()
1536
1532
  repo.writeFile(".gitignore", "node_modules\n")
1537
- repo.writeFile("gtd.config.ts", CONFIG_SCAFFOLD)
1533
+ repo.writeFile("gtd.config.ts", IDLE_CONFIG)
1538
1534
  repo.seedCommit("chore: initial commit")
1539
1535
  const { io, captured } = capturingIo(() => testLayers(repo))
1540
1536
  await Effect.runPromise(runCli(["node", "gtd.js", "land"], io))
package/src/cli/Cli.ts CHANGED
@@ -578,10 +578,8 @@ const COMMAND_ROWS: readonly CommandRow[] = [
578
578
  "agent (or a human) to build a gtd driver in any shell or",
579
579
  "runtime — the self-serve version of",
580
580
  "https://github.com/pmelab/gtd/blob/main/docs/driver.md's",
581
- "'Writing your own driver'. At a repository root no",
582
- "gtd.config.ts governs yet, it also writes one importing each",
583
- "shipped workflow from @pmelab/gtd/workflows/<name>; anywhere",
584
- "else it writes nothing.",
581
+ "'Writing your own driver', and how to get a workflow: gtd",
582
+ "ships none. It writes nothing.",
585
583
  ],
586
584
  },
587
585
  {