plumbbob 0.9.3 → 0.9.5
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 +15 -13
- package/dist/cli-core.js +19 -2
- package/dist/lib/check.js +46 -13
- package/dist/lib/intent.js +1 -1
- package/dist/lib/latch.js +3 -3
- package/dist/lib/reentry.js +70 -0
- package/dist/verbs/agent.js +2 -2
- package/dist/verbs/doctor.js +4 -4
- package/dist/verbs/finish.js +1 -1
- package/dist/verbs/recover.js +297 -0
- package/dist/verbs/revert.js +2 -2
- package/dist/verbs/spike.js +8 -2
- 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 +1003 -0
- package/node_modules/checkride/LICENSE +201 -21
- package/node_modules/checkride/README.md +171 -30
- 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 +18 -6
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +42 -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 +61 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js +189 -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 +84 -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 +58 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js +299 -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 +161 -58
- package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +7 -7
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +16 -19
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/index.d.ts +4 -4
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +4 -4
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +12 -4
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +16 -9
- 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 +17 -4
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +259 -18
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +84 -8
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +122 -18
- 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 +5 -5
- 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 +25 -6
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +193 -27
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +135 -0
- package/node_modules/checkride/dist/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/gate.js +490 -0
- package/node_modules/checkride/dist/gate.js.map +1 -0
- package/node_modules/checkride/dist/init.d.ts +72 -12
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +324 -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 +40 -10
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +196 -102
- 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 +6 -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 +51 -0
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/launch.js +92 -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/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 +5 -5
- package/node_modules/checkride/dist/smoke.js +5 -5
- package/node_modules/checkride/dist/snippets.d.ts +26 -29
- 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 +17 -15
- package/node_modules/checkride/schema/checkride.config.schema.json +62 -5
- package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
- package/node_modules/checkride/skills/check/SKILL.md +257 -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/package.json +2 -2
- package/skills/build/SKILL.md +23 -19
- package/skills/finish/SKILL.md +3 -1
- package/skills/harvest/SKILL.md +2 -1
- package/skills/plan/SKILL.md +8 -7
- package/skills/recover/SKILL.md +28 -0
- package/skills/refine/SKILL.md +5 -4
- package/skills/spike/SKILL.md +1 -1
- package/skills/step/SKILL.md +4 -4
- package/skills/verify/SKILL.md +19 -14
- package/templates/intent.md +8 -7
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `checkride gate` — the stop-gate verdict, harness-neutral.
|
|
3
|
+
*
|
|
4
|
+
* Every agent harness offers some "the turn is ending" hook, and checkride's
|
|
5
|
+
* gate hangs off it: run the repo's `check` script, and while it is red, refuse
|
|
6
|
+
* to let the turn finish. What differs between harnesses is only the *wire
|
|
7
|
+
* format* of that refusal — Claude Code reads stderr and blocks on exit 2,
|
|
8
|
+
* Cursor reads a `followup_message` on stdout and submits it as the next turn.
|
|
9
|
+
* The decision itself (is it dirty, is it green, which artifact should the agent
|
|
10
|
+
* read) is identical, so it lives here once and the generated hook scripts
|
|
11
|
+
* become two-line adapters that name their harness.
|
|
12
|
+
*
|
|
13
|
+
* That split is the point. Before this command, the whole body was duplicated
|
|
14
|
+
* shell inside `.claude/hooks/checkride-gate.sh`, untestable outside an e2e run
|
|
15
|
+
* and impossible to share with a second harness.
|
|
16
|
+
*
|
|
17
|
+
* The gate answers three verdicts, not two. Green and red are the pipeline's;
|
|
18
|
+
* the third is **could not run**, for a package manager that refused to start
|
|
19
|
+
* the check script at all — an `engines.node` pin the hook's Node does not
|
|
20
|
+
* satisfy is the common cause, and it exits non-zero exactly like a failing
|
|
21
|
+
* test. Reading that as red produced a permanent red no code change could clear,
|
|
22
|
+
* pointing at a `.check/summary.json` no run had written. It still blocks, for
|
|
23
|
+
* the same reason every unrunnable gate does; what changes is that it names the
|
|
24
|
+
* cause instead of naming an artifact. See `./pm/launch.ts` for the
|
|
25
|
+
* classification and `./node-pin.ts` for the alignment that avoids it.
|
|
26
|
+
*
|
|
27
|
+
* Not to be confused with `../triage/gate.ts`, which also runs the `check`
|
|
28
|
+
* script: that one runs it *verbatim* as a reader's preflight and classifies the
|
|
29
|
+
* result for a report. This one appends `--strict --digest` because it IS the
|
|
30
|
+
* gate, and its output is a verdict for the harness, not prose for a human.
|
|
31
|
+
*/
|
|
32
|
+
import { spawn } from 'node:child_process';
|
|
33
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { formatDuration, readSummary } from './artifacts/index.js';
|
|
36
|
+
import { loadConfig } from './config.js';
|
|
37
|
+
import { DIGEST_FILE } from './digest/index.js';
|
|
38
|
+
import { alignNode, NODE_BIN_VAR, realPinEnv, withNodeBin } from './node-pin.js';
|
|
39
|
+
import { detectPackageManager, launchRefusal, runScript, SPAWN_FAILED_MARKER, } from './pm/index.js';
|
|
40
|
+
/**
|
|
41
|
+
* The edit marker: touched by the harness's "a file changed" hook, checked here
|
|
42
|
+
* under `--if-dirty`, and cleared by a green gate. Dot-named so it can never
|
|
43
|
+
* collide with a slot's `<slot>.json`/`<slot>.stdout.txt` artifacts, which the
|
|
44
|
+
* orchestrator deletes per slot before a re-run — the marker must survive every
|
|
45
|
+
* run.
|
|
46
|
+
*
|
|
47
|
+
* It lives here, not in `agent-setup`, because the gate is what reads and clears
|
|
48
|
+
* it; the hook writers import it from this module to build the touch command.
|
|
49
|
+
*/
|
|
50
|
+
export const DIRTY_MARKER = '.check/.dirty';
|
|
51
|
+
/** Where the gate points a red agent when `--digest` wrote nothing. */
|
|
52
|
+
const SUMMARY_PATH = '.check/summary.json';
|
|
53
|
+
/**
|
|
54
|
+
* Cursor's own hook config. It lives here rather than in `agent-setup/cursor.ts`
|
|
55
|
+
* for the same reason {@link DIRTY_MARKER} does: the gate *reads* it (see
|
|
56
|
+
* {@link deferredToCursor}), and the writer importing from the gate is the
|
|
57
|
+
* direction that has no cycle. `agent-setup/cursor.ts` re-exports it.
|
|
58
|
+
*/
|
|
59
|
+
export const CURSOR_HOOKS_FILE = '.cursor/hooks.json';
|
|
60
|
+
/**
|
|
61
|
+
* What a registered checkride gate looks like inside a harness's hook config.
|
|
62
|
+
* Both harnesses name the script the same, so finding it in Cursor's config is
|
|
63
|
+
* enough to know Cursor has a native gate of its own.
|
|
64
|
+
*/
|
|
65
|
+
const GATE_SCRIPT_SENTINEL = 'checkride-gate.sh';
|
|
66
|
+
/** Every harness whose stop-hook protocol the gate can speak. */
|
|
67
|
+
export const HARNESS_NAMES = ['claude', 'cursor'];
|
|
68
|
+
/** Script name every checkride repo exposes; `checkride init` writes it. */
|
|
69
|
+
const CHECK_SCRIPT = 'check';
|
|
70
|
+
const spawnForward = (command, args, opts) => new Promise((resolve) => {
|
|
71
|
+
const proc = spawn(command, [...args], {
|
|
72
|
+
cwd: opts.cwd,
|
|
73
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
74
|
+
env: { ...opts.env, FORCE_COLOR: '0', NO_COLOR: '1' },
|
|
75
|
+
});
|
|
76
|
+
proc.stdout.setEncoding('utf8');
|
|
77
|
+
proc.stderr.setEncoding('utf8');
|
|
78
|
+
proc.stdout.on('data', (chunk) => opts.stderr.write(chunk));
|
|
79
|
+
proc.stderr.on('data', (chunk) => opts.stderr.write(chunk));
|
|
80
|
+
// A gate that cannot spawn its package manager is not a crash: the harness
|
|
81
|
+
// gets a verdict it can act on rather than a stack trace. The message goes
|
|
82
|
+
// to the stream so it is both visible and classifiable — a spawn failure
|
|
83
|
+
// that printed nothing at all was reported as a red with no explanation.
|
|
84
|
+
proc.on('error', (err) => {
|
|
85
|
+
opts.stderr.write(`${SPAWN_FAILED_MARKER} \`${command}\`: ${err.message}\n`);
|
|
86
|
+
resolve(null);
|
|
87
|
+
});
|
|
88
|
+
proc.on('close', (code) => resolve(code));
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* How much of the child's output to keep for {@link launchRefusal}.
|
|
92
|
+
*
|
|
93
|
+
* The **head**, not the tail: a package manager that refuses to launch says so
|
|
94
|
+
* before anything else runs, so the evidence is always in the first bytes. A
|
|
95
|
+
* check that fails after this much output is a pipeline red, which needs no
|
|
96
|
+
* classification.
|
|
97
|
+
*/
|
|
98
|
+
const CAPTURE_LIMIT = 8 * 1024;
|
|
99
|
+
/** An `Out` that forwards everything and remembers the beginning. */
|
|
100
|
+
function recording(out) {
|
|
101
|
+
let kept = '';
|
|
102
|
+
return {
|
|
103
|
+
write: (chunk) => {
|
|
104
|
+
if (kept.length < CAPTURE_LIMIT)
|
|
105
|
+
kept += chunk;
|
|
106
|
+
return out.write(chunk);
|
|
107
|
+
},
|
|
108
|
+
text: () => kept,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* True when this `--harness claude` invocation is really Cursor running the
|
|
113
|
+
* repo's `.claude/settings.json` hooks, *and* Cursor has a native checkride gate
|
|
114
|
+
* of its own that will run for the same turn.
|
|
115
|
+
*
|
|
116
|
+
* Cursor loads third-party (Claude Code) hook configs when "Include third-party
|
|
117
|
+
* Plugins, Skills, and other configs" is on, maps `Stop` onto `stop`, and — the
|
|
118
|
+
* load-bearing sentence — runs **all** matching hooks from **every** source. A
|
|
119
|
+
* repo wired for both harnesses therefore fires two full pipelines into one
|
|
120
|
+
* `.check/` directory, concurrently and with nothing to serialize them, where
|
|
121
|
+
* the orchestrator clears each slot's artifacts before re-running it.
|
|
122
|
+
*
|
|
123
|
+
* So one of the two has to stand down, and it is this one: under Cursor the
|
|
124
|
+
* native `.cursor/hooks.json` gate is the only one that can answer in Cursor's
|
|
125
|
+
* protocol, which makes it authoritative there.
|
|
126
|
+
*
|
|
127
|
+
* Deliberately narrow — it defers only when a Cursor gate is *registered*, not
|
|
128
|
+
* merely when Cursor is running, so the failure mode of a stale environment
|
|
129
|
+
* variable is a duplicate run (today's behavior) rather than no gate at all.
|
|
130
|
+
*/
|
|
131
|
+
function deferredToCursor(cwd, env) {
|
|
132
|
+
if (!env['CURSOR_PROJECT_DIR'])
|
|
133
|
+
return false;
|
|
134
|
+
try {
|
|
135
|
+
return readFileSync(join(cwd, CURSOR_HOOKS_FILE), 'utf8').includes(GATE_SCRIPT_SENTINEL);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The gate's `check`-script invocation for `pm` (the alias `agent-setup`
|
|
143
|
+
* ensures exists). The hook IS a gate, so it runs `--strict` (zero checks
|
|
144
|
+
* running is exit 2, not a pass — docs/contract.md) and `--digest` (the
|
|
145
|
+
* token-bounded failure excerpt is a far better landing spot for an agent than
|
|
146
|
+
* raw summary.json). npm alone needs `--` to reach the script with flags;
|
|
147
|
+
* pnpm/yarn/bun forward them directly.
|
|
148
|
+
*/
|
|
149
|
+
export function checkArgs(pm, profile = null) {
|
|
150
|
+
return [
|
|
151
|
+
'run',
|
|
152
|
+
CHECK_SCRIPT,
|
|
153
|
+
...(pm === 'npm' ? ['--'] : []),
|
|
154
|
+
'--strict',
|
|
155
|
+
'--digest',
|
|
156
|
+
...profileArgs(profile),
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The profile's flags, appended **after** the check script's own.
|
|
161
|
+
*
|
|
162
|
+
* Last wins in `parseArgs`, so a repo whose `check` script already carries
|
|
163
|
+
* `--only` has the profile override it rather than fight it — the profile is the
|
|
164
|
+
* more specific statement, made about this hook in particular.
|
|
165
|
+
*/
|
|
166
|
+
function profileArgs(profile) {
|
|
167
|
+
if (profile === null)
|
|
168
|
+
return [];
|
|
169
|
+
return [
|
|
170
|
+
...(profile.only && profile.only.length > 0 ? ['--only', profile.only.join(',')] : []),
|
|
171
|
+
...(profile.skip && profile.skip.length > 0 ? ['--skip', profile.skip.join(',')] : []),
|
|
172
|
+
...(profile.changed === true ? ['--changed'] : []),
|
|
173
|
+
];
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The repo's gate profile, or `null` when it configures none and the gate should
|
|
177
|
+
* run the whole check.
|
|
178
|
+
*
|
|
179
|
+
* A profile with nothing in it is `null` too: `"gate": {}` narrows nothing, and
|
|
180
|
+
* carrying it forward would put "not the full check" on a verdict that *was* the
|
|
181
|
+
* full check.
|
|
182
|
+
*/
|
|
183
|
+
export function gateProfile(cwd, config = loadConfig(cwd)) {
|
|
184
|
+
const profile = config?.gate;
|
|
185
|
+
if (!profile)
|
|
186
|
+
return null;
|
|
187
|
+
return profileArgs(profile).length > 0 ? profile : null;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* How a narrowed run must describe itself — appended to every verdict the gate
|
|
191
|
+
* produces while a profile is active.
|
|
192
|
+
*
|
|
193
|
+
* This is the price of the profile and it is not optional. A gate that runs two
|
|
194
|
+
* of eighteen slots and reports a bare `✔ green` has told the reader the work is
|
|
195
|
+
* done, which is exactly what it does not know. Naming the profile turns a
|
|
196
|
+
* silent partial pass into a stated one.
|
|
197
|
+
*/
|
|
198
|
+
function profileClause(profile) {
|
|
199
|
+
if (profile === null)
|
|
200
|
+
return null;
|
|
201
|
+
const parts = [
|
|
202
|
+
profile.only && profile.only.length > 0 ? `only ${profile.only.join(', ')}` : null,
|
|
203
|
+
profile.skip && profile.skip.length > 0 ? `without ${profile.skip.join(', ')}` : null,
|
|
204
|
+
profile.changed === true ? 'affected-only' : null,
|
|
205
|
+
].filter((p) => p !== null);
|
|
206
|
+
return `gate profile: ${parts.join(', ')} — NOT the full check`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Below this, naming the slowest check is noise rather than insight — every
|
|
210
|
+
* check in a fast repo is "the slowest" by some millisecond.
|
|
211
|
+
*/
|
|
212
|
+
const SLOWEST_FLOOR_MS = 1000;
|
|
213
|
+
/** The checks that actually ran, in report order. */
|
|
214
|
+
function ranChecks(checks) {
|
|
215
|
+
return checks.filter((c) => c.skipped !== true);
|
|
216
|
+
}
|
|
217
|
+
/** `15 checks, slowest test 21.4s` — what a green run is worth saying about itself. */
|
|
218
|
+
function greenDetail(ran) {
|
|
219
|
+
const count = `${ran.length} check${ran.length === 1 ? '' : 's'}`;
|
|
220
|
+
const slowest = ran.reduce((worst, c) => (worst === null || c.duration_ms > worst.duration_ms ? c : worst), null);
|
|
221
|
+
if (slowest === null || slowest.duration_ms < SLOWEST_FLOOR_MS)
|
|
222
|
+
return count;
|
|
223
|
+
return `${count}, slowest ${slowest.name} ${formatDuration(slowest.duration_ms)}`;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Is the summary on disk the one *this* run wrote?
|
|
227
|
+
*
|
|
228
|
+
* A gate that could not run wrote none, and a report that invents "0 checks
|
|
229
|
+
* failed" from a missing file is exactly the vacuous green the artifact exists
|
|
230
|
+
* to disprove. A summary older than `startedAt` belongs to a *previous* run and
|
|
231
|
+
* is dropped for the same reason — not hypothetical: a check script of the shape
|
|
232
|
+
* `tsc --build && checkride` leaves the summary untouched when the build fails,
|
|
233
|
+
* so trusting whatever is on disk would report the last run's failing slots as
|
|
234
|
+
* this one's, confidently and wrongly.
|
|
235
|
+
*/
|
|
236
|
+
function isFresh(read, startedAt) {
|
|
237
|
+
return read.state === 'ok' && read.mtimeMs >= startedAt;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* What the run itself says, read back out of a summary already known to be this
|
|
241
|
+
* run's, or `null` when there is none. The caller still has the elapsed time,
|
|
242
|
+
* which is true regardless.
|
|
243
|
+
*/
|
|
244
|
+
function runDetail(read, green) {
|
|
245
|
+
if (read.state !== 'ok')
|
|
246
|
+
return null;
|
|
247
|
+
const ran = ranChecks(read.summary.checks);
|
|
248
|
+
if (green)
|
|
249
|
+
return greenDetail(ran);
|
|
250
|
+
const failed = ran.filter((c) => !c.ok).map((c) => c.name);
|
|
251
|
+
// Red with nothing failing means the check script itself died (or --strict
|
|
252
|
+
// caught a vacuous run); say the honest thing rather than name no slots.
|
|
253
|
+
if (failed.length === 0)
|
|
254
|
+
return `${ran.length} checks ran, none failed — the check script itself failed`;
|
|
255
|
+
return `${failed.length} of ${ran.length} failed: ${failed.join(', ')}`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The one line a human reads to know the gate ran, what it decided, and how long
|
|
259
|
+
* they waited on it.
|
|
260
|
+
*
|
|
261
|
+
* The elapsed time is the gate's own wall clock — package-manager startup,
|
|
262
|
+
* incremental build and all — not the pipeline's `total_duration_ms`, because
|
|
263
|
+
* the honest answer to "why did that pause" is the whole pause, not the part
|
|
264
|
+
* checkride chooses to measure.
|
|
265
|
+
*/
|
|
266
|
+
function headline(verdict, elapsedMs, detail) {
|
|
267
|
+
return `checkride ${verdict} in ${formatDuration(elapsedMs)}${detail === null ? '' : ` — ${detail}`}`;
|
|
268
|
+
}
|
|
269
|
+
/** Join the run's own detail with the narrowing note, dropping whichever is absent. */
|
|
270
|
+
function detailWith(detail, profile) {
|
|
271
|
+
const parts = [detail, profileClause(profile)].filter((p) => p !== null);
|
|
272
|
+
return parts.length === 0 ? null : parts.join(' — ');
|
|
273
|
+
}
|
|
274
|
+
/** The sentence a red gate hands the agent, naming what to open first. */
|
|
275
|
+
function redMessage(cwd, pm, profile) {
|
|
276
|
+
const where = existsSync(join(cwd, '.check', DIGEST_FILE)) ? `.check/${DIGEST_FILE}` : SUMMARY_PATH;
|
|
277
|
+
// A narrowed red is still a red, but it is not the whole story: the slots the
|
|
278
|
+
// profile skipped were never asked, so fixing what it names may not be enough.
|
|
279
|
+
const narrowed = profile === null
|
|
280
|
+
? ''
|
|
281
|
+
: ` This gate ran a profile, not the full check — run \`${runScript(pm, CHECK_SCRIPT)}\` for the rest.`;
|
|
282
|
+
return (`checkride: the gate is red — read ${where}, fix the failing slot, then finish ` +
|
|
283
|
+
`(do not stop while checkride is red). Run \`${pm} exec checkride triage\` for full triage.${narrowed}`);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* The Node half of a refusal message: what this process is running, what the
|
|
287
|
+
* repo asked for, and the one lever that closes the gap.
|
|
288
|
+
*
|
|
289
|
+
* This clause is why the whole classification exists. The default cause of a
|
|
290
|
+
* refused launch is that the harness ran its hook in a non-login shell, so the
|
|
291
|
+
* hook got the machine's default interpreter rather than the contributor's —
|
|
292
|
+
* a fact no amount of reading `.check/` would ever reveal.
|
|
293
|
+
*/
|
|
294
|
+
function nodeClause(alignment, running) {
|
|
295
|
+
const context = 'Agent harnesses run hooks in a non-login shell, so a hook gets the machine’s default ' +
|
|
296
|
+
'Node rather than the one your terminal has.';
|
|
297
|
+
const pin = alignment?.pin ?? null;
|
|
298
|
+
if (pin === null) {
|
|
299
|
+
return (`${context} This process is on Node ${running} and the repo pins no ` +
|
|
300
|
+
`\`.nvmrc\`/\`.node-version\` for checkride to align to; add one, or set ${NODE_BIN_VAR} to the ` +
|
|
301
|
+
'bin directory of a Node the repo accepts.');
|
|
302
|
+
}
|
|
303
|
+
return (`${context} This one is on Node ${running}, the repo pins ${pin.version} ` +
|
|
304
|
+
`(${pin.file}), and no matching install was found in a known version-manager layout — ` +
|
|
305
|
+
`so checkride could not align to it. Install that Node, or set ${NODE_BIN_VAR} to its bin directory.`);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* The verdict for a package manager that refused to start the check script.
|
|
309
|
+
*
|
|
310
|
+
* It says the three things a red verdict would get wrong here: nothing ran, so
|
|
311
|
+
* `.check/` describes some earlier turn and not this one; the code is not what
|
|
312
|
+
* failed, so no edit will clear it; and here is the actual cause. A gate that
|
|
313
|
+
* sent a reader to `summary.json` for a run that never happened is the confusion
|
|
314
|
+
* this replaces.
|
|
315
|
+
*/
|
|
316
|
+
function refusalMessage(cause, context) {
|
|
317
|
+
return [
|
|
318
|
+
`checkride: the gate could not run — ${cause}.`,
|
|
319
|
+
'Nothing ran: no check executed and no artifact was written, so `.check/` holds nothing from ' +
|
|
320
|
+
'this turn and the code is not what to look at.',
|
|
321
|
+
nodeClause(context.alignment, context.running),
|
|
322
|
+
`Run \`${context.pm} run check\` in a terminal to see the same failure directly.`,
|
|
323
|
+
].join(' ');
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Put the pinned Node in front of the child's `PATH`, and say so on stderr.
|
|
327
|
+
*
|
|
328
|
+
* Never silent, by rule: which interpreter the whole pipeline runs on is not
|
|
329
|
+
* something to change behind the reader's back, and the line is what turns a
|
|
330
|
+
* mysteriously-different result into an explained one. It goes to stderr with
|
|
331
|
+
* the rest of the run's progress rather than into the one-line verdict, so it is
|
|
332
|
+
* present on a red run too — which is exactly when it is worth knowing.
|
|
333
|
+
*
|
|
334
|
+
* Returns `env` unchanged when there is nothing to align, which is every repo
|
|
335
|
+
* that pins nothing and every hook that already arrived on the right Node.
|
|
336
|
+
*/
|
|
337
|
+
function announceAlignment(alignment, env, stderr) {
|
|
338
|
+
if (alignment === null)
|
|
339
|
+
return env;
|
|
340
|
+
const { bin } = alignment;
|
|
341
|
+
if (bin === null)
|
|
342
|
+
return env;
|
|
343
|
+
const target = alignment.version ?? alignment.pin?.version ?? 'the pinned version';
|
|
344
|
+
const why = alignment.pin === null ? NODE_BIN_VAR : `${alignment.pin.file} pins ${alignment.pin.version}`;
|
|
345
|
+
stderr.write(`checkride: running the check on Node ${target} from ${bin} (${why}; this hook started on ${alignment.running}).\n`);
|
|
346
|
+
return withNodeBin(env, bin);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Classify a non-green run and phrase it.
|
|
350
|
+
*
|
|
351
|
+
* The one judgement here is whether the pipeline ran. A summary from this run
|
|
352
|
+
* proves it did, so on that branch nothing in the output can mean otherwise —
|
|
353
|
+
* the guard that keeps a check which merely *printed* a package-manager error
|
|
354
|
+
* code from being read as a launch refusal.
|
|
355
|
+
*/
|
|
356
|
+
function failedVerdict(f) {
|
|
357
|
+
const refusal = f.fresh ? null : launchRefusal(f.output);
|
|
358
|
+
if (refusal === null) {
|
|
359
|
+
return {
|
|
360
|
+
status: headline('✘ red', f.elapsedMs, detailWith(f.fresh ? runDetail(f.summary, false) : null, f.profile)),
|
|
361
|
+
instruction: redMessage(f.cwd, f.pm, f.profile),
|
|
362
|
+
refusal: null,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// A launch refusal names no profile: nothing ran, so how much would have run
|
|
366
|
+
// is beside the point.
|
|
367
|
+
return {
|
|
368
|
+
status: headline('⚠ could not run', f.elapsedMs, refusal.cause),
|
|
369
|
+
instruction: refusalMessage(refusal.cause, { alignment: f.alignment, running: f.running, pm: f.pm }),
|
|
370
|
+
refusal: refusal.cause,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Answer a green run in `harness`'s protocol.
|
|
375
|
+
*
|
|
376
|
+
* Claude Code gets `{"systemMessage": …}`, which it shows the user. Without it a
|
|
377
|
+
* green gate is completely silent: the turn simply takes a minute longer than it
|
|
378
|
+
* should have, with nothing on screen to say a full pipeline just ran, which is
|
|
379
|
+
* indistinguishable from a hung model.
|
|
380
|
+
*
|
|
381
|
+
* Cursor gets nothing, and cannot. Its stop hook accepts one field —
|
|
382
|
+
* `followup_message` — and that field *submits a new turn*, so using it to
|
|
383
|
+
* announce a pass would put the agent back to work every time it succeeded. No
|
|
384
|
+
* other channel is documented for the event; see docs/cursor.md.
|
|
385
|
+
*/
|
|
386
|
+
function reportGreen(harness, stdout, status) {
|
|
387
|
+
if (harness === 'cursor')
|
|
388
|
+
return;
|
|
389
|
+
stdout.write(`${JSON.stringify({ systemMessage: status })}\n`);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Answer a run that was not green in `harness`'s protocol — a pipeline red or a
|
|
393
|
+
* launch refusal alike, since both must block and the wire format is the same.
|
|
394
|
+
*
|
|
395
|
+
* Claude Code gets both spellings of the same verdict, because which one lands
|
|
396
|
+
* depends on the hook script the repo happens to have. The JSON body
|
|
397
|
+
* (`decision: "block"`) is what a current script forwards on its own exit 0 —
|
|
398
|
+
* Claude Code parses hook JSON *only* on exit 0, and that body is the one form
|
|
399
|
+
* that can carry a user-visible `systemMessage` alongside the block. The stderr
|
|
400
|
+
* line is what a script generated before that change relies on, where the block
|
|
401
|
+
* rides on exit 2 and stdout is ignored. Emitting both is what lets an
|
|
402
|
+
* already-installed hook script keep gating across the upgrade.
|
|
403
|
+
*
|
|
404
|
+
* Cursor gets `{"followup_message": …}` on stdout and **exit 0**: it reads a
|
|
405
|
+
* non-zero stop hook as a *failed hook* and lets the turn end anyway, so the
|
|
406
|
+
* verdict cannot ride on the exit code. Cursor caps those auto-followups at five
|
|
407
|
+
* per script by default; the hook entry `agent-setup` writes opts out
|
|
408
|
+
* (`loop_limit: null`), because a gate that stops replying after five turns is
|
|
409
|
+
* not a gate.
|
|
410
|
+
*/
|
|
411
|
+
function reportRed(harness, out, verdict) {
|
|
412
|
+
// The agent is told what to open and what not to do; the user is told what
|
|
413
|
+
// happened and how long it took. Handing the user the agent's marching orders
|
|
414
|
+
// would bury the one line they are actually reading.
|
|
415
|
+
const full = `${verdict.status}\n${verdict.instruction}`;
|
|
416
|
+
if (harness === 'cursor') {
|
|
417
|
+
out.stdout.write(`${JSON.stringify({ followup_message: full })}\n`);
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
420
|
+
out.stdout.write(`${JSON.stringify({ decision: 'block', reason: full, systemMessage: verdict.status })}\n`);
|
|
421
|
+
out.stderr.write(`${full}\n`);
|
|
422
|
+
return 2;
|
|
423
|
+
}
|
|
424
|
+
function gateContext(options) {
|
|
425
|
+
return {
|
|
426
|
+
cwd: options.cwd ?? process.cwd(),
|
|
427
|
+
harness: options.harness ?? 'claude',
|
|
428
|
+
stdout: options.stdout ?? process.stdout,
|
|
429
|
+
stderr: options.stderr ?? process.stderr,
|
|
430
|
+
run: options.spawn ?? spawnForward,
|
|
431
|
+
env: options.env ?? process.env,
|
|
432
|
+
pinEnv: options.pinEnv ?? realPinEnv,
|
|
433
|
+
now: options.now ?? Date.now,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Run the gate and answer in `harness`'s protocol.
|
|
438
|
+
*
|
|
439
|
+
* The exit code is unchanged and stays as `docs/contract.md` promises it: 2 while
|
|
440
|
+
* blocked under `--harness claude`, always 0 under `--harness cursor`. What each
|
|
441
|
+
* harness *displays* is carried by the JSON body on stdout — see
|
|
442
|
+
* {@link reportGreen} and {@link reportRed} — because that is the only channel
|
|
443
|
+
* either harness renders for a human.
|
|
444
|
+
*/
|
|
445
|
+
export async function runGate(options = {}) {
|
|
446
|
+
const { cwd, harness, stdout, stderr, run, env, pinEnv, now } = gateContext(options);
|
|
447
|
+
const marker = join(cwd, DIRTY_MARKER);
|
|
448
|
+
// Cursor running this repo's Claude hooks alongside its own. Stand down before
|
|
449
|
+
// touching the marker: the native Cursor gate owns both the verdict and the
|
|
450
|
+
// marker for this turn.
|
|
451
|
+
if (harness === 'claude' && deferredToCursor(cwd, env)) {
|
|
452
|
+
return { exitCode: 0, ran: false, green: true, refusal: null };
|
|
453
|
+
}
|
|
454
|
+
// No edit marker → this turn touched no files → nothing to gate. Stop hooks
|
|
455
|
+
// fire on every turn, including pure-conversation ones; without this the gate
|
|
456
|
+
// taxes every reply with a full pipeline run.
|
|
457
|
+
if (options.ifDirty === true && !existsSync(marker)) {
|
|
458
|
+
return { exitCode: 0, ran: false, green: true, refusal: null };
|
|
459
|
+
}
|
|
460
|
+
const pm = options.pm ?? detectPackageManager({ cwd });
|
|
461
|
+
const profile = options.profile !== undefined ? options.profile : gateProfile(cwd);
|
|
462
|
+
const alignment = alignNode(cwd, pinEnv);
|
|
463
|
+
const childEnv = announceAlignment(alignment, env, stderr);
|
|
464
|
+
const startedAt = now();
|
|
465
|
+
const output = recording(stderr);
|
|
466
|
+
const code = await run(pm, checkArgs(pm, profile), { cwd, stderr: output, env: childEnv });
|
|
467
|
+
const green = code === 0;
|
|
468
|
+
const summary = await readSummary(cwd);
|
|
469
|
+
const fresh = isFresh(summary, startedAt);
|
|
470
|
+
if (green) {
|
|
471
|
+
rmSync(marker, { force: true });
|
|
472
|
+
const detail = detailWith(fresh ? runDetail(summary, true) : null, profile);
|
|
473
|
+
reportGreen(harness, stdout, headline('✔ green', now() - startedAt, detail));
|
|
474
|
+
return { exitCode: 0, ran: true, green: true, refusal: null };
|
|
475
|
+
}
|
|
476
|
+
const verdict = failedVerdict({
|
|
477
|
+
cwd,
|
|
478
|
+
pm,
|
|
479
|
+
elapsedMs: now() - startedAt,
|
|
480
|
+
summary,
|
|
481
|
+
fresh,
|
|
482
|
+
output: output.text(),
|
|
483
|
+
alignment,
|
|
484
|
+
running: pinEnv.running(),
|
|
485
|
+
profile,
|
|
486
|
+
});
|
|
487
|
+
const exitCode = reportRed(harness, { stdout, stderr }, verdict);
|
|
488
|
+
return { exitCode, ran: true, green: false, refusal: verdict.refusal };
|
|
489
|
+
}
|
|
490
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAoB,MAAM,sBAAsB,CAAC;AAErF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,EACL,oBAAoB,EACpB,aAAa,EAEb,SAAS,EACT,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;AAE5C,uEAAuE;AACvE,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAEjD,iEAAiE;AACjE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAG3D,4EAA4E;AAC5E,MAAM,YAAY,GAAG,OAAO,CAAC;AAmB7B,MAAM,YAAY,GAAc,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CACtD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACtB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE;QACrC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;KACtD,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACpE,2EAA2E;IAC3E,2EAA2E;IAC3E,yEAAyE;IACzE,yEAAyE;IACzE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,mBAAmB,MAAM,OAAO,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEL;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,CAAC;AAE/B,qEAAqE;AACrE,SAAS,SAAS,CAAC,GAAQ;IACzB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,OAAO;QACL,KAAK,EAAE,CAAC,KAAa,EAAE,EAAE;YACvB,IAAI,IAAI,CAAC,MAAM,GAAG,aAAa;gBAAE,IAAI,IAAI,KAAK,CAAC;YAC/C,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI;KACjB,CAAC;AACJ,CAAC;AAsCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,gBAAgB,CAAC,GAAW,EAAE,GAAuC;IAC5E,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAC3F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,EAAkB,EAAE,UAA8B,IAAI;IAC9E,OAAO;QACL,KAAK;QACL,YAAY;QACZ,GAAG,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,UAAU;QACV,UAAU;QACV,GAAG,WAAW,CAAC,OAAO,CAAC;KACxB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,OAA2B;IAC9C,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO;QACL,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,SAAiC,UAAU,CAAC,GAAG,CAAC;IACvF,MAAM,OAAO,GAAG,MAAM,EAAE,IAAI,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,OAA2B;IAChD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,KAAK,GAAG;QACZ,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QAClF,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QACrF,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;KAClD,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACzC,OAAO,iBAAiB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,qDAAqD;AACrD,SAAS,SAAS,CAAC,MAA+B;IAChD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,uFAAuF;AACvF,SAAS,WAAW,CAAC,GAA4B;IAC/C,MAAM,KAAK,GAAG,GAAG,GAAG,CAAC,MAAM,SAAS,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IAClE,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CACxB,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAC/E,IAAI,CACL,CAAC;IACF,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,WAAW,GAAG,gBAAgB;QAAE,OAAO,KAAK,CAAC;IAC7E,OAAO,GAAG,KAAK,aAAa,OAAO,CAAC,IAAI,IAAI,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;AACpF,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,OAAO,CAAC,IAAiB,EAAE,SAAiB;IACnD,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,SAAS,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,IAAiB,EAAE,KAAc;IAClD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,KAAK;QAAE,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,2EAA2E;IAC3E,yEAAyE;IACzE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,CAAC,MAAM,2DAA2D,CAAC;IACzG,OAAO,GAAG,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAM,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,QAAQ,CAAC,OAAe,EAAE,SAAiB,EAAE,MAAqB;IACzE,OAAO,aAAa,OAAO,OAAO,cAAc,CAAC,SAAS,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,EAAE,CAAC;AACxG,CAAC;AAED,uFAAuF;AACvF,SAAS,UAAU,CAAC,MAAqB,EAAE,OAA2B;IACpE,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACtF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,0EAA0E;AAC1E,SAAS,UAAU,CAAC,GAAW,EAAE,EAAkB,EAAE,OAA2B;IAC9E,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,WAAW,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;IACpG,8EAA8E;IAC9E,+EAA+E;IAC/E,MAAM,QAAQ,GACZ,OAAO,KAAK,IAAI;QACd,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,wDAAwD,SAAS,CAAC,EAAE,EAAE,YAAY,CAAC,kBAAkB,CAAC;IAC5G,OAAO,CACL,qCAAqC,KAAK,sCAAsC;QAChF,+CAA+C,EAAE,4CAA4C,QAAQ,EAAE,CACxG,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,SAA+B,EAAE,OAAe;IAClE,MAAM,OAAO,GACX,uFAAuF;QACvF,6CAA6C,CAAC;IAChD,MAAM,GAAG,GAAG,SAAS,EAAE,GAAG,IAAI,IAAI,CAAC;IACnC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,CACL,GAAG,OAAO,4BAA4B,OAAO,wBAAwB;YACrE,2EAA2E,YAAY,UAAU;YACjG,2CAA2C,CAC5C,CAAC;IACJ,CAAC;IACD,OAAO,CACL,GAAG,OAAO,wBAAwB,OAAO,mBAAmB,GAAG,CAAC,OAAO,GAAG;QAC1E,IAAI,GAAG,CAAC,IAAI,2EAA2E;QACvF,iEAAiE,YAAY,wBAAwB,CACtG,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,cAAc,CACrB,KAAa,EACb,OAAiF;IAEjF,OAAO;QACL,uCAAuC,KAAK,GAAG;QAC/C,8FAA8F;YAC5F,gDAAgD;QAClD,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC;QAC9C,SAAS,OAAO,CAAC,EAAE,8DAA8D;KAClF,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CACxB,SAA+B,EAC/B,GAAuC,EACvC,MAAW;IAEX,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC;IAC1B,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,GAAG,EAAE,OAAO,IAAI,oBAAoB,CAAC;IACnF,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,SAAS,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC1G,MAAM,CAAC,KAAK,CAAC,wCAAwC,MAAM,SAAS,GAAG,KAAK,GAAG,0BAA0B,SAAS,CAAC,OAAO,MAAM,CAAC,CAAC;IAClI,OAAO,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAgBD;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,CAAU;IAC/B,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YAC3G,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;YAC/C,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IACD,6EAA6E;IAC7E,uBAAuB;IACvB,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC;QAC/D,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;QACpG,OAAO,EAAE,OAAO,CAAC,KAAK;KACvB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,WAAW,CAAC,OAAoB,EAAE,MAAW,EAAE,MAAc;IACpE,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO;IACjC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,SAAS,CAChB,OAAoB,EACpB,GAAiC,EACjC,OAAgD;IAEhD,2EAA2E;IAC3E,8EAA8E;IAC9E,qDAAqD;IACrD,MAAM,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;IACzD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;QACpE,OAAO,CAAC,CAAC;IACX,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,KAAK,CACd,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAC1F,CAAC;IACF,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC;AACX,CAAC;AAkBD,SAAS,WAAW,CAAC,OAAoB;IACvC,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QACjC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,QAAQ;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;QACxC,GAAG,EAAE,OAAO,CAAC,KAAK,IAAI,YAAY;QAClC,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG;QAC/B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,UAAU;QACpC,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;KAC7B,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,UAAuB,EAAE;IACrD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEvC,+EAA+E;IAC/E,4EAA4E;IAC5E,wBAAwB;IACxB,IAAI,OAAO,KAAK,QAAQ,IAAI,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACjE,CAAC;IAED,4EAA4E;IAC5E,8EAA8E;IAC9E,8CAA8C;IAC9C,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACjE,CAAC;IAED,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,oBAAoB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnF,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAE1C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5E,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7E,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,CAAC;QAC5B,GAAG;QACH,EAAE;QACF,SAAS,EAAE,GAAG,EAAE,GAAG,SAAS;QAC5B,OAAO;QACP,KAAK;QACL,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;QACrB,SAAS;QACT,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;QACzB,OAAO;KACR,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;IACjE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
* it afterward.
|
|
13
13
|
*/
|
|
14
14
|
import type { Adapter, Slot } from './adapters.js';
|
|
15
|
+
import type { HookName } from './agent-setup/index.js';
|
|
16
|
+
import type { HarnessName } from './gate.js';
|
|
15
17
|
import type { Out } from './orchestrator.js';
|
|
18
|
+
import type { PackageManager } from './pm/index.js';
|
|
16
19
|
export type Shape = 'flat' | 'monorepo' | 'hybrid';
|
|
17
20
|
export type InitOptions = {
|
|
18
21
|
cwd?: string;
|
|
@@ -25,16 +28,27 @@ export type InitOptions = {
|
|
|
25
28
|
add?: string[] | null;
|
|
26
29
|
checkrideSpec?: string;
|
|
27
30
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* existing mode
|
|
31
|
+
* Overwrite instead of refusing. Without it, `init` refuses (exit 2) rather
|
|
32
|
+
* than clobber any file new mode would scaffold, or an AGENTS.md stanza
|
|
33
|
+
* existing mode would refresh that carries local edits. Existing mode is
|
|
34
|
+
* otherwise additive-only, so the flag reaches nothing else there.
|
|
31
35
|
*/
|
|
32
36
|
force?: boolean;
|
|
33
37
|
/**
|
|
34
|
-
* Write the
|
|
38
|
+
* Write the agent stop-gate hooks into each selected harness's config.
|
|
35
39
|
* Opt-out: defaults to on; `--no-hook` sets it `false`.
|
|
36
40
|
*/
|
|
37
41
|
hook?: boolean;
|
|
42
|
+
/** Which hooks to write (`--hook <a,b>`). Omitted → all of them. */
|
|
43
|
+
hooks?: readonly HookName[];
|
|
44
|
+
/**
|
|
45
|
+
* Hooks to tear out of an already-wired repo (`--remove-hook <a,b>`): the
|
|
46
|
+
* config entry is stripped and the generated script deleted. Composes with
|
|
47
|
+
* `hook: false` to remove without refreshing anything else.
|
|
48
|
+
*/
|
|
49
|
+
removeHooks?: readonly HookName[];
|
|
50
|
+
/** Which harnesses to write them for (`--harness <a,b>`). Omitted → detected. */
|
|
51
|
+
harnesses?: readonly HarnessName[];
|
|
38
52
|
stdout?: Out;
|
|
39
53
|
slots?: readonly Slot[];
|
|
40
54
|
adapters?: readonly Adapter[];
|
|
@@ -42,7 +56,7 @@ export type InitOptions = {
|
|
|
42
56
|
probeFailures?: (slots: string[], cwd: string) => Promise<string[]>;
|
|
43
57
|
/**
|
|
44
58
|
* Existing mode: grandfather current debt into `checkride.baseline.json`
|
|
45
|
-
* instead of disabling failing slots (`--baseline
|
|
59
|
+
* instead of disabling failing slots (`--baseline`). A failing
|
|
46
60
|
* fingerprintable slot stays enabled and is masked by the baseline; a failing
|
|
47
61
|
* slot with no extractor still falls back to a `false` disable.
|
|
48
62
|
*/
|
|
@@ -54,6 +68,8 @@ export type InitResult = {
|
|
|
54
68
|
mode: 'new' | 'existing';
|
|
55
69
|
shape: Shape | null;
|
|
56
70
|
written: string[];
|
|
71
|
+
/** Files deleted by `--remove-hook`. */
|
|
72
|
+
removed: string[];
|
|
57
73
|
skipped: string[];
|
|
58
74
|
disabled: string[];
|
|
59
75
|
/** Slots whose failing debt was grandfathered into the baseline (`--baseline`). */
|
|
@@ -65,11 +81,37 @@ export type InventoryEntry = {
|
|
|
65
81
|
status: 'adopted' | 'empty';
|
|
66
82
|
adapter: string | null;
|
|
67
83
|
};
|
|
68
|
-
/**
|
|
69
|
-
|
|
84
|
+
/**
|
|
85
|
+
* How the stanza on disk relates to the one checkride would write:
|
|
86
|
+
*
|
|
87
|
+
* - `absent` — no stanza yet; writing one takes nothing away.
|
|
88
|
+
* - `pristine` — checkride's own output, untouched; refreshing it is lossless.
|
|
89
|
+
* Either it carries a stamp that still matches, or it predates stamping and
|
|
90
|
+
* its wording is one checkride shipped (see {@link LEGACY_STANZA_PREAMBLES}).
|
|
91
|
+
* - `edited` — stamped, but the body no longer matches its stamp: a human or an
|
|
92
|
+
* agent customized it, and a blind refresh would silently discard that.
|
|
93
|
+
* - `unstamped` — predates stamping and matches no wording checkride released,
|
|
94
|
+
* so it has been edited too; same protection as `edited`, and the message says
|
|
95
|
+
* so differently because the evidence is weaker.
|
|
96
|
+
*/
|
|
97
|
+
export type StanzaState = 'absent' | 'pristine' | 'edited' | 'unstamped';
|
|
98
|
+
/** Classify the stanza in `content` against the `body` checkride would write. */
|
|
99
|
+
export declare function inspectStanza(content: string, body: string): StanzaState;
|
|
100
|
+
/**
|
|
101
|
+
* The agent-facing contract block, parameterized by the active checks and the
|
|
102
|
+
* repo's package manager.
|
|
103
|
+
*
|
|
104
|
+
* `pm` is not cosmetic. The stanza is a list of commands an agent is told to
|
|
105
|
+
* run, and `pnpm check` in an npm repo is an instruction that cannot succeed —
|
|
106
|
+
* the agent either invents a substitute or reports the pipeline as broken.
|
|
107
|
+
* Everything here routes through {@link runScript}/{@link execCommand} for that
|
|
108
|
+
* reason; nothing spells a launcher literally.
|
|
109
|
+
*/
|
|
110
|
+
export declare function buildStanza(activeSlots: readonly string[], pm?: PackageManager): string;
|
|
70
111
|
/**
|
|
71
112
|
* Insert or refresh the checkride stanza in an AGENTS.md body. Idempotent:
|
|
72
|
-
* applying twice yields identical output.
|
|
113
|
+
* applying twice yields identical output. It rewrites the marked region without
|
|
114
|
+
* looking at what was there — callers gate on {@link inspectStanza} first.
|
|
73
115
|
*/
|
|
74
116
|
export declare function applyStanza(content: string, body: string): string;
|
|
75
117
|
/** For each default (non-opt-in) slot, report whether a tool config is present. */
|
|
@@ -86,23 +128,41 @@ export declare function runInit(options: InitOptions): Promise<InitResult>;
|
|
|
86
128
|
export type AgentSetupOptions = {
|
|
87
129
|
cwd?: string;
|
|
88
130
|
dryRun?: boolean;
|
|
89
|
-
/** Write the
|
|
131
|
+
/** Write the agent hooks (opt-out; `--no-hook`). Defaults to on. */
|
|
90
132
|
hook?: boolean;
|
|
133
|
+
/** Which hooks to write (`--hook <a,b>`). Omitted → all of them. */
|
|
134
|
+
hooks?: readonly HookName[];
|
|
135
|
+
/**
|
|
136
|
+
* Hooks to tear out (`--remove-hook <a,b>`): the config entry is stripped and
|
|
137
|
+
* the generated script deleted. With `hook: false` it removes those and
|
|
138
|
+
* refreshes nothing else.
|
|
139
|
+
*/
|
|
140
|
+
removeHooks?: readonly HookName[];
|
|
141
|
+
/** Which harnesses to write them for (`--harness <a,b>`). Omitted → detected. */
|
|
142
|
+
harnesses?: readonly HarnessName[];
|
|
143
|
+
/**
|
|
144
|
+
* Refresh the AGENTS.md stanza even when it carries local edits. Without it a
|
|
145
|
+
* customized stanza stops the run (exit 2) instead of being overwritten.
|
|
146
|
+
*/
|
|
147
|
+
force?: boolean;
|
|
91
148
|
stdout?: Out;
|
|
92
149
|
slots?: readonly Slot[];
|
|
93
150
|
adapters?: readonly Adapter[];
|
|
94
151
|
};
|
|
95
152
|
export type AgentSetupResult = {
|
|
96
153
|
written: string[];
|
|
154
|
+
/** Files deleted by `--remove-hook`. */
|
|
155
|
+
removed: string[];
|
|
97
156
|
skipped: string[];
|
|
98
157
|
exitCode: number;
|
|
99
158
|
};
|
|
100
159
|
/**
|
|
101
160
|
* `checkride agent-setup` — wire the agent contract into an *existing* repo
|
|
102
161
|
* without a full `init`: the `check` alias the hook resolves to, the AGENTS.md
|
|
103
|
-
* stanza (the human-readable contract),
|
|
104
|
-
* mechanical gate)
|
|
105
|
-
*
|
|
162
|
+
* stanza (the human-readable contract), the stop-gate hooks for every detected
|
|
163
|
+
* harness (the mechanical gate), and the skills a harness cannot get from the
|
|
164
|
+
* bundled plugin. Every write is additive and idempotent — a second run is a
|
|
165
|
+
* no-op — and the hooks are opt-out (`hook: false`).
|
|
106
166
|
*/
|
|
107
167
|
export declare function runAgentSetup(options: AgentSetupOptions): Promise<AgentSetupResult>;
|
|
108
168
|
//# sourceMappingURL=init.d.ts.map
|