plumbbob 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +6 -5
- package/dist/cli-core.js +10 -0
- package/dist/lib/buildlog.js +10 -0
- package/dist/lib/sidecar.js +16 -0
- package/dist/verbs/abandon.js +122 -0
- package/dist/verbs/doctor.js +9 -4
- package/dist/verbs/recover.js +1 -1
- package/dist/verbs/spike.js +4 -4
- package/package.json +4 -3
- package/skills/abandon/SKILL.md +20 -0
- package/skills/recover/SKILL.md +1 -1
- package/skills/revert/SKILL.md +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
3
|
"displayName": "PlumbBob",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.1",
|
|
5
5
|
"description": "Attention-first build process — driver skills + a CLI that keep you the decider: guidance on the work, a latch on the record.",
|
|
6
6
|
"author": { "name": "Rob McLarty", "email": "hello@robmclarty.com", "url": "https://robmclarty.com" },
|
|
7
7
|
"homepage": "https://github.com/robmclarty/plumbbob#readme",
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="https://robmclarty.github.io/plumbbob/api.html">API reference</a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
PlumbBob is a Claude Code plugin (
|
|
13
|
+
PlumbBob is a Claude Code plugin (fourteen skills and a small CLI) that runs
|
|
14
14
|
LLM-assisted coding as a loop you control. You and the model settle the plan in a file
|
|
15
15
|
*before* any code; then the model builds one small step at a time and **stops after
|
|
16
16
|
each one**, waiting for you to read the diff and approve it before anything is
|
|
@@ -145,7 +145,7 @@ You drive the whole loop from your IDE with `/plumbbob:*` skills: no step number
|
|
|
145
145
|
remember, no raw CLI to type. Every skill is `disable-model-invocation`, so *you* fire
|
|
146
146
|
every move, and `/plumbbob:status` always names your next one. (Claude Code namespaces
|
|
147
147
|
them under the plugin, so the command is `/plumbbob:plan`. The bare `/plan` reaches it
|
|
148
|
-
too, but only where nothing else owns that name, and four of the
|
|
148
|
+
too, but only where nothing else owns that name, and four of the fourteen share one with
|
|
149
149
|
a Claude Code built-in, so these docs write the full form.)
|
|
150
150
|
|
|
151
151
|
**The happy path**: the three moves every session makes; many sessions need
|
|
@@ -172,6 +172,7 @@ nothing else:
|
|
|
172
172
|
| `/plumbbob:status` | orient: where you are, the next step's done-when and seam, and the next move |
|
|
173
173
|
| `/plumbbob:verify` | the tick, standalone: check → self-review → validate → **PAUSE** → checkpoint, for a diff `/plumbbob:build` didn't write |
|
|
174
174
|
| `/plumbbob:revert` | rewind: `git reset --hard` to a recorded checkpoint |
|
|
175
|
+
| `/plumbbob:abandon` | drop the in-flight step, keep its work in the tree; the step stays planned |
|
|
175
176
|
| `/plumbbob:recover` | reconcile the session's own state when the dashboard looks wrong |
|
|
176
177
|
| `/plumbbob:doctor` | check the install from inside a session |
|
|
177
178
|
|
|
@@ -182,7 +183,7 @@ nothing else:
|
|
|
182
183
|
| `/plumbbob:park` | capture a mid-build idea without chasing it |
|
|
183
184
|
| `/plumbbob:harvest` | triage parked ideas between steps (blocker / tangent / pivot) |
|
|
184
185
|
|
|
185
|
-
All
|
|
186
|
+
All fourteen, with inputs and effects, are in
|
|
186
187
|
[`docs/skills-reference.md`](docs/skills-reference.md).
|
|
187
188
|
|
|
188
189
|
Under the skills ships a lean `plumbbob` CLI (the mechanical verbs the
|
|
@@ -240,7 +241,7 @@ modes, spec kits, native checkpoints), and that convergence is evidence *for* th
|
|
|
240
241
|
premise. PlumbBob is deliberately thin against that future: the artifacts are
|
|
241
242
|
plain markdown and ordinary git commits in your own repo, the CLI is lean
|
|
242
243
|
(node builtins plus one deliberate dependency), and nothing locks you in. The
|
|
243
|
-
investment is small (
|
|
244
|
+
investment is small (fourteen
|
|
244
245
|
skills over a plain-markdown sidecar, learnable in an afternoon), and what you're
|
|
245
246
|
actually learning is the method: decisions before code, one verified step at a
|
|
246
247
|
time, capture instead of chase. That transfers to whatever tool wins. If
|
|
@@ -289,7 +290,7 @@ Each doc answers one question (in rough reading order for a new user):
|
|
|
289
290
|
- *Show me the artifacts it leaves behind.* → [`examples/`](examples/): that same session's finished build folder, file by file.
|
|
290
291
|
- *Should I / can I / what about…?* → [`docs/faq.md`](docs/faq.md): the adoption questions, answered straight.
|
|
291
292
|
- *What is each method for?* → [`docs/techniques.md`](docs/techniques.md): steps, seams, the pause, park/harvest, spikes.
|
|
292
|
-
- *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md): all
|
|
293
|
+
- *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md): all fourteen skills: inputs, effects, when to reach for each.
|
|
293
294
|
- *How do I plug in my own agent?* → [`docs/agents.md`](docs/agents.md): the subprocess envelope, the manifest, `harness.json`, and working examples (including a local-model reviewer via Ollama).
|
|
294
295
|
- *How do I get a local model reviewing my steps?* → [`docs/local-model-review.md`](docs/local-model-review.md): the ollama-reviewer example walked end to end, install to every-pause review.
|
|
295
296
|
- *How do I install it, exactly?* → [`docs/install.md`](docs/install.md): the full guide and the agent-neutral roadmap.
|
package/dist/cli-core.js
CHANGED
|
@@ -19,6 +19,7 @@ import { handoff } from "./verbs/handoff.js";
|
|
|
19
19
|
import { check } from "./verbs/check.js";
|
|
20
20
|
import { checkpoint } from "./verbs/checkpoint.js";
|
|
21
21
|
import { revert } from "./verbs/revert.js";
|
|
22
|
+
import { abandon } from "./verbs/abandon.js";
|
|
22
23
|
import { spike } from "./verbs/spike.js";
|
|
23
24
|
import { use } from "./verbs/use.js";
|
|
24
25
|
import { finish } from "./verbs/finish.js";
|
|
@@ -113,6 +114,13 @@ const VERBS = [
|
|
|
113
114
|
],
|
|
114
115
|
notes: 'Refuses (exit 1) with no session, an invalid --to, or a step with no recorded checkpoint.',
|
|
115
116
|
},
|
|
117
|
+
{
|
|
118
|
+
name: 'abandon',
|
|
119
|
+
description: 'drop the in-flight step but keep its work in the tree',
|
|
120
|
+
synopsis: ['abandon [--build <slug>]'],
|
|
121
|
+
flags: [BUILD_FLAG],
|
|
122
|
+
notes: 'Refuses (exit 1) with no session, no step in flight, or a latched boundary (no human turn since the step began). Clears STEP/SEAM/TICK/handoff and logs the abandon; never touches the working tree, git, or the intent checkbox.',
|
|
123
|
+
},
|
|
116
124
|
{
|
|
117
125
|
name: 'park',
|
|
118
126
|
description: 'append a raw line to the park list',
|
|
@@ -331,6 +339,8 @@ async function dispatch(verb, cwd, rest) {
|
|
|
331
339
|
return checkpoint(cwd, rest);
|
|
332
340
|
case 'revert':
|
|
333
341
|
return revert(cwd, rest);
|
|
342
|
+
case 'abandon':
|
|
343
|
+
return abandon(cwd, rest);
|
|
334
344
|
case 'spike':
|
|
335
345
|
return spike(cwd, rest);
|
|
336
346
|
case 'use':
|
package/dist/lib/buildlog.js
CHANGED
|
@@ -102,3 +102,13 @@ export function checkpointLogLine(date, step, sha, title, stats = null) {
|
|
|
102
102
|
const titled = title !== null && title.length > 0 ? `${head} — ${title}` : head;
|
|
103
103
|
return stats !== null && stats.length > 0 ? `${titled} (${stats})` : titled;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* The Log line `abandon` writes for a dropped-but-kept step: the first
|
|
107
|
+
* non-checkpoint event the log records. Dated, names the step (its title when
|
|
108
|
+
* intent.md still carries it), and states plainly that the work stayed in the
|
|
109
|
+
* tree, so the narrative never has a step go quiet mid-flight.
|
|
110
|
+
*/
|
|
111
|
+
export function abandonLogLine(date, step, title) {
|
|
112
|
+
const head = `- ${date} — step ${step} abandoned · work kept in tree`;
|
|
113
|
+
return title !== null && title.length > 0 ? `${head} — ${title}` : head;
|
|
114
|
+
}
|
package/dist/lib/sidecar.js
CHANGED
|
@@ -191,12 +191,28 @@ export function spikePath(root, slug) {
|
|
|
191
191
|
export function seamPath(root, slug) {
|
|
192
192
|
return join(artifactDir(root, slug), 'SEAM');
|
|
193
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Remove the SEAM marker: `abandon` drops the in-flight step's edit grant, the
|
|
196
|
+
* same clear `checkpoint` and `revert` do inline. Homed here so a verb not
|
|
197
|
+
* sanctioned to delete on its own can still return to the boundary; absent is a
|
|
198
|
+
* no-op.
|
|
199
|
+
*/
|
|
200
|
+
export function clearSeam(root, slug) {
|
|
201
|
+
rmSync(seamPath(root, slug), { force: true });
|
|
202
|
+
}
|
|
194
203
|
/**
|
|
195
204
|
* STEP: the number of the step in flight, a bare number in a flat file.
|
|
196
205
|
*/
|
|
197
206
|
export function stepPath(root, slug) {
|
|
198
207
|
return join(artifactDir(root, slug), 'STEP');
|
|
199
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Remove the STEP marker alongside SEAM, dropping the build back to the DESIGN
|
|
211
|
+
* boundary; absent is a no-op.
|
|
212
|
+
*/
|
|
213
|
+
export function clearStep(root, slug) {
|
|
214
|
+
rmSync(stepPath(root, slug), { force: true });
|
|
215
|
+
}
|
|
200
216
|
/**
|
|
201
217
|
* The build's `checkpoints` ledger: the recorded SHAs of landed plan and step
|
|
202
218
|
* commits, one line each.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// `plumbbob abandon`: the third exit from an in-flight step. Checkpoint lands
|
|
2
|
+
// the step and revert destroys the work; abandon drops the attempt but keeps the
|
|
3
|
+
// working-tree diff exactly where it is. Its only writes are the in-flight
|
|
4
|
+
// control markers it clears (STEP, SEAM, TICK, and the step-scoped handoff
|
|
5
|
+
// ledger), a build-log line, and the stats receipt: it touches neither the
|
|
6
|
+
// working tree nor git nor the intent checkbox, so the step stays planned and
|
|
7
|
+
// re-buildable with its diff still in the tree for the human to keep, rework, or
|
|
8
|
+
// commit by hand.
|
|
9
|
+
//
|
|
10
|
+
// A step exit is a boundary crossing, so abandon honors the same approval latch
|
|
11
|
+
// as checkpoint. That is not decoration: abandon clears the entry stamp the
|
|
12
|
+
// latch reads, so an unlatched abandon followed by a same-turn checkpoint would
|
|
13
|
+
// hand that checkpoint an unlatched land, the same side door the settings-auto
|
|
14
|
+
// case closed. Every deletion routes through the sidecar helpers that own
|
|
15
|
+
// control-state clears; abandon itself never calls the raw fs deleters.
|
|
16
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { findRepoRoot } from "../lib/git.js";
|
|
18
|
+
import { bumpStepStat, buildLogPath, clearHandoff, clearSeam, clearStep, clearTick, hasSession, intentPath, resolveBuild, stepPath, } from "../lib/sidecar.js";
|
|
19
|
+
import { checkLatch } from "../lib/latch.js";
|
|
20
|
+
import { abandonLogLine, appendToSection } from "../lib/buildlog.js";
|
|
21
|
+
import { parseSteps } from "../lib/orient.js";
|
|
22
|
+
/**
|
|
23
|
+
* Drop the in-flight step while keeping its work: latch, clear the markers,
|
|
24
|
+
* record the event, and return to the boundary.
|
|
25
|
+
*
|
|
26
|
+
* Exit 1 on any refusal: no session, no step in flight, or a latched boundary
|
|
27
|
+
* (no human turn since the step began). Each refusal says what unblocks it.
|
|
28
|
+
*/
|
|
29
|
+
export function abandon(cwd, args) {
|
|
30
|
+
const root = findRepoRoot(cwd);
|
|
31
|
+
if (root === null || !hasSession(root)) {
|
|
32
|
+
process.stderr.write('plumbbob: no active session. Run `plumbbob start "<title>"` first.\n');
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
const { build: slug } = resolveBuild(root, args);
|
|
36
|
+
const step = readInFlightStep(root, slug);
|
|
37
|
+
if (step === null) {
|
|
38
|
+
process.stderr.write('plumbbob: no step in flight to abandon. abandon drops an in-flight step; from the boundary there is nothing to drop.\n');
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
// The same latch checkpoint runs, keyed on this step: a step exit is a boundary
|
|
42
|
+
// crossing, and abandon clears the entry stamp, so an unlatched abandon would
|
|
43
|
+
// hand a same-turn checkpoint an unlatched land. Refusal is the pause, not an
|
|
44
|
+
// error: exit 1 and hand the turn back.
|
|
45
|
+
const latch = checkLatch(root, step);
|
|
46
|
+
if (!latch.allow) {
|
|
47
|
+
process.stderr.write(abandonPause(latch.reason, step));
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
// Keep the work: clear only the in-flight control markers, never the tree,
|
|
51
|
+
// git, or the intent checkbox. The step stays [ ] and re-buildable.
|
|
52
|
+
clearStep(root, slug);
|
|
53
|
+
clearSeam(root, slug);
|
|
54
|
+
clearTick(root, slug);
|
|
55
|
+
clearHandoff(root, slug);
|
|
56
|
+
bumpStepStat(root, slug, step, 'abandons');
|
|
57
|
+
logAbandon(root, slug, step);
|
|
58
|
+
process.stdout.write(`plumbbob: step ${step} abandoned — work kept in the tree, back at the boundary. The step stays planned.\n`);
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The abandon-worded pause affordance.
|
|
63
|
+
*
|
|
64
|
+
* abandon runs checkpoint's latch, but the pause it hits is its own: the wording
|
|
65
|
+
* names the verb the human ran and the one self-approval that lands it, rather
|
|
66
|
+
* than sending them back to `checkpoint`. A `ceiling` reason (a range grant the
|
|
67
|
+
* step overran) keeps the range wording.
|
|
68
|
+
*/
|
|
69
|
+
function abandonPause(reason, step) {
|
|
70
|
+
if (reason === 'ceiling') {
|
|
71
|
+
return `plumbbob: abandon refused — step ${step} is past the range you granted; pause here, then run it again to continue.\n`;
|
|
72
|
+
}
|
|
73
|
+
return `plumbbob: abandon refused — no human turn since this step began. A step exit crosses the same
|
|
74
|
+
boundary as a checkpoint, so abandon honors the same pause: end the turn, and the human's approval on
|
|
75
|
+
their next turn is what lets it land. (An explicit \`/plumbbob:build --auto\` or a step range in the
|
|
76
|
+
human's own prompt is the only self-approval.)
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The in-flight STEP number from the marker, or null when none is in flight.
|
|
81
|
+
*/
|
|
82
|
+
function readInFlightStep(root, slug) {
|
|
83
|
+
try {
|
|
84
|
+
const raw = readFileSync(stepPath(root, slug), 'utf8').trim();
|
|
85
|
+
return /^\d+$/.test(raw) ? Number(raw) : null;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Append the abandon line to the build-log's `## Log`, the first non-checkpoint
|
|
93
|
+
* event the log records.
|
|
94
|
+
*
|
|
95
|
+
* Best-effort like every build-log write: a missing or odd log never fails an
|
|
96
|
+
* abandon, whose real record is the cleared markers and the stats bump.
|
|
97
|
+
*/
|
|
98
|
+
function logAbandon(root, slug, step) {
|
|
99
|
+
try {
|
|
100
|
+
const path = buildLogPath(root, slug);
|
|
101
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
102
|
+
const line = abandonLogLine(date, step, titleForStep(root, slug, step));
|
|
103
|
+
const updated = appendToSection(readFileSync(path, 'utf8'), 'Log', line);
|
|
104
|
+
if (updated !== null) {
|
|
105
|
+
writeFileSync(path, updated);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// best-effort ledger; never fail an abandon over the build-log.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The step's title from intent.md, or null when absent or unreadable.
|
|
114
|
+
*/
|
|
115
|
+
function titleForStep(root, slug, step) {
|
|
116
|
+
try {
|
|
117
|
+
return parseSteps(readFileSync(intentPath(root, slug), 'utf8')).find((s) => s.n === step)?.title ?? null;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
package/dist/verbs/doctor.js
CHANGED
|
@@ -252,8 +252,11 @@ function narrowExcludes(root) {
|
|
|
252
252
|
/**
|
|
253
253
|
* Perform the migration and return a human-readable list of what moved.
|
|
254
254
|
*
|
|
255
|
-
* STAGES the result but never commits: the human owns that commit.
|
|
256
|
-
*
|
|
255
|
+
* STAGES the result but never commits: the human owns that commit. In a repo
|
|
256
|
+
* that gitignores the sidecar the stage is skipped (stagePath's record-only
|
|
257
|
+
* guard) and the report says so: the files move but stay untracked. Call only
|
|
258
|
+
* when `inspectLegacy` reported a legacy layout; doctor prints the returned
|
|
259
|
+
* actions.
|
|
257
260
|
*/
|
|
258
261
|
export function migrateSidecar(root) {
|
|
259
262
|
const dir = sidecarDir(root);
|
|
@@ -297,8 +300,10 @@ export function migrateSidecar(root) {
|
|
|
297
300
|
}
|
|
298
301
|
rmSync(archiveDir, { recursive: true, force: true });
|
|
299
302
|
narrowExcludes(root);
|
|
300
|
-
stagePath(root, '.plumbbob');
|
|
301
|
-
actions.push(
|
|
303
|
+
const staged = stagePath(root, '.plumbbob');
|
|
304
|
+
actions.push(staged
|
|
305
|
+
? 'staged the move (builds/ + settings.json) — commit it yourself'
|
|
306
|
+
: 'moved but not staged (the sidecar is gitignored) — the files stay untracked');
|
|
302
307
|
return actions;
|
|
303
308
|
}
|
|
304
309
|
/**
|
package/dist/verbs/recover.js
CHANGED
|
@@ -159,7 +159,7 @@ function phaseFindings(root, slug, stepInFlight) {
|
|
|
159
159
|
name: 'step marker',
|
|
160
160
|
ok: false,
|
|
161
161
|
detail: `step ${marker} is in flight but the plan has no step ${marker} (planned: ${planned.join(', ')})`,
|
|
162
|
-
hint: 'the plan was rewritten under the step — sharpen it with /plumbbob:step, or plumbbob
|
|
162
|
+
hint: 'the plan was rewritten under the step — sharpen it with /plumbbob:step, plumbbob revert to drop the step and its work, or plumbbob abandon to drop the step but keep the work',
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
}
|
package/dist/verbs/spike.js
CHANGED
|
@@ -104,9 +104,9 @@ function readInFlightStep(root, buildSlug) {
|
|
|
104
104
|
* Open a spike: one throwaway worktree + `spike/<slug>-<opt>` branch per option.
|
|
105
105
|
*
|
|
106
106
|
* Refuses when a spike is already open or a step is in flight: a spike is a
|
|
107
|
-
* deliberate fork from a settled boundary, so the current step must
|
|
108
|
-
* or revert
|
|
109
|
-
* returning.
|
|
107
|
+
* deliberate fork from a settled boundary, so the current step must exit first
|
|
108
|
+
* (checkpoint, abandon, or revert). Marks the SPIKE control file and scaffolds
|
|
109
|
+
* the report before returning.
|
|
110
110
|
*/
|
|
111
111
|
function spikeStart(root, buildSlug, positionals) {
|
|
112
112
|
if (inSpike(root, buildSlug)) {
|
|
@@ -115,7 +115,7 @@ function spikeStart(root, buildSlug, positionals) {
|
|
|
115
115
|
}
|
|
116
116
|
if (existsSync(stepPath(root, buildSlug))) {
|
|
117
117
|
process.stderr.write('plumbbob: spike starts from a settled boundary, but a step is in flight. ' +
|
|
118
|
-
'A spike is a deliberate fork — checkpoint or revert the current step first.\n');
|
|
118
|
+
'A spike is a deliberate fork — checkpoint, abandon, or revert the current step first.\n');
|
|
119
119
|
return 1;
|
|
120
120
|
}
|
|
121
121
|
const slug = sanitize(positionals[0] ?? '');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"description": "Attention-first build process:
|
|
3
|
+
"version": "0.10.1",
|
|
4
|
+
"description": "Attention-first build process: 14 skills + a CLI that keep you the decider — guidance on the work, a latch on the record.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
7
7
|
"claude",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"test": "vitest run",
|
|
43
43
|
"eval:baseline": "pnpm build && node test/evals/run.ts --sweep baseline",
|
|
44
44
|
"eval:latched": "pnpm build && node test/evals/run.ts --sweep latched",
|
|
45
|
-
"eval:report": "node test/evals/run.ts --report"
|
|
45
|
+
"eval:report": "node test/evals/run.ts --report",
|
|
46
|
+
"size:prose": "node scripts/count-prose.ts"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@ast-grep/cli": "0.45.1",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: abandon
|
|
3
|
+
description: "Human-triggered driver for `plumbbob abandon`: drop the in-flight step while keeping its work in the tree, and return to the boundary. The step stays planned."
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
model: haiku
|
|
6
|
+
allowed-tools: Bash(plumbbob status:*), Bash(plumbbob abandon:*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# PlumbBob: abandon the in-flight step (driver)
|
|
10
|
+
|
|
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."`
|
|
12
|
+
|
|
13
|
+
This is a **driver skill**: a chat-side trigger for the mechanical `plumbbob abandon` verb, so the whole workflow runs from the agent window instead of a terminal. It is `disable-model-invocation: true`: only the human fires it. It carries **no Edit and no Write tool**; its only action is to shell the verb and report the verb's output verbatim, including any refusal. The CLI is the source of truth: never retry a refused transition, and never edit a file to work around one.
|
|
14
|
+
|
|
15
|
+
Abandon is the third exit from an in-flight step. `/plumbbob:verify` lands the step and `/plumbbob:revert` destroys its work; abandon drops the attempt and keeps the working-tree diff exactly where it is. The step keeps its `[ ]` in the plan, re-buildable later, and the diff stays yours to keep, rework, or commit by hand.
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
1. Run `plumbbob abandon` via Bash. It clears the in-flight markers (`STEP`, `SEAM`, `TICK`, and the step's agent handoff ledger), appends an abandon line to the build log, and touches neither the working tree nor git.
|
|
20
|
+
2. Report the verb's output verbatim: the step it dropped, or any refusal. A step exit crosses the same boundary as a checkpoint, so abandon honors the same approval latch; a "no human turn since this step began" refusal is the pause working, not an error. End the turn, and the human's next turn is what lets a re-fired abandon land.
|
package/skills/recover/SKILL.md
CHANGED
|
@@ -25,4 +25,4 @@ Reach for it when the dashboard and reality disagree: `status` shows a build you
|
|
|
25
25
|
|
|
26
26
|
- **Spike leftovers are reported, never removed.** Those worktrees sit outside the repo and may hold the only copy of what a spike learned, so the verb prints the exact `git worktree remove` / `git branch -D` commands and stops. Relay them; do not run them yourself.
|
|
27
27
|
- **A contradictory phase is yours to settle.** A spike and a step both marked in flight, or a step number the plan no longer contains, need a decision about which one is real; `--fix` deliberately leaves them alone.
|
|
28
|
-
- **It is not a rewind.** Recovering reconciles bookkeeping; it never restores lost work. Discarding a half-done step is `/plumbbob:revert
|
|
28
|
+
- **It is not a rewind, and not an exit.** Recovering reconciles bookkeeping; it never restores lost work and never drops a step. Discarding a half-done step is `/plumbbob:revert` (destructive), and dropping one while keeping its work in the tree is `/plumbbob:abandon`; both are different moves the human asks for by name.
|
package/skills/revert/SKILL.md
CHANGED
|
@@ -16,5 +16,5 @@ This is a **driver skill**: a chat-side trigger for the mechanical `plumbbob rev
|
|
|
16
16
|
## What it does
|
|
17
17
|
|
|
18
18
|
1. Read an optional target step from the way you were invoked (for example `/plumbbob:revert --to 2` → step `2`). With no target, revert goes to the last done-checkpoint.
|
|
19
|
-
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
|
+
2. Run `plumbbob revert` (or `plumbbob revert --to <n>`) via Bash. This is a `git reset --hard`; it discards the current in-progress step *and its work*. If the human wants to drop the step but keep the working-tree diff, that is `/plumbbob:abandon`, not revert. Run it exactly as the human asked; do not add or drop the `--to` on your own.
|
|
20
20
|
3. Report the verb's output verbatim: which checkpoint it reset to, or any refusal.
|