@pmelab/gtd 52.1.0 → 54.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 (94) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +91 -129
  3. package/claude/hooks/drive.ts +0 -3
  4. package/claude/hooks/register.tsx +3 -68
  5. package/claude/hooks/ship.ts +85 -1595
  6. package/dist/chunks/{reserved-DxB5CROj.mjs → reserved-CQ7amcKG.mjs} +45 -132
  7. package/dist/chunks/{review-CKMSdvgW.mjs → review-CXzUXJHL.mjs} +12 -5
  8. package/dist/chunks/{tail-QZc7Uz2U.mjs → tail-JZ9PAOwp.mjs} +2 -14
  9. package/dist/chunks/{wire-BJuZWR3C.mjs → wire-C12dahrH.mjs} +2 -130
  10. package/dist/flows.mjs +1 -16
  11. package/dist/gtd.bundle.mjs +476 -982
  12. package/dist/testkit.mjs +58 -417
  13. package/dist/wire.mjs +2 -2
  14. package/dist/workflows/cleanup.mjs +2 -2
  15. package/dist/workflows/feature.mjs +112 -363
  16. package/dist/workflows/fix.mjs +2 -2
  17. package/dist/workflows/review.mjs +3 -3
  18. package/dist/workflows/rollout.mjs +1 -1
  19. package/dist/workflows.mjs +321 -677
  20. package/package.json +1 -1
  21. package/src/cli/Cli.test.ts +1 -86
  22. package/src/cli/Cli.ts +13 -133
  23. package/src/cli/Metrics.test.ts +21 -31
  24. package/src/cli/Metrics.ts +25 -36
  25. package/src/cli/Program.test.ts +1 -35
  26. package/src/cli/Program.ts +5 -64
  27. package/src/cli/ToolCommands.ts +0 -3
  28. package/src/edge/Edge.test.ts +20 -322
  29. package/src/edge/Edge.ts +10 -181
  30. package/src/edge/index.ts +0 -1
  31. package/src/flows/runtime.test.ts +0 -27
  32. package/src/flows/runtime.ts +1 -49
  33. package/src/install/Install.ts +15 -24
  34. package/src/replay/Replay.test.ts +0 -169
  35. package/src/replay/Replay.ts +4 -93
  36. package/src/replay/index.ts +0 -1
  37. package/src/testkit/Layers.ts +0 -1
  38. package/src/testkit/steps.ts +23 -0
  39. package/src/wire/BeatDocument.ts +2 -12
  40. package/src/wire/BeatStatus.ts +0 -3
  41. package/src/wire/WireSchema.test.ts +0 -18
  42. package/src/wire/index.ts +0 -2
  43. package/src/wire/types.ts +0 -7
  44. package/src/workflow/Workflow.ts +1 -9
  45. package/src/workflows/feature/criteria.test.ts +1 -14
  46. package/src/workflows/feature/criteria.ts +6 -38
  47. package/src/workflows/feature/feature.ts +34 -118
  48. package/src/workflows/feature/hands-on-skip.feature +2 -70
  49. package/src/workflows/feature/index.ts +0 -1
  50. package/src/workflows/feature/lens-review.feature +50 -0
  51. package/src/workflows/feature/one-step-land.feature +0 -4
  52. package/src/workflows/feature/prototype-rounds.feature +1 -1
  53. package/src/workflows/feature/prototype.feature +3 -3
  54. package/src/workflows/feature/prototype.ts +18 -29
  55. package/src/workflows/feature/refactor-absorbs.feature +3 -8
  56. package/src/workflows/feature/refactor-first.feature +0 -5
  57. package/src/workflows/feature/refactor-rebuild.feature +0 -6
  58. package/src/workflows/feature/refactor-review.feature +49 -11
  59. package/src/workflows/feature/refactor.ts +30 -64
  60. package/src/workflows/feature/review-gate.feature +1 -1
  61. package/src/workflows/feature/reviewer-walkthrough.feature +0 -31
  62. package/src/workflows/feature/seam-rejection.feature +0 -4
  63. package/src/workflows/feature/seam.test.ts +7 -26
  64. package/src/workflows/feature/seam.ts +52 -165
  65. package/src/workflows/index.ts +0 -1
  66. package/src/workflows/shared/lenses.ts +0 -13
  67. package/src/workflows/shared/options.test.ts +2 -11
  68. package/src/workflows/shared/options.ts +4 -21
  69. package/src/workflows/shared/review.test.ts +3 -3
  70. package/src/workflows/shared/review.ts +12 -2
  71. package/src/workflows/shared/steps.ts +0 -2
  72. package/src/workflows/shared/text.test.ts +10 -80
  73. package/src/workflows/shared/text.ts +35 -211
  74. package/src/workflows/shared/vars.ts +2 -8
  75. package/src/workflows/shared/workflows.steps.ts +0 -13
  76. package/claude/hooks/context.ts +0 -66
  77. package/src/replay/Slices.test.ts +0 -73
  78. package/src/replay/Slices.ts +0 -45
  79. package/src/wire/ContextLimit.fixture.ts +0 -30
  80. package/src/wire/ContextLimit.test.ts +0 -96
  81. package/src/wire/ContextLimit.ts +0 -169
  82. package/src/wire/Slices.ts +0 -25
  83. package/src/wire/golden/1/slices.json +0 -11
  84. package/src/workflows/feature/context-limit.feature +0 -37
  85. package/src/workflows/feature/continuation-cap.feature +0 -69
  86. package/src/workflows/feature/continuation-rebuild.feature +0 -92
  87. package/src/workflows/feature/continuation-slices.feature +0 -68
  88. package/src/workflows/feature/continuation.feature +0 -209
  89. package/src/workflows/feature/slice-findings.feature +0 -256
  90. package/src/workflows/feature/slicing-cap.feature +0 -23
  91. package/src/workflows/feature/slicing.feature +0 -297
  92. package/src/workflows/feature/slicing.test.ts +0 -49
  93. package/src/workflows/feature/slicing.ts +0 -105
  94. package/src/workflows/shared/continuation.ts +0 -92
