@juzser/blacksmith 0.1.1 → 0.2.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/agents/planner.md +21 -0
- package/.claude/skills/bs/SKILL.md +35 -19
- package/.claude/skills/bs/audit.md +4 -2
- package/.claude/skills/bs/dispatch.md +5 -5
- package/.claude/skills/bs/new.md +4 -2
- package/.claude/skills/bs/plan.md +22 -5
- package/.claude/skills/bs/report.md +3 -3
- package/.claude/skills/bs/run.md +6 -4
- package/.claude/skills/bs/ui.md +10 -3
- package/.claude/skills/bs/wave.md +13 -7
- package/README.md +56 -53
- package/factory/orchestrator/dist/claims.js +27 -1
- package/factory/orchestrator/dist/cli.js +94 -25
- package/factory/orchestrator/dist/daemon.js +16 -5
- package/factory/orchestrator/dist/events.js +50 -0
- package/factory/orchestrator/dist/harness.js +233 -84
- package/factory/orchestrator/dist/impact.js +76 -5
- package/factory/orchestrator/dist/paths.js +2 -0
- package/factory/orchestrator/dist/plan.js +60 -0
- package/factory/orchestrator/dist/providers/cli-transport.js +7 -73
- package/factory/orchestrator/dist/run-cli.js +130 -0
- package/factory/orchestrator/dist/runner.js +264 -0
- package/factory/orchestrator/dist/spawn.js +84 -0
- package/factory/orchestrator/dist/usage.js +8 -6
- package/factory/orchestrator/dist/waveNext.js +42 -14
- package/factory/orchestrator/dist/waveSchedule.js +7 -1
- package/factory/policies/budgets.yml +1 -1
- package/factory/policies/harness.yml +82 -0
- package/factory/policies/severity.yml +1 -1
- package/factory/specs/schema/task-spec.schema.json +5 -0
- package/package.json +3 -2
|
@@ -58,6 +58,27 @@ types, config, shared fixtures) are never split across concurrent tasks —
|
|
|
58
58
|
mark them `serialize-always` and give overlapping tasks a dependency edge
|
|
59
59
|
instead of concurrent claims.
|
|
60
60
|
|
|
61
|
+
Before signing, read the slicing back from the project, not from the plan:
|
|
62
|
+
`smith claims impact --plan <plan.json> <every task id> --repo <project-dir>`
|
|
63
|
+
names each import edge that crosses between tasks the plan gave no
|
|
64
|
+
dependency, and `smith wave schedule <plan.json> --repo <project-dir>` says
|
|
65
|
+
how wide the plan can ever run. Every crossing the first reports is one of
|
|
66
|
+
three things, and the plan says which:
|
|
67
|
+
|
|
68
|
+
- a **dependency edge** — the consumer needs the producer's *new* export, so
|
|
69
|
+
the producer runs first;
|
|
70
|
+
- a **`keeps_exports` promise** on the producer — it only *adds* to that file
|
|
71
|
+
or leaves its exports alone, so both run now and the diff is checked
|
|
72
|
+
against the promise post-run;
|
|
73
|
+
- a **re-slice**.
|
|
74
|
+
|
|
75
|
+
Two shapes cross by construction, so do not draw them: a module's tests split
|
|
76
|
+
from the module (a `src/x.ts` task and a `test/x.test.ts` task always cross),
|
|
77
|
+
and a doc hub (runbook, README) claimed by one task and edited by many —
|
|
78
|
+
each task edits its own section inside its own claims, or one docs task takes
|
|
79
|
+
edges from the others. Target `widest >= 2` when the plan has two or more
|
|
80
|
+
tasks, or say in `planner_notes` why the work is a chain.
|
|
81
|
+
|
|
61
82
|
## Living spec doc (architecture §11)
|
|
62
83
|
|
|
63
84
|
You own the epic's living spec markdown: every worker reads it before
|
|
@@ -1,33 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bs
|
|
3
|
-
description: Operator console for the Blacksmith factory — invoke as `/bs <subcommand>` (new, plan, run, audit, status, ui, waivers, lessons, report) to scaffold a project, plan or drive an epic through the loop, audit a project on four axes and cut an epic from what the operator accepts, check live status, open the dashboard, answer a waiver batch, triage lesson candidates, or get a progress digest. This file routes; each subcommand's playbook is a sibling file read when that verb runs. Use this whenever the operator wants to interact with Blacksmith itself, from
|
|
3
|
+
description: Operator console for the Blacksmith factory — invoke as `/bs <subcommand>` (new, plan, run, audit, status, ui, waivers, lessons, report) to scaffold a project, plan or drive an epic through the loop, audit a project on four axes and cut an epic from what the operator accepts, check live status, open the dashboard, answer a waiver batch, triage lesson candidates, or get a progress digest. This file routes; each subcommand's playbook is a sibling file read when that verb runs. Use this whenever the operator wants to interact with Blacksmith itself, from any Claude Code session that has Blacksmith installed — a clone of the repo, or the plugin plus the `smith` CLI from npm.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /bs — Blacksmith operator console
|
|
7
7
|
|
|
8
8
|
You (the orchestrator session running this skill) are the human's one interface
|
|
9
9
|
to the factory. Every subcommand below is a **playbook**, not a script: the
|
|
10
|
-
deterministic mechanics run through the real `smith` CLI
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
planning, spec review, coding, testing,
|
|
14
|
-
sessions you dispatch
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
deterministic mechanics run through the real `smith` CLI, spelled `smith` in
|
|
11
|
+
every command below — on PATH from `npm i -g @juzser/blacksmith`, or inside a
|
|
12
|
+
clone `node factory/orchestrator/dist/cli.js` instead, `pnpm build` first if
|
|
13
|
+
`dist/` is stale. The judgment steps — planning, spec review, coding, testing,
|
|
14
|
+
reviewing — are separate Claude Code sessions you dispatch by role, each under
|
|
15
|
+
the contract in its own agent template beside this skill
|
|
16
|
+
(`.claude/agents/<role>.md`). **This skill never calls an LLM directly and
|
|
17
|
+
never embeds a role prompt** — the templates own that; duplicating them here
|
|
18
|
+
would let this file drift out of sync with the real contracts. Cite policy
|
|
19
|
+
files (`factory/policies/*.yml`) rather than restating their numbers.
|
|
19
20
|
|
|
20
21
|
**The project directory is an answer you ask for, not a path this file
|
|
21
22
|
knows.** A project this factory builds is not part of it: `smith new` puts one
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
*outside* the factory when no `--target-dir` says otherwise (beside the clone
|
|
24
|
+
if you run one, in the directory you ran an installed `smith` from), and
|
|
25
|
+
nothing downstream reads that location — `workspaces/` inside the repo is
|
|
26
|
+
still a legal answer, just no longer the assumed one. Every verb that touches
|
|
27
|
+
the project's git takes the directory itself: `<project-dir>` as a positional
|
|
28
|
+
on the `worktree` family, `--project <dir>` on everything else. A task's
|
|
29
|
+
worktrees are placed beside whatever directory it was handed, so a project
|
|
30
|
+
outside this repo keeps its worktrees outside it too (`AGENTS.md` "Worktrees",
|
|
31
|
+
a clone-only file — see "Where the files below live"). Ask for it once, at the
|
|
32
|
+
top of a run, and carry that one answer through every command below —
|
|
33
|
+
`<project-dir>` here means that answer, never a fixed path.
|
|
34
|
+
|
|
35
|
+
**Where the files below live.** This skill cites three kinds of path and they
|
|
36
|
+
do not all resolve the same way. Playbooks and agent templates are beside this
|
|
37
|
+
file and always resolve. `factory/policies/*.yml`, the JSON Schemas and the
|
|
38
|
+
scaffold are **read-only assets the CLI ships**: in a clone they are in the
|
|
39
|
+
checkout, and in an install they are inside the package — ask `smith` rather
|
|
40
|
+
than guessing, since `smith init` prints `repoRoot` (where they are) and
|
|
41
|
+
`workRoot` (where everything written goes) and is safe to re-run, keeping any
|
|
42
|
+
file it already seeded. `docs/` and `AGENTS.md` are **clone-only**: they are in
|
|
43
|
+
the repository and in no install, so a citation to one is a pointer for an
|
|
44
|
+
operator who has the repo, never a file to assume you can open. Read, do not
|
|
45
|
+
assume — and when a cited file is not there, say so instead of substituting a
|
|
46
|
+
remembered number.
|
|
31
47
|
|
|
32
48
|
Every write command needs an event-log envelope: `--session <id>
|
|
33
49
|
--plan-version <n> --causal-parent <event-id> [--actor operator]`. Open a
|
|
@@ -19,8 +19,10 @@ the `audit_id` the way you keep a session id.
|
|
|
19
19
|
nothing reads a cwd and nothing keeps a "current audit" on the side, so two
|
|
20
20
|
audits on two projects cannot be confused. The project must be a git
|
|
21
21
|
checkout; a directory this factory did not build is as auditable as one it
|
|
22
|
-
did. Then `mkdir -p state/audit` in
|
|
23
|
-
|
|
22
|
+
did. Then `mkdir -p state/audit` in the factory's **work root** — the clone
|
|
23
|
+
itself, or the `.blacksmith/` that `smith init` made beside your code; `smith
|
|
24
|
+
init` prints it as `workRoot`. The judges' artifacts live there, under runtime
|
|
25
|
+
state, never under the project.
|
|
24
26
|
|
|
25
27
|
## 2. Open the audit
|
|
26
28
|
|
|
@@ -140,11 +140,11 @@ memory of its previous attempt and will happily report round 1 forever.
|
|
|
140
140
|
evidence that the model was too small; it is usually evidence the spec was
|
|
141
141
|
unclear, and a bigger model will implement the same misreading more
|
|
142
142
|
convincingly.
|
|
143
|
-
- **Escalate to
|
|
144
|
-
the `dispatch_decision` (`model_tier`, `model`, plus the
|
|
145
|
-
escalation is a cost you cannot attribute later — and
|
|
146
|
-
cannot even tell you *which* frontier model you escalated
|
|
147
|
-
fable share the tier.
|
|
143
|
+
- **Escalate to the frontier tier only after two failed rounds**, and log the
|
|
144
|
+
escalation in the `dispatch_decision` (`model_tier`, `model`, plus the
|
|
145
|
+
reason). An unlogged escalation is a cost you cannot attribute later — and
|
|
146
|
+
`model_tier` alone cannot even tell you *which* frontier model you escalated
|
|
147
|
+
to, since opus and fable share the tier.
|
|
148
148
|
- **The grader's 2-round cap is a hard stop, not a ladder rung.** At a round-2
|
|
149
149
|
`fail` the task goes back to the planner for re-scoping. Do not re-dispatch
|
|
150
150
|
the grader, and do not escalate the *grader* — the gates decide pass/fail,
|
package/.claude/skills/bs/new.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# `/bs new <project> [--ui]` — scaffold a target project
|
|
2
2
|
|
|
3
3
|
1. Ask where the project should live, then run `smith new <project> [--ui]
|
|
4
|
-
[--target-dir <dir>]`. Without `--target-dir` it lands beside
|
|
5
|
-
`<repo-parent>/<project
|
|
4
|
+
[--target-dir <dir>]`. Without `--target-dir` it lands beside the clone, in
|
|
5
|
+
`<repo-parent>/<project>` — or, when `smith` is an installed package rather
|
|
6
|
+
than a clone, in the directory you run it from, never inside the package.
|
|
7
|
+
Either way that is a default rather than a requirement —
|
|
6
8
|
the answer becomes the `<project-dir>` every later command takes, so take
|
|
7
9
|
it here rather than assuming it six steps in. The call
|
|
8
10
|
copies `factory/scaffold/` (TS strict, Biome, Vitest, CI) and layers
|
|
@@ -37,8 +37,8 @@ there means "not looked at", not "looked at and clean".
|
|
|
37
37
|
guessed (architecture §5) — dispatch `researcher`
|
|
38
38
|
(`.claude/agents/researcher.md`) for an unfamiliar repo, or do it
|
|
39
39
|
directly with Grep/Read for a small one.
|
|
40
|
-
2. Dispatch a **`planner`** session (`.claude/agents/planner.md`,
|
|
41
|
-
|
|
40
|
+
2. Dispatch a **`planner`** session (`.claude/agents/planner.md`, frontier
|
|
41
|
+
tier) with the goal + the claims analysis. It drafts the epic spec and
|
|
42
42
|
task specs (`factory/specs/schema/task-spec.schema.json` shape).
|
|
43
43
|
3. Dispatch a **`spec-reviewer`** session (`.claude/agents/spec-reviewer.md`)
|
|
44
44
|
— a *different model* than the planner's, per its own frontmatter — to
|
|
@@ -117,9 +117,26 @@ there means "not looked at", not "looked at and clean".
|
|
|
117
117
|
can pay it cheaply; a `widest: 1` plan serializes the whole epic while
|
|
118
118
|
every gate downstream reports a healthy wave of one. Deferrals for
|
|
119
119
|
`dependency-pending` never appear there, because a chain of real
|
|
120
|
-
dependencies is the shape of the work and not a defect. Exit 2
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
dependencies is the shape of the work and not a defect. Exit 2 whose
|
|
121
|
+
`constraints` name `symbol-coupled` is a **re-plan round**, not
|
|
122
|
+
information: hand the planner the constraint list — the file pairs from
|
|
123
|
+
the deferral detail — and ask, per pair, for a dependency edge, a
|
|
124
|
+
`keeps_exports` promise on the producer, or a re-slice.
|
|
125
|
+
`claim-overlap` / `serialize-hotspot` constraints are the re-slice case
|
|
126
|
+
above. Exit 2 with no such constraint remains information, not a stop —
|
|
127
|
+
decide whether to re-slice, and say which you chose when you present the
|
|
128
|
+
plan.
|
|
129
|
+
|
|
130
|
+
Beside the schedule, the read that names what it only counts:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
smith claims impact --plan factory/specs/active/<epic>/plan-v1.json \
|
|
134
|
+
<task-id>... --repo <project-dir>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Its `crossings` are the file pairs a `symbol-coupled` constraint points
|
|
138
|
+
at; `promised` holds the ones a producer's `keeps_exports` already
|
|
139
|
+
answers, which the schedule admits and the diff is later checked against.
|
|
123
140
|
7. Log the sign-off itself as a decision checkpoint (this is exactly what
|
|
124
141
|
`smith dream`'s "plan sign-off" extraction looks for, `lessons.ts`):
|
|
125
142
|
`smith event append '{"session_id":"...","actor":"operator",
|
|
@@ -9,13 +9,13 @@ pending proposals lets them sit invisible instead of surfacing them.
|
|
|
9
9
|
|
|
10
10
|
1. **Ask what the factory is answerable for, then run the scheduler.**
|
|
11
11
|
`smith projects list` reads the roadmap's `- project:` bullets — the
|
|
12
|
-
register `smith new` writes to — and prints
|
|
12
|
+
register `smith new` writes to — and prints the factory itself plus every
|
|
13
13
|
project it built that has a checkout, ending in the `--project` line to
|
|
14
14
|
pass on. Do not assemble that list from memory: the maintenance pass
|
|
15
15
|
reads one lockfile per flag and reports one proposal per repo, so a repo
|
|
16
16
|
left off the line is not reported as missing, it is simply never
|
|
17
17
|
mentioned. (The daemon raises `unwatched-project` for a child project with
|
|
18
|
-
a checkout that is left off the line — not for
|
|
18
|
+
a checkout that is left off the line — not for the factory itself, which is in
|
|
19
19
|
the pass by default, and not for a declared project with no checkout at
|
|
20
20
|
all, which `smith projects list` marks with `?` instead of leaving it
|
|
21
21
|
off the printed list. The two surfaces no longer answer the same
|
|
@@ -97,7 +97,7 @@ pending proposals lets them sit invisible instead of surfacing them.
|
|
|
97
97
|
check before reporting it as a stall. A `partial` verdict is **not** a
|
|
98
98
|
finding; put `widest` in the digest as the one number that says how wide
|
|
99
99
|
this factory has ever actually run.
|
|
100
|
-
6. Dispatch **`scribe`** (`.claude/agents/scribe.md`,
|
|
100
|
+
6. Dispatch **`scribe`** (`.claude/agents/scribe.md`, small tier) with that
|
|
101
101
|
JSON plus step 2's admissions as input: shipped / in-flight / blocked /
|
|
102
102
|
budget burn / next milestone / **N rechecks pending, M maintenance
|
|
103
103
|
bumps auto-schedulable** (never silently dropped), ≤300 words, linking
|
package/.claude/skills/bs/run.md
CHANGED
|
@@ -53,10 +53,12 @@ file asked for would be running it below the floor.
|
|
|
53
53
|
- The same verdict carries `symbolImpact`. `valid: true` with
|
|
54
54
|
`symbolImpact.status: "coupled"` means the claims really are disjoint
|
|
55
55
|
and that is not enough: the two tasks sit on either end of an import
|
|
56
|
-
edge the plan declared no dependency for.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
edge the plan declared no dependency for. A crossing is not
|
|
57
|
+
overridable. The pair runs in the same wave only when the producer's
|
|
58
|
+
spec lists the exporting file in `keeps_exports` — then the crossing
|
|
59
|
+
sits in `symbolImpact.promised`, not `crossings`, and the promise is
|
|
60
|
+
verified against the diff at wave.md step 10. Otherwise split the wave
|
|
61
|
+
and run the producer first (operator-guide/wave.md §2).
|
|
60
62
|
- `wave check` is also where the wave is priced and where
|
|
61
63
|
`max_in_flight_tasks` is enforced (`budgets.yml`, `over-fan-out`).
|
|
62
64
|
`wave next` deliberately asks neither question: a proposer that also
|
package/.claude/skills/bs/ui.md
CHANGED
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
`smith ui serve [--port 4680] [--db state/smith.db] [--state-dir …]
|
|
4
4
|
[--roadmap-path …]`. If it errors `ui.not-built`, run `pnpm build:ui`
|
|
5
|
-
first (builds `ui/server/dist` + `ui/dist`), then retry
|
|
5
|
+
first (builds `ui/server/dist` + `ui/dist`), then retry, and print the local
|
|
6
6
|
URL (`http://127.0.0.1:<port>`).
|
|
7
7
|
|
|
8
|
+
**That build needs a clone.** `ui/` is not in the package's `files`, so an
|
|
9
|
+
install — npm or plugin — ships no dashboard at all and `ui.not-built` is the
|
|
10
|
+
permanent answer there, not a stale build. Check before you advise: if there
|
|
11
|
+
is no `ui/` beside the `smith` you are running, say the dashboard is
|
|
12
|
+
clone-only and stop, rather than sending the operator to a `pnpm` script they
|
|
13
|
+
do not have.
|
|
14
|
+
|
|
8
15
|
`--roadmap-path` travels with `--db`/`--state-dir`: the read path
|
|
9
16
|
re-projects each session on the first request, and that rebuilds the
|
|
10
17
|
milestones table from the roadmap file it was given — unset, it falls
|
|
11
|
-
back to
|
|
12
|
-
pointed at another project's db would show
|
|
18
|
+
back to Blacksmith's own `factory/specs/roadmap.md`, so a dashboard
|
|
19
|
+
pointed at another project's db would show the factory's roadmap.
|
|
@@ -122,7 +122,10 @@ one thing this playbook never asks you to.
|
|
|
122
122
|
4. Dispatch **`coder`** (`.claude/agents/coder.md`) in that worktree.
|
|
123
123
|
Token/diff caps (`budgets.yml`: 150k tokens, 400 diff lines) and YAGNI
|
|
124
124
|
are the coder's own constraints — don't restate them here, the template
|
|
125
|
-
does.
|
|
125
|
+
does. What the template does not read is the schema, so when the spec
|
|
126
|
+
carries `keeps_exports`, restate the promise in the dispatch in one line:
|
|
127
|
+
the file list, and "keep every existing export's name and declaration;
|
|
128
|
+
adding is fine".
|
|
126
129
|
5. Dispatch **`tester`** (`.claude/agents/tester.md`) for missing unit
|
|
127
130
|
coverage and epic-level e2e/screenshots.
|
|
128
131
|
- Then the **uiux visual pass**, but only when all three hold: the task
|
|
@@ -207,7 +210,7 @@ one thing this playbook never asks you to.
|
|
|
207
210
|
this is the timeline, not optional bookkeeping.
|
|
208
211
|
9. Gate outcome `blocked` → bounce to the coder on the **same branch**.
|
|
209
212
|
After 2 failed rounds on the same task, escalate model tier
|
|
210
|
-
automatically (
|
|
213
|
+
automatically (mid → frontier, logged — `budgets.yml`
|
|
211
214
|
`escalation_ladder`); after 3, escalate to the operator. Never skip a
|
|
212
215
|
rung, never loop past one. Count the rounds from this task's
|
|
213
216
|
`dispatch_decision` events, not from the agent's own account of itself
|
|
@@ -215,13 +218,16 @@ one thing this playbook never asks you to.
|
|
|
215
218
|
you: `smith escalation check <session-id> --task <task-id>`, which
|
|
216
219
|
exits 1 if the rung you just climbed is not evidenced.
|
|
217
220
|
10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting,
|
|
218
|
-
ask what the diff did to everyone outside the claims
|
|
221
|
+
ask what the diff did to everyone outside the claims, and whether it
|
|
222
|
+
kept what the spec promised:
|
|
219
223
|
`smith claims impact <worktree-dir> <spec.json>`. Exit 1 means a
|
|
220
224
|
`proven` break — this task removed an export a file outside its claims
|
|
221
|
-
still imports —
|
|
222
|
-
`
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
still imports — **or a broken promise**: a file the spec's
|
|
226
|
+
`keeps_exports` names lost an export or changed one's declaration.
|
|
227
|
+
Either is a bounce to the coder, not a merge; quote the `promises`
|
|
228
|
+
entries in the bounce. A `possible` / `signature-changed` entry in
|
|
229
|
+
`breaks` exits 0 and is a note: the scanner reads text, not types
|
|
230
|
+
(operator-guide/wave.md §2). Then admit into the
|
|
225
231
|
merge queue: `smith queue run <epic> --project <project-dir>
|
|
226
232
|
--test-cmd "<cumulative test command>" --tasks tasks.json`. On a
|
|
227
233
|
`rebase-conflict` outcome, dispatch **`merger`**
|
package/README.md
CHANGED
|
@@ -151,71 +151,72 @@ a chat transcript.
|
|
|
151
151
|
|
|
152
152
|
## Install
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
Say **"install Blacksmith"** to a Claude Code session and it does the whole
|
|
155
|
+
thing: [`INSTALL.md`](INSTALL.md) is an executable runbook, and it stops to ask
|
|
156
|
+
before anything that touches your machine.
|
|
156
157
|
|
|
157
|
-
|
|
158
|
+
By hand it is one command in a shell:
|
|
158
159
|
|
|
159
160
|
```bash
|
|
160
|
-
|
|
161
|
-
pnpm install --frozen-lockfile
|
|
162
|
-
pnpm run build # tsc -> factory/orchestrator/dist/
|
|
163
|
-
node factory/orchestrator/dist/cli.js --help
|
|
161
|
+
npm i -g @juzser/blacksmith
|
|
164
162
|
```
|
|
165
163
|
|
|
166
|
-
|
|
164
|
+
and two inside Claude Code:
|
|
167
165
|
|
|
168
|
-
```
|
|
169
|
-
|
|
166
|
+
```
|
|
167
|
+
/plugin marketplace add juzser/blacksmith
|
|
168
|
+
/plugin install blacksmith@blacksmith
|
|
170
169
|
```
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
Then `smith init` in the project you want it to work on. That creates
|
|
172
|
+
`.blacksmith/` beside your code — the event log, your epic plans, a roadmap and
|
|
173
|
+
a `stack.yml` to answer — and writes nothing anywhere else; `SMITH_HOME` moves
|
|
174
|
+
that root if you want one home for several projects. You now have `/bs`.
|
|
176
175
|
|
|
177
|
-
**
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
**Both halves are required.** The package
|
|
177
|
+
([`@juzser/blacksmith`](https://www.npmjs.com/package/@juzser/blacksmith)) is the
|
|
178
|
+
deterministic `smith` CLI; the plugin is `/bs` and the fourteen agent roles it
|
|
179
|
+
dispatches. `smith` alone never gives you `/bs`, because Claude Code loads
|
|
180
|
+
skills from a project's `.claude/`, your `~/.claude/`, or a plugin — never from
|
|
181
|
+
`node_modules` — and a `/bs` with no `smith` on PATH can run nothing. Take
|
|
182
|
+
`latest`: `0.1.0` predates `smith init` and keeps state inside its own install
|
|
183
|
+
directory, which the next `npm i` replaces.
|
|
182
184
|
|
|
183
|
-
|
|
185
|
+
<details>
|
|
186
|
+
<summary><b>A clone instead — the whole factory</b></summary>
|
|
184
187
|
|
|
185
|
-
|
|
186
|
-
|
|
188
|
+
A clone is for hacking on Blacksmith itself, and for the two things an install
|
|
189
|
+
does not carry: the dashboard and this repo's own enforcement.
|
|
187
190
|
|
|
188
191
|
```bash
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
git clone https://github.com/juzser/blacksmith.git && cd blacksmith
|
|
193
|
+
pnpm install --frozen-lockfile
|
|
194
|
+
pnpm run build # tsc -> factory/orchestrator/dist/
|
|
195
|
+
bash scripts/check.sh # the gate CI runs; ends in `== PASS ==`
|
|
192
196
|
```
|
|
193
197
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
`smith` verbs and not the loop that drives them: validate plans, run the gates,
|
|
210
|
-
append events, read stats — yes; `/bs plan` and `/bs run` — from a clone.
|
|
211
|
-
Closing that gap is its own milestone, scoped and not yet cut:
|
|
198
|
+
Open a Claude Code session in the clone and you have `/bs` already, from the
|
|
199
|
+
checkout's own `.claude/` — **do not also install the plugin there.** You would
|
|
200
|
+
get two of everything (`bs` and `blacksmith:bs`, `auditor` and
|
|
201
|
+
`blacksmith:auditor`, once per role), pay the always-on cost twice, and the two
|
|
202
|
+
copies are free to disagree: the plugin's is a pinned checkout of `main`, the
|
|
203
|
+
project's is whatever branch you have out. `claude plugin disable blacksmith`
|
|
204
|
+
settles it.
|
|
205
|
+
|
|
206
|
+
The dashboard is clone-only because `ui/` is in neither the tarball nor the
|
|
207
|
+
plugin, so `smith ui serve` answers `ui.not-built` in an install and means it.
|
|
208
|
+
Enforcement is clone-only because this repo's `.claude/settings.json` deny
|
|
209
|
+
rules and its policy hook resolve paths against a checkout; the plugin ships no
|
|
210
|
+
`hooks/hooks.json` and loads neither (`Hooks (0)`), since a `/bs` that asked
|
|
211
|
+
you about every Bash command would be worse than one that asks about none. The
|
|
212
|
+
rest of that port is scoped in
|
|
212
213
|
[`docs/specs/plugin-port-scope.md`](docs/specs/plugin-port-scope.md).
|
|
213
214
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
>
|
|
215
|
+
`INSTALL.md` Part 2 is the long form of the clone above: per-platform
|
|
216
|
+
setup (macOS, Debian/Ubuntu, Fedora, Alpine, WSL2), the stack interview,
|
|
217
|
+
troubleshooting, and the known platform gaps stated rather than papered over.
|
|
218
|
+
|
|
219
|
+
</details>
|
|
219
220
|
|
|
220
221
|
## Using it
|
|
221
222
|
|
|
@@ -390,10 +391,12 @@ runbook is written; the hosted UI stays deferred. Beside the phases, `/bs audit`
|
|
|
390
391
|
is built: an existing project can be read on four axes and one epic cut from
|
|
391
392
|
what you accept.
|
|
392
393
|
|
|
393
|
-
The CLI is on npm as `@juzser/blacksmith`, at `0.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
394
|
+
The CLI is on npm as `@juzser/blacksmith`, at `0.2.0` — two binaries now,
|
|
395
|
+
`smith` and `smith-run`, the second being what runs a rendered turn on a
|
|
396
|
+
`cli` harness such as Codex. `0.1.1` was the first version that ran beside
|
|
397
|
+
you and knew it was a package, rather than out of the clone-shaped install
|
|
398
|
+
`0.1.0` was. The package is the binaries and what they
|
|
399
|
+
read. The dashboard, the docs, the test suite and `/bs` itself come from a
|
|
397
400
|
clone; a plugin is what would move `/bs`, and it is
|
|
398
401
|
[scoped, not cut](docs/specs/plugin-port-scope.md).
|
|
399
402
|
|
|
@@ -418,7 +421,7 @@ version in
|
|
|
418
421
|
| [`docs/guide/extending.md`](docs/guide/extending.md) | Adding agents, policies, taxonomy values |
|
|
419
422
|
| [`docs/specs/black-smith-architecture.md`](docs/specs/black-smith-architecture.md) | Why it is shaped this way |
|
|
420
423
|
| [`docs/specs/audit-command-scope.md`](docs/specs/audit-command-scope.md) | What `/bs audit` promises an audited project, and why |
|
|
421
|
-
| [`docs/specs/plugin-port-scope.md`](docs/specs/plugin-port-scope.md) |
|
|
424
|
+
| [`docs/specs/plugin-port-scope.md`](docs/specs/plugin-port-scope.md) | How `/bs` runs without a clone, and what the plugin leaves behind |
|
|
422
425
|
| [`docs/guide/dashboard.md`](docs/guide/dashboard.md) | The dashboard tour |
|
|
423
426
|
| [`docs/runbooks/providers.md`](docs/runbooks/providers.md) | Setting up the cross-provider judges |
|
|
424
427
|
| [`docs/runbooks/ops.md`](docs/runbooks/ops.md) | Running `smith daemon` unattended |
|
|
@@ -226,7 +226,33 @@ export function readClaimList(task) {
|
|
|
226
226
|
'wave for the reason that it may not edit anything. Admitting it defers the mistake to ' +
|
|
227
227
|
'the first out-of-claim edit; task-spec.schema.json requires at least one claim.', { task_id: task.task_id });
|
|
228
228
|
}
|
|
229
|
-
|
|
229
|
+
const promises = readPromiseList(task);
|
|
230
|
+
return promises === undefined
|
|
231
|
+
? { task_id: task.task_id, claims: claims }
|
|
232
|
+
: { task_id: task.task_id, claims: claims, keeps_exports: promises };
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* The `keeps_exports` half of the same door. A promise the wave gate cannot
|
|
236
|
+
* read must not read as "no promise": that direction is safe for the gate
|
|
237
|
+
* (the crossing serializes), but it also hides the plan error until a
|
|
238
|
+
* `plan validate` nobody re-ran. The field is optional; absent stays absent.
|
|
239
|
+
*/
|
|
240
|
+
function readPromiseList(task) {
|
|
241
|
+
const promises = task.keeps_exports;
|
|
242
|
+
if (promises === undefined)
|
|
243
|
+
return undefined;
|
|
244
|
+
if (!Array.isArray(promises)) {
|
|
245
|
+
throw new ClaimsError('claims.unreadable-promises', `Task "${task.task_id}" declares keeps_exports as ${describeType(promises)}, not a list ` +
|
|
246
|
+
'of file paths, so the wave gate cannot tell which crossings it promised to keep. ' +
|
|
247
|
+
'keeps_exports is a list of repo-relative file paths (task-spec.schema.json).', { task_id: task.task_id, received: describeType(promises) });
|
|
248
|
+
}
|
|
249
|
+
const badIndex = promises.findIndex((file) => typeof file !== 'string' || file === '');
|
|
250
|
+
if (badIndex !== -1) {
|
|
251
|
+
const received = promises[badIndex] === '' ? 'an empty string' : describeType(promises[badIndex]);
|
|
252
|
+
throw new ClaimsError('claims.unreadable-promises', `Task "${task.task_id}" declares keeps_exports entry ${badIndex} as ${received}, not a ` +
|
|
253
|
+
'file path, so the wave gate cannot tell which crossings it promised to keep.', { task_id: task.task_id, index: badIndex, received });
|
|
254
|
+
}
|
|
255
|
+
return promises;
|
|
230
256
|
}
|
|
231
257
|
/**
|
|
232
258
|
* Refuse an edge list no traversal can read, before it is traversed.
|