@esneiderbravo/speclaw 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -59
- package/dist/cli/commands/cortex.js +49 -0
- package/dist/cli/commands/init.js +23 -16
- package/dist/cli/commands/lawbook.js +34 -1
- package/dist/cli/commands/update.js +66 -58
- package/dist/cli/index.js +12 -3
- package/dist/cli/lib/update-check.js +8 -5
- package/dist/modules/cortex/brief.js +75 -0
- package/dist/modules/cortex/harness.js +270 -0
- package/dist/modules/cortex/register.js +49 -0
- package/dist/modules/foundation/assets/AGENTS.template.md +13 -6
- package/dist/modules/foundation/assets/CLAUDE.template.md +15 -11
- package/dist/modules/foundation/assets/docs/standards/lawbook.template.md +24 -37
- package/dist/modules/foundation/assets/workflows/speclaw.yml +1 -1
- package/dist/modules/foundation/context-budget.js +2 -0
- package/dist/modules/foundation/setup-tool.js +3 -2
- package/dist/modules/lawbook/assets/agents/archiver.md +19 -0
- package/dist/modules/lawbook/assets/agents/explorer.md +24 -0
- package/dist/modules/lawbook/assets/agents/implementer.md +20 -0
- package/dist/modules/lawbook/assets/agents/planner.md +27 -0
- package/dist/modules/lawbook/assets/agents/reviewer.md +25 -0
- package/dist/modules/lawbook/assets/agents/tester.md +21 -0
- package/dist/modules/lawbook/assets/commands/cortex.md +11 -0
- package/dist/modules/lawbook/assets/rules/spec-tasks-mandatory-steps.md +11 -10
- package/dist/modules/lawbook/assets/skills/build/steps/03-implement.md +1 -1
- package/dist/modules/lawbook/assets/skills/build/steps/04-hand-off.md +17 -0
- package/dist/modules/lawbook/assets/skills/cortex/SKILL.md +13 -0
- package/dist/modules/lawbook/assets/skills/cortex/steps/01-load-or-start.md +25 -0
- package/dist/modules/lawbook/assets/skills/cortex/steps/02-dispatch-loop.md +19 -0
- package/dist/modules/lawbook/assets/skills/cortex/steps/03-complete.md +10 -0
- package/dist/modules/lawbook/assets/skills/test/SKILL.md +11 -0
- package/dist/modules/lawbook/assets/skills/{build/steps/04-quality-gates.md → test/steps/01-quality-gates.md} +1 -1
- package/dist/modules/lawbook/assets/skills/{build/steps/05-manual-verification.md → test/steps/02-manual-verification.md} +1 -1
- package/dist/modules/lawbook/assets/skills/{build/steps/06-discipline-reports.md → test/steps/03-discipline-reports.md} +5 -2
- package/dist/modules/lawbook/change-tool.js +22 -0
- package/dist/modules/lawbook/engine.js +4 -0
- package/dist/modules/lawbook/levels.js +9 -3
- package/dist/modules/lawbook/register.js +7 -0
- package/dist/modules/tools/assets/packs.json +1 -6
- package/dist/server.js +4 -1
- package/dist/shared/agents.js +3 -3
- package/dist/shared/exposure.js +2 -2
- package/dist/shared/tool-catalog.js +3 -2
- package/package.json +2 -2
- package/dist/modules/lawbook/assets/skills/build/steps/07-hand-off.md +0 -9
- package/dist/modules/tools/assets/packs/agents/backend-developer.md +0 -61
- package/dist/modules/tools/assets/packs/agents/frontend-developer.md +0 -62
- package/dist/modules/tools/assets/packs/agents/product-strategy-analyst.md +0 -56
package/README.md
CHANGED
|
@@ -9,16 +9,17 @@
|
|
|
9
9
|
<a href="https://www.npmjs.com/package/@esneiderbravo/speclaw?activeTab=versions"><img src="https://img.shields.io/badge/provenance-SLSA-0E8E8E?labelColor=0B0F10&style=flat-square" alt="npm provenance"></a>
|
|
10
10
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0E8E8E?labelColor=0B0F10&style=flat-square" alt="MIT"></a>
|
|
11
11
|
<img src="https://img.shields.io/badge/node-%E2%89%A522.16-0E8E8E?labelColor=0B0F10&style=flat-square" alt="Node >= 22.16">
|
|
12
|
-
<img src="https://img.shields.io/badge/
|
|
12
|
+
<img src="https://img.shields.io/badge/v2.0-0E8E8E?labelColor=0B0F10&style=flat-square" alt="v2.0">
|
|
13
13
|
|
|
14
14
|
<br/><br/>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
17
17
|
<b>AI agents are brilliant and blind</b> — brilliant at writing code, blind to <i>your</i>
|
|
18
|
-
project's rules. <b>speclaw
|
|
19
|
-
<b>written laws</b> (a
|
|
20
|
-
|
|
21
|
-
and the <b>gates</b> that make
|
|
18
|
+
project's rules. <b>speclaw 2.0</b> hands them what they're missing: the codebase's
|
|
19
|
+
<b>written laws</b> (Foundation), a <b>local map</b> (Compass), a <b>spec-driven lawbook</b>,
|
|
20
|
+
and <b>Cortex</b> — <i>One brain. Many agents.</i> — the multi-agent loop that plans,
|
|
21
|
+
builds, reviews, and archives every non-trivial change. Plus the <b>gates</b> that make
|
|
22
|
+
those laws hold in the editor, in CI, and in the PR.
|
|
22
23
|
<br/>
|
|
23
24
|
One command. No cloud, no LLM, no API keys — <b>everything runs on your machine.</b>
|
|
24
25
|
</p>
|
|
@@ -26,7 +27,8 @@ One command. No cloud, no LLM, no API keys — <b>everything runs on your machin
|
|
|
26
27
|
<img src="https://img.shields.io/badge/100%25_local-0E8E8E?labelColor=0B0F10&style=flat-square" alt="100% local">
|
|
27
28
|
<img src="https://img.shields.io/badge/no_LLM_·_no_cloud-0E8E8E?labelColor=0B0F10&style=flat-square" alt="no LLM">
|
|
28
29
|
<img src="https://img.shields.io/badge/CLI_+_MCP-0E8E8E?labelColor=0B0F10&style=flat-square" alt="CLI + MCP">
|
|
29
|
-
<img src="https://img.shields.io/badge/
|
|
30
|
+
<img src="https://img.shields.io/badge/9_canonical_tools-0E8E8E?labelColor=0B0F10&style=flat-square" alt="9 tools">
|
|
31
|
+
<img src="https://img.shields.io/badge/Cortex-One_brain._Many_agents.-0E8E8E?labelColor=0B0F10&style=flat-square" alt="Cortex">
|
|
30
32
|
<img src="https://img.shields.io/badge/any_agent-0E8E8E?labelColor=0B0F10&style=flat-square" alt="any agent">
|
|
31
33
|
|
|
32
34
|
</div>
|
|
@@ -37,10 +39,10 @@ One command. No cloud, no LLM, no API keys — <b>everything runs on your machin
|
|
|
37
39
|
> **One command. Detects your agents and wires only those.** Paste
|
|
38
40
|
> `npx @esneiderbravo/speclaw@latest init` — speclaw detects Claude Code, Cursor,
|
|
39
41
|
> Codex, Windsurf, and generic `AGENTS.md` surfaces, scaffolds the constitution +
|
|
40
|
-
> lawbook, indexes your code, and registers the local MCP server
|
|
41
|
-
> agents you pick. This one-liner
|
|
42
|
-
> [CONTRIBUTING.md](CONTRIBUTING.md)); do not invent
|
|
43
|
-
> directories or newsletters.
|
|
42
|
+
> lawbook + Cortex roles, indexes your code, and registers the local MCP server
|
|
43
|
+
> (nine tools including `cortex`) — only for the agents you pick. This one-liner
|
|
44
|
+
> is a **stable contract** (see [CONTRIBUTING.md](CONTRIBUTING.md)); do not invent
|
|
45
|
+
> alternate install commands in directories or newsletters.
|
|
44
46
|
|
|
45
47
|
<br/>
|
|
46
48
|
|
|
@@ -74,7 +76,7 @@ The `speclaw` command is then available everywhere — run `speclaw index`,
|
|
|
74
76
|
**`speclaw.lock`** baseline for rule-file integrity.
|
|
75
77
|
3. **Index your code** with a live progress bar and a summary of what it found.
|
|
76
78
|
4. Register the speclaw **MCP server** in each chosen agent's config
|
|
77
|
-
(
|
|
79
|
+
(nine canonical tools; use `--minimal` to omit setup/lifecycle tools).
|
|
78
80
|
5. Print a prompt to paste into your agent so it fills the constitution with your
|
|
79
81
|
project's real architecture and conventions.
|
|
80
82
|
|
|
@@ -109,15 +111,16 @@ gh attestation verify <tarball> --owner esneiderbravo
|
|
|
109
111
|
|
|
110
112
|
<br/>
|
|
111
113
|
|
|
112
|
-
## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle"> The suite —
|
|
114
|
+
## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle"> The suite — six modules
|
|
113
115
|
|
|
114
116
|
| Module | What it does |
|
|
115
117
|
| :-- | :-- |
|
|
116
118
|
| **Foundation** | The project's constitution: `LAWS.md` binding granular standards under `docs/standards/`, plus `CLAUDE.md` / `AGENTS.md`. **Enforced** via agent hooks (`speclaw check`), deterministic graph laws (`speclaw verify` / `laws verify`), multidialect compile (`laws compile`), and committed **`speclaw.lock`** digests + injection scan (`laws lock` / `accept` / `scan`). |
|
|
117
119
|
| **Compass** | Local code graph (tree-sitter → `node:sqlite`): hybrid find (FTS5 + vectors + RRF + PageRank), impact, affected-tests, hotspots, coupling, visualize. Schema **10**. No LLM — lives in `.speclaw/` (gitignored). |
|
|
118
|
-
| **Lawbook** | Spec-driven workflow with **adaptive ceremony** (levels 0–3),
|
|
120
|
+
| **Lawbook** | Spec-driven workflow with **adaptive ceremony** (levels 0–3), EARS linting, requirement **coverage**, sealed **drift** anchors, bugfix + `lawbook_investigate`. Artifacts under `lawbook/`. |
|
|
121
|
+
| **Cortex** | **One brain. Many agents.** The multi-agent loop: durable `harness.json`, MCP tool `cortex` (`status`/`start`/`advance`/`rework`/`brief`), CLI `speclaw cortex`, skill `/lawbook/cortex`. Dispatches explorer → planner → implementer → reviewer → tester → archiver. |
|
|
119
122
|
| **Team** | Declare `team.owners` in `lawbook/config.yaml`; `speclaw owners --write` compiles a managed trailing block in `.github/CODEOWNERS` (GitHub: last match wins). Doctor checks the posture. CLI-only — no MCP tool. |
|
|
120
|
-
| **Tools** | Opt-in
|
|
123
|
+
| **Tools** | Opt-in pack catalog (empty by default). Role agents (explorer/planner/implementer/reviewer/tester/archiver) ship with Lawbook + Cortex. |
|
|
121
124
|
|
|
122
125
|
Compass is inspired by [CodeGraph](https://github.com/colbymchenry/codegraph) and the Lawbook module by [OpenSpec](https://github.com/Fission-AI/openspec) — both MIT. speclaw reimplements the ideas as its own code and gives full credit; see [ATTRIBUTION.md](ATTRIBUTION.md).
|
|
123
126
|
|
|
@@ -131,7 +134,7 @@ tokenizer on this corpus — not a BPE dependency):
|
|
|
131
134
|
|
|
132
135
|
| | Tokens |
|
|
133
136
|
| :-- | --: |
|
|
134
|
-
| **speclaw budget (always-on)** | **~13.7k** (
|
|
137
|
+
| **speclaw budget (always-on)** | **~13.7k** (9 MCP tools · ceiling **14.6k**) |
|
|
135
138
|
| Spec Kit commands alone | ~18.6k ([spec-kit#1401](https://github.com/github/spec-kit/issues/1401)) |
|
|
136
139
|
|
|
137
140
|
```bash
|
|
@@ -150,28 +153,45 @@ definitions, omitted registration (`--minimal`), and JIT skill steps.
|
|
|
150
153
|
|
|
151
154
|
<br/>
|
|
152
155
|
|
|
153
|
-
## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle">
|
|
156
|
+
## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle"> Cortex — One brain. Many agents.
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
and
|
|
158
|
-
|
|
158
|
+
The biggest risk with AI agents isn't capability — it's **one session doing
|
|
159
|
+
everything**: explore, code, "review," test, and archive with no durable state
|
|
160
|
+
and no role boundaries. **Cortex** is speclaw's answer: a coordinator brain that
|
|
161
|
+
dispatches specialized roles through a durable harness. Lawbook holds the specs;
|
|
162
|
+
Cortex runs the loop. Cheat sheet: [`docs/cortex.md`](docs/cortex.md).
|
|
159
163
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
└─────────┘ └───────┘ └───────┘ └──────┘ └─────────┘
|
|
164
|
-
```
|
|
164
|
+
<p align="center">
|
|
165
|
+
<img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/cortex-loop.png" width="800" alt="Cortex — One brain. Many agents.">
|
|
166
|
+
</p>
|
|
165
167
|
|
|
166
|
-
|
|
|
167
|
-
| :-- | :-- |
|
|
168
|
-
| **
|
|
169
|
-
| **
|
|
170
|
-
| **
|
|
171
|
-
| **
|
|
172
|
-
| **
|
|
168
|
+
| Role | Stage | Owns |
|
|
169
|
+
| :-- | :-- | :-- |
|
|
170
|
+
| **explorer** | exploring | Compass-first investigation; writes nothing under `lawbook/` / `src/` |
|
|
171
|
+
| **planner** | planning | Ceremony level + change artifacts; questions always go to the human |
|
|
172
|
+
| **implementer** | implementing | Code + tests; stops at hand-off (no final gates, no archive) |
|
|
173
|
+
| **reviewer** | reviewing | `reports/review.md` PASS/FAIL; no code patches (skipped at level 0) |
|
|
174
|
+
| **tester** | testing | Quality gates, manual verification, discipline reports |
|
|
175
|
+
| **archiver** | archiving | Sync + `lawbook_archive` **in the same PR** |
|
|
176
|
+
|
|
177
|
+
State lives in `lawbook/changes/<name>/harness.json`. Archive is gated on
|
|
178
|
+
harness verdicts (test PASS; review PASS when level ≥ 1) plus tasks, reports,
|
|
179
|
+
sync, and coverage. Max 3 reworks; then the coordinator asks you.
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
<p align="center">
|
|
182
|
+
<img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/terminal-cortex.png" width="800" alt="speclaw cortex brief">
|
|
183
|
+
</p>
|
|
184
|
+
|
|
185
|
+
**Drive it three ways — same engine:**
|
|
186
|
+
|
|
187
|
+
- **In your agent** — `/lawbook/cortex` (coordinator) plus per-role skills
|
|
188
|
+
- **MCP** — canonical tool `cortex` (`status` / `start` / `advance` / `rework` / `brief`)
|
|
189
|
+
- **CLI** — `speclaw cortex …`
|
|
190
|
+
|
|
191
|
+
### Lawbook — the specs Cortex runs
|
|
192
|
+
|
|
193
|
+
Lawbook is the **artifact layer**: adaptive ceremony (levels 0–3), EARS linting,
|
|
194
|
+
requirement **coverage**, sealed **drift** anchors, bugfix + investigate.
|
|
175
195
|
|
|
176
196
|
| Level | When | Artifacts |
|
|
177
197
|
| :-- | :-- | :-- |
|
|
@@ -181,25 +201,15 @@ non-trivial lands without a spec change. It's a loop of five steps:
|
|
|
181
201
|
| **3** | Full ceremony | proposal + design + tasks + deltas + `reports/` |
|
|
182
202
|
| **bug** | Regression / RCA | `speclaw lawbook draft --bug` → `bugfix.md` + investigate first |
|
|
183
203
|
|
|
184
|
-
Plus a **`reports/`** folder — scaffolded at draft, filled at build with one report
|
|
185
|
-
per discipline (`backend.md`, `frontend.md`, `api.md`, …). `lawbook_archive`
|
|
186
|
-
refuses to archive without it.
|
|
187
|
-
|
|
188
204
|
> [!NOTE]
|
|
189
205
|
> **Delta specs are normative and testable.** Requirements use `SHALL`/`MUST`
|
|
190
206
|
> under `### Requirement:` headers (EARS-friendly), each with `#### Scenario:`
|
|
191
207
|
> blocks. `lawbook_validate` checks structure; `speclaw coverage` tracks
|
|
192
208
|
> `req~…~N` → impl/test via `// Covers:` comments.
|
|
193
209
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
- **MCP tools** — eight canonical: `compass_explore`, `compass_find`, `compass_diff_context`, `compass_index`, `lawbook_change`, `lawbook_investigate`, `speclaw_setup`, `speclaw_check`.
|
|
198
|
-
- **CLI** — `speclaw lawbook …`, `speclaw quick`, `speclaw coverage`, `speclaw drift`.
|
|
199
|
-
|
|
200
|
-
The workspace is committed under `lawbook/`: `specs/`, `changes/`,
|
|
201
|
-
`changes/archive/`, `anchors/` (drift photographs), and `config.yaml`
|
|
202
|
-
(mandatory tasks, ceremony cuts, `team.owners`, EARS knobs).
|
|
210
|
+
The workspace is committed under `lawbook/`: `specs/`, `changes/` (with
|
|
211
|
+
`harness.json` per active change), `changes/archive/`, `anchors/`, and
|
|
212
|
+
`config.yaml`.
|
|
203
213
|
|
|
204
214
|
<br/>
|
|
205
215
|
|
|
@@ -208,12 +218,12 @@ The workspace is committed under `lawbook/`: `specs/`, `changes/`,
|
|
|
208
218
|
speclaw meets you where you are. Everything works through the **CLI** — so no one
|
|
209
219
|
is blocked by MCP setup — and the same capabilities are exposed as **MCP tools**
|
|
210
220
|
for a smoother, integrated experience once configured. An agent without MCP can
|
|
211
|
-
still use Compass and the lawbook engine by calling the CLI from its shell.
|
|
221
|
+
still use Compass, Cortex, and the lawbook engine by calling the CLI from its shell.
|
|
212
222
|
|
|
213
223
|
<p align="center"><b>CLI</b> — the installer & operator, runs anywhere <code>node</code> does</p>
|
|
214
224
|
<p align="center"><img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/terminal-cli.png" width="800" alt="speclaw CLI commands"></p>
|
|
215
225
|
|
|
216
|
-
<p align="center"><b>MCP</b> —
|
|
226
|
+
<p align="center"><b>MCP</b> — nine canonical tools (incl. <code>cortex</code>), auto-registered by <code>init</code></p>
|
|
217
227
|
<p align="center"><img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/terminal-mcp.png" width="800" alt="speclaw MCP tools"></p>
|
|
218
228
|
|
|
219
229
|
<br/>
|
|
@@ -225,10 +235,10 @@ still use Compass and the lawbook engine by calling the CLI from its shell.
|
|
|
225
235
|
</p>
|
|
226
236
|
|
|
227
237
|
**Committed vs. local.** Your **personalized source** is committed — `LAWS.md`,
|
|
228
|
-
`CLAUDE.md`, `AGENTS.md`, `docs/standards/*`, `docs/compass.md`,
|
|
229
|
-
workspace, and **`speclaw.lock`** (rule digests at the repo root).
|
|
230
|
-
**regenerable workflow content is local, not committed**: only `ai-specs/`
|
|
231
|
-
(skills, commands, rules,
|
|
238
|
+
`CLAUDE.md`, `AGENTS.md`, `docs/standards/*`, `docs/compass.md`, `docs/cortex.md`,
|
|
239
|
+
the `lawbook/` workspace, and **`speclaw.lock`** (rule digests at the repo root).
|
|
240
|
+
speclaw's **regenerable workflow content is local, not committed**: only `ai-specs/`
|
|
241
|
+
(skills, commands, rules, role agents, and its `.speclaw.json` manifest) is
|
|
232
242
|
gitignored, because `init`/`update` reconstruct it from the package. So **after
|
|
233
243
|
cloning a speclaw project, run `speclaw init` (or `speclaw update`)** to
|
|
234
244
|
regenerate `ai-specs/` locally. Optional **`team.owners`** in
|
|
@@ -252,7 +262,8 @@ follows `apps/*/src`, `packages/*/src`, or `src/` rather than copying
|
|
|
252
262
|
> of AI coding agents isn't lack of capability — it's working without the project's
|
|
253
263
|
> tacit knowledge: the rules the team actually lives by. speclaw makes that
|
|
254
264
|
> knowledge explicit, executable, and binding, and gives agents a local map
|
|
255
|
-
> (Compass)
|
|
265
|
+
> (Compass), a lawbook of specs, and **Cortex** to run the multi-agent loop —
|
|
266
|
+
> without burning tokens.
|
|
256
267
|
|
|
257
268
|
This is why "enforced" is literal, not a metaphor. Anthropic's own guidance puts
|
|
258
269
|
it plainly:
|
|
@@ -308,7 +319,7 @@ of the lock in this release. Regenerable IDE mirrors (e.g. `.cursor/rules` →
|
|
|
308
319
|
On GitHub:
|
|
309
320
|
|
|
310
321
|
```yaml
|
|
311
|
-
- uses: esneiderbravo/speclaw@
|
|
322
|
+
- uses: esneiderbravo/speclaw@v2
|
|
312
323
|
```
|
|
313
324
|
|
|
314
325
|
`init` / `update` write `.github/workflows/speclaw.yml` only when that path is
|
|
@@ -321,14 +332,19 @@ review from Code Owners* when you declare `team.owners`.
|
|
|
321
332
|
## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle"> Staying up to date
|
|
322
333
|
|
|
323
334
|
speclaw checks for new releases in the background (at most once a day) and nudges
|
|
324
|
-
you when one lands. To
|
|
335
|
+
you when one lands. To bring a project up to date (migrations only — never runs
|
|
336
|
+
`npm install -g`):
|
|
325
337
|
|
|
326
338
|
```bash
|
|
327
339
|
speclaw update
|
|
328
340
|
```
|
|
329
341
|
|
|
330
|
-
|
|
331
|
-
|
|
342
|
+
If a newer binary is on npm, `update` prints an advisory (`current → latest`) and
|
|
343
|
+
how to upgrade the binary separately (`npm i -g @esneiderbravo/speclaw@latest`, or
|
|
344
|
+
prefer `npx @esneiderbravo/speclaw@latest update`), then still migrates the project.
|
|
345
|
+
|
|
346
|
+
`update` brings the current project up to date without a re-init, splitting files
|
|
347
|
+
by who owns them:
|
|
332
348
|
|
|
333
349
|
- **Managed files** (skills/commands/rules under `ai-specs/`) are **refreshed**.
|
|
334
350
|
Pass `--backup` to keep a `<file>.bak` before overwrite.
|
|
@@ -337,7 +353,8 @@ without a re-init, splitting files by who owns them:
|
|
|
337
353
|
prints a prompt for the agent you're using.
|
|
338
354
|
- **`speclaw.lock`** and the CODEOWNERS owners block are refreshed when configured.
|
|
339
355
|
|
|
340
|
-
- `speclaw update --check` — report
|
|
356
|
+
- `speclaw update --check` — report version status only; do not migrate.
|
|
357
|
+
- `speclaw update --migrate-only` — silent no-op alias of the default (compat).
|
|
341
358
|
- `NO_UPDATE_NOTIFIER=1` — silence the reminder.
|
|
342
359
|
|
|
343
360
|
<br/>
|
|
@@ -357,6 +374,6 @@ without a re-init, splitting files by who owns them:
|
|
|
357
374
|
[CodeGraph](https://github.com/colbymchenry/codegraph) ·
|
|
358
375
|
see [ATTRIBUTION.md](ATTRIBUTION.md)
|
|
359
376
|
|
|
360
|
-
<i>speclaw
|
|
377
|
+
<i>speclaw 2.0 · where specs become law</i>
|
|
361
378
|
|
|
362
379
|
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { handleHarness, readHarness } from "../../modules/cortex/harness.js";
|
|
2
|
+
import { briefForStage } from "../../modules/cortex/brief.js";
|
|
3
|
+
import { list } from "../lib/args.js";
|
|
4
|
+
import { ui } from "../lib/ui.js";
|
|
5
|
+
const OPS = ["status", "start", "advance", "rework", "brief"];
|
|
6
|
+
/**
|
|
7
|
+
* Run `speclaw cortex <op> --change <name> […]`.
|
|
8
|
+
*
|
|
9
|
+
* @param flags - Parsed flags; `_[0]` is the op.
|
|
10
|
+
*/
|
|
11
|
+
export async function runCortex(flags) {
|
|
12
|
+
const cwd = process.cwd();
|
|
13
|
+
const opRaw = flags._[0];
|
|
14
|
+
const op = opRaw;
|
|
15
|
+
if (!OPS.includes(op)) {
|
|
16
|
+
ui.err("Usage: speclaw cortex <status|start|advance|rework|brief> --change <name> [--verdict PASS|FAIL] [--question …] [--pause-questions] [--note …] [--json]");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const changeName = typeof flags.change === "string" ? flags.change : flags._[1];
|
|
20
|
+
if (!changeName) {
|
|
21
|
+
ui.err("Usage: speclaw cortex <op> --change <name>");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
if (op === "brief") {
|
|
26
|
+
const state = readHarness(cwd, changeName);
|
|
27
|
+
const brief = briefForStage(state?.stage ?? null);
|
|
28
|
+
const result = { state, ...brief };
|
|
29
|
+
console.log(JSON.stringify(result, null, 2));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const verdictRaw = typeof flags.verdict === "string" ? flags.verdict.toUpperCase() : undefined;
|
|
33
|
+
const verdict = verdictRaw === "PASS" || verdictRaw === "FAIL" ? verdictRaw : undefined;
|
|
34
|
+
const result = handleHarness({
|
|
35
|
+
projectPath: cwd,
|
|
36
|
+
change: changeName,
|
|
37
|
+
harnessOp: op,
|
|
38
|
+
verdict: verdict ?? null,
|
|
39
|
+
openQuestions: list(flags.question),
|
|
40
|
+
pauseForQuestions: Boolean(flags["pause-questions"]),
|
|
41
|
+
note: typeof flags.note === "string" ? flags.note : undefined,
|
|
42
|
+
});
|
|
43
|
+
console.log(JSON.stringify(result, null, 2));
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
ui.err(err.message);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -10,9 +10,7 @@ import { list } from "../lib/args.js";
|
|
|
10
10
|
import { ui, c, banner, renderProgress, clearProgress } from "../lib/ui.js";
|
|
11
11
|
import { checkForUpdates } from "../lib/update-check.js";
|
|
12
12
|
import { reportTrackedLocalContent } from "../lib/untrack.js";
|
|
13
|
-
const PACK_LABELS = {
|
|
14
|
-
agents: "dev-agents (backend · frontend · product)",
|
|
15
|
-
};
|
|
13
|
+
const PACK_LABELS = {};
|
|
16
14
|
/** Best-effort project name: the package.json name (unscoped) or the directory name. */
|
|
17
15
|
export function detectProjectName(cwd) {
|
|
18
16
|
try {
|
|
@@ -26,7 +24,8 @@ export function detectProjectName(cwd) {
|
|
|
26
24
|
return path.basename(cwd);
|
|
27
25
|
}
|
|
28
26
|
/**
|
|
29
|
-
* Interactive setup: pick agents and packs, scaffold, index,
|
|
27
|
+
* Interactive setup: pick agents (and packs when any exist), scaffold, index,
|
|
28
|
+
* and print the handoff prompt.
|
|
30
29
|
*
|
|
31
30
|
* @param flags - Parsed flags; runs interactively on a TTY unless `--agents`, `--yes`, or
|
|
32
31
|
* `-y` is set. Honors `--project-name`, `--packs`, and `--no-index`.
|
|
@@ -37,6 +36,8 @@ export async function runInit(flags) {
|
|
|
37
36
|
const interactive = Boolean(process.stdin.isTTY) && !flags.agents && !flags.yes && !flags.y;
|
|
38
37
|
let agents;
|
|
39
38
|
let packs;
|
|
39
|
+
const availablePacks = loadPacks();
|
|
40
|
+
const packIds = Object.keys(availablePacks);
|
|
40
41
|
banner();
|
|
41
42
|
// Scaffolding with a stale version writes yesterday's foundation, so recommend
|
|
42
43
|
// upgrading first — prominently, before any prompts, so there's time to cancel.
|
|
@@ -44,34 +45,39 @@ export async function runInit(flags) {
|
|
|
44
45
|
const upd = await checkForUpdates();
|
|
45
46
|
if (upd.updateAvailable && upd.latest) {
|
|
46
47
|
ui.warn(`You're on ${c.muted(upd.current)} — latest is ${c.bold(c.cyan(upd.latest))}.`);
|
|
47
|
-
ui.info(`Recommended:
|
|
48
|
+
ui.info(`Recommended: refresh the binary with ${ui.code("npx @esneiderbravo/speclaw@latest init")} ` +
|
|
49
|
+
`(or ${ui.code("npm i -g @esneiderbravo/speclaw@latest")}), then run ${ui.code("speclaw init")} again.`);
|
|
48
50
|
ui.plain();
|
|
49
51
|
}
|
|
50
52
|
if (interactive) {
|
|
51
|
-
|
|
53
|
+
// Covers: req~remove-agents-pack~1
|
|
54
|
+
const prompts = {
|
|
52
55
|
agents: () => clack.multiselect({
|
|
53
56
|
message: "Which agents do you use? (space to select)",
|
|
54
57
|
options: AGENTS.map((a) => ({ value: a.id, label: a.label })),
|
|
55
58
|
initialValues: ["claude"],
|
|
56
59
|
required: true,
|
|
57
60
|
}),
|
|
58
|
-
|
|
61
|
+
};
|
|
62
|
+
if (packIds.length > 0) {
|
|
63
|
+
prompts.packs = () => clack.multiselect({
|
|
59
64
|
message: "Which tool packs to install?",
|
|
60
|
-
options:
|
|
65
|
+
options: packIds.map((id) => ({
|
|
61
66
|
value: id,
|
|
62
67
|
label: id,
|
|
63
|
-
hint:
|
|
68
|
+
hint: availablePacks[id].description.slice(0, 50),
|
|
64
69
|
})),
|
|
65
|
-
initialValues: [
|
|
70
|
+
initialValues: [],
|
|
66
71
|
required: false,
|
|
67
|
-
})
|
|
68
|
-
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const answers = await clack.group(prompts, { onCancel: () => process.exit(1) });
|
|
69
75
|
agents = answers.agents;
|
|
70
|
-
packs = answers.packs;
|
|
76
|
+
packs = answers.packs ?? [];
|
|
71
77
|
}
|
|
72
78
|
else {
|
|
73
79
|
agents = list(flags.agents).length ? list(flags.agents) : ["claude"];
|
|
74
|
-
packs = list(flags.packs)
|
|
80
|
+
packs = list(flags.packs);
|
|
75
81
|
}
|
|
76
82
|
const unknownAgents = agents.filter((a) => !agentById(a));
|
|
77
83
|
if (unknownAgents.length) {
|
|
@@ -91,7 +97,8 @@ export async function runInit(flags) {
|
|
|
91
97
|
minimal: Boolean(flags.minimal),
|
|
92
98
|
});
|
|
93
99
|
ui.ok(`Foundation ${c.muted("— LAWS.md + 8 standards + CLAUDE.md/AGENTS.md")}`);
|
|
94
|
-
ui.ok(`Lawbook workflow ${c.muted("— draft · build · sync · archive
|
|
100
|
+
ui.ok(`Lawbook workflow ${c.muted("— cortex · explore · draft · build · sync · archive")}`);
|
|
101
|
+
ui.ok(`Role agents ${c.muted("— explorer · planner · implementer · reviewer · tester · archiver")}`);
|
|
95
102
|
for (const p of packs)
|
|
96
103
|
ui.ok(`${PACK_LABELS[p] ?? p + " pack"}`);
|
|
97
104
|
specInit(cwd);
|
|
@@ -132,7 +139,7 @@ export async function runInit(flags) {
|
|
|
132
139
|
"commit messages, branch names, PR and ticket language — don't assume. " +
|
|
133
140
|
"Start with init_project."));
|
|
134
141
|
ui.plain();
|
|
135
|
-
ui.info(`
|
|
142
|
+
ui.info(`Non-trivial work runs through Cortex — use ${ui.code("/lawbook/cortex")} (or the cortex skill).`);
|
|
136
143
|
ui.plain();
|
|
137
144
|
ui.info(`Add an agent: ${ui.code("speclaw agent add cursor")}`);
|
|
138
145
|
ui.info(`Refresh index: ${ui.code("speclaw index")}`);
|
|
@@ -2,6 +2,7 @@ import { specInit, specValidate, specSync, specArchive, specList, } from "../../
|
|
|
2
2
|
import { handleLevel } from "../../modules/lawbook/quick.js";
|
|
3
3
|
import { scaffoldBugfix } from "../../modules/lawbook/bugfix.js";
|
|
4
4
|
import { investigate, formatInvestigateResult } from "../../modules/lawbook/investigate.js";
|
|
5
|
+
import { handleHarness } from "../../modules/cortex/harness.js";
|
|
5
6
|
import { list } from "../lib/args.js";
|
|
6
7
|
import { ui } from "../lib/ui.js";
|
|
7
8
|
function today() {
|
|
@@ -144,8 +145,40 @@ export async function runSpec(flags) {
|
|
|
144
145
|
}
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
148
|
+
case "harness": {
|
|
149
|
+
const opRaw = change;
|
|
150
|
+
const op = opRaw;
|
|
151
|
+
if (!["status", "start", "advance", "rework"].includes(op)) {
|
|
152
|
+
ui.err("Usage: speclaw lawbook harness <status|start|advance|rework> --change <name> [--verdict PASS|FAIL] [--question …] [--pause-questions] [--note …] [--json]");
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
const changeName = typeof flags.change === "string" ? flags.change : flags._[2];
|
|
156
|
+
if (!changeName) {
|
|
157
|
+
ui.err("Usage: speclaw lawbook harness <op> --change <name>");
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
const verdictRaw = typeof flags.verdict === "string" ? flags.verdict.toUpperCase() : undefined;
|
|
161
|
+
const verdict = verdictRaw === "PASS" || verdictRaw === "FAIL"
|
|
162
|
+
? verdictRaw
|
|
163
|
+
: undefined;
|
|
164
|
+
const result = handleHarness({
|
|
165
|
+
projectPath: cwd,
|
|
166
|
+
change: changeName,
|
|
167
|
+
harnessOp: op,
|
|
168
|
+
verdict: verdict ?? null,
|
|
169
|
+
openQuestions: list(flags.question),
|
|
170
|
+
pauseForQuestions: Boolean(flags["pause-questions"]),
|
|
171
|
+
note: typeof flags.note === "string" ? flags.note : undefined,
|
|
172
|
+
});
|
|
173
|
+
if (flags.json) {
|
|
174
|
+
console.log(JSON.stringify(result, null, 2));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
console.log(JSON.stringify(result, null, 2));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
147
180
|
default:
|
|
148
|
-
ui.err("Usage: speclaw lawbook <init|list|validate|sync|archive|level|draft|investigate> [change]");
|
|
181
|
+
ui.err("Usage: speclaw lawbook <init|list|validate|sync|archive|level|draft|investigate|harness> [change]");
|
|
149
182
|
process.exit(1);
|
|
150
183
|
}
|
|
151
184
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { ui, c } from "../lib/ui.js";
|
|
@@ -11,6 +10,17 @@ import { readManifest } from "../../shared/manifest.js";
|
|
|
11
10
|
import { loadPacks } from "../../modules/tools/packs.js";
|
|
12
11
|
import { detectProjectName } from "./init.js";
|
|
13
12
|
import { reportTrackedLocalContent } from "../lib/untrack.js";
|
|
13
|
+
/**
|
|
14
|
+
* How to upgrade the installed binary without this command spawning `npm install -g`.
|
|
15
|
+
*
|
|
16
|
+
* @param name - Package name on npm (usually `pkgName()`).
|
|
17
|
+
* @returns One-line advisory for the terminal.
|
|
18
|
+
*/
|
|
19
|
+
export function binaryUpgradeHint(name) {
|
|
20
|
+
return (`Upgrade the binary separately with ${ui.code(`npm i -g ${name}@latest`)} ` +
|
|
21
|
+
`(or prefer ${ui.code(`npx ${name}@latest update`)}). ` +
|
|
22
|
+
`This command only migrates the project.`);
|
|
23
|
+
}
|
|
14
24
|
// The first migration must be tagged at the version that introduces this
|
|
15
25
|
// mechanism (0.1.12): `isNewer` is strict, so an entry tagged at an already-
|
|
16
26
|
// shipped version (e.g. 0.1.11) would never fire for projects already on it.
|
|
@@ -168,8 +178,9 @@ const MIGRATIONS = [
|
|
|
168
178
|
version: "1.0.0",
|
|
169
179
|
describe: "speclaw 1.0 — official release (enforcement + graph + lawbook + owners)",
|
|
170
180
|
agentPrompt: "- speclaw **1.0** is the official release: Foundation (hooks + `speclaw.lock` integrity), " +
|
|
171
|
-
"Compass (schema 10
|
|
172
|
-
"
|
|
181
|
+
"Compass (schema 10), Lawbook (ceremony 0–3, coverage, drift, bugfix), " +
|
|
182
|
+
"Cortex (multi-agent loop; nine canonical MCP tools including `cortex`), " +
|
|
183
|
+
"and Team (`team.owners` → `speclaw owners --write`). Install remains " +
|
|
173
184
|
"`npx @esneiderbravo/speclaw@latest init`. CI consumers use `esneiderbravo/speclaw@v1`.\n" +
|
|
174
185
|
"- Preserve all project-specific wording; only apply these speclaw-authored changes.",
|
|
175
186
|
},
|
|
@@ -184,70 +195,67 @@ const MIGRATIONS = [
|
|
|
184
195
|
"`speclaw laws compile` if agent rule files look stale, then `speclaw index`.\n" +
|
|
185
196
|
"- Preserve all project-specific wording; only apply these speclaw-authored changes.",
|
|
186
197
|
},
|
|
198
|
+
{
|
|
199
|
+
version: "2.0.0",
|
|
200
|
+
describe: "speclaw 2.0 — Cortex (One brain. Many agents.) + nine MCP tools",
|
|
201
|
+
agentPrompt: "- speclaw **2.0** ships **Cortex**: durable multi-agent loop (`harness.json`), " +
|
|
202
|
+
"MCP tool `cortex` (`status`/`start`/`advance`/`rework`/`brief`), CLI " +
|
|
203
|
+
"`speclaw cortex`, skill `/lawbook/cortex`, and role agents " +
|
|
204
|
+
"(explorer → planner → implementer → reviewer → tester → archiver). " +
|
|
205
|
+
"Canonical MCP surface is **nine** tools. CI consumers: " +
|
|
206
|
+
"`esneiderbravo/speclaw@v2`. See `docs/cortex.md`.\n" +
|
|
207
|
+
"- Preserve all project-specific wording; only apply these speclaw-authored changes.",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
version: "2.0.1",
|
|
211
|
+
describe: "speclaw update no longer upgrades the global npm package",
|
|
212
|
+
agentPrompt: "- `speclaw update` applies **project** migrations only — it does **not** run " +
|
|
213
|
+
"`npm install -g`. If a newer binary exists on npm it prints an advisory; " +
|
|
214
|
+
"upgrade the CLI separately (`npm i -g @esneiderbravo/speclaw@latest` or " +
|
|
215
|
+
"`npx @esneiderbravo/speclaw@latest update`). Prefer `npx @…@latest` when the " +
|
|
216
|
+
"installed binary is stale so migrations match the latest package.\n" +
|
|
217
|
+
"- Preserve all project-specific wording; only apply these speclaw-authored changes.",
|
|
218
|
+
},
|
|
187
219
|
];
|
|
188
220
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* commands, and feature steps this
|
|
221
|
+
* Bring the current project up to date without a full re-init: check whether a
|
|
222
|
+
* newer binary exists on npm (advisory only — never runs `npm install -g`), then
|
|
223
|
+
* additively apply any new standards, skills, commands, and feature steps this
|
|
224
|
+
* project is missing (existing files untouched).
|
|
192
225
|
*
|
|
193
|
-
* @param flags - `--check` reports
|
|
194
|
-
*
|
|
195
|
-
*
|
|
226
|
+
* @param flags - `--check` reports version status only (no migrate);
|
|
227
|
+
* `--migrate-only` is a silent no-op alias of the default (compat).
|
|
228
|
+
* @param hooks - Optional test seams for the version check and migrate path.
|
|
196
229
|
*/
|
|
197
|
-
export async function runUpdate(flags) {
|
|
230
|
+
export async function runUpdate(flags, hooks = {}) {
|
|
198
231
|
const cwd = process.cwd();
|
|
199
|
-
|
|
232
|
+
// `--migrate-only` is accepted for compat but is a no-op alias of default
|
|
233
|
+
// (version check + project migrations). It no longer skips the version check.
|
|
200
234
|
const checkOnly = Boolean(flags.check);
|
|
201
235
|
const backup = Boolean(flags.backup);
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
ui.step(`Updating ${pkgName()} globally`);
|
|
216
|
-
const install = spawnSync("npm", ["install", "-g", `${pkgName()}@latest`], {
|
|
217
|
-
stdio: "inherit",
|
|
218
|
-
shell: winShell,
|
|
219
|
-
});
|
|
220
|
-
if (install.status !== 0) {
|
|
221
|
-
ui.err("Global update failed. Try again with elevated permissions (e.g. sudo), or check your npm setup.");
|
|
222
|
-
process.exit(1);
|
|
223
|
-
}
|
|
224
|
-
ui.ok(`Updated to ${latest}`);
|
|
225
|
-
// Re-exec the NEWLY installed binary so migrations run with the new assets
|
|
226
|
-
// and any new feature steps — not this (now-stale) process. Carry --backup
|
|
227
|
-
// through so the refresh honors it after the upgrade.
|
|
228
|
-
const reArgs = [
|
|
229
|
-
"update",
|
|
230
|
-
"--migrate-only",
|
|
231
|
-
...(backup ? ["--backup"] : []),
|
|
232
|
-
...(flags.minimal ? ["--minimal"] : []),
|
|
233
|
-
];
|
|
234
|
-
const re = spawnSync("speclaw", reArgs, {
|
|
235
|
-
stdio: "inherit",
|
|
236
|
-
shell: winShell,
|
|
237
|
-
});
|
|
238
|
-
if (re.error) {
|
|
239
|
-
ui.warn(`Upgraded — now run ${ui.code("speclaw update --migrate-only")} to apply project changes.`);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
process.exit(re.status ?? 0);
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
ui.ok(`Already on the latest version (${current}).`);
|
|
246
|
-
if (checkOnly)
|
|
247
|
-
return;
|
|
236
|
+
const check = hooks.checkForUpdates ?? checkForUpdates;
|
|
237
|
+
const migrate = hooks.applyProjectMigrations ?? applyProjectMigrations;
|
|
238
|
+
ui.step("Checking for updates");
|
|
239
|
+
const { current, latest, updateAvailable } = await check({ force: true });
|
|
240
|
+
if (!latest) {
|
|
241
|
+
ui.warn("Could not reach the npm registry — skipping the version check.");
|
|
242
|
+
}
|
|
243
|
+
else if (updateAvailable) {
|
|
244
|
+
ui.info(`${c.muted(current)} ${c.muted("→")} ${c.cyan(latest)}`);
|
|
245
|
+
ui.info(binaryUpgradeHint(pkgName()));
|
|
246
|
+
if (checkOnly) {
|
|
247
|
+
ui.info(`Run ${ui.code("speclaw update")} to apply project migrations with the binary you have.`);
|
|
248
|
+
return;
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
|
-
|
|
251
|
+
else {
|
|
252
|
+
ui.ok(`Already on the latest version (${current}).`);
|
|
253
|
+
if (checkOnly)
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (checkOnly)
|
|
257
|
+
return;
|
|
258
|
+
migrate(cwd, backup, flags.minimal ? true : undefined);
|
|
251
259
|
}
|
|
252
260
|
/**
|
|
253
261
|
* Additively apply the current version's content and feature steps to a project.
|