@pmelab/gtd 24.0.0 → 24.2.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 (203) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +42 -13
  3. package/claude/hooks/access.ts +26 -1
  4. package/claude/hooks/assist.ts +135 -0
  5. package/claude/hooks/drive.ts +15 -7
  6. package/claude/hooks/judge.ts +27 -0
  7. package/claude/hooks/register.tsx +294 -135
  8. package/claude/hooks/tools.ts +121 -0
  9. package/claude/hooks/wording.ts +2 -35
  10. package/claude/types/index.d.ts +9 -0
  11. package/dist/chunks/Schema-Dh-mkwkq.mjs +25353 -0
  12. package/dist/chunks/reserved-ejn-LVYO.mjs +1388 -0
  13. package/dist/chunks/rolldown-runtime-8H4AJuhK.mjs +14 -0
  14. package/dist/chunks/steering-D-PvRC5W.mjs +16296 -0
  15. package/dist/chunks/wire-DA7q2ikx.mjs +473 -0
  16. package/dist/flows.mjs +525 -0
  17. package/dist/gtd.bundle.mjs +43339 -55575
  18. package/dist/steering.mjs +2 -0
  19. package/dist/testkit.mjs +13006 -0
  20. package/dist/wire.mjs +2 -0
  21. package/dist/workflow-kit.mjs +352 -0
  22. package/dist/workflows/feature.mjs +315 -0
  23. package/dist/workflows/fix.mjs +40 -0
  24. package/dist/workflows/review.mjs +25 -0
  25. package/dist/workflows.mjs +8 -6
  26. package/package.json +69 -8
  27. package/src/cli/Cli.test.ts +1329 -0
  28. package/src/cli/Cli.ts +1348 -0
  29. package/src/cli/Program.test.ts +2262 -0
  30. package/src/cli/Program.ts +1331 -0
  31. package/src/cli/ToolCommands.ts +43 -0
  32. package/src/cli/index.ts +14 -0
  33. package/src/edge/Edge.test.ts +1016 -0
  34. package/src/edge/Edge.ts +1225 -0
  35. package/src/edge/index.ts +16 -0
  36. package/src/emit/Emit.test.ts +425 -0
  37. package/src/emit/Emit.ts +139 -0
  38. package/src/emit/GitScript.test.ts +260 -0
  39. package/src/emit/GitScript.ts +131 -0
  40. package/src/emit/ModeContradiction.test.ts +120 -0
  41. package/src/emit/ModeContradiction.ts +66 -0
  42. package/src/emit/OutcomeScript.test.ts +238 -0
  43. package/src/emit/OutcomeScript.ts +73 -0
  44. package/src/emit/SteeringMode.test.ts +274 -0
  45. package/src/emit/SteeringMode.ts +256 -0
  46. package/src/emit/index.ts +40 -0
  47. package/src/install/ConfigScaffold.ts +19 -0
  48. package/src/install/Install.test.ts +426 -0
  49. package/src/install/Install.ts +634 -0
  50. package/src/install/Version.ts +27 -0
  51. package/src/install/index.ts +3 -0
  52. package/src/platform/CommandRunner.ts +74 -0
  53. package/src/platform/Commentary.test.ts +126 -0
  54. package/src/platform/Commentary.ts +92 -0
  55. package/src/platform/Git.test.ts +50 -0
  56. package/src/platform/Git.ts +552 -0
  57. package/src/platform/Host.ts +42 -0
  58. package/src/platform/RetainedHistory.test.ts +65 -0
  59. package/src/platform/RetainedHistory.ts +34 -0
  60. package/src/platform/Workspace.test.ts +669 -0
  61. package/src/platform/Workspace.ts +631 -0
  62. package/src/platform/WorktreeState.test.ts +162 -0
  63. package/src/platform/WorktreeState.ts +50 -0
  64. package/src/platform/index.ts +22 -0
  65. package/src/replay/Access.test.ts +53 -0
  66. package/src/replay/Access.ts +46 -0
  67. package/src/replay/Format.test.ts +20 -0
  68. package/src/replay/Format.ts +14 -0
  69. package/src/replay/Replay.test.ts +887 -0
  70. package/src/replay/Replay.ts +669 -0
  71. package/src/replay/Trailers.test.ts +117 -0
  72. package/src/replay/Trailers.ts +213 -0
  73. package/src/replay/Tree.test.ts +25 -0
  74. package/src/replay/Tree.ts +45 -0
  75. package/src/replay/index.ts +11 -0
  76. package/src/steering/Blocks.fixture.ts +6 -0
  77. package/src/steering/Blocks.test.ts +167 -0
  78. package/src/steering/Blocks.ts +321 -0
  79. package/src/steering/CodeThreads.test.ts +127 -0
  80. package/src/steering/CodeThreads.ts +197 -0
  81. package/src/steering/Edits.test.ts +19 -0
  82. package/src/steering/Edits.ts +20 -0
  83. package/src/steering/Eol.test.ts +16 -0
  84. package/src/steering/Eol.ts +2 -0
  85. package/src/steering/FORMATS.md +58 -0
  86. package/src/steering/Footnotes.test.ts +977 -0
  87. package/src/steering/Footnotes.ts +661 -0
  88. package/src/steering/Inline.fixture.ts +7 -0
  89. package/src/steering/Inline.test.ts +227 -0
  90. package/src/steering/Inline.ts +195 -0
  91. package/src/steering/MarkdownTree.test.ts +247 -0
  92. package/src/steering/MarkdownTree.ts +260 -0
  93. package/src/steering/Outline.ts +19 -0
  94. package/src/steering/SteeringFormat.test.ts +91 -0
  95. package/src/steering/SteeringFormat.ts +267 -0
  96. package/src/steering/SteeringFormats.test.ts +163 -0
  97. package/src/steering/Threads.ts +163 -0
  98. package/src/steering/contract.test.ts +264 -0
  99. package/src/steering/fixtures/code-threads/cache.stripped.ts.txt +6 -0
  100. package/src/steering/fixtures/code-threads/cache.ts.md +15 -0
  101. package/src/steering/fixtures/free-form/notes.md +12 -0
  102. package/src/steering/fixtures/free-form/notes.read.json +42 -0
  103. package/src/steering/fixtures/qa/answered.md +23 -0
  104. package/src/steering/fixtures/qa/answered.read.json +119 -0
  105. package/src/steering/fixtures/qa/requirements.md +160 -0
  106. package/src/steering/fixtures/qa/requirements.read.json +475 -0
  107. package/src/steering/fixtures/review/review.md +76 -0
  108. package/src/steering/fixtures/review/review.read.json +237 -0
  109. package/src/steering/fixtures/review/ticked.md +23 -0
  110. package/src/steering/fixtures/review/ticked.read.json +99 -0
  111. package/src/steering/fixtures/review-notes/baseline.md +14 -0
  112. package/src/steering/fixtures/review-notes/noted.md +19 -0
  113. package/src/steering/freeform.test.ts +311 -0
  114. package/src/steering/freeform.ts +148 -0
  115. package/src/steering/index.test.ts +62 -0
  116. package/src/steering/index.ts +140 -0
  117. package/src/steering/qa.test.ts +2160 -0
  118. package/src/steering/qa.ts +1104 -0
  119. package/src/steering/review.test.ts +1829 -0
  120. package/src/steering/review.ts +1180 -0
  121. package/src/step/LandStep.ts +31 -0
  122. package/src/step/RepoSnapshot.ts +29 -0
  123. package/src/step/accessRefusal.test.ts +58 -0
  124. package/src/step/accessRefusal.ts +26 -0
  125. package/src/step/index.ts +5 -0
  126. package/src/step/planStart.test.ts +202 -0
  127. package/src/step/planStart.ts +167 -0
  128. package/src/step/planStep.test.ts +104 -0
  129. package/src/step/planStep.ts +89 -0
  130. package/src/step/snapshot.fixture.ts +28 -0
  131. package/src/testkit/EmittedScriptRecognizer.test.ts +639 -0
  132. package/src/testkit/EmittedScriptRecognizer.ts +642 -0
  133. package/src/testkit/FakeGitOperations.ts +95 -0
  134. package/src/testkit/GitTiers.test.ts +11 -0
  135. package/src/testkit/GitTiers.ts +1052 -0
  136. package/src/testkit/InMemRepo.ts +467 -0
  137. package/src/testkit/Layers.test.ts +58 -0
  138. package/src/testkit/Layers.ts +241 -0
  139. package/src/testkit/StrictGitOperations.ts +31 -0
  140. package/src/testkit/TestRepo.test.ts +119 -0
  141. package/src/testkit/TestRepo.ts +274 -0
  142. package/src/testkit/cliIo.ts +36 -0
  143. package/src/testkit/index.ts +9 -0
  144. package/src/testkit/steps.ts +177 -0
  145. package/src/testkit/testkit.feature +65 -0
  146. package/src/wire/BeatDocument.test.ts +419 -0
  147. package/src/wire/BeatDocument.ts +114 -0
  148. package/src/wire/BeatPlain.test.ts +113 -0
  149. package/src/wire/BeatPlain.ts +127 -0
  150. package/src/wire/BeatStatus.test.ts +59 -0
  151. package/src/wire/BeatStatus.ts +81 -0
  152. package/src/wire/Demand.test.ts +142 -0
  153. package/src/wire/Demand.ts +108 -0
  154. package/src/wire/ExitCodes.test.ts +21 -0
  155. package/src/wire/ExitCodes.ts +16 -0
  156. package/src/wire/Glob.ts +29 -0
  157. package/src/wire/Judge.ts +22 -0
  158. package/src/wire/Land.test.ts +120 -0
  159. package/src/wire/Land.ts +75 -0
  160. package/src/wire/Select.test.ts +148 -0
  161. package/src/wire/Select.ts +78 -0
  162. package/src/wire/Sessions.test.ts +59 -0
  163. package/src/wire/Sessions.ts +39 -0
  164. package/src/wire/WireSchema.test.ts +162 -0
  165. package/src/wire/constants.ts +22 -0
  166. package/src/wire/golden/1/beat-capture.json +34 -0
  167. package/src/wire/golden/1/beat-message.json +34 -0
  168. package/src/wire/golden/1/beat-prompt.json +44 -0
  169. package/src/wire/golden/1/beat-script.json +34 -0
  170. package/src/wire/golden/1/beat-stalled.json +34 -0
  171. package/src/wire/golden/1/judge.json +14 -0
  172. package/src/wire/golden/1/land-noop.json +10 -0
  173. package/src/wire/golden/1/land.json +10 -0
  174. package/src/wire/index.ts +12 -0
  175. package/src/wire/types.ts +46 -0
  176. package/src/workflow/Diagnostic.test.ts +93 -0
  177. package/src/workflow/Diagnostic.ts +54 -0
  178. package/src/workflow/SteeringFormats.test.ts +27 -0
  179. package/src/workflow/SteeringFormats.ts +6 -0
  180. package/src/workflow/Workflow.ts +77 -0
  181. package/src/workflow/compile.test.ts +53 -0
  182. package/src/workflow/compile.ts +56 -0
  183. package/src/workflow/discovery.ts +122 -0
  184. package/src/workflow/fingerprint.test.ts +52 -0
  185. package/src/workflow/fingerprint.ts +53 -0
  186. package/src/workflow/index.ts +33 -0
  187. package/src/workflow/load.test.ts +1017 -0
  188. package/src/workflow/load.ts +836 -0
  189. package/src/workflow/skills.test.ts +109 -0
  190. package/src/workflow/skills.ts +90 -0
  191. package/src/workflow/vars.ts +33 -0
  192. package/src/workflows/feature/feature.test.ts +27 -33
  193. package/src/workflows/feature/index.ts +10 -0
  194. package/src/workflows/fix/fix.feature +80 -0
  195. package/src/workflows/fix/fix.test.ts +58 -78
  196. package/src/workflows/fix/index.ts +6 -0
  197. package/src/workflows/index.ts +2 -10
  198. package/src/workflows/review/index.ts +6 -0
  199. package/src/workflows/review/review.test.ts +85 -29
  200. package/src/workflows/review/review.ts +3 -1
  201. package/src/workflows/shared/config.fixture.ts +12 -0
  202. package/src/workflows/shared/reserved.ts +11 -0
  203. package/src/workflows/shared/workflows.steps.ts +7 -0
