plumbbob 0.9.5 → 0.10.1

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 (144) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +89 -87
  3. package/dist/cli-core.js +18 -8
  4. package/dist/cli.js +1 -1
  5. package/dist/lib/agents.js +26 -26
  6. package/dist/lib/buildlog.js +21 -11
  7. package/dist/lib/buildlogsync.js +8 -8
  8. package/dist/lib/check.js +14 -14
  9. package/dist/lib/commitbody.js +72 -0
  10. package/dist/lib/commitmsg.js +7 -7
  11. package/dist/lib/git.js +44 -15
  12. package/dist/lib/intent.js +20 -19
  13. package/dist/lib/latch.js +16 -16
  14. package/dist/lib/orient.js +36 -17
  15. package/dist/lib/reentry.js +3 -3
  16. package/dist/lib/settings.js +16 -16
  17. package/dist/lib/sidecar.js +70 -54
  18. package/dist/lib/templates.js +1 -1
  19. package/dist/verbs/abandon.js +122 -0
  20. package/dist/verbs/agent.js +25 -25
  21. package/dist/verbs/build.js +9 -9
  22. package/dist/verbs/check.js +6 -6
  23. package/dist/verbs/checkpoint.js +48 -47
  24. package/dist/verbs/doctor.js +35 -29
  25. package/dist/verbs/finish.js +27 -40
  26. package/dist/verbs/handoff.js +9 -8
  27. package/dist/verbs/init.js +9 -9
  28. package/dist/verbs/park.js +3 -3
  29. package/dist/verbs/recover.js +12 -12
  30. package/dist/verbs/revert.js +12 -12
  31. package/dist/verbs/spike.js +21 -21
  32. package/dist/verbs/start.js +49 -19
  33. package/dist/verbs/status.js +8 -8
  34. package/dist/verbs/turn.js +16 -16
  35. package/dist/verbs/use.js +4 -4
  36. package/node_modules/checkride/.claude-plugin/plugin.json +1 -1
  37. package/node_modules/checkride/CHANGELOG.md +401 -0
  38. package/node_modules/checkride/README.md +27 -9
  39. package/node_modules/checkride/dist/adapters.d.ts +12 -4
  40. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  41. package/node_modules/checkride/dist/adapters.js +21 -1
  42. package/node_modules/checkride/dist/adapters.js.map +1 -1
  43. package/node_modules/checkride/dist/agent-setup/cursor.d.ts +9 -1
  44. package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -1
  45. package/node_modules/checkride/dist/agent-setup/cursor.js +59 -1
  46. package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -1
  47. package/node_modules/checkride/dist/agent-setup/harness.d.ts +1 -1
  48. package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -1
  49. package/node_modules/checkride/dist/agent-setup/harness.js +10 -1
  50. package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -1
  51. package/node_modules/checkride/dist/agent-setup/scripts.d.ts +23 -0
  52. package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -1
  53. package/node_modules/checkride/dist/agent-setup/scripts.js +387 -82
  54. package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -1
  55. package/node_modules/checkride/dist/baseline/fallow.js +2 -2
  56. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +14 -5
  57. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  58. package/node_modules/checkride/dist/baseline/fingerprint.js +58 -5
  59. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  60. package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
  61. package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
  62. package/node_modules/checkride/dist/baseline/history.js +232 -0
  63. package/node_modules/checkride/dist/baseline/history.js.map +1 -0
  64. package/node_modules/checkride/dist/baseline/index.d.ts +4 -2
  65. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  66. package/node_modules/checkride/dist/baseline/index.js +2 -1
  67. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  68. package/node_modules/checkride/dist/baseline/store.d.ts +30 -7
  69. package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
  70. package/node_modules/checkride/dist/baseline/store.js +50 -16
  71. package/node_modules/checkride/dist/baseline/store.js.map +1 -1
  72. package/node_modules/checkride/dist/cli.d.ts +5 -1
  73. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  74. package/node_modules/checkride/dist/cli.js +98 -25
  75. package/node_modules/checkride/dist/cli.js.map +1 -1
  76. package/node_modules/checkride/dist/config.d.ts +33 -4
  77. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  78. package/node_modules/checkride/dist/config.js +21 -6
  79. package/node_modules/checkride/dist/config.js.map +1 -1
  80. package/node_modules/checkride/dist/digest/digest.js +1 -1
  81. package/node_modules/checkride/dist/doctor.d.ts +2 -0
  82. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  83. package/node_modules/checkride/dist/doctor.js +39 -1
  84. package/node_modules/checkride/dist/doctor.js.map +1 -1
  85. package/node_modules/checkride/dist/gate.d.ts +49 -4
  86. package/node_modules/checkride/dist/gate.d.ts.map +1 -1
  87. package/node_modules/checkride/dist/gate.js +143 -16
  88. package/node_modules/checkride/dist/gate.js.map +1 -1
  89. package/node_modules/checkride/dist/init.d.ts +8 -1
  90. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  91. package/node_modules/checkride/dist/init.js +68 -7
  92. package/node_modules/checkride/dist/init.js.map +1 -1
  93. package/node_modules/checkride/dist/node-pin.d.ts +1 -1
  94. package/node_modules/checkride/dist/node-pin.js +1 -1
  95. package/node_modules/checkride/dist/orchestrator.d.ts +13 -0
  96. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  97. package/node_modules/checkride/dist/orchestrator.js +118 -13
  98. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  99. package/node_modules/checkride/dist/pm/index.d.ts +1 -0
  100. package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
  101. package/node_modules/checkride/dist/pm/index.js.map +1 -1
  102. package/node_modules/checkride/dist/pm/launch.d.ts +23 -2
  103. package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -1
  104. package/node_modules/checkride/dist/pm/launch.js +16 -0
  105. package/node_modules/checkride/dist/pm/launch.js.map +1 -1
  106. package/node_modules/checkride/dist/pm/translate.js +1 -1
  107. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  108. package/node_modules/checkride/dist/qa/mutation.d.ts +4 -4
  109. package/node_modules/checkride/dist/qa/mutation.js +4 -4
  110. package/node_modules/checkride/dist/recover.d.ts +48 -0
  111. package/node_modules/checkride/dist/recover.d.ts.map +1 -0
  112. package/node_modules/checkride/dist/recover.js +220 -0
  113. package/node_modules/checkride/dist/recover.js.map +1 -0
  114. package/node_modules/checkride/dist/smoke.d.ts +1 -1
  115. package/node_modules/checkride/dist/smoke.js +1 -1
  116. package/node_modules/checkride/dist/snippets.d.ts +1 -1
  117. package/node_modules/checkride/package.json +3 -2
  118. package/node_modules/checkride/schema/checkride.config.schema.json +9 -1
  119. package/node_modules/checkride/skills/check/SKILL.md +9 -1
  120. package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
  121. package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
  122. package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
  123. package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
  124. package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
  125. package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
  126. package/node_modules/checkride/templates/shared/vale.ini +34 -0
  127. package/package.json +13 -11
  128. package/skills/abandon/SKILL.md +20 -0
  129. package/skills/build/SKILL.md +69 -64
  130. package/skills/doctor/SKILL.md +11 -11
  131. package/skills/finish/SKILL.md +20 -19
  132. package/skills/harvest/SKILL.md +13 -12
  133. package/skills/park/SKILL.md +10 -10
  134. package/skills/plan/SKILL.md +63 -54
  135. package/skills/recover/SKILL.md +7 -7
  136. package/skills/refine/SKILL.md +18 -16
  137. package/skills/revert/SKILL.md +6 -6
  138. package/skills/spike/SKILL.md +9 -9
  139. package/skills/status/SKILL.md +5 -5
  140. package/skills/step/SKILL.md +17 -12
  141. package/skills/verify/SKILL.md +45 -40
  142. package/templates/build-log.md +7 -7
  143. package/templates/intent.md +49 -39
  144. package/templates/spike-report.md +10 -10
