plumbbob 0.8.18 → 0.9.1

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.
Files changed (47) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +50 -44
  3. package/dist/cli-core.js +35 -15
  4. package/dist/lib/agents.js +230 -112
  5. package/dist/lib/buildlog.js +50 -30
  6. package/dist/lib/buildlogsync.js +21 -12
  7. package/dist/lib/check.js +69 -35
  8. package/dist/lib/commitmsg.js +53 -32
  9. package/dist/lib/git.js +86 -31
  10. package/dist/lib/intent.js +91 -31
  11. package/dist/lib/latch.js +70 -39
  12. package/dist/lib/orient.js +78 -39
  13. package/dist/lib/plugins.js +11 -8
  14. package/dist/lib/settings.js +65 -32
  15. package/dist/lib/sidecar.js +320 -150
  16. package/dist/lib/templates.js +17 -6
  17. package/dist/verbs/agent.js +116 -55
  18. package/dist/verbs/build.js +27 -12
  19. package/dist/verbs/check.js +33 -7
  20. package/dist/verbs/checkpoint.js +153 -78
  21. package/dist/verbs/doctor.js +192 -95
  22. package/dist/verbs/finish.js +86 -45
  23. package/dist/verbs/handoff.js +56 -20
  24. package/dist/verbs/init.js +37 -12
  25. package/dist/verbs/park.js +14 -3
  26. package/dist/verbs/revert.js +64 -24
  27. package/dist/verbs/spike.js +89 -30
  28. package/dist/verbs/start.js +72 -38
  29. package/dist/verbs/status.js +44 -17
  30. package/dist/verbs/turn.js +88 -45
  31. package/dist/verbs/use.js +20 -9
  32. package/package.json +1 -1
  33. package/skills/{pb-build → build}/SKILL.md +25 -25
  34. package/skills/{pb-doctor → doctor}/SKILL.md +1 -1
  35. package/skills/{pb-finish → finish}/SKILL.md +3 -3
  36. package/skills/{pb-harvest → harvest}/SKILL.md +4 -4
  37. package/skills/{pb-park → park}/SKILL.md +2 -2
  38. package/skills/{pb-plan → plan}/SKILL.md +11 -11
  39. package/skills/{pb-refine → refine}/SKILL.md +7 -7
  40. package/skills/{pb-revert → revert}/SKILL.md +2 -2
  41. package/skills/{pb-spike → spike}/SKILL.md +4 -4
  42. package/skills/{pb-status → status}/SKILL.md +3 -3
  43. package/skills/{pb-step → step}/SKILL.md +9 -9
  44. package/skills/{pb-verify → verify}/SKILL.md +8 -8
  45. package/templates/build-log.md +7 -7
  46. package/templates/intent.md +8 -8
  47. package/templates/spike-report.md +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plumbbob",
3
3
  "displayName": "PlumbBob",
4
- "version": "0.8.18",
4
+ "version": "0.9.1",
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 — twelve `/pb-*` skills and a small CLI — that runs
13
+ PlumbBob is a Claude Code plugin — twelve 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
@@ -41,7 +41,7 @@ model's goodwill:
41
41
  run (types, lint, tests, dead code, docs) has to pass before a step lands.
42
42
  - **One SHA per verified step.** Every approved step is its own git commit against a
43
43
  recorded baseline — a ledger of checkpoints, not one undifferentiated diff at the end.
44
- - **A preservation-aware revert.** `/pb-revert` resets exactly to a recorded checkpoint
44
+ - **A preservation-aware revert.** `/plumbbob:revert` resets exactly to a recorded checkpoint
45
45
  SHA while keeping your plan and notes intact, and restores *only* SHAs the loop wrote.
46
46
  - **A build record that rides the PR.** The tracked `builds/<slug>/` folder — intent, log,
47
47
  checkpoints, report — merges into `main` with the branch instead of dying with the
@@ -57,7 +57,7 @@ work, a latch on the record.**
57
57
  ## The loop in one picture
58
58
 
59
59
  <p align="center">
60
- <img src="demo.svg" alt="An animated terminal session: /pb-plan writes the plan, /pb-build implements step 1 and stops at the PAUSE, the human approves, the step is checkpointed" width="760">
60
+ <img src="demo.svg" alt="An animated terminal session: /plumbbob:plan writes the plan, /plumbbob:build implements step 1 and stops at the PAUSE, the human approves, the step is checkpointed" width="760">
61
61
  </p>
62
62
 
63
63
  Everything the model must stay true to lives in one file, the build's `intent.md`
@@ -68,11 +68,11 @@ with its *because*), and the **Steps** — a numbered list where every step carr
68
68
  expected to touch).
