plumbbob 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -143
- package/dist/cli.js +18 -37
- package/dist/lib/archive.js +6 -3
- package/dist/lib/git.js +0 -4
- package/dist/lib/intent.js +1 -1
- package/dist/lib/orient.js +160 -0
- package/dist/lib/settings.js +12 -8
- package/dist/lib/sidecar.js +0 -3
- package/dist/verbs/build.js +1 -1
- package/dist/verbs/check.js +17 -0
- package/dist/verbs/checkpoint.js +83 -0
- package/dist/verbs/reset.js +54 -0
- package/dist/verbs/revert.js +43 -3
- package/dist/verbs/setup.js +1 -1
- package/dist/verbs/status.js +23 -4
- package/package.json +2 -2
- package/skills/pb-build/SKILL.md +39 -9
- package/skills/pb-harvest/SKILL.md +47 -0
- package/skills/pb-park/SKILL.md +40 -0
- package/skills/pb-plan/SKILL.md +38 -0
- package/skills/pb-reset/SKILL.md +39 -0
- package/skills/pb-status/SKILL.md +22 -0
- package/skills/pb-step/SKILL.md +37 -0
- package/skills/pb-verify/SKILL.md +47 -0
- package/dist/verbs/done.js +0 -63
- package/dist/verbs/finish.js +0 -54
- package/dist/verbs/mode.js +0 -26
- package/dist/verbs/review.js +0 -24
- package/dist/verbs/wrap.js +0 -28
- package/hooks/bash-guard.sh +0 -62
- package/hooks/pre-edit.sh +0 -95
- package/skills/park/SKILL.md +0 -26
- package/skills/pb-done/SKILL.md +0 -18
- package/skills/pb-finish/SKILL.md +0 -18
- package/skills/pb-review/SKILL.md +0 -18
- package/skills/pb-start/SKILL.md +0 -19
- package/skills/pb-wrap/SKILL.md +0 -18
- package/skills/plumbbob-docs/SKILL.md +0 -25
- package/skills/plumbbob-report/SKILL.md +0 -35
- package/skills/plumbbob-triage/SKILL.md +0 -38
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// `plumbbob checkpoint [<n>] [-m <msg>]` — the executor-agnostic commit tick (D3).
|
|
2
|
+
// Unlike v1 `done`, it does NOT require BUILD state or a STEP file: the step is
|
|
3
|
+
// whatever you pass, else the in-flight STEP, else the next undone step in intent.
|
|
4
|
+
// It gates on a green check, then commits any pending work (or records the existing
|
|
5
|
+
// HEAD when the tree is already clean — the human's commit skill may have committed
|
|
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: `/pb-build`, your hands,
|
|
8
|
+
// a vibe session, or another harness all checkpoint the same way.
|
|
9
|
+
import { appendFileSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { commit, findRepoRoot, headSha, isDirty, stageAll } from "../lib/git.js";
|
|
11
|
+
import { checkpointsPath, hasSession, intentPath, seamPath, stepPath, writeState } from "../lib/sidecar.js";
|
|
12
|
+
import { runCheck } from "../lib/check.js";
|
|
13
|
+
import { markStepDone, parseSteps } from "../lib/orient.js";
|
|
14
|
+
export function checkpoint(cwd, args) {
|
|
15
|
+
const root = findRepoRoot(cwd);
|
|
16
|
+
if (root === null || !hasSession(root)) {
|
|
17
|
+
process.stderr.write('plumbbob: no active session. Run `plumbbob start "<title>"` first.\n');
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
const step = resolveStep(root, args);
|
|
21
|
+
if (step === null) {
|
|
22
|
+
process.stderr.write('plumbbob: no step to checkpoint — pass a number, or plan a step in intent.md first.\n');
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
if (runCheck(root) !== 0) {
|
|
26
|
+
process.stderr.write('plumbbob: check failed (red) — checkpoint refuses on red. Fix it and re-run.\n');
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
let sha;
|
|
30
|
+
if (isDirty(root)) {
|
|
31
|
+
stageAll(root);
|
|
32
|
+
sha = commit(root, messageArg(args) ?? `plumbbob: step ${step} done`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
sha = headSha(root);
|
|
36
|
+
}
|
|
37
|
+
appendFileSync(checkpointsPath(root), `step ${step} ${sha}\n`);
|
|
38
|
+
flipIntent(root, step);
|
|
39
|
+
rmSync(seamPath(root), { force: true });
|
|
40
|
+
rmSync(stepPath(root), { force: true });
|
|
41
|
+
writeState(root, 'DESIGN');
|
|
42
|
+
process.stdout.write(`plumbbob: step ${step} checkpointed — ${sha.slice(0, 9)}. STATE=DESIGN.\n`);
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
// Step resolution (D3): explicit arg > in-flight STEP file > first undone step in
|
|
46
|
+
// intent.md. Returns null when none can be determined.
|
|
47
|
+
function resolveStep(root, args) {
|
|
48
|
+
const explicit = args.find((a) => /^\d+$/.test(a));
|
|
49
|
+
if (explicit !== undefined) {
|
|
50
|
+
return Number(explicit);
|
|
51
|
+
}
|
|
52
|
+
const inFlight = readStep(root);
|
|
53
|
+
if (inFlight !== null) {
|
|
54
|
+
return inFlight;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return parseSteps(readFileSync(intentPath(root), 'utf8')).find((s) => !s.done)?.n ?? null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function flipIntent(root, step) {
|
|
64
|
+
try {
|
|
65
|
+
writeFileSync(intentPath(root), markStepDone(readFileSync(intentPath(root), 'utf8'), step));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// best-effort bookkeeping; the checkpoint SHA is the source of truth.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function messageArg(args) {
|
|
72
|
+
const i = args.indexOf('-m');
|
|
73
|
+
return i !== -1 && i + 1 < args.length ? (args[i + 1] ?? null) : null;
|
|
74
|
+
}
|
|
75
|
+
function readStep(root) {
|
|
76
|
+
try {
|
|
77
|
+
const raw = readFileSync(stepPath(root), 'utf8').trim();
|
|
78
|
+
return /^\d+$/.test(raw) ? Number(raw) : null;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// `plumbbob reset` (D9) — the v2 close-out, replacing the v1
|
|
2
|
+
// wrap → report → docs → finish ceremony. It archives intent + build-log + report
|
|
3
|
+
// (the `/pb-reset` skill writes the report by default) under .plumbbob/archive/,
|
|
4
|
+
// clears the active files, and deletes the control state (STATE last). Unlike v1
|
|
5
|
+
// `finish` there is NO refuse-without-report gate — guidance offers the artifact, it
|
|
6
|
+
// does not wall the exit. Archive-then-clear, never destroy (C4); git untouched (C5).
|
|
7
|
+
import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs';
|
|
8
|
+
import { join, relative } from 'node:path';
|
|
9
|
+
import { findRepoRoot } from "../lib/git.js";
|
|
10
|
+
import { buildLogPath, checkpointsPath, hasSession, intentPath, seamPath, sidecarDir, stepPath } from "../lib/sidecar.js";
|
|
11
|
+
import { archiveSession, reportPath } from "../lib/archive.js";
|
|
12
|
+
export function reset(cwd) {
|
|
13
|
+
const root = findRepoRoot(cwd);
|
|
14
|
+
if (root === null || !hasSession(root)) {
|
|
15
|
+
process.stderr.write('plumbbob: no active session. Run `plumbbob start "<title>"` first.\n');
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
if (existsSync(reportPath(root))) {
|
|
19
|
+
appendCheckpointShas(root);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
process.stderr.write('plumbbob: note — no report.md found; archiving intent + build-log without one ' +
|
|
23
|
+
'(/pb-reset normally writes the report first). No gate (D9).\n');
|
|
24
|
+
}
|
|
25
|
+
const archived = archiveSession(root);
|
|
26
|
+
// Clear the active files — now safely archived — then the control state, STATE
|
|
27
|
+
// last so "no session" flips exactly at the end.
|
|
28
|
+
rmSync(intentPath(root), { force: true });
|
|
29
|
+
rmSync(buildLogPath(root), { force: true });
|
|
30
|
+
rmSync(reportPath(root), { force: true });
|
|
31
|
+
rmSync(seamPath(root), { force: true });
|
|
32
|
+
rmSync(stepPath(root), { force: true });
|
|
33
|
+
rmSync(join(sidecarDir(root), 'STATE'), { force: true });
|
|
34
|
+
process.stdout.write(`plumbbob: reset — archived to ${relative(root, archived)}. Sidecar cleared. ` +
|
|
35
|
+
'Run `/pb-plan` (or `plumbbob start "<title>"`) to frame the next goal.\n');
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
// Append the recorded checkpoints (baseline + each `step n <sha>`) to the report so
|
|
39
|
+
// the archived report lists the SHAs.
|
|
40
|
+
function appendCheckpointShas(root) {
|
|
41
|
+
let raw = '';
|
|
42
|
+
try {
|
|
43
|
+
raw = readFileSync(checkpointsPath(root), 'utf8');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
raw = '';
|
|
47
|
+
}
|
|
48
|
+
const lines = raw
|
|
49
|
+
.split('\n')
|
|
50
|
+
.map((l) => l.trim())
|
|
51
|
+
.filter((l) => l.length > 0)
|
|
52
|
+
.map((l) => `- ${l}`);
|
|
53
|
+
appendFileSync(reportPath(root), ['', '## Checkpoints', '', ...lines, ''].join('\n'));
|
|
54
|
+
}
|
package/dist/verbs/revert.js
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
// recent step, or `--to n`, with the baseline as fallback), then remove untracked
|
|
3
3
|
// files under the SEAM only. The sidecar is git-excluded (D17), so the reset
|
|
4
4
|
// never touches it — park lines and intent edits survive the revert (C4).
|
|
5
|
-
|
|
5
|
+
//
|
|
6
|
+
// Plumbbob also installs its driver skills INTO the repo (.claude/skills/<driver>/
|
|
7
|
+
// for a self-contained install), so a blunt reset would discard an out-of-seam
|
|
8
|
+
// skill edit — or a `pnpm up plumbbob` re-setup — together with the half-done
|
|
9
|
+
// step. revert discards the step's WORK, never plumbbob's own machinery, so those
|
|
10
|
+
// paths are carried across the reset unchanged.
|
|
11
|
+
import { cpSync, existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
6
13
|
import { join } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
7
15
|
import { findRepoRoot, resetHard, untrackedPaths } from "../lib/git.js";
|
|
8
|
-
import { checkpointsPath, hasSession, seamPath, stepPath, writeState } from "../lib/sidecar.js";
|
|
16
|
+
import { checkpointsPath, hasSession, seamPath, sidecarDir, stepPath, writeState } from "../lib/sidecar.js";
|
|
9
17
|
import { matchesSeam } from "../lib/intent.js";
|
|
10
18
|
export function revert(cwd, args) {
|
|
11
19
|
const root = findRepoRoot(cwd);
|
|
@@ -39,7 +47,7 @@ export function revert(cwd, args) {
|
|
|
39
47
|
// ignored files alone, so they must be removed explicitly afterward).
|
|
40
48
|
const seam = readSeamTokens(root);
|
|
41
49
|
const toRemove = untrackedPaths(root).filter((p) => matchesSeam(p, seam));
|
|
42
|
-
|
|
50
|
+
resetPreserving(root, sha, plumbbobOwnedPaths(root));
|
|
43
51
|
for (const rel of toRemove) {
|
|
44
52
|
rmSync(join(root, rel), { force: true, recursive: true });
|
|
45
53
|
}
|
|
@@ -49,6 +57,38 @@ export function revert(cwd, args) {
|
|
|
49
57
|
process.stdout.write(`plumbbob: reverted to ${sha.slice(0, 9)} — STATE=DESIGN. Park lines and intent edits were preserved.\n`);
|
|
50
58
|
return 0;
|
|
51
59
|
}
|
|
60
|
+
// The repo paths that belong to plumbbob, not to the work being reverted: the
|
|
61
|
+
// sidecar (already git-excluded, listed so revert is robust even where `.plumbbob/`
|
|
62
|
+
// was tracked by mistake) and each installed driver skill under .claude/skills/.
|
|
63
|
+
// Skill names come from plumbbob's own bundled `skills/` dir — the same source
|
|
64
|
+
// `setup` copies from — so only plumbbob's own skills are protected, never the
|
|
65
|
+
// user's. Only paths that currently exist are returned.
|
|
66
|
+
function plumbbobOwnedPaths(root) {
|
|
67
|
+
const paths = [sidecarDir(root)];
|
|
68
|
+
try {
|
|
69
|
+
for (const name of readdirSync(fileURLToPath(new URL('../../skills', import.meta.url)))) {
|
|
70
|
+
paths.push(join(root, '.claude', 'skills', name));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Bundled skills dir not resolvable (unexpected) — protect just the sidecar.
|
|
75
|
+
}
|
|
76
|
+
return paths.filter((p) => existsSync(p));
|
|
77
|
+
}
|
|
78
|
+
// `git reset --hard <sha>` is repo-wide, so paths that must survive it are copied
|
|
79
|
+
// to a temp snapshot first, then copied back over whatever the reset produced.
|
|
80
|
+
// Restoring on top (no pre-delete) keeps the live sidecar safe if a copy throws.
|
|
81
|
+
function resetPreserving(root, sha, preserve) {
|
|
82
|
+
const snap = mkdtempSync(join(tmpdir(), 'plumbbob-revert-'));
|
|
83
|
+
try {
|
|
84
|
+
preserve.forEach((p, i) => cpSync(p, join(snap, String(i)), { recursive: true }));
|
|
85
|
+
resetHard(root, sha);
|
|
86
|
+
preserve.forEach((p, i) => cpSync(join(snap, String(i)), p, { recursive: true }));
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
rmSync(snap, { force: true, recursive: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
52
92
|
function parseTo(args) {
|
|
53
93
|
const idx = args.indexOf('--to');
|
|
54
94
|
if (idx === -1) {
|
package/dist/verbs/setup.js
CHANGED
|
@@ -24,7 +24,7 @@ import { join, sep } from 'node:path';
|
|
|
24
24
|
import { fileURLToPath } from 'node:url';
|
|
25
25
|
import { findRepoRoot } from "../lib/git.js";
|
|
26
26
|
import { mergeRegistration, readSettings, stripRegistration, writeSettings } from "../lib/settings.js";
|
|
27
|
-
const HOOK_FILES = ['
|
|
27
|
+
const HOOK_FILES = ['post-edit.sh'];
|
|
28
28
|
// The placeholder every skill carries in place of its `plumbbob` invocation;
|
|
29
29
|
// setup substitutes it with the form the chosen install shape resolves.
|
|
30
30
|
const BIN_PLACEHOLDER = '__PLUMBBOB_BIN__';
|
package/dist/verbs/status.js
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
|
-
// `plumbbob status` —
|
|
2
|
-
// always exits 0. Skills pre-inject this output to gate their own
|
|
1
|
+
// `plumbbob status` — the orientation dashboard (D8/D15), or NO ACTIVE SESSION.
|
|
2
|
+
// Read-only, always exits 0. Skills pre-inject this output to gate their own
|
|
3
|
+
// behavior, so the `NO ACTIVE SESSION` sentinel is kept exact.
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
3
5
|
import { findRepoRoot } from "../lib/git.js";
|
|
4
|
-
import { hasSession, readState } from "../lib/sidecar.js";
|
|
6
|
+
import { buildLogPath, checkpointsPath, hasSession, intentPath, readState, stepPath } from "../lib/sidecar.js";
|
|
7
|
+
import { formatOrientation, orient } from "../lib/orient.js";
|
|
8
|
+
function readOr(path) {
|
|
9
|
+
try {
|
|
10
|
+
return readFileSync(path, 'utf8');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
5
16
|
export function status(cwd) {
|
|
6
17
|
const root = findRepoRoot(cwd);
|
|
7
18
|
if (root === null || !hasSession(root)) {
|
|
8
19
|
process.stdout.write('NO ACTIVE SESSION\n');
|
|
9
20
|
return 0;
|
|
10
21
|
}
|
|
11
|
-
|
|
22
|
+
const inFlightRaw = readOr(stepPath(root)).trim();
|
|
23
|
+
const orientation = orient({
|
|
24
|
+
state: readState(root) ?? 'UNKNOWN',
|
|
25
|
+
intent: readOr(intentPath(root)),
|
|
26
|
+
buildLog: readOr(buildLogPath(root)),
|
|
27
|
+
checkpoints: readOr(checkpointsPath(root)),
|
|
28
|
+
inFlight: /^\d+$/.test(inFlightRaw) ? Number(inFlightRaw) : null,
|
|
29
|
+
});
|
|
30
|
+
process.stdout.write(`${formatOrientation(orientation)}\n`);
|
|
12
31
|
return 0;
|
|
13
32
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Attention-first build process:
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Attention-first build process: eight skills + a CLI that keep you the decider — guidance, not enforcement.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
7
7
|
"claude",
|
package/skills/pb-build/SKILL.md
CHANGED
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pb-build
|
|
3
|
-
description:
|
|
3
|
+
description: The optional engine — read the current planned step from intent, implement it (its done-when, seam, Decisions, Constraints), then verify it through to the approval pause. Skip it entirely to implement by hand, vibed, or with another harness.
|
|
4
4
|
disable-model-invocation: true
|
|
5
|
-
model:
|
|
6
|
-
allowed-tools: Bash(__PLUMBBOB_BIN__ status:*), Bash(__PLUMBBOB_BIN__ build:*)
|
|
5
|
+
model: opus
|
|
6
|
+
allowed-tools: Read, Edit, Write, Bash(__PLUMBBOB_BIN__ status:*), Bash(__PLUMBBOB_BIN__ build:*), Bash(__PLUMBBOB_BIN__ check:*), Bash(__PLUMBBOB_BIN__ checkpoint:*), Bash(git diff:*)
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# Plumbbob — build a step (
|
|
9
|
+
# Plumbbob — build a step (the optional engine)
|
|
10
10
|
|
|
11
11
|
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
12
|
|
|
13
|
-
This is
|
|
13
|
+
This is the **bundled executor** — one way to turn a planned step into code. It is
|
|
14
|
+
**optional** (D3): you can implement any step by hand, in a vibe session, or with
|
|
15
|
+
another harness and go straight to `/pb-verify` instead — plumbbob does not care how
|
|
16
|
+
the diff appeared. When you do run it, it reads the plan, writes the step, and
|
|
17
|
+
carries straight through to the verify pause.
|
|
14
18
|
|
|
15
|
-
## What
|
|
19
|
+
## What this skill does, in order
|
|
16
20
|
|
|
17
|
-
1.
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
1. **Pick the step.** Use the number you were invoked with (e.g. `/pb-build 4`), else
|
|
22
|
+
the next undone, planned step in `.plumbbob/intent.md`. If there is no planned step
|
|
23
|
+
to build, stop and tell the human to `/pb-step` first.
|
|
24
|
+
2. **Enter the step.** Run `__PLUMBBOB_BIN__ build <n>` (records the in-flight STEP +
|
|
25
|
+
SEAM so `/pb-status` shows the step in flight; in v2 the seam is awareness, not a
|
|
26
|
+
lock).
|
|
27
|
+
3. **Read the plan.** Read the step's **done-when**, its **seam**, and the
|
|
28
|
+
**Decisions** and **Constraints** in `intent.md`. Build to *that* — the deciding
|
|
29
|
+
already happened, off the chat.
|
|
30
|
+
4. **Implement** the step, and only that step, staying within the declared seam. A
|
|
31
|
+
new problem or "ooh what if" that surfaces mid-build is a `/pb-park`, **not** an
|
|
32
|
+
edit — capture it and stay on the step. If you genuinely cannot finish without
|
|
33
|
+
touching more than the seam, that is scope drift: surface it to the human rather
|
|
34
|
+
than sprawling.
|
|
35
|
+
5. **Verify, through to the pause.** Run the verify tick exactly as `/pb-verify`
|
|
36
|
+
does: `__PLUMBBOB_BIN__ check` → self-review the diff against the done-when, the
|
|
37
|
+
Decisions, and the Constraints (a single structured read, D16) → validate → **PAUSE
|
|
38
|
+
for the human's approval** → only on approval, checkpoint with
|
|
39
|
+
`__PLUMBBOB_BIN__ checkpoint`. Do **not** bump the version or changelog — that is
|
|
40
|
+
the human's `/version` call.
|
|
41
|
+
|
|
42
|
+
## The hard contracts
|
|
43
|
+
|
|
44
|
+
- **Optional, never required.** The loop works without this skill; `/pb-verify`
|
|
45
|
+
checkpoints a hand-built or vibed diff just the same (D3).
|
|
46
|
+
- **Build the decided step, not a new one.** Implement what `intent.md` settled. A
|
|
47
|
+
new idea mid-build is a `/pb-park`, not an edit.
|
|
48
|
+
- **Always end at the pause.** Implement → verify → wait for approval. Never
|
|
49
|
+
checkpoint without it; the human is the clock.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-harvest
|
|
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
|
+
disable-model-invocation: true
|
|
5
|
+
model: opus
|
|
6
|
+
allowed-tools: Read, Edit, Bash(__PLUMBBOB_BIN__ status:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — harvest the park list
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
`/pb-harvest` is the complement of `/pb-park` (D7): you parked ideas as seeds during a
|
|
14
|
+
build; now, at a boundary, you harvest them — decide what each one is.
|
|
15
|
+
|
|
16
|
+
## When to run it — wrong-state refusal
|
|
17
|
+
|
|
18
|
+
Harvest at a **step boundary**: after a step is checkpointed and you are back in
|
|
19
|
+
DESIGN, not mid-step. Read the state injected above:
|
|
20
|
+
|
|
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 `/pb-verify`
|
|
23
|
+
before harvesting. Chasing parked items mid-step is the disease parking prevents.
|
|
24
|
+
- `DESIGN` (and other settled states) — go ahead.
|
|
25
|
+
|
|
26
|
+
## What this skill does
|
|
27
|
+
|
|
28
|
+
Walk the **Park list** in `build-log.md` item by item and, for each, **propose exactly
|
|
29
|
+
one class**:
|
|
30
|
+
|
|
31
|
+
- **blocker** — the plan was wrong or incomplete; can't proceed. Folds into `intent.md`
|
|
32
|
+
and is handled now.
|
|
33
|
+
- **tangent** — a different path, not clearly better. **The default** — defer or kill.
|
|
34
|
+
- **pivot signal** — real evidence the whole approach is wrong. Stop and replan.
|
|
35
|
+
|
|
36
|
+
## The one hard contract
|
|
37
|
+
|
|
38
|
+
You **propose**; the **human calls it**. For each item, state your proposed class and
|
|
39
|
+
one line of reasoning, then **wait for the human to confirm or override**. Write
|
|
40
|
+
**only after** per-item confirmation:
|
|
41
|
+
|
|
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 `/pb-status`
|
|
44
|
+
stops counting it as open.
|
|
45
|
+
- A confirmed **blocker** also folds its decision into `intent.md`.
|
|
46
|
+
- Never reclassify or resolve an item the human hasn't confirmed, and default every
|
|
47
|
+
uncertain item to **tangent**, never to blocker.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-park
|
|
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
|
+
disable-model-invocation: true
|
|
5
|
+
model: haiku
|
|
6
|
+
allowed-tools: Bash(__PLUMBBOB_BIN__ status:*), Bash(__PLUMBBOB_BIN__ park:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — park an idea (capture, don't chase)
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
`/pb-park` is the **capture** half of the loop; `/pb-harvest` is where parked items
|
|
14
|
+
get triaged later (D7). Capturing the instant an idea arrives — instead of acting on
|
|
15
|
+
it — is the whole point: it protects the step in flight.
|
|
16
|
+
|
|
17
|
+
## Wrong-state refusal
|
|
18
|
+
|
|
19
|
+
Parking needs an **active session** to capture into. Read the state injected above: if
|
|
20
|
+
it is `NO ACTIVE SESSION`, **refuse** and tell the human to run `plumbbob start
|
|
21
|
+
"<title>"` first. Every active state (`DESIGN`, `BUILD`, `SPIKE`, `FINISH`) is fine —
|
|
22
|
+
capture is always available, which is the whole point of parking.
|
|
23
|
+
|
|
24
|
+
## What this skill does
|
|
25
|
+
|
|
26
|
+
Take the idea, problem, or "ooh what if" the human just had and **compose it into one
|
|
27
|
+
tidy, tagged line** — short, legible, self-contained, so it still reads cleanly weeks
|
|
28
|
+
later. Then:
|
|
29
|
+
|
|
30
|
+
1. **Show the composed line to the human** and wait for **in-turn approval** — they
|
|
31
|
+
confirm it as-is or edit the wording.
|
|
32
|
+
2. **Only after** that approval, capture it by running `__PLUMBBOB_BIN__ park "<the
|
|
33
|
+
approved line>"` via Bash.
|
|
34
|
+
|
|
35
|
+
## The one hard contract
|
|
36
|
+
|
|
37
|
+
The capture itself is the **dumb CLI**, never an edit. This skill carries **no Edit and
|
|
38
|
+
no Write tool** on purpose: you may not append to `build-log.md` yourself. Compose, get
|
|
39
|
+
approval, then shell `__PLUMBBOB_BIN__ park` — that is the only write path. If approval
|
|
40
|
+
never comes, capture nothing.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-plan
|
|
3
|
+
description: Frame a fresh goal — scaffold the session and author the intent's Frame, Decisions, and Constraints before any code. Steps stay empty; they come one at a time from /pb-step.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
model: opus
|
|
6
|
+
allowed-tools: Read, Edit, Write, Bash(__PLUMBBOB_BIN__ status:*), Bash(__PLUMBBOB_BIN__ start:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — frame a goal (the whole-goal move)
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
`/pb-plan` is the **whole-goal** move — it opens a session and gets the deciding out
|
|
14
|
+
of your head and onto `intent.md` *before* any code. (Planning a single increment is
|
|
15
|
+
the separate `/pb-step` move; do not confuse the two.)
|
|
16
|
+
|
|
17
|
+
## What this skill does
|
|
18
|
+
|
|
19
|
+
1. **Scaffold.** If there is no active session, run `__PLUMBBOB_BIN__ start "<title>"`
|
|
20
|
+
to create `.plumbbob/` (STATE=DESIGN, baseline recorded). If a session already
|
|
21
|
+
exists, say so and edit the existing `intent.md` rather than starting over.
|
|
22
|
+
2. **Frame** (`.plumbbob/intent.md`), with the human: the **Problem** in plain words,
|
|
23
|
+
the **smallest thing** that solves it, what **done looks like**, and what you are
|
|
24
|
+
**explicitly NOT doing**. This is the human's convergence — propose wording, but
|
|
25
|
+
the human decides every line.
|
|
26
|
+
3. **Decisions & Constraints.** Record the settled calls (one line each, with the
|
|
27
|
+
*because*) and the hard rules the build must honor. An unresolved hole goes to
|
|
28
|
+
**Open questions**, never guessed into a Decision.
|
|
29
|
+
4. **Leave `## Steps` empty.** Steps are planned just-in-time (D6) — one at a time
|
|
30
|
+
with `/pb-step` — so do not write the build plan here.
|
|
31
|
+
|
|
32
|
+
## The hard contracts
|
|
33
|
+
|
|
34
|
+
- **Deciding before code.** `/pb-plan` writes `intent.md` only — never source.
|
|
35
|
+
- **The human converges.** You surface options and draft wording; the human picks.
|
|
36
|
+
An unresolved hole is an Open question, not a guessed Decision.
|
|
37
|
+
- **Size to the work.** A small change fills Frame + a couple of Decisions and stops;
|
|
38
|
+
ceremony on a one-liner is the failure mode, not thoroughness.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-reset
|
|
3
|
+
description: Close out the build — write the report (what shipped, decisions, parked/harvested items, deferred tangents), then archive intent + build-log + report and clear for a fresh goal. Report by default, no gate.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
model: opus
|
|
6
|
+
allowed-tools: Read, Write, Bash(__PLUMBBOB_BIN__ status:*), Bash(__PLUMBBOB_BIN__ reset:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — reset for a new goal (the close-out)
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
`/pb-reset` ends the build: it captures what happened, archives it, and clears the
|
|
14
|
+
sidecar for the next goal. **Report by default** (D9) — no refuse-without-report gate,
|
|
15
|
+
and no separate docs phase.
|
|
16
|
+
|
|
17
|
+
## What this skill does, in order
|
|
18
|
+
|
|
19
|
+
1. **Write the report** to `.plumbbob/report.md`, from `intent.md` + `build-log.md`:
|
|
20
|
+
- **What shipped** — the steps completed and what they delivered.
|
|
21
|
+
- **Decisions and why** — the settled calls that shaped the build.
|
|
22
|
+
- **Parked & harvested** — what was captured and how each was classified.
|
|
23
|
+
- **Final status** — done or partial, and what is left.
|
|
24
|
+
- **Deferred tangents** — the harvested items that become future work.
|
|
25
|
+
This is the "yeah, I did that" artifact. Write it by default; the human may edit it.
|
|
26
|
+
2. **Archive & clear.** Run `__PLUMBBOB_BIN__ reset`, which appends the checkpoint SHAs
|
|
27
|
+
to the report, archives intent + build-log + report to
|
|
28
|
+
`.plumbbob/archive/<date>-<slug>/`, and clears the sidecar (STATE last). Git is not
|
|
29
|
+
touched.
|
|
30
|
+
3. **Point at the next goal** — `/pb-plan` to frame the next one.
|
|
31
|
+
|
|
32
|
+
## The hard contracts
|
|
33
|
+
|
|
34
|
+
- **Report by default, never a gate.** Always offer the report; never wall the exit. A
|
|
35
|
+
bug fix's report can be three lines — size it to the work.
|
|
36
|
+
- **Archive-then-clear, never destroy** (C4). The archive is the record; the active
|
|
37
|
+
files only clear once they are safely copied.
|
|
38
|
+
- **No version bump, no docs phase.** Updating real docs is a separate, explicit ask;
|
|
39
|
+
cutting a release is the human's `/version`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-status
|
|
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
|
+
disable-model-invocation: true
|
|
5
|
+
model: haiku
|
|
6
|
+
allowed-tools: Bash(__PLUMBBOB_BIN__ status:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — orient (the where-am-I move)
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
This is a **thin driver** for `__PLUMBBOB_BIN__ status`. The dashboard above is your
|
|
14
|
+
orientation — the intent, the step list, the parked/open-question counts, and the
|
|
15
|
+
inferred next move. Report it verbatim and point the human at that next move. This
|
|
16
|
+
skill carries **no Edit and no Write tool**: the CLI is the source of truth, so
|
|
17
|
+
**never retry**, and never edit a file to change what the orientation says.
|
|
18
|
+
|
|
19
|
+
## What it does
|
|
20
|
+
|
|
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 `/pb-plan` to frame a goal.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-step
|
|
3
|
+
description: Plan the next increment — propose one small step with a done-when criterion and a seam, get the human's OK, and append it to intent's ## Steps. One by default; several only if the human already knows them.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
model: opus
|
|
6
|
+
allowed-tools: Read, Edit, Bash(__PLUMBBOB_BIN__ status:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — plan the next step (the single-increment move)
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
`/pb-step` plans the **next increment** just-in-time (D6) — the smallest verifiable
|
|
14
|
+
piece of the framed goal, planned only when you reach it. (Framing the whole goal is
|
|
15
|
+
the separate `/pb-plan` move.)
|
|
16
|
+
|
|
17
|
+
## What this skill does
|
|
18
|
+
|
|
19
|
+
1. **Read the plan.** Read `intent.md`'s Frame, Decisions, Constraints, and the steps
|
|
20
|
+
already done, to see what the *next* increment should be.
|
|
21
|
+
2. **Propose one step.** Draft a single step:
|
|
22
|
+
- a one-line **title**,
|
|
23
|
+
- a **done-when** criterion — ideally a test or check result, something
|
|
24
|
+
`/pb-verify` can actually validate,
|
|
25
|
+
- a **seam**: the specific files it should touch (exact paths, or a `dir/` grant).
|
|
26
|
+
Keep it small enough to verify in one review pass.
|
|
27
|
+
3. **Get the human's OK**, then **append** it to `## Steps` in the standard format —
|
|
28
|
+
`N. [ ] <title> — **done when:** <criterion>` with a `- seam:` sub-line. Default
|
|
29
|
+
to **one** step; plan several only when the human already knows them.
|
|
30
|
+
|
|
31
|
+
## The hard contracts
|
|
32
|
+
|
|
33
|
+
- **One verifiable increment.** Each step carries a done-when `/pb-verify` can check
|
|
34
|
+
and a seam small enough to review in one pass.
|
|
35
|
+
- **Append to `## Steps` only**, in the standard format `status` and `build` parse —
|
|
36
|
+
never the Roadmap, never loose prose.
|
|
37
|
+
- **The human approves the step** before it lands. You propose; they decide.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pb-verify
|
|
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
|
+
disable-model-invocation: true
|
|
5
|
+
model: opus
|
|
6
|
+
allowed-tools: Read, Bash(__PLUMBBOB_BIN__ status:*), Bash(__PLUMBBOB_BIN__ check:*), Bash(__PLUMBBOB_BIN__ checkpoint:*), Bash(git diff:*), Bash(git status:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plumbbob — verify a step (the tick)
|
|
10
|
+
|
|
11
|
+
Current session state (injected when this skill runs): !`__PLUMBBOB_BIN__ status`
|
|
12
|
+
|
|
13
|
+
This is the **tick** — the one beat where the human is the clock. Whatever produced
|
|
14
|
+
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** (D3).
|
|
16
|
+
|
|
17
|
+
## What this skill does, in order
|
|
18
|
+
|
|
19
|
+
1. **Check.** Run `__PLUMBBOB_BIN__ check` (the heavy gate). If it comes back
|
|
20
|
+
**red**, stop here: report what failed and do **not** pause for approval — there
|
|
21
|
+
is nothing to approve yet. The human fixes it and re-invokes.
|
|
22
|
+
2. **Self-review** *(a single structured read, D16)*. Read `git diff` and
|
|
23
|
+
`.plumbbob/intent.md`, then in one pass check the diff against:
|
|
24
|
+
- the current step's **done-when** criterion — is it actually met?
|
|
25
|
+
- the **Decisions** — does anything contradict a settled call?
|
|
26
|
+
- the **Constraints** — are any violated?
|
|
27
|
+
Surface every mismatch plainly. You are reviewing, not building — do not fix anything.
|
|
28
|
+
3. **Validate.** State, yes or no, whether the step's done-when is met, with the evidence.
|
|
29
|
+
4. **PAUSE.** Present the check result, the self-review, and the validation, then
|
|
30
|
+
**stop and wait for the human's explicit approval.** This is the convergence beat;
|
|
31
|
+
the human is the clock. Never checkpoint without it.
|
|
32
|
+
5. **Checkpoint** *(only after approval)*. Commit the work — the human's
|
|
33
|
+
commit-with-TIL skill for a rich message, or let `checkpoint` make the WIP commit
|
|
34
|
+
— then run `__PLUMBBOB_BIN__ checkpoint` to record the SHA, flip the step to done,
|
|
35
|
+
and return to DESIGN. Do **not** bump the version or touch the changelog — that is
|
|
36
|
+
the human's `/version` call.
|
|
37
|
+
|
|
38
|
+
## The hard contracts
|
|
39
|
+
|
|
40
|
+
- **Never skip the pause.** Check → self-review → validate, then wait. Approval is
|
|
41
|
+
the only thing that triggers the checkpoint.
|
|
42
|
+
- **Read the diff, not the author** (D3). Verify identically whether the code was
|
|
43
|
+
built by `/pb-build`, by hand, vibed, or by another harness.
|
|
44
|
+
- **Red means stop, not pause.** A failing check is not an approval decision; report
|
|
45
|
+
it and end your turn.
|
|
46
|
+
- **You review; you do not build.** If the self-review finds a problem, surface it
|
|
47
|
+
and stop — fixing is a new build beat, not part of verify.
|