@@ -15,8 +15,8 @@
15
15
  * still takes all three. The gate is the one both need, because it alone has a
16
16
  * branch neither config can carry (see {@link gateTail}).
17
17
  */
18
- import { DIRTY_MARKER } from '../gate.js';
19
- import { execCommand } from '../pm/index.js';
18
+ import { DIRTY_MARKER, GATE_RETRY_ENV } from '../gate.js';
19
+ import { execCommand, runScript } from '../pm/index.js';
20
20
  /**
21
21
  * The repo root, however the calling harness spells it. Cursor sets
22
22
  * `CURSOR_PROJECT_DIR` (and `CLAUDE_PROJECT_DIR` as an alias); Claude Code sets
@@ -32,100 +32,283 @@ function checkrideCommand(pm, args) {
32
32
  return execCommand(pm, ['checkride', ...args], { quiet: true });
33
33
  }
34
34
  /**
35
- * What a harness is told when `checkride gate` itself could not run.
35
+ * Every message this script can emit is passed to `printf` as an *argument*,
36
+ * never inlined into a double-quoted format string, so its backticks survive:
37
+ * `sh` runs a backtick span inside double quotes as a command, but it does not
38
+ * re-scan the result of a variable expansion. An early draft used `echo "…"`
39
+ * with the text inline and the shell silently ate two quoted command names.
36
40
  *
37
- * Emitted through `printf '%s\n' '…'` — single-quoted, never double — because
38
- * the text carries backticks and `sh` runs a backtick span inside double quotes
39
- * as a command. The first draft of this message used `echo "…"` and the shell
40
- * silently ate the two quoted command names, substituting their output. Keep it
41
- * free of single quotes for the same reason.
41
+ * Two characters are therefore banned from every message here, and a test
42
+ * enforces both. A single quote would end the `'…'` the call site wraps it in. A
43
+ * double quote (or a backslash) would break {@link standDown}, which builds its
44
+ * JSON body with `printf '{"systemMessage":"%s"}'` rather than carrying a
45
+ * second, pre-encoded copy of the same sentence out of step with the first.
42
46
  */