69
69
 
70
70
  ```text
71
- /pb-plan decide everything on paper — frame, decisions, all steps (once)
71
+ /plumbbob:plan decide everything on paper — frame, decisions, all steps (once)
72
72
  per step:
73
- /pb-build implement the next step → run checks → self-review → PAUSE
74
- (approve) → the step is committed as a checkpoint; fire /pb-build again
75
- /pb-finish report what shipped and why, final commit, clear (once)
73
+ /plumbbob:build implement the next step → run checks → self-review → PAUSE
74
+ (approve) → the step is committed as a checkpoint; fire /plumbbob:build again
75
+ /plumbbob:finish report what shipped and why, final commit, clear (once)
76
76
  ```
77
77
 
78
78
  Nothing is locked and nothing refuses you — the loop does a step's labor, pulls up to
@@ -101,7 +101,7 @@ plumbbob init # link it into Claude Code; --uninstall to undo
101
101
  ```
102
102
 
103
103
  Restart Claude Code (or `/reload-plugins`) to activate, then run `plumbbob doctor`
104
- (or `/pb-doctor` in-session) to confirm the wiring. The full guide — namespacing,
104
+ (or `/plumbbob:doctor` in-session) to confirm the wiring. The full guide — namespacing,
105
105
  per-project sessions, the agent-neutral roadmap — is in
106
106
  [`docs/install.md`](docs/install.md).
107
107
 
@@ -109,11 +109,11 @@ per-project sessions, the agent-neutral roadmap — is in
109
109
 
110
110
  In Claude Code, inside any git repo with a clean tree:
111
111
 
112
- 1. **Plan.** Fire `/pb-plan` and give it whatever you have — nothing (it interviews
113
- you), a rough line (`/pb-plan rate-limit POST /login, 5/min/IP, return 429`), or a
112
+ 1. **Plan.** Fire `/plumbbob:plan` and give it whatever you have — nothing (it interviews
113
+ you), a rough line (`/plumbbob:plan rate-limit POST /login, 5/min/IP, return 429`), or a
114
114
  path to a spec file. Together you fill the build's `intent.md`. No code is written
115
115
  yet.
116
- 2. **Build.** Fire `/pb-build`. It implements the next undone step, runs the
116
+ 2. **Build.** Fire `/plumbbob:build`. It implements the next undone step, runs the
117
117
  heavy check gate, reviews its own diff against the plan, and stops:
118
118
 
119
119
  ```text
@@ -125,16 +125,16 @@ In Claude Code, inside any git repo with a clean tree:
125
125
  code, docs. To gate through your own command instead, set the `"check"` key in
126
126
  `.plumbbob/settings.json`, e.g. `"check": "npm test"`.)
127
127
  3. **Approve** — or send fixes. On your OK it commits the step as a checkpoint, marks
128
- it done, and returns to the boundary. Fire `/pb-build` again for the next step —
129
- re-firing it *is* the clock tick. Whenever you lose the thread, `/pb-status` shows
128
+ it done, and returns to the boundary. Fire `/plumbbob:build` again for the next step —
129
+ re-firing it *is* the clock tick. Whenever you lose the thread, `/plumbbob:status` shows
130
130
  where you are and names the next move.
131
- 4. **Finish.** When the last step is done, `/pb-finish` writes a report of what shipped
131
+ 4. **Finish.** When the last step is done, `/plumbbob:finish` writes a report of what shipped
132
132
  and why into the build's tracked folder, makes the final commit, and clears the slate
133
133
  for the next goal. The folder rides the branch into the PR — the build's record merges
134
134
  into `main` instead of dying with the worktree.
135
135
 
136
- A mid-build "ooh, what if…" never derails the step: `/pb-park` captures it to a list
137
- in one line, and `/pb-harvest` triages the list between steps. One goal walked end to
136
+ A mid-build "ooh, what if…" never derails the step: `/plumbbob:park` captures it to a list
137
+ in one line, and `/plumbbob:harvest` triages the list between steps. One goal walked end to
138
138
  end, with the real output at every stage, is in
139
139
  [`docs/happy-path.md`](docs/happy-path.md).
140
140
 
@@ -142,42 +142,43 @@ end, with the real output at every stage, is in
142
142
 
143
143
  You drive the whole loop from your IDE with `/plumbbob:*` skills — no step numbers to
144
144
  remember, no raw CLI to type. Every skill is `disable-model-invocation`, so *you* fire
