plumbbob 0.6.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +24 -7
- package/dist/cli-core.js +7 -2
- package/dist/lib/buildlog.js +5 -2
- package/dist/lib/check.js +33 -1
- package/dist/lib/git.js +17 -0
- package/dist/lib/latch.js +97 -0
- package/dist/lib/orient.js +24 -0
- package/dist/lib/sidecar.js +116 -0
- package/dist/verbs/build.js +5 -1
- package/dist/verbs/checkpoint.js +51 -2
- package/dist/verbs/doctor.js +33 -2
- package/dist/verbs/finish.js +57 -1
- package/dist/verbs/revert.js +19 -1
- package/dist/verbs/start.js +22 -2
- package/dist/verbs/status.js +10 -3
- package/dist/verbs/turn.js +106 -0
- package/hooks/hooks.json +15 -0
- package/hooks/pre-bash-commit.sh +69 -0
- package/node_modules/checkride/CHANGELOG.md +111 -0
- package/node_modules/checkride/README.md +185 -20
- package/node_modules/checkride/dist/adapters.d.ts +9 -4
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +62 -2
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +63 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.js +92 -0
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/index.d.ts +8 -0
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/index.js +7 -0
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +13 -0
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -0
- package/node_modules/checkride/dist/atomic.js +24 -0
- package/node_modules/checkride/dist/atomic.js.map +1 -0
- package/node_modules/checkride/dist/baseline/command.d.ts +42 -0
- package/node_modules/checkride/dist/baseline/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/command.js +58 -0
- package/node_modules/checkride/dist/baseline/command.js.map +1 -0
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +41 -0
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/fingerprint.js +119 -0
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +13 -0
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/index.js +10 -0
- package/node_modules/checkride/dist/baseline/index.js.map +1 -0
- package/node_modules/checkride/dist/baseline/store.d.ts +67 -0
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/store.js +110 -0
- package/node_modules/checkride/dist/baseline/store.js.map +1 -0
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +37 -3
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +43 -3
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +95 -22
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +52 -0
- package/node_modules/checkride/dist/digest/digest.d.ts.map +1 -0
- package/node_modules/checkride/dist/digest/digest.js +147 -0
- package/node_modules/checkride/dist/digest/digest.js.map +1 -0
- package/node_modules/checkride/dist/digest/index.d.ts +8 -0
- package/node_modules/checkride/dist/digest/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/digest/index.js +7 -0
- package/node_modules/checkride/dist/digest/index.js.map +1 -0
- package/node_modules/checkride/dist/doctor.d.ts +4 -0
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +33 -10
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/index.d.ts +1 -1
- package/node_modules/checkride/dist/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/index.js +1 -1
- package/node_modules/checkride/dist/index.js.map +1 -1
- package/node_modules/checkride/dist/init.d.ts +38 -0
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +97 -8
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/orchestrator.d.ts +58 -1
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +155 -27
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +21 -0
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/detect.js +60 -0
- package/node_modules/checkride/dist/pm/detect.js.map +1 -0
- package/node_modules/checkride/dist/pm/index.d.ts +10 -0
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/index.js +9 -0
- package/node_modules/checkride/dist/pm/index.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +28 -0
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/translate.js +40 -0
- package/node_modules/checkride/dist/pm/translate.js.map +1 -0
- package/node_modules/checkride/package.json +20 -17
- package/node_modules/checkride/schema/checkride.config.schema.json +97 -0
- package/node_modules/checkride/schema/checkride.summary.schema.json +57 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/prettierrc.json +6 -0
- package/package.json +8 -4
- package/skills/pb-build/SKILL.md +14 -0
- package/skills/pb-plan/SKILL.md +17 -0
- package/skills/pb-verify/SKILL.md +18 -0
|
@@ -35,17 +35,28 @@ pnpm exec checkride init # set up a project (new or existing, auto-detected)
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
`init` writes a `"check": "checkride"` alias, so daily usage is `pnpm check`
|
|
38
|
-
regardless of the tool's name.
|
|
38
|
+
regardless of the tool's name. It also writes the agent contract: an AGENTS.md
|
|
39
|
+
stanza (the "exit 0 = done" rule) and a Claude Code **Stop hook** in
|
|
40
|
+
`.claude/settings.json` that blocks an agent from finishing while the pipeline is
|
|
41
|
+
red. The hook uses your detected package manager (`pnpm`/`npm`/`yarn`/`bun run
|
|
42
|
+
check`); skip it with `--no-hook`, or add both to a repo you already set up with
|
|
43
|
+
`checkride agent-setup`.
|
|
39
44
|
|
|
40
45
|
## Commands
|
|
41
46
|
|
|
42
47
|
```text
|
|
43
48
|
checkride Run the default checks. Exit 0 pass / 1 fail / 2 error.
|
|
44
49
|
--only <a,b> --skip <a,b> --bail --json --changed --all --include <a,b>
|
|
50
|
+
--digest --strict (zero checks running = exit 2 — use wherever checkride gates)
|
|
45
51
|
checkride init Set up a project (new or existing — auto-detected).
|
|
46
52
|
--shape flat|monorepo|hybrid --name <n> --scope <@s> --license <id> --dry-run
|
|
53
|
+
--baseline (existing mode) grandfather current debt instead of disabling slots
|
|
54
|
+
--no-hook skip writing the Claude Code Stop hook
|
|
47
55
|
checkride doctor Verify environment + every slot's status (read-only, exit 0/1).
|
|
48
56
|
checkride fix Run every active adapter's fix command (oxlint --fix, ...).
|
|
57
|
+
checkride baseline Record current diagnostics as a committed baseline.
|
|
58
|
+
checkride agent-setup Add the AGENTS.md stanza + Claude Code Stop hook to a repo.
|
|
59
|
+
--no-hook skip the Stop hook (write only the stanza)
|
|
49
60
|
```
|
|
50
61
|
|
|
51
62
|
During iteration, narrow the loop: `checkride --bail`, `checkride --only
|
|
@@ -66,6 +77,7 @@ generates config for the blessed default.
|
|
|
66
77
|
| Slot | Role | Blessed default | Alternates |
|
|
67
78
|
| ---------- | -------------------------------------- | ------------------- | ---------------- |
|
|
68
79
|
| `types` | Type checking | `tsc --build` | — |
|
|
80
|
+
| `format` | Formatting (opt-in) | `prettier` | `biome` |
|
|
69
81
|
| `lint` | Linting | `oxlint` | `biome`, `eslint`|
|
|
70
82
|
| `struct` | Structural rules (deep modules) | `ast-grep` | — |
|
|
71
83
|
| `dead` | Dead code, deps, cycles, boundaries | `fallow` | `knip` |
|
|
@@ -75,16 +87,47 @@ generates config for the blessed default.
|
|
|
75
87
|
| `spell` | Spelling | `cspell` | — |
|
|
76
88
|
| `mutation` | Mutation testing (opt-in) | `stryker` | — |
|
|
77
89
|
| `security` | Dependency audit (opt-in) | `pnpm audit` | — |
|
|
90
|
+
| `publint` | Package publishing lint (opt-in) | `publint` | — |
|
|
91
|
+
| `attw` | Type resolution across module systems (opt-in) | `attw --pack`| — |
|
|
78
92
|
|
|
79
93
|
Zero-config: for each slot, checkride runs the first adapter whose config file
|
|
80
94
|
exists, and skips slots with no detected tool. The core has **no runtime
|
|
81
|
-
dependency** on any checked tool — it spawns
|
|
95
|
+
dependency** on any checked tool — it spawns `<pm> exec <tool>`; the project
|
|
82
96
|
owns the pinned tool versions.
|
|
83
97
|
|
|
98
|
+
**Opt-in slots** (`format`, `mutation`, `security`, `publint`, `attw`) stay out of the
|
|
99
|
+
default run so adopting checkride — or bumping its version — never turns a repo red on a
|
|
100
|
+
check it didn't ask for. Turn one on with `--include <slot>` (or `--all`), or by **naming
|
|
101
|
+
it in `checks`**: an explicit entry like `"format": "prettier"` opts the slot into every
|
|
102
|
+
run. `checkride fix` then runs its write form (e.g. `prettier --write`) alongside the
|
|
103
|
+
other fixers. `format` sits before `lint` so the tree is tidy before the linters look.
|
|
104
|
+
|
|
105
|
+
`publint` and `attw` are the **library-publishing** pair — turn them on for a package you
|
|
106
|
+
ship to npm to make "the published artifact is correct" part of your definition of done.
|
|
107
|
+
`publint` lints `package.json`'s publishing surface (exports, files, types); `attw` runs
|
|
108
|
+
`attw --pack` to check that your types resolve under every module system (`--format json`,
|
|
109
|
+
captured to `.check/attw.json`). Both stay opt-in so apps that never publish don't run
|
|
110
|
+
them.
|
|
111
|
+
|
|
112
|
+
### Package managers
|
|
113
|
+
|
|
114
|
+
checkride is package-manager-agnostic. It detects the repo's package manager
|
|
115
|
+
from the `packageManager` field or the lockfile (`pnpm-lock.yaml`,
|
|
116
|
+
`package-lock.json`, `yarn.lock`, `bun.lock`), defaulting to **pnpm**, and
|
|
117
|
+
translates each adapter's canonical `pnpm exec <tool>` into that manager's form
|
|
118
|
+
(`npx`, `yarn`, or `bunx`). The default pnpm run is unchanged; `doctor` reports
|
|
119
|
+
the detected manager. One exception: the opt-in `security` slot is `pnpm audit`,
|
|
120
|
+
whose flags and JSON shape are pnpm-specific, so it is **unavailable on a
|
|
121
|
+
non-pnpm manager** until a per-manager audit adapter lands.
|
|
122
|
+
|
|
84
123
|
## The `.check/` contract
|
|
85
124
|
|
|
86
|
-
Every run writes to `.check/`. This is a public API for agents
|
|
87
|
-
|
|
125
|
+
Every run writes to `.check/`. This is a public API for agents — the promised
|
|
126
|
+
surfaces (exit codes, summary shape, flags, exports) are spelled out in
|
|
127
|
+
[docs/contract.md](./docs/contract.md) and locked by a `test/contract/` suite.
|
|
128
|
+
The summary shape is published as a JSON Schema at
|
|
129
|
+
[`schema/checkride.summary.schema.json`](./schema/checkride.summary.schema.json);
|
|
130
|
+
fields are additive-only under a given `schema_version`.
|
|
88
131
|
|
|
89
132
|
- `summary.json` — the aggregate report:
|
|
90
133
|
|
|
@@ -93,6 +136,7 @@ changes as breaking.
|
|
|
93
136
|
"schema_version": 1,
|
|
94
137
|
"timestamp": "…",
|
|
95
138
|
"ok": true,
|
|
139
|
+
"checks_run": 8, // checks that actually executed; 0 + ok = vacuous green
|
|
96
140
|
"total_duration_ms": 4200,
|
|
97
141
|
"checks": [
|
|
98
142
|
{ "name": "lint", "adapter": "oxlint", "description": "…",
|
|
@@ -101,12 +145,32 @@ changes as breaking.
|
|
|
101
145
|
}
|
|
102
146
|
```
|
|
103
147
|
|
|
148
|
+
`ok: true` with `checks_run: 0` means **nothing was verified** — every slot
|
|
149
|
+
sat out (no tool detected, disabled, or skipped). The run warns loudly on
|
|
150
|
+
stderr when that happens, and `--strict` turns it into exit 2, so a gate can
|
|
151
|
+
never mistake "nothing ran" for "everything passed". Anything that gates on
|
|
152
|
+
checkride (CI, commit hooks) should pass `--strict`.
|
|
153
|
+
|
|
104
154
|
- `<slot>.json` — the raw tool JSON when stdout parses as JSON; otherwise
|
|
105
155
|
`<slot>.stdout.txt` / `<slot>.stderr.txt`. Tools that write their own files
|
|
106
156
|
(vitest `--outputFile`, stryker) keep doing so.
|
|
107
157
|
|
|
158
|
+
When a [baseline](#baseline) masks a slot's findings, that check gains an additive
|
|
159
|
+
`"baselined": <n>` field counting the grandfathered diagnostics; it is absent on
|
|
160
|
+
runs with no baseline, so `schema_version` is unchanged.
|
|
161
|
+
|
|
162
|
+
- `digest.md` — written only under `--digest`: a **token-bounded** Markdown
|
|
163
|
+
excerpt of the *failing* slots, so an agent working through a big red repo
|
|
164
|
+
reads a capped index instead of every raw file. Each section lists the first few
|
|
165
|
+
findings (reusing the baseline fingerprint extractors, or a tail of raw text
|
|
166
|
+
for slots without one) and links the authoritative `.check/<slot>.json`, which
|
|
167
|
+
is never modified. It **truncates, never normalizes**; a green run leaves no
|
|
168
|
+
digest (any stale one is removed), so its presence always means "this run
|
|
169
|
+
failed". It is a file, never stdout — the machine-output split holds.
|
|
170
|
+
|
|
108
171
|
To debug a failure: read `summary.json` to find the failing slot, then read that
|
|
109
|
-
slot's raw output for structured diagnostics.
|
|
172
|
+
slot's raw output for structured diagnostics. On a large repo, `--digest` writes
|
|
173
|
+
`digest.md` as a capped starting point.
|
|
110
174
|
|
|
111
175
|
## Configuration
|
|
112
176
|
|
|
@@ -114,14 +178,17 @@ slot's raw output for structured diagnostics.
|
|
|
114
178
|
|
|
115
179
|
```jsonc
|
|
116
180
|
{
|
|
117
|
-
"
|
|
181
|
+
"$schema": "https://raw.githubusercontent.com/robmclarty/checkride/v0.2.1/schema/checkride.config.schema.json",
|
|
182
|
+
"extends": "@acme/checkride-preset", // inherit a shared preset, then override below
|
|
183
|
+
"timeout": 1200, // global per-check timeout in seconds (default: 600)
|
|
118
184
|
"checks": {
|
|
185
|
+
"format": "prettier", // enable the opt-in format slot (blessed: prettier)
|
|
119
186
|
"lint": "biome", // pick an alternate adapter
|
|
120
187
|
"spell": false, // disable a slot
|
|
121
188
|
"test": { "use": "vitest", "timeout": 0, "changedArgs": ["--changed", "origin/master"] },
|
|
122
|
-
"
|
|
189
|
+
"tidy": { // a bespoke custom check that runs FIRST, ahead of the built-ins
|
|
123
190
|
"command": "pnpm",
|
|
124
|
-
"args": ["exec", "
|
|
191
|
+
"args": ["exec", "some-formatter", "--write"],
|
|
125
192
|
"order": "first"
|
|
126
193
|
},
|
|
127
194
|
"licenses": { // a custom check (runs last by default)
|
|
@@ -132,19 +199,101 @@ slot's raw output for structured diagnostics.
|
|
|
132
199
|
}
|
|
133
200
|
```
|
|
134
201
|
|
|
202
|
+
The `"$schema"` pointer is optional but recommended: it turns on validation and
|
|
203
|
+
autocompletion for `checkride.config.json` in editors that understand JSON
|
|
204
|
+
Schema (VS Code and friends). `checkride init` writes a version-pinned pointer
|
|
205
|
+
into the config it generates; the schema itself ships in the package at
|
|
206
|
+
[`schema/checkride.config.schema.json`](./schema/checkride.config.schema.json).
|
|
207
|
+
|
|
208
|
+
Use `"extends"` to inherit a shared preset — a file path (`"./base.json"`) or an
|
|
209
|
+
installed package (`"@acme/checkride-preset"`), or an array of them to layer
|
|
210
|
+
several. Bases merge left to right and your local config wins over all of them:
|
|
211
|
+
objects deep-merge (so overriding one field of a check keeps the rest), while
|
|
212
|
+
arrays and scalars replace outright — arrays are **not** concatenated. Pair it
|
|
213
|
+
with `detect` above to publish one org-wide preset that stays safe across repos
|
|
214
|
+
that don't all use the same tools. An `extends` that can't be found, or a config
|
|
215
|
+
that extends itself in a loop, fails fast with
|
|
216
|
+
`invalid checkride.config.json: <reason>`.
|
|
217
|
+
|
|
135
218
|
A custom check (one keyed by a name that isn't a built-in slot) runs *after* the
|
|
136
219
|
built-in catalogue by default. Set `"order": "first"` to run it ahead of every
|
|
137
|
-
built-in check instead — handy for a formatter
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
220
|
+
built-in check instead — handy for a **bespoke** formatter that should normalize
|
|
221
|
+
the tree before the linters and tests look at it. `"order": "last"` is the explicit
|
|
222
|
+
form of the default. Within each group, custom checks run in the order they appear
|
|
223
|
+
in the config.
|
|
224
|
+
|
|
225
|
+
For formatting, the blessed `format` slot (prettier, or biome) is the paved road —
|
|
226
|
+
enable it with `"format": "prettier"` and `checkride fix` writes formatting for you.
|
|
227
|
+
The `order: "first"` custom-check hatch coexists with it, for a one-off formatter the
|
|
228
|
+
slot doesn't cover; the slot didn't retire it.
|
|
229
|
+
|
|
230
|
+
Add `"detect": ["<file>", …]` to a custom check to gate it on marker files: it
|
|
231
|
+
runs only when at least one listed file exists in the repo, and is skipped —
|
|
232
|
+
skipped, not failed — otherwise. This keeps a shared config safe across repos
|
|
233
|
+
that don't all use the same tools: a check for a tool a given repo lacks quietly
|
|
234
|
+
stands down instead of lighting up red. `detect` applies only to custom checks
|
|
235
|
+
that run alongside the catalogue; a custom check that fills a built-in slot
|
|
236
|
+
always runs.
|
|
237
|
+
|
|
238
|
+
A per-check timeout guards against a hung tool, and it is **on by default**: a
|
|
239
|
+
definition-of-done gate that can hang forever fails its one job on the worst
|
|
240
|
+
day. The default cap is a generous **600 seconds per check**; a check that hits
|
|
241
|
+
it gets SIGTERM (then SIGKILL after a short grace) and is recorded as failed
|
|
242
|
+
with a `timed out after 600s` note — red, never vacuous. Tune it with a global
|
|
243
|
+
`timeout` (seconds), override it per check (`"timeout": <n>`), or set
|
|
244
|
+
`"timeout": 0` (globally or per check) to disable the cap. Give `dead`, `test`,
|
|
245
|
+
and `mutation` a higher cap — or `0` — on a large repo where they legitimately
|
|
246
|
+
run long.
|
|
247
|
+
|
|
248
|
+
## Baseline
|
|
249
|
+
|
|
250
|
+
Adopting checkride on an existing repo shouldn't be a cleanup project. A
|
|
251
|
+
**baseline** grandfathers the diagnostics a repo has *today* so day-one runs pass,
|
|
252
|
+
while any *new* diagnostic still fails — "don't make it worse" as the definition of
|
|
253
|
+
done for legacy code.
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
checkride baseline # record current diagnostics into checkride.baseline.json
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`checkride.baseline.json` lives at the repo root beside `checkride.config.json` and
|
|
260
|
+
**is committed** — it must be in version control to work. It records a per-slot set
|
|
261
|
+
of stable *fingerprints* (a `file:rule:message` key that survives line moves), not
|
|
262
|
+
raw output:
|
|
263
|
+
|
|
264
|
+
```jsonc
|
|
265
|
+
{
|
|
266
|
+
"schema_version": 1,
|
|
267
|
+
"slots": {
|
|
268
|
+
"lint": ["src/legacy.ts:no-explicit-any:Unexpected any"],
|
|
269
|
+
"spell": ["docs/old.md::teh"]
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Once it exists, every normal run is **baseline-aware**:
|
|
275
|
+
|
|
276
|
+
- Each slot's current findings have the grandfathered ones subtracted. A slot is
|
|
277
|
+
**green when only baselined findings remain**, and **fails listing only the new
|
|
278
|
+
ones** — the raw `.check/<slot>.json` still holds everything, and the failing
|
|
279
|
+
check gains a `"baselined": <n>` count.
|
|
280
|
+
- The baseline is a **ratchet**: fixing a grandfathered finding prunes it from the
|
|
281
|
+
file (it only ever shrinks), so debt can't silently creep back. A partial run
|
|
282
|
+
(`--only`, `--skip`, `--changed`, or a `--bail` that stops early) never prunes —
|
|
283
|
+
it can't tell an unobserved finding from a fixed one, so it leaves the baseline
|
|
284
|
+
untouched.
|
|
285
|
+
- Never add to the baseline to make a check pass; fix the finding, or re-run
|
|
286
|
+
`checkride baseline` deliberately to re-grandfather.
|
|
287
|
+
|
|
288
|
+
Only slots whose tool has a fingerprint extractor participate (currently `lint` via
|
|
289
|
+
oxlint, `struct` via ast-grep, `spell` via cspell); other slots (`types`, `dead`,
|
|
290
|
+
`test`, …) never appear in the baseline. A crash or empty output is never masked —
|
|
291
|
+
a slot only goes green when there are findings and all of them are grandfathered.
|
|
292
|
+
|
|
293
|
+
To adopt on an existing repo, `checkride init --baseline` grandfathers today's
|
|
294
|
+
failing (fingerprintable) slots into the baseline and keeps them enabled, instead of
|
|
295
|
+
writing them off as `false`; a failing slot with no extractor still falls back to a
|
|
296
|
+
disable.
|
|
148
297
|
|
|
149
298
|
## Project shapes
|
|
150
299
|
|
|
@@ -171,7 +320,23 @@ Module boundaries, enforced by `ast-grep` and `fallow`:
|
|
|
171
320
|
- Named exports only; no classes; `.js` extensions on relative imports
|
|
172
321
|
(NodeNext); tests colocated with the code they cover.
|
|
173
322
|
|
|
174
|
-
|
|
323
|
+
## Tested envelope
|
|
324
|
+
|
|
325
|
+
Every push runs the full suite — unit, contract, and end-to-end (generated
|
|
326
|
+
projects, installed and checked for real) — on **macOS and Linux**, at **Node
|
|
327
|
+
22.18.0 (the exact supported floor) and Node 24**, and the e2e suite exercises
|
|
328
|
+
the full package-manager quartet: **pnpm, npm, yarn, and bun**. Windows is not
|
|
329
|
+
tested and not claimed; it waits for a real consumer who needs it.
|
|
330
|
+
|
|
331
|
+
The promised surfaces (exit codes, `summary.json` shape, flags, exports) are
|
|
332
|
+
locked by a dedicated [contract suite](./test/contract/) — see
|
|
333
|
+
[docs/contract.md](./docs/contract.md). Beyond line coverage (enforced at 70%),
|
|
334
|
+
the test suite is itself tested: Stryker mutation testing runs with a hard
|
|
335
|
+
floor of 55; the current mutation score is **67%** (`pnpm mutation` to
|
|
336
|
+
reproduce).
|
|
337
|
+
|
|
338
|
+
See [AGENTS.md](./AGENTS.md) for the contract agents follow,
|
|
339
|
+
[CONTRIBUTING.md](./CONTRIBUTING.md) for the release ritual, and
|
|
175
340
|
[CHANGELOG.md](./CHANGELOG.md) for release notes.
|
|
176
341
|
|
|
177
342
|
## License
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* data only: no logic lives here. Resolution (config vs detection) lives in
|
|
7
7
|
* `../config`, and execution lives in `../orchestrator`.
|
|
8
8
|
*
|
|
9
|
-
* Phase 1 ships the blessed defaults plus the
|
|
10
|
-
*
|
|
9
|
+
* Phase 1 ships the blessed defaults plus the opt-in slots (format, mutation,
|
|
10
|
+
* security, and the library-publishing pair publint + attw). Alternates (biome,
|
|
11
|
+
* knip, eslint, jest) land in Phase 2.
|
|
11
12
|
*/
|
|
12
13
|
/** The aggregate-report schema version written to `.check/summary.json`. */
|
|
13
14
|
export declare const SCHEMA_VERSION = 1;
|
|
@@ -15,7 +16,11 @@ export declare const SCHEMA_VERSION = 1;
|
|
|
15
16
|
export type Slot = {
|
|
16
17
|
/** Stable slot name, e.g. `'lint'`. Used by `--only`/`--skip` and the report. */
|
|
17
18
|
name: string;
|
|
18
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Opt-in slots are excluded from the default run. Enable one with
|
|
21
|
+
* `--all`/`--include <slot>`, or by naming it in `checks` (an explicit config
|
|
22
|
+
* entry opts you in — see `resolveChecks`/`selectChecks`).
|
|
23
|
+
*/
|
|
19
24
|
optIn?: boolean;
|
|
20
25
|
};
|
|
21
26
|
/** A concrete tool that can fill a slot. */
|
|
@@ -38,7 +43,7 @@ export type Adapter = {
|
|
|
38
43
|
changedArgs?: string[];
|
|
39
44
|
/** Used by `checkride fix`. */
|
|
40
45
|
fixArgs?: string[];
|
|
41
|
-
/** Per-check timeout in seconds; `0` disables the cap. Falls back to the config-level `timeout
|
|
46
|
+
/** Per-check timeout in seconds; `0` disables the cap. Falls back to the config-level `timeout`, then the built-in 600s default. */
|
|
42
47
|
timeout?: number;
|
|
43
48
|
/** In-process check id (e.g. `'links'`); when set, the orchestrator runs it directly. */
|
|
44
49
|
builtin?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,8BAA8B;AAC9B,MAAM,MAAM,IAAI,GAAG;IACjB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,4CAA4C;AAC5C,MAAM,MAAM,OAAO,GAAG;IACpB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qFAAqF;IACrF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oIAAoI;IACpI,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,KAAK,EAAE,SAAS,IAAI,EAchC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,OAAO,EAkPtC,CAAC"}
|
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
* data only: no logic lives here. Resolution (config vs detection) lives in
|
|
7
7
|
* `../config`, and execution lives in `../orchestrator`.
|
|
8
8
|
*
|
|
9
|
-
* Phase 1 ships the blessed defaults plus the
|
|
10
|
-
*
|
|
9
|
+
* Phase 1 ships the blessed defaults plus the opt-in slots (format, mutation,
|
|
10
|
+
* security, and the library-publishing pair publint + attw). Alternates (biome,
|
|
11
|
+
* knip, eslint, jest) land in Phase 2.
|
|
11
12
|
*/
|
|
12
13
|
/** The aggregate-report schema version written to `.check/summary.json`. */
|
|
13
14
|
export const SCHEMA_VERSION = 1;
|
|
14
15
|
/** Pipeline order. Cheapest first so `--bail` fails fast. */
|
|
15
16
|
export const SLOTS = [
|
|
16
17
|
{ name: 'types' },
|
|
18
|
+
{ name: 'format', optIn: true },
|
|
17
19
|
{ name: 'lint' },
|
|
18
20
|
{ name: 'struct' },
|
|
19
21
|
{ name: 'dead' },
|
|
@@ -23,6 +25,8 @@ export const SLOTS = [
|
|
|
23
25
|
{ name: 'spell' },
|
|
24
26
|
{ name: 'mutation', optIn: true },
|
|
25
27
|
{ name: 'security', optIn: true },
|
|
28
|
+
{ name: 'publint', optIn: true },
|
|
29
|
+
{ name: 'attw', optIn: true },
|
|
26
30
|
];
|
|
27
31
|
/**
|
|
28
32
|
* Adapter registry. The FIRST adapter for a slot is its blessed default: it
|
|
@@ -40,6 +44,42 @@ export const ADAPTERS = [
|
|
|
40
44
|
outputFile: null,
|
|
41
45
|
devDeps: { typescript: '6.0.3', '@types/node': '22.20.0' },
|
|
42
46
|
},
|
|
47
|
+
{
|
|
48
|
+
name: 'prettier',
|
|
49
|
+
slot: 'format',
|
|
50
|
+
description: 'Prettier formatting check',
|
|
51
|
+
detect: [
|
|
52
|
+
'.prettierrc',
|
|
53
|
+
'.prettierrc.json',
|
|
54
|
+
'.prettierrc.yml',
|
|
55
|
+
'.prettierrc.yaml',
|
|
56
|
+
'.prettierrc.json5',
|
|
57
|
+
'.prettierrc.js',
|
|
58
|
+
'.prettierrc.cjs',
|
|
59
|
+
'.prettierrc.mjs',
|
|
60
|
+
'.prettierrc.toml',
|
|
61
|
+
'prettier.config.js',
|
|
62
|
+
'prettier.config.cjs',
|
|
63
|
+
'prettier.config.mjs',
|
|
64
|
+
'prettier.config.ts',
|
|
65
|
+
],
|
|
66
|
+
command: 'pnpm',
|
|
67
|
+
args: ['exec', 'prettier', '--check', '.'],
|
|
68
|
+
outputFile: null,
|
|
69
|
+
fixArgs: ['exec', 'prettier', '--write', '.'],
|
|
70
|
+
devDeps: { prettier: '3.6.2' },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'biome-format',
|
|
74
|
+
slot: 'format',
|
|
75
|
+
description: 'Biome formatting check',
|
|
76
|
+
detect: ['biome.json', 'biome.jsonc'],
|
|
77
|
+
command: 'pnpm',
|
|
78
|
+
args: ['exec', 'biome', 'format', '.'],
|
|
79
|
+
outputFile: null,
|
|
80
|
+
fixArgs: ['exec', 'biome', 'format', '--write', '.'],
|
|
81
|
+
devDeps: { '@biomejs/biome': '2.2.4' },
|
|
82
|
+
},
|
|
43
83
|
{
|
|
44
84
|
name: 'oxlint',
|
|
45
85
|
slot: 'lint',
|
|
@@ -215,5 +255,25 @@ export const ADAPTERS = [
|
|
|
215
255
|
outputFile: 'security.json',
|
|
216
256
|
devDeps: {},
|
|
217
257
|
},
|
|
258
|
+
{
|
|
259
|
+
name: 'publint',
|
|
260
|
+
slot: 'publint',
|
|
261
|
+
description: 'publint: package.json publishing correctness',
|
|
262
|
+
detect: [],
|
|
263
|
+
command: 'pnpm',
|
|
264
|
+
args: ['exec', 'publint'],
|
|
265
|
+
outputFile: null,
|
|
266
|
+
devDeps: { publint: '0.3.21' },
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: 'attw',
|
|
270
|
+
slot: 'attw',
|
|
271
|
+
description: 'Are the types wrong? (type resolution across module systems)',
|
|
272
|
+
detect: [],
|
|
273
|
+
command: 'pnpm',
|
|
274
|
+
args: ['exec', 'attw', '--pack', '.', '--format', 'json'],
|
|
275
|
+
outputFile: 'attw.json',
|
|
276
|
+
devDeps: { '@arethetypeswrong/cli': '0.18.4' },
|
|
277
|
+
},
|
|
218
278
|
];
|
|
219
279
|
//# sourceMappingURL=adapters.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Stop hook — the mechanical half of the agent contract.
|
|
3
|
+
*
|
|
4
|
+
* `init` and `checkride agent-setup` write an idempotent Stop hook into
|
|
5
|
+
* `.claude/settings.json`. It runs the project's `check` script as a hard gate:
|
|
6
|
+
* exit 2 blocks the agent from finishing while the pipeline is red, so "exit 0 =
|
|
7
|
+
* done" becomes enforcement, not just advice. The command uses the *detected*
|
|
8
|
+
* package manager's run form (`pnpm run check`, `npm run check`, `yarn run
|
|
9
|
+
* check`, `bun run check`) so the hook works in any repo, not only pnpm ones
|
|
10
|
+
* (b7). Merging is surgical: unrelated hooks, other Stop groups, and every other
|
|
11
|
+
* settings key are preserved, and re-applying with the same PM is a no-op.
|
|
12
|
+
*
|
|
13
|
+
* This module owns only the hook; the AGENTS.md stanza and the higher-level
|
|
14
|
+
* `agent-setup` command live in `init.ts`, which imports the writer here — a
|
|
15
|
+
* single direction, so there is no cycle.
|
|
16
|
+
*/
|
|
17
|
+
import { type PackageManager } from '../pm/index.js';
|
|
18
|
+
/** Project-shared Claude Code settings (committed), relative to the repo root. */
|
|
19
|
+
export declare const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
|
|
20
|
+
/** A single Claude Code command hook (fields optional — settings.json is untrusted). */
|
|
21
|
+
type CommandHook = {
|
|
22
|
+
type?: string;
|
|
23
|
+
command?: string;
|
|
24
|
+
};
|
|
25
|
+
/** A Stop-hook group. Only `hooks` is meaningful for Stop; other keys pass through. */
|
|
26
|
+
type StopGroup = {
|
|
27
|
+
hooks?: CommandHook[];
|
|
28
|
+
} & Record<string, unknown>;
|
|
29
|
+
/** The subset of `.claude/settings.json` we touch; every other key is preserved. */
|
|
30
|
+
type ClaudeSettings = {
|
|
31
|
+
hooks?: {
|
|
32
|
+
Stop?: StopGroup[];
|
|
33
|
+
} & Record<string, unknown>;
|
|
34
|
+
} & Record<string, unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* The Stop-hook command for `pm`: run the gate, and on red print guidance and
|
|
37
|
+
* exit 2 (the blocking code — a plain exit 1 lets the agent stop anyway).
|
|
38
|
+
*/
|
|
39
|
+
export declare function stopHookCommand(pm: PackageManager): string;
|
|
40
|
+
/**
|
|
41
|
+
* Merge checkride's Stop hook into parsed settings, idempotently. An existing
|
|
42
|
+
* checkride hook (found by its sentinel) has its command refreshed in place;
|
|
43
|
+
* otherwise a new Stop group is appended. Sibling hooks in the same group, other
|
|
44
|
+
* Stop groups, and every unrelated settings key are left untouched, so applying
|
|
45
|
+
* twice with the same command yields deep-equal settings.
|
|
46
|
+
*/
|
|
47
|
+
export declare function applyStopHook(settings: ClaudeSettings, command: string): ClaudeSettings;
|
|
48
|
+
export type StopHookResult = {
|
|
49
|
+
path: string;
|
|
50
|
+
changed: boolean;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Write or refresh the Stop hook in `cwd/.claude/settings.json` for the detected
|
|
54
|
+
* (or provided) package manager, preserving any other settings. Returns whether
|
|
55
|
+
* the file changed: a second run with the same PM leaves it byte-identical, so
|
|
56
|
+
* `changed` is `false`. `dryRun` computes the result without writing.
|
|
57
|
+
*/
|
|
58
|
+
export declare function writeStopHook(cwd: string, opts?: {
|
|
59
|
+
pm?: PackageManager;
|
|
60
|
+
dryRun?: boolean;
|
|
61
|
+
}): Promise<StopHookResult>;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/agent-setup/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE3E,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAS5D,wFAAwF;AACxF,KAAK,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACvD,uFAAuF;AACvF,KAAK,SAAS,GAAG;IAAE,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACrE,oFAAoF;AACpF,KAAK,cAAc,GAAG;IAAE,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAO7G;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAI1D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAcvF;AAUD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhE;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,EAAE,CAAC,EAAE,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,cAAc,CAAC,CAazB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Stop hook — the mechanical half of the agent contract.
|
|
3
|
+
*
|
|
4
|
+
* `init` and `checkride agent-setup` write an idempotent Stop hook into
|
|
5
|
+
* `.claude/settings.json`. It runs the project's `check` script as a hard gate:
|
|
6
|
+
* exit 2 blocks the agent from finishing while the pipeline is red, so "exit 0 =
|
|
7
|
+
* done" becomes enforcement, not just advice. The command uses the *detected*
|
|
8
|
+
* package manager's run form (`pnpm run check`, `npm run check`, `yarn run
|
|
9
|
+
* check`, `bun run check`) so the hook works in any repo, not only pnpm ones
|
|
10
|
+
* (b7). Merging is surgical: unrelated hooks, other Stop groups, and every other
|
|
11
|
+
* settings key are preserved, and re-applying with the same PM is a no-op.
|
|
12
|
+
*
|
|
13
|
+
* This module owns only the hook; the AGENTS.md stanza and the higher-level
|
|
14
|
+
* `agent-setup` command live in `init.ts`, which imports the writer here — a
|
|
15
|
+
* single direction, so there is no cycle.
|
|
16
|
+
*/
|
|
17
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
import { detectPackageManager } from '../pm/index.js';
|
|
20
|
+
/** Project-shared Claude Code settings (committed), relative to the repo root. */
|
|
21
|
+
export const CLAUDE_SETTINGS_FILE = '.claude/settings.json';
|
|
22
|
+
/**
|
|
23
|
+
* Stable, package-manager-independent marker identifying checkride's Stop hook
|
|
24
|
+
* inside a settings file. The run prefix varies by PM, so identity keys on the
|
|
25
|
+
* guidance message, which does not.
|
|
26
|
+
*/
|
|
27
|
+
const HOOK_SENTINEL = 'checkride: the gate is red';
|
|
28
|
+
/** True when a hook entry is checkride's (matched by the PM-independent sentinel). */
|
|
29
|
+
function isCheckrideHook(hook) {
|
|
30
|
+
return hook.type === 'command' && typeof hook.command === 'string' && hook.command.includes(HOOK_SENTINEL);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The Stop-hook command for `pm`: run the gate, and on red print guidance and
|
|
34
|
+
* exit 2 (the blocking code — a plain exit 1 lets the agent stop anyway).
|
|
35
|
+
*/
|
|
36
|
+
export function stopHookCommand(pm) {
|
|
37
|
+
const run = `${pm} run check`;
|
|
38
|
+
const guidance = `${HOOK_SENTINEL} — read .check/summary.json, fix the failing slot, then finish (do not stop while checkride is red).`;
|
|
39
|
+
return `${run} || { echo '${guidance}' >&2; exit 2; }`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Merge checkride's Stop hook into parsed settings, idempotently. An existing
|
|
43
|
+
* checkride hook (found by its sentinel) has its command refreshed in place;
|
|
44
|
+
* otherwise a new Stop group is appended. Sibling hooks in the same group, other
|
|
45
|
+
* Stop groups, and every unrelated settings key are left untouched, so applying
|
|
46
|
+
* twice with the same command yields deep-equal settings.
|
|
47
|
+
*/
|
|
48
|
+
export function applyStopHook(settings, command) {
|
|
49
|
+
const hooks = { ...settings.hooks };
|
|
50
|
+
const stop = Array.isArray(hooks.Stop) ? hooks.Stop.map((g) => ({ ...g })) : [];
|
|
51
|
+
const idx = stop.findIndex((g) => (g.hooks ?? []).some(isCheckrideHook));
|
|
52
|
+
const existing = idx >= 0 ? stop[idx] : undefined;
|
|
53
|
+
if (existing) {
|
|
54
|
+
stop[idx] = {
|
|
55
|
+
...existing,
|
|
56
|
+
hooks: (existing.hooks ?? []).map((h) => (isCheckrideHook(h) ? { ...h, command } : h)),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
stop.push({ hooks: [{ type: 'command', command }] });
|
|
61
|
+
}
|
|
62
|
+
return { ...settings, hooks: { ...hooks, Stop: stop } };
|
|
63
|
+
}
|
|
64
|
+
async function readIfExists(path) {
|
|
65
|
+
try {
|
|
66
|
+
return await readFile(path, 'utf8');
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Write or refresh the Stop hook in `cwd/.claude/settings.json` for the detected
|
|
74
|
+
* (or provided) package manager, preserving any other settings. Returns whether
|
|
75
|
+
* the file changed: a second run with the same PM leaves it byte-identical, so
|
|
76
|
+
* `changed` is `false`. `dryRun` computes the result without writing.
|
|
77
|
+
*/
|
|
78
|
+
export async function writeStopHook(cwd, opts = {}) {
|
|
79
|
+
const path = join(cwd, CLAUDE_SETTINGS_FILE);
|
|
80
|
+
const pm = opts.pm ?? detectPackageManager({ cwd });
|
|
81
|
+
const raw = await readIfExists(path);
|
|
82
|
+
const settings = raw ? JSON.parse(raw) : {};
|
|
83
|
+
const next = applyStopHook(settings, stopHookCommand(pm));
|
|
84
|
+
const nextRaw = `${JSON.stringify(next, null, 2)}\n`;
|
|
85
|
+
const changed = raw !== nextRaw;
|
|
86
|
+
if (changed && !opts.dryRun) {
|
|
87
|
+
await mkdir(dirname(path), { recursive: true });
|
|
88
|
+
await writeFile(path, nextRaw);
|
|
89
|
+
}
|
|
90
|
+
return { path: CLAUDE_SETTINGS_FILE, changed };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/agent-setup/hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-setup module — the Claude Code Stop hook (step 12). This barrel is the
|
|
3
|
+
* module's only public surface (C2): siblings import from `../agent-setup`,
|
|
4
|
+
* never from `./hook.js` directly.
|
|
5
|
+
*/
|
|
6
|
+
export type { StopHookResult } from './hook.js';
|
|
7
|
+
export { applyStopHook, CLAUDE_SETTINGS_FILE, stopHookCommand, writeStopHook } from './hook.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-setup/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-setup module — the Claude Code Stop hook (step 12). This barrel is the
|
|
3
|
+
* module's only public surface (C2): siblings import from `../agent-setup`,
|
|
4
|
+
* never from `./hook.js` directly.
|
|
5
|
+
*/
|
|
6
|
+
export { applyStopHook, CLAUDE_SETTINGS_FILE, stopHookCommand, writeStopHook } from './hook.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|