43
- const UNRUNNABLE = 'checkride: the gate could not run — `checkride gate` is not resolvable in this repo. ' +
44
- 'Install checkride (or re-run `checkride agent-setup`) before finishing.';
47
+ const NOTHING_RAN = 'Nothing ran: no check executed and no artifact was written, so `.check/` holds nothing from this turn.';
48
+ /** `<pm> install`, which is how all four package managers spell it. */
49
+ const installFix = (pm) => `Run \`${pm} install\` (checkride is a devDependency of this repo), then run \`${runScript(pm, 'check')}\`.`;
45
50
  /**
46
- * The one line that hands `harness` the {@link UNRUNNABLE} verdict, and the exit
47
- * status that goes with it.
51
+ * The repo has no checkride to run, because its dependencies were never
52
+ * installed — someone pulled a branch that added checkride, or cloned fresh, and
53
+ * started a session before installing.
48
54
  *
49
- * The statuses differ because the harnesses do: Claude Code blocks on 2, while
50
- * Cursor reads any non-zero stop hook as a *broken* hook and ends the turn —
51
- * there, blocking has to ride in the body with an exit of 0. Kept as a pair so
52
- * no branch can pick one without the other.
55
+ * This stands the gate down instead of blocking, and it is the one case where
56
+ * that is clearly right. Blocking exists to make an agent fix what it broke; an
57
+ * absent toolchain is not something the turn broke, and the remedy is an install
58
+ * rather than an edit. Blocking on it re-asks the same agent every turn, forever,
59
+ * for a change that cannot clear it — the loop that ends with a contributor
60
+ * removing the gate. See `../gate.ts` {@link reportStandDown} for the same
61
+ * judgement one layer down.
53
62
  */
