plumbbob 0.6.4 → 0.6.6
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/dist/lib/buildlog.js +1 -1
- package/dist/lib/intent.js +3 -2
- package/dist/lib/orient.js +16 -10
- package/dist/verbs/check.js +1 -1
- package/dist/verbs/checkpoint.js +12 -7
- package/dist/verbs/doctor.js +2 -2
- package/dist/verbs/finish.js +2 -2
- package/dist/verbs/park.js +1 -1
- package/package.json +1 -1
- package/skills/pb-build/SKILL.md +18 -14
- package/skills/pb-finish/SKILL.md +5 -6
- package/skills/pb-harvest/SKILL.md +1 -2
- package/skills/pb-park/SKILL.md +1 -1
- package/skills/pb-plan/SKILL.md +20 -5
- package/skills/pb-refine/SKILL.md +0 -1
- package/skills/pb-status/SKILL.md +5 -1
- package/skills/pb-step/SKILL.md +5 -4
- package/skills/pb-verify/SKILL.md +8 -9
- package/templates/intent.md +4 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
3
|
"displayName": "PlumbBob",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.6",
|
|
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/dist/lib/buildlog.js
CHANGED
|
@@ -27,7 +27,7 @@ export function appendToSection(content, heading, line) {
|
|
|
27
27
|
}
|
|
28
28
|
// The Log line `checkpoint` writes for a landed step: dated, names the step (its
|
|
29
29
|
// title when intent.md still carries it), and carries the short SHA. One line of the
|
|
30
|
-
// build's history — `/
|
|
30
|
+
// build's history — `/pb-finish` reads these instead of re-narrating the build.
|
|
31
31
|
export function checkpointLogLine(date, step, sha, title) {
|
|
32
32
|
const head = `- ${date} — step ${step} checkpointed · ${sha.slice(0, 9)}`;
|
|
33
33
|
return title !== null && title.length > 0 ? `${head} — ${title}` : head;
|
package/dist/lib/intent.js
CHANGED
|
@@ -51,12 +51,13 @@ export function parseStepSeam(content, step) {
|
|
|
51
51
|
// The seam declaration is the seam line plus any wrapped continuation lines.
|
|
52
52
|
// Truncate each at an HTML-comment opener so a trailing `<!-- ... -->` note
|
|
53
53
|
// (which may carry its own backticks) is never read as a seam token; a
|
|
54
|
-
// continuation ends at the first line whose pre-comment text has no backtick
|
|
54
|
+
// continuation ends at the first line whose pre-comment text has no backtick
|
|
55
|
+
// or that opens a new sub-bullet (a `- model:` note may carry backticks too, D62).
|
|
55
56
|
const seamStart = seamLines[0] ?? 0;
|
|
56
57
|
const decl = [];
|
|
57
58
|
for (let i = seamStart; i < itemLines.length; i++) {
|
|
58
59
|
const beforeComment = (itemLines[i] ?? '').split('<!--')[0] ?? '';
|
|
59
|
-
if (i > seamStart && !beforeComment.includes('`')) {
|
|
60
|
+
if (i > seamStart && (!beforeComment.includes('`') || /^\s*-\s/.test(beforeComment))) {
|
|
60
61
|
break;
|
|
61
62
|
}
|
|
62
63
|
decl.push(beforeComment);
|
package/dist/lib/orient.js
CHANGED
|
@@ -49,7 +49,9 @@ export function parseSteps(intent) {
|
|
|
49
49
|
const block = lines.slice(s.idx, blockEnd).join('\n');
|
|
50
50
|
const dw = /\*\*done when:\*\*\s*(.+)/i.exec(block);
|
|
51
51
|
const doneWhen = dw ? (dw[1] ?? '').trim() : null;
|
|
52
|
-
|
|
52
|
+
const md = /^\s*-\s*model:\s*(.+)$/im.exec(block);
|
|
53
|
+
const model = md ? (md[1] ?? '').trim() : null;
|
|
54
|
+
return { n: s.n, done: s.done, title: s.title, planned: /done when/i.test(block), doneWhen, model };
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
// Flip step N's `[ ]` checkbox to `[x]` within the `## Steps` section — mechanical
|
|
@@ -77,7 +79,7 @@ export function parseOpenQuestions(intent) {
|
|
|
77
79
|
.length;
|
|
78
80
|
}
|
|
79
81
|
// Open parked items: `- [ ]` lines under `## Park list` (the `park` verb's format).
|
|
80
|
-
// A harvested item is flipped to `- [x]` by `/
|
|
82
|
+
// A harvested item is flipped to `- [x]` by `/pb-harvest` and no longer counts; the
|
|
81
83
|
// `(none yet)` placeholder and the blockquote instructions never match.
|
|
82
84
|
export function parseParked(buildLog) {
|
|
83
85
|
return sectionLines(buildLog, '## Park list').filter((l) => /^-\s+\[ \]\s+\S/.test(l.trim())).length;
|
|
@@ -101,22 +103,22 @@ function nextMove(spiking, steps, inFlight, parked) {
|
|
|
101
103
|
return 'close the spike — `plumbbob spike done`';
|
|
102
104
|
}
|
|
103
105
|
if (inFlight !== null) {
|
|
104
|
-
return `finish step ${inFlight} — \`/
|
|
106
|
+
return `finish step ${inFlight} — \`/pb-verify\` (or keep editing, then \`/pb-verify\`)`;
|
|
105
107
|
}
|
|
106
108
|
// At the boundary (DESIGN): the move follows from the steps.
|
|
107
109
|
const nextUndone = steps.find((s) => !s.done);
|
|
108
110
|
if (nextUndone === undefined) {
|
|
109
111
|
if (steps.length === 0) {
|
|
110
|
-
return 'plan the first step — `/
|
|
112
|
+
return 'plan the first step — `/pb-step`';
|
|
111
113
|
}
|
|
112
114
|
// Batch-default: the steps were planned up front, so finishing them usually
|
|
113
|
-
// means "finish up" — but `/
|
|
114
|
-
const harvest = parked > 0 ? `harvest ${parked} parked idea${parked === 1 ? '' : 's'} — \`/
|
|
115
|
-
return `${harvest}finish up — \`/
|
|
115
|
+
// means "finish up" — but `/pb-step` can still add an increment if reality grew.
|
|
116
|
+
const harvest = parked > 0 ? `harvest ${parked} parked idea${parked === 1 ? '' : 's'} — \`/pb-harvest\`; then ` : '';
|
|
117
|
+
return `${harvest}finish up — \`/pb-finish\` (or \`/pb-step\` to add another increment)`;
|
|
116
118
|
}
|
|
117
119
|
return nextUndone.planned
|
|
118
|
-
? `build step ${nextUndone.n} — \`/
|
|
119
|
-
: `plan step ${nextUndone.n} — \`/
|
|
120
|
+
? `build step ${nextUndone.n} — \`/pb-build\` (or \`/pb-step\` to revise it first)`
|
|
121
|
+
: `plan step ${nextUndone.n} — \`/pb-step\``;
|
|
120
122
|
}
|
|
121
123
|
export function orient(input) {
|
|
122
124
|
const steps = parseSteps(input.intent);
|
|
@@ -134,6 +136,7 @@ export function orient(input) {
|
|
|
134
136
|
next: nextMove(input.spiking, steps, input.inFlight, parked),
|
|
135
137
|
nextDoneWhen: nextUndone?.doneWhen ?? null,
|
|
136
138
|
nextSeam: seamParse !== null && seamParse.ok ? seamParse.seam : [],
|
|
139
|
+
nextModel: nextUndone?.model ?? null,
|
|
137
140
|
};
|
|
138
141
|
}
|
|
139
142
|
export function formatOrientation(o) {
|
|
@@ -146,7 +149,7 @@ export function formatOrientation(o) {
|
|
|
146
149
|
if (s !== nextUndone) {
|
|
147
150
|
return head;
|
|
148
151
|
}
|
|
149
|
-
// Surface the next step's detail so the human can review it (and `/
|
|
152
|
+
// Surface the next step's detail so the human can review it (and `/pb-step`-
|
|
150
153
|
// revise) before building. Only what's present — a rough step shows neither.
|
|
151
154
|
const detail = [];
|
|
152
155
|
if (o.nextDoneWhen !== null) {
|
|
@@ -155,6 +158,9 @@ export function formatOrientation(o) {
|
|
|
155
158
|
if (o.nextSeam.length > 0) {
|
|
156
159
|
detail.push(` seam: ${o.nextSeam.join(', ')}`);
|
|
157
160
|
}
|
|
161
|
+
if (o.nextModel !== null) {
|
|
162
|
+
detail.push(` model: ${o.nextModel}`);
|
|
163
|
+
}
|
|
158
164
|
return detail.length > 0 ? [head, ...detail].join('\n') : head;
|
|
159
165
|
});
|
|
160
166
|
const stepsBlock = o.steps.length === 0 ? ' (no steps planned yet)' : ` steps ${doneCount}/${o.steps.length} done\n${stepLines.join('\n')}`;
|
package/dist/verbs/check.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// `plumbbob check` — run the heavy gate (D16/D24/D32) and report, with NO state
|
|
2
|
-
// change. The read-only half of the verify tick: `/
|
|
2
|
+
// change. The read-only half of the verify tick: `/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, 1 = red, 2 = the gate itself broke).
|
|
5
5
|
//
|
package/dist/verbs/checkpoint.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// is already clean — the human's commit skill may have committed first), records the
|
|
6
6
|
// SHA, flips the intent checkbox to `[x]`, and clears any STEP/SEAM (which returns
|
|
7
7
|
// the dashboard to the DESIGN boundary). The diff's author is irrelevant:
|
|
8
|
-
// `/
|
|
8
|
+
// `/pb-build`, your hands, a vibe session, or another harness all checkpoint
|
|
9
9
|
// the same way.
|
|
10
10
|
import { appendFileSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { commit, findRepoRoot, headSha, isDirty, stageAll, stagePath, stagedPaths, stagedStat } from "../lib/git.js";
|
|
@@ -81,9 +81,10 @@ function planSubject(root) {
|
|
|
81
81
|
return title ? `plumbbob: plan — ${title}` : 'plumbbob: plan';
|
|
82
82
|
}
|
|
83
83
|
// Step resolution (D3): explicit arg > in-flight STEP file > first undone step in
|
|
84
|
-
// intent.md. Returns null when none can be determined.
|
|
84
|
+
// intent.md. Returns null when none can be determined. A `-m` value is a message,
|
|
85
|
+
// never a step — `checkpoint -m "2"` must not read as step 2.
|
|
85
86
|
function resolveStep(root, args) {
|
|
86
|
-
const explicit = args.find((a) => /^\d+$/.test(a));
|
|
87
|
+
const explicit = args.filter((_, i) => args[i - 1] !== '-m').find((a) => /^\d+$/.test(a));
|
|
87
88
|
if (explicit !== undefined) {
|
|
88
89
|
return Number(explicit);
|
|
89
90
|
}
|
|
@@ -108,7 +109,7 @@ function warnScopeDrift(root, step) {
|
|
|
108
109
|
const outside = scopeDrift(stagedPaths(root), seam);
|
|
109
110
|
if (outside.length > 0) {
|
|
110
111
|
process.stderr.write(`plumbbob: heads-up — staged paths outside step ${step}'s seam: ${outside.join(', ')}. ` +
|
|
111
|
-
`The checkpoint captures them; if that's real scope drift, the plan may need a \`/
|
|
112
|
+
`The checkpoint captures them; if that's real scope drift, the plan may need a \`/pb-step\` revision.\n`);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
// The seam tokens for the in-flight step: the normalized SEAM file `build` wrote
|
|
@@ -135,7 +136,10 @@ function flipIntent(root, step) {
|
|
|
135
136
|
writeFileSync(intentPath(root), markStepDone(readFileSync(intentPath(root), 'utf8'), step));
|
|
136
137
|
}
|
|
137
138
|
catch {
|
|
138
|
-
//
|
|
139
|
+
// Best-effort bookkeeping — the checkpoint SHA is the source of truth — but the
|
|
140
|
+
// dashboard reads intent.md, so a swallowed failure here makes orientation lie.
|
|
141
|
+
process.stderr.write(`plumbbob: heads-up — could not flip step ${step} to [x] in intent.md; ` +
|
|
142
|
+
`the checkpoint is recorded, but the dashboard will still show step ${step} as next. Flip it by hand.\n`);
|
|
139
143
|
}
|
|
140
144
|
}
|
|
141
145
|
// Append a dated line to the build-log's `## Log` so the build's history accrues at
|
|
@@ -179,9 +183,10 @@ function messageArg(args) {
|
|
|
179
183
|
// so the skill can compose proportional prose the CLI never could. Returns null
|
|
180
184
|
// when the flag is absent or stdin is empty — either way the deterministic
|
|
181
185
|
// fallback body takes over. Reading fd 0 blocks until EOF, which the heredoc
|
|
182
|
-
// supplies; a read error (no stdin attached) degrades to the fallback
|
|
186
|
+
// supplies; a read error (no stdin attached) degrades to the fallback, and an
|
|
187
|
+
// interactive TTY — which would never send EOF — skips the read instead of hanging.
|
|
183
188
|
function bodyArg(args) {
|
|
184
|
-
if (!args.includes('--body')) {
|
|
189
|
+
if (!args.includes('--body') || process.stdin.isTTY === true) {
|
|
185
190
|
return null;
|
|
186
191
|
}
|
|
187
192
|
try {
|
package/dist/verbs/doctor.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Code loads it in place as a plugin (skills `/plumbbob:*`, the post-edit hook from
|
|
6
6
|
// hooks.json). doctor verifies the link resolves to a package carrying the manifest,
|
|
7
7
|
// the skills, and the hook — and names the fix for anything missing. The failure
|
|
8
|
-
// class it exists for is SILENT (a `/
|
|
8
|
+
// class it exists for is SILENT (a `/pb-status` that opens an empty dashboard
|
|
9
9
|
// because the plugin never linked).
|
|
10
10
|
//
|
|
11
11
|
// 2. The repo sidecar layout. A repo scaffolded by a pre-restructure plumbbob carries a
|
|
@@ -419,7 +419,7 @@ export async function doctor(cwd, args = []) {
|
|
|
419
419
|
out.push(failed === 0
|
|
420
420
|
? 'plumbbob: all checks passed. If a skill still misbehaves, restart Claude Code (or /reload-plugins).'
|
|
421
421
|
: `plumbbob: ${failed} problem(s) — apply the → fixes, then restart Claude Code.`);
|
|
422
|
-
out.push('plumbbob: skills shell a bare `plumbbob`. The marketplace plugin puts it on PATH only inside a Claude Code session (via bin/) — there is no terminal `plumbbob`; for one (or the skills-dir install) run `npm i -g plumbbob`. In-session you can also run this as `/
|
|
422
|
+
out.push('plumbbob: skills shell a bare `plumbbob`. The marketplace plugin puts it on PATH only inside a Claude Code session (via bin/) — there is no terminal `plumbbob`; for one (or the skills-dir install) run `npm i -g plumbbob`. In-session you can also run this as `/pb-doctor`. Sessions are per-project via `plumbbob start`.');
|
|
423
423
|
process.stdout.write(`${out.join('\n')}\n`);
|
|
424
424
|
return failed === 0 ? 0 : 1;
|
|
425
425
|
}
|
package/dist/verbs/finish.js
CHANGED
|
@@ -23,7 +23,7 @@ export function finish(cwd, args = []) {
|
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
25
|
process.stderr.write('plumbbob: note — no report.md found; finishing without one ' +
|
|
26
|
-
'(/
|
|
26
|
+
'(/pb-finish normally writes the report first). No gate (D9).\n');
|
|
27
27
|
}
|
|
28
28
|
// The final commit (D34): stage the report just written plus the build folder's
|
|
29
29
|
// tail (the last step's checkpoint line lands one commit late, D37) and commit it
|
|
@@ -49,7 +49,7 @@ export function finish(cwd, args = []) {
|
|
|
49
49
|
rmSync(join(sidecarDir(root), 'STATE'), { force: true });
|
|
50
50
|
const where = slug === null ? '.plumbbob/' : `.plumbbob/builds/${slug}/`;
|
|
51
51
|
process.stdout.write(`plumbbob: finished — ${sha.slice(0, 9)}. ${where} rides your branch into the PR. ` +
|
|
52
|
-
'Run `/
|
|
52
|
+
'Run `/pb-plan` (or `plumbbob start "<title>"`) to frame the next goal.\n');
|
|
53
53
|
return 0;
|
|
54
54
|
}
|
|
55
55
|
// The CLI-owned final-commit subject (D34): `plumbbob: finish — <title>`, mirroring
|
package/dist/verbs/park.js
CHANGED
|
@@ -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 /
|
|
2
|
+
// the build-log's Park list. No model turn, no composition (that is /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";
|
package/package.json
CHANGED
package/skills/pb-build/SKILL.md
CHANGED
|
@@ -3,7 +3,6 @@ name: pb-build
|
|
|
3
3
|
description: The default engine — read the next planned step from intent, implement it (its done-when, seam, Decisions, Constraints), then verify it through to the approval pause. Swappable — build by hand/vibed/another harness and run /pb-verify instead. `--auto` self-approves and chains to done; a step range like `1-3` self-approves through step 3, then pauses.
|
|
4
4
|
argument-hint: "[step-number | step-range] [--auto]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
model: opus
|
|
7
6
|
allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob build:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(plumbbob agent:*), Bash(git diff:*)
|
|
8
7
|
---
|
|
9
8
|
|
|
@@ -12,7 +11,7 @@ allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob build:*
|
|
|
12
11
|
Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not on PATH in this session. Marketplace install: confirm the plugin is enabled in /plugin, then /reload-plugins. Skills-dir/global install: npm i -g plumbbob && plumbbob init."`
|
|
13
12
|
|
|
14
13
|
This is the **bundled executor** — the default engine, not the only one. It is
|
|
15
|
-
**swappable
|
|
14
|
+
**swappable**: you can implement any step by hand, in a vibe session, or with
|
|
16
15
|
another harness and go straight to `/pb-verify` instead — plumbbob does not care how
|
|
17
16
|
the diff appeared. When you do run it, it reads the plan, writes the step, and
|
|
18
17
|
carries straight through to the verify pause.
|
|
@@ -21,6 +20,11 @@ Since `/pb-plan` lays down the whole step list up front, the happy path is to fi
|
|
|
21
20
|
`/pb-build` once per step until done — each run builds the next undone step and stops
|
|
22
21
|
at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick.**
|
|
23
22
|
|
|
23
|
+
A model note: this skill **inherits the session model** — nothing pins or switches
|
|
24
|
+
it. If the step you're about to build carries a `- model:` recommendation that
|
|
25
|
+
differs from the model you're running as, say so before implementing — the human can
|
|
26
|
+
`/model` and re-fire to honor it, or wave you on. Advisory, never a gate.
|
|
27
|
+
|
|
24
28
|
## What this skill does, in order
|
|
25
29
|
|
|
26
30
|
1. **Pick the step.** Use the number you were invoked with (e.g. `/pb-build 4`) — or,
|
|
@@ -34,7 +38,7 @@ at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick
|
|
|
34
38
|
3. **Read the plan.** Read the step's **done-when**, its **seam**, and the
|
|
35
39
|
**Decisions** and **Constraints** in `intent.md`. Build to *that* — the deciding
|
|
36
40
|
already happened, off the chat.
|
|
37
|
-
- **Run any bound `before`-agents
|
|
41
|
+
- **Run any bound `before`-agents**. If the build's `harness.json` binds
|
|
38
42
|
agents to this step's `before` slot, run `plumbbob agent run --step <n> --mode
|
|
39
43
|
before` first: each returns a validated envelope on stdout that plumbbob also
|
|
40
44
|
appends to the step's `handoff.json`, and its `summary`/`body` become **context you
|
|
@@ -45,17 +49,17 @@ at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick
|
|
|
45
49
|
edit — capture it and stay on the step. If you genuinely cannot finish without
|
|
46
50
|
touching more than the seam, that is scope drift: surface it to the human rather
|
|
47
51
|
than sprawling.
|
|
48
|
-
- **If a `build`-slot agent is bound, delegate the diff to it
|
|
52
|
+
- **If a `build`-slot agent is bound, delegate the diff to it**. Run
|
|
49
53
|
`plumbbob agent run --step <n> --mode build` and let that agent author the step's
|
|
50
54
|
code instead of writing it yourself; its envelope reports what it did. You still
|
|
51
|
-
own the verify tick below — the diff is reviewed the same way whoever wrote it
|
|
52
|
-
- **A manifest's `when` prose is your cue to fire an agent mid-build
|
|
55
|
+
own the verify tick below — the diff is reviewed the same way whoever wrote it.
|
|
56
|
+
- **A manifest's `when` prose is your cue to fire an agent mid-build**.
|
|
53
57
|
The three slots are the only *declarative* lifecycle points; there is no config for
|
|
54
58
|
"a salient moment in the middle." That is judgment, and you are the frontier model
|
|
55
59
|
in the room: when the work reaches the situation a bound agent's `when` (or a step
|
|
56
60
|
`note`) describes, fire `plumbbob agent run <name> --step <n>` yourself. Prose is
|
|
57
61
|
the orchestration language; you are the workflow engine.
|
|
58
|
-
- **Route a non-`done` envelope by its status
|
|
62
|
+
- **Route a non-`done` envelope by its status**. An agent that returns
|
|
59
63
|
`blocked` couldn't finish (missing input, failed precondition): surface its `notes`,
|
|
60
64
|
let the human unblock, and re-run it — don't work around it. One that returns
|
|
61
65
|
`drift` finished but found the plan no longer matches reality: **stop and send the
|
|
@@ -68,9 +72,9 @@ at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick
|
|
|
68
72
|
still runs everything) → run any bound `after`-agents (`plumbbob agent run --step
|
|
69
73
|
<n> --mode after`) and fold their envelopes into the self-review as **advisory
|
|
70
74
|
input** — they inform, they never gate (checkride gates, the human is the clock; an
|
|
71
|
-
`after`-agent that could fail a step is the lock in autonomy's costume
|
|
75
|
+
`after`-agent that could fail a step is the lock in autonomy's costume) →
|
|
72
76
|
self-review the diff against the done-when, the Decisions, and the Constraints (a
|
|
73
|
-
single structured read
|
|
77
|
+
single structured read) → validate → **PAUSE
|
|
74
78
|
for the human's approval** → only on approval, checkpoint with
|
|
75
79
|
`plumbbob checkpoint <n> --body <<'BODY' … BODY` — a commit body **proportional to the
|
|
76
80
|
step** (a line for a trivial change, a short paragraph for a meatier one; no TIL scan,
|
|
@@ -85,8 +89,8 @@ at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick
|
|
|
85
89
|
progress instead of approving each step. It does the same work, but **the agent reviews
|
|
86
90
|
and approves in the human's place**, and it **chains**:
|
|
87
91
|
|
|
88
|
-
- Build the next step, running its slots in the same order as the default
|
|
89
|
-
|
|
92
|
+
- Build the next step, running its slots in the same order as the default
|
|
93
|
+
path: bound `before`-agents → implement (or the bound `build`-agent) → bound
|
|
90
94
|
`after`-agents → `check` → self-review → **if the check is green AND the
|
|
91
95
|
self-review finds no done-when / Decision / Constraint mismatch, checkpoint** and move
|
|
92
96
|
straight on to the next planned step. Repeat. `--auto` adds no new machinery — the
|
|
@@ -94,7 +98,7 @@ and approves in the human's place**, and it **chains**:
|
|
|
94
98
|
- **Stop and hand back to the human** the moment any of these is true: the check is red,
|
|
95
99
|
the self-review finds a mismatch (surface exactly what, and do not checkpoint it), a
|
|
96
100
|
bound agent returns `blocked` or `drift` (unblock-and-re-run, or `/pb-refine` — an
|
|
97
|
-
agent cannot advance the loop
|
|
101
|
+
agent cannot advance the loop), a new decision is needed, no planned steps
|
|
98
102
|
remain, or the top of a requested range is reached.
|
|
99
103
|
|
|
100
104
|
`--auto` and a step range are the only paths that checkpoint without a human pause, and
|
|
@@ -126,14 +130,14 @@ just the one more entry already in the halt list above.
|
|
|
126
130
|
## The hard contracts
|
|
127
131
|
|
|
128
132
|
- **Swappable, never required.** The loop works without this skill; `/pb-verify`
|
|
129
|
-
checkpoints a hand-built or vibed diff just the same
|
|
133
|
+
checkpoints a hand-built or vibed diff just the same.
|
|
130
134
|
- **Build the decided step, not a new one.** Implement what `intent.md` settled. A
|
|
131
135
|
new idea mid-build is a `/pb-park`, not an edit.
|
|
132
136
|
- **Default ends at the pause.** Implement → verify → wait for approval; never
|
|
133
137
|
checkpoint without it. Only an explicit `--auto` or a step range lets the agent approve
|
|
134
138
|
in your place, and it still halts on a red check or any mismatch — a range also stops
|
|
135
139
|
at its top.
|
|
136
|
-
- **Agents feed the beat; they never advance it
|
|
140
|
+
- **Agents feed the beat; they never advance it**. `before` loads context,
|
|
137
141
|
`build` writes the diff, `after` is advisory — none can checkpoint, flip a step, or
|
|
138
142
|
chain. `blocked` → unblock and re-run; `drift` → `/pb-refine`. You are still the one
|
|
139
143
|
who verifies and (bar `--auto`) the human is still the clock.
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
name: pb-finish
|
|
3
3
|
description: Finish the build — write the report (what shipped, decisions, parked/harvested items, deferred tangents), then make the final commit that closes the session. The build folder rides the branch into the PR — no separate archive. Report by default, no gate.
|
|
4
4
|
disable-model-invocation: true
|
|
5
|
-
model: opus
|
|
6
5
|
allowed-tools: Read, Write, Bash(plumbbob status:*), Bash(plumbbob finish:*)
|
|
7
6
|
---
|
|
8
7
|
|
|
@@ -11,8 +10,8 @@ allowed-tools: Read, Write, Bash(plumbbob status:*), Bash(plumbbob finish:*)
|
|
|
11
10
|
Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not on PATH in this session. Marketplace install: confirm the plugin is enabled in /plugin, then /reload-plugins. Skills-dir/global install: npm i -g plumbbob && plumbbob init."`
|
|
12
11
|
|
|
13
12
|
`/pb-finish` ends the build: it captures what happened, then makes the final commit
|
|
14
|
-
that closes the session. **Report by default**
|
|
15
|
-
and no separate docs phase. The build folder is tracked
|
|
13
|
+
that closes the session. **Report by default** — no refuse-without-report gate,
|
|
14
|
+
and no separate docs phase. The build folder is tracked: it merges with the
|
|
16
15
|
branch and shows up in the PR, so there is nothing to archive — the folder *is* the
|
|
17
16
|
record.
|
|
18
17
|
|
|
@@ -30,8 +29,8 @@ record.
|
|
|
30
29
|
- **Deferred tangents** — the harvested items that become future work.
|
|
31
30
|
This is the "yeah, I did that" artifact. Write it by default; the human may edit it.
|
|
32
31
|
2. **Finish.** Run `plumbbob finish`, which appends the checkpoint SHAs to the
|
|
33
|
-
report and makes the final commit — subject `plumbbob: finish — <title
|
|
34
|
-
with an optional proportional body via `--body` (the
|
|
32
|
+
report and makes the final commit — subject `plumbbob: finish — <title>`,
|
|
33
|
+
with an optional proportional body via `--body` (the stdin heredoc) — then
|
|
35
34
|
clears the control state (markers, the `activeBuild` cursor, STATE last). The
|
|
36
35
|
build folder stays in place, committed, and rides the branch into the PR.
|
|
37
36
|
3. **Point at the next goal** — `/pb-plan` to frame the next one.
|
|
@@ -43,7 +42,7 @@ record.
|
|
|
43
42
|
- **The log is the history; the report is the synthesis.** `checkpoint` already recorded
|
|
44
43
|
what shipped, step by step — finish applies the *unique additions* (the why, the deferred
|
|
45
44
|
tangents, the final status), it does not rewrite the timeline.
|
|
46
|
-
- **The folder is the archive, never destroy
|
|
45
|
+
- **The folder is the archive, never destroy**. `finish` keeps intent +
|
|
47
46
|
build-log + report in `builds/<slug>/` and commits them; nothing is copied out and
|
|
48
47
|
nothing is deleted but the untracked control markers.
|
|
49
48
|
- **No version bump, no docs phase.** Updating real docs is a separate, explicit ask;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
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
|
-
model: opus
|
|
6
5
|
allowed-tools: Read, Edit, Bash(plumbbob status:*)
|
|
7
6
|
---
|
|
8
7
|
|
|
@@ -10,7 +9,7 @@ allowed-tools: Read, Edit, Bash(plumbbob status:*)
|
|
|
10
9
|
|
|
11
10
|
Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not on PATH in this session. Marketplace install: confirm the plugin is enabled in /plugin, then /reload-plugins. Skills-dir/global install: npm i -g plumbbob && plumbbob init."`
|
|
12
11
|
|
|
13
|
-
`/pb-harvest` is the complement of `/pb-park
|
|
12
|
+
`/pb-harvest` is the complement of `/pb-park`: you parked ideas as seeds during a
|
|
14
13
|
build; now, at a boundary, you harvest them — decide what each one is.
|
|
15
14
|
|
|
16
15
|
## When to run it — boundary only
|
package/skills/pb-park/SKILL.md
CHANGED
|
@@ -12,7 +12,7 @@ allowed-tools: Bash(plumbbob status:*), Bash(plumbbob park:*)
|
|
|
12
12
|
Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not on PATH in this session. Marketplace install: confirm the plugin is enabled in /plugin, then /reload-plugins. Skills-dir/global install: npm i -g plumbbob && plumbbob init."`
|
|
13
13
|
|
|
14
14
|
`/pb-park` is the **capture** half of the loop; `/pb-harvest` is where parked items
|
|
15
|
-
get triaged later
|
|
15
|
+
get triaged later. Capturing the instant an idea arrives — instead of acting on
|
|
16
16
|
it — is the whole point: it protects the step in flight.
|
|
17
17
|
|
|
18
18
|
## Wrong-state refusal
|
package/skills/pb-plan/SKILL.md
CHANGED
|
@@ -3,7 +3,6 @@ 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
|
argument-hint: "[spec-path | intent]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
model: opus
|
|
7
6
|
allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob start:*), Bash(plumbbob checkpoint:*), Bash(plumbbob agent list:*)
|
|
8
7
|
---
|
|
9
8
|
|
|
@@ -17,6 +16,11 @@ of your head and onto `intent.md` *before* any code. By default it authors the
|
|
|
17
16
|
`/pb-build` until done. (Revising a single increment later is the separate `/pb-step`
|
|
18
17
|
move; do not confuse the two.)
|
|
19
18
|
|
|
19
|
+
A model note: this skill **inherits the session model** — nothing pins or switches
|
|
20
|
+
it. Planning is where frontier-class judgment pays for itself, so if the session is
|
|
21
|
+
running a small model, suggest `/model opus` (or better) before framing — the
|
|
22
|
+
human's call, never a gate.
|
|
23
|
+
|
|
20
24
|
## Three input modes (disambiguated for you — no quotes needed)
|
|
21
25
|
|
|
22
26
|
Look at the argument the human gave and pick the mode yourself:
|
|
@@ -54,13 +58,24 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
|
|
|
54
58
|
```markdown
|
|
55
59
|
1. [ ] <title> — **done when:** <criterion, ideally a test or check result>
|
|
56
60
|
- seam: `<file>`, `<file>`
|
|
61
|
+
- model: <optional — smallest that can carry it, with the one-phrase why>
|
|
57
62
|
```
|
|
58
63
|
|
|
59
64
|
Every step needs a **done-when** `/pb-verify` can check and a **seam** (the exact
|
|
60
65
|
paths it touches). Later steps may be fuzzier than the first — that's fine; they get
|
|
61
66
|
sharpened just-in-time when you reach them with `/pb-step`. Keep each small enough to
|
|
62
67
|
verify in one review pass.
|
|
63
|
-
|
|
68
|
+
|
|
69
|
+
**Recommend a model per step where the signal is clear** *(optional)*: the
|
|
70
|
+
`- model:` sub-line names the **smallest model that can carry the step**, with the
|
|
71
|
+
one-phrase why — the human buys capability only where the step needs it. E.g.
|
|
72
|
+
`model: sonnet — mechanical, fully specified by the done-when` for rote edits;
|
|
73
|
+
`model: opus — strong-assertion test authoring` where the tests do the thinking;
|
|
74
|
+
`model: fable — subtle cross-cutting design` for judgment-heavy or creative work.
|
|
75
|
+
It is advisory metadata for the human — `/pb-status` surfaces it before each build —
|
|
76
|
+
never a gate, and nothing switches models automatically. Write it plain (no
|
|
77
|
+
backticks) and omit it when any model would do.
|
|
78
|
+
5. **Offer harness bindings** *(optional)*. If the build will lean on
|
|
64
79
|
user-authored agents, author `harness.json` in the build folder (beside `intent.md`)
|
|
65
80
|
and review it at the **same plan pause**, alongside the steps — bindings are
|
|
66
81
|
plan-adjacent configuration, so they converge with the plan. It binds agents to a
|
|
@@ -76,15 +91,15 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
|
|
|
76
91
|
}
|
|
77
92
|
```
|
|
78
93
|
|
|
79
|
-
Keep it **bindings + prose only, never a conditional
|
|
94
|
+
Keep it **bindings + prose only, never a conditional**: the file says *which*
|
|
80
95
|
agent, not *when* — the host model reads each manifest's `when` prose and a step's
|
|
81
96
|
`note` and decides when to fire one mid-build. Skip the file entirely when no step
|
|
82
|
-
uses an agent — the loop runs identically without it
|
|
97
|
+
uses an agent — the loop runs identically without it. The plan commit picks it
|
|
83
98
|
up automatically (it lives in the build folder).
|
|
84
99
|
6. **Commit the plan.** Once the human approves the frame and steps, run
|
|
85
100
|
`plumbbob checkpoint --plan` to commit the scaffold on its own — subject
|
|
86
101
|
`plumbbob: plan — <title>`, only `.plumbbob/builds/<slug>/`, a `plan <sha>` line in
|
|
87
|
-
`checkpoints
|
|
102
|
+
`checkpoints`. This keeps the first step's diff clean, so history reads
|
|
88
103
|
baseline → plan → steps. Pass a proportional `--body` (the single-quoted stdin
|
|
89
104
|
heredoc) when the rationale is worth carrying; skip it for a small plan. Do this
|
|
90
105
|
only on the human's approval — the plan is their convergence.
|
|
@@ -3,7 +3,6 @@ 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
|
argument-hint: "[focus]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
model: opus
|
|
7
6
|
allowed-tools: Read, Edit, Bash(plumbbob status:*)
|
|
8
7
|
---
|
|
9
8
|
|
|
@@ -19,4 +19,8 @@ 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
|
|
22
|
+
2. If the next step's detail carries a `model:` line, point it out: it is the
|
|
23
|
+
plan's recommendation of the smallest model that can carry that step, so the human
|
|
24
|
+
can switch (e.g. `/model sonnet`) before firing `/pb-build` — or ignore it. Guidance,
|
|
25
|
+
never a gate.
|
|
26
|
+
3. If it reads `NO ACTIVE SESSION`, tell the human to `/pb-plan` to frame a goal.
|
package/skills/pb-step/SKILL.md
CHANGED
|
@@ -3,7 +3,6 @@ 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
|
argument-hint: "[what-changed]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
model: opus
|
|
7
6
|
allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob agent list:*)
|
|
8
7
|
---
|
|
9
8
|
|
|
@@ -36,17 +35,19 @@ grew — but its everyday job is to sharpen, not to invent.
|
|
|
36
35
|
2. **Propose the revision** (or the new/split step): a one-line **title**, a **done-when**
|
|
37
36
|
`/pb-verify` can validate, and a **seam** (exact paths, or a `dir/` grant). Keep it
|
|
38
37
|
small enough to verify in one review pass. Show the before/after so the human can see
|
|
39
|
-
what you changed and why.
|
|
38
|
+
what you changed and why. Re-check the optional `- model:` recommendation too:
|
|
39
|
+
a step that sharpened into rote work can drop to a smaller model; one that grew subtle
|
|
40
|
+
earns a frontier one. Advisory, plain text (no backticks), never a gate.
|
|
40
41
|
3. **Get the human's OK**, then write it into `## Steps` in the standard format —
|
|
41
42
|
`N. [ ] <title> — **done when:** <criterion>` with a `- seam:` sub-line. Revise the
|
|
42
43
|
existing step in place; only append when you are genuinely adding an increment.
|
|
43
|
-
4. **Revise the step's harness bindings if they drifted too** *(optional
|
|
44
|
+
4. **Revise the step's harness bindings if they drifted too** *(optional)*. If the
|
|
44
45
|
build carries a `harness.json` (beside `intent.md`) and the reality that moved the
|
|
45
46
|
step also changed which agents it wants, sharpen that step's slot bindings
|
|
46
47
|
(`before`/`build`/`after`) and `note` at the same time — this is the just-in-time
|
|
47
48
|
counterpart to `/pb-plan`'s plan-time binding. `plumbbob agent list` shows what's
|
|
48
49
|
resolvable. Same rule as the plan move: bindings + prose only, never a conditional
|
|
49
|
-
|
|
50
|
+
. Leave it untouched when the step's agents are still right, or when the build
|
|
50
51
|
uses none.
|
|
51
52
|
|
|
52
53
|
## The hard contracts
|
|
@@ -2,7 +2,6 @@
|
|
|
2
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
|
-
model: opus
|
|
6
5
|
allowed-tools: Read, Bash(plumbbob status:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(plumbbob agent:*), Bash(git diff:*), Bash(git status:*)
|
|
7
6
|
---
|
|
8
7
|
|
|
@@ -12,29 +11,29 @@ Current session state (injected when this skill runs): !`plumbbob status 2>/dev/
|
|
|
12
11
|
|
|
13
12
|
This is the **tick** — the one beat where the human is the clock. Whatever produced
|
|
14
13
|
the current diff — `/pb-build`, your own hands, a vibe session, another harness —
|
|
15
|
-
this skill verifies it the same way: **it reads the diff, not the author
|
|
14
|
+
this skill verifies it the same way: **it reads the diff, not the author**.
|
|
16
15
|
|
|
17
16
|
## What this skill does, in order
|
|
18
17
|
|
|
19
18
|
1. **Check.** Run `plumbbob check` (the heavy gate — checkride unless the repo
|
|
20
|
-
configures a `check` override
|
|
19
|
+
configures a `check` override). If it comes back **red**, stop here: the gate
|
|
21
20
|
names the failing slots and where each tool's raw output landed — read
|
|
22
21
|
`.check/summary.json`, then the failing slot's own file (`.check/<slot>.json` or
|
|
23
22
|
`.check/<slot>.stdout.txt`) for the actual diagnostics instead of scraping
|
|
24
23
|
scrollback. Report what failed and do **not** pause for approval — there is
|
|
25
24
|
nothing to approve yet. The human fixes it and re-invokes. (Exit 2 means the gate
|
|
26
25
|
itself broke — a harness problem to surface, not a code failure.)
|
|
27
|
-
2. **Run any bound `after`-agents** *(optional
|
|
26
|
+
2. **Run any bound `after`-agents** *(optional)*. If the build's `harness.json`
|
|
28
27
|
binds agents to this step's `after` slot, run `plumbbob agent run --step <n> --mode
|
|
29
28
|
after`. Their envelopes are **advisory input to the self-review, never a gate** —
|
|
30
29
|
`plumbbob check` already gated in step 1, and an `after`-agent that could fail a step
|
|
31
|
-
is the lock returning in autonomy's costume
|
|
32
|
-
`summary`/`body` into the review below; route a non-`done` one by its status
|
|
30
|
+
is the lock returning in autonomy's costume. Fold a `done` envelope's
|
|
31
|
+
`summary`/`body` into the review below; route a non-`done` one by its status: a
|
|
33
32
|
`blocked` agent couldn't finish — surface its `notes`, let the human unblock, re-run;
|
|
34
33
|
a `drift` agent found the plan no longer matches reality — stop and send the human to
|
|
35
34
|
`/pb-refine` to repair the plan before checkpointing. No binding, or no harness, is a
|
|
36
35
|
clean no-op.
|
|
37
|
-
3. **Self-review** *(a single structured read
|
|
36
|
+
3. **Self-review** *(a single structured read)*. Read `git diff` and
|
|
38
37
|
`.plumbbob/intent.md`, then in one pass check the diff against:
|
|
39
38
|
- the current step's **done-when** criterion — is it actually met?
|
|
40
39
|
- the **Decisions** — does anything contradict a settled call?
|
|
@@ -68,12 +67,12 @@ this skill verifies it the same way: **it reads the diff, not the author** (D3).
|
|
|
68
67
|
|
|
69
68
|
- **Never skip the pause.** Check → self-review → validate, then wait. Approval is
|
|
70
69
|
the only thing that triggers the checkpoint.
|
|
71
|
-
- **Read the diff, not the author
|
|
70
|
+
- **Read the diff, not the author**. Verify identically whether the code was
|
|
72
71
|
built by `/pb-build`, by hand, vibed, or by another harness.
|
|
73
72
|
- **Red means stop, not pause.** A failing check is not an approval decision; report
|
|
74
73
|
it and end your turn.
|
|
75
74
|
- **You review; you do not build.** If the self-review finds a problem, surface it
|
|
76
75
|
and stop — fixing is a new build beat, not part of verify.
|
|
77
|
-
- **`after`-agents advise; they never gate
|
|
76
|
+
- **`after`-agents advise; they never gate**. Their output feeds the
|
|
78
77
|
self-review — checkride gates, the human approves. `blocked` → unblock and re-run;
|
|
79
78
|
`drift` → `/pb-refine` before checkpointing. No code path makes them blocking.
|
package/templates/intent.md
CHANGED
|
@@ -47,12 +47,16 @@ holes `/pb-refine` surfaces, and as blockers fold in during BUILD.)*
|
|
|
47
47
|
*(The build plan. `/pb-plan` authors the **whole list up front** — each step a small,
|
|
48
48
|
verifiable increment with its own **done-when** and **seam** (the paths it will touch,
|
|
49
49
|
which `/pb-build` records in the build folder's `SEAM` for orientation — awareness, not a lock).
|
|
50
|
+
An optional **model** line recommends the smallest model that can carry the step —
|
|
51
|
+
mechanical work runs fine on a small model; subtle or creative work earns a frontier
|
|
52
|
+
one. Advisory for the human, never a gate; write it plain, no backticks (D62).
|
|
50
53
|
Then drive `/pb-build` until done. Later steps may be fuzzier than the first;
|
|
51
54
|
sharpen the next one just-in-time with `/pb-step` (empty input auto-syncs it), and use
|
|
52
55
|
`/pb-refine` to repair the whole plan when a blocker rewrites it.)*
|
|
53
56
|
|
|
54
57
|
1. [ ] <step> — **done when:** <criterion, ideally a test or check result>
|
|
55
58
|
- seam: `<file>`, `<file>`
|
|
59
|
+
- model: <smallest that can carry it, e.g. sonnet — mechanical, fully specified>
|
|
56
60
|
2. [ ] <step> — **done when:** <criterion>
|
|
57
61
|
- seam: `<file>`
|
|
58
62
|
|