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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +92 -89
- package/dist/cli-core.js +25 -8
- package/dist/cli.js +1 -1
- package/dist/lib/agents.js +26 -26
- package/dist/lib/buildlog.js +11 -11
- package/dist/lib/buildlogsync.js +8 -8
- package/dist/lib/check.js +55 -22
- package/dist/lib/commitbody.js +72 -0
- package/dist/lib/commitmsg.js +7 -7
- package/dist/lib/git.js +44 -15
- package/dist/lib/intent.js +21 -20
- package/dist/lib/latch.js +19 -19
- package/dist/lib/orient.js +36 -17
- package/dist/lib/reentry.js +70 -0
- package/dist/lib/settings.js +16 -16
- package/dist/lib/sidecar.js +54 -54
- package/dist/lib/templates.js +1 -1
- package/dist/verbs/agent.js +27 -27
- package/dist/verbs/build.js +9 -9
- package/dist/verbs/check.js +6 -6
- package/dist/verbs/checkpoint.js +48 -47
- package/dist/verbs/doctor.js +31 -30
- package/dist/verbs/finish.js +28 -41
- package/dist/verbs/handoff.js +9 -8
- package/dist/verbs/init.js +9 -9
- package/dist/verbs/park.js +3 -3
- package/dist/verbs/recover.js +297 -0
- package/dist/verbs/revert.js +13 -13
- package/dist/verbs/spike.js +25 -19
- package/dist/verbs/start.js +49 -19
- package/dist/verbs/status.js +8 -8
- package/dist/verbs/turn.js +16 -16
- package/dist/verbs/use.js +4 -4
- package/hooks/post-edit.sh +4 -4
- package/hooks/pre-bash-commit.sh +3 -3
- package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
- package/node_modules/checkride/CHANGELOG.md +1404 -0
- package/node_modules/checkride/LICENSE +201 -21
- package/node_modules/checkride/README.md +197 -38
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
- package/node_modules/checkride/dist/adapters.d.ts +30 -10
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +62 -23
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
- package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
- package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.js +77 -0
- package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts +69 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js +247 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.js +80 -0
- package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.js +93 -0
- package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.js +9 -4
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts +81 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js +604 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
- package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.js +45 -0
- package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.js +56 -0
- package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
- package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.js +19 -0
- package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.js +47 -0
- package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.js +91 -0
- package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.js +108 -0
- package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +16 -1
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
- package/node_modules/checkride/dist/atomic.js +25 -3
- package/node_modules/checkride/dist/atomic.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
- package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.js +163 -60
- package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +20 -11
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +73 -23
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
- package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/history.js +232 -0
- package/node_modules/checkride/dist/baseline/history.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +7 -5
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +5 -4
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +41 -10
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +65 -24
- package/node_modules/checkride/dist/baseline/store.js.map +1 -1
- package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
- package/node_modules/checkride/dist/baseline-command.js +7 -7
- package/node_modules/checkride/dist/baseline-command.js.map +1 -1
- package/node_modules/checkride/dist/cli.d.ts +21 -4
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +350 -36
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +114 -9
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +142 -23
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
- package/node_modules/checkride/dist/digest/digest.js +6 -6
- package/node_modules/checkride/dist/digest/index.d.ts +2 -2
- package/node_modules/checkride/dist/digest/index.js +2 -2
- package/node_modules/checkride/dist/doctor.d.ts +27 -6
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +231 -27
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +180 -0
- package/node_modules/checkride/dist/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/gate.js +617 -0
- package/node_modules/checkride/dist/gate.js.map +1 -0
- package/node_modules/checkride/dist/init.d.ts +79 -12
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +385 -81
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/json.d.ts +21 -0
- package/node_modules/checkride/dist/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/json.js +21 -0
- package/node_modules/checkride/dist/json.js.map +1 -0
- package/node_modules/checkride/dist/links.d.ts +31 -5
- package/node_modules/checkride/dist/links.d.ts.map +1 -1
- package/node_modules/checkride/dist/links.js +141 -32
- package/node_modules/checkride/dist/links.js.map +1 -1
- package/node_modules/checkride/dist/node-pin.d.ts +129 -0
- package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
- package/node_modules/checkride/dist/node-pin.js +211 -0
- package/node_modules/checkride/dist/node-pin.js.map +1 -0
- package/node_modules/checkride/dist/orchestrator.d.ts +53 -10
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +312 -113
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pack.d.ts +8 -9
- package/node_modules/checkride/dist/pack.d.ts.map +1 -1
- package/node_modules/checkride/dist/pack.js +53 -17
- package/node_modules/checkride/dist/pack.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/detect.js +6 -1
- package/node_modules/checkride/dist/pm/detect.js.map +1 -1
- package/node_modules/checkride/dist/pm/index.d.ts +7 -2
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/index.js +6 -2
- package/node_modules/checkride/dist/pm/index.js.map +1 -1
- package/node_modules/checkride/dist/pm/launch.d.ts +72 -0
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/launch.js +108 -0
- package/node_modules/checkride/dist/pm/launch.js.map +1 -0
- package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
- package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/tools.js +124 -0
- package/node_modules/checkride/dist/pm/tools.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/translate.js +78 -18
- package/node_modules/checkride/dist/pm/translate.js.map +1 -1
- package/node_modules/checkride/dist/proc.d.ts +30 -0
- package/node_modules/checkride/dist/proc.d.ts.map +1 -0
- package/node_modules/checkride/dist/proc.js +42 -0
- package/node_modules/checkride/dist/proc.js.map +1 -0
- package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
- package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/cli.js +24 -0
- package/node_modules/checkride/dist/qa/cli.js.map +1 -0
- package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
- package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dead.js +88 -0
- package/node_modules/checkride/dist/qa/dead.js.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.js +62 -0
- package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
- package/node_modules/checkride/dist/qa/health.d.ts +85 -0
- package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/health.js +109 -0
- package/node_modules/checkride/dist/qa/health.js.map +1 -0
- package/node_modules/checkride/dist/qa/index.d.ts +23 -0
- package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/index.js +22 -0
- package/node_modules/checkride/dist/qa/index.js.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.js +195 -0
- package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
- package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
- package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/qa.js +158 -0
- package/node_modules/checkride/dist/qa/qa.js.map +1 -0
- package/node_modules/checkride/dist/qa/read.d.ts +40 -0
- package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/read.js +47 -0
- package/node_modules/checkride/dist/qa/read.js.map +1 -0
- package/node_modules/checkride/dist/qa/render.d.ts +26 -0
- package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/render.js +310 -0
- package/node_modules/checkride/dist/qa/render.js.map +1 -0
- package/node_modules/checkride/dist/recover.d.ts +48 -0
- package/node_modules/checkride/dist/recover.d.ts.map +1 -0
- package/node_modules/checkride/dist/recover.js +220 -0
- package/node_modules/checkride/dist/recover.js.map +1 -0
- package/node_modules/checkride/dist/security.d.ts +45 -0
- package/node_modules/checkride/dist/security.d.ts.map +1 -0
- package/node_modules/checkride/dist/security.js +86 -0
- package/node_modules/checkride/dist/security.js.map +1 -0
- package/node_modules/checkride/dist/smoke.d.ts +6 -6
- package/node_modules/checkride/dist/smoke.js +6 -6
- package/node_modules/checkride/dist/snippets.d.ts +27 -30
- package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
- package/node_modules/checkride/dist/snippets.js +26 -30
- package/node_modules/checkride/dist/snippets.js.map +1 -1
- package/node_modules/checkride/dist/tool-json.d.ts +33 -0
- package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
- package/node_modules/checkride/dist/tool-json.js +58 -0
- package/node_modules/checkride/dist/tool-json.js.map +1 -0
- package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
- package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/cli.js +26 -0
- package/node_modules/checkride/dist/triage/cli.js.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
- package/node_modules/checkride/dist/triage/env.d.ts +54 -0
- package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/env.js +81 -0
- package/node_modules/checkride/dist/triage/env.js.map +1 -0
- package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
- package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/gate.js +121 -0
- package/node_modules/checkride/dist/triage/gate.js.map +1 -0
- package/node_modules/checkride/dist/triage/index.d.ts +22 -0
- package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/index.js +20 -0
- package/node_modules/checkride/dist/triage/index.js.map +1 -0
- package/node_modules/checkride/dist/triage/render.d.ts +19 -0
- package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/render.js +419 -0
- package/node_modules/checkride/dist/triage/render.js.map +1 -0
- package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
- package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/triage.js +131 -0
- package/node_modules/checkride/dist/triage/triage.js.map +1 -0
- package/node_modules/checkride/package.json +18 -15
- package/node_modules/checkride/schema/checkride.config.schema.json +70 -5
- package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
- package/node_modules/checkride/skills/check/SKILL.md +265 -0
- package/node_modules/checkride/skills/qa/SKILL.md +231 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
- package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
- package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
- package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
- package/node_modules/checkride/templates/shared/vale.ini +34 -0
- package/package.json +10 -9
- package/skills/build/SKILL.md +85 -76
- package/skills/doctor/SKILL.md +11 -11
- package/skills/finish/SKILL.md +21 -18
- package/skills/harvest/SKILL.md +13 -11
- package/skills/park/SKILL.md +10 -10
- package/skills/plan/SKILL.md +69 -59
- package/skills/recover/SKILL.md +28 -0
- package/skills/refine/SKILL.md +21 -18
- package/skills/revert/SKILL.md +6 -6
- package/skills/spike/SKILL.md +9 -9
- package/skills/status/SKILL.md +5 -5
- package/skills/step/SKILL.md +20 -15
- package/skills/verify/SKILL.md +56 -46
- package/templates/build-log.md +7 -7
- package/templates/intent.md +52 -41
- 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
|