plumbbob 0.9.3 → 0.10.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/.claude-plugin/plugin.json +1 -1
- package/README.md +92 -89
- package/dist/cli-core.js +25 -8
- package/dist/cli.js +1 -1
- package/dist/lib/agents.js +26 -26
- package/dist/lib/buildlog.js +11 -11
- package/dist/lib/buildlogsync.js +8 -8
- package/dist/lib/check.js +55 -22
- package/dist/lib/commitbody.js +72 -0
- package/dist/lib/commitmsg.js +7 -7
- package/dist/lib/git.js +44 -15
- package/dist/lib/intent.js +21 -20
- package/dist/lib/latch.js +19 -19
- package/dist/lib/orient.js +36 -17
- package/dist/lib/reentry.js +70 -0
- package/dist/lib/settings.js +16 -16
- package/dist/lib/sidecar.js +54 -54
- package/dist/lib/templates.js +1 -1
- package/dist/verbs/agent.js +27 -27
- package/dist/verbs/build.js +9 -9
- package/dist/verbs/check.js +6 -6
- package/dist/verbs/checkpoint.js +48 -47
- package/dist/verbs/doctor.js +31 -30
- package/dist/verbs/finish.js +28 -41
- package/dist/verbs/handoff.js +9 -8
- package/dist/verbs/init.js +9 -9
- package/dist/verbs/park.js +3 -3
- package/dist/verbs/recover.js +297 -0
- package/dist/verbs/revert.js +13 -13
- package/dist/verbs/spike.js +25 -19
- package/dist/verbs/start.js +49 -19
- package/dist/verbs/status.js +8 -8
- package/dist/verbs/turn.js +16 -16
- package/dist/verbs/use.js +4 -4
- package/hooks/post-edit.sh +4 -4
- package/hooks/pre-bash-commit.sh +3 -3
- package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
- package/node_modules/checkride/CHANGELOG.md +1404 -0
- package/node_modules/checkride/LICENSE +201 -21
- package/node_modules/checkride/README.md +197 -38
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
- package/node_modules/checkride/dist/adapters.d.ts +30 -10
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +62 -23
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
- package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
- package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.js +77 -0
- package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts +69 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js +247 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.js +80 -0
- package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.js +93 -0
- package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.js +9 -4
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts +81 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js +604 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
- package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.js +45 -0
- package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.js +56 -0
- package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
- package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.js +19 -0
- package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.js +47 -0
- package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.js +91 -0
- package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.js +108 -0
- package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +16 -1
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
- package/node_modules/checkride/dist/atomic.js +25 -3
- package/node_modules/checkride/dist/atomic.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
- package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.js +163 -60
- package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +20 -11
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +73 -23
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
- package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/history.js +232 -0
- package/node_modules/checkride/dist/baseline/history.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +7 -5
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +5 -4
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +41 -10
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +65 -24
- package/node_modules/checkride/dist/baseline/store.js.map +1 -1
- package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
- package/node_modules/checkride/dist/baseline-command.js +7 -7
- package/node_modules/checkride/dist/baseline-command.js.map +1 -1
- package/node_modules/checkride/dist/cli.d.ts +21 -4
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +350 -36
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +114 -9
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +142 -23
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
- package/node_modules/checkride/dist/digest/digest.js +6 -6
- package/node_modules/checkride/dist/digest/index.d.ts +2 -2
- package/node_modules/checkride/dist/digest/index.js +2 -2
- package/node_modules/checkride/dist/doctor.d.ts +27 -6
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +231 -27
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +180 -0
- package/node_modules/checkride/dist/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/gate.js +617 -0
- package/node_modules/checkride/dist/gate.js.map +1 -0
- package/node_modules/checkride/dist/init.d.ts +79 -12
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +385 -81
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/json.d.ts +21 -0
- package/node_modules/checkride/dist/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/json.js +21 -0
- package/node_modules/checkride/dist/json.js.map +1 -0
- package/node_modules/checkride/dist/links.d.ts +31 -5
- package/node_modules/checkride/dist/links.d.ts.map +1 -1
- package/node_modules/checkride/dist/links.js +141 -32
- package/node_modules/checkride/dist/links.js.map +1 -1
- package/node_modules/checkride/dist/node-pin.d.ts +129 -0
- package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
- package/node_modules/checkride/dist/node-pin.js +211 -0
- package/node_modules/checkride/dist/node-pin.js.map +1 -0
- package/node_modules/checkride/dist/orchestrator.d.ts +53 -10
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +312 -113
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pack.d.ts +8 -9
- package/node_modules/checkride/dist/pack.d.ts.map +1 -1
- package/node_modules/checkride/dist/pack.js +53 -17
- package/node_modules/checkride/dist/pack.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/detect.js +6 -1
- package/node_modules/checkride/dist/pm/detect.js.map +1 -1
- package/node_modules/checkride/dist/pm/index.d.ts +7 -2
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/index.js +6 -2
- package/node_modules/checkride/dist/pm/index.js.map +1 -1
- package/node_modules/checkride/dist/pm/launch.d.ts +72 -0
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/launch.js +108 -0
- package/node_modules/checkride/dist/pm/launch.js.map +1 -0
- package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
- package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/tools.js +124 -0
- package/node_modules/checkride/dist/pm/tools.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/translate.js +78 -18
- package/node_modules/checkride/dist/pm/translate.js.map +1 -1
- package/node_modules/checkride/dist/proc.d.ts +30 -0
- package/node_modules/checkride/dist/proc.d.ts.map +1 -0
- package/node_modules/checkride/dist/proc.js +42 -0
- package/node_modules/checkride/dist/proc.js.map +1 -0
- package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
- package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/cli.js +24 -0
- package/node_modules/checkride/dist/qa/cli.js.map +1 -0
- package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
- package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dead.js +88 -0
- package/node_modules/checkride/dist/qa/dead.js.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.js +62 -0
- package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
- package/node_modules/checkride/dist/qa/health.d.ts +85 -0
- package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/health.js +109 -0
- package/node_modules/checkride/dist/qa/health.js.map +1 -0
- package/node_modules/checkride/dist/qa/index.d.ts +23 -0
- package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/index.js +22 -0
- package/node_modules/checkride/dist/qa/index.js.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.js +195 -0
- package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
- package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
- package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/qa.js +158 -0
- package/node_modules/checkride/dist/qa/qa.js.map +1 -0
- package/node_modules/checkride/dist/qa/read.d.ts +40 -0
- package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/read.js +47 -0
- package/node_modules/checkride/dist/qa/read.js.map +1 -0
- package/node_modules/checkride/dist/qa/render.d.ts +26 -0
- package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/render.js +310 -0
- package/node_modules/checkride/dist/qa/render.js.map +1 -0
- package/node_modules/checkride/dist/recover.d.ts +48 -0
- package/node_modules/checkride/dist/recover.d.ts.map +1 -0
- package/node_modules/checkride/dist/recover.js +220 -0
- package/node_modules/checkride/dist/recover.js.map +1 -0
- package/node_modules/checkride/dist/security.d.ts +45 -0
- package/node_modules/checkride/dist/security.d.ts.map +1 -0
- package/node_modules/checkride/dist/security.js +86 -0
- package/node_modules/checkride/dist/security.js.map +1 -0
- package/node_modules/checkride/dist/smoke.d.ts +6 -6
- package/node_modules/checkride/dist/smoke.js +6 -6
- package/node_modules/checkride/dist/snippets.d.ts +27 -30
- package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
- package/node_modules/checkride/dist/snippets.js +26 -30
- package/node_modules/checkride/dist/snippets.js.map +1 -1
- package/node_modules/checkride/dist/tool-json.d.ts +33 -0
- package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
- package/node_modules/checkride/dist/tool-json.js +58 -0
- package/node_modules/checkride/dist/tool-json.js.map +1 -0
- package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
- package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/cli.js +26 -0
- package/node_modules/checkride/dist/triage/cli.js.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
- package/node_modules/checkride/dist/triage/env.d.ts +54 -0
- package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/env.js +81 -0
- package/node_modules/checkride/dist/triage/env.js.map +1 -0
- package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
- package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/gate.js +121 -0
- package/node_modules/checkride/dist/triage/gate.js.map +1 -0
- package/node_modules/checkride/dist/triage/index.d.ts +22 -0
- package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/index.js +20 -0
- package/node_modules/checkride/dist/triage/index.js.map +1 -0
- package/node_modules/checkride/dist/triage/render.d.ts +19 -0
- package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/render.js +419 -0
- package/node_modules/checkride/dist/triage/render.js.map +1 -0
- package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
- package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/triage.js +131 -0
- package/node_modules/checkride/dist/triage/triage.js.map +1 -0
- package/node_modules/checkride/package.json +18 -15
- package/node_modules/checkride/schema/checkride.config.schema.json +70 -5
- package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
- package/node_modules/checkride/skills/check/SKILL.md +265 -0
- package/node_modules/checkride/skills/qa/SKILL.md +231 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
- package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
- package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
- package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
- package/node_modules/checkride/templates/shared/vale.ini +34 -0
- package/package.json +10 -9
- package/skills/build/SKILL.md +85 -76
- package/skills/doctor/SKILL.md +11 -11
- package/skills/finish/SKILL.md +21 -18
- package/skills/harvest/SKILL.md +13 -11
- package/skills/park/SKILL.md +10 -10
- package/skills/plan/SKILL.md +69 -59
- package/skills/recover/SKILL.md +28 -0
- package/skills/refine/SKILL.md +21 -18
- package/skills/revert/SKILL.md +6 -6
- package/skills/spike/SKILL.md +9 -9
- package/skills/status/SKILL.md +5 -5
- package/skills/step/SKILL.md +20 -15
- package/skills/verify/SKILL.md +56 -46
- package/templates/build-log.md +7 -7
- package/templates/intent.md +52 -41
- package/templates/spike-report.md +10 -10
package/dist/verbs/check.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
// `plumbbob check
|
|
1
|
+
// `plumbbob check`: run the heavy gate and report, with NO state change. This
|
|
2
2
|
// is the read-only half of the verify tick: `/plumbbob:verify` runs it before the
|
|
3
3
|
// pause so the human approves on a known-green check. The gate itself lives in
|
|
4
4
|
// lib/check.ts: checkride (our sibling check-runner package) by default, or a
|
|
5
|
-
// spawn command named by the `check` setting
|
|
6
|
-
// ladder (flag → local overlay → tracked settings.json → default)
|
|
5
|
+
// spawn command named by the `check` setting (resolved through the settings
|
|
6
|
+
// ladder (flag → local overlay → tracked settings.json → default)) for repos
|
|
7
7
|
// that gate through something else. Exits with the check's own code (0 = green,
|
|
8
8
|
// 1 = red, 2 = the gate itself broke).
|
|
9
9
|
//
|
|
10
10
|
// Narrowing flags for the iteration loop (`check --bail --only types,lint`) map
|
|
11
|
-
// straight onto checkride's run flags. Only this verb takes them
|
|
11
|
+
// straight onto checkride's run flags. Only this verb takes them: the
|
|
12
12
|
// checkpoint gate is always full-fat.
|
|
13
13
|
import { findRepoRoot } from "../lib/git.js";
|
|
14
14
|
import { hasSession } from "../lib/sidecar.js";
|
|
@@ -35,7 +35,7 @@ export async function check(cwd, args = []) {
|
|
|
35
35
|
* Only the declared flags are read here; an undeclared one never reaches this
|
|
36
36
|
* function, because `run` screens argv against the verb spec in cli-core.ts and
|
|
37
37
|
* refuses before dispatch. This used to tolerate unknown args on the grounds
|
|
38
|
-
* that running the gate mattered more than flag hygiene
|
|
38
|
+
* that running the gate mattered more than flag hygiene, but the same tolerance
|
|
39
39
|
* on the mutating verbs let `checkpoint --help` commit, so the screen is now
|
|
40
40
|
* central and uniform.
|
|
41
41
|
*/
|
|
@@ -69,7 +69,7 @@ function slotList(args, flag) {
|
|
|
69
69
|
/**
|
|
70
70
|
* The one-line human verdict for a check exit code.
|
|
71
71
|
*
|
|
72
|
-
* A broken gate (exit 2) reports distinctly from red
|
|
72
|
+
* A broken gate (exit 2) reports distinctly from red: a misconfigured harness
|
|
73
73
|
* must never read as broken code, though both block a checkpoint.
|
|
74
74
|
*/
|
|
75
75
|
function verdictLine(code) {
|
package/dist/verbs/checkpoint.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
// `plumbbob checkpoint [<n>] [-m <msg>]
|
|
1
|
+
// `plumbbob checkpoint [<n>] [-m <msg>]`: the commit tick that lands a step.
|
|
2
2
|
// Executor-agnostic and author-blind: it reads the diff, not who wrote it, so a
|
|
3
3
|
// `/plumbbob:build` run, your own hands, a vibe session, or another harness all
|
|
4
4
|
// checkpoint identically. It does NOT require a STEP marker (the flat control
|
|
5
5
|
// file recording the step in flight): the step is whatever you pass, else the
|
|
6
6
|
// in-flight STEP, else the next undone step in intent.md. It evaluates the
|
|
7
7
|
// approval latch, gates on a green check, commits any pending work (or records
|
|
8
|
-
// the existing HEAD when the tree is already clean
|
|
8
|
+
// the existing HEAD when the tree is already clean: the human's commit skill
|
|
9
9
|
// may have committed first), records the SHA, flips the intent checkbox to
|
|
10
|
-
// `[x]`, and clears the STEP/SEAM markers
|
|
10
|
+
// `[x]`, and clears the STEP/SEAM markers: returning the dashboard to the
|
|
11
11
|
// DESIGN boundary.
|
|
12
12
|
import { appendFileSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import { commit, findRepoRoot, headSha, isDirty, stageAll, stagePath, stagedPaths, stagedStat } from "../lib/git.js";
|
|
14
14
|
import { activeBuild, buildFolder, buildLogPath, buildScope, bumpStepStat, checkpointsPath, clearHandoff, clearTick, hasSession, intentPath, readStats, refreshExcludes, seamPath, stampStepStat, stepPath, } from "../lib/sidecar.js";
|
|
15
15
|
import { runCheck } from "../lib/check.js";
|
|
16
|
+
import { readCommitBody } from "../lib/commitbody.js";
|
|
16
17
|
import { checkLatch } from "../lib/latch.js";
|
|
17
18
|
import { markStepDone, parseSteps } from "../lib/orient.js";
|
|
18
19
|
import { parseBuildScope, parseStepSeam, scopeDrift } from "../lib/intent.js";
|
|
@@ -22,7 +23,7 @@ import { AT_BOUNDARY, syncBuildLogState } from "../lib/buildlogsync.js";
|
|
|
22
23
|
/**
|
|
23
24
|
* Land a step: latch, check gate, commit, record, return to the boundary.
|
|
24
25
|
*
|
|
25
|
-
* Exit 1 on any refusal
|
|
26
|
+
* Exit 1 on any refusal: no session, no resolvable step, a latched tick, or a
|
|
26
27
|
* red/broken check; each refusal says what unblocks it.
|
|
27
28
|
*/
|
|
28
29
|
export async function checkpoint(cwd, args) {
|
|
@@ -31,7 +32,7 @@ export async function checkpoint(cwd, args) {
|
|
|
31
32
|
process.stderr.write('plumbbob: no active session. Run `plumbbob start "<title>"` first.\n');
|
|
32
33
|
return 1;
|
|
33
34
|
}
|
|
34
|
-
// Before either staging path
|
|
35
|
+
// Before either staging path: `-A` below, and the plan commit's folder-scoped
|
|
35
36
|
// add, which also leans on the excludes to skip the in-flight markers inside
|
|
36
37
|
// the build folder.
|
|
37
38
|
refreshExcludes(root);
|
|
@@ -43,10 +44,18 @@ export async function checkpoint(cwd, args) {
|
|
|
43
44
|
process.stderr.write('plumbbob: no step to checkpoint — pass a number, or plan a step in intent.md first.\n');
|
|
44
45
|
return 1;
|
|
45
46
|
}
|
|
46
|
-
//
|
|
47
|
+
// Checked before the latch and the check gate: a socket stdin can never
|
|
48
|
+
// deliver the requested body, so there is no point latching or running a
|
|
49
|
+
// ~55s check only to discover that afterward.
|
|
50
|
+
const bodyResult = readCommitBody(args);
|
|
51
|
+
if (!bodyResult.ok) {
|
|
52
|
+
process.stderr.write(bodyResult.message);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
// The approval latch runs before the check gate: cheap first. The tick may
|
|
47
56
|
// not land without a human turn since the step was entered, a one-turn grant
|
|
48
57
|
// the human typed, or a dormant ledger. A refusal is not an error: the
|
|
49
|
-
// message is the pause affordance
|
|
58
|
+
// message is the pause affordance: exit 1 and hand the turn back.
|
|
50
59
|
const latch = checkLatch(root, step);
|
|
51
60
|
if (!latch.allow) {
|
|
52
61
|
process.stderr.write(latch.message);
|
|
@@ -69,7 +78,7 @@ export async function checkpoint(cwd, args) {
|
|
|
69
78
|
if (isDirty(root)) {
|
|
70
79
|
stageAll(root);
|
|
71
80
|
warnScopeDrift(root, step);
|
|
72
|
-
const body = withMarker(`plumbbob step ${step}`,
|
|
81
|
+
const body = withMarker(`plumbbob step ${step}`, bodyResult.body ?? fallbackBody(root, step));
|
|
73
82
|
sha = commit(root, messageArg(args) ?? subjectForStep(root, step), body);
|
|
74
83
|
}
|
|
75
84
|
else {
|
|
@@ -86,8 +95,8 @@ export async function checkpoint(cwd, args) {
|
|
|
86
95
|
syncBuildLogState(root, activeBuild(root), AT_BOUNDARY);
|
|
87
96
|
rmSync(seamPath(root), { force: true });
|
|
88
97
|
rmSync(stepPath(root), { force: true });
|
|
89
|
-
clearHandoff(root); // the agent-run handoff ledger is step-scoped
|
|
90
|
-
clearTick(root); // the step's entry stamp is spent
|
|
98
|
+
clearHandoff(root); // the agent-run handoff ledger is step-scoped; clear it with the markers.
|
|
99
|
+
clearTick(root); // the step's entry stamp is spent; the next `build <n>` re-stamps.
|
|
91
100
|
process.stdout.write(`plumbbob: step ${step} checkpointed — ${sha.slice(0, 9)}. Back at the boundary.\n`);
|
|
92
101
|
return 0;
|
|
93
102
|
}
|
|
@@ -97,33 +106,46 @@ export async function checkpoint(cwd, args) {
|
|
|
97
106
|
*
|
|
98
107
|
* Giving the plan its own commit keeps the first step's diff from absorbing the
|
|
99
108
|
* scaffold, so `git log` reads baseline → plan → steps. No check gate (there is
|
|
100
|
-
* no code work to verify yet), no intent flip, no step markers
|
|
109
|
+
* no code work to verify yet), no intent flip, no step markers: the plan lives
|
|
101
110
|
* entirely in DESIGN. An optional `--body` (stdin heredoc) rides after the
|
|
102
111
|
* `plumbbob plan` marker line; the folder is whitelisted artifact plane, so
|
|
103
112
|
* there is no scope drift to warn about.
|
|
104
113
|
*/
|
|
105
114
|
function checkpointPlan(root, args) {
|
|
106
115
|
// The latch covers the plan commit too, keyed on the TICK that `start`
|
|
107
|
-
// stamped: the plan pause is a real pause. No step number
|
|
116
|
+
// stamped: the plan pause is a real pause. No step number: a step-range
|
|
108
117
|
// grant does not speak to a plan.
|
|
109
118
|
const latch = checkLatch(root, null);
|
|
110
119
|
if (!latch.allow) {
|
|
111
120
|
process.stderr.write(latch.message);
|
|
112
121
|
return 1;
|
|
113
122
|
}
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
const bodyResult = readCommitBody(args);
|
|
124
|
+
if (!bodyResult.ok) {
|
|
125
|
+
process.stderr.write(bodyResult.message);
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
// A repo that gitignores the sidecar (its `.gitignore`, not plumbbob's own
|
|
129
|
+
// info/exclude) makes this folder-scoped add impossible; stagePath skips it
|
|
130
|
+
// and reports false, and the plan still lands as a valid record-only commit:
|
|
131
|
+
// its body carries the plan, the files stay untracked, and `--allow-empty`
|
|
132
|
+
// (in commit) makes the empty diff legal. History still reads baseline → plan
|
|
133
|
+
// → steps.
|
|
134
|
+
const staged = stagePath(root, buildFolder(root));
|
|
135
|
+
const sha = commit(root, planSubject(root), withMarker('plumbbob plan', bodyResult.body ?? undefined));
|
|
116
136
|
appendFileSync(checkpointsPath(root), `plan ${sha}\n`);
|
|
117
137
|
// Landing the plan consumes `start`'s entry stamp: a later hand-built diff
|
|
118
138
|
// (no `build <n>`) must find no stale TICK and stay guidance-governed.
|
|
119
139
|
clearTick(root);
|
|
120
|
-
process.stdout.write(
|
|
140
|
+
process.stdout.write(staged
|
|
141
|
+
? `plumbbob: plan committed — ${sha.slice(0, 9)}. Baseline → plan → steps.\n`
|
|
142
|
+
: `plumbbob: plan committed (record-only — sidecar is gitignored; the plan rides the commit message, the files stay untracked) — ${sha.slice(0, 9)}. Baseline → plan → steps.\n`);
|
|
121
143
|
return 0;
|
|
122
144
|
}
|
|
123
145
|
/**
|
|
124
146
|
* Compose the plan commit's CLI-owned subject: `chore(<scope>): plan`.
|
|
125
147
|
*
|
|
126
|
-
* The scope resolves through the build-default fallback chain
|
|
148
|
+
* The scope resolves through the build-default fallback chain: a bare
|
|
127
149
|
* `chore: plan` when none resolves. The `plumbbob plan` identifier rides the
|
|
128
150
|
* body marker line, never the subject.
|
|
129
151
|
*/
|
|
@@ -134,7 +156,7 @@ function planSubject(root) {
|
|
|
134
156
|
* Resolve the step being checkpointed, or null when none can be determined.
|
|
135
157
|
*
|
|
136
158
|
* Explicit arg > in-flight STEP file > first undone step in intent.md. A `-m`
|
|
137
|
-
* value is a message, never a step
|
|
159
|
+
* value is a message, never a step: `checkpoint -m "2"` must not read as
|
|
138
160
|
* step 2.
|
|
139
161
|
*/
|
|
140
162
|
function resolveStep(root, args) {
|
|
@@ -154,10 +176,10 @@ function resolveStep(root, args) {
|
|
|
154
176
|
}
|
|
155
177
|
}
|
|
156
178
|
/**
|
|
157
|
-
* Warn
|
|
179
|
+
* Warn (never refuse) when the staged tree reaches beyond the step's seam.
|
|
158
180
|
*
|
|
159
181
|
* Guidance, not a gate: the checkpoint captures the drift and says so. The seam
|
|
160
|
-
* (the step's edit grant
|
|
182
|
+
* (the step's edit grant: exact paths or `dir/` prefixes) comes from the
|
|
161
183
|
* in-flight SEAM file when a build is live, else the step's declared seam in
|
|
162
184
|
* intent.md. Plumbbob's own artifact plane is whitelisted inside `scopeDrift`,
|
|
163
185
|
* so the `[x]` flip and build-log line this very checkpoint stages never read
|
|
@@ -177,7 +199,7 @@ function warnScopeDrift(root, step) {
|
|
|
177
199
|
*
|
|
178
200
|
* The normalized SEAM file `build` wrote is authoritative while a build is
|
|
179
201
|
* live; fall back to the step's declared seam parsed from intent.md. Empty when
|
|
180
|
-
* neither resolves
|
|
202
|
+
* neither resolves: the caller then skips the drift warning rather than
|
|
181
203
|
* flagging the whole tree.
|
|
182
204
|
*/
|
|
183
205
|
function seamTokens(root, step) {
|
|
@@ -191,14 +213,14 @@ function seamTokens(root, step) {
|
|
|
191
213
|
}
|
|
192
214
|
}
|
|
193
215
|
catch {
|
|
194
|
-
// no SEAM file
|
|
216
|
+
// no SEAM file: fall through to the declared seam.
|
|
195
217
|
}
|
|
196
218
|
return seamForStep(root, step);
|
|
197
219
|
}
|
|
198
220
|
/**
|
|
199
221
|
* Flip the step's checkbox in intent.md to `[x]`.
|
|
200
222
|
*
|
|
201
|
-
* Best-effort bookkeeping
|
|
223
|
+
* Best-effort bookkeeping (the checkpoint SHA is the source of truth) but the
|
|
202
224
|
* dashboard reads intent.md, so a swallowed failure here would make orientation
|
|
203
225
|
* lie; the catch warns and asks for a hand flip instead.
|
|
204
226
|
*/
|
|
@@ -216,7 +238,7 @@ function flipIntent(root, step) {
|
|
|
216
238
|
*
|
|
217
239
|
* The build's history accrues at each checkpoint instead of being reconstructed
|
|
218
240
|
* at finish; the step's title is lifted from intent.md when still present.
|
|
219
|
-
* Best-effort: a missing or odd build-log never blocks a checkpoint
|
|
241
|
+
* Best-effort: a missing or odd build-log never blocks a checkpoint; the
|
|
220
242
|
* `checkpoints` SHA is the source of truth.
|
|
221
243
|
*/
|
|
222
244
|
function logCheckpoint(root, step, sha) {
|
|
@@ -236,7 +258,7 @@ function logCheckpoint(root, step, sha) {
|
|
|
236
258
|
/**
|
|
237
259
|
* The compact stats receipt riding the Log line, or null when nothing accrued.
|
|
238
260
|
*
|
|
239
|
-
* Only what happened is shown
|
|
261
|
+
* Only what happened is shown: a clean first-try step gets no suffix at all.
|
|
240
262
|
* Wall-clock needs both stamps: a hand-built step never ran `build <n>`, so it
|
|
241
263
|
* has no startedAt.
|
|
242
264
|
*/
|
|
@@ -280,7 +302,7 @@ function subjectForStep(root, step) {
|
|
|
280
302
|
* The build-default Conventional scope, or null for a bare subject.
|
|
281
303
|
*
|
|
282
304
|
* The `**Scope:**` header field in intent.md wins when authored and filled;
|
|
283
|
-
* else the build slug with its date prefix stripped; else null
|
|
305
|
+
* else the build slug with its date prefix stripped; else null: a build with
|
|
284
306
|
* neither field keeps producing bare subjects.
|
|
285
307
|
*/
|
|
286
308
|
function buildDefaultScope(root) {
|
|
@@ -291,7 +313,7 @@ function buildDefaultScope(root) {
|
|
|
291
313
|
}
|
|
292
314
|
}
|
|
293
315
|
catch {
|
|
294
|
-
// no intent.md yet
|
|
316
|
+
// no intent.md yet: fall through to the slug rung.
|
|
295
317
|
}
|
|
296
318
|
return buildScope(activeBuild(root));
|
|
297
319
|
}
|
|
@@ -313,27 +335,6 @@ function messageArg(args) {
|
|
|
313
335
|
const i = args.indexOf('-m');
|
|
314
336
|
return i !== -1 && i + 1 < args.length ? (args[i + 1] ?? null) : null;
|
|
315
337
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Read the `--body` commit body from stdin, or null when absent or empty.
|
|
318
|
-
*
|
|
319
|
-
* The stdin heredoc lets a skill compose proportional prose the CLI never
|
|
320
|
-
* could; null either way hands over to the deterministic fallback body. Reading
|
|
321
|
-
* fd 0 blocks until EOF, which the heredoc supplies; a read error (no stdin
|
|
322
|
-
* attached) degrades to the fallback, and an interactive TTY — which would
|
|
323
|
-
* never send EOF — skips the read instead of hanging.
|
|
324
|
-
*/
|
|
325
|
-
function bodyArg(args) {
|
|
326
|
-
if (!args.includes('--body') || process.stdin.isTTY === true) {
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
try {
|
|
330
|
-
const raw = readFileSync(0, 'utf8').trimEnd();
|
|
331
|
-
return raw.length > 0 ? raw : null;
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
338
|
/**
|
|
338
339
|
* The deterministic fallback commit body: the step's done-when, its seam, and
|
|
339
340
|
* the staged diffstat.
|
package/dist/verbs/doctor.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
// `plumbbob doctor
|
|
1
|
+
// `plumbbob doctor`: the health report, five sections under one verb.
|
|
2
2
|
//
|
|
3
3
|
// 1. The global plugin link (read-only). After `plumbbob init`, plumbbob lives as a
|
|
4
4
|
// symlink at ~/.claude/skills/plumbbob pointing at the installed package; Claude
|
|
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
|
-
// the skills, and the hook
|
|
7
|
+
// the skills, and the hook, and names the fix for anything missing. The failure
|
|
8
8
|
// class it exists for is SILENT (a `/plumbbob:status` that opens an empty dashboard
|
|
9
9
|
// because the plugin never linked).
|
|
10
10
|
//
|
|
11
11
|
// 2. The repo sidecar layout. plumbbob keeps its per-repo state in a `.plumbbob/`
|
|
12
12
|
// sidecar: a tracked artifact plane (settings.json plus one `builds/<slug>/` folder
|
|
13
|
-
// per build
|
|
13
|
+
// per build (intent, build-log, checkpoints, report) that rides its branch into
|
|
14
14
|
// the PR) and a git-excluded control plane of per-worktree files. A repo scaffolded
|
|
15
15
|
// by an older plumbbob carries the legacy FLAT sidecar instead (`.plumbbob/intent.md`,
|
|
16
16
|
// `config`, `archive/`), fully git-excluded, whose record dies with
|
|
17
17
|
// `git worktree remove`. doctor detects it and, with `--migrate`, moves it into the
|
|
18
18
|
// tracked `builds/<slug>/` layout: archive entries and the active session become
|
|
19
19
|
// build folders, `config` becomes `settings.json`, and the whole move is STAGED but
|
|
20
|
-
// never committed
|
|
20
|
+
// never committed: the human owns that commit.
|
|
21
21
|
//
|
|
22
22
|
// 3. Agent validation. Every resolvable user-authored agent gets its manifest and its
|
|
23
23
|
// `command` statically checked, so a broken agent surfaces here rather than mid-build.
|
|
24
24
|
//
|
|
25
25
|
// 4. The check gate. When a `check` setting overrides checkride (the default heavy
|
|
26
26
|
// check), doctor names the command; otherwise it runs checkride's own doctor and
|
|
27
|
-
// prints the slot/adapter table
|
|
27
|
+
// prints the slot/adapter table: "detected but tool missing" is the footgun this
|
|
28
28
|
// exists for.
|
|
29
29
|
//
|
|
30
30
|
// 5. The approval latch. Reports whether the harness's turn ledger is live (checkpoint
|
|
31
|
-
// can latch on a recorded human turn) or dormant (guidance only)
|
|
31
|
+
// can latch on a recorded human turn) or dormant (guidance only): dormant is a
|
|
32
32
|
// state, never a failure.
|
|
33
33
|
//
|
|
34
34
|
// Functional, node builtins plus checkride.
|
|
@@ -44,7 +44,7 @@ import { findRepoRoot, gitPath, stagePath } from "../lib/git.js";
|
|
|
44
44
|
import { buildDir, excludeControl, listBuilds, readTurn, setActiveBuild, sidecarDir, slugify } from "../lib/sidecar.js";
|
|
45
45
|
import { resolveBoolean, resolveString, settingsPath } from "../lib/settings.js";
|
|
46
46
|
/**
|
|
47
|
-
* Resolve a directory shipped inside the plumbbob package (
|
|
47
|
+
* Resolve a directory shipped inside the plumbbob package (for example its skills/).
|
|
48
48
|
*
|
|
49
49
|
* Resolved off this module's URL so it lands the same from src/ (dev) and
|
|
50
50
|
* dist/ (published).
|
|
@@ -103,10 +103,10 @@ function buildChecks(link, pkg, shipped) {
|
|
|
103
103
|
/**
|
|
104
104
|
* The plugin-link diagnostic: how plumbbob is installed into Claude Code, as checks.
|
|
105
105
|
*
|
|
106
|
-
* Two install kinds are recognized: the marketplace plugin (one green line
|
|
106
|
+
* Two install kinds are recognized: the marketplace plugin (one green line: skills
|
|
107
107
|
* load and the CLI rides the plugin's bin/, nothing to verify piecemeal) and the
|
|
108
108
|
* init-style link at ~/.claude/skills/plumbbob (verified piece by piece). Both at
|
|
109
|
-
* once is a collision
|
|
109
|
+
* once is a collision (two plugins named plumbbob fight over /plumbbob:*), reported
|
|
110
110
|
* first, with the keep-one fix.
|
|
111
111
|
*/
|
|
112
112
|
function pluginChecks() {
|
|
@@ -148,7 +148,7 @@ function subdirs(dir) {
|
|
|
148
148
|
* Detect a legacy flat sidecar, or null when the repo is on the current layout
|
|
149
149
|
* (or has no sidecar).
|
|
150
150
|
*
|
|
151
|
-
* `config` and `archive/` are unambiguous legacy markers
|
|
151
|
+
* `config` and `archive/` are unambiguous legacy markers: the builds/ layout has
|
|
152
152
|
* neither. A flat `intent.md` alone would also match today's `--local` layout, so it
|
|
153
153
|
* only counts as legacy when the repo is NOT already migrated (no `builds/`, no
|
|
154
154
|
* `settings.json`): that guard is what keeps `--local` untouched.
|
|
@@ -166,7 +166,7 @@ export function inspectLegacy(root) {
|
|
|
166
166
|
return { config, archive, session };
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
|
-
* The first `# Heading` in a flat intent.md
|
|
169
|
+
* The first `# Heading` in a flat intent.md: the build title the slug derives from.
|
|
170
170
|
*/
|
|
171
171
|
function titleFromIntent(path) {
|
|
172
172
|
try {
|
|
@@ -200,7 +200,7 @@ function configCheck(path) {
|
|
|
200
200
|
/**
|
|
201
201
|
* A slug not already claimed by `taken`, suffixing `-2`, `-3`, … only on collision.
|
|
202
202
|
*
|
|
203
|
-
* `start` refuses a colliding slug outright
|
|
203
|
+
* `start` refuses a colliding slug outright (the human picks a new title), but
|
|
204
204
|
* migration is mechanically moving folders that already exist, so it disambiguates
|
|
205
205
|
* rather than aborting mid-move.
|
|
206
206
|
*/
|
|
@@ -245,14 +245,14 @@ function narrowExcludes(root) {
|
|
|
245
245
|
writeFileSync(exclude, kept.join('\n'));
|
|
246
246
|
}
|
|
247
247
|
catch {
|
|
248
|
-
/* no exclude yet
|
|
248
|
+
/* no exclude yet: excludeControl creates it */
|
|
249
249
|
}
|
|
250
250
|
excludeControl(root);
|
|
251
251
|
}
|
|
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
|
|
255
|
+
* STAGES the result but never commits: the human owns that commit. Call only when
|
|
256
256
|
* `inspectLegacy` reported a legacy layout; doctor prints the returned actions.
|
|
257
257
|
*/
|
|
258
258
|
export function migrateSidecar(root) {
|
|
@@ -263,7 +263,7 @@ export function migrateSidecar(root) {
|
|
|
263
263
|
const configPath = join(dir, 'config');
|
|
264
264
|
if (existsSync(configPath)) {
|
|
265
265
|
if (!existsSync(settingsPath(root))) {
|
|
266
|
-
// Carry forward ONLY what the legacy config actually held
|
|
266
|
+
// Carry forward ONLY what the legacy config actually held: a `check` line,
|
|
267
267
|
// nothing else. No invented `auto` (the config never had one, it defaults to
|
|
268
268
|
// false, and it belongs in settings.local.json): migration yields exactly what
|
|
269
269
|
// a fresh `start` would, plus the check. An empty config → an empty `{}`.
|
|
@@ -336,7 +336,7 @@ function sidecarReport(cwd, migrate) {
|
|
|
336
336
|
/**
|
|
337
337
|
* Interpreters recognized as the program of a manifest `command`: when the command
|
|
338
338
|
* is `sh <script>` / `node <script>` / …, the checkable artifact is the script
|
|
339
|
-
* argument (the interpreter reads it
|
|
339
|
+
* argument (the interpreter reads it: the file need not carry +x), not the
|
|
340
340
|
* interpreter, which we trust to be on PATH.
|
|
341
341
|
*/
|
|
342
342
|
const INTERPRETERS = new Set(['sh', 'bash', 'zsh', 'dash', 'node', 'deno', 'bun', 'python', 'python3', 'ruby', 'perl']);
|
|
@@ -344,7 +344,7 @@ const INTERPRETERS = new Set(['sh', 'bash', 'zsh', 'dash', 'node', 'deno', 'bun'
|
|
|
344
344
|
* Expand PLUMBBOB_AGENT_DIR in a command and split it into bare tokens
|
|
345
345
|
* (surrounding quotes stripped).
|
|
346
346
|
*
|
|
347
|
-
* A deliberately shallow split
|
|
347
|
+
* A deliberately shallow split: enough for the static command check below, not a
|
|
348
348
|
* shell parser (which is neither possible nor wanted for a best-effort diagnostic).
|
|
349
349
|
*/
|
|
350
350
|
function commandTokens(command, agentDir) {
|
|
@@ -356,11 +356,11 @@ function commandTokens(command, agentDir) {
|
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
358
358
|
* Statically check a manifest `command` for the silent footguns: a script file that
|
|
359
|
-
* does not exist, or a directly
|
|
359
|
+
* does not exist, or a directly invoked one missing its +x bit.
|
|
360
360
|
*
|
|
361
361
|
* The command is a shell string spawned via `sh -c` with the repo root as cwd, so a
|
|
362
362
|
* relative path resolves against `root` and PLUMBBOB_AGENT_DIR points at the agent's
|
|
363
|
-
* own dir. Best-effort by design
|
|
363
|
+
* own dir. Best-effort by design: a path-shaped program or an interpreter's script
|
|
364
364
|
* argument is checked; a bare command (a PATH binary, an inline `node -e` program)
|
|
365
365
|
* is trusted, because we cannot prove a shell line broken without running it.
|
|
366
366
|
* Returns the problem string, or null when nothing checkable is wrong.
|
|
@@ -423,7 +423,7 @@ function agentReport(cwd) {
|
|
|
423
423
|
const listings = listAgents(root);
|
|
424
424
|
if (listings.length === 0)
|
|
425
425
|
return { lines: [], failed: 0 };
|
|
426
|
-
const lines = ['', 'plumbbob doctor — agents (
|
|
426
|
+
const lines = ['', 'plumbbob doctor — agents — D48 (doctor-validates-agents)'];
|
|
427
427
|
let failed = 0;
|
|
428
428
|
for (const listing of listings) {
|
|
429
429
|
const problem = agentProblem(listing, root);
|
|
@@ -443,7 +443,7 @@ function agentReport(cwd) {
|
|
|
443
443
|
*
|
|
444
444
|
* A configured `check` setting names the spawn override and asks nothing more;
|
|
445
445
|
* otherwise checkride's own doctor reports the slot/adapter table. Only rows
|
|
446
|
-
* checkride marks `required` count as problems
|
|
446
|
+
* checkride marks `required` count as problems; an empty slot ("no tool detected")
|
|
447
447
|
* is informational, not a failure, because the runtime gate already refuses a
|
|
448
448
|
* vacuous run.
|
|
449
449
|
*/
|
|
@@ -451,7 +451,7 @@ async function gateReport(cwd, env) {
|
|
|
451
451
|
const root = findRepoRoot(cwd);
|
|
452
452
|
if (root === null)
|
|
453
453
|
return { lines: [], failed: 0 };
|
|
454
|
-
const lines = ['', 'plumbbob doctor — check gate (
|
|
454
|
+
const lines = ['', 'plumbbob doctor — check gate — D32 (checkride-gate)'];
|
|
455
455
|
const command = resolveString(root, 'check', '');
|
|
456
456
|
if (command.length > 0) {
|
|
457
457
|
lines.push(` ✓ gate: '${command}' — the "check" setting overrides checkride`);
|
|
@@ -474,7 +474,7 @@ async function gateReport(cwd, env) {
|
|
|
474
474
|
failed += 1;
|
|
475
475
|
}
|
|
476
476
|
// Called out where the human can see it coming: no CODE checks detected means
|
|
477
|
-
// checkpoints would be gated only by checkride's always-on repo checks
|
|
477
|
+
// checkpoints would be gated only by checkride's always-on repo checks: either
|
|
478
478
|
// a vacuous refusal or, worse, a green that tested nothing. Informational, with
|
|
479
479
|
// the exact fix; `start` surfaces the same probe at plan time.
|
|
480
480
|
if (!gateDetectsTools(report.checks)) {
|
|
@@ -490,7 +490,8 @@ async function gateReport(cwd, env) {
|
|
|
490
490
|
}
|
|
491
491
|
/**
|
|
492
492
|
* One slot line: `✓ types ← tsc (6.0.3)`, `✗ dead ← fallow` with its install
|
|
493
|
-
* hint, or `○ spell
|
|
493
|
+
* hint, or `○ spell` joined by an em dash to "no tool detected" for an empty
|
|
494
|
+
* (skipping) slot.
|
|
494
495
|
*/
|
|
495
496
|
function toolRow(c) {
|
|
496
497
|
if (c.adapter === null || c.adapter === undefined) {
|
|
@@ -507,8 +508,8 @@ function toolRow(c) {
|
|
|
507
508
|
* The latch is `checkpoint`'s refusal to land a step until the harness has recorded
|
|
508
509
|
* a human turn since the step was entered. `.plumbbob/TURN` holds a count once the
|
|
509
510
|
* UserPromptSubmit hook has ticked at least once, so a present count means the latch
|
|
510
|
-
* is armed
|
|
511
|
-
* state (a host with no hooks simply has no ledger), never a failure
|
|
511
|
+
* is armed; its absence means the tick is guidance-only. Dormant is a legitimate
|
|
512
|
+
* state (a host with no hooks simply has no ledger), never a failure, so this
|
|
512
513
|
* section adds nothing to the problem count. The dormant hint names the hook because
|
|
513
514
|
* a missing ledger almost always means it never wired, and that hook rides in either
|
|
514
515
|
* install kind (the marketplace plugin's hooks/hooks.json or an init-style link).
|
|
@@ -523,13 +524,13 @@ function latchReport(cwd) {
|
|
|
523
524
|
const line = turn === null
|
|
524
525
|
? ' ○ latch: dormant — guidance only (no turn ledger yet; it ticks on your first prompt when the UserPromptSubmit hook is wired — re-run after one to confirm)'
|
|
525
526
|
: ` ✓ latch: live (turn ${turn})`;
|
|
526
|
-
const lines = ['', 'plumbbob doctor — approval latch (
|
|
527
|
+
const lines = ['', 'plumbbob doctor — approval latch — D64 (approval-latch)', line];
|
|
527
528
|
// The latch ignores a settings `auto`: a model can write that file, and a grant
|
|
528
|
-
// the model can forge is no grant
|
|
529
|
+
// the model can forge is no grant: self-approval comes only from the human's
|
|
529
530
|
// typed `/build --auto`. Surface a set value so a human relying on it isn't
|
|
530
|
-
// silently changed
|
|
531
|
+
// silently changed: informational, never a problem.
|
|
531
532
|
if (resolveBoolean(root, 'auto', false)) {
|
|
532
|
-
lines.push(' ○ auto: set in settings but not a grant since D67 — self-approve per run with `/plumbbob:build --auto`');
|
|
533
|
+
lines.push(' ○ auto: set in settings but not a grant since D67 (auto-not-a-grant) — self-approve per run with `/plumbbob:build --auto`');
|
|
533
534
|
}
|
|
534
535
|
return { lines, failed: 0 };
|
|
535
536
|
}
|
package/dist/verbs/finish.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// `plumbbob finish
|
|
1
|
+
// `plumbbob finish`: the close-out. It appends the checkpoint SHAs and per-step
|
|
2
2
|
// stats to report.md, makes the final commit, and clears the control state (the
|
|
3
3
|
// untracked session and step markers under `.plumbbob/`). The build folder is NOT
|
|
4
|
-
// deleted
|
|
4
|
+
// deleted; the tracked `.plumbbob/builds/<slug>/` folder IS the archive: it
|
|
5
5
|
// merges with the branch and shows up in the PR, so nothing is copied into a
|
|
6
|
-
// separate local archive. A missing report never blocks the exit
|
|
6
|
+
// separate local archive. A missing report never blocks the exit: guidance
|
|
7
7
|
// offers the artifact, it does not wall the door. The git footprint stays
|
|
8
8
|
// additive: one forward commit under the Conventional `chore(<scope>): finish`
|
|
9
9
|
// subject, with the `plumbbob finish` identifier riding a marker line at the head
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
13
|
import { commit, findRepoRoot, isDirty, stageAll } from "../lib/git.js";
|
|
14
|
+
import { readCommitBody } from "../lib/commitbody.js";
|
|
14
15
|
import { buildScope, checkpointsPath, clearTick, hasSession, intentPath, readStats, refreshExcludes, reportPath, resolveBuild, seamPath, setGrant, sidecarDir, spikePath, stepPath, } from "../lib/sidecar.js";
|
|
15
16
|
import { conventionalSubject, withMarker } from "../lib/commitmsg.js";
|
|
16
17
|
import { parseBuildScope } from "../lib/intent.js";
|
|
@@ -19,7 +20,7 @@ import { parseBuildScope } from "../lib/intent.js";
|
|
|
19
20
|
*
|
|
20
21
|
* Requires an active session (the `.plumbbob/STATE` sentinel). When report.md
|
|
21
22
|
* exists it gains a `## Checkpoints` SHA list and a `## Stats` table first; when
|
|
22
|
-
* it doesn't, finish notes the absence and proceeds
|
|
23
|
+
* it doesn't, finish notes the absence and proceeds; the exit is never gated on
|
|
23
24
|
* a report.
|
|
24
25
|
*/
|
|
25
26
|
export function finish(cwd, args = []) {
|
|
@@ -28,6 +29,15 @@ export function finish(cwd, args = []) {
|
|
|
28
29
|
process.stderr.write('plumbbob: no active session. Run `plumbbob start "<title>"` first.\n');
|
|
29
30
|
return 1;
|
|
30
31
|
}
|
|
32
|
+
// Checked before any of finish's writes: appendCheckpointShas/appendStats
|
|
33
|
+
// are plain appends, not idempotent, so a refusal after they ran would
|
|
34
|
+
// duplicate those sections on retry. A socket stdin can never deliver the
|
|
35
|
+
// requested body, so there is nothing to do first.
|
|
36
|
+
const bodyResult = readCommitBody(args);
|
|
37
|
+
if (!bodyResult.ok) {
|
|
38
|
+
process.stderr.write(bodyResult.message);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
31
41
|
const { build: slug } = resolveBuild(root, args);
|
|
32
42
|
if (existsSync(reportPath(root, slug))) {
|
|
33
43
|
appendCheckpointShas(root, slug);
|
|
@@ -35,11 +45,11 @@ export function finish(cwd, args = []) {
|
|
|
35
45
|
}
|
|
36
46
|
else {
|
|
37
47
|
process.stderr.write('plumbbob: note — no report.md found; finishing without one ' +
|
|
38
|
-
'(/plumbbob:finish normally writes the report first). No gate (
|
|
48
|
+
'(/plumbbob:finish normally writes the report first). No gate — D9 (finish-no-gate).\n');
|
|
39
49
|
}
|
|
40
|
-
// The final commit: stage the report just written plus the build folder's tail
|
|
41
|
-
// a step's commit sweeps its own bookkeeping along with the work, so the last
|
|
42
|
-
// step's `checkpoints` line lands one commit late and finish absorbs it
|
|
50
|
+
// The final commit: stage the report just written plus the build folder's tail
|
|
51
|
+
// (a step's commit sweeps its own bookkeeping along with the work, so the last
|
|
52
|
+
// step's `checkpoints` line lands one commit late and finish absorbs it) and
|
|
43
53
|
// commit under the `finish` subject. `--allow-empty` (via `commit`) still marks
|
|
44
54
|
// the narrative endpoint when the tree is already clean, or under `--local`,
|
|
45
55
|
// where the whole sidecar is git-excluded and there is nothing tracked to stage.
|
|
@@ -47,18 +57,18 @@ export function finish(cwd, args = []) {
|
|
|
47
57
|
if (isDirty(root)) {
|
|
48
58
|
stageAll(root);
|
|
49
59
|
}
|
|
50
|
-
const sha = commit(root, subject(root, slug), withMarker('plumbbob finish',
|
|
60
|
+
const sha = commit(root, subject(root, slug), withMarker('plumbbob finish', bodyResult.body ?? undefined));
|
|
51
61
|
// Clear the control state: the in-flight markers first, then the session
|
|
52
62
|
// sentinel last (so "no session" flips exactly at the end). Deleting STATE also
|
|
53
|
-
// drops the active-build cursor
|
|
54
|
-
// single delete does both. The tracked artifacts stay in place
|
|
63
|
+
// drops the active-build cursor: cursor and session share that one file, so a
|
|
64
|
+
// single delete does both. The tracked artifacts stay in place; only the
|
|
55
65
|
// ephemera go.
|
|
56
66
|
rmSync(seamPath(root, slug), { force: true });
|
|
57
67
|
rmSync(stepPath(root, slug), { force: true });
|
|
58
68
|
rmSync(spikePath(root, slug), { force: true });
|
|
59
69
|
// The checkpoint latch's per-build entry stamp and the one-turn self-approval
|
|
60
70
|
// grant go with the session: a grant lives one turn by construction, but only
|
|
61
|
-
// because every tick rewrites it
|
|
71
|
+
// because every tick rewrites it: the session's last tick was the last rewrite,
|
|
62
72
|
// so left behind, a stale `auto` could self-approve the next session's first
|
|
63
73
|
// landing.
|
|
64
74
|
clearTick(root, slug);
|
|
@@ -74,7 +84,7 @@ export function finish(cwd, args = []) {
|
|
|
74
84
|
*
|
|
75
85
|
* The scope resolves through the same build-default fallback chain as the plan
|
|
76
86
|
* and step subjects: the intent.md `**Scope:**` header field, else the build
|
|
77
|
-
* slug, else bare (
|
|
87
|
+
* slug, else bare (for example `--local`). The `plumbbob finish` identifier rides the
|
|
78
88
|
* body marker, not the subject.
|
|
79
89
|
*/
|
|
80
90
|
function subject(root, slug) {
|
|
@@ -83,7 +93,7 @@ function subject(root, slug) {
|
|
|
83
93
|
/**
|
|
84
94
|
* Resolve the commit scope: the intent.md `**Scope:**` header, else the slug.
|
|
85
95
|
*
|
|
86
|
-
* Twin of checkpoint.ts's helper
|
|
96
|
+
* Twin of checkpoint.ts's helper: same fallback chain, but with this verb's own
|
|
87
97
|
* `slug` resolution instead of the active-build cursor.
|
|
88
98
|
*/
|
|
89
99
|
function buildDefaultScope(root, slug) {
|
|
@@ -94,38 +104,15 @@ function buildDefaultScope(root, slug) {
|
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
catch {
|
|
97
|
-
// no intent.md
|
|
107
|
+
// no intent.md: fall through to the slug rung.
|
|
98
108
|
}
|
|
99
109
|
return buildScope(slug);
|
|
100
110
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Read the `--body` final-commit body from stdin, or null.
|
|
103
|
-
*
|
|
104
|
-
* The body arrives as a single-quoted stdin heredoc — the CLI owns every commit
|
|
105
|
-
* subject, but the finish skill composes a proportional close-out body this
|
|
106
|
-
* way. Returns null when the flag is absent or stdin is empty; the commit then
|
|
107
|
-
* carries subject only. Reading fd 0 blocks until EOF, which the heredoc
|
|
108
|
-
* supplies; a read error (no stdin attached) degrades to null, and an
|
|
109
|
-
* interactive TTY — which would never send EOF — skips the read instead of
|
|
110
|
-
* hanging (twin of checkpoint.ts's guard).
|
|
111
|
-
*/
|
|
112
|
-
function bodyArg(args) {
|
|
113
|
-
if (!args.includes('--body') || process.stdin.isTTY === true) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
const raw = readFileSync(0, 'utf8').trimEnd();
|
|
118
|
-
return raw.length > 0 ? raw : null;
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
111
|
/**
|
|
125
112
|
* Roll the per-step receipts into report.md as a `## Stats` table.
|
|
126
113
|
*
|
|
127
|
-
* One row per step plus totals
|
|
128
|
-
* wall-clock
|
|
114
|
+
* One row per step plus totals (red checks, drift warnings, reverts,
|
|
115
|
+
* wall-clock) so "is the loop worth it?" is a table, not a feeling. Silently
|
|
129
116
|
* skipped when nothing accrued (a build with no red/revert/drift and no
|
|
130
117
|
* `build <n>` stamps has nothing to say).
|
|
131
118
|
*/
|
|
@@ -173,7 +160,7 @@ function wallClockMs(s) {
|
|
|
173
160
|
/**
|
|
174
161
|
* Format a wall-clock duration for the stats table.
|
|
175
162
|
*
|
|
176
|
-
*
|
|
163
|
+
* An em dash when unknown (a hand-built step never ran `build <n>`, so it has no
|
|
177
164
|
* startedAt), `<1m` under a minute, whole minutes otherwise.
|
|
178
165
|
*/
|
|
179
166
|
function formatWall(ms) {
|