plumbbob 0.4.4 → 0.4.8

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plumbbob",
3
- "displayName": "Plumbbob",
4
- "version": "0.4.4",
3
+ "displayName": "PlumbBob",
4
+ "version": "0.4.8",
5
5
  "description": "Attention-first build process — driver skills + a CLI that keep you the decider; guidance, not enforcement.",
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
@@ -1,4 +1,4 @@
1
- # Plumbbob
1
+ # PlumbBob
2
2
 
3
3
  <p align="center">
4
4
  <img src="hero.jpg" alt="A row of plumb bobs of varying shapes hanging from strings" width="600">
@@ -66,55 +66,55 @@ and it cannot be skipped.
66
66
 
67
67
  You drive the whole loop from your IDE with eight `/plumbbob:*` skills — no step numbers to
68
68
  remember, no raw CLI to type. Each is `disable-model-invocation`, so *you* fire
69
- every move, and `/plumbbob:status` always names your next one.
69
+ every move, and `/plumbbob:pb-status` always names your next one.
70
70
 
71
71
  | Skill | Does |
72
72
  |-------|------|
73
- | `/plumbbob:plan` | plan the whole goal — scaffold the session + author intent's Frame, Decisions, Constraints, **and all Steps** |
74
- | `/plumbbob:step` | revise/sharpen the next step (empty input auto-syncs it to reality) |
75
- | `/plumbbob:build` | *(optional)* implement the next planned step, then verify it to the pause — `--auto` self-approves and chains to done |
76
- | `/plumbbob:verify` | the tick — check → self-review → validate → **PAUSE** → checkpoint |
77
- | `/plumbbob:park` | capture an idea without chasing it |
78
- | `/plumbbob:status` | orient — where you are, the next step's done-when + seam, and the next move |
79
- | `/plumbbob:harvest` | triage parked ideas at a boundary (blocker / tangent / pivot) |
80
- | `/plumbbob:wrap` | wrap up — write the report, archive safely, clear for a fresh goal |
81
-
82
- Three optional power moves survive for when you need them: `/plumbbob:revert` (recover to
83
- a checkpoint), `/plumbbob:spike` (throwaway worktree experiment), and `/plumbbob:refine` (attack
73
+ | `/plumbbob:pb-plan` | plan the whole goal — scaffold the session + author intent's Frame, Decisions, Constraints, **and all Steps** |
74
+ | `/plumbbob:pb-step` | revise/sharpen the next step (empty input auto-syncs it to reality) |
75
+ | `/plumbbob:pb-build` | *(optional)* implement the next planned step, then verify it to the pause — `--auto` self-approves and chains to done |
76
+ | `/plumbbob:pb-verify` | the tick — check → self-review → validate → **PAUSE** → checkpoint |
77
+ | `/plumbbob:pb-park` | capture an idea without chasing it |
78
+ | `/plumbbob:pb-status` | orient — where you are, the next step's done-when + seam, and the next move |
79
+ | `/plumbbob:pb-harvest` | triage parked ideas at a boundary (blocker / tangent / pivot) |
80
+ | `/plumbbob:pb-wrap` | wrap up — write the report, archive safely, clear for a fresh goal |
81
+
82
+ Three optional power moves survive for when you need them: `/plumbbob:pb-revert` (recover to
83
+ a checkpoint), `/plumbbob:pb-spike` (throwaway worktree experiment), and `/plumbbob:pb-refine` (attack
84
84
  the frame for holes, or repair the plan when it drifts — usable at any point).
85
85
 
86
86
  ## The loop
87
87
 
88
- The happy path is **plan the whole thing up front, then drive `/plumbbob:build` until
88
+ The happy path is **plan the whole thing up front, then drive `/plumbbob:pb-build` until
89
89
  done** — approving each step at its verify pause:
90
90
 
91
91
  ```text
92
- /plumbbob:plan author the whole plan (incl. all steps) (once)
92
+ /plumbbob:pb-plan author the whole plan (incl. all steps) (once)
93
93
  └ per step:
94
- /plumbbob:status review the next step (done-when + seam)
95
- /plumbbob:step (optional) sharpen/revise it first if needed
96
- /plumbbob:build (or DIY) implement it → verify → PAUSE → checkpoint
97
- /plumbbob:park capture strays mid-build
98
- /plumbbob:harvest triage them at a boundary
99
- /plumbbob:wrap report + archive + clear (once)
94
+ /plumbbob:pb-status review the next step (done-when + seam)
95
+ /plumbbob:pb-step (optional) sharpen/revise it first if needed
96
+ /plumbbob:pb-build (or DIY) implement it → verify → PAUSE → checkpoint
97
+ /plumbbob:pb-park capture strays mid-build
98
+ /plumbbob:pb-harvest triage them at a boundary
99
+ /plumbbob:pb-wrap report + archive + clear (once)
100
100
  ```
101
101
 
102
- Each `/plumbbob:build` builds the next undone step and stops at the pause for your
103
- approval — re-firing it is itself the clock tick. (`/plumbbob:build --auto` is the opt-in
102
+ Each `/plumbbob:pb-build` builds the next undone step and stops at the pause for your
103
+ approval — re-firing it is itself the clock tick. (`/plumbbob:pb-build --auto` is the opt-in
104
104
  that lets the agent self-approve and chain to done, halting on a red check or any
105
105
  mismatch.) For a worked example that walks one goal end to end — planning, building
106
106
  each step, wrapping up, archiving, and starting the next task — see
107
107
  [`docs/happy-path.md`](docs/happy-path.md).
108
108
 
109
- **Three ways to plan.** `/plumbbob:plan` produces the same artifact — a complete, standalone
109
+ **Three ways to plan.** `/plumbbob:pb-plan` produces the same artifact — a complete, standalone
110
110
  `intent.md` — from whichever seed you give it: **no argument** runs a short interview;
111
111
  **a file path** absorbs an out-of-band spec (retaining its detail so the plan stands on
112
112
  its own); **any other text** expands your inline intent. No quotes required — it
113
113
  disambiguates the mode itself.
114
114
 
115
- **The pluggable executor.** `/plumbbob:build` is one way to turn a planned step into code
115
+ **The pluggable executor.** `/plumbbob:pb-build` is one way to turn a planned step into code
116
116
  — it is *optional*. Implement by hand, in a vibe session, or with another harness,
117
- and run `/plumbbob:verify` instead: it reads the *diff, not the author*. Plumbbob is the
117
+ and run `/plumbbob:pb-verify` instead: it reads the *diff, not the author*. Plumbbob is the
118
118
  harness-agnostic spine; how the diff appears is a slot you fill however you like.
119
119
 
120
120
  ## Calibration: size everything to the work
@@ -123,7 +123,7 @@ The fastest way to abandon this is ceremony on a one-liner. The discipline is
123
123
  *decisions before code*, not *always produce three files*.
124
124
 
125
125
  - **Tiny** (typo, one-liner): no session. Just fix it.
126
- - **Small** (a contained bug/change): `/plumbbob:plan` a frame + 2–3 decisions; one or two
126
+ - **Small** (a contained bug/change): `/plumbbob:pb-plan` a frame + 2–3 decisions; one or two
127
127
  steps; build → verify → checkpoint.
128
128
  - **Medium** (a feature touching a few modules): the full loop above.
129
129
  - **Large / architectural**: that's Ridgeline's job, not Plumbbob's.
@@ -134,7 +134,8 @@ Calibration is the skill. When in doubt, smaller.
134
134
 
135
135
  - A `plumbbob` CLI (TypeScript, run natively by Node ≥ 22.18, zero runtime
136
136
  dependencies) — the dumb mechanical verbs the skills shell out to. You never type
137
- it by hand.
137
+ it by hand (beyond `plumbbob --help` and `plumbbob --version`). The marketplace
138
+ plugin carries it on PATH via `bin/` shims; `npm i -g` installs it globally.
138
139
  - The eight `/plumbbob:*` skills plus the optional power moves, each
139
140
  `disable-model-invocation` so *you* fire every move.
140
141
  - One session-gated Claude Code hook — `post-edit.sh`, a non-blocking light-feedback
@@ -150,14 +151,14 @@ Calibration is the skill. When in doubt, smaller.
150
151
  blocks an edit. It exists only because Claude can't see your editor's LSP, so the
151
152
  light tier *serves the model*.
152
153
  - **Heavy** — the full `pnpm check` (tsc, oxlint, ast-grep, vitest, knip,
153
- markdownlint). Not a hook: it runs *inside* `/plumbbob:verify`, which refuses to
154
+ markdownlint). Not a hook: it runs *inside* `/plumbbob:pb-verify`, which refuses to
154
155
  checkpoint while red. The hard gate lives on the deliberate boundary, not the
155
156
  keystroke.
156
157
 
157
158
  ## STATE is orientation, not a gate
158
159
 
159
160
  The current position lives in one word in `.plumbbob/STATE` — `DESIGN`, `BUILD`, or
160
- `SPIKE`. It no longer gates anything. It is read by `/plumbbob:status` to tell you where
161
+ `SPIKE`. It no longer gates anything. It is read by `/plumbbob:pb-status` to tell you where
161
162
  you are and what to do next; a wrong state is a mislabeled position on a map, not a
162
163
  locked door. The post-edit hook is session-gated: a repo with no `.plumbbob/STATE`
163
164
  behaves exactly like plain Claude Code.
@@ -177,7 +178,8 @@ archives plain markdown under `.plumbbob/archive/` and never touches git.
177
178
  STATE # one word: DESIGN | BUILD | SPIKE — orientation, not a gate
178
179
  SEAM # the in-flight step's declared paths (awareness, not a lock)
179
180
  STEP # the in-flight step number
180
- checkpoints # "step N <git-sha>", one per verified step
181
+ config # key=value; check=<heavy-check command> (defaults to pnpm run check)
182
+ checkpoints # "baseline <sha>" then "step N <sha>", one per verified step
181
183
  intent.md # canonical intent
182
184
  build-log.md # live ledger
183
185
  archive/
@@ -189,24 +191,34 @@ archives plain markdown under `.plumbbob/archive/` and never touches git.
189
191
 
190
192
  ## Install
191
193
 
192
- Plumbbob is a personal tool — install it once, globally, like `gh` or your
193
- dotfiles. The npm package ships the CLI plus the skills and hook; `plumbbob init`
194
- links them into Claude Code as an in-place plugin.
194
+ Plumbbob installs **once, globally** like `gh` or your dotfiles. There are two co-equal,
195
+ mutually-exclusive ways to do it (both register a Claude Code plugin named `plumbbob`;
196
+ running both collides over the `/plumbbob:*` namespace).
197
+
198
+ **npm global + `init`** — the npm package ships the CLI, the skills, and the hook; `plumbbob
199
+ init` links them into Claude Code as an in-place plugin:
195
200
 
196
201
  ```sh
197
202
  npm i -g plumbbob # the CLI (also a `pb` shorthand)
198
203
  plumbbob init # link it into Claude Code; --uninstall to undo
199
204
  ```
200
205
 
201
- `init` symlinks the package into `~/.claude/skills/plumbbob`, where Claude Code
202
- loads it as a plugin. Claude Code namespaces a plugin's skills as
203
- `/plumbbob:<skill>`, so they appear as `/plumbbob:plan`, `/plumbbob:status`, and the
204
- rest — already clear of built-in and other-plugin commands, no prefix needed. The
205
- post-edit hook auto-registers from `hooks.json`.
206
- Nothing else under `~` is touched
207
- and `settings.json` is left alone — restart Claude Code (or `/reload-plugins`) to
208
- activate. Because it's a symlink, a later `npm i -g plumbbob@latest` stays live with
209
- no re-link.
206
+ `init` symlinks the package into `~/.claude/skills/plumbbob`, where Claude Code loads it as a
207
+ plugin; the post-edit hook auto-registers from `hooks.json`. Because it's a symlink, a later
208
+ `npm i -g plumbbob@latest` stays live with no re-link.
209
+
210
+ **The marketplace plugin** — self-contained: it ships the skills *and* the `plumbbob`/`pb`
211
+ CLI on PATH (via its `bin/` shims), so it needs neither `npm i -g` nor `plumbbob init`:
212
+
213
+ ```text
214
+ /plugin install plumbbob@<marketplace>
215
+ ```
216
+
217
+ Either way, Claude Code namespaces the skills as `/plumbbob:<skill>`, so they appear as
218
+ `/plumbbob:pb-plan`, `/plumbbob:pb-status`, and the rest. Nothing else under `~` is touched
219
+ and `settings.json` is left alone — restart Claude Code (or `/reload-plugins`) to activate.
220
+ If a marketplace plumbbob is already installed, `plumbbob init` refuses rather than create
221
+ the collision (`--force` overrides), and `plumbbob doctor` flags a double-install.
210
222
 
211
223
  **Sessions are per-project.** Install scope is not session scope: you install the
212
224
  tool once, but each goal lives in its own repo — `plumbbob start "<goal>"` writes a
@@ -222,10 +234,11 @@ look. Claude Code is the first, first-class target.
222
234
  plumbbob doctor
223
235
  ```
224
236
 
225
- `doctor` checks that the link resolves to the plugin manifest, the skills, and the
226
- hook, and prints the exact fix for anything broken. Run it first if a `/plumbbob:*`
227
- skill ever opens with an empty dashboard; [`docs/troubleshooting.md`](docs/troubleshooting.md)
228
- covers the rest.
237
+ `doctor` works for either install path: it confirms a marketplace plugin, or checks that the
238
+ skills-dir link resolves to the plugin manifest, the skills, and the hook flagging a
239
+ double-install collision and prints the exact fix for anything broken. Run it first if a
240
+ `/plumbbob:*` skill ever opens with an empty dashboard;
241
+ [`docs/troubleshooting.md`](docs/troubleshooting.md) covers the rest.
229
242
 
230
243
  ## Development
231
244
 
package/bin/pb ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+ # pb — short alias for `plumbbob` (see bin/plumbbob for the full note). Same PATH
3
+ # shim: resolves the bundled CLI relative to this file so the plugin works on PATH
4
+ # without a global install.
5
+ here=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
6
+ exec node "$here/../dist/cli.js" "$@"
package/bin/plumbbob ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ # plumbbob — PATH shim for the marketplace / plugin install. Claude Code adds this
3
+ # bin/ dir to the Bash tool's PATH while the plugin is enabled (the plugins `bin/`
4
+ # convention), so the driver skills can shell a bare `plumbbob` without a global
5
+ # `npm i -g`. The CLI is resolved relative to this file, so it works from the
6
+ # plugin's install/cache dir wherever that lands. Requires node >=22 on PATH (the
7
+ # same requirement as the published `bin`/`dist/cli.js` shebang).
8
+ here=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
9
+ exec node "$here/../dist/cli.js" "$@"
package/dist/cli-core.js CHANGED
@@ -2,6 +2,8 @@
2
2
  // entry (cli.ts) so the routing/help logic is unit-testable in-process without
3
3
  // the top-level process.exit. Zero runtime deps, node: builtins only (D1/C2).
4
4
  // Functional/procedural: no classes, no `this`, no default export (C1).
5
+ import { readFileSync } from 'node:fs';
6
+ import { fileURLToPath } from 'node:url';
5
7
  import { start } from "./verbs/start.js";
6
8
  import { status } from "./verbs/status.js";
7
9
  import { park } from "./verbs/park.js";
@@ -36,6 +38,19 @@ export function formatHelp() {
36
38
  const rows = VERBS.map((v) => ` ${v.name.padEnd(width)} ${v.summary}`);
37
39
  return ['plumbbob — attention-first build process', '', 'Usage: plumbbob <verb> [args]', '', 'Verbs:', ...rows, ''].join('\n');
38
40
  }
41
+ // The package version, read from the package.json that ships one level above the
42
+ // compiled CLI (dist/cli-core.js → ../package.json; in tests src/cli-core.ts →
43
+ // ../package.json, the repo root). Builtins only (C2); an absent or malformed
44
+ // manifest degrades to 'unknown' rather than throwing, so `--version` never errors.
45
+ export function readVersion() {
46
+ try {
47
+ const raw = readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8');
48
+ return JSON.parse(raw).version ?? 'unknown';
49
+ }
50
+ catch {
51
+ return 'unknown';
52
+ }
53
+ }
39
54
  function dispatch(verb, cwd, rest) {
40
55
  switch (verb) {
41
56
  case 'start':
@@ -72,6 +87,10 @@ export function run(argv) {
72
87
  process.stdout.write(`${formatHelp()}\n`);
73
88
  return 0;
74
89
  }
90
+ if (verb === 'version' || verb === '--version' || verb === '-v') {
91
+ process.stdout.write(`plumbbob ${readVersion()}\n`);
92
+ return 0;
93
+ }
75
94
  try {
76
95
  return dispatch(verb, process.cwd(), rest);
77
96
  }
@@ -77,7 +77,7 @@ export function parseOpenQuestions(intent) {
77
77
  .length;
78
78
  }
79
79
  // Open parked items: `- [ ]` lines under `## Park list` (the `park` verb's format).
80
- // A harvested item is flipped to `- [x]` by `/plumbbob:harvest` and no longer counts; the
80
+ // A harvested item is flipped to `- [x]` by `/plumbbob:pb-harvest` and no longer counts; the
81
81
  // `(none yet)` placeholder and the blockquote instructions never match.
82
82
  export function parseParked(buildLog) {
83
83
  return sectionLines(buildLog, '## Park list').filter((l) => /^-\s+\[ \]\s+\S/.test(l.trim())).length;
@@ -99,30 +99,30 @@ function nextMove(state, steps, inFlight, parked) {
99
99
  case 'SPIKE':
100
100
  return 'close the spike — `plumbbob spike done`';
101
101
  case 'FINISH':
102
- return 'wrap up — `/plumbbob:wrap`';
102
+ return 'wrap up — `/plumbbob:pb-wrap`';
103
103
  case 'REVIEW':
104
- return 'read the diff cold against intent, then `/plumbbob:verify`';
104
+ return 'read the diff cold against intent, then `/plumbbob:pb-verify`';
105
105
  case 'BUILD': {
106
106
  const n = inFlight ?? steps.find((s) => !s.done)?.n;
107
107
  return n === undefined
108
- ? 'finish the step in flight — `/plumbbob:verify`'
109
- : `finish step ${n} — \`/plumbbob:verify\` (or keep editing, then \`/plumbbob:verify\`)`;
108
+ ? 'finish the step in flight — `/plumbbob:pb-verify`'
109
+ : `finish step ${n} — \`/plumbbob:pb-verify\` (or keep editing, then \`/plumbbob:pb-verify\`)`;
110
110
  }
111
111
  default: {
112
112
  // DESIGN (and any unknown state): you are at the boundary.
113
113
  const nextUndone = steps.find((s) => !s.done);
114
114
  if (nextUndone === undefined) {
115
115
  if (steps.length === 0) {
116
- return 'plan the first step — `/plumbbob:step`';
116
+ return 'plan the first step — `/plumbbob:pb-step`';
117
117
  }
118
118
  // Batch-default: the steps were planned up front, so finishing them usually
119
- // means "wrap up" — but `/plumbbob:step` can still add an increment if reality grew.
120
- const harvest = parked > 0 ? `harvest ${parked} parked idea${parked === 1 ? '' : 's'} — \`/plumbbob:harvest\`; then ` : '';
121
- return `${harvest}wrap up — \`/plumbbob:wrap\` (or \`/plumbbob:step\` to add another increment)`;
119
+ // means "wrap up" — but `/plumbbob:pb-step` can still add an increment if reality grew.
120
+ const harvest = parked > 0 ? `harvest ${parked} parked idea${parked === 1 ? '' : 's'} — \`/plumbbob:pb-harvest\`; then ` : '';
121
+ return `${harvest}wrap up — \`/plumbbob:pb-wrap\` (or \`/plumbbob:pb-step\` to add another increment)`;
122
122
  }
123
123
  return nextUndone.planned
124
- ? `build step ${nextUndone.n} — \`/plumbbob:build\` (or \`/plumbbob:step\` to revise it first)`
125
- : `plan step ${nextUndone.n} — \`/plumbbob:step\``;
124
+ ? `build step ${nextUndone.n} — \`/plumbbob:pb-build\` (or \`/plumbbob:pb-step\` to revise it first)`
125
+ : `plan step ${nextUndone.n} — \`/plumbbob:pb-step\``;
126
126
  }
127
127
  }
128
128
  }
@@ -153,7 +153,7 @@ export function formatOrientation(o) {
153
153
  if (s !== nextUndone) {
154
154
  return head;
155
155
  }
156
- // Surface the next step's detail so the human can review it (and `/plumbbob:step`-
156
+ // Surface the next step's detail so the human can review it (and `/plumbbob:pb-step`-
157
157
  // revise) before building. Only what's present — a rough step shows neither.
158
158
  const detail = [];
159
159
  if (o.nextDoneWhen !== null) {
@@ -0,0 +1,24 @@
1
+ // Detect a marketplace-installed plumbbob plugin from Claude Code's plugin state.
2
+ // Used to keep the skills-dir install (`plumbbob init`) from colliding with a
3
+ // marketplace install: two plugins both named `plumbbob` fight over the
4
+ // `/plumbbob:*` namespace and the skills can drop to flat names (`/pb-status`).
5
+ // Read-only, node builtins only (C1/C2): a missing / unreadable / malformed file
6
+ // means "none installed".
7
+ import { readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ // Marketplace plugin ids for plumbbob recorded in installed_plugins.json
10
+ // (e.g. "plumbbob@robmclarty"). Empty when none, or the file is absent/unreadable.
11
+ // `plumbbob@<marketplace>` matches; `plumbbob-spike@x` and the like do not.
12
+ export function marketplacePlumbbob(home) {
13
+ try {
14
+ const raw = readFileSync(join(home, '.claude', 'plugins', 'installed_plugins.json'), 'utf8');
15
+ const plugins = JSON.parse(raw).plugins;
16
+ if (plugins === undefined || plugins === null) {
17
+ return [];
18
+ }
19
+ return Object.keys(plugins).filter((id) => id === 'plumbbob' || id.startsWith('plumbbob@'));
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
@@ -1,5 +1,5 @@
1
1
  // `plumbbob check` — run the heavy gate (D16/D24) and report, with NO state
2
- // change. The read-only half of the verify tick: `/plumbbob:verify` runs this before the
2
+ // change. The read-only half of the verify tick: `/plumbbob:pb-verify` runs this before the
3
3
  // pause so the human approves on a known-green check. Exits with the check's own
4
4
  // code (0 = green).
5
5
  import { findRepoRoot } from "../lib/git.js";
@@ -4,7 +4,7 @@
4
4
  // It gates on a green check, then commits any pending work (or records the existing
5
5
  // HEAD when the tree is already clean — the human's commit skill may have committed
6
6
  // first), records the SHA, flips the intent checkbox to `[x]`, clears any STEP/SEAM,
7
- // and returns to DESIGN. The diff's author is irrelevant: `/plumbbob:build`, your hands,
7
+ // and returns to DESIGN. The diff's author is irrelevant: `/plumbbob:pb-build`, your hands,
8
8
  // a vibe session, or another harness all checkpoint the same way.
9
9
  import { appendFileSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { commit, findRepoRoot, headSha, isDirty, stageAll } from "../lib/git.js";
@@ -4,12 +4,13 @@
4
4
  // in place as a plugin (skills `/plumbbob:*`, the post-edit hook from hooks.json).
5
5
  // doctor verifies the link resolves to a package carrying the manifest, the skills,
6
6
  // and the hook — and names the fix for anything missing. The failure class it
7
- // exists for is SILENT (a `/plumbbob:status` that opens an empty dashboard because the
7
+ // exists for is SILENT (a `/plumbbob:pb-status` that opens an empty dashboard because the
8
8
  // plugin never linked). Functional, node builtins only (C1/C2).
9
9
  import { existsSync, lstatSync, readdirSync, readlinkSync } from 'node:fs';
10
10
  import { homedir } from 'node:os';
11
11
  import { join } from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
+ import { marketplacePlumbbob } from "../lib/plugins.js";
13
14
  // The plumbbob package's own skills/ dir (the canonical set), off this module's
14
15
  // URL so it resolves the same from src/ (dev) and dist/ (published).
15
16
  function packageDir(name) {
@@ -58,8 +59,25 @@ export function doctor() {
58
59
  const link = join(home, '.claude', 'skills', 'plumbbob');
59
60
  const shipped = listSkills(packageDir('skills'));
60
61
  const pkg = linkedPackage(link);
61
- const checks = pkg === null ? [{ ok: false, label: `not linked — no plugin at ${link}`, fix: 'run: plumbbob init' }] : buildChecks(link, pkg, shipped);
62
- const out = ['plumbbob doctor — global plugin install'];
62
+ const market = marketplacePlumbbob(home);
63
+ let checks;
64
+ if (pkg === null) {
65
+ checks =
66
+ market.length > 0
67
+ ? [{ ok: true, label: `installed via marketplace (${market.join(', ')}) — skills load as /plumbbob:*, the CLI is on PATH from the plugin bin/. No init needed.` }]
68
+ : [{ ok: false, label: `not linked — no plugin at ${link}`, fix: 'install the marketplace plugin (/plugin install plumbbob@<marketplace>) or run: plumbbob init' }];
69
+ }
70
+ else {
71
+ checks = buildChecks(link, pkg, shipped);
72
+ if (market.length > 0) {
73
+ checks.unshift({
74
+ ok: false,
75
+ label: `collision — also installed via marketplace (${market.join(', ')}); two plugins named plumbbob fight over /plumbbob:* and skills can drop to flat names`,
76
+ fix: 'keep one — `plumbbob init --uninstall` to use the marketplace plugin, or `/plugin uninstall` the marketplace one to keep this link',
77
+ });
78
+ }
79
+ }
80
+ const out = ['plumbbob doctor — plugin install'];
63
81
  for (const c of checks) {
64
82
  out.push(c.ok ? ` ✓ ${c.label}` : ` ✗ ${c.label}\n → ${c.fix}`);
65
83
  }
@@ -68,7 +86,7 @@ export function doctor() {
68
86
  out.push(failed === 0
69
87
  ? 'plumbbob: all checks passed. If a skill still misbehaves, restart Claude Code (or /reload-plugins).'
70
88
  : `plumbbob: ${failed} problem(s) — apply the → fixes, then restart Claude Code.`);
71
- out.push('plumbbob: skills shell a bare `plumbbob` ensure it is on PATH (`npm i -g plumbbob`). Sessions are per-project via `plumbbob start`.');
89
+ out.push('plumbbob: skills shell a bare `plumbbob`. The marketplace plugin puts it on PATH via bin/; for the skills-dir/global install run `npm i -g plumbbob`. Sessions are per-project via `plumbbob start`.');
72
90
  process.stdout.write(`${out.join('\n')}\n`);
73
91
  return failed === 0 ? 0 : 1;
74
92
  }
@@ -1,16 +1,24 @@
1
- // `plumbbob init [--uninstall]` — link plumbbob into Claude Code as an in-place
2
- // skills-directory plugin. This is the whole install: it symlinks the installed
3
- // package into ~/.claude/skills/plumbbob, where Claude Code discovers it as
4
- // `plumbbob@skills-dir` — the skills load namespaced (`/plumbbob:*`) and the
5
- // post-edit hook auto-registers from hooks/hooks.json. Global-only by design:
6
- // plumbbob is a personal tool (like firecrawl/gh), and install scope is NOT
7
- // session scope sessions stay per-project via `plumbbob start`. Idempotent +
8
- // reversible (`--uninstall` drops the link); it NEVER writes settings.json.
9
- // Functional, node builtins only (C1/C2).
1
+ // `plumbbob init [--uninstall] [--force]` — link plumbbob into Claude Code as an
2
+ // in-place skills-directory plugin. It symlinks the installed package into
3
+ // ~/.claude/skills/plumbbob, where Claude Code discovers it as `plumbbob@skills-dir`
4
+ // — the skills load namespaced (`/plumbbob:*`) and the post-edit hook auto-registers
5
+ // from hooks/hooks.json. This is the deliberate non-marketplace install path — kept
6
+ // first-class, not deprecated: it serves an `npm i -g` global, local dev, and clients
7
+ // predating plugins, and keeps plumbbob usable as a standalone CLI (and, later, under
8
+ // other agents) rather than Claude-marketplace-only. The marketplace plugin is the
9
+ // co-equal self-contained alternative (it ships skills AND the `plumbbob` CLI on PATH
10
+ // via bin/, so it needs neither `npm i -g` nor `init`). The two are mutually
11
+ // exclusive: both register a plugin named `plumbbob`, and a double-install collides
12
+ // over the /plumbbob:* namespace (skills can drop to flat `/pb-status` names). So init
13
+ // REFUSES when a marketplace plumbbob is already installed — `--force` overrides
14
+ // (the dev-install path uses it). Global-only by design: install scope is NOT session
15
+ // scope — sessions stay per-project via `plumbbob start`. Idempotent + reversible
16
+ // (`--uninstall` drops the link); it NEVER writes settings.json. Node builtins only.
10
17
  import { lstatSync, mkdirSync, readlinkSync, rmSync, symlinkSync } from 'node:fs';
11
18
  import { homedir } from 'node:os';
12
19
  import { join } from 'node:path';
13
20
  import { fileURLToPath } from 'node:url';
21
+ import { marketplacePlumbbob } from "../lib/plugins.js";
14
22
  // The installed package root (parent of .claude-plugin/, skills/, hooks/, dist/),
15
23
  // off this module's URL — the global install when run as the published bin, the
16
24
  // checkout in dev. No trailing slash, so it compares clean against a readlink.
@@ -47,6 +55,14 @@ export function init(args) {
47
55
  if (args.includes('--uninstall')) {
48
56
  return uninstall(link);
49
57
  }
58
+ const market = marketplacePlumbbob(home);
59
+ if (market.length > 0 && !args.includes('--force')) {
60
+ process.stderr.write(`plumbbob: a marketplace plumbbob plugin is already installed (${market.join(', ')}).\n` +
61
+ 'plumbbob: it already provides the skills (`/plumbbob:*`) and the `plumbbob` CLI on PATH (the plugin bin/) — no `plumbbob init` needed.\n' +
62
+ `plumbbob: linking ${link} would register a SECOND plugin named \`plumbbob\`; the two collide over the /plumbbob:* namespace and skills can drop to flat names (\`/pb-status\`).\n` +
63
+ 'plumbbob: to use the skills-dir link instead, first remove the marketplace one (`/plugin uninstall plumbbob@<marketplace>`), or re-run with `--force` if you know what you are doing.\n');
64
+ return 1;
65
+ }
50
66
  mkdirSync(join(home, '.claude', 'skills'), { recursive: true });
51
67
  if (present(link)) {
52
68
  const current = symlinkTarget(link);
@@ -1,5 +1,5 @@
1
1
  // `plumbbob park "<text>"` — the dumb capture path: append one raw line under
2
- // the build-log's Park list. No model turn, no composition (that is /park's job).
2
+ // the build-log's Park list. No model turn, no composition (that is /plumbbob:pb-park's job).
3
3
  // Capture is not a transition, so it runs in any context (terminal or in-session).
4
4
  import { readFileSync, writeFileSync } from 'node:fs';
5
5
  import { findRepoRoot } from "../lib/git.js";
@@ -1,6 +1,6 @@
1
1
  // `plumbbob wrap` (D9) — the v2 close-out, replacing the v1 four-verb
2
2
  // finish ceremony. It archives intent + build-log + report
3
- // (the `/plumbbob:wrap` skill writes the report by default) under .plumbbob/archive/,
3
+ // (the `/plumbbob:pb-wrap` skill writes the report by default) under .plumbbob/archive/,
4
4
  // clears the active files, and deletes the control state (STATE last). Unlike v1
5
5
  // `finish` there is NO refuse-without-report gate — guidance offers the artifact, it
6
6
  // does not wall the exit. Archive-then-clear, never destroy (C4); git untouched (C5).
@@ -20,7 +20,7 @@ export function wrap(cwd) {
20
20
  }
21
21
  else {
22
22
  process.stderr.write('plumbbob: note — no report.md found; archiving intent + build-log without one ' +
23
- '(/plumbbob:wrap normally writes the report first). No gate (D9).\n');
23
+ '(/plumbbob:pb-wrap normally writes the report first). No gate (D9).\n');
24
24
  }
25
25
  const archived = archiveSession(root);
26
26
  // Clear the active files — now safely archived — then the control state, STATE
@@ -32,7 +32,7 @@ export function wrap(cwd) {
32
32
  rmSync(stepPath(root), { force: true });
33
33
  rmSync(join(sidecarDir(root), 'STATE'), { force: true });
34
34
  process.stdout.write(`plumbbob: wrap — archived to ${relative(root, archived)}. Sidecar cleared. ` +
35
- 'Run `/plumbbob:plan` (or `plumbbob start "<title>"`) to frame the next goal.\n');
35
+ 'Run `/plumbbob:pb-plan` (or `plumbbob start "<title>"`) to frame the next goal.\n');
36
36
  return 0;
37
37
  }
38
38
  // Append the recorded checkpoints (baseline + each `step n <sha>`) to the report so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plumbbob",
3
- "version": "0.4.4",
3
+ "version": "0.4.8",
4
4
  "description": "Attention-first build process: 11 skills + a CLI that keep you the decider — guidance, not enforcement.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "files": [
27
27
  ".claude-plugin",
28
+ "bin",
28
29
  "dist",
29
30
  "hooks",
30
31
  "skills",
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: build
2
+ name: pb-build
3
3
  description: The optional engine — read the next planned step from intent, implement it (its done-when, seam, Decisions, Constraints), then verify it through to the approval pause. Skip it to build by hand/vibed/another harness. `--auto` self-approves and chains to done.
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -12,31 +12,31 @@ Current session state (injected when this skill runs): !`plumbbob status 2>/dev/
12
12
 
13
13
  This is the **bundled executor** — one way to turn a planned step into code. It is
14
14
  **optional** (D3): you can implement any step by hand, in a vibe session, or with
15
- another harness and go straight to `/plumbbob:verify` instead — plumbbob does not care how
15
+ another harness and go straight to `/plumbbob:pb-verify` instead — plumbbob does not care how
16
16
  the diff appeared. When you do run it, it reads the plan, writes the step, and
17
17
  carries straight through to the verify pause.
18
18
 
19
- Since `/plumbbob:plan` lays down the whole step list up front, the happy path is to fire
20
- `/plumbbob:build` once per step until done — each run builds the next undone step and stops
21
- at the pause for your approval. **Re-firing `/plumbbob:build` is itself the clock tick.**
19
+ Since `/plumbbob:pb-plan` lays down the whole step list up front, the happy path is to fire
20
+ `/plumbbob:pb-build` once per step until done — each run builds the next undone step and stops
21
+ at the pause for your approval. **Re-firing `/plumbbob:pb-build` is itself the clock tick.**
22
22
 
23
23
  ## What this skill does, in order
24
24
 
25
- 1. **Pick the step.** Use the number you were invoked with (e.g. `/plumbbob:build 4`), else
25
+ 1. **Pick the step.** Use the number you were invoked with (e.g. `/plumbbob:pb-build 4`), else
26
26
  the next undone, planned step in `.plumbbob/intent.md`. If there is no planned step
27
- to build, stop and tell the human to `/plumbbob:step` first.
27
+ to build, stop and tell the human to `/plumbbob:pb-step` first.
28
28
  2. **Enter the step.** Run `plumbbob build <n>` (records the in-flight STEP +
29
- SEAM so `/plumbbob:status` shows the step in flight; in v2 the seam is awareness, not a
29
+ SEAM so `/plumbbob:pb-status` shows the step in flight; in v2 the seam is awareness, not a
30
30
  lock).
31
31
  3. **Read the plan.** Read the step's **done-when**, its **seam**, and the
32
32
  **Decisions** and **Constraints** in `intent.md`. Build to *that* — the deciding
33
33
  already happened, off the chat.
34
34
  4. **Implement** the step, and only that step, staying within the declared seam. A
35
- new problem or "ooh what if" that surfaces mid-build is a `/plumbbob:park`, **not** an
35
+ new problem or "ooh what if" that surfaces mid-build is a `/plumbbob:pb-park`, **not** an
36
36
  edit — capture it and stay on the step. If you genuinely cannot finish without
37
37
  touching more than the seam, that is scope drift: surface it to the human rather
38
38
  than sprawling.
39
- 5. **Verify, through to the pause.** Run the verify tick exactly as `/plumbbob:verify`
39
+ 5. **Verify, through to the pause.** Run the verify tick exactly as `/plumbbob:pb-verify`
40
40
  does: `plumbbob check` → self-review the diff against the done-when, the
41
41
  Decisions, and the Constraints (a single structured read, D16) → validate → **PAUSE
42
42
  for the human's approval** → only on approval, checkpoint with
@@ -45,7 +45,7 @@ at the pause for your approval. **Re-firing `/plumbbob:build` is itself the cloc
45
45
 
46
46
  ## `--auto` — let the agent be the clock (opt-in)
47
47
 
48
- `/plumbbob:build --auto` is the explicit escape hatch when the human wants unattended
48
+ `/plumbbob:pb-build --auto` is the explicit escape hatch when the human wants unattended
49
49
  progress instead of approving each step. It does the same work, but **the agent reviews
50
50
  and approves in the human's place**, and it **chains**:
51
51
 
@@ -61,10 +61,10 @@ human asked for it by name. The default — no flag — always ends at the pause
61
61
 
62
62
  ## The hard contracts
63
63
 
64
- - **Optional, never required.** The loop works without this skill; `/plumbbob:verify`
64
+ - **Optional, never required.** The loop works without this skill; `/plumbbob:pb-verify`
65
65
  checkpoints a hand-built or vibed diff just the same (D3).
66
66
  - **Build the decided step, not a new one.** Implement what `intent.md` settled. A
67
- new idea mid-build is a `/plumbbob:park`, not an edit.
67
+ new idea mid-build is a `/plumbbob:pb-park`, not an edit.
68
68
  - **Default ends at the pause.** Implement → verify → wait for approval; never
69
69
  checkpoint without it. Only an explicit `--auto` lets the agent approve in your place,
70
70
  and it still halts on a red check or any mismatch.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: harvest
2
+ name: pb-harvest
3
3
  description: Triage the park list at a step boundary — propose one class (blocker/tangent/pivot) per parked item, write only after the human confirms each, record under ## Harvest, and fold a confirmed blocker into intent.
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -10,7 +10,7 @@ allowed-tools: Read, Edit, Bash(plumbbob status:*)
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
- `/plumbbob:harvest` is the complement of `/plumbbob:park` (D7): you parked ideas as seeds during a
13
+ `/plumbbob:pb-harvest` is the complement of `/plumbbob:pb-park` (D7): you parked ideas as seeds during a
14
14
  build; now, at a boundary, you harvest them — decide what each one is.
15
15
 
16
16
  ## When to run it — wrong-state refusal
@@ -19,7 +19,7 @@ Harvest at a **step boundary**: after a step is checkpointed and you are back in
19
19
  DESIGN, not mid-step. Read the state injected above:
20
20
 
21
21
  - `NO ACTIVE SESSION` — **refuse**; tell the human to `plumbbob start "<title>"` first.
22
- - `BUILD` — a step is in flight; **refuse** and suggest finishing it with `/plumbbob:verify`
22
+ - `BUILD` — a step is in flight; **refuse** and suggest finishing it with `/plumbbob:pb-verify`
23
23
  before harvesting. Chasing parked items mid-step is the disease parking prevents.
24
24
  - `DESIGN` (and other settled states) — go ahead.
25
25
 
@@ -40,7 +40,7 @@ one line of reasoning, then **wait for the human to confirm or override**. Write
40
40
  **only after** per-item confirmation:
41
41
 
42
42
  - Record each confirmed class in `build-log.md`'s `## Harvest` section.
43
- - **Flip the harvested item** from `- [ ]` to `- [x]` in the Park list, so `/plumbbob:status`
43
+ - **Flip the harvested item** from `- [ ]` to `- [x]` in the Park list, so `/plumbbob:pb-status`
44
44
  stops counting it as open.
45
45
  - A confirmed **blocker** also folds its decision into `intent.md`.
46
46
  - Never reclassify or resolve an item the human hasn't confirmed, and default every
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: park
2
+ name: pb-park
3
3
  description: Compose one tidy tagged park line, get the human's OK in-turn, then capture it by shelling `plumbbob park` — never by editing a file. The capture half of the park/harvest loop.
4
4
  disable-model-invocation: true
5
5
  model: haiku
@@ -10,7 +10,7 @@ allowed-tools: Bash(plumbbob status:*), Bash(plumbbob park:*)
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
- `/plumbbob:park` is the **capture** half of the loop; `/plumbbob:harvest` is where parked items
13
+ `/plumbbob:pb-park` is the **capture** half of the loop; `/plumbbob:pb-harvest` is where parked items
14
14
  get triaged later (D7). Capturing the instant an idea arrives — instead of acting on
15
15
  it — is the whole point: it protects the step in flight.
16
16
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: plan
2
+ name: pb-plan
3
3
  description: "Frame a fresh goal and author the whole plan — Frame, Decisions, Constraints, and all Steps — before any code. Three input modes: no arg interviews you; a file path absorbs a spec; any other text expands your inline intent."
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -10,10 +10,10 @@ allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob start:*
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
- `/plumbbob:plan` is the **whole-goal** move — it opens a session and gets the deciding out
13
+ `/plumbbob:pb-plan` is the **whole-goal** move — it opens a session and gets the deciding out
14
14
  of your head and onto `intent.md` *before* any code. By default it authors the
15
15
  **complete plan, including all the Steps**, so the happy path afterward is just
16
- `/plumbbob:build` until done. (Revising a single increment later is the separate `/plumbbob:step`
16
+ `/plumbbob:pb-build` until done. (Revising a single increment later is the separate `/plumbbob:pb-step`
17
17
  move; do not confuse the two.)
18
18
 
19
19
  ## Three input modes (disambiguated for you — no quotes needed)
@@ -33,7 +33,7 @@ Look at the argument the human gave and pick the mode yourself:
33
33
  genuinely ambiguous.
34
34
 
35
35
  All three modes converge on the **same artifact**: a complete, standalone `intent.md`
36
- an agent can follow with `/plumbbob:build`. The argument only seeds how you get there.
36
+ an agent can follow with `/plumbbob:pb-build`. The argument only seeds how you get there.
37
37
 
38
38
  ## What this skill does
39
39
 
@@ -55,11 +55,11 @@ an agent can follow with `/plumbbob:build`. The argument only seeds how you get
55
55
  - seam: `<file>`, `<file>`
56
56
  ```
57
57
 
58
- Every step needs a **done-when** `/plumbbob:verify` can check and a **seam** (the exact
58
+ Every step needs a **done-when** `/plumbbob:pb-verify` can check and a **seam** (the exact
59
59
  paths it touches). Later steps may be fuzzier than the first — that's fine; they get
60
- sharpened just-in-time when you reach them with `/plumbbob:step`. Keep each small enough to
60
+ sharpened just-in-time when you reach them with `/plumbbob:pb-step`. Keep each small enough to
61
61
  verify in one review pass.
62
- 5. **Offer to stress-test it.** Suggest `/plumbbob:refine` to attack the frame for holes (or
62
+ 5. **Offer to stress-test it.** Suggest `/plumbbob:pb-refine` to attack the frame for holes (or
63
63
  to repair the plan as it drifts). Optional, the human's call.
64
64
 
65
65
  ## The interview (mode 1)
@@ -72,11 +72,11 @@ Make it easy and non-intrusive:
72
72
  without typing** ("done-when: the 6th request in 60s returns 429 — good?"), while
73
73
  taking arbitrary detail when they want to give it, including pointers to other files.
74
74
  - **Let them double back.** They will revise as the picture sharpens; that's expected.
75
- They can also edit `intent.md` by hand at any time, or call `/plumbbob:refine` to repair it.
75
+ They can also edit `intent.md` by hand at any time, or call `/plumbbob:pb-refine` to repair it.
76
76
 
77
77
  ## The hard contracts
78
78
 
79
- - **Deciding before code.** `/plumbbob:plan` writes `intent.md` only — never source.
79
+ - **Deciding before code.** `/plumbbob:pb-plan` writes `intent.md` only — never source.
80
80
  - **The human converges.** You surface options and draft wording; the human picks.
81
81
  An unresolved hole is an Open question, not a guessed Decision.
82
82
  - **Stands on its own.** Whatever the input mode, the finished `intent.md` carries
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: refine
2
+ name: pb-refine
3
3
  description: Keep intent.md true — attack the plan for holes (append as Open questions) and refine or repair the Frame, Decisions, Constraints, and Steps to match reality. Usable at any point; you propose, the human approves.
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -10,16 +10,16 @@ allowed-tools: Read, Edit, Bash(plumbbob status:*)
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
- `/plumbbob:refine` keeps `intent.md` honest. Use it at **any point** — right after `/plumbbob:plan`
13
+ `/plumbbob:pb-refine` keeps `intent.md` honest. Use it at **any point** — right after `/plumbbob:pb-plan`
14
14
  to stress-test a fresh frame, or mid-build to repair a plan that drifted from what the
15
- code is actually doing. It is the document-level complement to `/plumbbob:step` (which only
16
- sharpens the *next* step): `/plumbbob:refine` works the *whole* plan.
15
+ code is actually doing. It is the document-level complement to `/plumbbob:pb-step` (which only
16
+ sharpens the *next* step): `/plumbbob:pb-refine` works the *whole* plan.
17
17
 
18
18
  ## No-session refusal
19
19
 
20
20
  This skill refines an existing plan, so it needs one. Read the state injected above: if
21
21
  it is `NO ACTIVE SESSION`, **refuse** in one line and tell the human to run
22
- `plumbbob start "<title>"` (or `/plumbbob:plan`) first, and edit nothing. Every active state
22
+ `plumbbob start "<title>"` (or `/plumbbob:pb-plan`) first, and edit nothing. Every active state
23
23
  is fine — refining is always available.
24
24
 
25
25
  ## Two modes
@@ -41,5 +41,5 @@ is fine — refining is always available.
41
41
  approves every change to `intent.md`. Never guess a hole into a Decision.
42
42
  - **Open questions for holes, edits for drift.** New uncertainty goes to
43
43
  `## Open questions`; settled drift gets repaired in place once the human OKs it.
44
- - **Refine the plan, not the code.** `/plumbbob:refine` touches `intent.md` only — turning a
45
- decision into a diff is `/plumbbob:build` or your own hands, never this skill.
44
+ - **Refine the plan, not the code.** `/plumbbob:pb-refine` touches `intent.md` only — turning a
45
+ decision into a diff is `/plumbbob:pb-build` or your own hands, never this skill.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: revert
2
+ name: pb-revert
3
3
  description: Human-triggered driver for `plumbbob revert` — git reset --hard to a checkpoint SHA (discarding the half-done step) and return to DESIGN.
4
4
  disable-model-invocation: true
5
5
  model: haiku
@@ -14,6 +14,6 @@ This is a **driver skill** — a chat-side trigger for the mechanical `plumbbob
14
14
 
15
15
  ## What it does
16
16
 
17
- 1. Read an optional target step from the way you were invoked (e.g. `/plumbbob:revert --to 2` → step `2`). With no target, revert goes to the last done-checkpoint.
17
+ 1. Read an optional target step from the way you were invoked (e.g. `/plumbbob:pb-revert --to 2` → step `2`). With no target, revert goes to the last done-checkpoint.
18
18
  2. Run `plumbbob revert` (or `plumbbob revert --to <n>`) via Bash. This is a `git reset --hard` — it discards the current in-progress step. Run it exactly as the human asked; do not add or drop the `--to` on your own.
19
19
  3. Report the verb's output verbatim — which checkpoint it reset to, or any refusal.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: spike
2
+ name: pb-spike
3
3
  description: Human-triggered driver for `plumbbob spike` — open a throwaway worktree experiment for a genuine fork, or tear it down with `spike done`.
4
4
  disable-model-invocation: true
5
5
  model: haiku
@@ -14,6 +14,6 @@ This is a **driver skill** — a chat-side trigger for the mechanical `plumbbob
14
14
 
15
15
  ## What it does
16
16
 
17
- 1. Read the spike target from the way you were invoked: a slug to open one (e.g. `/plumbbob:spike redis-cache`), or the literal `done` to tear the current spike down (`/plumbbob:spike done`). If neither is present, ask which and run nothing.
17
+ 1. Read the spike target from the way you were invoked: a slug to open one (e.g. `/plumbbob:pb-spike redis-cache`), or the literal `done` to tear the current spike down (`/plumbbob:pb-spike done`). If neither is present, ask which and run nothing.
18
18
  2. Run `plumbbob spike "<slug>"` or `plumbbob spike done` via Bash.
19
19
  3. Report the verb's output verbatim — the worktree it created or removed, or any refusal.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: status
2
+ name: pb-status
3
3
  description: Show the orientation dashboard — where you are, what's done, what's parked, and the next move. A thin trigger for `plumbbob status`.
4
4
  disable-model-invocation: true
5
5
  model: haiku
@@ -19,4 +19,4 @@ skill carries **no Edit and no Write tool**: the CLI is the source of truth, so
19
19
  ## What it does
20
20
 
21
21
  1. Surface the injected `status` output — the dashboard and its suggested next move.
22
- 2. If it reads `NO ACTIVE SESSION`, tell the human to `/plumbbob:plan` to frame a goal.
22
+ 2. If it reads `NO ACTIVE SESSION`, tell the human to `/plumbbob:pb-plan` to frame a goal.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: step
2
+ name: pb-step
3
3
  description: Revise the next increment just-in-time — sharpen the next undone step against what's now true, or (with input) re-cut, split, or add a step. Empty input runs an automatic sharpen. One at a time; the human approves.
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -10,21 +10,21 @@ allowed-tools: Read, Edit, Bash(plumbbob status:*)
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
- `/plumbbob:plan` authors the **whole** step list up front, so `/plumbbob:step` is the
13
+ `/plumbbob:pb-plan` authors the **whole** step list up front, so `/plumbbob:pb-step` is the
14
14
  **just-in-time revision** move: it keeps the *next* undone step honest against what the
15
- build has actually taught you, right before you `/plumbbob:build` it. (Framing the whole goal
16
- is the separate `/plumbbob:plan` move.) It can also add or re-cut a step when scope genuinely
15
+ build has actually taught you, right before you `/plumbbob:pb-build` it. (Framing the whole goal
16
+ is the separate `/plumbbob:pb-plan` move.) It can also add or re-cut a step when scope genuinely
17
17
  grew — but its everyday job is to sharpen, not to invent.
18
18
 
19
19
  ## Two ways to fire it
20
20
 
21
- - **`/plumbbob:step` (no input) → automatic sharpen.** Re-examine the next undone step
21
+ - **`/plumbbob:pb-step` (no input) → automatic sharpen.** Re-examine the next undone step
22
22
  against the completed code, the Decisions, the Constraints, and the build-log, then
23
23
  make the obvious revisions to its **done-when** and **seam** so it matches reality —
24
24
  e.g. a file moved, a decision narrowed the scope, an earlier step already did part of
25
25
  it. This is the zero-effort "keep my next step in sync" move: if the human does
26
26
  nothing else, the next step stays current.
27
- - **`/plumbbob:step <what changed>` → directed revision.** Take the human's input and propose
27
+ - **`/plumbbob:pb-step <what changed>` → directed revision.** Take the human's input and propose
28
28
  the matching change: tighten the done-when, adjust the seam, split the step in two, or
29
29
  add a new increment the plan was missing.
30
30
 
@@ -33,7 +33,7 @@ grew — but its everyday job is to sharpen, not to invent.
33
33
  1. **Read the plan and the reality.** Read `intent.md`'s Frame, Decisions, Constraints,
34
34
  and the steps already done, plus the next undone step, to see what it *should* now be.
35
35
  2. **Propose the revision** (or the new/split step): a one-line **title**, a **done-when**
36
- `/plumbbob:verify` can validate, and a **seam** (exact paths, or a `dir/` grant). Keep it
36
+ `/plumbbob:pb-verify` can validate, and a **seam** (exact paths, or a `dir/` grant). Keep it
37
37
  small enough to verify in one review pass. Show the before/after so the human can see
38
38
  what you changed and why.
39
39
  3. **Get the human's OK**, then write it into `## Steps` in the standard format —
@@ -42,7 +42,7 @@ grew — but its everyday job is to sharpen, not to invent.
42
42
 
43
43
  ## The hard contracts
44
44
 
45
- - **One verifiable increment.** Each step carries a done-when `/plumbbob:verify` can check
45
+ - **One verifiable increment.** Each step carries a done-when `/plumbbob:pb-verify` can check
46
46
  and a seam small enough to review in one pass.
47
47
  - **Edit `## Steps` only**, in the standard format `status` and `build` parse — never
48
48
  the Roadmap, never loose prose. A done step (`[x]`) is history; do not rewrite it.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: verify
2
+ name: pb-verify
3
3
  description: "The verify tick — run the check, self-review the diff against intent, validate the step's done-when, pause for your approval, then checkpoint. Executor-agnostic: it reads the diff, not who wrote it."
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -11,7 +11,7 @@ allowed-tools: Read, Bash(plumbbob status:*), Bash(plumbbob check:*), Bash(plumb
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
13
  This is the **tick** — the one beat where the human is the clock. Whatever produced
14
- the current diff — `/plumbbob:build`, your own hands, a vibe session, another harness —
14
+ the current diff — `/plumbbob:pb-build`, your own hands, a vibe session, another harness —
15
15
  this skill verifies it the same way: **it reads the diff, not the author** (D3).
16
16
 
17
17
  ## What this skill does, in order
@@ -40,7 +40,7 @@ this skill verifies it the same way: **it reads the diff, not the author** (D3).
40
40
  - **Never skip the pause.** Check → self-review → validate, then wait. Approval is
41
41
  the only thing that triggers the checkpoint.
42
42
  - **Read the diff, not the author** (D3). Verify identically whether the code was
43
- built by `/plumbbob:build`, by hand, vibed, or by another harness.
43
+ built by `/plumbbob:pb-build`, by hand, vibed, or by another harness.
44
44
  - **Red means stop, not pause.** A failing check is not an approval decision; report
45
45
  it and end your turn.
46
46
  - **You review; you do not build.** If the self-review finds a problem, surface it
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: wrap
2
+ name: pb-wrap
3
3
  description: Wrap up the build — write the report (what shipped, decisions, parked/harvested items, deferred tangents), then safely archive intent + build-log + report before clearing for a fresh goal. Archive-then-clear, never destroy. Report by default, no gate.
4
4
  disable-model-invocation: true
5
5
  model: opus
@@ -10,7 +10,7 @@ allowed-tools: Read, Write, Bash(plumbbob status:*), Bash(plumbbob wrap:*)
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
12
 
13
- `/plumbbob:wrap` ends the build: it captures what happened, archives it, and clears the
13
+ `/plumbbob:pb-wrap` ends the build: it captures what happened, archives it, and clears the
14
14
  sidecar for the next goal. **Report by default** (D9) — no refuse-without-report gate,
15
15
  and no separate docs phase.
16
16
 
@@ -27,7 +27,7 @@ and no separate docs phase.
27
27
  to the report, archives intent + build-log + report to
28
28
  `.plumbbob/archive/<date>-<slug>/`, and clears the sidecar (STATE last). Git is not
29
29
  touched.
30
- 3. **Point at the next goal** — `/plumbbob:plan` to frame the next one.
30
+ 3. **Point at the next goal** — `/plumbbob:pb-plan` to frame the next one.
31
31
 
32
32
  ## The hard contracts
33
33
 
@@ -5,7 +5,7 @@ step boundaries. The antidote to "my plan got lost in the noise."
5
5
  Steps : where you are. One step in flight at a time.
6
6
  Park list : where ideas go so you do not chase them. CAPTURE, never act inline.
7
7
  Harvest : the boundary ritual that keeps you on one branch.
8
- Log : the audit trail. Feeds the /plumbbob:wrap report, then gets archived.
8
+ Log : the audit trail. Feeds the /plumbbob:pb-wrap report, then gets archived.
9
9
  -->
10
10
 
11
11
  # Build log — {{TITLE}}
@@ -16,8 +16,8 @@ step boundaries. The antidote to "my plan got lost in the noise."
16
16
  ## Steps
17
17
 
18
18
  *(Mirror of intent.md's Steps, with live status. Only ONE step is in flight. A step
19
- is done only after a checkpoint — check green + checkpoint taken, via `/plumbbob:verify` or
20
- `/plumbbob:build`.)*
19
+ is done only after a checkpoint — check green + checkpoint taken, via `/plumbbob:pb-verify` or
20
+ `/plumbbob:pb-build`.)*
21
21
 
22
22
  - ☐ 1. <step>
23
23
 
@@ -25,16 +25,16 @@ is done only after a checkpoint — check green + checkpoint taken, via `/plumbb
25
25
 
26
26
  > Mid-step, every new problem / idea / "ooh what if" lands HERE, untouched, and you
27
27
  > go straight back to the step. Acting the instant an idea arrives is the disease.
28
- > Capture is one line (`/plumbbob:park` composes it). Harvest happens only at the boundary.
28
+ > Capture is one line (`/plumbbob:pb-park` composes it). Harvest happens only at the boundary.
29
29
 
30
- ## Harvest *(run `/plumbbob:harvest` at each step boundary, after green)*
30
+ ## Harvest *(run `/plumbbob:pb-harvest` at each step boundary, after green)*
31
31
 
32
32
  Classify each parked item as exactly ONE. Naming it before acting is what keeps you
33
33
  from sprawling across branches.
34
34
 
35
35
  | Class | Meaning | Action |
36
36
  |------------------|-------------------------------------------|---------------------------------|
37
- | **blocker** | Plan was wrong/incomplete; can't proceed | `/plumbbob:revert`, fold into intent |
37
+ | **blocker** | Plan was wrong/incomplete; can't proceed | `/plumbbob:pb-revert`, fold into intent |
38
38
  | **tangent** | A different path, not clearly better | Defer or kill. Default here. |
39
39
  | **pivot signal** | Evidence the whole approach is wrong | Stop. Replan deliberately. |
40
40
 
@@ -48,7 +48,7 @@ Harvest results this boundary:
48
48
  ## Log
49
49
 
50
50
  *(Append-only. One decision or event per line, dated. What you point at to say "I
51
- did that — the LLM helped, but those were my calls." `/plumbbob:wrap` reads this for the
51
+ did that — the LLM helped, but those were my calls." `/plumbbob:pb-wrap` reads this for the
52
52
  report; `plumbbob wrap` archives it under `.plumbbob/archive/`.)*
53
53
 
54
54
  - <date> — <decision / event / what shipped this step>
@@ -33,24 +33,24 @@ open. If the implementor (you-later, or the LLM) has to guess, the doc failed.
33
33
  ## Decisions
34
34
 
35
35
  *(One line each. Settled, not re-litigated in the chat. Grows as you resolve the
36
- holes `/plumbbob:refine` surfaces, and as blockers fold in during BUILD.)*
36
+ holes `/plumbbob:pb-refine` surfaces, and as blockers fold in during BUILD.)*
37
37
 
38
38
  - D1: <decision> — *because* <the one reason that mattered>
39
39
 
40
40
  ## Constraints
41
41
 
42
- *(Hard rules the build must honor. `/plumbbob:verify` and `/plumbbob:refine` read against these.)*
42
+ *(Hard rules the build must honor. `/plumbbob:pb-verify` and `/plumbbob:pb-refine` read against these.)*
43
43
 
44
44
  - C1: <e.g. functional/procedural only; no new dependencies>
45
45
 
46
46
  ## Steps
47
47
 
48
- *(The build plan. `/plumbbob:plan` authors the **whole list up front** — each step a small,
48
+ *(The build plan. `/plumbbob:pb-plan` authors the **whole list up front** — each step a small,
49
49
  verifiable increment with its own **done-when** and **seam** (the paths it will touch,
50
- which `/plumbbob:build` records in `.plumbbob/SEAM` for orientation — awareness, not a lock
51
- in v2). Then drive `/plumbbob:build` until done. Later steps may be fuzzier than the first;
52
- sharpen the next one just-in-time with `/plumbbob:step` (empty input auto-syncs it), and use
53
- `/plumbbob:refine` to repair the whole plan when a blocker rewrites it.)*
50
+ which `/plumbbob:pb-build` records in `.plumbbob/SEAM` for orientation — awareness, not a lock
51
+ in v2). Then drive `/plumbbob:pb-build` until done. Later steps may be fuzzier than the first;
52
+ sharpen the next one just-in-time with `/plumbbob:pb-step` (empty input auto-syncs it), and use
53
+ `/plumbbob:pb-refine` to repair the whole plan when a blocker rewrites it.)*
54
54
 
55
55
  1. [ ] <step> — **done when:** <criterion, ideally a test or check result>
56
56
  - seam: `<file>`, `<file>`