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/lib/sidecar.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
// The .plumbbob/ sidecar
|
|
1
|
+
// The .plumbbob/ sidecar: plumbbob's control directory beside the code. Control
|
|
2
2
|
// state lives in flat files (bare numbers, plain path lists, one-line markers) so
|
|
3
3
|
// shell hooks can read it with a grep and no markdown parsing. Functional and
|
|
4
4
|
// procedural, node builtins only.
|
|
5
5
|
//
|
|
6
6
|
// Two planes share the directory: the tracked ARTIFACT plane (settings.json and
|
|
7
|
-
// each build's `builds/<slug>/` folder
|
|
7
|
+
// each build's `builds/<slug>/` folder: intent, build-log, checkpoints, report),
|
|
8
8
|
// which rides its branch into the PR, and the git-excluded CONTROL plane (STATE,
|
|
9
9
|
// the personal settings overlay, the turn ledger, and each build's in-flight
|
|
10
10
|
// markers), which stays per-worktree and never commits.
|
|
11
11
|
//
|
|
12
12
|
// STATE is the session sentinel AND the active-build cursor: its EXISTENCE means
|
|
13
|
-
// "a session is active", and its CONTENT names the build that session is on
|
|
13
|
+
// "a session is active", and its CONTENT names the build that session is on: the
|
|
14
14
|
// two reinforce each other (a session is always on some build; the cursor is
|
|
15
15
|
// meaningless without a session). Content is empty under --local / no build.
|
|
16
16
|
// hasSession stays existence-only; activeBuild reads the content. Homing the
|
|
17
|
-
// cursor here
|
|
17
|
+
// cursor here (not in settings.local.json) keeps that overlay purely
|
|
18
18
|
// human-owned (check/auto): the tool only ever reads it, never rewrites it. The
|
|
19
|
-
// phase the dashboard shows (DESIGN/BUILD/SPIKE) is derived, not stored
|
|
19
|
+
// phase the dashboard shows (DESIGN/BUILD/SPIKE) is derived, not stored: BUILD ⇔
|
|
20
20
|
// a STEP is in flight, SPIKE ⇔ the SPIKE marker is present, otherwise DESIGN.
|
|
21
21
|
import { existsSync, readdirSync, readFileSync, writeFileSync, appendFileSync, rmSync, mkdirSync } from 'node:fs';
|
|
22
22
|
import { dirname, join } from 'node:path';
|
|
@@ -48,7 +48,7 @@ export function buildDir(root, slug) {
|
|
|
48
48
|
* Derive a filesystem-safe slug from a build title.
|
|
49
49
|
*
|
|
50
50
|
* Lowercased, every run of non-alphanumerics collapsed to a single hyphen,
|
|
51
|
-
* trimmed of leading/trailing hyphens. The CLI stays dumb and explicit
|
|
51
|
+
* trimmed of leading/trailing hyphens. The CLI stays dumb and explicit:
|
|
52
52
|
* collision handling belongs to the caller (`start` refuses an existing slug
|
|
53
53
|
* rather than silently suffixing `-2`, and prepends the `YYYY-MM-DD-` date that
|
|
54
54
|
* keeps `listBuilds`' lexical sort chronological). A title with no alphanumerics
|
|
@@ -65,7 +65,7 @@ export function slugify(title) {
|
|
|
65
65
|
* date prefix `start` prepends stripped off.
|
|
66
66
|
*
|
|
67
67
|
* `2026-07-18-escape-hatch` yields the human-meaningful `escape-hatch`. Null
|
|
68
|
-
* when no build resolves (`--local`, or a slug that is nothing but a date)
|
|
68
|
+
* when no build resolves (`--local`, or a slug that is nothing but a date); the
|
|
69
69
|
* caller then omits the `(scope)` segment, which Conventional Commits permit.
|
|
70
70
|
*/
|
|
71
71
|
export function buildScope(slug) {
|
|
@@ -76,7 +76,7 @@ export function buildScope(slug) {
|
|
|
76
76
|
return stripped.length > 0 ? stripped : null;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* Existing build slugs
|
|
79
|
+
* Existing build slugs: the directory names under `builds/`, sorted.
|
|
80
80
|
*
|
|
81
81
|
* Empty when `builds/` is absent (a `--local` repo, or before the first tracked
|
|
82
82
|
* `start`).
|
|
@@ -110,7 +110,7 @@ export function activeBuild(root, flag) {
|
|
|
110
110
|
return builds.length === 1 ? (builds[0] ?? null) : null;
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
|
-
* STATE's content
|
|
113
|
+
* STATE's content (the active-build cursor) or null when it names no build.
|
|
114
114
|
*
|
|
115
115
|
* Empty means "no build" (--local, or a session whose cursor is unset), so
|
|
116
116
|
* activeBuild falls through to the sole-build rule. The literal value `active`
|
|
@@ -135,7 +135,7 @@ function readCursor(root) {
|
|
|
135
135
|
*
|
|
136
136
|
* Every verb resolves through this: an explicit `--build <slug>` wins, else the
|
|
137
137
|
* cursor / sole-build fallback of `activeBuild`. `rest` matters because the slug
|
|
138
|
-
* is a bare token
|
|
138
|
+
* is a bare token: scanning positionals on the raw argv would let it masquerade
|
|
139
139
|
* as a step number or a spike slug, so callers scan `rest` instead.
|
|
140
140
|
*/
|
|
141
141
|
export function resolveBuild(root, args) {
|
|
@@ -145,7 +145,7 @@ export function resolveBuild(root, args) {
|
|
|
145
145
|
return { build: activeBuild(root, args[i + 1]), rest: [...args.slice(0, i), ...args.slice(i + 2)] };
|
|
146
146
|
}
|
|
147
147
|
/**
|
|
148
|
-
* The STATE file
|
|
148
|
+
* The STATE file: session sentinel and active-build cursor in one.
|
|
149
149
|
*/
|
|
150
150
|
function statePath(root) {
|
|
151
151
|
return join(root, DIRNAME, 'STATE');
|
|
@@ -158,7 +158,7 @@ function statePath(root) {
|
|
|
158
158
|
* omit it and the dir resolves from the cursor (the default the
|
|
159
159
|
* executor-agnostic path reads lean on). Either way a `null` slug falls back to
|
|
160
160
|
* the flat sidecar root, which covers the `--local` layout and any
|
|
161
|
-
* no-cursor/no-build repo
|
|
161
|
+
* no-cursor/no-build repo: the path reads stay stable even before a tracked
|
|
162
162
|
* build exists or when a "no active session" guard is about to fire.
|
|
163
163
|
*/
|
|
164
164
|
function artifactDir(root, slug) {
|
|
@@ -166,7 +166,7 @@ function artifactDir(root, slug) {
|
|
|
166
166
|
return resolved === null ? sidecarDir(root) : buildDir(root, resolved);
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
|
-
* The resolved build's artifact folder
|
|
169
|
+
* The resolved build's artifact folder: the `builds/<slug>/` dir for the active
|
|
170
170
|
* build (or the flat sidecar root under `--local`/no-cursor).
|
|
171
171
|
*
|
|
172
172
|
* Public so the plan-approval commit can stage exactly this build's scaffold and
|
|
@@ -176,7 +176,7 @@ export function buildFolder(root, slug) {
|
|
|
176
176
|
return artifactDir(root, slug);
|
|
177
177
|
}
|
|
178
178
|
/**
|
|
179
|
-
* The SPIKE marker
|
|
179
|
+
* The SPIKE marker: a single-purpose presence flag, like SEAM/STEP.
|
|
180
180
|
*
|
|
181
181
|
* Written by `spike` on open, removed on `spike done`. Its existence is the one
|
|
182
182
|
* signal the dashboard and the spike gates read to know "a spike is active".
|
|
@@ -185,39 +185,39 @@ export function spikePath(root, slug) {
|
|
|
185
185
|
return join(artifactDir(root, slug), 'SPIKE');
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
|
-
* SEAM
|
|
188
|
+
* SEAM: the in-flight step's edit grant: a plain list of the paths the step may
|
|
189
189
|
* touch, one per line, so the hooks read it with a grep and no markdown parsing.
|
|
190
190
|
*/
|
|
191
191
|
export function seamPath(root, slug) {
|
|
192
192
|
return join(artifactDir(root, slug), 'SEAM');
|
|
193
193
|
}
|
|
194
194
|
/**
|
|
195
|
-
* STEP
|
|
195
|
+
* STEP: the number of the step in flight, a bare number in a flat file.
|
|
196
196
|
*/
|
|
197
197
|
export function stepPath(root, slug) {
|
|
198
198
|
return join(artifactDir(root, slug), 'STEP');
|
|
199
199
|
}
|
|
200
200
|
/**
|
|
201
|
-
* The build's `checkpoints` ledger
|
|
201
|
+
* The build's `checkpoints` ledger: the recorded SHAs of landed plan and step
|
|
202
202
|
* commits, one line each.
|
|
203
203
|
*/
|
|
204
204
|
export function checkpointsPath(root, slug) {
|
|
205
205
|
return join(artifactDir(root, slug), 'checkpoints');
|
|
206
206
|
}
|
|
207
207
|
/**
|
|
208
|
-
* intent.md
|
|
208
|
+
* intent.md: the build's plan: steps, seams, decisions, done-when.
|
|
209
209
|
*/
|
|
210
210
|
export function intentPath(root, slug) {
|
|
211
211
|
return join(artifactDir(root, slug), 'intent.md');
|
|
212
212
|
}
|
|
213
213
|
/**
|
|
214
|
-
* build-log.md
|
|
214
|
+
* build-log.md: the human-facing build ledger (the Steps mirror and the Log).
|
|
215
215
|
*/
|
|
216
216
|
export function buildLogPath(root, slug) {
|
|
217
217
|
return join(artifactDir(root, slug), 'build-log.md');
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
|
-
* report.md
|
|
220
|
+
* report.md: the close-out report, beside intent.md / build-log.md.
|
|
221
221
|
*
|
|
222
222
|
* The finish skill writes it and `finish` commits it with the folder; the
|
|
223
223
|
* tracked build folder is the archive, so the report rides the branch into the
|
|
@@ -227,7 +227,7 @@ export function reportPath(root, slug) {
|
|
|
227
227
|
return join(artifactDir(root, slug), 'report.md');
|
|
228
228
|
}
|
|
229
229
|
// --- Spike reports: `spike-NN-<slug>.md` files in the build folder, beside
|
|
230
|
-
// report.md. One per spike
|
|
230
|
+
// report.md. One per spike (an explicit `/plumbbob:spike` or a planned `spike:` step)
|
|
231
231
|
// so a build with several forks tells its decision history through the folder
|
|
232
232
|
// listing. NN is CLI-allocated (the human never numbers them), zero-padded for a
|
|
233
233
|
// stable sort. ---
|
|
@@ -251,7 +251,7 @@ export function listSpikeReports(root, slug) {
|
|
|
251
251
|
* sanitized spike slug.
|
|
252
252
|
*
|
|
253
253
|
* One past the highest existing NN, so a gap never reallocates a number. Pure
|
|
254
|
-
* allocation
|
|
254
|
+
* allocation: the caller writes the stamped template there.
|
|
255
255
|
*/
|
|
256
256
|
export function nextSpikeReportPath(root, slug, spikeSlug) {
|
|
257
257
|
const used = listSpikeReports(root, slug).map((name) => Number(name.slice('spike-'.length, 'spike-'.length + 2)));
|
|
@@ -260,10 +260,10 @@ export function nextSpikeReportPath(root, slug, spikeSlug) {
|
|
|
260
260
|
return join(artifactDir(root, slug), `spike-${nn}-${spikeSlug}.md`);
|
|
261
261
|
}
|
|
262
262
|
/**
|
|
263
|
-
* handoff.json
|
|
263
|
+
* handoff.json: the agent-run ledger.
|
|
264
264
|
*
|
|
265
265
|
* Each `agent run` appends its validated envelope here so a later invocation can
|
|
266
|
-
* thread earlier results into the next call's `context[]
|
|
266
|
+
* thread earlier results into the next call's `context[]`: the CLI itself is
|
|
267
267
|
* memoryless between runs, and the file survives context-window compaction where
|
|
268
268
|
* inline stdout does not. It is in-flight control state like STEP/SEAM:
|
|
269
269
|
* untracked (excludeControl), scoped to the current step, cleared when the step
|
|
@@ -276,7 +276,7 @@ export function handoffPath(root, slug) {
|
|
|
276
276
|
* Append one entry to the build's handoff.json.
|
|
277
277
|
*
|
|
278
278
|
* Creates the file (as a JSON array) when absent and tolerates a malformed
|
|
279
|
-
* existing file by starting fresh
|
|
279
|
+
* existing file by starting fresh: a corrupt ledger must never wedge a run.
|
|
280
280
|
* Pretty-printed so it stays readable.
|
|
281
281
|
*/
|
|
282
282
|
export function appendHandoff(root, slug, entry) {
|
|
@@ -295,7 +295,7 @@ export function appendHandoff(root, slug, entry) {
|
|
|
295
295
|
writeFileSync(path, `${JSON.stringify(entries, null, 2)}\n`);
|
|
296
296
|
}
|
|
297
297
|
/**
|
|
298
|
-
* Remove the build's handoff.json
|
|
298
|
+
* Remove the build's handoff.json: the step-scoped ledger clears when the step
|
|
299
299
|
* checkpoints, the same beat that clears STEP/SEAM.
|
|
300
300
|
*
|
|
301
301
|
* An absent file is a no-op (`force`).
|
|
@@ -304,13 +304,13 @@ export function clearHandoff(root, slug) {
|
|
|
304
304
|
rmSync(handoffPath(root, slug), { force: true });
|
|
305
305
|
}
|
|
306
306
|
/**
|
|
307
|
-
* stats.json
|
|
307
|
+
* stats.json: the per-build stats receipt, beside checkpoints.
|
|
308
308
|
*/
|
|
309
309
|
export function statsPath(root, slug) {
|
|
310
310
|
return join(artifactDir(root, slug), 'stats.json');
|
|
311
311
|
}
|
|
312
312
|
/**
|
|
313
|
-
* The whole stats file, or {} when absent or corrupt
|
|
313
|
+
* The whole stats file, or {} when absent or corrupt: malformed contributes
|
|
314
314
|
* nothing.
|
|
315
315
|
*/
|
|
316
316
|
export function readStats(root, slug) {
|
|
@@ -325,14 +325,14 @@ export function readStats(root, slug) {
|
|
|
325
325
|
/**
|
|
326
326
|
* Increment one counter on one step.
|
|
327
327
|
*
|
|
328
|
-
* Best-effort by contract: a failed write is swallowed
|
|
328
|
+
* Best-effort by contract: a failed write is swallowed; the stats are a
|
|
329
329
|
* receipt, never a gate on the verb that accrues them.
|
|
330
330
|
*/
|
|
331
331
|
export function bumpStepStat(root, slug, step, key) {
|
|
332
332
|
patchStepStat(root, slug, step, (current) => ({ [key]: (current[key] ?? 0) + 1 }));
|
|
333
333
|
}
|
|
334
334
|
/**
|
|
335
|
-
* Stamp one timestamp on one step
|
|
335
|
+
* Stamp one timestamp on one step: startedAt at `build <n>`, landedAt at
|
|
336
336
|
* checkpoint.
|
|
337
337
|
*
|
|
338
338
|
* Same best-effort contract as bumpStepStat.
|
|
@@ -362,7 +362,7 @@ function patchStepStat(root, slug, step, patch) {
|
|
|
362
362
|
/**
|
|
363
363
|
* A session exists iff STATE exists.
|
|
364
364
|
*
|
|
365
|
-
* Deleting STATE (at finish) is what flips the repo back to "no session"
|
|
365
|
+
* Deleting STATE (at finish) is what flips the repo back to "no session": the
|
|
366
366
|
* file is the single source of truth for "is there a session". `start` calls
|
|
367
367
|
* beginSession; `finish` removes the file. Existence is the whole session
|
|
368
368
|
* signal; STATE's content is a separate axis (the cursor).
|
|
@@ -381,7 +381,7 @@ export function beginSession(root, slug = null) {
|
|
|
381
381
|
writeCursor(root, slug);
|
|
382
382
|
}
|
|
383
383
|
/**
|
|
384
|
-
* Re-point the cursor at an existing build (`use`)
|
|
384
|
+
* Re-point the cursor at an existing build (`use`): a plain content rewrite
|
|
385
385
|
* that leaves the session sentinel (STATE's existence) intact.
|
|
386
386
|
*
|
|
387
387
|
* Callers guard hasSession first, so this never resurrects a finished session.
|
|
@@ -396,13 +396,13 @@ function writeCursor(root, slug) {
|
|
|
396
396
|
writeFileSync(statePath(root), slug === null ? '' : `${slug}\n`);
|
|
397
397
|
}
|
|
398
398
|
/**
|
|
399
|
-
* True while a spike is open
|
|
399
|
+
* True while a spike is open: the SPIKE marker exists (content is irrelevant).
|
|
400
400
|
*/
|
|
401
401
|
export function inSpike(root, slug) {
|
|
402
402
|
return existsSync(spikePath(root, slug));
|
|
403
403
|
}
|
|
404
404
|
/**
|
|
405
|
-
* Drop the SPIKE marker
|
|
405
|
+
* Drop the SPIKE marker: existence is the whole signal.
|
|
406
406
|
*/
|
|
407
407
|
export function markSpike(root, slug) {
|
|
408
408
|
writeFileSync(spikePath(root, slug), 'active\n');
|
|
@@ -415,20 +415,20 @@ export function clearSpike(root, slug) {
|
|
|
415
415
|
}
|
|
416
416
|
// --- The turn ledger: the checkpoint latch's inputs. `.plumbbob/TURN` is a count
|
|
417
417
|
// of human prompts the model never writes, with a one-turn `.plumbbob/GRANT`
|
|
418
|
-
// beside it
|
|
418
|
+
// beside it: both flat, per-worktree control (never per-build, never committed).
|
|
419
419
|
// `plumbbob turn` (the UserPromptSubmit hook) ticks TURN once per human prompt
|
|
420
420
|
// and rewrites GRANT from the literal prompt; the checkpoint latch reads them to
|
|
421
421
|
// refuse a land until a human turn has intervened since the step was entered.
|
|
422
422
|
// Kept in the sidecar so the lone GRANT `rmSync` stays where deletions of control
|
|
423
423
|
// state are allowed. ---
|
|
424
424
|
/**
|
|
425
|
-
* TURN
|
|
425
|
+
* TURN: the human-turn count the UserPromptSubmit hook maintains.
|
|
426
426
|
*/
|
|
427
427
|
export function turnPath(root) {
|
|
428
428
|
return join(root, DIRNAME, 'TURN');
|
|
429
429
|
}
|
|
430
430
|
/**
|
|
431
|
-
* GRANT
|
|
431
|
+
* GRANT: the one-turn self-approval minted from the human's literal prompt.
|
|
432
432
|
*/
|
|
433
433
|
export function grantPath(root) {
|
|
434
434
|
return join(root, DIRNAME, 'GRANT');
|
|
@@ -437,8 +437,8 @@ export function grantPath(root) {
|
|
|
437
437
|
* Rewrite (or clear) the one-turn GRANT: a non-null value is the minted grant
|
|
438
438
|
* (`auto` | `range M`), null clears it.
|
|
439
439
|
*
|
|
440
|
-
* The turn hook calls this on every human prompt
|
|
441
|
-
* otherwise
|
|
440
|
+
* The turn hook calls this on every human prompt (minted on a match, cleared
|
|
441
|
+
* otherwise) so a grant lives exactly one turn by construction. Pairing the
|
|
442
442
|
* write with its delete here means a grant can never half-persist, and the
|
|
443
443
|
* delete lives where deletions belong.
|
|
444
444
|
*/
|
|
@@ -451,7 +451,7 @@ export function setGrant(root, grant) {
|
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
/**
|
|
454
|
-
* TICK
|
|
454
|
+
* TICK: the per-build entry stamp: the TURN value recorded when work was
|
|
455
455
|
* entered (`build <n>` for a step, `start` for the plan), cleared when
|
|
456
456
|
* `checkpoint` lands.
|
|
457
457
|
*
|
|
@@ -474,7 +474,7 @@ export function stampTick(root, slug) {
|
|
|
474
474
|
writeFileSync(tickPath(root, slug), `${turn}\n`);
|
|
475
475
|
}
|
|
476
476
|
/**
|
|
477
|
-
* Consume the entry stamp
|
|
477
|
+
* Consume the entry stamp: `checkpoint` clears it when a step (or the plan)
|
|
478
478
|
* lands, the same beat that clears STEP/SEAM/handoff.
|
|
479
479
|
*
|
|
480
480
|
* Absent is a no-op; the rmSync lives here with the sidecar's other deletions.
|
|
@@ -486,7 +486,7 @@ export function clearTick(root, slug) {
|
|
|
486
486
|
* The TURN count, or null when the ledger is absent or unreadable.
|
|
487
487
|
*
|
|
488
488
|
* Absence is the "dormant" signal stampTick keys off and the doctor latch probe
|
|
489
|
-
* reports
|
|
489
|
+
* reports: never an error.
|
|
490
490
|
*/
|
|
491
491
|
export function readTurn(root) {
|
|
492
492
|
try {
|
|
@@ -502,7 +502,7 @@ export function readTurn(root) {
|
|
|
502
502
|
* `.plumbbob/STATE` file), or null when there is none.
|
|
503
503
|
*
|
|
504
504
|
* The turn hook runs on every human prompt in every repo; this is the cheap,
|
|
505
|
-
* git-free probe that keeps it a silent no-op outside a live plumbbob session
|
|
505
|
+
* git-free probe that keeps it a silent no-op outside a live plumbbob session:
|
|
506
506
|
* filesystem only, never a host sniff.
|
|
507
507
|
*/
|
|
508
508
|
export function findSessionRoot(cwd) {
|
|
@@ -519,8 +519,8 @@ export function findSessionRoot(cwd) {
|
|
|
519
519
|
/**
|
|
520
520
|
* Append `patterns` to the repo's info/exclude, each at most once.
|
|
521
521
|
*
|
|
522
|
-
* Idempotent
|
|
523
|
-
* to the *common* gitdir's exclude
|
|
522
|
+
* Idempotent: a re-`start` after finish must not double-add. `gitPath` resolves
|
|
523
|
+
* to the *common* gitdir's exclude (the only one git reads) so this works from
|
|
524
524
|
* a linked worktree, whose per-worktree gitdir has no `info/`. info/exclude
|
|
525
525
|
* keeps the exclusion personal machinery, never something imposed on the repo's
|
|
526
526
|
* own tracked ignore file.
|
|
@@ -546,7 +546,7 @@ function addExcludes(root, patterns) {
|
|
|
546
546
|
* Git-exclude the control plane while the artifact plane stays tracked.
|
|
547
547
|
*
|
|
548
548
|
* With `builds/<slug>/` tracked, only the per-worktree control files are
|
|
549
|
-
* excluded
|
|
549
|
+
* excluded: the session sentinel STATE (whose content is the active-build
|
|
550
550
|
* cursor), the personal settings overlay, the turn ledger, and the in-flight
|
|
551
551
|
* markers inside every build. Everything else under `.plumbbob/` (settings.json,
|
|
552
552
|
* and each build's intent/build-log/checkpoints/report) rides the branch into
|
|
@@ -563,11 +563,11 @@ export function excludeControl(root) {
|
|
|
563
563
|
`${DIRNAME}/builds/*/STEP`,
|
|
564
564
|
`${DIRNAME}/builds/*/SEAM`,
|
|
565
565
|
`${DIRNAME}/builds/*/SPIKE`,
|
|
566
|
-
// The entry stamp is in-flight control like STEP/SEAM
|
|
566
|
+
// The entry stamp is in-flight control like STEP/SEAM: checkpoint's
|
|
567
567
|
// stageAll must never sweep it into a step commit.
|
|
568
568
|
`${DIRNAME}/builds/*/TICK`,
|
|
569
569
|
// The agent-run handoff ledger is step-scoped in-flight state, not a
|
|
570
|
-
// tracked artifact
|
|
570
|
+
// tracked artifact: it must never ride a step commit into the PR.
|
|
571
571
|
`${DIRNAME}/builds/*/handoff.json`,
|
|
572
572
|
// The checkride gate writes raw tool output to `.check/`; checkpoint's
|
|
573
573
|
// stageAll must never sweep it into a step commit.
|
|
@@ -575,7 +575,7 @@ export function excludeControl(root) {
|
|
|
575
575
|
]);
|
|
576
576
|
}
|
|
577
577
|
/**
|
|
578
|
-
* `start --local`: opt out of the tracked layout into a fully
|
|
578
|
+
* `start --local`: opt out of the tracked layout into a fully untracked sidecar:
|
|
579
579
|
* some team repos won't accept tool folders in-tree.
|
|
580
580
|
*
|
|
581
581
|
* Excludes the whole `.plumbbob/` directory.
|
|
@@ -586,7 +586,7 @@ export function excludeSidecar(root) {
|
|
|
586
586
|
/**
|
|
587
587
|
* Whether info/exclude already carries `pattern` verbatim.
|
|
588
588
|
*
|
|
589
|
-
* Absent or unreadable reads as "no"
|
|
589
|
+
* Absent or unreadable reads as "no": the caller's job is to write it, not to
|
|
590
590
|
* fail on a repo that has never been excluded.
|
|
591
591
|
*/
|
|
592
592
|
function hasExclude(root, pattern) {
|
|
@@ -603,15 +603,15 @@ function hasExclude(root, pattern) {
|
|
|
603
603
|
* Re-apply the control-plane excludes on the way into a `git add -A`.
|
|
604
604
|
*
|
|
605
605
|
* `start` writes them once, but a plumbbob upgraded mid-build can add a control
|
|
606
|
-
* file the running session's exclude never learned
|
|
607
|
-
* handoff.json all arrived that way
|
|
606
|
+
* file the running session's exclude never learned (TICK, GRANT and
|
|
607
|
+
* handoff.json all arrived that way) and checkpoint/finish stage with `-A`,
|
|
608
608
|
* which would sweep the unexcluded file into the commit and ride it into the PR.
|
|
609
609
|
* Idempotent, so the common path is one read and no write.
|
|
610
610
|
*
|
|
611
|
-
* A fully
|
|
611
|
+
* A fully untracked sidecar is already covered by its blanket `.plumbbob/` line;
|
|
612
612
|
* layering the narrow patterns underneath would only add noise, so skip it. That
|
|
613
613
|
* check reads info/exclude rather than the cursor because a null cursor means
|
|
614
|
-
* "`--local` OR no build named"
|
|
614
|
+
* "`--local` OR no build named", and calling excludeSidecar on the tracked
|
|
615
615
|
* layout would untrack the whole artifact plane.
|
|
616
616
|
*/
|
|
617
617
|
export function refreshExcludes(root) {
|
package/dist/lib/templates.js
CHANGED
|
@@ -9,7 +9,7 @@ import { readFileSync } from 'node:fs';
|
|
|
9
9
|
* Read a template by filename from the package's `templates/` directory.
|
|
10
10
|
*
|
|
11
11
|
* Resolved relative to the compiled module URL, so the same relative hop works
|
|
12
|
-
* from `src/lib/` and `dist/lib/` alike
|
|
12
|
+
* from `src/lib/` and `dist/lib/` alike: the templates ship with the package,
|
|
13
13
|
* never with the consumer's repo.
|
|
14
14
|
*/
|
|
15
15
|
export function readTemplate(name) {
|
package/dist/verbs/agent.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
// `plumbbob agent <subcommand
|
|
1
|
+
// `plumbbob agent <subcommand>`: the doorway to user-authored agents: anything
|
|
2
2
|
// executable that speaks the JSON envelope contract (JSON in on stdin, JSON out
|
|
3
|
-
// on stdout, prose streamed on stderr). `agent list` walks the two agent tiers
|
|
4
|
-
// the repo's tracked `.plumbbob/agents/<name>/`, then the personal
|
|
5
|
-
// `~/.plumbbob/agents/<name>/`
|
|
3
|
+
// on stdout, prose streamed on stderr). `agent list` walks the two agent tiers
|
|
4
|
+
// (the repo's tracked `.plumbbob/agents/<name>/`, then the personal
|
|
5
|
+
// `~/.plumbbob/agents/<name>/`) and prints each resolvable agent. `agent run
|
|
6
6
|
// <name> [--step N] [--mode before|build|after]` composes the StepContext,
|
|
7
7
|
// spawns the manifest command, streams its stderr live, captures and validates
|
|
8
8
|
// the child's envelope, re-emits it on this verb's own stdout (machine) with the
|
|
9
9
|
// human summary on stderr, lands `parked[]` through the build-log, and appends
|
|
10
|
-
// the envelope to the handoff ledger (`builds/<slug>/handoff.json
|
|
10
|
+
// the envelope to the handoff ledger (`builds/<slug>/handoff.json`: untracked,
|
|
11
11
|
// step-scoped, cleared at checkpoint) so later runs can thread it back in.
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// construction, not by policy. A thin read-write shell: resolution,
|
|
12
|
+
// No code path here can checkpoint, flip a step, or chain agents, and that
|
|
13
|
+
// absence is deliberate; the subprocess boundary keeps the human as the clock
|
|
14
|
+
// by construction, not by policy. A thin read-write shell: resolution,
|
|
15
15
|
// composition, and spawn mechanics live in lib/agents.ts.
|
|
16
16
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
17
17
|
import { join } from 'node:path';
|
|
@@ -50,10 +50,10 @@ function list(cwd, _args) {
|
|
|
50
50
|
* Run one named agent, or a slot's harness-bound agents, against the step in flight.
|
|
51
51
|
*
|
|
52
52
|
* With a name (or `--agent` flag) it runs exactly that agent, failing loud on a
|
|
53
|
-
* miss
|
|
53
|
+
* miss; the user who typed the name asked for it specifically. With no name it
|
|
54
54
|
* runs whatever the build's harness.json binds to the requested slot. Either
|
|
55
|
-
* way it composes the StepContext, spawns, and applies side effects
|
|
56
|
-
* advancing the loop.
|
|
55
|
+
* way it composes the StepContext, spawns, and applies side effects (never
|
|
56
|
+
* advancing the loop).
|
|
57
57
|
*/
|
|
58
58
|
async function run(cwd, args) {
|
|
59
59
|
const root = findRepoRoot(cwd);
|
|
@@ -72,7 +72,7 @@ async function run(cwd, args) {
|
|
|
72
72
|
process.stderr.write('plumbbob: no step to run against — pass --step N, or `plumbbob build N` first.\n');
|
|
73
73
|
return 1;
|
|
74
74
|
}
|
|
75
|
-
// A `--agent <path>` flag still needs a name
|
|
75
|
+
// A `--agent <path>` flag still needs a name; it labels an explicit run (the
|
|
76
76
|
// handoff ledger and the human summary key on the name), it does not name one.
|
|
77
77
|
if (parsed.name === undefined && parsed.flagPath !== undefined) {
|
|
78
78
|
process.stderr.write('plumbbob: --agent needs an agent name too, for the run label. Try: plumbbob agent run reviewer --agent ./path --step 3.\n');
|
|
@@ -94,21 +94,21 @@ async function run(cwd, args) {
|
|
|
94
94
|
* One agent's full run: resolve it, pick its slot, compose the StepContext, spawn, report.
|
|
95
95
|
*
|
|
96
96
|
* `ambient` marks a harness-bound run whose resolution or slot mismatch
|
|
97
|
-
* degrades to a warning so a batch keeps going
|
|
97
|
+
* degrades to a warning so a batch keeps going; a binding is ambient
|
|
98
98
|
* configuration the loop must survive without; an explicit ask
|
|
99
99
|
* (`ambient: false`) fails loud on the same miss. A run that actually starts
|
|
100
|
-
* and fails (non-zero exit, timeout, …) is a hard failure either way
|
|
100
|
+
* and fails (non-zero exit, timeout, …) is a hard failure either way: the
|
|
101
101
|
* softening covers a *missing* agent, never a broken one.
|
|
102
102
|
*/
|
|
103
103
|
async function runOne(root, slug, step, spec) {
|
|
104
104
|
const resolution = resolveAgent(root, spec.name, spec.flagPath !== undefined ? { flagPath: spec.flagPath } : {});
|
|
105
105
|
if (!resolution.ok) {
|
|
106
|
-
return degrade(spec.ambient, `plumbbob: ${resolution.error}`, `plumbbob: bound agent "${spec.name}" did not resolve — ${resolution.error} Skipping
|
|
106
|
+
return degrade(spec.ambient, `plumbbob: ${resolution.error}`, `plumbbob: bound agent "${spec.name}" did not resolve — ${resolution.error} Skipping — D54 (bindings-degrade-soft): the loop works without it.`);
|
|
107
107
|
}
|
|
108
108
|
const { manifest, dir } = resolution.agent;
|
|
109
109
|
const resolved = resolveMode(spec.mode, manifest);
|
|
110
110
|
if (!resolved.ok) {
|
|
111
|
-
return degrade(spec.ambient, resolved.error, `plumbbob: bound agent "${spec.name}" — ${resolved.error.replace(/^plumbbob:\s*/, '')} Skipping (
|
|
111
|
+
return degrade(spec.ambient, resolved.error, `plumbbob: bound agent "${spec.name}" — ${resolved.error.replace(/^plumbbob:\s*/, '')} Skipping — D54 (bindings-degrade-soft).`);
|
|
112
112
|
}
|
|
113
113
|
const mode = resolved.mode;
|
|
114
114
|
let intent;
|
|
@@ -126,17 +126,17 @@ async function runOne(root, slug, step, spec) {
|
|
|
126
126
|
mode,
|
|
127
127
|
settings: {
|
|
128
128
|
// The envelope reports the `auto` setting to agents as information only.
|
|
129
|
-
// The checkpoint latch never reads it
|
|
129
|
+
// The checkpoint latch never reads it; a model can write a settings
|
|
130
130
|
// file, so a standing `auto` cannot be a self-approval grant; approval
|
|
131
131
|
// comes only from what the human literally typed. A `true` here informs
|
|
132
132
|
// an agent, it does not self-approve.
|
|
133
133
|
auto: resolveBoolean(root, 'auto', false),
|
|
134
134
|
agentTimeout: resolveNumber(root, 'agentTimeout', 0),
|
|
135
135
|
// Hand this agent its own config block over the envelope's existing
|
|
136
|
-
// `settings` field
|
|
136
|
+
// `settings` field: no new envelope field, no new verb; the config just
|
|
137
137
|
// rides here. Resolution: settings.json's agentConfig[name], with the
|
|
138
138
|
// untracked personal overlay (settings.local.json) replacing the project
|
|
139
|
-
// entry whole
|
|
139
|
+
// entry whole (no deep merge) and {} when neither defines it.
|
|
140
140
|
agent: resolveRecord(root, 'agentConfig')[spec.name] ?? {},
|
|
141
141
|
},
|
|
142
142
|
});
|
|
@@ -159,7 +159,7 @@ async function runOne(root, slug, step, spec) {
|
|
|
159
159
|
/**
|
|
160
160
|
* No name given: run the agents the build's harness.json binds to the requested slot.
|
|
161
161
|
*
|
|
162
|
-
* Bindings merge as a ladder
|
|
162
|
+
* Bindings merge as a ladder: the per-step harness entry beats the harness
|
|
163
163
|
* `defaults`, which beat the settings-level `agents` key; the first level that
|
|
164
164
|
* names the slot wins, replace not append. A missing bound agent degrades to a
|
|
165
165
|
* warning; an absent harness, or one that binds nothing to this slot, is a
|
|
@@ -216,7 +216,7 @@ function degrade(ambient, hard, soft) {
|
|
|
216
216
|
*
|
|
217
217
|
* A value flag missing its value, or `--step` given a non-number, is a loud
|
|
218
218
|
* error (returned as a string) rather than a silent default. An undeclared
|
|
219
|
-
* `--flag` never arrives here
|
|
219
|
+
* `--flag` never arrives here; `run` screens argv against the verb spec in
|
|
220
220
|
* cli-core.ts and refuses before dispatch.
|
|
221
221
|
*/
|
|
222
222
|
function parseRunArgs(args) {
|
|
@@ -254,7 +254,7 @@ function parseRunArgs(args) {
|
|
|
254
254
|
/**
|
|
255
255
|
* Resolve the slot the agent runs in.
|
|
256
256
|
*
|
|
257
|
-
* An explicit `--mode` must name a real slot AND one the manifest declares
|
|
257
|
+
* An explicit `--mode` must name a real slot AND one the manifest declares;
|
|
258
258
|
* an undeclared slot is refused loud, because the user asked for that exact
|
|
259
259
|
* run. With no `--mode`, a single-slot agent uses its only slot; a multi-slot
|
|
260
260
|
* agent must be told which one (harness bindings pick the slot from the
|
|
@@ -282,11 +282,11 @@ function resolveMode(flag, manifest) {
|
|
|
282
282
|
/**
|
|
283
283
|
* Map the run outcome to reporting and side effects.
|
|
284
284
|
*
|
|
285
|
-
* A failed run
|
|
286
|
-
* shell that never started
|
|
285
|
+
* A failed run (non-zero exit, out-of-contract stdout, timeout, interrupt, or a
|
|
286
|
+
* shell that never started) reports on stderr and stops with NO side effects:
|
|
287
287
|
* the envelope of a failed child is not authoritative. A clean run lands
|
|
288
288
|
* `parked[]`, records the envelope in the handoff ledger, prints the human
|
|
289
|
-
* summary on stderr, and re-emits the machine envelope on stdout
|
|
289
|
+
* summary on stderr, and re-emits the machine envelope on stdout, nothing else,
|
|
290
290
|
* ever (the stream discipline: stdout carries the envelope alone).
|
|
291
291
|
*/
|
|
292
292
|
function report(root, slug, name, mode, step, result) {
|
|
@@ -321,7 +321,7 @@ function failureLine(name, result) {
|
|
|
321
321
|
/**
|
|
322
322
|
* Land each parked concern through the build-log's Park list.
|
|
323
323
|
*
|
|
324
|
-
* The agent never writes .plumbbob/ itself
|
|
324
|
+
* The agent never writes .plumbbob/ itself; parked lines only reach the
|
|
325
325
|
* build-log through this verb. A build-log with no "## Park list" section, or no
|
|
326
326
|
* build-log at all, warns once rather than losing the lines silently.
|
|
327
327
|
*/
|
|
@@ -351,7 +351,7 @@ function applyParked(root, slug, parked) {
|
|
|
351
351
|
/**
|
|
352
352
|
* The human-facing summary printed on stderr.
|
|
353
353
|
*
|
|
354
|
-
* A headline plus, for a halt, the route out
|
|
354
|
+
* A headline plus, for a halt, the route out: `blocked` unblocks and re-runs,
|
|
355
355
|
* `drift` sends the plan to /plumbbob:refine. The machine envelope on stdout carries
|
|
356
356
|
* the same status for the calling skill; this stderr copy is the terminal read.
|
|
357
357
|
*/
|
package/dist/verbs/build.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// `plumbbob build <n
|
|
1
|
+
// `plumbbob build <n>`: enter a step: read step n's seam from intent.md and
|
|
2
2
|
// write the normalized SEAM + STEP control files (flat, untracked, per-build).
|
|
3
|
-
// The seam is the step's edit grant
|
|
3
|
+
// The seam is the step's edit grant: the exact paths and `dir/` prefixes the
|
|
4
4
|
// step expects to touch, for orientation, never a lock. The STEP file is the
|
|
5
5
|
// in-flight signal (the dashboard derives the BUILD phase from it); this verb
|
|
6
|
-
// never checkpoints
|
|
6
|
+
// never checkpoints; only `checkpoint` commits.
|
|
7
7
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
8
8
|
import { findRepoRoot } from "../lib/git.js";
|
|
9
9
|
import { hasSession, intentPath, resolveBuild, seamPath, stampStepStat, stampTick, stepPath } from "../lib/sidecar.js";
|
|
@@ -11,7 +11,7 @@ import { parseStepSeam } from "../lib/intent.js";
|
|
|
11
11
|
import { parseSteps } from "../lib/orient.js";
|
|
12
12
|
import { stepLabel, syncBuildLogState } from "../lib/buildlogsync.js";
|
|
13
13
|
/**
|
|
14
|
-
* Enter step n
|
|
14
|
+
* Enter step n (or the next undone step) and write its SEAM/STEP markers.
|
|
15
15
|
*
|
|
16
16
|
* Refuses a missing session, a malformed step argument, an `N-M` range (a
|
|
17
17
|
* `/plumbbob:build` skill feature, not a CLI one), and a seam that fails to parse.
|
|
@@ -27,13 +27,13 @@ export function build(cwd, args) {
|
|
|
27
27
|
const { build: slug, rest } = resolveBuild(root, args);
|
|
28
28
|
const raw = rest.find((a) => !a.startsWith('--'));
|
|
29
29
|
// A step range like `1-3` is a `/plumbbob:build` skill affordance (auto-approve
|
|
30
|
-
// through the range, then pause), not a CLI capability
|
|
30
|
+
// through the range, then pause), not a CLI capability: the CLI records one
|
|
31
31
|
// in-flight step at a time. Name it rather than bounce off the generic usage.
|
|
32
32
|
if (raw !== undefined && /^\d+-\d*$/.test(raw)) {
|
|
33
33
|
process.stderr.write(`plumbbob: build takes one step number; \`${raw}\` step ranges are a \`/plumbbob:build\` feature (auto-approve through the range, then pause). Try \`plumbbob build ${raw.split('-')[0]}\`.\n`);
|
|
34
34
|
return 1;
|
|
35
35
|
}
|
|
36
|
-
// An explicit arg that isn't a positive integer is a usage error
|
|
36
|
+
// An explicit arg that isn't a positive integer is a usage error: caught before
|
|
37
37
|
// reading intent.md so the message doesn't depend on the plan being present.
|
|
38
38
|
if (raw !== undefined && (!/^\d+$/.test(raw) || Number(raw) < 1)) {
|
|
39
39
|
process.stderr.write('plumbbob: build needs a step number. Try: plumbbob build 2.\n');
|
|
@@ -65,14 +65,14 @@ export function build(cwd, args) {
|
|
|
65
65
|
writeFileSync(stepPath(root, slug), `${step}\n`);
|
|
66
66
|
// Stamp the turn ledger on entry (TICK = TURN): the checkpoint latch demands
|
|
67
67
|
// a human turn after this point before the step may land. Skipped when the
|
|
68
|
-
// ledger is dormant
|
|
68
|
+
// ledger is dormant: a host with no hooks grows no TURN/TICK files.
|
|
69
69
|
stampTick(root, slug);
|
|
70
70
|
// The wall-clock receipt starts here: checkpoint stamps landedAt, and the
|
|
71
71
|
// pair becomes the step's duration in the finish report.
|
|
72
72
|
stampStepStat(root, slug, step, 'startedAt', new Date().toISOString());
|
|
73
73
|
// The build-log's top half is CLI-owned so it never lies: show this step in
|
|
74
|
-
// flight
|
|
75
|
-
// Best-effort
|
|
74
|
+
// flight: Current step plus the ☐/☑ mirror, re-rendered from intent.md.
|
|
75
|
+
// Best-effort: a missing or hand-edited build-log never blocks the build.
|
|
76
76
|
const title = parseSteps(intent).find((s) => s.n === step)?.title ?? null;
|
|
77
77
|
syncBuildLogState(root, slug, stepLabel(step, title));
|
|
78
78
|
const picked = raw === undefined ? ' (next undone)' : '';
|