@@ -18,7 +18,8 @@ export interface MetricCommit {
18
18
  }
19
19
 
20
20
  export interface Metrics {
21
- readonly slices: {
21
+ /** Each naive pass's decision: landed as it is, or rebuilt (after a refactor or not). */
22
+ readonly builds: {
22
23
  readonly decided: number
23
24
  readonly landed: number
24
25
  readonly rebuilt: number
@@ -26,8 +27,6 @@ export interface Metrics {
26
27
  }
27
28
  /** `rebuilt / decided`, to three places; null without a decision. */
28
29
  readonly refactorFirstRate: number | null
29
- /** Continuation turns per phase, keyed by the step without its `continue` segment. */
30
- readonly continuations: Readonly<Record<string, number>>
31
30
  readonly cost: {
32
31
  readonly total: number
33
32
  readonly byModel: Readonly<Record<string, number>>
@@ -37,7 +36,7 @@ export interface Metrics {
37
36
  readonly implement: number
38
37
  readonly other: number
39
38
  }
40
- /** A rebuilt slice's naive pass and the implement turns that rebuilt it. */
39
+ /** A rebuilt build's naive pass and the implement turns that rebuilt it. */
41
40
  readonly doubleWork: {
42
41
  readonly naive: number
43
42
  readonly rebuild: number
@@ -54,11 +53,9 @@ type Phase = keyof Metrics["cost"]["byPhase"]
54
53
  // Keyed by the lap's path without `.reviewing`, so the refactor lap's lenses stay apart from the feature's.
55
54
  const LENS = /^((?:.*\.)?quality\.[^.]+)\.reviewing$/
56
55
 
57
- const CONTINUE = /(^|\.)continue\./
58
-
59
56
  const inSeam = (step: string): boolean => step.startsWith("seam.")
60
57
 
61
- // A naive retry after a continuation runs under `seam.fresh.naive`: any seam step named `naive` is the pass.
58
+ // Any seam step named `naive` is the pass, whatever scope an older history ran it under.
62
59
  const phaseOf = (step: string): Phase => {
63
60
  const scope = step.split(".")[0]
64
61
  if (inSeam(step) && step.endsWith(".naive")) return "naive"
@@ -83,8 +80,7 @@ const roundAll = (record: Record<string, number>): Record<string, number> =>
83
80
  Object.fromEntries(Object.entries(record).map(([k, v]) => [k, round(v)]))
84
81
 
85
82
  interface Tally {
86
- readonly slices: { decided: number; landed: number; rebuilt: number; refactored: number }
87
- readonly continuations: Record<string, number>
83
+ readonly builds: { decided: number; landed: number; rebuilt: number; refactored: number }
88
84
  readonly byModel: Record<string, number>
89
85
  readonly byPhase: Record<Phase, number>
90
86
  readonly doubleWork: { naive: number; rebuild: number }
@@ -93,8 +89,7 @@ interface Tally {
93
89
  }
94
90
 
95
91
  const emptyTally = (): Tally => ({
96
- slices: { decided: 0, landed: 0, rebuilt: 0, refactored: 0 },
97
- continuations: record(),
92
+ builds: { decided: 0, landed: 0, rebuilt: 0, refactored: 0 },
98
93
  byModel: record(),
99
94
  byPhase: { naive: 0, refactor: 0, implement: 0, other: 0 },
100
95
  doubleWork: { naive: 0, rebuild: 0 },
@@ -106,9 +101,8 @@ const sum = (record: Readonly<Record<string, number>>): number =>
106
101
  Object.values(record).reduce((a, b) => a + b, 0)
107
102
 
108
103
  const finish = (t: Tally): Metrics => ({
109
- slices: t.slices,
110
- refactorFirstRate: t.slices.decided === 0 ? null : round(t.slices.rebuilt / t.slices.decided, 3),
111
- continuations: t.continuations,
104
+ builds: t.builds,
105
+ refactorFirstRate: t.builds.decided === 0 ? null : round(t.builds.rebuilt / t.builds.decided, 3),
112
106
  cost: {
113
107
  total: round(sum(t.byModel)),
114
108
  byModel: roundAll(t.byModel),
@@ -123,8 +117,8 @@ const finish = (t: Tally): Metrics => ({
123
117
  flaky: { total: sum(t.flaky), byStep: t.flaky },
124
118
  })
125
119
 
126
- /** The open slice: its naive cost so far, and whether its implement turns rebuild it. */
127
- interface Slice {
120
+ /** The open build: its naive cost so far, and whether its implement turns rebuild it. */
121
+ interface Build {
128
122
  naive: number
129
123
  rebuilding: boolean
130
124
  /** Landed, not yet applied, with its naive cost: an apply that fails turns it into a rebuild. */
@@ -133,7 +127,7 @@ interface Slice {
133
127
 
134
128
  const tallyCost = (
135
129
  t: Tally,
136
- open: Slice,
130
+ open: Build,
137
131
  costs: ReturnType<typeof parseCommitMessage>["cost"],
138
132
  phase: Phase,
139
133
  ): void => {
@@ -145,50 +139,48 @@ const tallyCost = (
145
139
  }
146
140
  }
147
141
 
148
- /** A slice is decided where its turns leave the seam: for `implement.applying` the naive pass landed — unless it fails to apply and is built afresh — anywhere else it is rebuilt. */
149
- const tallyDecision = (t: Tally, open: Slice, at: string, to: string): void => {
150
- // The slice's closing ends its rebuild: later implement turns (an amend after review) are new work.
142
+ /** A build is decided where its turns leave the seam: for `implement.applying` the naive pass landed — unless it fails to apply and is built afresh — anywhere else it is rebuilt. */
143
+ const tallyDecision = (t: Tally, open: Build, at: string, to: string): void => {
144
+ // The build's closing ends its rebuild: later implement turns (an amend after review) are new work.
151
145
  if (at === "implement.closing") open.rebuilding = false
152
146
  if (open.applying !== undefined && at === "implement.applying") {
153
147
  const naive = open.applying
154
148
  open.applying = undefined
155
149
  if (to !== "implement.building") return
156
- t.slices.landed--
150
+ t.builds.landed--
157
151
  rebuild(t, open, naive)
158
152
  return
159
153
  }
160
154
  if (!inSeam(at) || inSeam(to)) return
161
- t.slices.decided++
155
+ t.builds.decided++
162
156
  const naive = open.naive
163
157
  open.naive = 0
164
158
  if (to === "implement.applying") {
165
159
  open.applying = naive
166
160
  open.rebuilding = false
167
- t.slices.landed++
161
+ t.builds.landed++
168
162
  return
169
163
  }
170
164
  rebuild(t, open, naive)
171
- if (at === "seam.note") t.slices.refactored++
165
+ if (at === "seam.note") t.builds.refactored++
172
166
  }
173
167
 
174
- const rebuild = (t: Tally, open: Slice, naive: number): void => {
168
+ const rebuild = (t: Tally, open: Build, naive: number): void => {
175
169
  open.rebuilding = true
176
- t.slices.rebuilt++
170
+ t.builds.rebuilt++
177
171
  t.doubleWork.naive += naive
178
172
  }
179
173
 
180
174
  /** Oldest first; a commit gtd did not write is skipped. */
181
175
  export const processMetrics = (commits: readonly MetricCommit[]): Metrics => {
182
176
  const t = emptyTally()
183
- const open: Slice = { naive: 0, rebuilding: false, applying: undefined }
177
+ const open: Build = { naive: 0, rebuilding: false, applying: undefined }
184
178
  for (const commit of commits) {
185
179
  const message = parseCommitMessage(commit.message)
186
180
  const subject = message.parsed
187
181
  if (subject === undefined) continue
188
182
  const at = subject.from ?? subject.to
189
183
  tallyCost(t, open, message.cost, phaseOf(at))
190
- if (subject.actor === "agent" && CONTINUE.test(at))
191
- add(t.continuations, at.replace(CONTINUE, "$1"), 1)
192
184
  const lens = LENS.exec(at)?.[1]
193
185
  if (lens !== undefined && commit.findings > 0) add(t.byLens, lens, commit.findings)
194
186
  if (message.marks.some((m) => m.name === "flaky")) add(t.flaky, at, 1)
@@ -200,9 +192,8 @@ export const processMetrics = (commits: readonly MetricCommit[]): Metrics => {
200
192
  export const totalMetrics = (all: readonly Metrics[]): Metrics & { readonly processes: number } => {
201
193
  const t = emptyTally()
202
194
  for (const m of all) {
203
- for (const key of Object.keys(t.slices) as (keyof Tally["slices"])[])
204
- t.slices[key] += m.slices[key]
205
- for (const [k, v] of Object.entries(m.continuations)) add(t.continuations, k, v)
195
+ for (const key of Object.keys(t.builds) as (keyof Tally["builds"])[])
196
+ t.builds[key] += m.builds[key]
206
197
  for (const [k, v] of Object.entries(m.cost.byModel)) add(t.byModel, k, v)
207
198
  for (const key of Object.keys(t.byPhase) as Phase[]) t.byPhase[key] += m.cost.byPhase[key]
208
199
  t.doubleWork.naive += m.cost.doubleWork.naive
@@ -256,12 +247,10 @@ const listed = (record: Readonly<Record<string, number>>): string =>
256
247
  .join(", ")
257
248
 
258
249
  const line = (label: string, m: Metrics): string => {
259
- const { slices, cost } = m
250
+ const { builds, cost } = m
260
251
  const percent = m.refactorFirstRate === null ? "-" : `${Math.round(m.refactorFirstRate * 100)}%`
261
- const continued = sum(m.continuations)
262
252
  return [
263
- `${label}: ${slices.decided} slices, ${slices.rebuilt} rebuilt (refactor-first ${percent}), ${slices.refactored} refactored`,
264
- `${continued} continuations${continued > 0 ? ` (${listed(m.continuations)})` : ""}`,
253
+ `${label}: ${builds.decided} builds, ${builds.rebuilt} rebuilt (refactor-first ${percent}), ${builds.refactored} refactored`,
265
254
  `cost ${cost.total} (${listed(cost.byPhase)}; by model ${listed(cost.byModel) || "-"}), double work ${cost.doubleWork.total}`,
266
255
  `${m.findings.total} findings${m.findings.total > 0 ? ` (${listed(m.findings.byLens)})` : ""}`,
267
256
  `${m.flaky.total} flaky${m.flaky.total > 0 ? ` (${listed(m.flaky.byStep)})` : ""}`,
@@ -1677,7 +1677,6 @@ describe("runCommand — refuses in a repository with no commits", () => {
1677
1677
  start: { kind: "start", actor: "human", workflow: "feature", vars: {}, label: "" },
1678
1678
  door: { kind: "door", name: "fix", args: [] },
1679
1679
  doors: { kind: "doors" },
1680
- slices: { kind: "slices" },
1681
1680
  abandon: { kind: "abandon" },
1682
1681
  restore: { kind: "restore" },
1683
1682
  next: { kind: "next" },
@@ -1685,7 +1684,6 @@ describe("runCommand — refuses in a repository with no commits", () => {
1685
1684
  check: { kind: "check", mode: "qa", file: ".gtd/TODO.md" },
1686
1685
  uncheck: { kind: "uncheck", file: ".gtd/REVIEW.md" },
1687
1686
  install: { kind: "install" },
1688
- contextHook: { kind: "contextHook" },
1689
1687
  summary: { kind: "summary" },
1690
1688
  base: { kind: "base" },
1691
1689
  judge: { kind: "judge" },
@@ -1706,14 +1704,13 @@ describe("runCommand — refuses in a repository with no commits", () => {
1706
1704
  const NO_COMMITS_MESSAGE =
1707
1705
  "gtd requires a repository with at least one commit — make an initial commit, then run gtd again"
1708
1706
 
1709
- it("derives exactly the nineteen non-standalone kinds — a canary for the table-driven cases below", () => {
1707
+ it("derives exactly the eighteen non-standalone kinds — a canary for the table-driven cases below", () => {
1710
1708
  expect(stateKinds.sort()).toEqual(
1711
1709
  [
1712
1710
  "abandon",
1713
1711
  "base",
1714
1712
  "door",
1715
1713
  "doors",
1716
- "slices",
1717
1714
  "start",
1718
1715
  "land",
1719
1716
  "next",
@@ -1856,37 +1853,6 @@ export const summary = (c) => \`entry=\${c.entryCommit} tip=\${c.processTip} hum
1856
1853
  })
1857
1854
  })
1858
1855
 
1859
- describe("gtd summary --slice", () => {
1860
- const SLICED = `import { agent, human, slice } from "@pmelab/gtd/flows"
1861
-
1862
- export default async () => {
1863
- await human("idle", { message: "hi" })
1864
- await slice("build", async () => {
1865
- await agent("working", "go")
1866
- })
1867
- await slice("verify", async () => {
1868
- await agent("polishing", "polish")
1869
- })
1870
- }
1871
-
1872
- export const summary = (c) => \`\${c.slice ? c.slice.name + " " + c.slice.index + "/" + c.slice.count : "plain"} \${c.processBase}..\${c.processTip}\`
1873
- `
1874
-
1875
- it("a version warning goes to stderr, stdout still carries the prompt", async () => {
1876
- const repo = seed(SLICED)
1877
- await landTurn(repo, { "NOTE.md": "a note\n" })
1878
- await landTurn(repo, { "WORK.md": "done\n" })
1879
- repo.writeFile("POLISH.md", "ok\n")
1880
- repo.seedCommit(
1881
- "gtd(agent): polishing → idle\n\nGtd-Step: polishing#1\nGtd-Version: 0.0.1\nGtd-Format: 1",
1882
- )
1883
- const { exitCode, stdout, stderr } = await run(repo, "summary", "--slice", "verify")
1884
- expect(exitCode).toBe(0)
1885
- expect(stderr).toContain("recorded by gtd 0.0.1")
1886
- expect(stdout).toContain("verify 2/2")
1887
- })
1888
- })
1889
-
1890
1856
  describe("gtd base — prints the review anchor hash", () => {
1891
1857
  // Each round's review window starts at the commit that entered `deciding`;
1892
1858
  // FEEDBACK.md sends the process round again, a clean accept finishes it.
@@ -43,8 +43,6 @@ import {
43
43
  type StatusChange,
44
44
  selectPath,
45
45
  EXIT_OK,
46
- renderSlicesJson,
47
- slicesDocument,
48
46
  } from "../wire/index.js"
49
47
  import {
50
48
  currentRest,
@@ -53,7 +51,6 @@ import {
53
51
  renderRest,
54
52
  restAt,
55
53
  previewLanding,
56
- processSlices,
57
54
  noProcessUnderway,
58
55
  restIsIdle,
59
56
  reviewBaseFor,
@@ -148,7 +145,6 @@ export type Command =
148
145
  }
149
146
  | { readonly kind: "door"; readonly name: string; readonly args: readonly string[] }
150
147
  | { readonly kind: "doors" }
151
- | { readonly kind: "slices" }
152
148
  | { readonly kind: "abandon" }
153
149
  | { readonly kind: "restore" }
154
150
  | { readonly kind: "next" }
@@ -163,13 +159,7 @@ export type Command =
163
159
  }
164
160
  | { readonly kind: "uncheck"; readonly file: string }
165
161
  | { readonly kind: "install" }
166
- | {
167
- readonly kind: "contextHook"
168
- readonly soft?: number
169
- readonly hard?: number
170
- readonly window?: number
171
- }
172
- | { readonly kind: "summary"; readonly slice?: string }
162
+ | { readonly kind: "summary" }
173
163
  | { readonly kind: "base" }
174
164
  | { readonly kind: "judge" }
175
165
  | { readonly kind: "judgeAnswer" }
@@ -337,21 +327,12 @@ const planLanding = (
337
327
  * closes or sits inside its own closing message. Writes nothing — no git, no
338
328
  * state transition, no file. Refuses (throws, mapped to the runtime-error
339
329
  * exit code) when the workflow declares no `summary` prompt, or the resolved
340
- * run has no commits to name. `--slice` also refuses an unknown slice name
341
- * (listing the valid slices), a config edited since the process closed, and a
342
- * replay that diverged. The edge never narrates: the CLI prints the
343
- * version warning, before the unknown-slice refusal; a replay refusal carries
344
- * it in its message.
330
+ * run has no commits to name.
345
331
  */
346
- const runSummaryCommand = (
347
- out: ArtifactOut,
348
- sliceName: string | undefined,
349
- ): Effect.Effect<void, Error, CommandRequirements> =>
332
+ const runSummaryCommand = (out: ArtifactOut): Effect.Effect<void, Error, CommandRequirements> =>
350
333
  Effect.gen(function* () {
351
334
  const run = yield* summaryRun
352
- const { prompt, versionWarning, unknownSlice } = yield* summaryFor(run, sliceName)
353
- if (versionWarning !== undefined) yield* (yield* Narrator).warn(versionWarning)
354
- if (unknownSlice !== undefined) return yield* Effect.fail(new Error(unknownSlice))
335
+ const { prompt } = yield* summaryFor(run)
355
336
  if (prompt === undefined) {
356
337
  return yield* Effect.fail(
357
338
  new Error(
@@ -445,24 +426,6 @@ const readStdin = (): Effect.Effect<string, Error> =>
445
426
  catch: (e) => (e instanceof Error ? e : new Error(String(e))),
446
427
  })
447
428
 
448
- /** Stdout is written once: a hook reads one JSON document, or none. */
449
- const runContextHookCommand = (
450
- command: Extract<Command, { kind: "contextHook" }>,
451
- out: ArtifactOut,
452
- ): Effect.Effect<void, Error, ToolCommands> =>
453
- Effect.gen(function* () {
454
- const tools = yield* ToolCommands
455
- const input = yield* readStdin()
456
- out.write(
457
- yield* tools.contextHook({
458
- input,
459
- ...(command.soft !== undefined ? { soft: command.soft } : {}),
460
- ...(command.hard !== undefined ? { hard: command.hard } : {}),
461
- ...(command.window !== undefined ? { window: command.window } : {}),
462
- }),
463
- )
464
- })
465
-
466
429
  /** Stdout is written once, after the answerer resolves — a rejection leaves it empty. */
467
430
  const runJudgeRunCommand = (
468
431
  command: Extract<Command, { kind: "judgeRun" }>,
@@ -701,22 +664,6 @@ const runDoorsCommand = (
701
664
  else yield* writeSelection(out, document, json.path)
702
665
  })
703
666
 
704
- const runSlicesCommand = (
705
- json: JsonMode,
706
- out: ArtifactOut,
707
- ): Effect.Effect<void, Error, CommandRequirements> =>
708
- Effect.gen(function* () {
709
- const { slices, versionWarning } = yield* processSlices
710
- if (versionWarning !== undefined) yield* (yield* Narrator).warn(versionWarning)
711
- if (json.kind === "off") {
712
- out.write(slices.map((s) => `${s.name} ${s.base}..${s.tip}\n`).join(""))
713
- return
714
- }
715
- const document = slicesDocument(slices)
716
- if (json.kind === "document") out.write(renderSlicesJson(document))
717
- else yield* writeSelection(out, document, json.path)
718
- })
719
-
720
667
  /**
721
668
  * Ends the process underway without completing it. Nothing is discarded: a
722
669
  * `git reset --mixed` to the process's start commit leaves every turn
@@ -1217,7 +1164,6 @@ export const needsOf = (kind: Command["kind"]): Needs => {
1217
1164
  switch (kind) {
1218
1165
  case "lsp":
1219
1166
  case "install":
1220
- case "contextHook":
1221
1167
  case "judgeRun":
1222
1168
  case "strays":
1223
1169
  return "none"
@@ -1235,7 +1181,6 @@ export const standaloneKinds = (): readonly Command["kind"][] => [
1235
1181
  "check",
1236
1182
  "uncheck",
1237
1183
  "install",
1238
- "contextHook",
1239
1184
  "judgeRun",
1240
1185
  "strays",
1241
1186
  ]
@@ -1281,8 +1226,6 @@ const dispatchVoidCommand = (
1281
1226
  return runDoorCommand(command.name, command.args, out)
1282
1227
  case "doors":
1283
1228
  return runDoorsCommand(json, out)
1284
- case "slices":
1285
- return runSlicesCommand(json, out)
1286
1229
  case "abandon":
1287
1230
  return runAbandonCommand(out)
1288
1231
  case "restore":
@@ -1305,10 +1248,8 @@ const dispatchVoidCommand = (
1305
1248
  return runUncheckCommand(command.file)
1306
1249
  case "install":
1307
1250
  return runInstallCommand(out)
1308
- case "contextHook":
1309
- return runContextHookCommand(command, out)
1310
1251
  case "summary":
1311
- return runSummaryCommand(out, command.slice)
1252
+ return runSummaryCommand(out)
1312
1253
  case "base":
1313
1254
  return runBaseCommand(out)
1314
1255
  case "judge":
@@ -1,7 +1,6 @@
1
1
  import { Context } from "effect"
2
2
  import type { Effect } from "effect"
3
3
  import type { UiSettings } from "../flows/index.js"
4
- import type { ContextHookRequest } from "../wire/index.js"
5
4
 
6
5
  /** How to run THIS gtd — a tool (the LSP) never reads a different version's state than the one it ships with. */
7
6
  export type SelfGtd = readonly [string, ...string[]]
@@ -38,8 +37,6 @@ export class ToolCommands extends Context.Tag("ToolCommands")<
38
37
  out: { readonly write: (chunk: string) => void; readonly flush: () => void },
39
38
  ) => Effect.Effect<void, Error>
40
39
  readonly lsp: (gtd: SelfGtd) => Effect.Effect<void, Error>
41
- /** The hook's stdout: a JSON document, or empty to let the turn go on. */
42
- readonly contextHook: (request: ContextHookRequest) => Effect.Effect<string, Error>
43
40
  readonly judge: (request: JudgeRequest) => Promise<string>
44
41
  }
45
42
  >() {}