@@ -1,36 +1,92 @@
1
- import { afterEach, describe, expect, it } from "vitest"
2
- import { installContext, type StepRequest } from "../../flows/index.js"
3
- import { fixtureContext } from "../shared/text.fixture.js"
4
- import { base, reviewFlow } from "./review.js"
1
+ import { describe, expect, it } from "vitest"
2
+ import { runsItsScript, testRepo, type TestRepo } from "../../testkit/index.js"
3
+ import { CONFIG_SCAFFOLD } from "../../install/index.js"
4
+ import { reviewDoc } from "../shared/config.fixture.js"
5
+ import { base } from "./review.js"
5
6
 
6
- afterEach(() => installContext(undefined))
7
+ const REVIEW = ".gtd/REVIEW.md"
8
+ const ORIG = ".gtd/REVIEW.orig.md"
9
+
10
+ /** A repository with one commit to review, the review run until the human holds `.gtd/REVIEW.md`. */
11
+ const reviewed = async (): Promise<TestRepo> => {
12
+ const repo = testRepo({ workflow: CONFIG_SCAFFOLD, env: { GTD_QUALITYREVIEWS: "" } })
13
+ repo.write("src/calc.ts", "export const add = (a: number, b: number) => a + b\n")
14
+ repo.commit("feat: add calculator")
15
+ await repo.door("review", "HEAD~1")
16
+ const rest = await repo.drive({
17
+ "review-gate.check": () => undefined,
18
+ "build.health.check": () => undefined,
19
+ "build.review.reviewing": (beat, r) => {
20
+ expect(beat.file).toBe(REVIEW)
21
+ r.write(REVIEW, reviewDoc("new add function"))
22
+ },
23
+ })
24
+ expect(rest).toMatchObject({ state: "build.review.await-review", actor: "human" })
25
+ return repo
26
+ }
7
27
 
