plumbbob 0.8.17 → 0.9.1

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