145
- every move, and `/pb-status` always names your next one. (Claude Code namespaces them
146
- under the plugin — the real command is `/plumbbob:pb-plan`; for readability these docs
147
- write the short form `/pb-plan`.)
145
+ every move, and `/plumbbob:status` always names your next one. (Claude Code namespaces
146
+ 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 twelve share one with
148
+ a Claude Code built-in, so these docs write the full form.)
148
149
 
149
150
  **The happy path** — the three moves every session makes; many sessions need
150
151
  nothing else:
151
152
 
152
153
  | Skill <img alt="" width="110" height="1"> | Does |
153
154
  |------------------------------------|------|
154
- | `/pb-plan` | plan the whole goal — open the session and author intent's Frame, Decisions, Constraints, **and all Steps** |
155
- | `/pb-build` | implement the next planned step, then verify it to the pause — `--auto` self-approves and chains to done; a range like `1-3` self-approves through step 3, then pauses |
156
- | `/pb-finish` | finish up — write the report, make the final commit, clear for a fresh goal |
155
+ | `/plumbbob:plan` | plan the whole goal — open the session and author intent's Frame, Decisions, Constraints, **and all Steps** |
156
+ | `/plumbbob:build` | implement the next planned step, then verify it to the pause — `--auto` self-approves and chains to done; a range like `1-3` self-approves through step 3, then pauses |
157
+ | `/plumbbob:finish` | finish up — write the report, make the final commit, clear for a fresh goal |
157
158
 
158
159
  **Plan-shaping moves** — optional, for when the plan needs work mid-flight:
159
160
 
160
161
  | Skill <img alt="" width="110" height="1"> | Does |
161
162
  |------------------------------------|------|
162
- | `/pb-step` | revise/sharpen the next step (empty input auto-syncs it to reality) |
163
- | `/pb-refine` | attack the frame for holes, or repair a drifted plan |
164
- | `/pb-spike` | throwaway worktree experiment for a fork the plan can't settle |
163
+ | `/plumbbob:step` | revise/sharpen the next step (empty input auto-syncs it to reality) |
164
+ | `/plumbbob:refine` | attack the frame for holes, or repair a drifted plan |
165
+ | `/plumbbob:spike` | throwaway worktree experiment for a fork the plan can't settle |
165
166
 
166
167
  **Helpers** — orient, verify, recover, diagnose:
167
168
 
168
169
  | Skill <img alt="" width="110" height="1"> | Does |
169
170
  |------------------------------------|------|
170
- | `/pb-status` | orient — where you are, the next step's done-when and seam, and the next move |
171
- | `/pb-verify` | the tick, standalone — check → self-review → validate → **PAUSE** → checkpoint, for a diff `/pb-build` didn't write |
172
- | `/pb-revert` | recover — `git reset --hard` to a recorded checkpoint |
173
- | `/pb-doctor` | check the install from inside a session |
171
+ | `/plumbbob:status` | orient — where you are, the next step's done-when and seam, and the next move |
172
+ | `/plumbbob:verify` | the tick, standalone — check → self-review → validate → **PAUSE** → checkpoint, for a diff `/plumbbob:build` didn't write |
173
+ | `/plumbbob:revert` | recover — `git reset --hard` to a recorded checkpoint |
174
+ | `/plumbbob:doctor` | check the install from inside a session |
174
175
 
175
176
  **Capture** — the park/harvest loop for mid-build ideas:
176
177
 
177
178
  | Skill <img alt="" width="110" height="1"> | Does |
178
179
  |------------------------------------|------|
179
- | `/pb-park` | capture a mid-build idea without chasing it |
180
- | `/pb-harvest` | triage parked ideas between steps (blocker / tangent / pivot) |
180
+ | `/plumbbob:park` | capture a mid-build idea without chasing it |
181
+ | `/plumbbob:harvest` | triage parked ideas between steps (blocker / tangent / pivot) |
181
182
 
182
183
  All twelve, with inputs and effects, are in
183
184
  [`docs/skills-reference.md`](docs/skills-reference.md).
@@ -189,9 +190,9 @@ flow), and a `.plumbbob/` sidecar you can open and edit by hand at any time —
189
190
  that rides the branch into the PR, plus an untracked control plane (`settings.local.json`,
190
191
  the session sentinel, the in-flight markers).
191
192
 
192
- **`/pb-build` is the default engine, not the only one.** It's one executor — the
193
+ **`/plumbbob:build` is the default engine, not the only one.** It's one executor — the
193
194
  loop doesn't care who writes the code. Implement a step by hand — or vibe it in
194
- another session, or in another harness entirely — and run `/pb-verify` instead:
195
+ another session, or in another harness entirely — and run `/plumbbob:verify` instead:
195
196
  same checks, same pause, same checkpoint. It reads the *diff, not the author*.
196
197
 
