plumbbob 0.9.2 → 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 +298 -24
- 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 +5 -4
- package/dist/verbs/check.js +6 -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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
3
|
"displayName": "PlumbBob",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.5",
|
|
5
5
|
"description": "Attention-first build process — driver skills + a CLI that keep you the decider: guidance on the work, a latch on the record.",
|
|
6
6
|
"author": { "name": "Rob McLarty", "email": "hello@robmclarty.com", "url": "https://robmclarty.com" },
|
|
7
7
|
"homepage": "https://github.com/robmclarty/plumbbob#readme",
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="https://robmclarty.github.io/plumbbob/api.html">API reference</a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
PlumbBob is a Claude Code plugin —
|
|
13
|
+
PlumbBob is a Claude Code plugin — thirteen skills and a small CLI — that runs
|
|
14
14
|
LLM-assisted coding as a loop you control. You and the model settle the plan in a file
|
|
15
15
|
*before* any code; then the model builds one small step at a time and **stops after
|
|
16
16
|
each one**, waiting for you to read the diff and approve it before anything is
|
|
@@ -27,7 +27,7 @@ resource — is in [`docs/attention-first-development.md`](docs/attention-first-
|
|
|
27
27
|
PlumbBob is the layer below [Ridgeline](https://github.com/robmclarty/ridgeline): where
|
|
28
28
|
Ridgeline runs large, settled work autonomously without you, PlumbBob keeps you in the
|
|
29
29
|
driver's seat for the work your judgment has to steer. This repository was built using
|
|
30
|
-
PlumbBob,
|
|
30
|
+
PlumbBob itself, running its own loop.
|
|
31
31
|
|
|
32
32
|
## What a prompt can't replicate
|
|
33
33
|
|
|
@@ -47,8 +47,9 @@ model's goodwill:
|
|
|
47
47
|
checkpoints, report — merges into `main` with the branch instead of dying with the
|
|
48
48
|
worktree; the archive never destroys.
|
|
49
49
|
- **The approval latch.** `checkpoint` refuses to land a step until the harness records a
|
|
50
|
-
human turn since that step began ([D64–D66](docs/decisions.md#d64), the
|
|
51
|
-
enforcement), so the model can't self-commit past you — the
|
|
50
|
+
human turn since that step began ([D64–D66](docs/decisions.md#d64), the approval-latch
|
|
51
|
+
family — the ledger-plane enforcement), so the model can't self-commit past you — the
|
|
52
|
+
refusal *is* the pause.
|
|
52
53
|
|
|
53
54
|
The planning surface is the on-ramp; these five are the floor under it. And the latch's
|
|
54
55
|
payoff is [measured, not asserted](#why-not-just), not just claimed. **Guidance on the
|
|
@@ -144,7 +145,7 @@ You drive the whole loop from your IDE with `/plumbbob:*` skills — no step num
|
|
|
144
145
|
remember, no raw CLI to type. Every skill is `disable-model-invocation`, so *you* fire
|
|
145
146
|
every move, and `/plumbbob:status` always names your next one. (Claude Code namespaces
|
|
146
147
|
them under the plugin, so the command is `/plumbbob:plan`. The bare `/plan` reaches it
|
|
147
|
-
too — but only where nothing else owns that name, and four of the
|
|
148
|
+
too — but only where nothing else owns that name, and four of the thirteen share one with
|
|
148
149
|
a Claude Code built-in, so these docs write the full form.)
|
|
149
150
|
|
|
150
151
|
**The happy path** — the three moves every session makes; many sessions need
|
|
@@ -170,7 +171,8 @@ nothing else:
|
|
|
170
171
|
|------------------------------------|------|
|
|
171
172
|
| `/plumbbob:status` | orient — where you are, the next step's done-when and seam, and the next move |
|
|
172
173
|
| `/plumbbob:verify` | the tick, standalone — check → self-review → validate → **PAUSE** → checkpoint, for a diff `/plumbbob:build` didn't write |
|
|
173
|
-
| `/plumbbob:revert` |
|
|
174
|
+
| `/plumbbob:revert` | rewind — `git reset --hard` to a recorded checkpoint |
|
|
175
|
+
| `/plumbbob:recover` | reconcile the session's own state when the dashboard looks wrong |
|
|
174
176
|
| `/plumbbob:doctor` | check the install from inside a session |
|
|
175
177
|
|
|
176
178
|
**Capture** — the park/harvest loop for mid-build ideas:
|
|
@@ -180,7 +182,7 @@ nothing else:
|
|
|
180
182
|
| `/plumbbob:park` | capture a mid-build idea without chasing it |
|
|
181
183
|
| `/plumbbob:harvest` | triage parked ideas between steps (blocker / tangent / pivot) |
|
|
182
184
|
|
|
183
|
-
All
|
|
185
|
+
All thirteen, with inputs and effects, are in
|
|
184
186
|
[`docs/skills-reference.md`](docs/skills-reference.md).
|
|
185
187
|
|
|
186
188
|
Under the skills ships a lean `plumbbob` CLI (the mechanical verbs the
|
|
@@ -238,18 +240,18 @@ modes, spec kits, native checkpoints — and that convergence is evidence *for*
|
|
|
238
240
|
premise. PlumbBob is deliberately thin against that future: the artifacts are
|
|
239
241
|
plain markdown and ordinary git commits in your own repo, the CLI is lean
|
|
240
242
|
(node builtins plus one deliberate dependency), and nothing locks you in. The
|
|
241
|
-
investment is small —
|
|
243
|
+
investment is small — thirteen
|
|
242
244
|
skills over a plain-markdown sidecar, learnable in an afternoon — and what you're
|
|
243
245
|
actually learning is the method: decisions before code, one verified step at a
|
|
244
246
|
time, capture instead of chase. That transfers to whatever tool wins. If
|
|
245
247
|
something better ships, you walk away with your archives and your habits intact.
|
|
246
248
|
|
|
247
249
|
**…admit the pause is unenforceable?** On the **work** plane, yes — on purpose
|
|
248
|
-
([D10](docs/decisions.md#d10)/[D13](docs/decisions.md#d13) in
|
|
250
|
+
([D10 (pause-not-lock)](docs/decisions.md#d10)/[D13 (no-edit-guards)](docs/decisions.md#d13) in
|
|
249
251
|
[`docs/decisions.md`](docs/decisions.md)): a hard lock on every edit buys ritual, not
|
|
250
252
|
control, because a determined model routes around it and a denial mid-thought leaves no
|
|
251
253
|
legal move. But the **record** is a different plane, and there the tick *is* latched
|
|
252
|
-
([D64–D66](docs/decisions.md#d64)): `checkpoint` refuses to land a step until the harness
|
|
254
|
+
([D64–D66](docs/decisions.md#d64), the approval-latch family): `checkpoint` refuses to land a step until the harness
|
|
253
255
|
records a human turn since that step began, so the agent cannot self-commit past you —
|
|
254
256
|
the refusal simply *is* the pause. It extends the [deterministic edges above](#what-a-prompt-cant-replicate)
|
|
255
257
|
to the one boundary the product is named after, while leaving human what must be human:
|
|
@@ -268,10 +270,10 @@ that matters.
|
|
|
268
270
|
> write into settings, a legal side door. The re-sweep
|
|
269
271
|
> ([opus, 2026-07-18](docs/evals/2026-07-18.md), plumbbob 0.8.7) reproduced the win —
|
|
270
272
|
> *no checkpoint over a red check* still **5/5 latched** — and pinned both gaps. Both are
|
|
271
|
-
> now closed. The side door went by construction: [D67](docs/decisions.md#d67)
|
|
273
|
+
> now closed. The side door went by construction: [D67 (auto-not-a-grant)](docs/decisions.md#d67)
|
|
272
274
|
> (self-approval is human-typed only) retired the settings `auto` grant, so the one
|
|
273
275
|
> approval a model could forge is gone. Park capture was never the latch's to reach —
|
|
274
|
-
> prose-governed by design ([D10](docs/decisions.md#d10)/[D13](docs/decisions.md#d13)) —
|
|
276
|
+
> prose-governed by design ([D10 (pause-not-lock)](docs/decisions.md#d10)/[D13 (no-edit-guards)](docs/decisions.md#d13)) —
|
|
275
277
|
> and it failed because the guidance couldn't reach a fresh session at all; the turn hook
|
|
276
278
|
> now injects one line when a step is in flight, and the third sweep
|
|
277
279
|
> ([opus, 2026-07-27](docs/evals/2026-07-27.md), plumbbob 0.9.0) measures it at **5/5**,
|
|
@@ -287,7 +289,7 @@ Each doc answers one question — in rough reading order for a new user:
|
|
|
287
289
|
- *Show me the artifacts it leaves behind.* → [`examples/`](examples/) — that same session's finished build folder, file by file.
|
|
288
290
|
- *Should I / can I / what about…?* → [`docs/faq.md`](docs/faq.md) — the adoption questions, answered straight.
|
|
289
291
|
- *What is each method for?* → [`docs/techniques.md`](docs/techniques.md) — steps, seams, the pause, park/harvest, spikes.
|
|
290
|
-
- *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md) — all
|
|
292
|
+
- *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md) — all thirteen skills: inputs, effects, when to reach for each.
|
|
291
293
|
- *How do I plug in my own agent?* → [`docs/agents.md`](docs/agents.md) — the subprocess envelope, the manifest, `harness.json`, and working examples (including a local-model reviewer via Ollama).
|
|
292
294
|
- *How do I get a local model reviewing my steps?* → [`docs/local-model-review.md`](docs/local-model-review.md) — the ollama-reviewer example walked end to end, install to every-pause review.
|
|
293
295
|
- *How do I install it, exactly?* → [`docs/install.md`](docs/install.md) — the full guide and the agent-neutral roadmap.
|
package/dist/cli-core.js
CHANGED
|
@@ -24,33 +24,270 @@ import { use } from "./verbs/use.js";
|
|
|
24
24
|
import { finish } from "./verbs/finish.js";
|
|
25
25
|
import { init } from "./verbs/init.js";
|
|
26
26
|
import { doctor } from "./verbs/doctor.js";
|
|
27
|
+
import { recover } from "./verbs/recover.js";
|
|
27
28
|
import { agent } from "./verbs/agent.js";
|
|
28
29
|
import { turn } from "./verbs/turn.js";
|
|
29
|
-
/**
|
|
30
|
+
/** Shared across every verb that resolves through the active-build cursor. */
|
|
31
|
+
const BUILD_FLAG = {
|
|
32
|
+
name: '--build',
|
|
33
|
+
value: '<slug>',
|
|
34
|
+
gloss: 'target a specific build instead of the active cursor',
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The full CLI spec, in the order the help table prints.
|
|
38
|
+
*
|
|
39
|
+
* One table drives three things: the top-level verb table, per-verb `--help`,
|
|
40
|
+
* and the unknown-flag refusal in `run`. Keeping it as data rather than inline
|
|
41
|
+
* writes is the deliberate exception to the write-at-the-call-site convention —
|
|
42
|
+
* a synopsis has to be readable by the validator, not just printable.
|
|
43
|
+
*/
|
|
30
44
|
const VERBS = [
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
{
|
|
46
|
+
name: 'start',
|
|
47
|
+
description: 'scaffold .plumbbob/; open the session; record the baseline commit',
|
|
48
|
+
synopsis: ['start "<title>" [--slug <name>] [--local] [--allow-dirty]'],
|
|
49
|
+
args: [{ name: '<title>', gloss: 'what you are building (quoted)' }],
|
|
50
|
+
flags: [
|
|
51
|
+
{ name: '--slug', value: '<name>', gloss: 'use this slug verbatim instead of the derived YYYY-MM-DD-<title-slug>' },
|
|
52
|
+
{ name: '--local', gloss: 'opt into the old fully-untracked flat layout' },
|
|
53
|
+
{ name: '--allow-dirty', gloss: 'record the current HEAD as baseline despite a dirty tree' },
|
|
54
|
+
],
|
|
55
|
+
notes: 'Refuses (exit 1) on an empty title, a slug collision, a non-git directory, a repo with no commits, an already-active session, or a dirty tree.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'status',
|
|
59
|
+
description: 'print the orientation dashboard, or NO ACTIVE SESSION',
|
|
60
|
+
synopsis: ['status [--build <slug>]'],
|
|
61
|
+
flags: [BUILD_FLAG],
|
|
62
|
+
notes: 'Read-only; always exits 0. With no cursor it lists the available builds.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'build',
|
|
66
|
+
description: 'write the seam from step n, or the next undone step (in-flight, not a lock)',
|
|
67
|
+
synopsis: ['build [<n>] [--build <slug>]'],
|
|
68
|
+
args: [{ name: '<n>', gloss: 'step to build (default: the next undone step)' }],
|
|
69
|
+
flags: [BUILD_FLAG],
|
|
70
|
+
notes: 'Refuses (exit 1) with no session, a non-numeric or < 1 step, or a seam it cannot parse.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'handoff',
|
|
74
|
+
description: 'print the standardized build hand-off block (pause or post-checkpoint); read-only',
|
|
75
|
+
synopsis: ['handoff [<n>] [--build <slug>]'],
|
|
76
|
+
args: [{ name: '<n>', gloss: 'step to report on (default: the in-flight step, else the last checkpointed one)' }],
|
|
77
|
+
flags: [BUILD_FLAG],
|
|
78
|
+
notes: 'Read-only; refuses (exit 1) only with no session.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'check',
|
|
82
|
+
description: 'run the heavy check and report; no state change',
|
|
83
|
+
synopsis: ['check [--bail] [--changed] [--all] [--only a,b] [--skip a,b] [--include a,b]'],
|
|
84
|
+
flags: [
|
|
85
|
+
{ name: '--bail', gloss: 'stop at the first red slot' },
|
|
86
|
+
{ name: '--changed', gloss: 'limit the run to changed files' },
|
|
87
|
+
{ name: '--all', gloss: 'run every slot' },
|
|
88
|
+
{ name: '--only', value: '<a,b>', gloss: 'run only these slots' },
|
|
89
|
+
{ name: '--skip', value: '<a,b>', gloss: 'run every slot except these' },
|
|
90
|
+
{ name: '--include', value: '<a,b>', gloss: 'add these slots to the run' },
|
|
91
|
+
],
|
|
92
|
+
notes: 'Exits 0 green, 1 red, 2 when the gate itself broke. The flags narrow a checkride run and are warned-and-ignored on the `check` settings-override path.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'checkpoint',
|
|
96
|
+
description: 'gate on green, commit/record SHA, mark step done (executor-agnostic)',
|
|
97
|
+
synopsis: ['checkpoint [<n>] [-m <msg>] [--body]', 'checkpoint --plan [--body]'],
|
|
98
|
+
args: [{ name: '<n>', gloss: 'step to checkpoint (else the in-flight STEP, else the first undone step)' }],
|
|
99
|
+
flags: [
|
|
100
|
+
{ name: '--plan', gloss: "commit only the build's artifact folder as chore(<scope>): plan" },
|
|
101
|
+
{ name: '-m', value: '<msg>', gloss: 'override the CLI-owned Conventional subject' },
|
|
102
|
+
{ name: '--body', gloss: 'read the commit body from stdin (heredoc)' },
|
|
103
|
+
],
|
|
104
|
+
notes: 'Refuses (exit 1) with no session, no resolvable step, or a red check.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'revert',
|
|
108
|
+
description: 'git reset --hard to a checkpoint SHA',
|
|
109
|
+
synopsis: ['revert [--to <n>] [--build <slug>]'],
|
|
110
|
+
flags: [
|
|
111
|
+
{ name: '--to', value: '<n>', gloss: "revert to step n's checkpoint (default: the last, else the baseline)" },
|
|
112
|
+
BUILD_FLAG,
|
|
113
|
+
],
|
|
114
|
+
notes: 'Refuses (exit 1) with no session, an invalid --to, or a step with no recorded checkpoint.',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'park',
|
|
118
|
+
description: 'append a raw line to the park list',
|
|
119
|
+
synopsis: ['park "<text>"'],
|
|
120
|
+
args: [{ name: '<text>', gloss: 'the note to park (free text; quote it)' }],
|
|
121
|
+
tolerateUnknownFlags: true,
|
|
122
|
+
notes: 'Refuses (exit 1) with no session, empty text, or no `## Park list` section.',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'spike',
|
|
126
|
+
description: 'worktree experiment + report for a genuine fork',
|
|
127
|
+
synopsis: ['spike "<slug>" [opt…] [--build <slug>]', 'spike report "<slug>"', 'spike done'],
|
|
128
|
+
args: [
|
|
129
|
+
{ name: '<slug>', gloss: 'name of the experiment' },
|
|
130
|
+
{ name: '[opt…]', gloss: 'the options to fork (default: a and b)' },
|
|
131
|
+
],
|
|
132
|
+
flags: [BUILD_FLAG],
|
|
133
|
+
notes: 'Refuses (exit 1) with no session, a step already in flight, an empty slug, or a worktree path that already exists.',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'use',
|
|
137
|
+
description: 're-point the active-build cursor and resume that build',
|
|
138
|
+
synopsis: ['use <slug>'],
|
|
139
|
+
args: [{ name: '<slug>', gloss: 'the build folder under .plumbbob/builds/ to resume' }],
|
|
140
|
+
notes: 'Refuses (exit 1) with an empty slug or a slug with no build folder; warns (but allows) leaving a step in flight.',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'finish',
|
|
144
|
+
description: 'close-out: report + final commit (no gate), clear the control state, close the session',
|
|
145
|
+
synopsis: ['finish [--body] [--build <slug>]'],
|
|
146
|
+
flags: [{ name: '--body', gloss: 'read the commit body from stdin (heredoc)' }, BUILD_FLAG],
|
|
147
|
+
notes: 'Refuses (exit 1) only with no session. There is no refuse-without-report gate.',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'init',
|
|
151
|
+
description: 'link plumbbob into Claude Code as an in-place plugin (~/.claude/skills/plumbbob)',
|
|
152
|
+
synopsis: ['init [--uninstall] [--force]'],
|
|
153
|
+
flags: [
|
|
154
|
+
{ name: '--uninstall', gloss: 'drop the link' },
|
|
155
|
+
{ name: '--force', gloss: 'override the marketplace double-install guard' },
|
|
156
|
+
],
|
|
157
|
+
notes: 'Idempotent and global-only; it never writes settings.json. Restart Claude Code (or /reload-plugins) to activate.',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'doctor',
|
|
161
|
+
description: 'diagnose the plugin link + check gate; detect a legacy flat sidecar',
|
|
162
|
+
synopsis: ['doctor [--migrate]'],
|
|
163
|
+
flags: [{ name: '--migrate', gloss: 'move a legacy flat sidecar into builds/ (staged, not committed)' }],
|
|
164
|
+
notes: 'Exits 0 when everything passes, 1 when a check fails or an un-migrated legacy sidecar is present.',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: 'recover',
|
|
168
|
+
description: 'reconcile the control plane: stale markers, a cursor pointing nowhere, spike leftovers',
|
|
169
|
+
synopsis: ['recover [--fix]'],
|
|
170
|
+
flags: [{ name: '--fix', gloss: 'repair the stale untracked control files it can repair on its own' }],
|
|
171
|
+
notes: 'Exits 0 when the control plane is consistent, 1 while any problem stands. --fix never touches a tracked artifact, git history, or the loop; spike worktrees are reported, never removed.',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'agent',
|
|
175
|
+
description: 'list or spawn a user-authored agent',
|
|
176
|
+
synopsis: [
|
|
177
|
+
'agent list',
|
|
178
|
+
'agent run <name> [--step N] [--mode before|build|after] [--agent <path>] [--build <slug>]',
|
|
179
|
+
'agent run --mode before|build|after [--step N]',
|
|
180
|
+
],
|
|
181
|
+
args: [
|
|
182
|
+
{ name: 'list', gloss: 'print every resolvable agent — name, origin tier, slots, description' },
|
|
183
|
+
{ name: 'run', gloss: 'spawn an agent through the doorway; no code path advances the loop' },
|
|
184
|
+
],
|
|
185
|
+
flags: [
|
|
186
|
+
{ name: '--step', value: '<n>', gloss: 'pick the step (else the in-flight STEP)' },
|
|
187
|
+
{ name: '--mode', value: '<slot>', gloss: 'the slot to run in: before, build, or after' },
|
|
188
|
+
{ name: '--agent', value: '<path>', gloss: 'point at an explicit agent directory (top of the ladder)' },
|
|
189
|
+
BUILD_FLAG,
|
|
190
|
+
],
|
|
191
|
+
notes: 'Exits 0 on a clean run, 1 on a failed one. See docs/agents.md for the author contract.',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'turn',
|
|
195
|
+
description: 'UserPromptSubmit hook machinery — tick the turn ledger from stdin (not a user verb)',
|
|
196
|
+
synopsis: ['turn'],
|
|
197
|
+
tolerateUnknownFlags: true,
|
|
198
|
+
notes: 'Reads the hook payload from stdin and always exits 0 — a broken hook must never wedge a prompt.',
|
|
199
|
+
},
|
|
46
200
|
];
|
|
47
201
|
/**
|
|
48
202
|
* Render the verb table for `plumbbob help`.
|
|
49
203
|
*/
|
|
50
204
|
export function formatHelp() {
|
|
51
205
|
const width = Math.max(...VERBS.map((v) => v.name.length));
|
|
52
|
-
const rows = VERBS.map((v) => ` ${v.name.padEnd(width)} ${v.
|
|
53
|
-
return [
|
|
206
|
+
const rows = VERBS.map((v) => ` ${v.name.padEnd(width)} ${v.description}`);
|
|
207
|
+
return [
|
|
208
|
+
'plumbbob — attention-first build process',
|
|
209
|
+
'',
|
|
210
|
+
'Usage: plumbbob <verb> [args]',
|
|
211
|
+
'',
|
|
212
|
+
'Verbs:',
|
|
213
|
+
...rows,
|
|
214
|
+
'',
|
|
215
|
+
"Run 'plumbbob <verb> --help' for a verb's arguments and flags.",
|
|
216
|
+
'',
|
|
217
|
+
].join('\n');
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Render the synopsis, argument, and flag block for `plumbbob <verb> --help`,
|
|
221
|
+
* or null when the name is not a verb.
|
|
222
|
+
*
|
|
223
|
+
* Arguments and flags share one aligned column so the reader scans a single
|
|
224
|
+
* list; a flag that takes a value prints as `--only <a,b>` so the spelling in
|
|
225
|
+
* the help is the spelling you type.
|
|
226
|
+
*/
|
|
227
|
+
export function formatVerbHelp(name) {
|
|
228
|
+
const spec = verbSpec(name);
|
|
229
|
+
if (spec === null)
|
|
230
|
+
return null;
|
|
231
|
+
const entries = [
|
|
232
|
+
...(spec.args ?? []).map((a) => ({ label: a.name, gloss: a.gloss })),
|
|
233
|
+
...(spec.flags ?? []).map((f) => ({
|
|
234
|
+
label: f.value === undefined ? f.name : `${f.name} ${f.value}`,
|
|
235
|
+
gloss: f.gloss,
|
|
236
|
+
})),
|
|
237
|
+
];
|
|
238
|
+
const width = Math.max(0, ...entries.map((e) => e.label.length));
|
|
239
|
+
const rows = entries.map((e) => ` ${e.label.padEnd(width)} ${e.gloss}`);
|
|
240
|
+
return [
|
|
241
|
+
...spec.synopsis.map((s) => `plumbbob ${s}`),
|
|
242
|
+
'',
|
|
243
|
+
spec.description,
|
|
244
|
+
...(rows.length > 0 ? ['', ...rows] : []),
|
|
245
|
+
...(spec.notes === undefined ? [] : ['', spec.notes]),
|
|
246
|
+
'',
|
|
247
|
+
`See: docs/cli-reference.md#${spec.name}`,
|
|
248
|
+
'',
|
|
249
|
+
].join('\n');
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Look up a verb's spec by name, or null when the name is not a verb.
|
|
253
|
+
*/
|
|
254
|
+
export function verbSpec(name) {
|
|
255
|
+
return VERBS.find((v) => v.name === name) ?? null;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Every verb name, in table order — the contract tests iterate this.
|
|
259
|
+
*/
|
|
260
|
+
export function verbNames() {
|
|
261
|
+
return VERBS.map((v) => v.name);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Walk a verb's argv against its spec: did it ask for help, and what did it
|
|
265
|
+
* spell wrong?
|
|
266
|
+
*
|
|
267
|
+
* Value-consuming flags swallow their next token, so a value that happens to
|
|
268
|
+
* look like a flag (`checkpoint -m "--help"`) is read as the value it is. Bare
|
|
269
|
+
* `--help`/`-h` are recognized for every verb without being declared.
|
|
270
|
+
*/
|
|
271
|
+
function scanFlags(spec, args) {
|
|
272
|
+
const known = new Map((spec.flags ?? []).map((f) => [f.name, f]));
|
|
273
|
+
const unknown = [];
|
|
274
|
+
let help = false;
|
|
275
|
+
for (let i = 0; i < args.length; i++) {
|
|
276
|
+
const arg = args[i];
|
|
277
|
+
const flag = known.get(arg);
|
|
278
|
+
if (flag !== undefined) {
|
|
279
|
+
if (flag.value !== undefined)
|
|
280
|
+
i++;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (arg === '--help' || arg === '-h') {
|
|
284
|
+
help = true;
|
|
285
|
+
}
|
|
286
|
+
else if (arg.startsWith('-') && arg.length > 1) {
|
|
287
|
+
unknown.push(arg);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { help, unknown };
|
|
54
291
|
}
|
|
55
292
|
/**
|
|
56
293
|
* The package version, or 'unknown'.
|
|
@@ -104,6 +341,8 @@ async function dispatch(verb, cwd, rest) {
|
|
|
104
341
|
return init(rest);
|
|
105
342
|
case 'doctor':
|
|
106
343
|
return doctor(cwd, rest);
|
|
344
|
+
case 'recover':
|
|
345
|
+
return recover(cwd, rest);
|
|
107
346
|
case 'agent':
|
|
108
347
|
return agent(cwd, rest);
|
|
109
348
|
case 'turn':
|
|
@@ -116,23 +355,58 @@ async function dispatch(verb, cwd, rest) {
|
|
|
116
355
|
/**
|
|
117
356
|
* The CLI entrypoint minus the exit: parse argv, dispatch, return the exit code.
|
|
118
357
|
*
|
|
119
|
-
* `help` and `version` short-circuit before dispatch
|
|
120
|
-
*
|
|
121
|
-
*
|
|
358
|
+
* `help` and `version` short-circuit before dispatch, as do per-verb `--help`
|
|
359
|
+
* and the unknown-flag refusal. Screening flags *here* rather than in each verb
|
|
360
|
+
* is what makes it safe by construction: a mutating verb can no longer be
|
|
361
|
+
* reached by an argv it does not understand, which is how `checkpoint --help`
|
|
362
|
+
* used to commit a real checkpoint. Any thrown error becomes a one-line stderr
|
|
363
|
+
* report and exit 1, so the bin entry only ever exits with the code returned
|
|
364
|
+
* here.
|
|
365
|
+
*
|
|
366
|
+
* `cwd` is the repo the verbs act on. It defaults to the process's — the only
|
|
367
|
+
* thing the bin entry ever wants — and is a parameter solely so a test can name
|
|
368
|
+
* a fixture. Screening argv does not help when the argv is *valid*: `checkpoint
|
|
369
|
+
* -m --help` reads `--help` as the subject, exactly as it should, and then
|
|
370
|
+
* commits. Whose repo it commits is decided here, and a default is not a choice
|
|
371
|
+
* a test should inherit silently.
|
|
122
372
|
*/
|
|
123
|
-
export async function run(argv) {
|
|
373
|
+
export async function run(argv, cwd = process.cwd()) {
|
|
124
374
|
const verb = argv[0] ?? 'help';
|
|
125
375
|
const rest = argv.slice(1);
|
|
126
376
|
if (verb === 'help' || verb === '--help' || verb === '-h') {
|
|
127
|
-
|
|
377
|
+
// `plumbbob help <verb>` is the same page as `plumbbob <verb> --help` —
|
|
378
|
+
// both spellings get typed, so both work.
|
|
379
|
+
const topic = rest.find((a) => !a.startsWith('-'));
|
|
380
|
+
if (topic === undefined) {
|
|
381
|
+
process.stdout.write(`${formatHelp()}\n`);
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
const help = formatVerbHelp(topic);
|
|
385
|
+
if (help === null) {
|
|
386
|
+
process.stderr.write(`plumbbob: unknown verb '${topic}'. Run 'plumbbob help' for the verb table.\n`);
|
|
387
|
+
return 1;
|
|
388
|
+
}
|
|
389
|
+
process.stdout.write(`${help}\n`);
|
|
128
390
|
return 0;
|
|
129
391
|
}
|
|
130
392
|
if (verb === 'version' || verb === '--version' || verb === '-v') {
|
|
131
393
|
process.stdout.write(`plumbbob ${readVersion()}\n`);
|
|
132
394
|
return 0;
|
|
133
395
|
}
|
|
396
|
+
const spec = verbSpec(verb);
|
|
397
|
+
if (spec !== null) {
|
|
398
|
+
const { help, unknown } = scanFlags(spec, rest);
|
|
399
|
+
if (help) {
|
|
400
|
+
process.stdout.write(`${formatVerbHelp(verb)}\n`);
|
|
401
|
+
return 0;
|
|
402
|
+
}
|
|
403
|
+
if (unknown.length > 0 && spec.tolerateUnknownFlags !== true) {
|
|
404
|
+
process.stderr.write(`plumbbob: ${verb}: unknown flag '${unknown[0]}'. Run 'plumbbob ${verb} --help' for the flags.\n`);
|
|
405
|
+
return 1;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
134
408
|
try {
|
|
135
|
-
return await dispatch(verb,
|
|
409
|
+
return await dispatch(verb, cwd, rest);
|
|
136
410
|
}
|
|
137
411
|
catch (err) {
|
|
138
412
|
const message = err instanceof Error ? err.message : String(err);
|
package/dist/lib/check.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// because a misconfigured gate must not read as broken code (both still block).
|
|
16
16
|
import { spawnSync } from 'node:child_process';
|
|
17
17
|
import { runChecks, runDoctor } from 'checkride';
|
|
18
|
+
import { gateIsRunningFor, withGateMarker } from "./reentry.js";
|
|
18
19
|
import { resolveString } from "./settings.js";
|
|
19
20
|
/**
|
|
20
21
|
* Probe whether the check gate would have anything to run in `root`.
|
|
@@ -42,17 +43,33 @@ export async function detectGate(root) {
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
45
|
-
* Checkride's always-on repo checks: the adapters
|
|
46
|
-
*
|
|
47
|
-
* scan, the publint/attw package-shape probes, and the
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
46
|
+
* Checkride's always-on repo checks: the adapters that resolve with no tool
|
|
47
|
+
* config of their own — the built-in `links` slot, the pnpm-audit security
|
|
48
|
+
* scan, the publint/attw package-shape probes, and the pack/smoke/snippets
|
|
49
|
+
* publish-bundle slots. Several are named for their shape rather than because
|
|
50
|
+
* a bare directory reports them: `publint`/`attw` resolve off a devDependency,
|
|
51
|
+
* `build` off a `scripts.build` entry — which says the package can be built,
|
|
52
|
+
* not that checkride can see the code that goes into it — and `snippets`
|
|
53
|
+
* carries two adapters since checkride 0.10.2, so the dist-facing
|
|
54
|
+
* `snippets-dist` is named beside it (this list matches ADAPTER names, not
|
|
55
|
+
* slot names). Every one of them but `links` is an opt-in slot, so a default
|
|
56
|
+
* run selects none of them: they exercise the repo's plumbing or its shipped
|
|
57
|
+
* artifact, never its code. A gate made only of these green-lights every
|
|
58
|
+
* checkpoint while the human believes their work is being checked. Coupled to
|
|
59
|
+
* checkride's built-in set by construction; the check.test.ts and
|
|
53
60
|
* doctor.test.ts probes pin the coupling.
|
|
54
61
|
*/
|
|
55
|
-
const ALWAYS_ON_ADAPTERS = [
|
|
62
|
+
const ALWAYS_ON_ADAPTERS = [
|
|
63
|
+
'links',
|
|
64
|
+
'pnpm-audit',
|
|
65
|
+
'publint',
|
|
66
|
+
'attw',
|
|
67
|
+
'build',
|
|
68
|
+
'pack',
|
|
69
|
+
'smoke',
|
|
70
|
+
'snippets',
|
|
71
|
+
'snippets-dist',
|
|
72
|
+
];
|
|
56
73
|
/**
|
|
57
74
|
* The detection rule, shared by `start`'s probe and doctor's callout: some
|
|
58
75
|
* tool slot beyond the always-on family has an adapter — i.e. checkride can
|
|
@@ -72,11 +89,19 @@ export function gateDetectsTools(checks) {
|
|
|
72
89
|
* ladder; a resolved command means the spawn path, no setting means checkride.
|
|
73
90
|
*/
|
|
74
91
|
export async function runCheck(root, flags = {}, commandFlag) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
if (gateIsRunningFor(root)) {
|
|
93
|
+
// This repo re-entering its OWN gate is never a slow gate — it is a
|
|
94
|
+
// recursion whose next generation forks wider than this one. Exit 2 (the
|
|
95
|
+
// harness broke), not 1: nothing has been learned about the code. A gate on
|
|
96
|
+
// a DIFFERENT root is ordinary nested work (the test suite gating its
|
|
97
|
+
// fixtures) and never reaches here.
|
|
98
|
+
process.stderr.write(`plumbbob: refusing to run the check gate for ${root} inside its own gate — this would recurse.\n` +
|
|
99
|
+
' If a test reached here, it is driving a mutating verb against a real repo;\n' +
|
|
100
|
+
' point it at a fixture instead.\n');
|
|
101
|
+
return 2;
|
|
78
102
|
}
|
|
79
|
-
|
|
103
|
+
const command = resolveString(root, 'check', '', commandFlag);
|
|
104
|
+
return withGateMarker(root, () => (command.length > 0 ? runCommand(root, command, flags) : runCheckride(root, flags)));
|
|
80
105
|
}
|
|
81
106
|
/**
|
|
82
107
|
* The spawn override: stream the configured command's own output to the
|
|
@@ -106,6 +131,14 @@ async function runCheckride(root, flags) {
|
|
|
106
131
|
only: flags.only !== undefined ? [...flags.only] : null,
|
|
107
132
|
skip: flags.skip !== undefined ? [...flags.skip] : null,
|
|
108
133
|
include: flags.include !== undefined ? [...flags.include] : null,
|
|
134
|
+
// Checkride's own vacuous-green refusal (0.10.2): zero checks executed is
|
|
135
|
+
// an error, not a pass. It overlaps the refusal below and never reaches
|
|
136
|
+
// the human — our predicate is the broader one (it also catches a
|
|
137
|
+
// links-only run, where a check DID execute) and fires first, so
|
|
138
|
+
// plumbbob's message and its exit 1 are what a reader sees. Kept anyway:
|
|
139
|
+
// it is the contract checkride asks a gate to run under, so a future
|
|
140
|
+
// summary shape our predicate misreads still refuses.
|
|
141
|
+
strict: true,
|
|
109
142
|
});
|
|
110
143
|
summary = result.summary;
|
|
111
144
|
exitCode = result.exitCode;
|
package/dist/lib/intent.js
CHANGED
|
@@ -98,7 +98,7 @@ export function parseStepSeam(content, step) {
|
|
|
98
98
|
return fail(`step ${step}'s seam has an empty token.`);
|
|
99
99
|
}
|
|
100
100
|
if (GLOB_CHARS.test(token)) {
|
|
101
|
-
return fail(`step ${step}'s seam token \`${raw}\` is a glob; seams are exact paths or \`dir/\` grants (
|
|
101
|
+
return fail(`step ${step}'s seam token \`${raw}\` is a glob; seams are exact paths or \`dir/\` grants — D23 (no-glob-seams).`);
|
|
102
102
|
}
|
|
103
103
|
if (token.startsWith('/')) {
|
|
104
104
|
return fail(`step ${step}'s seam token \`${raw}\` is absolute; seams are repo-relative.`);
|
package/dist/lib/latch.js
CHANGED
|
@@ -35,7 +35,7 @@ boundary; \`/plumbbob:build\` only starts the next step, it never lands this one
|
|
|
35
35
|
*/
|
|
36
36
|
const NO_TURN_PLAN_MESSAGE = `plumbbob: checkpoint refused — no human turn since \`start\` stamped this plan. This
|
|
37
37
|
is the plan pause: present the plan, then end the turn; the human's approving
|
|
38
|
-
message is the tick that lets it land on
|
|
38
|
+
message is the tick that lets it land on the next run. (An explicit \`/plumbbob:build --auto\` or a
|
|
39
39
|
step range in the human's own prompt is the only self-approval.)
|
|
40
40
|
`;
|
|
41
41
|
/**
|
|
@@ -43,13 +43,13 @@ step range in the human's own prompt is the only self-approval.)
|
|
|
43
43
|
* it can forge is no grant. When one is set, the refusal names it rather than
|
|
44
44
|
* silently ignoring it, and points at the only self-approval that works.
|
|
45
45
|
*/
|
|
46
|
-
const SETTINGS_AUTO_NOTE = `note: \`auto\` is set in settings but is no longer a grant (
|
|
46
|
+
const SETTINGS_AUTO_NOTE = `note: \`auto\` is set in settings but is no longer a grant — D67 (auto-not-a-grant). Type \`/plumbbob:build --auto\` yourself to self-approve.
|
|
47
47
|
`;
|
|
48
48
|
/**
|
|
49
49
|
* The refusal for a range grant whose ceiling the landing step exceeds.
|
|
50
50
|
*/
|
|
51
51
|
function ceilingMessage(ceiling) {
|
|
52
|
-
return `plumbbob: checkpoint refused — the range you granted ends at step ${ceiling} — pause here;
|
|
52
|
+
return `plumbbob: checkpoint refused — the range you granted ends at step ${ceiling} — pause here; run it again to continue.\n`;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* The five-row matrix, in order, first hit wins.
|