plumbbob 0.9.3 → 0.10.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 (320) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +92 -89
  3. package/dist/cli-core.js +25 -8
  4. package/dist/cli.js +1 -1
  5. package/dist/lib/agents.js +26 -26
  6. package/dist/lib/buildlog.js +11 -11
  7. package/dist/lib/buildlogsync.js +8 -8
  8. package/dist/lib/check.js +55 -22
  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 +21 -20
  13. package/dist/lib/latch.js +19 -19
  14. package/dist/lib/orient.js +36 -17
  15. package/dist/lib/reentry.js +70 -0
  16. package/dist/lib/settings.js +16 -16
  17. package/dist/lib/sidecar.js +54 -54
  18. package/dist/lib/templates.js +1 -1
  19. package/dist/verbs/agent.js +27 -27
  20. package/dist/verbs/build.js +9 -9
  21. package/dist/verbs/check.js +6 -6
  22. package/dist/verbs/checkpoint.js +48 -47
  23. package/dist/verbs/doctor.js +31 -30
  24. package/dist/verbs/finish.js +28 -41
  25. package/dist/verbs/handoff.js +9 -8
  26. package/dist/verbs/init.js +9 -9
  27. package/dist/verbs/park.js +3 -3
  28. package/dist/verbs/recover.js +297 -0
  29. package/dist/verbs/revert.js +13 -13
  30. package/dist/verbs/spike.js +25 -19
  31. package/dist/verbs/start.js +49 -19
  32. package/dist/verbs/status.js +8 -8
  33. package/dist/verbs/turn.js +16 -16
  34. package/dist/verbs/use.js +4 -4
  35. package/hooks/post-edit.sh +4 -4
  36. package/hooks/pre-bash-commit.sh +3 -3
  37. package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
  38. package/node_modules/checkride/CHANGELOG.md +1404 -0
  39. package/node_modules/checkride/LICENSE +201 -21
  40. package/node_modules/checkride/README.md +197 -38
  41. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
  42. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
  43. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
  44. package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
  45. package/node_modules/checkride/dist/adapters.d.ts +30 -10
  46. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  47. package/node_modules/checkride/dist/adapters.js +62 -23
  48. package/node_modules/checkride/dist/adapters.js.map +1 -1
  49. package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
  50. package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
  51. package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
  52. package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
  53. package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
  54. package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
  55. package/node_modules/checkride/dist/agent-setup/command.js +77 -0
  56. package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
  57. package/node_modules/checkride/dist/agent-setup/cursor.d.ts +69 -0
  58. package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
  59. package/node_modules/checkride/dist/agent-setup/cursor.js +247 -0
  60. package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
  61. package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
  62. package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
  63. package/node_modules/checkride/dist/agent-setup/files.js +80 -0
  64. package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
  65. package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
  66. package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
  67. package/node_modules/checkride/dist/agent-setup/harness.js +93 -0
  68. package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
  69. package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
  70. package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
  71. package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
  72. package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
  73. package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
  74. package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
  75. package/node_modules/checkride/dist/agent-setup/index.js +9 -4
  76. package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
  77. package/node_modules/checkride/dist/agent-setup/scripts.d.ts +81 -0
  78. package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
  79. package/node_modules/checkride/dist/agent-setup/scripts.js +604 -0
  80. package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
  81. package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
  82. package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
  83. package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
  84. package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
  85. package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
  86. package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
  87. package/node_modules/checkride/dist/artifacts/configured.js +45 -0
  88. package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
  89. package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
  90. package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
  91. package/node_modules/checkride/dist/artifacts/format.js +56 -0
  92. package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
  93. package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
  94. package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
  95. package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
  96. package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
  97. package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
  98. package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
  99. package/node_modules/checkride/dist/artifacts/index.js +19 -0
  100. package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
  101. package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
  102. package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
  103. package/node_modules/checkride/dist/artifacts/json.js +47 -0
  104. package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
  105. package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
  106. package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
  107. package/node_modules/checkride/dist/artifacts/raw.js +91 -0
  108. package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
  109. package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
  110. package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
  111. package/node_modules/checkride/dist/artifacts/summary.js +108 -0
  112. package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
  113. package/node_modules/checkride/dist/atomic.d.ts +16 -1
  114. package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
  115. package/node_modules/checkride/dist/atomic.js +25 -3
  116. package/node_modules/checkride/dist/atomic.js.map +1 -1
  117. package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
  118. package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
  119. package/node_modules/checkride/dist/baseline/fallow.js +163 -60
  120. package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
  121. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +20 -11
  122. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  123. package/node_modules/checkride/dist/baseline/fingerprint.js +73 -23
  124. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  125. package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
  126. package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
  127. package/node_modules/checkride/dist/baseline/history.js +232 -0
  128. package/node_modules/checkride/dist/baseline/history.js.map +1 -0
  129. package/node_modules/checkride/dist/baseline/index.d.ts +7 -5
  130. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  131. package/node_modules/checkride/dist/baseline/index.js +5 -4
  132. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  133. package/node_modules/checkride/dist/baseline/store.d.ts +41 -10
  134. package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
  135. package/node_modules/checkride/dist/baseline/store.js +65 -24
  136. package/node_modules/checkride/dist/baseline/store.js.map +1 -1
  137. package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
  138. package/node_modules/checkride/dist/baseline-command.js +7 -7
  139. package/node_modules/checkride/dist/baseline-command.js.map +1 -1
  140. package/node_modules/checkride/dist/cli.d.ts +21 -4
  141. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  142. package/node_modules/checkride/dist/cli.js +350 -36
  143. package/node_modules/checkride/dist/cli.js.map +1 -1
  144. package/node_modules/checkride/dist/config.d.ts +114 -9
  145. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  146. package/node_modules/checkride/dist/config.js +142 -23
  147. package/node_modules/checkride/dist/config.js.map +1 -1
  148. package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
  149. package/node_modules/checkride/dist/digest/digest.js +6 -6
  150. package/node_modules/checkride/dist/digest/index.d.ts +2 -2
  151. package/node_modules/checkride/dist/digest/index.js +2 -2
  152. package/node_modules/checkride/dist/doctor.d.ts +27 -6
  153. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  154. package/node_modules/checkride/dist/doctor.js +231 -27
  155. package/node_modules/checkride/dist/doctor.js.map +1 -1
  156. package/node_modules/checkride/dist/gate.d.ts +180 -0
  157. package/node_modules/checkride/dist/gate.d.ts.map +1 -0
  158. package/node_modules/checkride/dist/gate.js +617 -0
  159. package/node_modules/checkride/dist/gate.js.map +1 -0
  160. package/node_modules/checkride/dist/init.d.ts +79 -12
  161. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  162. package/node_modules/checkride/dist/init.js +385 -81
  163. package/node_modules/checkride/dist/init.js.map +1 -1
  164. package/node_modules/checkride/dist/json.d.ts +21 -0
  165. package/node_modules/checkride/dist/json.d.ts.map +1 -0
  166. package/node_modules/checkride/dist/json.js +21 -0
  167. package/node_modules/checkride/dist/json.js.map +1 -0
  168. package/node_modules/checkride/dist/links.d.ts +31 -5
  169. package/node_modules/checkride/dist/links.d.ts.map +1 -1
  170. package/node_modules/checkride/dist/links.js +141 -32
  171. package/node_modules/checkride/dist/links.js.map +1 -1
  172. package/node_modules/checkride/dist/node-pin.d.ts +129 -0
  173. package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
  174. package/node_modules/checkride/dist/node-pin.js +211 -0
  175. package/node_modules/checkride/dist/node-pin.js.map +1 -0
  176. package/node_modules/checkride/dist/orchestrator.d.ts +53 -10
  177. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  178. package/node_modules/checkride/dist/orchestrator.js +312 -113
  179. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  180. package/node_modules/checkride/dist/pack.d.ts +8 -9
  181. package/node_modules/checkride/dist/pack.d.ts.map +1 -1
  182. package/node_modules/checkride/dist/pack.js +53 -17
  183. package/node_modules/checkride/dist/pack.js.map +1 -1
  184. package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
  185. package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
  186. package/node_modules/checkride/dist/pm/detect.js +6 -1
  187. package/node_modules/checkride/dist/pm/detect.js.map +1 -1
  188. package/node_modules/checkride/dist/pm/index.d.ts +7 -2
  189. package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
  190. package/node_modules/checkride/dist/pm/index.js +6 -2
  191. package/node_modules/checkride/dist/pm/index.js.map +1 -1
  192. package/node_modules/checkride/dist/pm/launch.d.ts +72 -0
  193. package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
  194. package/node_modules/checkride/dist/pm/launch.js +108 -0
  195. package/node_modules/checkride/dist/pm/launch.js.map +1 -0
  196. package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
  197. package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
  198. package/node_modules/checkride/dist/pm/tools.js +124 -0
  199. package/node_modules/checkride/dist/pm/tools.js.map +1 -0
  200. package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
  201. package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
  202. package/node_modules/checkride/dist/pm/translate.js +78 -18
  203. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  204. package/node_modules/checkride/dist/proc.d.ts +30 -0
  205. package/node_modules/checkride/dist/proc.d.ts.map +1 -0
  206. package/node_modules/checkride/dist/proc.js +42 -0
  207. package/node_modules/checkride/dist/proc.js.map +1 -0
  208. package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
  209. package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
  210. package/node_modules/checkride/dist/qa/cli.js +24 -0
  211. package/node_modules/checkride/dist/qa/cli.js.map +1 -0
  212. package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
  213. package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
  214. package/node_modules/checkride/dist/qa/dead.js +88 -0
  215. package/node_modules/checkride/dist/qa/dead.js.map +1 -0
  216. package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
  217. package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
  218. package/node_modules/checkride/dist/qa/dupes.js +62 -0
  219. package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
  220. package/node_modules/checkride/dist/qa/health.d.ts +85 -0
  221. package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
  222. package/node_modules/checkride/dist/qa/health.js +109 -0
  223. package/node_modules/checkride/dist/qa/health.js.map +1 -0
  224. package/node_modules/checkride/dist/qa/index.d.ts +23 -0
  225. package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
  226. package/node_modules/checkride/dist/qa/index.js +22 -0
  227. package/node_modules/checkride/dist/qa/index.js.map +1 -0
  228. package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
  229. package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
  230. package/node_modules/checkride/dist/qa/mutation.js +195 -0
  231. package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
  232. package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
  233. package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
  234. package/node_modules/checkride/dist/qa/qa.js +158 -0
  235. package/node_modules/checkride/dist/qa/qa.js.map +1 -0
  236. package/node_modules/checkride/dist/qa/read.d.ts +40 -0
  237. package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
  238. package/node_modules/checkride/dist/qa/read.js +47 -0
  239. package/node_modules/checkride/dist/qa/read.js.map +1 -0
  240. package/node_modules/checkride/dist/qa/render.d.ts +26 -0
  241. package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
  242. package/node_modules/checkride/dist/qa/render.js +310 -0
  243. package/node_modules/checkride/dist/qa/render.js.map +1 -0
  244. package/node_modules/checkride/dist/recover.d.ts +48 -0
  245. package/node_modules/checkride/dist/recover.d.ts.map +1 -0
  246. package/node_modules/checkride/dist/recover.js +220 -0
  247. package/node_modules/checkride/dist/recover.js.map +1 -0
  248. package/node_modules/checkride/dist/security.d.ts +45 -0
  249. package/node_modules/checkride/dist/security.d.ts.map +1 -0
  250. package/node_modules/checkride/dist/security.js +86 -0
  251. package/node_modules/checkride/dist/security.js.map +1 -0
  252. package/node_modules/checkride/dist/smoke.d.ts +6 -6
  253. package/node_modules/checkride/dist/smoke.js +6 -6
  254. package/node_modules/checkride/dist/snippets.d.ts +27 -30
  255. package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
  256. package/node_modules/checkride/dist/snippets.js +26 -30
  257. package/node_modules/checkride/dist/snippets.js.map +1 -1
  258. package/node_modules/checkride/dist/tool-json.d.ts +33 -0
  259. package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
  260. package/node_modules/checkride/dist/tool-json.js +58 -0
  261. package/node_modules/checkride/dist/tool-json.js.map +1 -0
  262. package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
  263. package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
  264. package/node_modules/checkride/dist/triage/cli.js +26 -0
  265. package/node_modules/checkride/dist/triage/cli.js.map +1 -0
  266. package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
  267. package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
  268. package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
  269. package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
  270. package/node_modules/checkride/dist/triage/env.d.ts +54 -0
  271. package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
  272. package/node_modules/checkride/dist/triage/env.js +81 -0
  273. package/node_modules/checkride/dist/triage/env.js.map +1 -0
  274. package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
  275. package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
  276. package/node_modules/checkride/dist/triage/gate.js +121 -0
  277. package/node_modules/checkride/dist/triage/gate.js.map +1 -0
  278. package/node_modules/checkride/dist/triage/index.d.ts +22 -0
  279. package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
  280. package/node_modules/checkride/dist/triage/index.js +20 -0
  281. package/node_modules/checkride/dist/triage/index.js.map +1 -0
  282. package/node_modules/checkride/dist/triage/render.d.ts +19 -0
  283. package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
  284. package/node_modules/checkride/dist/triage/render.js +419 -0
  285. package/node_modules/checkride/dist/triage/render.js.map +1 -0
  286. package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
  287. package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
  288. package/node_modules/checkride/dist/triage/triage.js +131 -0
  289. package/node_modules/checkride/dist/triage/triage.js.map +1 -0
  290. package/node_modules/checkride/package.json +18 -15
  291. package/node_modules/checkride/schema/checkride.config.schema.json +70 -5
  292. package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
  293. package/node_modules/checkride/skills/check/SKILL.md +265 -0
  294. package/node_modules/checkride/skills/qa/SKILL.md +231 -0
  295. package/node_modules/checkride/templates/shared/cspell.json +1 -0
  296. package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
  297. package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
  298. package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
  299. package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
  300. package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
  301. package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
  302. package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
  303. package/node_modules/checkride/templates/shared/vale.ini +34 -0
  304. package/package.json +10 -9
  305. package/skills/build/SKILL.md +85 -76
  306. package/skills/doctor/SKILL.md +11 -11
  307. package/skills/finish/SKILL.md +21 -18
  308. package/skills/harvest/SKILL.md +13 -11
  309. package/skills/park/SKILL.md +10 -10
  310. package/skills/plan/SKILL.md +69 -59
  311. package/skills/recover/SKILL.md +28 -0
  312. package/skills/refine/SKILL.md +21 -18
  313. package/skills/revert/SKILL.md +6 -6
  314. package/skills/spike/SKILL.md +9 -9
  315. package/skills/status/SKILL.md +5 -5
  316. package/skills/step/SKILL.md +20 -15
  317. package/skills/verify/SKILL.md +56 -46
  318. package/templates/build-log.md +7 -7
  319. package/templates/intent.md +52 -41
  320. package/templates/spike-report.md +10 -10
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Tolerant parsing of a tool's stdout as JSON.
3
+ *
4
+ * A tool that emits JSON does not always get stdout to itself: any launcher,
5
+ * wrapper or shell profile can print before it does. checkride suppresses the
6
+ * one case it controls at the invocation (pnpm's dependency-check narration —
7
+ * see `translateExec` and `docs/tools.md` §Launcher quirks), but a consumer's
8
+ * launcher is not checkride's to pin, so the read tolerates a preamble too.
9
+ *
10
+ * **Skipping a preamble is not normalizing.** The tool's own bytes are returned
11
+ * verbatim from the first character of the JSON onward — nothing inside them is
12
+ * rewritten, reordered or reformatted, and what gets dropped was never the
13
+ * tool's output to begin with. That is what keeps this compatible with "the raw
14
+ * file is the truth" (`docs/contract.md`), which is a promise, not a detail.
15
+ */
16
+ /** A tool's stdout parsed as JSON: the value, and the exact text it came from. */
17
+ export type ToolJson = {
18
+ /** The parsed value. */
19
+ value: unknown;
20
+ /**
21
+ * The JSON text with any preamble removed — what belongs in
22
+ * `.check/<slot>.json`, so the artifact a consumer opens actually parses.
23
+ */
24
+ text: string;
25
+ };
26
+ /**
27
+ * Parse `raw` as JSON, tolerating up to {@link MAX_PREAMBLE_LINES} lines of
28
+ * non-JSON ahead of it. Clean output takes the fast path and is returned byte
29
+ * for byte. Returns `null` when nothing parses — every caller treats that as a
30
+ * loud failure, never as "clean".
31
+ */
32
+ export declare function parseToolJson(raw: string): ToolJson | null;
33
+ //# sourceMappingURL=tool-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-json.d.ts","sourceRoot":"","sources":["../src/tool-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG;IACrB,wBAAwB;IACxB,KAAK,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAYF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAgB1D"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Tolerant parsing of a tool's stdout as JSON.
3
+ *
4
+ * A tool that emits JSON does not always get stdout to itself: any launcher,
5
+ * wrapper or shell profile can print before it does. checkride suppresses the
6
+ * one case it controls at the invocation (pnpm's dependency-check narration —
7
+ * see `translateExec` and `docs/tools.md` §Launcher quirks), but a consumer's
8
+ * launcher is not checkride's to pin, so the read tolerates a preamble too.
9
+ *
10
+ * **Skipping a preamble is not normalizing.** The tool's own bytes are returned
11
+ * verbatim from the first character of the JSON onward — nothing inside them is
12
+ * rewritten, reordered or reformatted, and what gets dropped was never the
13
+ * tool's output to begin with. That is what keeps this compatible with "the raw
14
+ * file is the truth" (`docs/contract.md`), which is a promise, not a detail.
15
+ */
16
+ /**
17
+ * How many leading lines may precede the JSON before parsing gives up. Real
18
+ * preambles are one or two lines; the cap keeps a large non-JSON payload (a 650
19
+ * kB test log, say) from costing one parse attempt per line.
20
+ */
21
+ const MAX_PREAMBLE_LINES = 10;
22
+ /** Parse `text` as JSON, or `undefined` when it is not JSON at all. */
23
+ function tryParse(text) {
24
+ if (text.trim() === '')
25
+ return undefined;
26
+ try {
27
+ return JSON.parse(text);
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ /**
34
+ * Parse `raw` as JSON, tolerating up to {@link MAX_PREAMBLE_LINES} lines of
35
+ * non-JSON ahead of it. Clean output takes the fast path and is returned byte
36
+ * for byte. Returns `null` when nothing parses — every caller treats that as a
37
+ * loud failure, never as "clean".
38
+ */
39
+ export function parseToolJson(raw) {
40
+ const whole = tryParse(raw);
41
+ if (whole !== undefined)
42
+ return { value: whole, text: raw };
43
+ // Retry from each subsequent line start. The tool's JSON begins at a line
44
+ // boundary because the preamble that precedes it ended with a newline.
45
+ let offset = 0;
46
+ for (let line = 0; line < MAX_PREAMBLE_LINES; line++) {
47
+ const newline = raw.indexOf('\n', offset);
48
+ if (newline === -1)
49
+ break;
50
+ offset = newline + 1;
51
+ const rest = raw.slice(offset);
52
+ const value = tryParse(rest);
53
+ if (value !== undefined)
54
+ return { value, text: rest };
55
+ }
56
+ return null;
57
+ }
58
+ //# sourceMappingURL=tool-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-json.js","sourceRoot":"","sources":["../src/tool-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAa9B,uEAAuE;AACvE,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAE5D,0EAA0E;IAC1E,uEAAuE;IACvE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,kBAAkB,EAAE,IAAI,EAAE,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,IAAI,OAAO,KAAK,CAAC,CAAC;YAAE,MAAM;QAC1B,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;QACrB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Executable form of the triage reader — what the bundled `/checkride:check`
4
+ * skill invokes:
5
+ *
6
+ * ```
7
+ * node <plugin-root>/dist/triage/cli.js [cwd]
8
+ * ```
9
+ *
10
+ * It ships in the package's existing `dist/`, so both the published tarball and
11
+ * an installed plugin cache already carry it and no consumer build step exists.
12
+ * It adds no CLI command, flag or config to checkride itself: `dist/cli.js`
13
+ * knows nothing about this file.
14
+ *
15
+ * Exit code 0 means *a report was rendered*, however the gate came back — the
16
+ * gate's verdict is in the Markdown, not in this process's status, so a red repo
17
+ * does not look to the caller like a broken reader. Only a failure to produce a
18
+ * report at all exits non-zero.
19
+ */
20
+ export {};
21
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/triage/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Executable form of the triage reader — what the bundled `/checkride:check`
4
+ * skill invokes:
5
+ *
6
+ * ```
7
+ * node <plugin-root>/dist/triage/cli.js [cwd]
8
+ * ```
9
+ *
10
+ * It ships in the package's existing `dist/`, so both the published tarball and
11
+ * an installed plugin cache already carry it and no consumer build step exists.
12
+ * It adds no CLI command, flag or config to checkride itself: `dist/cli.js`
13
+ * knows nothing about this file.
14
+ *
15
+ * Exit code 0 means *a report was rendered*, however the gate came back — the
16
+ * gate's verdict is in the Markdown, not in this process's status, so a red repo
17
+ * does not look to the caller like a broken reader. Only a failure to produce a
18
+ * report at all exits non-zero.
19
+ */
20
+ import { realEnv } from './env.js';
21
+ import { renderTriage } from './render.js';
22
+ import { triage } from './triage.js';
23
+ const [, , cwdArg] = process.argv;
24
+ const report = await triage(cwdArg ?? process.cwd(), realEnv);
25
+ process.stdout.write(`${renderTriage(report)}\n`);
26
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/triage/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,EAAE,AAAD,EAAG,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;AAClC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;AAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Folding `checkride doctor` into the broken-harness branch.
3
+ *
4
+ * Exit 2 means the harness broke or was misused, and every check result from
5
+ * that run is untrustworthy. "Broken harness" on its own costs a round-trip:
6
+ * the agent has to go ask what broke. `doctor` already answers that — it is
7
+ * read-only, runs no checks, and splits 0/1 the same way — so triage runs it at
8
+ * the one moment it is the right answer and arrives with the diagnosis attached.
9
+ *
10
+ * It is spawned as `node <repo's checkride>/dist/cli.js doctor --json`, not
11
+ * through the package manager: `<pm> exec` can print its own noise onto stdout,
12
+ * and the JSON report is the thing being parsed. It is also deliberately the
13
+ * *repo's* installed checkride rather than the one this reader ships inside —
14
+ * diagnosing an environment with a different version's doctor is how a reader
15
+ * reports a problem the repo does not have.
16
+ */
17
+ import type { TriageEnv } from './env.js';
18
+ /** One required doctor row that is not ok — an environment problem to fix. */
19
+ export type DoctorFinding = {
20
+ name: string;
21
+ status: string;
22
+ found: string | null;
23
+ expected: string | null;
24
+ hint: string | null;
25
+ };
26
+ /** What the fold-in produced. Every state is reportable; none is an error. */
27
+ export type DoctorFold = {
28
+ state: 'ran';
29
+ ok: boolean;
30
+ exitCode: number | null;
31
+ findings: DoctorFinding[];
32
+ } | {
33
+ state: 'unavailable';
34
+ reason: string;
35
+ } | {
36
+ state: 'unreadable';
37
+ reason: string;
38
+ };
39
+ /**
40
+ * The checkride CLI this repo would run: its installed copy, or its own build
41
+ * when `cwd` *is* the checkride package (the dogfooding case). Never some other
42
+ * repo's `dist/cli.js` — the package name has to say so.
43
+ */
44
+ export declare function resolveCheckrideCli(cwd: string): string | null;
45
+ /**
46
+ * Run the repo's `checkride doctor --json` and extract what is broken. Never
47
+ * throws; an absent or unparseable doctor is reported as such so the report can
48
+ * say "the harness broke and I could not ask why" instead of inventing a cause.
49
+ */
50
+ export declare function foldDoctor(cwd: string, env: TriageEnv): Promise<DoctorFold>;
51
+ //# sourceMappingURL=doctor-fold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-fold.d.ts","sourceRoot":"","sources":["../../src/triage/doctor-fold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAClB;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,GACjF;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAe5C;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM9D;AAwBD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAUjF"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Folding `checkride doctor` into the broken-harness branch.
3
+ *
4
+ * Exit 2 means the harness broke or was misused, and every check result from
5
+ * that run is untrustworthy. "Broken harness" on its own costs a round-trip:
6
+ * the agent has to go ask what broke. `doctor` already answers that — it is
7
+ * read-only, runs no checks, and splits 0/1 the same way — so triage runs it at
8
+ * the one moment it is the right answer and arrives with the diagnosis attached.
9
+ *
10
+ * It is spawned as `node <repo's checkride>/dist/cli.js doctor --json`, not
11
+ * through the package manager: `<pm> exec` can print its own noise onto stdout,
12
+ * and the JSON report is the thing being parsed. It is also deliberately the
13
+ * *repo's* installed checkride rather than the one this reader ships inside —
14
+ * diagnosing an environment with a different version's doctor is how a reader
15
+ * reports a problem the repo does not have.
16
+ */
17
+ import { existsSync, readFileSync } from 'node:fs';
18
+ import { join } from 'node:path';
19
+ import { asStringOrNull, isRecord, parseJson } from '../artifacts/index.js';
20
+ /** A doctor probe is a read; it should never be the thing that hangs a triage. */
21
+ const DOCTOR_TIMEOUT_MS = 120_000;
22
+ /** Read a package.json `name` field, or `null` when it cannot be read. */
23
+ function packageName(cwd) {
24
+ try {
25
+ const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'));
26
+ return pkg.name ?? null;
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ /**
33
+ * The checkride CLI this repo would run: its installed copy, or its own build
34
+ * when `cwd` *is* the checkride package (the dogfooding case). Never some other
35
+ * repo's `dist/cli.js` — the package name has to say so.
36
+ */
37
+ export function resolveCheckrideCli(cwd) {
38
+ const installed = join(cwd, 'node_modules', 'checkride', 'dist', 'cli.js');
39
+ if (existsSync(installed))
40
+ return installed;
41
+ const own = join(cwd, 'dist', 'cli.js');
42
+ if (packageName(cwd) === 'checkride' && existsSync(own))
43
+ return own;
44
+ return null;
45
+ }
46
+ /** Narrow one `checks[]` row to a finding, or `null` when it is not a problem. */
47
+ function toFinding(value) {
48
+ if (!isRecord(value))
49
+ return null;
50
+ if (value['required'] !== true || value['status'] === 'ok')
51
+ return null;
52
+ return {
53
+ name: asStringOrNull(value['name']) ?? 'unnamed check',
54
+ status: asStringOrNull(value['status']) ?? 'unknown',
55
+ found: asStringOrNull(value['found']),
56
+ expected: asStringOrNull(value['expected']),
57
+ hint: asStringOrNull(value['hint']),
58
+ };
59
+ }
60
+ /** Pull the required-and-not-ok rows out of a `doctor --json` report. */
61
+ function readFindings(stdout) {
62
+ const report = parseJson(stdout);
63
+ if (!isRecord(report))
64
+ return null;
65
+ const checks = report['checks'];
66
+ if (!Array.isArray(checks))
67
+ return null;
68
+ return checks.map(toFinding).filter((f) => f !== null);
69
+ }
70
+ /**
71
+ * Run the repo's `checkride doctor --json` and extract what is broken. Never
72
+ * throws; an absent or unparseable doctor is reported as such so the report can
73
+ * say "the harness broke and I could not ask why" instead of inventing a cause.
74
+ */
75
+ export async function foldDoctor(cwd, env) {
76
+ const cli = resolveCheckrideCli(cwd);
77
+ if (cli === null) {
78
+ return { state: 'unavailable', reason: 'checkride is not installed in this repo (no node_modules/checkride)' };
79
+ }
80
+ const outcome = await env.spawn(process.execPath, [cli, 'doctor', '--json'], { cwd, timeoutMs: DOCTOR_TIMEOUT_MS });
81
+ if (outcome.error !== null)
82
+ return { state: 'unreadable', reason: `could not run doctor: ${outcome.error}` };
83
+ const findings = readFindings(outcome.stdout);
84
+ if (findings === null)
85
+ return { state: 'unreadable', reason: 'doctor --json did not produce a readable report' };
86
+ return { state: 'ran', ok: outcome.code === 0, exitCode: outcome.code, findings };
87
+ }
88
+ //# sourceMappingURL=doctor-fold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-fold.js","sourceRoot":"","sources":["../../src/triage/doctor-fold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The triage reader's process surface: spawning, and the clock.
3
+ *
4
+ * Both are injectable because the reader's hardest guarantees are about
5
+ * *failure* — it must survive a red gate, a broken harness, a missing binary
6
+ * and a hung run without dying itself, and none of those are reachable from a
7
+ * test that has to spawn a real toolchain to get there.
8
+ *
9
+ * {@link spawnCapture} never rejects. A reader that throws on a red gate would
10
+ * fail exactly when it is needed, so a spawn failure is a *value* — an outcome
11
+ * with `error` set — not an exception.
12
+ */
13
+ /**
14
+ * A finished (or failed) child process. `code` is `null` when the process died
15
+ * by signal or never started; `error` is set only in the latter case.
16
+ */
17
+ export type SpawnOutcome = {
18
+ code: number | null;
19
+ signal: string | null;
20
+ stdout: string;
21
+ stderr: string;
22
+ /** The spawn error message when the process could not start at all. */
23
+ error: string | null;
24
+ };
25
+ /** Run a command and capture it. The one process touch the reader makes. */
26
+ export type Spawner = (command: string, args: readonly string[], opts: {
27
+ cwd: string;
28
+ timeoutMs: number;
29
+ }) => Promise<SpawnOutcome>;
30
+ /** Everything the reader needs from the outside world. */
31
+ export type TriageEnv = {
32
+ spawn: Spawner;
33
+ now: () => number;
34
+ /** Budget for the gate run. A hung check must not hang the agent waiting on it. */
35
+ timeoutMs: number;
36
+ };
37
+ /**
38
+ * Spawn `command`, capture both streams, and resolve however it ends. Never rejects.
39
+ *
40
+ * `detached`, so the child leads its own process group and the timeout can
41
+ * reap the whole tree. The gate is `<pm> run check`, which means the process
42
+ * actually doing the work is a *grandchild* — a bare `proc.kill()` signals the
43
+ * package-manager wrapper and leaves the checks themselves running, which both
44
+ * defeats the budget and can hold this promise open forever, since `close`
45
+ * waits on every inherited pipe. `killGroupEscalating` signals the group and
46
+ * insists with SIGKILL after the grace (see `../proc.ts`).
47
+ */
48
+ export declare function spawnCapture(command: string, args: readonly string[], opts: {
49
+ cwd: string;
50
+ timeoutMs: number;
51
+ }): Promise<SpawnOutcome>;
52
+ /** The real process surface. */
53
+ export declare const realEnv: TriageEnv;
54
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/triage/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,CACpB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACrC,OAAO,CAAC,YAAY,CAAC,CAAC;AAE3B,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAoBF;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvC,OAAO,CAAC,YAAY,CAAC,CA+BvB;AAED,gCAAgC;AAChC,eAAO,MAAM,OAAO,EAAE,SAIrB,CAAC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The triage reader's process surface: spawning, and the clock.
3
+ *
4
+ * Both are injectable because the reader's hardest guarantees are about
5
+ * *failure* — it must survive a red gate, a broken harness, a missing binary
6
+ * and a hung run without dying itself, and none of those are reachable from a
7
+ * test that has to spawn a real toolchain to get there.
8
+ *
9
+ * {@link spawnCapture} never rejects. A reader that throws on a red gate would
10
+ * fail exactly when it is needed, so a spawn failure is a *value* — an outcome
11
+ * with `error` set — not an exception.
12
+ */
13
+ import { spawn } from 'node:child_process';
14
+ import { killGroupEscalating } from '../proc.js';
15
+ /**
16
+ * A full gate can legitimately take minutes (mutation testing, a cold build),
17
+ * so the ceiling is generous: it exists to break a hang, not to police a slow
18
+ * repo. checkride's own per-check timeouts fire long before this does.
19
+ */
20
+ const DEFAULT_GATE_TIMEOUT_MS = 20 * 60 * 1000;
21
+ /**
22
+ * Keep the tail of each stream. The reader only ever renders an excerpt, and a
23
+ * runaway tool must not be able to exhaust memory before the run even ends.
24
+ */
25
+ const MAX_CAPTURE_CHARS = 256 * 1024;
26
+ function appendCapped(buffer, chunk) {
27
+ const next = buffer + chunk;
28
+ return next.length > MAX_CAPTURE_CHARS ? next.slice(next.length - MAX_CAPTURE_CHARS) : next;
29
+ }
30
+ /**
31
+ * Spawn `command`, capture both streams, and resolve however it ends. Never rejects.
32
+ *
33
+ * `detached`, so the child leads its own process group and the timeout can
34
+ * reap the whole tree. The gate is `<pm> run check`, which means the process
35
+ * actually doing the work is a *grandchild* — a bare `proc.kill()` signals the
36
+ * package-manager wrapper and leaves the checks themselves running, which both
37
+ * defeats the budget and can hold this promise open forever, since `close`
38
+ * waits on every inherited pipe. `killGroupEscalating` signals the group and
39
+ * insists with SIGKILL after the grace (see `../proc.ts`).
40
+ */
41
+ export function spawnCapture(command, args, opts) {
42
+ return new Promise((resolve) => {
43
+ const proc = spawn(command, [...args], {
44
+ cwd: opts.cwd,
45
+ detached: true,
46
+ stdio: ['ignore', 'pipe', 'pipe'],
47
+ env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
48
+ });
49
+ let stdout = '';
50
+ let stderr = '';
51
+ let settled = false;
52
+ let escalation = null;
53
+ const timer = setTimeout(() => { escalation = killGroupEscalating(proc.pid); }, opts.timeoutMs);
54
+ const settle = (outcome) => {
55
+ if (settled)
56
+ return;
57
+ settled = true;
58
+ clearTimeout(timer);
59
+ // A pending SIGKILL timer would hold the event loop open past the run.
60
+ if (escalation !== null)
61
+ clearTimeout(escalation);
62
+ resolve(outcome);
63
+ };
64
+ // Decoded string streams: the internal StringDecoder holds a partial
65
+ // multibyte sequence until its continuation bytes arrive, so `+=` never
66
+ // concatenates half a character.
67
+ proc.stdout.setEncoding('utf8');
68
+ proc.stderr.setEncoding('utf8');
69
+ proc.stdout.on('data', (chunk) => { stdout = appendCapped(stdout, chunk); });
70
+ proc.stderr.on('data', (chunk) => { stderr = appendCapped(stderr, chunk); });
71
+ proc.on('error', (err) => { settle({ code: null, signal: null, stdout, stderr, error: err.message }); });
72
+ proc.on('close', (code, signal) => { settle({ code, signal, stdout, stderr, error: null }); });
73
+ });
74
+ }
75
+ /** The real process surface. */
76
+ export const realEnv = {
77
+ spawn: spawnCapture,
78
+ now: () => Date.now(),
79
+ timeoutMs: DEFAULT_GATE_TIMEOUT_MS,
80
+ };
81
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/triage/env.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Running the gate — the preflight every triage begins with.
3
+ *
4
+ * The reader runs the repo's *own* `check` script rather than `checkride`
5
+ * directly, because that script is the repo's definition of done and may carry
6
+ * deliberate `--only` / `--skip` / `--changed` that a direct invocation would
7
+ * bypass. It also means a pre-existing `summary.json` is never mistaken for
8
+ * evidence: every run overwrites it, so `ok: true` on disk can mean "three of
9
+ * seventeen slots passed fourteen minutes ago".
10
+ *
11
+ * checkride promises the 0/1/2 exit split, so the verdict branches on that
12
+ * before anything is read. Anything outside it — 127, a signal death, a wrapper
13
+ * command that failed before checkride ran — is `off-contract`, reported as
14
+ * itself rather than folded into one of the three.
15
+ *
16
+ * One exit *inside* the split still is not checkride's: a package manager that
17
+ * refuses to start the script — an `engines.node` pin the running Node does not
18
+ * satisfy, most often — exits 1, exactly as a red pipeline does. Left
19
+ * unclassified, the reader an agent is sent to by a red gate would confirm the
20
+ * red and point at slots that never ran. See `../pm/launch.ts`.
21
+ */
22
+ import type { PackageManager } from '../pm/index.js';
23
+ import type { TriageEnv } from './env.js';
24
+ /** How the gate ended, in the reader's vocabulary. */
25
+ export type GateVerdict = 'green' | 'red' | 'could-not-start' | 'harness-broken' | 'off-contract' | 'not-run';
26
+ /** The gate run: what was executed, how it ended, and what it printed. */
27
+ export type GateOutcome = {
28
+ verdict: GateVerdict;
29
+ /** The repo's `scripts.check`, verbatim — the definition of done. */
30
+ script: string | null;
31
+ /** The command line as run, for the report to quote. */
32
+ command: string;
33
+ pm: PackageManager;
34
+ exitCode: number | null;
35
+ signal: string | null;
36
+ startedMs: number;
37
+ durationMs: number;
38
+ stdout: string;
39
+ stderr: string;
40
+ spawnError: string | null;
41
+ /** Why the package manager never started the script, on the `could-not-start` verdict. */
42
+ refusal: string | null;
43
+ /** Selection flags found in the script text: it narrows the run on purpose. */
44
+ narrowingFlags: string[];
45
+ };
46
+ /**
47
+ * Run `<pm> run check` in `cwd` and classify the result. Never throws: a repo
48
+ * with no `check` script, a missing package manager and a gate that fails are
49
+ * all outcomes, because each is something the report has to say.
50
+ */
51
+ export declare function runGate(cwd: string, env: TriageEnv): Promise<GateOutcome>;
52
+ /**
53
+ * Whether this ending means the harness itself is suspect, not the code.
54
+ *
55
+ * `could-not-start` belongs here for the strongest version of that reason:
56
+ * nothing ran at all, so the environment is the entire finding and the slot
57
+ * table is not evidence of anything. It is also what folds `doctor` into the
58
+ * report, which is where the reader will find the Node the hook got.
59
+ */
60
+ export declare function isHarnessProblem(verdict: GateVerdict): boolean;
61
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/triage/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,UAAU,CAAC;AAExD,sDAAsD;AACtD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAE9G,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,WAAW,CAAC;IACrB,qEAAqE;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,cAAc,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,0FAA0F;IAC1F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,+EAA+E;IAC/E,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AA0DF;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA0B/E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE9D"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Running the gate — the preflight every triage begins with.
3
+ *
4
+ * The reader runs the repo's *own* `check` script rather than `checkride`
5
+ * directly, because that script is the repo's definition of done and may carry
6
+ * deliberate `--only` / `--skip` / `--changed` that a direct invocation would
7
+ * bypass. It also means a pre-existing `summary.json` is never mistaken for
8
+ * evidence: every run overwrites it, so `ok: true` on disk can mean "three of
9
+ * seventeen slots passed fourteen minutes ago".
10
+ *
11
+ * checkride promises the 0/1/2 exit split, so the verdict branches on that
12
+ * before anything is read. Anything outside it — 127, a signal death, a wrapper
13
+ * command that failed before checkride ran — is `off-contract`, reported as
14
+ * itself rather than folded into one of the three.
15
+ *
16
+ * One exit *inside* the split still is not checkride's: a package manager that
17
+ * refuses to start the script — an `engines.node` pin the running Node does not
18
+ * satisfy, most often — exits 1, exactly as a red pipeline does. Left
19
+ * unclassified, the reader an agent is sent to by a red gate would confirm the
20
+ * red and point at slots that never ran. See `../pm/launch.ts`.
21
+ */
22
+ import { readFileSync } from 'node:fs';
23
+ import { join } from 'node:path';
24
+ import { readSummary } from '../artifacts/index.js';
25
+ import { detectPackageManager, launchRefusal } from '../pm/index.js';
26
+ /** Script name every checkride repo exposes; `checkride init` writes it. */
27
+ const CHECK_SCRIPT = 'check';
28
+ /**
29
+ * Flags that make a green run cover less than the repo configures. Detected
30
+ * textually in the script, which is exact and needs no knowledge of the
31
+ * installed checkride's catalogue.
32
+ */
33
+ const NARROWING_FLAGS = ['--only', '--skip', '--changed'];
34
+ /** checkride's promised exit split. Everything else is off-contract. */
35
+ const VERDICT_BY_CODE = { 0: 'green', 1: 'red', 2: 'harness-broken' };
36
+ /** Read `scripts.check` from `cwd`'s package.json, or `null` when there is none. */
37
+ function readCheckScript(cwd) {
38
+ try {
39
+ const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'));
40
+ return pkg.scripts?.[CHECK_SCRIPT] ?? null;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ /**
47
+ * Tolerance for a summary written moments before the gate's own clock reading —
48
+ * mirrors `MTIME_TOLERANCE_MS` in `./triage.ts`, where the same comparison
49
+ * decides whether the summary belongs to this run.
50
+ */
51
+ const MTIME_TOLERANCE_MS = 1000;
52
+ /**
53
+ * Did this run write a summary? Proof that the pipeline started, whatever its
54
+ * output happens to contain — the guard that keeps a check which merely
55
+ * *printed* a package-manager error code from being read as a refusal to launch.
56
+ */
57
+ async function wroteSummary(cwd, startedMs) {
58
+ const read = await readSummary(cwd);
59
+ return read.state === 'ok' && read.mtimeMs >= startedMs - MTIME_TOLERANCE_MS;
60
+ }
61
+ /**
62
+ * Classify an exit-1 gate: a red pipeline, or a package manager that never
63
+ * started one. Only exit 1 is examined — 0 and 2 are checkride's own answers and
64
+ * mean the pipeline ran, and everything else is already `off-contract`.
65
+ */
66
+ async function refusalFor(outcome, cwd, startedMs) {
67
+ if (outcome.code !== 1 || (await wroteSummary(cwd, startedMs)))
68
+ return null;
69
+ return launchRefusal(`${outcome.stdout}${outcome.stderr}`)?.cause ?? null;
70
+ }
71
+ function verdictFor(outcome, refusal) {
72
+ if (outcome.error !== null || outcome.code === null)
73
+ return 'off-contract';
74
+ if (refusal !== null)
75
+ return 'could-not-start';
76
+ return VERDICT_BY_CODE[outcome.code] ?? 'off-contract';
77
+ }
78
+ /**
79
+ * Run `<pm> run check` in `cwd` and classify the result. Never throws: a repo
80
+ * with no `check` script, a missing package manager and a gate that fails are
81
+ * all outcomes, because each is something the report has to say.
82
+ */
83
+ export async function runGate(cwd, env) {
84
+ const pm = detectPackageManager({ cwd });
85
+ const script = readCheckScript(cwd);
86
+ const base = {
87
+ script,
88
+ command: `${pm} run ${CHECK_SCRIPT}`,
89
+ pm,
90
+ startedMs: env.now(),
91
+ narrowingFlags: NARROWING_FLAGS.filter((flag) => script?.includes(flag) ?? false),
92
+ };
93
+ if (script === null) {
94
+ return { ...base, verdict: 'not-run', exitCode: null, signal: null, durationMs: 0, stdout: '', stderr: '', spawnError: null, refusal: null };
95
+ }
96
+ const outcome = await env.spawn(pm, ['run', CHECK_SCRIPT], { cwd, timeoutMs: env.timeoutMs });
97
+ const refusal = await refusalFor(outcome, cwd, base.startedMs);
98
+ return {
99
+ ...base,
100
+ verdict: verdictFor(outcome, refusal),
101
+ exitCode: outcome.code,
102
+ signal: outcome.signal,
103
+ durationMs: env.now() - base.startedMs,
104
+ stdout: outcome.stdout,
105
+ stderr: outcome.stderr,
106
+ spawnError: outcome.error,
107
+ refusal,
108
+ };
109
+ }
110
+ /**
111
+ * Whether this ending means the harness itself is suspect, not the code.
112
+ *
113
+ * `could-not-start` belongs here for the strongest version of that reason:
114
+ * nothing ran at all, so the environment is the entire finding and the slot
115
+ * table is not evidence of anything. It is also what folds `doctor` into the
116
+ * report, which is where the reader will find the Node the hook got.
117
+ */
118
+ export function isHarnessProblem(verdict) {
119
+ return verdict === 'harness-broken' || verdict === 'off-contract' || verdict === 'could-not-start';
120
+ }
121
+ //# sourceMappingURL=gate.js.map