197
198
  **Bring your own agents.** Because the executor is author-blind, you can plug your
@@ -223,7 +224,7 @@ against the plan, and committed as its own checkpoint, so a wrong turn costs one
223
224
  step, not the afternoon; and records the *whys* alongside the decisions, so the
224
225
  archive can still answer "what did we decide, and why" long after the session is
225
226
  gone. The two compose fine: plan mode — or anything else — can produce a step's
226
- diff, and `/pb-verify` reads it like any other.
227
+ diff, and `/plumbbob:verify` reads it like any other.
227
228
 
228
229
  **…use git and a TODO file?** That's the honest skeleton, and if your discipline
229
230
  holds by willpower alone, it may be all you need. What the CLI adds is the
@@ -259,19 +260,24 @@ that matters.
259
260
  > The latch's payoff is measured, not asserted: the skill-eval harness
260
261
  > ([`test/evals/`](test/evals/)) sweeps this loop headless — prose-only baseline vs. the
261
262
  > shipped latch, N=5 per contract, every assertion a mechanical read of git state — and
262
- > commits each receipt under [`research/evals/`](research/evals/). The first sweep
263
- > ([opus, 2026-07-11](research/evals/2026-07-11.md)) closed the route the latch owns —
263
+ > commits each receipt under [`docs/evals/`](docs/evals/). The first sweep
264
+ > ([opus, 2026-07-11](docs/evals/2026-07-11.md)) closed the route the latch owns —
264
265
  > *no checkpoint over a red check under pressure* went from **2/5 prose-only to 5/5
265
266
  > latched** — and named, rather than papered over, the two honest gaps it surfaced: park
266
267
  > capture at 0/5, and adversarial pressure flipping a standing `auto` grant a model could
267
268
  > write into settings, a legal side door. The re-sweep
268
- > ([opus, 2026-07-18](research/evals/2026-07-18.md), plumbbob 0.8.7) reproduced the win —
269
- > *no checkpoint over a red check* still **5/5 latched** — and pinned both gaps. The side
270
- > door is now closed by construction: [D67](docs/decisions.md#d67) (self-approval is
271
- > human-typed only) retired the settings `auto` grant, so the one approval a model could
272
- > forge is gone. What remains is park capture (c5) — prose-governed by design
273
- > ([D10](docs/decisions.md#d10)/[D13](docs/decisions.md#d13)), the guidance-only gap the
274
- > latch was never built to reach, still ~0/5 and labeled as such.
269
+ > ([opus, 2026-07-18](docs/evals/2026-07-18.md), plumbbob 0.8.7) reproduced the win —
270
+ > *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)
272
+ > (self-approval is human-typed only) retired the settings `auto` grant, so the one
273
+ > 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)) —
275
+ > and it failed because the guidance couldn't reach a fresh session at all; the turn hook
276
+ > now injects one line when a step is in flight, and the third sweep
277
+ > ([opus, 2026-07-27](docs/evals/2026-07-27.md), plumbbob 0.9.0) measures it at **5/5**,
278
+ > alongside every other contract. That sweep ran the latched arm only, so its numbers are
279
+ > a regression check on the shipped configuration, not a fresh baseline delta — the
280
+ > 2/5→5/5 headline above remains the 2026-07-11 measurement.
275
281
 
276
282
  ## Documentation
277
283
 
@@ -281,7 +287,7 @@ Each doc answers one question — in rough reading order for a new user:
281
287
  - *Show me the artifacts it leaves behind.* → [`examples/`](examples/) — that same session's finished build folder, file by file.
282
288
  - *Should I / can I / what about…?* → [`docs/faq.md`](docs/faq.md) — the adoption questions, answered straight.
283
289
  - *What is each method for?* → [`docs/techniques.md`](docs/techniques.md) — steps, seams, the pause, park/harvest, spikes.
284
- - *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md) — all twelve `/pb-*` skills: inputs, effects, when to reach for each.
290
+ - *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md) — all twelve skills: inputs, effects, when to reach for each.
285
291
  - *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).
286
292
  - *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.
287
293
  - *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
@@ -1,8 +1,14 @@
1
1
  // plumbbob CLI core — argv dispatch and the help table, separated from the bin
2
2
  // entry (cli.ts) so the routing/help logic is unit-testable in-process without