8
28
  describe("the review workflow", () => {
9
- it("refuses a pending hand-off before requesting any step, the gate's check included", async () => {
10
- const requested: string[] = []
11
- const files = new Map([
12
- [".gtd/REVIEW.md", "notes"],
13
- [".gtd/REVIEW.orig.md", "orig"],
29
+ it("checks, reviews and hands the review to the human", async () => {
30
+ const repo = await reviewed()
31
+ expect(repo.subjects().slice(2)).toEqual([
32
+ "gtd(human): review-gate.check",
33
+ "gtd(check): review-gate.check → build.health.check",
34
+ "gtd(check): build.health.check → build.review.reviewing",
35
+ "gtd(agent): build.review.reviewing → build.review.await-review",
36
+ ])
37
+ })
38
+
39
+ it("closes a review the human signs off", async () => {
40
+ const repo = await reviewed()
41
+ repo.write(REVIEW, reviewDoc("new add function").replace("- [ ]", "- [x]"))
42
+ await repo.land()
43
+ const rest = await repo.drive({ "build.review.closing": runsItsScript })
44
+ expect(rest.state).toBe("idle")
45
+ expect(repo.subjects().slice(-2)).toEqual([
46
+ "gtd(human): build.review.await-review → build.review.closing",
47
+ "gtd(check): build.review.closing → idle",
48
+ ])
49
+ expect(repo.read(REVIEW)).toBeUndefined()
50
+ })
51
+
52
+ it("never lands a review the review format rejects", async () => {
53
+ const repo = testRepo({ workflow: CONFIG_SCAFFOLD, env: { GTD_QUALITYREVIEWS: "" } })
54
+ repo.write("src/calc.ts", "export const add = (a: number, b: number) => a + b\n")
55
+ repo.commit("feat: add calculator")
56
+ await repo.door("review", "HEAD~1")
57
+ await expect(
58
+ repo.drive({
59
+ "review-gate.check": () => undefined,
60
+ "build.health.check": () => undefined,
61
+ "build.review.reviewing": (_, r) => r.write(REVIEW, "not a review\n"),
62
+ }),
63
+ ).rejects.toThrow(/validating \.gtd\/REVIEW\.md/)
64
+ expect(repo.subjects().at(-1)).toBe("gtd(check): build.health.check → build.review.reviewing")
65
+ })
66
+
67
+ it("ends with the review and the reviewer's copy kept when the human leaves a note", async () => {
68
+ const repo = await reviewed()
69
+ repo.write(REVIEW, reviewDoc("new add function — rename it to sum"))
70
+ await repo.land()
71
+ const rest = await repo.drive({
72
+ "build.review.handing-off": runsItsScript,
73
+ })
74
+ expect(rest.state).toBe("idle")
75
+ expect(repo.subjects().slice(-2)).toEqual([
76
+ "gtd(human): build.review.await-review → build.review.handing-off",
77
+ "gtd(check): build.review.handing-off → idle",
14
78
  ])
15
- installContext(
16
- fixtureContext(
17
- {},
18
- {
19
- step: (request: StepRequest) => {
20
- requested.push(request.kind)
21
- return Promise.resolve()
22
- },
23
- refuse: (message: string): never => {
24
- throw new Error(message)
25
- },
26
- pushScope: () => undefined,
27
- popScope: () => undefined,
28
- read: (path) => files.get(path),
29
- },
30
- ),
31
- )
32
- await expect(reviewFlow()).rejects.toThrow(".gtd/REVIEW.orig.md is a pending hand-off")
33
- expect(requested).toEqual([])
79
+ expect(repo.read(REVIEW)).toContain("rename it to sum")
80
+ expect(repo.read(ORIG)).toBe(reviewDoc("new add function"))
81
+ })
82
+
83
+ it("refuses a pending hand-off before requesting any step, the gate's check included", async () => {
84
+ const repo = testRepo({
85
+ workflow: CONFIG_SCAFFOLD,
86
+ files: { [REVIEW]: reviewDoc("note"), [ORIG]: reviewDoc("orig") },
87
+ })
88
+ await expect(repo.door("review")).rejects.toThrow(".gtd/REVIEW.orig.md is a pending hand-off")
89
+ expect(repo.subjects()).toEqual(["chore: initial commit"])
34
90
  })
35
91
  })
36
92
 
@@ -20,7 +20,9 @@ export const review = shipped(
20
20
  {
21
21
  review: {
22
22
  args: [{ name: "base", optional: true }],
23
- vars: ({ base }) => (base === undefined ? {} : { reviewBase: base }),
23
+ // Annotated: inferred, the literal union only fits under exactOptionalPropertyTypes.
24
+ vars: ({ base }): Readonly<Record<string, string>> =>
25
+ base === undefined ? {} : { reviewBase: base },
24
26
  },
25
27
  },
26
28
  (_workflow, vars) => vars.reviewBase || "",
@@ -0,0 +1,12 @@
1
+ /** A `.gtd/REVIEW.md` on the fixture calculator, one hunk per note. */
2
+ export const reviewDoc = (...notes: string[]): string =>
3
+ [
4
+ "# Review: abc1234",
5
+ "",
6
+ "<!-- base: 0000000 -->",
7
+ "",
8
+ "## calc",
9
+ "",
10
+ ...notes.flatMap((note, i) => [`- [ ] ./src/calc.ts#${i + 1}-${i + 1}`, ` ${note}`]),
11
+ "",
12
+ ].join("\n")
@@ -0,0 +1,11 @@
1
+ import type { Summary } from "../../flows/index.js"
2
+ import * as t from "./text.js"
3
+ import { ARCHITECTURE, REQUIREMENTS, REVIEW } from "./steps.js"
4
+
5
+ export { defaults, envDefaults } from "./vars.js"
6
+ export { skills } from "./skills.js"
7
+ export { access } from "./access.js"
8
+
9
+ export const summary: Summary = t.summaryPrompt
10
+
11
+ export const steering = { [REQUIREMENTS]: "qa", [ARCHITECTURE]: "qa", [REVIEW]: "review" }
@@ -0,0 +1,7 @@
1
+ import { Given } from "quickpickle"
2
+ import { CONFIG_SCAFFOLD } from "../../install/index.js"
3
+ import type { TestkitWorld } from "../../testkit/index.js"
4
+
5
+ Given("the shipped workflows, as gtd install scaffolds them", (world: TestkitWorld) => {
6
+ world.configure(CONFIG_SCAFFOLD)
7
+ })