54
- function unrunnable(harness) {
55
- return harness === 'cursor'
56
- ? { emit: `printf '%s\\n' '${JSON.stringify({ followup_message: UNRUNNABLE })}'`, exit: 0 }
57
- : { emit: `printf '%s\\n' '${UNRUNNABLE}' >&2`, exit: 2 };
63
+ const notInstalled = (pm) => [
64
+ 'checkride: the gate could not run — checkride is not installed in this repo.',
65
+ NOTHING_RAN,
66
+ installFix(pm),
67
+ 'Not blocking: the fix is an install rather than an edit, so blocking would only repeat this ' +
68
+ 'message. Nothing was verified this turn.',
69
+ ].join(' ');
70
+ /**
71
+ * checkride *is* installed and still did not answer with one of its own two gate
72
+ * codes — a broken launcher, a corrupt install, a crash. This one blocks: it is
73
+ * rare, it is not self-explanatory, and unlike a missing install there is no
74
+ * known remedy to name, so the honest move is to stop and be looked at. The
75
+ * retry guard in {@link gatePreamble} still bounds it.
76
+ */
77
+ const notAnswering = (pm, harness) => [
78
+ 'checkride: the gate could not run — checkride is installed here but `checkride gate` did not answer.',
79
+ NOTHING_RAN,
80
+ `Run \`${execCommand(pm, ['checkride', 'gate', '--harness', harness])}\` in a terminal to see the failure directly.`,
81
+ ].join(' ');
82
+ /** The harness pointed the hook at a directory that is not there. */
83
+ const noRepo = 'checkride: the gate could not run — the project directory the harness named does not exist or ' +
84
+ 'cannot be entered, so there is no repo to check.';
85
+ /**
86
+ * The shell functions every branch below answers through: one for a verdict that
87
+ * blocks, one for a verdict that gives up on blocking.
88
+ *
89
+ * `block` consults `retry` first, which is what bounds *every* could-not-run
90
+ * cause rather than only the ones enumerated here. Claude Code sends
91
+ * `stop_hook_active` on the Stop payload and Cursor sends `loop_count`; either
92
+ * one says the harness has already auto-submitted a turn on this hook, and a
93
+ * second identical block has been demonstrated not to help. A red pipeline never
94
+ * reaches this — that loop is the gate working, and is deliberately unbounded.
95
+ *
96
+ * Under Cursor `stand_down` consults it too, for a different reason. Blocking
97
+ * there is a `followup_message`, and so is the only way to reach a *user*, so
98
+ * the same guard has to serve both: it stops `block` repeating itself, and it
99
+ * rations `stand_down` to the single message that names the fix. `retry` is
100
+ * conservative in exactly the direction that keeps that safe — Cursor's
101
+ * `loop_count` counts every follow-up in the conversation rather than the
102
+ * consecutive ones, so an over-count costs a quiet stand-down, never a loop.
103
+ */
104
+ function gatePreamble(harness) {
105
+ const cursor = harness === 'cursor';
106
+ return [
107
+ '# The Stop payload arrives as JSON on stdin and says whether the harness has',
108
+ '# already been round this loop once. Read it before anything else can consume',
109
+ '# it, and only from a pipe: `cat` on a terminal would hang a hand-run forever.',
110
+ "payload=''",
111
+ '[ -t 0 ] || payload=$(cat)',
112
+ '',
113
+ '# True once the harness has already auto-submitted a turn on this hook.',
114
+ 'retry() {',
115
+ ' printf %s "$payload" |',
116
+ ` grep -Eq '"stop_hook_active"[[:space:]]*:[[:space:]]*true|"loop_count"[[:space:]]*:[[:space:]]*[1-9]'`,
117
+ '}',
118
+ '',
119
+ '# Give up on blocking, and say so. Nothing here is silent: a turn that ended',
120
+ '# unverified must never look like one that passed.',
121
+ 'stand_down() {',
122
+ ...(cursor
123
+ ? [
124
+ ' # Cursor names no channel that shows hook stderr to a user — only a Hooks',
125
+ ' # output panel under Customize — so stderr alone would hide this from the',
126
+ ' # one party who can fix an environment. `followup_message` does reach the',
127
+ ' # chat, and it also *submits a new turn*, so it goes out exactly once:',
128
+ ' # `loop_count` says a follow-up already went, and a second is the loop.',
129
+ ' retry || printf \'{"followup_message":"%s"}\\n\' "$1"',
130
+ ' printf \'%s\\n\' "$1" >&2',
131
+ ]
132
+ : [
133
+ ' # `systemMessage` reaches the user — the one party who can fix an',
134
+ ' # environment. With no `decision` in the body, Claude Code does not block.',
135
+ ' printf \'{"systemMessage":"%s"}\\n\' "$1"',
136
+ ' printf \'%s\\n\' "$1" >&2',
137
+ ]),
138
+ ' exit 0',
139
+ '}',
140
+ '',
141
+ '# Block the turn — unless this is the second consecutive attempt, in which',
142
+ '# case blocking is the thing already shown not to work.',
143
+ 'block() {',
144
+ ' retry && stand_down "$1 Standing down rather than blocking a second time on the same verdict."',
145
+ ...(cursor
146
+ ? [
147
+ ' # Cursor reads any non-zero stop hook as a *broken* hook and ends the turn,',
148
+ ' # so the block has to ride in the body with an exit of 0.',
149
+ ' printf \'{"followup_message":"%s"}\\n\' "$1"',
150
+ ' exit 0',
151
+ ]
152
+ : [' printf \'%s\\n\' "$1" >&2', ' exit 2']),
153
+ '}',
154
+ ...(cursor
155
+ ? [
156
+ '',
157
+ '# checkride also stands *itself* down, for a refusal it detects from inside',
158
+ '# (an `engines` pin the hook Node fails, a corepack it cannot verify) — and',
159
+ '# it never sees the stop payload, so hand it the one fact that bounds that',
160
+ '# message to a single turn the same way. It sends a followup only on a 0.',
161
+ `if retry; then ${GATE_RETRY_ENV}=1; else ${GATE_RETRY_ENV}=0; fi`,
162
+ `export ${GATE_RETRY_ENV}`,
163
+ ]
164
+ : []),
165
+ ];
58
166
  }
59
167
  /**
60
168
  * Translate the gate's exit status into `harness`'s protocol.
61
169
  *
62
- * The interesting branch is the last one. `checkride gate` exits 0 or 2 by
63
- * design; anything else means it never ran — an uninstalled checkride, a broken
64
- * launcher — and that must **block**, not pass. Claude Code reads a plain exit 1
65
- * as "hook failed, carry on", which would leave a repo whose gate had silently
66
- * stopped gating: precisely the vacuous green checkride exists to prevent. So
67
- * the gate alone fails closed. (`protect` fails open, deliberately: the cost of
68
- * a broken protect hook is a repo where nothing can be written at all.)
170
+ * `checkride gate` exits 0 or 2 by design, and only on those two is its stdout a
171
+ * hook body worth forwarding. Anything else means checkride never ran, and
172
+ * whatever is on stdout then is the *launcher's* error — pnpm writes
173
+ * `ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL` there, ahead of a stray `undefined` on
174
+ * pnpm 11. Forwarding that puts unparseable text on the one channel a harness
175
+ * reads as protocol, so the status is checked first and the body is forwarded
176
+ * only when checkride is the one that wrote it. That is why {@link invokeGate}
177
+ * captures under both harnesses: output already streamed past the script cannot
178
+ * be withheld by it.
69
179
  */
70
- function gateTail(harness) {
71
- const { emit, exit } = unrunnable(harness);
72
- if (harness === 'cursor') {
73
- return [
74
- '# On success checkride has already written its JSON to stdout, which is what',
75
- '# Cursor reads. Only the never-ran case is left to answer for.',
76
- '[ "$status" -eq 0 ] && exit 0',
77
- emit,
78
- `exit ${exit}`,
180
+ function gateTail(harness, pm) {
181
+ const answered = harness === 'cursor'
182
+ ? [
183
+ '# On 0 checkride has already written its JSON body, and on 2 the followup',
184
+ '# body. Either way it answered, so hand back what it wrote, unchanged.',
185
+ '# A green that had nothing to do writes nothing, and that is a body too.',
186
+ 'case "$status" in',
187
+ ' 0|2)',
188
+ ' [ -n "$body" ] && printf \'%s\\n\' "$body"',
189
+ ' exit 0',
190
+ ' ;;',
191
+ 'esac',
192
+ ]
193
+ : [
194
+ '# Claude Code parses a hook body only on exit 0, and only a body can carry a',
195
+ '# user-visible message alongside the block. So when checkride produced one,',
196
+ '# forward it and exit 0: the verdict rides in `decision`, not in the status.',
197
+ 'case "$status" in',
198
+ ' 0|2)',
199
+ ' if [ -n "$body" ]; then',
200
+ " printf '%s\\n' \"$body\"",
201
+ ' exit 0',
202
+ ' fi',
203
+ ' # No body — an older checkride that reports only through the exit code.',
204
+ ' exit "$status"',
205
+ ' ;;',
206
+ 'esac',
79
207
  ];
208
+ return [
209
+ ...answered,
210
+ '',
211
+ "# Neither of checkride's two gate codes: checkride itself never ran. Which of",
212
+ '# the two reasons it was decides whether blocking could accomplish anything.',
213
+ 'if [ -e node_modules/.bin/checkride ] || [ -e .pnp.cjs ] || [ -e .pnp.js ]; then',
214
+ ` block '${notAnswering(pm, harness)}'`,
215
+ 'fi',
216
+ `stand_down '${notInstalled(pm)}'`,
217
+ ];
218
+ }
219
+ /**
220
+ * Characters a `gate.preflight` path may not contain, because the generated
221
+ * script embeds it inside `'…'` and inside a message that later becomes a JSON
222
+ * string field.
223
+ *
224
+ * Rejected loudly rather than escaped or stripped. Every one of these in a
225
+ * repo-relative script path is a mistake, and a mangled path would fail later as
226
+ * "preflight not found", pointing at the wrong thing.
227
+ */
228
+ const PREFLIGHT_HOSTILE = /['"\\`\n]/;
229
+ /**
230
+ * The repo-owned preflight, run from the repo root before checkride is started.
231
+ *
232
+ * Its exit code is read in the gate's own vocabulary — 0 runs the gate, 2 blocks
233
+ * the turn, anything else stands it down — so there is one meaning per code
234
+ * across the whole system rather than a second convention to learn. Both
235
+ * non-zero branches use the script's own output as the message and never start
236
+ * checkride, which is what lets a preflight answer for a repo where checkride
237
+ * *cannot* start.
238
+ *
239
+ * A configured path that is not there **blocks**, and deliberately: treating it
240
+ * as a stand-down would mean a typo in one config key silently disarms the gate
241
+ * forever. A missing file is repo-fixable, so blocking is the consistent answer.
242
+ */
243
+ function preflight(path) {
244
+ if (PREFLIGHT_HOSTILE.test(path)) {
245
+ throw new Error(`checkride.config.json: gate.preflight '${path}' contains a quote, backslash, backtick or newline. ` +
246
+ 'Use a plain repo-relative path.');
80
247
  }
248
+ // `sh` resolves a command with no `/` against PATH, which is never what a
249
+ // repo-relative config value means.
250
+ const target = path.includes('/') ? path : `./${path}`;
81
251
  return [
82
- '# Claude Code parses a hook body only on exit 0, and only a body can carry a',
83
- '# user-visible message alongside the block. So when checkride produced one,',
84
- '# forward it and exit 0: the verdict rides in `decision`, not in the status.',
85
- 'if [ -n "$body" ]; then',
86
- " printf '%s\\n' \"$body\"",
87
- ' case "$status" in',
88
- ' 0|2) exit 0 ;;',
89
- ' esac',
252
+ '# The repo-owned preflight, from `gate.preflight` in checkride.config.json.',
253
+ '# checkride owns this script and overwrites it; the preflight is where a repo',
254
+ '# says something before the gate and keeps it across every refresh.',
255
+ `if [ ! -e '${target}' ]; then`,
256
+ ` block 'checkride: the gate could not run — checkride.config.json names a gate.preflight (${target}) that is not there. Add the script, or drop the key.'`,
90
257
  'fi',
91
258
  '',
92
- '# No body — an older checkride that reports only through the exit code.',
93
- '[ "$status" -eq 0 ] && exit 0',
94
- '[ "$status" -eq 2 ] && exit 2',
259
+ '# A preflight prints for humans; this has to survive a JSON string field. The',
260
+ '# two characters that would break one are dropped and newlines folded.',
261
+ 'say() {',
262
+ ` printf %s "$1" | tr -d '"\\\\' | tr '\\n' ' '`,
263
+ '}',
264
+ '',
265
+ '# Honour the shebang when the file is executable, and fall back to `sh` when it',
266
+ '# is not — a checked-in script routinely arrives without its exec bit.',
267
+ `if [ -x '${target}' ]; then`,
268
+ ` said=$('${target}' 2>&1)`,
269
+ 'else',
270
+ ` said=$(sh '${target}' 2>&1)`,
271
+ 'fi',
272
+ 'code=$?',
273
+ `[ "$code" -eq 0 ] || [ -n "$said" ] ||`,
274
+ ` said='checkride: the gate could not run — the preflight ${target} exited non-zero and printed nothing.'`,
95
275
  '',
96
- "# Neither of checkride's two gate codes: it never ran. Block anyway.",
97
- emit,
98
- `exit ${exit}`,
276
+ '# 0 runs the gate; 2 blocks; anything else stands down. checkride is never',
277
+ '# started on a non-zero branch.',
278
+ 'case "$code" in',
279
+ ' 0) ;;',
280
+ ' 2) block "$(say "$said")" ;;',
281
+ ' *) stand_down "$(say "$said")" ;;',
282
+ 'esac',
99
283
  ];
100
284
  }
101
285
  /**
102
- * Enter the repo, or report the same "could not run" verdict as any other way
103
- * the gate can fail to start.
286
+ * Enter the repo, or report a verdict rather than a bare status.
104
287
  *
105
288
  * Spelled as a block rather than `cd … || exit 2` for two reasons: a bare status
106
289
  * tells the agent nothing, and under Cursor a non-zero stop hook is a *broken*
107
290
  * hook, so the one branch that used to exit 2 there was the one branch that
108
291
  * silently let a turn end.
109
292
  */
110
- function enterRepo(harness) {
111
- const { emit, exit } = unrunnable(harness);
112
- return [`if ! cd ${PROJECT_DIR}; then`, ` ${emit}`, ` exit ${exit}`, 'fi'];
293
+ function enterRepo() {
294
+ return [`if ! cd ${PROJECT_DIR}; then`, ` block '${noRepo}'`, 'fi'];
113
295
  }
114
296
  /**
115
- * How the gate's stdout is taken.
297
+ * How the gate's stdout is taken: captured, under both harnesses.
116
298
  *
117
- * Cursor's script lets it stream straight through: Cursor reads the hook's own
118
- * stdout, and there is nothing for the script to decide. Claude Code's captures
119
- * it, because the script has to *choose an exit code based on whether a body
120
- * came back* — see {@link gateTail}. Capturing costs nothing that is visible:
121
- * the pipeline's human-readable progress is on stderr and streams live either
122
- * way; stdout carries only the one-line JSON verdict, written at the very end.
299
+ * Letting it stream through is tempting under Cursor, which reads the hook's own
300
+ * stdout and would need no forwarding — but a *failed launch* writes there too,
301
+ * and a script that never held that output cannot keep it off a channel with
302
+ * defined semantics. Capturing is what gives {@link gateTail} the choice, and
303
+ * `block`/`stand_down` sole ownership of stdout on the paths where checkride
304
+ * never answered.
305
+ *
306
+ * It costs nothing visible: the pipeline's human-readable progress is on stderr
307
+ * and streams live either way, and stdout carries only the one-line JSON
308
+ * verdict, written at the very end.
123
309
  */
124
- function invokeGate(pm, harness, args) {
125
- const command = checkrideCommand(pm, args);
126
- return harness === 'cursor'
127
- ? [command, 'status=$?']
128
- : [`body=$(${command})`, 'status=$?'];
310
+ function invokeGate(pm, args) {
311
+ return [`body=$(${checkrideCommand(pm, args)})`, 'status=$?'];
129
312
  }
130
313
  /**
131
314
  * The gate script: `cd` to the repo, hand off to `checkride gate`, and translate
@@ -135,6 +318,12 @@ function invokeGate(pm, harness, args) {
135
318
  * makes the gate conditional on the edit marker, so pure-conversation turns
136
319
  * don't pay for a full pipeline run. Without the marker hook the guard would
137
320
  * disarm the gate entirely, so a `--hook gate` selection writes it unguarded.
321
+ *
322
+ * `preflight` is the repo's own script, from `gate.preflight` in
323
+ * checkride.config.json, baked in here rather than looked up at run time — see
324
+ * {@link preflight} and `../gate.ts` `gatePreflight`. It runs *before* the edit
325
+ * marker is consulted, because a repo that cannot run the gate at all wants to
326
+ * say so on every turn, not only on the ones that edited a file.
138
327
  */
139
328
  export function gateScript(pm, opts) {
140
329
  const args = ['gate', '--harness', opts.harness, ...((opts.dirtyGuard ?? true) ? ['--if-dirty'] : [])];
@@ -143,16 +332,20 @@ export function gateScript(pm, opts) {
143
332
  `# checkride-gate.sh — the ${opts.harness} stop-hook gate.`,
144
333
  '#',
145
334
  '# checkride owns this file: `checkride agent-setup` (and `checkride init`)',
146
- '# overwrite it on every run. Customize via a sibling script or the',
147
- '# environment, not by editing here — edits are lost on the next refresh.',
335
+ '# overwrite it on every run. Customize through checkride.config.json (a `gate`',
336
+ '# key narrows what runs) or the environment (CHECKRIDE_NODE_BIN), not by',
337
+ '# editing here — edits are lost on the next refresh.',
148
338
  '#',
149
339
  "# Runs the repo's `check` script as a hard gate, and reports the verdict in",
150
340
  `# ${opts.harness}'s hook protocol. See \`checkride gate --help\`.`,
151
341
  '',
152
- ...enterRepo(opts.harness),
342
+ ...gatePreamble(opts.harness),
153
343
  '',
154
- ...invokeGate(pm, opts.harness, args),
155
- ...gateTail(opts.harness),
344
+ ...enterRepo(),
345
+ '',
346
+ ...(opts.preflight === undefined ? [] : [...preflight(opts.preflight), '']),
347
+ ...invokeGate(pm, args),
348
+ ...gateTail(opts.harness, pm),
156
349
  '',
157
350
  ].join('\n');
158
351
  }
@@ -202,6 +395,19 @@ export function dirtyScript() {
202
395
  * is older and accepted.)
203
396
  */
204
397
  const PATH_KEYS = ['file_path', 'notebook_path', 'path', 'target_file', 'filePath', 'relative_workspace_path'];
398
+ /**
399
+ * Shell verbs that can destroy *any* path they are handed, so every positional
400
+ * argument is a write target. `mv` belongs here rather than with the
401
+ * destination-only verbs because moving an accounting file away removes it just
402
+ * as effectively as overwriting it.
403
+ */
404
+ const ALL_ARGS_VERBS = ['rm', 'unlink', 'shred', 'mv', 'truncate', 'tee', 'rmdir'];
405
+ /**
406
+ * Shell verbs whose sources are *read* and whose last positional argument is the
407
+ * only thing written. `cp .check/summary.json /tmp/x` has to stay allowed — it
408
+ * is a backup, not an edit — and treating its source as a target would deny it.
409
+ */
410
+ const DEST_ONLY_VERBS = ['cp', 'install', 'ln', 'rsync'];
205
411
  /** The deny message, shared by both harnesses. */
206
412
  const DENY_MESSAGE = "'checkride: ' + rel + ' is checkride-owned accounting. Never edit the baseline or .check ' +\n" +
207
413
  " 'artifacts to make a check pass — fix the finding instead (the ratchet prunes the baseline on its own).'";
@@ -229,11 +435,27 @@ function denyTail() {
229
435
  * matched — reads are never denied, because triage depends on reading `.check/`
230
436
  * artifacts.
231
437
  *
438
+ * Two Cursor events land here. `preToolUse` carries a file-tool call, whose
439
+ * target is read from {@link PATH_KEYS}. `beforeShellExecution` carries a
440
+ * command line, which is scanned for a write *to* an accounting path — the gap
441
+ * a tool-name matcher cannot close, since `echo … > checkride.baseline.json` is
442
+ * a shell call and not a `Write`.
443
+ *
444
+ * The command scan is deliberately timid, because a guard that fires on a wrong
445
+ * parse is worse than a known hole. It denies only on demonstrated write intent
446
+ * — a `>`/`>>` redirect target, or a positional argument of a known mutating
447
+ * verb — and anything it cannot read that way is allowed. `cat`, `grep`, `jq`
448
+ * and `cp … /tmp/x` over `.check/` all pass, which they must: triage reads those
449
+ * artifacts. The hook's own `matcher` narrows the input further, so a command
450
+ * that never names an accounting path is not parsed at all.
451
+ *
232
452
  * **Cursor only.** Claude Code enforces the same paths through
233
453
  * `permissions.deny`, which is checked below the hook layer and costs nothing
234
454
  * per tool call, so it needs no script. Cursor has no documented equivalent —
235
455
  * its config is hooks — so the script survives for the harness that still
236
456
  * requires one. If Cursor grows a file-path deny list, this file goes away.
457
+ * (Claude Code's deny rules have the same shell-redirect hole and no script to
458
+ * close it with; that half of the gap is still open. See docs/cursor.md.)
237
459
  */
238
460
  export function protectScript() {
239
461
  return [
@@ -242,14 +464,16 @@ export function protectScript() {
242
464
  '// checkride owns this file: `checkride agent-setup` (and `checkride init`)',
243
465
  '// overwrite it on every run.',
244
466
  '//',
245
- '// Pre-tool hook for Cursor. Reads the tool call as JSON on stdin and',
246
- '// denies the write when it targets the baseline or .check/. Reads are never',
247
- '// matched — triage depends on reading .check artifacts.',
467
+ '// Guard hook for Cursor. Reads the hook payload as JSON on stdin and denies',
468
+ '// the call when it *writes* to the baseline or .check/. Reads are never',
469
+ '// denied — triage depends on reading .check artifacts.',
248
470
  "'use strict';",
249
471
  "const { realpathSync } = require('node:fs');",
250
472
  "const { basename, dirname, isAbsolute, relative, resolve, sep } = require('node:path');",
251
473
  '',
252
474
  `const PATH_KEYS = ${JSON.stringify(PATH_KEYS)};`,
475
+ `const ALL_ARGS_VERBS = ${JSON.stringify(ALL_ARGS_VERBS)};`,
476
+ `const DEST_ONLY_VERBS = ${JSON.stringify(DEST_ONLY_VERBS)};`,
253
477
  '',
254
478
  '// Resolve symlinks in `p`, so a symlinked prefix cannot make an in-repo path',
255
479
  '// look external. On macOS the repo root routinely arrives in two spellings —',
@@ -274,6 +498,84 @@ export function protectScript() {
274
498
  ' }',
275
499
  '}',
276
500
  '',
501
+ '// Split a command line into tokens, keeping quoted spans whole and emitting',
502
+ '// each unquoted operator as a token of its own — so `echo x>f` and `echo x > f`',
503
+ '// tokenize alike, and a `>` inside quotes is never mistaken for a redirect.',
504
+ 'function tokenize(command) {',
505
+ ' const tokens = [];',
506
+ " let cur = '';",
507
+ ' let quote = null;',
508
+ " const flush = () => { if (cur !== '') { tokens.push(cur); cur = ''; } };",
509
+ ' for (let i = 0; i < command.length; i += 1) {',
510
+ ' const c = command[i];',
511
+ ' if (quote !== null) {',
512
+ ' if (c === quote) quote = null;',
513
+ ' else cur += c;',
514
+ ' } else if (/["\']/.test(c)) {',
515
+ ' quote = c;',
516
+ " } else if (c === '\\\\') {",
517
+ " i += 1; cur += command[i] || '';",
518
+ ' } else if (/\\s/.test(c)) {',
519
+ ' flush();',
520
+ ' } else if (/[<>|&;]/.test(c)) {',
521
+ ' flush();',
522
+ ' let op = c;',
523
+ ' while (command[i + 1] === c) { op += c; i += 1; }',
524
+ ' tokens.push(op);',
525
+ ' } else {',
526
+ ' cur += c;',
527
+ ' }',
528
+ ' }',
529
+ ' flush();',
530
+ ' return tokens;',
531
+ '}',
532
+ '',
533
+ 'const isOperator = (t) => /^[<>|&;]+$/.test(t);',
534
+ '',
535
+ '// Break a token stream at the operators that start a new command, so the verb',
536
+ '// of `cat x | tee .check/f` is read as `tee` and not as `cat`.',
537
+ 'function segments(tokens) {',
538
+ ' const out = [[]];',
539
+ ' for (const t of tokens) {',
540
+ ' if (/^[|&;]+$/.test(t)) out.push([]);',
541
+ ' else out[out.length - 1].push(t);',
542
+ ' }',
543
+ ' return out;',
544
+ '}',
545
+ '',
546
+ '// Every path a command line demonstrably writes to. Anything this cannot read',
547
+ '// as a write yields nothing, and the caller then allows the command.',
548
+ 'function commandTargets(command) {',
549
+ ' const targets = [];',
550
+ ' for (const segment of segments(tokenize(command))) {',
551
+ ' for (let i = 0; i < segment.length; i += 1) {',
552
+ " if (segment[i] !== '>' && segment[i] !== '>>') continue;",
553
+ ' const next = segment[i + 1];',
554
+ ' if (next !== undefined && !isOperator(next)) targets.push(next);',
555
+ ' }',
556
+ ' const words = segment.filter((t) => !isOperator(t));',
557
+ ' // Step over any leading `VAR=value` assignments to reach the verb itself.',
558
+ ' let i = 0;',
559
+ ' while (i < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[i])) i += 1;',
560
+ " const verb = (words[i] || '').split('/').pop();",
561
+ ' const rest = words.slice(i + 1);',
562
+ " const args = rest.filter((a) => !a.startsWith('-'));",
563
+ ' if (ALL_ARGS_VERBS.indexOf(verb) >= 0) targets.push(...args);',
564
+ ' else if (DEST_ONLY_VERBS.indexOf(verb) >= 0 && args.length > 0) targets.push(args[args.length - 1]);',
565
+ " else if ((verb === 'sed' || verb === 'perl') && rest.some((a) => /^-[a-zA-Z]*i/.test(a))) targets.push(...args);",
566
+ " else if (verb === 'dd') targets.push(...rest.filter((a) => a.startsWith('of=')).map((a) => a.slice(3)));",
567
+ ' }',
568
+ ' return targets;',
569
+ '}',
570
+ '',
571
+ "// `target`'s repo-relative path when it is checkride accounting, else undefined.",
572
+ 'function accounting(target, root, base) {',
573
+ ' const abs = canon(isAbsolute(target) ? target : resolve(base, target));',
574
+ " const rel = relative(root, abs).split(sep).join('/');",
575
+ " if (rel === 'checkride.baseline.json' || rel === '.check' || rel.startsWith('.check/')) return rel;",
576
+ ' return undefined;',
577
+ '}',
578
+ '',
277
579
  "let raw = '';",
278
580
  "process.stdin.on('data', (chunk) => { raw += chunk; });",
279
581
  "process.stdin.on('end', () => {",
@@ -283,14 +585,17 @@ export function protectScript() {
283
585
  ' } catch {',
284
586
  ' process.exit(0); // fail open: a broken hook must not brick every edit',
285
587
  ' }',
286
- ' const tool = (input && input.tool_input) || {};',
287
- " const target = PATH_KEYS.map((k) => tool[k]).find((v) => typeof v === 'string' && v.length > 0);",
288
- ' if (target === undefined) process.exit(0);',
588
+ " if (input === null || typeof input !== 'object') process.exit(0);",
289
589
  ' const root = canon(process.env.CURSOR_PROJECT_DIR || process.env.CLAUDE_PROJECT_DIR || process.cwd());',
290
- ' const abs = canon(isAbsolute(target) ? target : resolve(root, target));',
291
- " const rel = relative(root, abs).split(sep).join('/');",
292
- " const denied = rel === 'checkride.baseline.json' || rel === '.check' || rel.startsWith('.check/');",
293
- ' if (!denied) process.exit(0);',
590
+ " // A shell command resolves against the shell's own cwd, which Cursor sends;",
591
+ ' // a file-tool path has always been resolved against the repo root.',
592
+ " const base = typeof input.cwd === 'string' && input.cwd.length > 0 ? canon(input.cwd) : root;",
593
+ ' const tool = input.tool_input || {};',
594
+ " const targets = input.hook_event_name === 'beforeShellExecution'",
595
+ " ? commandTargets(typeof input.command === 'string' ? input.command : '')",
596
+ " : PATH_KEYS.map((k) => tool[k]).filter((v) => typeof v === 'string' && v.length > 0);",
597
+ ' const rel = targets.map((t) => accounting(t, root, base)).find((r) => r !== undefined);',
598
+ ' if (rel === undefined) process.exit(0);',
294
599
  ...denyTail(),
295
600
  '});',
296
601
  '',
@@ -1 +1 @@
1
- {"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/agent-setup/scripts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/agent-setup/scripts.ts"],"names":[],"mappings":"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"}
@@ -191,9 +191,9 @@ function collectDeadKeys(j) {
191
191
  * Extract dead-code findings and the authoritative issue count (`total_issues`).
192
192
  * Findings come from the top-level detail arrays (`unused_exports`,
193
193
  * `unused_dev_dependencies`, `circular_dependencies`, …) rather than the summary
194
- * keys: the summary aggregates a few categories under one name (e.g. dev and
194
+ * keys: the summary aggregates a few categories under one name (for example dev and
195
195
  * optional deps both count as `unused_dependencies`) whose items live in
196
- * separately-named arrays, so keying off the summary would miss them. Iterating
196
+ * separately named arrays, so keying off the summary would miss them. Iterating
197
197
  * every array except the {@link UNCOUNTED_ARRAYS} covers whatever categories
198
198
  * fallow reports — including ones added in a future minor — without a
199
199
  * hand-maintained list of the ones that do count. A category whose items carry