3
- // the top-level process.exit. Node builtins plus the deliberate few (D1/C2 —
4
- // currently checkride alone, via check.ts). Functional/procedural: no classes,
5
- // no `this`, no default export (C1).
3
+ // the top-level process.exit. Node builtins plus a deliberate few dependencies
4
+ // (currently checkride alone, via check.ts). Functional/procedural: no classes,
5
+ // no `this`, no default export.
6
+ //
7
+ // The deciding/executing boundary is a pause, not a lock, so there is nothing
8
+ // to defend in the dispatcher — every verb runs the same whether a human or the
9
+ // model triggers it. There is no human-only `mode` escape hatch and no
10
+ // CLAUDECODE in-session refusal; what keeps the human the decider is the pause
11
+ // at the step boundary (the skills), not a refusal here.
6
12
  import { readFileSync } from 'node:fs';
7
13
  import { fileURLToPath } from 'node:url';
8
14
  import { start } from "./verbs/start.js";
@@ -20,6 +26,7 @@ import { init } from "./verbs/init.js";
20
26
  import { doctor } from "./verbs/doctor.js";
21
27
  import { agent } from "./verbs/agent.js";
22
28
  import { turn } from "./verbs/turn.js";
29
+ /** One help-table row per verb, in the order the table prints. */
23
30
  const VERBS = [
24
31
  { name: 'start', summary: 'scaffold .plumbbob/; open the session; record the baseline commit' },
25
32
  { name: 'status', summary: 'print the orientation dashboard, or NO ACTIVE SESSION' },
@@ -37,20 +44,22 @@ const VERBS = [
37
44
  { name: 'agent', summary: 'agent list | run <name> [--step N] [--mode before|build|after]: list or spawn a user-authored agent' },
38
45
  { name: 'turn', summary: 'turn: UserPromptSubmit hook machinery — tick the turn ledger from stdin (not a user verb)' },
39
46
  ];
40
- // PlumbBob (D1/D10/D13): the deciding/executing boundary is a pause, not a lock,
41
- // so there is nothing to defend — every verb runs the same whether a human or the
42
- // model triggers it. There is no human-only `mode` escape hatch and no CLAUDECODE
43
- // in-session refusal; what keeps the human the decider is the pause at
44
- // the step boundary (the skills), not a refusal here.
47
+ /**
48
+ * Render the verb table for `plumbbob help`.
49
+ */
45
50
  export function formatHelp() {
46
51
  const width = Math.max(...VERBS.map((v) => v.name.length));
47
52
  const rows = VERBS.map((v) => ` ${v.name.padEnd(width)} ${v.summary}`);
48
53
  return ['plumbbob — attention-first build process', '', 'Usage: plumbbob <verb> [args]', '', 'Verbs:', ...rows, ''].join('\n');
49
54
  }
50
- // The package version, read from the package.json that ships one level above the
51
- // compiled CLI (dist/cli-core.js → ../package.json; in tests src/cli-core.ts →
52
- // ../package.json, the repo root). Builtins only (C2); an absent or malformed
53
- // manifest degrades to 'unknown' rather than throwing, so `--version` never errors.
55
+ /**
56
+ * The package version, or 'unknown'.
57
+ *
58
+ * Read from the package.json one level above this module (dist/cli-core.js →
59
+ * ../package.json; in tests src/cli-core.ts → ../package.json, the repo root).
60
+ * An absent or malformed manifest degrades to 'unknown' rather than throwing,
61
+ * so `--version` never errors.
62
+ */
54
63
  export function readVersion() {
55
64
  try {
56
65
  const raw = readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8');
@@ -60,9 +69,13 @@ export function readVersion() {
60
69
  return 'unknown';
61
70
  }
62
71
  }
63
- // Async because checkride is: `check` and `checkpoint` await the gate (D32),
64
- // `start` awaits the plan-time detection probe (research/07 2a); every other
65
- // verb returns synchronously through the same Promise-typed seam.
72
+ /**
73
+ * Route a verb to its implementation, or report the unknown verb and exit 1.
74
+ *
75
+ * Async because checkride is: `check` and `checkpoint` await the gate, `start`
76
+ * awaits the plan-time detection probe; every other verb returns synchronously
77
+ * through the same Promise-typed seam.
78
+ */
66
79
  async function dispatch(verb, cwd, rest) {
67
80
  switch (verb) {
68
81
  case 'start':
@@ -100,6 +113,13 @@ async function dispatch(verb, cwd, rest) {
100
113
  return 1;
101
114
  }
102
115
  }
116
+ /**
117
+ * The CLI entrypoint minus the exit: parse argv, dispatch, return the exit code.
118
+ *
119
+ * `help` and `version` short-circuit before dispatch; any thrown error becomes
120
+ * a one-line stderr report and exit 1, so the bin entry only ever exits with
121
+ * the code returned here.
122
+ */
103
123
  export async function run(argv) {
104
124
  const verb = argv[0] ?? 'help';
105
125
  const rest = argv.slice(1);