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,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cursor hook writer — `.cursor/hooks.json` plus the scripts its entries
|
|
3
|
+
* invoke.
|
|
4
|
+
*
|
|
5
|
+
* Same contract as the Claude writer (`./claude.ts`): sentinel-keyed identity,
|
|
6
|
+
* in-place refresh, every unrelated key preserved, re-applying is a no-op. The
|
|
7
|
+
* schema is what differs, and it differs enough that sharing the merge would
|
|
8
|
+
* cost more than it saved:
|
|
9
|
+
*
|
|
10
|
+
* - Cursor requires a top-level `"version": 1`.
|
|
11
|
+
* - An event maps to a flat list of `{command, matcher?, timeout?}` entries —
|
|
12
|
+
* there is no group wrapper and no `type` discriminator, so a hook is one
|
|
13
|
+
* object where Claude Code needs two levels.
|
|
14
|
+
* - The events are lowercase and differently cut: `stop`, `afterFileEdit`
|
|
15
|
+
* (purpose-built, no matcher) and `preToolUse` (matched against Cursor's own
|
|
16
|
+
* tool names — `Shell`, `Read`, `Write`, `Grep`, `Delete`, `Task`, `MCP:*`).
|
|
17
|
+
*
|
|
18
|
+
* The gate entry carries three fields the Claude writer has no equivalent for —
|
|
19
|
+
* `timeout`, `loop_limit` and `failClosed` — because Cursor's defaults for all
|
|
20
|
+
* three end a turn that checkride would keep blocking. See {@link hookSpecs}.
|
|
21
|
+
*/
|
|
22
|
+
import { CURSOR_HOOKS_FILE } from '../gate.js';
|
|
23
|
+
import { GATE_TIMEOUT_SECONDS, HOOK_NAMES, writeHarnessHooks } from './harness.js';
|
|
24
|
+
/**
|
|
25
|
+
* Project-shared Cursor hook config (committed), relative to the repo root.
|
|
26
|
+
* Declared in `../gate.js`, which reads it to detect a native Cursor gate, and
|
|
27
|
+
* re-exported here so this module stays the one place a caller looks for it.
|
|
28
|
+
*/
|
|
29
|
+
export { CURSOR_HOOKS_FILE };
|
|
30
|
+
const GATE_SCRIPT_FILE = '.cursor/hooks/checkride-gate.sh';
|
|
31
|
+
const DIRTY_SCRIPT_FILE = '.cursor/hooks/checkride-dirty.sh';
|
|
32
|
+
const PROTECT_SCRIPT_FILE = '.cursor/hooks/checkride-protect.cjs';
|
|
33
|
+
const HARNESS = 'cursor';
|
|
34
|
+
/** The schema version Cursor's loader expects at the top of the file. */
|
|
35
|
+
const SCHEMA_VERSION = 1;
|
|
36
|
+
/**
|
|
37
|
+
* `sh` (not the exec bit) runs the script so a clone that lost file modes still
|
|
38
|
+
* gates; `CURSOR_PROJECT_DIR` anchors it when the session cwd is a subdirectory.
|
|
39
|
+
*
|
|
40
|
+
* **Unverified against a live Cursor.** Cursor documents `command` as "a shell
|
|
41
|
+
* string, an absolute path, or a relative path", but every documented example is
|
|
42
|
+
* a bare path, and it states separately that project hooks already run from the
|
|
43
|
+
* project root. If Cursor ever spawns these without a shell, `${…}` stays
|
|
44
|
+
* literal, the spawn fails, and Cursor's fail-open default ends the turn with no
|
|
45
|
+
* signal at all — the one failure mode `failClosed` cannot cover, because the
|
|
46
|
+
* hook it would guard never started. `failClosed` is set regardless; if that
|
|
47
|
+
* turns out not to catch it, drop both the interpreter and the variable and
|
|
48
|
+
* write the bare `.cursor/hooks/checkride-gate.sh` the docs show, accepting the
|
|
49
|
+
* dependence on the exec bit. See docs/cursor.md.
|
|
50
|
+
*/
|
|
51
|
+
const run = (interpreter, file) => `${interpreter} "\${CURSOR_PROJECT_DIR:-.}/${file}"`;
|
|
52
|
+
/**
|
|
53
|
+
* Cursor's file-mutating tool names. There is no separate `Edit` tool — `Write`
|
|
54
|
+
* covers both creating and modifying — and `Delete` can remove an accounting
|
|
55
|
+
* file just as effectively as a write can overwrite it.
|
|
56
|
+
*/
|
|
57
|
+
const EDIT_TOOLS = 'Write|Delete';
|
|
58
|
+
/**
|
|
59
|
+
* Cursor's defaults are tuned for observers; the gate is not one, so all three
|
|
60
|
+
* of its non-command fields are set against the default rather than with it:
|
|
61
|
+
*
|
|
62
|
+
* - `timeout` — a full pipeline run is minutes, and the platform default is not.
|
|
63
|
+
* - `loop_limit: null` — the default of **5** stops the auto-followup after five
|
|
64
|
+
* turns, at which point a red repo finishes anyway. Claude Code re-blocks
|
|
65
|
+
* indefinitely; without this the two harnesses are not the same gate, and the
|
|
66
|
+
* Cursor one degrades into five nudges.
|
|
67
|
+
* - `failClosed: true` — Cursor's default is fail-*open*: a hook that crashes,
|
|
68
|
+
* times out, or emits unparseable JSON lets the turn end silently. That is the
|
|
69
|
+
* vacuous green the whole contract exists to prevent (docs/contract.md).
|
|
70
|
+
*
|
|
71
|
+
* The two guards keep Cursor's fail-open default on purpose — see `dirty` and
|
|
72
|
+
* `protect` in `./scripts.ts` for why each must never block an edit.
|
|
73
|
+
*/
|
|
74
|
+
function hookSpecs() {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
name: 'gate',
|
|
78
|
+
event: 'stop',
|
|
79
|
+
timeout: GATE_TIMEOUT_SECONDS,
|
|
80
|
+
loopLimit: null,
|
|
81
|
+
failClosed: true,
|
|
82
|
+
sentinels: [GATE_SCRIPT_FILE],
|
|
83
|
+
command: run('sh', GATE_SCRIPT_FILE),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
// `afterFileEdit` rather than `postToolUse`: it is purpose-built for "a
|
|
87
|
+
// file changed", needs no matcher, and so cannot drift when Cursor renames
|
|
88
|
+
// a tool.
|
|
89
|
+
name: 'dirty',
|
|
90
|
+
event: 'afterFileEdit',
|
|
91
|
+
sentinels: [DIRTY_SCRIPT_FILE],
|
|
92
|
+
command: run('sh', DIRTY_SCRIPT_FILE),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'protect',
|
|
96
|
+
event: 'preToolUse',
|
|
97
|
+
matcher: EDIT_TOOLS,
|
|
98
|
+
sentinels: [PROTECT_SCRIPT_FILE],
|
|
99
|
+
command: run('node', PROTECT_SCRIPT_FILE),
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
/** True when an entry belongs to `spec` (matched by any of its sentinels). */
|
|
104
|
+
function isSpecHook(spec, hook) {
|
|
105
|
+
const command = hook.command;
|
|
106
|
+
return typeof command === 'string' && spec.sentinels.some((s) => command.includes(s));
|
|
107
|
+
}
|
|
108
|
+
/** The event's entry list, tolerating a malformed (non-array) config value. */
|
|
109
|
+
function eventHooks(config, event) {
|
|
110
|
+
const entries = config.hooks?.[event];
|
|
111
|
+
return Array.isArray(entries) ? entries.map((h) => ({ ...h })) : [];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The canonical fields for `spec`, omitting the optional ones it doesn't set.
|
|
115
|
+
* `loopLimit` is compared against `undefined` rather than truth-tested, because
|
|
116
|
+
* the value checkride writes is `null`.
|
|
117
|
+
*/
|
|
118
|
+
function specFields(spec) {
|
|
119
|
+
return {
|
|
120
|
+
command: spec.command,
|
|
121
|
+
...(spec.matcher !== undefined ? { matcher: spec.matcher } : {}),
|
|
122
|
+
...(spec.timeout !== undefined ? { timeout: spec.timeout } : {}),
|
|
123
|
+
...(spec.loopLimit !== undefined ? { loop_limit: spec.loopLimit } : {}),
|
|
124
|
+
...(spec.failClosed !== undefined ? { failClosed: spec.failClosed } : {}),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Merge one hook into a parsed config, idempotently. An existing entry (found by
|
|
129
|
+
* sentinel) has its fields refreshed in place, keeping any key checkride does not
|
|
130
|
+
* own; otherwise a new entry is appended.
|
|
131
|
+
*/
|
|
132
|
+
function applyHook(config, spec) {
|
|
133
|
+
const hooks = { ...config.hooks };
|
|
134
|
+
const entries = eventHooks(config, spec.event);
|
|
135
|
+
const idx = entries.findIndex((h) => isSpecHook(spec, h));
|
|
136
|
+
if (idx >= 0) {
|
|
137
|
+
entries[idx] = { ...entries[idx], ...specFields(spec) };
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
entries.push(specFields(spec));
|
|
141
|
+
}
|
|
142
|
+
return { ...config, hooks: { ...hooks, [spec.event]: entries } };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Merge the selected hooks into a parsed config, seeding `version` when absent.
|
|
146
|
+
* An existing `version` is never overwritten: if a future Cursor bumps it, the
|
|
147
|
+
* repo's value is the one that matches the rest of the file.
|
|
148
|
+
*/
|
|
149
|
+
export function applyCursorHooks(config = {}, names = HOOK_NAMES) {
|
|
150
|
+
const seeded = config.version === undefined ? { version: SCHEMA_VERSION, ...config } : config;
|
|
151
|
+
return hookSpecs()
|
|
152
|
+
.filter((spec) => names.includes(spec.name))
|
|
153
|
+
.reduce((acc, spec) => applyHook(acc, spec), seeded);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Strip one hook from a parsed config. An event left with no entries loses its
|
|
157
|
+
* key; `version` and every unrelated entry stay. Cursor's schema is flat, so
|
|
158
|
+
* there is no empty group to prune — the Claude writer's extra step has no
|
|
159
|
+
* counterpart here.
|
|
160
|
+
*/
|
|
161
|
+
function removeHook(config, spec) {
|
|
162
|
+
const hooks = { ...config.hooks };
|
|
163
|
+
const entries = eventHooks(config, spec.event).filter((h) => !isSpecHook(spec, h));
|
|
164
|
+
if (entries.length > 0)
|
|
165
|
+
hooks[spec.event] = entries;
|
|
166
|
+
else
|
|
167
|
+
delete hooks[spec.event];
|
|
168
|
+
return { ...config, hooks };
|
|
169
|
+
}
|
|
170
|
+
/** Remove the named hooks from a parsed config — the inverse of {@link applyCursorHooks}. */
|
|
171
|
+
export function removeCursorHooks(config = {}, names = HOOK_NAMES) {
|
|
172
|
+
return hookSpecs()
|
|
173
|
+
.filter((spec) => names.includes(spec.name))
|
|
174
|
+
.reduce((acc, spec) => removeHook(acc, spec), config);
|
|
175
|
+
}
|
|
176
|
+
const SPEC = {
|
|
177
|
+
name: HARNESS,
|
|
178
|
+
configFile: CURSOR_HOOKS_FILE,
|
|
179
|
+
// Cursor's config has no field that renders the gate's command in its UI, so
|
|
180
|
+
// the package manager reaches its hooks only through the generated script.
|
|
181
|
+
apply: applyCursorHooks,
|
|
182
|
+
scripts: { gate: GATE_SCRIPT_FILE, dirty: DIRTY_SCRIPT_FILE, protect: PROTECT_SCRIPT_FILE },
|
|
183
|
+
remove: removeCursorHooks,
|
|
184
|
+
};
|
|
185
|
+
/** Write or refresh the selected Cursor hooks in `cwd`. */
|
|
186
|
+
export function writeCursorHooks(cwd, opts) {
|
|
187
|
+
return writeHarnessHooks(cwd, SPEC, opts);
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agent-setup/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem helpers shared by the per-harness hook writers.
|
|
3
|
+
*
|
|
4
|
+
* Every write `agent-setup` makes is idempotent — a second run must report
|
|
5
|
+
* `changed: false` for every file — so the writers never write blind: they read
|
|
6
|
+
* first, compare, and only then touch the disk. `dryRun` computes the same
|
|
7
|
+
* result without writing anything.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* One file a hook writer produced, and whether this run actually changed it.
|
|
11
|
+
* `removed` marks a deletion, so a caller reporting the run can tell a file that
|
|
12
|
+
* was written from one that was taken away — both are changes.
|
|
13
|
+
*/
|
|
14
|
+
export type HookFile = {
|
|
15
|
+
path: string;
|
|
16
|
+
changed: boolean;
|
|
17
|
+
removed?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function readIfExists(path: string): Promise<string | null>;
|
|
20
|
+
/** Write `content` to `cwd/rel` (creating directories) unless it already matches. */
|
|
21
|
+
export declare function putFile(cwd: string, rel: string, content: string, opts: {
|
|
22
|
+
dryRun: boolean;
|
|
23
|
+
executable?: boolean;
|
|
24
|
+
}): Promise<HookFile>;
|
|
25
|
+
/**
|
|
26
|
+
* Delete `cwd/rel` if it is there. Reports `changed: true` only when a file was
|
|
27
|
+
* actually removed, so a second removal is the no-op every other write here is.
|
|
28
|
+
*/
|
|
29
|
+
export declare function dropFile(cwd: string, rel: string, opts: {
|
|
30
|
+
dryRun: boolean;
|
|
31
|
+
}): Promise<HookFile>;
|
|
32
|
+
/**
|
|
33
|
+
* Merge `next` into the JSON config at `cwd/rel` and write it back when it
|
|
34
|
+
* changed. `merge` receives the parsed file — or `undefined` when there is none
|
|
35
|
+
* — and returns the merged value; it must be idempotent.
|
|
36
|
+
*
|
|
37
|
+
* `skipIfMissing` suppresses creating the file at all: a run that only *removes*
|
|
38
|
+
* hooks has nothing to say to a repo that never had that harness configured, and
|
|
39
|
+
* writing `{}` into a `.claude/settings.json` that did not exist would be a
|
|
40
|
+
* confusing way to say "nothing to remove".
|
|
41
|
+
*
|
|
42
|
+
* A malformed file is named in the error, so a consumer sees
|
|
43
|
+
* `invalid .claude/settings.json: <reason>` instead of a bare `SyntaxError`
|
|
44
|
+
* stack (mirrors `invalidConfig` in `config.ts`).
|
|
45
|
+
*/
|
|
46
|
+
export declare function putJson<T>(cwd: string, rel: string, merge: (current?: T) => T, opts: {
|
|
47
|
+
dryRun: boolean;
|
|
48
|
+
skipIfMissing?: boolean;
|
|
49
|
+
}): Promise<HookFile>;
|
|
50
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/agent-setup/files.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7E,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMvE;AAED,qFAAqF;AACrF,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C,OAAO,CAAC,QAAQ,CAAC,CAUnB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACxB,OAAO,CAAC,QAAQ,CAAC,CAKnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EACzB,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD,OAAO,CAAC,QAAQ,CAAC,CAoBnB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem helpers shared by the per-harness hook writers.
|
|
3
|
+
*
|
|
4
|
+
* Every write `agent-setup` makes is idempotent — a second run must report
|
|
5
|
+
* `changed: false` for every file — so the writers never write blind: they read
|
|
6
|
+
* first, compare, and only then touch the disk. `dryRun` computes the same
|
|
7
|
+
* result without writing anything.
|
|
8
|
+
*/
|
|
9
|
+
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
export async function readIfExists(path) {
|
|
12
|
+
try {
|
|
13
|
+
return await readFile(path, 'utf8');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Write `content` to `cwd/rel` (creating directories) unless it already matches. */
|
|
20
|
+
export async function putFile(cwd, rel, content, opts) {
|
|
21
|
+
const path = join(cwd, rel);
|
|
22
|
+
const raw = await readIfExists(path);
|
|
23
|
+
const changed = raw !== content;
|
|
24
|
+
if (changed && !opts.dryRun) {
|
|
25
|
+
await mkdir(dirname(path), { recursive: true });
|
|
26
|
+
await writeFile(path, content);
|
|
27
|
+
if (opts.executable)
|
|
28
|
+
await chmod(path, 0o755);
|
|
29
|
+
}
|
|
30
|
+
return { path: rel, changed };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Delete `cwd/rel` if it is there. Reports `changed: true` only when a file was
|
|
34
|
+
* actually removed, so a second removal is the no-op every other write here is.
|
|
35
|
+
*/
|
|
36
|
+
export async function dropFile(cwd, rel, opts) {
|
|
37
|
+
const path = join(cwd, rel);
|
|
38
|
+
const changed = (await readIfExists(path)) !== null;
|
|
39
|
+
if (changed && !opts.dryRun)
|
|
40
|
+
await rm(path, { force: true });
|
|
41
|
+
return { path: rel, changed, removed: true };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Merge `next` into the JSON config at `cwd/rel` and write it back when it
|
|
45
|
+
* changed. `merge` receives the parsed file — or `undefined` when there is none
|
|
46
|
+
* — and returns the merged value; it must be idempotent.
|
|
47
|
+
*
|
|
48
|
+
* `skipIfMissing` suppresses creating the file at all: a run that only *removes*
|
|
49
|
+
* hooks has nothing to say to a repo that never had that harness configured, and
|
|
50
|
+
* writing `{}` into a `.claude/settings.json` that did not exist would be a
|
|
51
|
+
* confusing way to say "nothing to remove".
|
|
52
|
+
*
|
|
53
|
+
* A malformed file is named in the error, so a consumer sees
|
|
54
|
+
* `invalid .claude/settings.json: <reason>` instead of a bare `SyntaxError`
|
|
55
|
+
* stack (mirrors `invalidConfig` in `config.ts`).
|
|
56
|
+
*/
|
|
57
|
+
export async function putJson(cwd, rel, merge, opts) {
|
|
58
|
+
const path = join(cwd, rel);
|
|
59
|
+
const raw = await readIfExists(path);
|
|
60
|
+
if (raw === null && opts.skipIfMissing === true)
|
|
61
|
+
return { path: rel, changed: false };
|
|
62
|
+
let current;
|
|
63
|
+
if (raw !== null) {
|
|
64
|
+
try {
|
|
65
|
+
current = JSON.parse(raw);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
69
|
+
throw new Error(`invalid ${rel}: ${reason}`, { cause: err });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const nextRaw = `${JSON.stringify(merge(current), null, 2)}\n`;
|
|
73
|
+
const changed = raw !== nextRaw;
|
|
74
|
+
if (changed && !opts.dryRun) {
|
|
75
|
+
await mkdir(dirname(path), { recursive: true });
|
|
76
|
+
await writeFile(path, nextRaw);
|
|
77
|
+
}
|
|
78
|
+
return { path: rel, changed };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/agent-setup/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape a harness writer fills in, and the one routine that drives it.
|
|
3
|
+
*
|
|
4
|
+
* Every harness needs the same two writes — merge its hook entries into its
|
|
5
|
+
* config file, then drop whichever scripts those entries invoke — and differs
|
|
6
|
+
* only in *where* each lands, *how* its config merges, and how much of the work
|
|
7
|
+
* its config can do without a script at all. Keeping that common body here is
|
|
8
|
+
* what makes a new harness a description rather than a copy: a `HarnessSpec`
|
|
9
|
+
* plus a merge function, and nothing else.
|
|
10
|
+
*/
|
|
11
|
+
import type { HarnessName } from '../gate.js';
|
|
12
|
+
import { type PackageManager } from '../pm/index.js';
|
|
13
|
+
import type { HookFile } from './files.js';
|
|
14
|
+
/**
|
|
15
|
+
* Every hook `agent-setup` can write; `--hook <a,b>` selects a subset. It lives
|
|
16
|
+
* here rather than in `./hook.ts` so the per-harness writers can name it without
|
|
17
|
+
* importing the module that imports them.
|
|
18
|
+
*/
|
|
19
|
+
export declare const HOOK_NAMES: readonly ["gate", "dirty", "protect"];
|
|
20
|
+
export type HookName = (typeof HOOK_NAMES)[number];
|
|
21
|
+
/**
|
|
22
|
+
* Ceiling for the gate run, in seconds, for any harness whose config carries
|
|
23
|
+
* one. Generous on purpose: it exists to break a hang, not to police a slow
|
|
24
|
+
* repo, and checkride's own per-check timeouts fire long before it does.
|
|
25
|
+
*
|
|
26
|
+
* Both harnesses default lower than a real pipeline takes — Cursor's default is
|
|
27
|
+
* short, and Claude Code's is 600s — and both read a timed-out stop hook as a
|
|
28
|
+
* *broken* one, which under Claude Code means the turn simply ends. A gate that
|
|
29
|
+
* quietly stops gating on the slowest repos is the vacuous green the contract
|
|
30
|
+
* exists to prevent, so neither default is left in place.
|
|
31
|
+
*/
|
|
32
|
+
export declare const GATE_TIMEOUT_SECONDS = 900;
|
|
33
|
+
/**
|
|
34
|
+
* The spinner text a harness shows while the gate runs.
|
|
35
|
+
*
|
|
36
|
+
* Without it the only feedback during a full pipeline run is the harness's
|
|
37
|
+
* default spinner, which is indistinguishable from a hung model — the user is
|
|
38
|
+
* left guessing whether anything is happening at all. Naming the command that
|
|
39
|
+
* is actually running answers that in the one place they are already looking.
|
|
40
|
+
*/
|
|
41
|
+
export declare function gateStatusMessage(pm: PackageManager): string;
|
|
42
|
+
/**
|
|
43
|
+
* Where one harness keeps its hooks, and how to merge into its config.
|
|
44
|
+
*
|
|
45
|
+
* `T` is that harness's config shape, so each writer keeps its own types all
|
|
46
|
+
* the way through — `apply` is never handed a config it does not understand.
|
|
47
|
+
*/
|
|
48
|
+
export type HarnessSpec<T> = {
|
|
49
|
+
name: HarnessName;
|
|
50
|
+
/** The harness's hook config, relative to the repo root. */
|
|
51
|
+
configFile: string;
|
|
52
|
+
/**
|
|
53
|
+
* Where each hook's script lives, relative to the repo root — for the hooks
|
|
54
|
+
* that still need one.
|
|
55
|
+
*
|
|
56
|
+
* A hook is absent here when the harness can express it as *configuration*
|
|
57
|
+
* instead: Claude Code's `protect` is a `permissions.deny` rule and its
|
|
58
|
+
* `dirty` is an inline one-liner, so neither writes a file. Generated code in
|
|
59
|
+
* a consumer's repo is a cost — a file to review, to keep in sync, and to
|
|
60
|
+
* wonder about — and it should be paid only where config cannot do the job.
|
|
61
|
+
*/
|
|
62
|
+
scripts: Partial<Record<HookName, string>>;
|
|
63
|
+
/**
|
|
64
|
+
* Scripts an earlier checkride wrote for this harness that this one does not.
|
|
65
|
+
* Deleted whenever their hook is written or removed, so a repo upgrading into
|
|
66
|
+
* the config-only form does not keep a dead file it will never run again.
|
|
67
|
+
*/
|
|
68
|
+
retired?: Partial<Record<HookName, string>>;
|
|
69
|
+
/**
|
|
70
|
+
* Merge the named hooks into a parsed config. Must be idempotent. `pm` is the
|
|
71
|
+
* repo's package manager, which a harness that renders the gate's command in
|
|
72
|
+
* its own UI (Claude Code's `statusMessage`) needs in the config, not just in
|
|
73
|
+
* the script.
|
|
74
|
+
*/
|
|
75
|
+
apply: (config: T | undefined, names: readonly string[], pm: PackageManager) => T;
|
|
76
|
+
/** Strip the named hooks from a parsed config. Must be idempotent. */
|
|
77
|
+
remove: (config: T | undefined, names: readonly string[]) => T;
|
|
78
|
+
};
|
|
79
|
+
export type WriteOptions = {
|
|
80
|
+
pm?: PackageManager;
|
|
81
|
+
dryRun?: boolean;
|
|
82
|
+
hooks: readonly string[];
|
|
83
|
+
/**
|
|
84
|
+
* Hooks to tear out: their config entries are stripped and their scripts
|
|
85
|
+
* deleted. Removal wins over `hooks`, so a name in both is removed — the
|
|
86
|
+
* caller-facing flags (`--hook`/`--remove-hook`) reject that combination
|
|
87
|
+
* before it gets here, and this is the safe reading if one ever slips through.
|
|
88
|
+
*/
|
|
89
|
+
remove?: readonly string[];
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Write or refresh `spec`'s hooks in `cwd` and tear out the ones marked for
|
|
93
|
+
* removal: the config entries first, then only the scripts the selected hooks
|
|
94
|
+
* actually invoke. Every write is idempotent, and `dryRun` computes the result
|
|
95
|
+
* without touching the disk.
|
|
96
|
+
*
|
|
97
|
+
* A run that only removes never *creates* the harness's config file — a repo
|
|
98
|
+
* that was never wired for this harness has nothing to un-wire.
|
|
99
|
+
*/
|
|
100
|
+
export declare function writeHarnessHooks<T extends object>(cwd: string, spec: HarnessSpec<T>, opts: WriteOptions): Promise<HookFile[]>;
|
|
101
|
+
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/agent-setup/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAwB,KAAK,cAAc,EAAa,MAAM,gBAAgB,CAAC;AACtF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C;;;;GAIG;AACH,eAAO,MAAM,UAAU,uCAAwC,CAAC;AAChE,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAE5D;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;OASG;IACH,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C;;;;;OAKG;IACH,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,cAAc,KAAK,CAAC,CAAC;IAClF,sEAAsE;IACtE,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EACtD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgDrB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape a harness writer fills in, and the one routine that drives it.
|
|
3
|
+
*
|
|
4
|
+
* Every harness needs the same two writes — merge its hook entries into its
|
|
5
|
+
* config file, then drop whichever scripts those entries invoke — and differs
|
|
6
|
+
* only in *where* each lands, *how* its config merges, and how much of the work
|
|
7
|
+
* its config can do without a script at all. Keeping that common body here is
|
|
8
|
+
* what makes a new harness a description rather than a copy: a `HarnessSpec`
|
|
9
|
+
* plus a merge function, and nothing else.
|
|
10
|
+
*/
|
|
11
|
+
import { detectPackageManager, runScript } from '../pm/index.js';
|
|
12
|
+
import { dropFile, putFile, putJson } from './files.js';
|
|
13
|
+
import { dirtyScript, gateScript, protectScript } from './scripts.js';
|
|
14
|
+
/**
|
|
15
|
+
* Every hook `agent-setup` can write; `--hook <a,b>` selects a subset. It lives
|
|
16
|
+
* here rather than in `./hook.ts` so the per-harness writers can name it without
|
|
17
|
+
* importing the module that imports them.
|
|
18
|
+
*/
|
|
19
|
+
export const HOOK_NAMES = ['gate', 'dirty', 'protect'];
|
|
20
|
+
/**
|
|
21
|
+
* Ceiling for the gate run, in seconds, for any harness whose config carries
|
|
22
|
+
* one. Generous on purpose: it exists to break a hang, not to police a slow
|
|
23
|
+
* repo, and checkride's own per-check timeouts fire long before it does.
|
|
24
|
+
*
|
|
25
|
+
* Both harnesses default lower than a real pipeline takes — Cursor's default is
|
|
26
|
+
* short, and Claude Code's is 600s — and both read a timed-out stop hook as a
|
|
27
|
+
* *broken* one, which under Claude Code means the turn simply ends. A gate that
|
|
28
|
+
* quietly stops gating on the slowest repos is the vacuous green the contract
|
|
29
|
+
* exists to prevent, so neither default is left in place.
|
|
30
|
+
*/
|
|
31
|
+
export const GATE_TIMEOUT_SECONDS = 900;
|
|
32
|
+
/**
|
|
33
|
+
* The spinner text a harness shows while the gate runs.
|
|
34
|
+
*
|
|
35
|
+
* Without it the only feedback during a full pipeline run is the harness's
|
|
36
|
+
* default spinner, which is indistinguishable from a hung model — the user is
|
|
37
|
+
* left guessing whether anything is happening at all. Naming the command that
|
|
38
|
+
* is actually running answers that in the one place they are already looking.
|
|
39
|
+
*/
|
|
40
|
+
export function gateStatusMessage(pm) {
|
|
41
|
+
return `checkride gate — running \`${runScript(pm, 'check')}\``;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Write or refresh `spec`'s hooks in `cwd` and tear out the ones marked for
|
|
45
|
+
* removal: the config entries first, then only the scripts the selected hooks
|
|
46
|
+
* actually invoke. Every write is idempotent, and `dryRun` computes the result
|
|
47
|
+
* without touching the disk.
|
|
48
|
+
*
|
|
49
|
+
* A run that only removes never *creates* the harness's config file — a repo
|
|
50
|
+
* that was never wired for this harness has nothing to un-wire.
|
|
51
|
+
*/
|
|
52
|
+
export async function writeHarnessHooks(cwd, spec, opts) {
|
|
53
|
+
const pm = opts.pm ?? detectPackageManager({ cwd });
|
|
54
|
+
const dryRun = opts.dryRun ?? false;
|
|
55
|
+
const gone = opts.remove ?? [];
|
|
56
|
+
const names = opts.hooks.filter((name) => !gone.includes(name));
|
|
57
|
+
const bodies = {
|
|
58
|
+
// The gate guards on the edit marker only when the hook that sets it is
|
|
59
|
+
// written too; alone, the guard would disarm the gate entirely. Removing
|
|
60
|
+
// `dirty` therefore rewrites a surviving gate unguarded, which is why the
|
|
61
|
+
// config merge and the script bodies both read the post-removal selection.
|
|
62
|
+
gate: () => gateScript(pm, { harness: spec.name, dirtyGuard: names.includes('dirty') }),
|
|
63
|
+
dirty: dirtyScript,
|
|
64
|
+
protect: protectScript,
|
|
65
|
+
};
|
|
66
|
+
const config = await putJson(cwd, spec.configFile, (c) => spec.apply(spec.remove(c, gone), names, pm), { dryRun, skipIfMissing: names.length === 0 });
|
|
67
|
+
// A hook this harness expresses as config has no script to write, and may have
|
|
68
|
+
// a retired one to clear away. `touched` is both directions at once, because a
|
|
69
|
+
// dead file is worth deleting whether the hook is arriving or leaving.
|
|
70
|
+
const touched = HOOK_NAMES.filter((name) => names.includes(name) || gone.includes(name));
|
|
71
|
+
const [written, removed, swept] = await Promise.all([
|
|
72
|
+
Promise.all(HOOK_NAMES.filter((name) => names.includes(name) && spec.scripts[name] !== undefined).map((name) => putFile(cwd, spec.scripts[name] ?? '', bodies[name](), { dryRun, executable: true }))),
|
|
73
|
+
Promise.all(HOOK_NAMES.filter((name) => gone.includes(name) && spec.scripts[name] !== undefined).map((name) => dropFile(cwd, spec.scripts[name] ?? '', { dryRun }))),
|
|
74
|
+
Promise.all(touched
|
|
75
|
+
.map((name) => spec.retired?.[name])
|
|
76
|
+
.filter((path) => path !== undefined)
|
|
77
|
+
.map((path) => dropFile(cwd, path, { dryRun }))),
|
|
78
|
+
]);
|
|
79
|
+
// Only report a retired script that was actually there. Every repo set up
|
|
80
|
+
// after these became configuration has none, and listing files that never
|
|
81
|
+
// existed as "absent" would make the common run read like a cleanup.
|
|
82
|
+
return [config, ...written, ...removed, ...swept.filter((f) => f.changed)];
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/agent-setup/harness.ts"],"names":[],"mappings":"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"}
|