@wairon/cli 5.1.1-dev.101 → 5.1.1-dev.103
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 +93 -45
- package/dist/cli/index.js +2186 -687
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +37667 -0
- package/dist/index.js +1613 -536
- package/dist/index.js.map +1 -1
- package/dist/templates/architect.yaml +14 -12
- package/dist/templates/domain-owner.yaml +3 -2
- package/dist/templates/skills/sdd-architect.md +11 -5
- package/dist/templates/skills/sdd-auditor.md +5 -4
- package/dist/templates/skills/sdd-delegate.md +3 -2
- package/dist/templates/skills/sdd-implement.md +4 -4
- package/dist/templates/skills/sdd-narrative.md +1 -1
- package/dist/webapp.html +770 -0
- package/docs/architecture.md +83 -0
- package/docs/cli.md +771 -0
- package/docs/design-export.md +308 -0
- package/docs/extending-wairon.md +615 -0
- package/docs/requirements.md +65 -0
- package/docs/roadmap.md +133 -0
- package/docs/standards/INDEX.md +23 -0
- package/docs/standards/architecture.md +953 -0
- package/docs/standards/language-bindings.md +120 -0
- package/docs/templates.md +61 -0
- package/docs/vision.md +59 -0
- package/docs/wairon-feature-requests-hosted-mcp.md +74 -0
- package/package.json +5 -2
- package/schemas/design-export-1.json +4 -4
package/docs/cli.md
ADDED
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
# wairon — CLI Reference
|
|
2
|
+
|
|
3
|
+
All commands operate on the `.wai/` directory in the current project. Global
|
|
4
|
+
flags: `--verbose`, `--silent`, `-v`/`--version`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Project
|
|
9
|
+
|
|
10
|
+
### `wairon init [-y, --yes] [--pack <source>]`
|
|
11
|
+
Bootstrap `.wai/` in the current project: project config, the SDD spec tree
|
|
12
|
+
(an L0 `.index.yaml` is seeded), the shared `.wai/context/` and the AI guide
|
|
13
|
+
(`CLAUDE.md` / `GEMINI.md`), the project-local MCP registration, and the SDD
|
|
14
|
+
skills installed into each selected target tool. No agent files are written:
|
|
15
|
+
agents are served as live briefs (`sdd_get_agent_brief`, `wairon agent brief
|
|
16
|
+
<id>`) unless the project opts into `rules.materializeAgentFiles: true`. `--yes`
|
|
17
|
+
uses defaults without prompts (a shell with no terminal needs it): the project name from the folder, profile backend, target claude only — another tool is one interactive answer or one `targets` entry away;
|
|
18
|
+
`--pack <source>` (repeatable) vendors + registers an extension pack right after
|
|
19
|
+
init (see `wairon pack`). Re-running on an
|
|
20
|
+
initialized project is a no-op that points you back to the SDD flow. Run in a
|
|
21
|
+
folder an existing project binds (see *Which project a command acts on* below),
|
|
22
|
+
it asks on a terminal whether to make that folder a **member** of the parent —
|
|
23
|
+
which edits the parent's `project.yaml` — and with `--yes` it refuses, writing
|
|
24
|
+
nothing and printing the exact `wairon member add <alias> <path> --project` to
|
|
25
|
+
run from the parent (or: make the folder its own repository with `git init` and
|
|
26
|
+
re-run `wairon init` for an independent project).
|
|
27
|
+
|
|
28
|
+
#### Which project a command acts on
|
|
29
|
+
Every command, the MCP server (`wairon mcp serve`) and `wairon dev` bind the same
|
|
30
|
+
project: from the folder you are in, the nearest folder whose `.wai/specs` holds
|
|
31
|
+
an L0 — but the walk **never climbs past the repository root** (the nearest folder
|
|
32
|
+
holding `.git`) unless a project above it declares the crossing as a member
|
|
33
|
+
(`members` naming the repository's folder, or a folder inside it). A stray `.wai`
|
|
34
|
+
in a parent folder therefore binds nothing in a child repository. Outside any
|
|
35
|
+
repository the walk is unbounded. Each command that acts on a project prints
|
|
36
|
+
`project <id> at <root>` on stderr first, so `wairon export` and every `--json`
|
|
37
|
+
output keep a pure stdout.
|
|
38
|
+
|
|
39
|
+
### `wairon status [--subsystem <id>] [--no-recursive] [--all]`
|
|
40
|
+
Print the SDD spec tree as a hierarchy. Its percentages measure **authoring
|
|
41
|
+
progress** — 80% once a component's component, contract and implementation specs
|
|
42
|
+
are written, 100% once its implementation names source files that exist, capped
|
|
43
|
+
at 50% while any of them is `draft` or `design` — and are
|
|
44
|
+
separate from **approval**, which is the lock record in `.wai/lock.json` and is
|
|
45
|
+
printed on its own line. `wairon lock` never rewrites a spec's status, so an
|
|
46
|
+
approved tree can still show draft specs. `--subsystem <id>` shows one
|
|
47
|
+
subsystem; `--no-recursive` shows this project without its members; `--all` lists
|
|
48
|
+
every spec that moved since the approval instead of the first few.
|
|
49
|
+
Each member project prints as `[Project] alias (id)` holding its own subsystems,
|
|
50
|
+
with its **approval state** computed at the member's own root (`approved`,
|
|
51
|
+
`drifted`, `never`) and how this project's lock pinned it (`matches`, `moved`,
|
|
52
|
+
`unpinned`). The report closes with this project's own state. Asked at the parent
|
|
53
|
+
or at the member, the answer is the same.
|
|
54
|
+
|
|
55
|
+
### `wairon validate [--ci] [--all] [--subsystem <id>] [--family] [--no-recursive]`
|
|
56
|
+
Run the architecture-conformance gate over the spec tree: reference integrity,
|
|
57
|
+
contract↔implementation method symmetry, narrative-call resolution, component
|
|
58
|
+
stereotype dependency rules, and dependency-cycle detection. `--ci` treats
|
|
59
|
+
warnings as errors (notices are printed and counted, never fatal).
|
|
60
|
+
|
|
61
|
+
- The first 100 findings per severity are printed; the rest are counted ("N
|
|
62
|
+
more not shown") and a per-code total follows. `--all` prints every finding.
|
|
63
|
+
- `--subsystem <id>` validates one subsystem.
|
|
64
|
+
- At a project that declares members, `validate` is the **family run**: every
|
|
65
|
+
member's own gate, and this project's externals composed against their live
|
|
66
|
+
producers. `--no-recursive` runs this project's gate alone; `--family` runs
|
|
67
|
+
the family run from a member.
|
|
68
|
+
|
|
69
|
+
### `wairon generate [--target <name>] [--domain <id>] [--domains <ids>] [--root] [--family] [--no-prune] [--global] [--dry-run]`
|
|
70
|
+
Reconcile the generated guides, skills and context, and — only when the project
|
|
71
|
+
opts in — write the agent files from the spec-derived topology.
|
|
72
|
+
|
|
73
|
+
**Agent files are opt-in.** With `rules.materializeAgentFiles` off (the default
|
|
74
|
+
for a new project) no agent file is written, and a run removes the ones an
|
|
75
|
+
earlier version wrote; agents are served as live briefs instead
|
|
76
|
+
(`sdd_get_agent_brief`, `wairon agent brief <id>`). Set
|
|
77
|
+
`rules.materializeAgentFiles: true` in `.wai/project.yaml` to keep agent files
|
|
78
|
+
on disk (`rules.generateComponentImplementers: true` adds one implementer per
|
|
79
|
+
component).
|
|
80
|
+
|
|
81
|
+
- `--target <name>` limits generation to one of the project's configured
|
|
82
|
+
targets (`claude`, `agy`, …); an unknown name exits non-zero and lists the
|
|
83
|
+
targets the project configures.
|
|
84
|
+
- `--domain <id>` / `--domains <ids>` / `--root` limit it to some domains.
|
|
85
|
+
- `--no-prune` keeps wairon-managed agent files that left the topology.
|
|
86
|
+
- `--global` also writes a target whose output directory is outside the project
|
|
87
|
+
root, backing up each file it replaces.
|
|
88
|
+
- `--dry-run` previews without writing.
|
|
89
|
+
|
|
90
|
+
`generate` writes **only this project's** outputs. A parent's topology lists a
|
|
91
|
+
member's agents by reference (`delegatesTo: <alias>::<agentId>`) instead of
|
|
92
|
+
copying them, and a brief for `<alias>::<agent>` composes at the member's own
|
|
93
|
+
root. `--family` also generates each member's own layer, in its own root.
|
|
94
|
+
(`--no-recurse` is accepted for one release; not cascading is now the default.)
|
|
95
|
+
|
|
96
|
+
### `wairon lock [-y, --yes] [--subsystem <id>]`
|
|
97
|
+
Review and approve the design. Validates the spec tree **as if complete** (full
|
|
98
|
+
strictness, no draft-status relaxation) and — only if the **design** passes —
|
|
99
|
+
records the current tree as approved and refreshes this project's generated
|
|
100
|
+
outputs (agent files only when `rules.materializeAgentFiles` is on).
|
|
101
|
+
|
|
102
|
+
**It approves; it does not change statuses.** No spec's `status` is rewritten:
|
|
103
|
+
approval lives in `.wai/lock.json`. Implementation gates on that approval —
|
|
104
|
+
`wairon lock-check` in CI, `sdd_get_status` for an AI tool — not on
|
|
105
|
+
`status: complete`. Commit `.wai/lock.json` with the change it approves.
|
|
106
|
+
|
|
107
|
+
**What it certifies is the design.** Only design findings can refuse a lock.
|
|
108
|
+
Code-conformance findings (the code↔spec checks) are recorded **beside** the
|
|
109
|
+
claim in the record's `code` block, with the analyzer that produced them, and
|
|
110
|
+
printed as `code: N error(s), … recorded beside the claim`. CI enforces them:
|
|
111
|
+
`wairon validate --ci` still fails on a code error. A design can be approved
|
|
112
|
+
before its code exists.
|
|
113
|
+
|
|
114
|
+
**It approves this project only.** The gate identity it records covers this
|
|
115
|
+
project's own specs, the design doctrine, its declared inputs, its `composition`
|
|
116
|
+
block, and each direct member's **composition subject** — the `stateId` in the
|
|
117
|
+
member's own lock record. The record (format 2) lists each direct member under
|
|
118
|
+
`members` with its subject and state (`approved`, `drifted`, `never`). Nothing is
|
|
119
|
+
written below the project: each member locks at its own root. With
|
|
120
|
+
`composition.requireApprovedMembers: true` in `project.yaml`, the lock refuses
|
|
121
|
+
while a direct member is drifted or never approved, naming each.
|
|
122
|
+
|
|
123
|
+
The identity is captured **before** validating and confirmed **before** writing:
|
|
124
|
+
if a spec, the doctrine, an input or a member's approval moves while the lock
|
|
125
|
+
runs, it refuses and writes nothing. (`--no-recursive` is accepted for one
|
|
126
|
+
release and changes nothing: a lock never reaches below its project.)
|
|
127
|
+
|
|
128
|
+
It writes **nothing into your spec tree**. The approval is one sha256 per spec
|
|
129
|
+
file on `.wai/lock.json`, the record that was always committed — so your
|
|
130
|
+
teammates, a fresh clone and CI all see the same approval you gave, and
|
|
131
|
+
`wairon status` elsewhere can name what has drifted from it. Keys are sorted, so
|
|
132
|
+
re-approving a one-spec change shows up as a two-line diff. A failed or
|
|
133
|
+
cancelled lock changes nothing at all.
|
|
134
|
+
|
|
135
|
+
`lockedBy` records who approved **and how that identity was established**: your
|
|
136
|
+
git author identity where there is one (so a reviewer can match it against the
|
|
137
|
+
author of the commit carrying the lock), else `user@hostname`. On a hosted
|
|
138
|
+
instance it is the authenticated subject. None of it is proof — the commit that
|
|
139
|
+
introduces `lock.json` is what carries that.
|
|
140
|
+
|
|
141
|
+
Before approving, it reports what moved since the last approval — the question
|
|
142
|
+
a human is actually answering:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
3 spec(s) changed since the last approval:
|
|
146
|
+
~ sdd_core/spec_loader/.index.yaml
|
|
147
|
+
~ sdd_core/spec_loader/.interface.yaml
|
|
148
|
+
+ sdd_core/spec_index/.index.yaml
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`--yes` skips the confirmation (for CI); `--subsystem`
|
|
152
|
+
limits the scope. A shell with no terminal and no `--yes` writes nothing.
|
|
153
|
+
|
|
154
|
+
### `wairon lock-check [--strict]`
|
|
155
|
+
The **merge gate**. One question, one exit code: *is the design in this working
|
|
156
|
+
tree the design that was approved?* It compares the tree's gate identity against
|
|
157
|
+
the one recorded in `.wai/lock.json` **as it is in the working tree**. In CI that
|
|
158
|
+
is the file committed in the checked-out revision; locally an uncommitted lock
|
|
159
|
+
record counts too, so commit it before you rely on a local pass.
|
|
160
|
+
|
|
161
|
+
| What it finds | Default | `--strict` |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| **`locked`** — the approval still covers this design | pass (0) | pass (0) |
|
|
164
|
+
| **`stale`** — the design moved past its approval | **fail (1)** | **fail (1)** |
|
|
165
|
+
| **`unlocked`** — nothing was ever approved | pass, with a notice (0) | **fail (1)** |
|
|
166
|
+
| no `.wai/specs` in this directory at all | pass, saying so (0) | **fail (1)** |
|
|
167
|
+
| a member project has spec changes nobody approved at its own root | **fail (1)** | **fail (1)** |
|
|
168
|
+
| a member project was never approved by anyone | pass, naming it as *not gated* (0) | **fail (1)** |
|
|
169
|
+
| only a member's own approval moved (it re-locked) | **fail (1)**, naming the member: re-lock here to pin its new approval | **fail (1)** |
|
|
170
|
+
|
|
171
|
+
**What plain `lock-check` guarantees:** that a *committed* approval still covers the
|
|
172
|
+
design, and that no member project carries unapproved changes. Nothing more: a
|
|
173
|
+
project with no `.wai/lock.json` — never locked, *or the record deleted in the
|
|
174
|
+
pull request* — passes, because it reads exactly like one that never opted in.
|
|
175
|
+
**In CI, run `wairon lock-check --strict`** (or the reusable workflow with
|
|
176
|
+
`strict: true`) once the project has been locked: then a missing record, or a
|
|
177
|
+
member nobody ever approved, fails.
|
|
178
|
+
|
|
179
|
+
**Members.** The gate identity pins each direct member's *recorded* approval, so
|
|
180
|
+
the root reads every member project's own approval at its own root too. A member
|
|
181
|
+
whose specs moved past its own approval fails the root's check — run `wairon lock`
|
|
182
|
+
in that member's folder. A member that re-locked is expected (its own approver
|
|
183
|
+
signed it off), and the root is judged by whether its pin of the member is still
|
|
184
|
+
the member's approved one: after a member re-locks it is not, and the verdict says
|
|
185
|
+
exactly that — no own spec moved, run `wairon lock` here to pin the new approval.
|
|
186
|
+
|
|
187
|
+
A lock taken by a wairon release before 6.0.0 reads `stale` once, because the
|
|
188
|
+
gate identity gained inputs. The message says so — the approval *was taken under
|
|
189
|
+
an earlier gate identity* — and whether any own spec file changed since; re-lock
|
|
190
|
+
once and commit `.wai/lock.json`.
|
|
191
|
+
|
|
192
|
+
**It is optional by construction.** By default only a moved approval (or a
|
|
193
|
+
member's unapproved changes) refuses, and neither can happen in a project that
|
|
194
|
+
never locked — so adding this to an existing repository's CI cannot make it start
|
|
195
|
+
failing. `--strict` is what turns "never approved" into a failure, and a project
|
|
196
|
+
has to ask for it — which is why a project that has locked should run it strict.
|
|
197
|
+
|
|
198
|
+
It is **not** `wairon validate`. Validate asks whether the design is *legal* and
|
|
199
|
+
runs the whole rule set to answer; this asks whether it is *approved* and reads
|
|
200
|
+
one JSON file, which is why it is safe to run on every pull request. The two are
|
|
201
|
+
independent — a tree can be approved and illegal, or legal and unapproved — so
|
|
202
|
+
run both.
|
|
203
|
+
|
|
204
|
+
It gates on the **gate identity** (the hashed parsed tree plus the governing
|
|
205
|
+
doctrine), not on the per-spec content digests the same lock record carries.
|
|
206
|
+
Those answer "has this file changed since you approved it": a whitespace-only
|
|
207
|
+
edit moves them, and a gate that demands a re-lock for reformatting is one people
|
|
208
|
+
learn to bypass.
|
|
209
|
+
|
|
210
|
+
**What it proves:** that the design in the commit being merged is the design that
|
|
211
|
+
was approved. **What it does not prove:** *who* approved. `lockedBy` is a claim,
|
|
212
|
+
not an attestation — anyone who can run the CLI can write a lock record. Pull
|
|
213
|
+
request review is what establishes who reviewed; this establishes that the thing
|
|
214
|
+
merging is the thing that was reviewed.
|
|
215
|
+
|
|
216
|
+
#### Using it in GitHub Actions
|
|
217
|
+
|
|
218
|
+
This repository publishes it as a **reusable workflow** (from v6.0.0 — the
|
|
219
|
+
workflow and the `lock-check` command do not exist in earlier releases). Add one
|
|
220
|
+
job to your own workflow:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
# .github/workflows/ci.yml in YOUR repository
|
|
224
|
+
on: [pull_request]
|
|
225
|
+
|
|
226
|
+
jobs:
|
|
227
|
+
approved-design:
|
|
228
|
+
uses: SYW-Apps/Waffle-AIron/.github/workflows/lock-check.yml@v6.0.0
|
|
229
|
+
with:
|
|
230
|
+
wairon-version: '6.0.0'
|
|
231
|
+
strict: true # a deleted .wai/lock.json fails instead of switching the gate off
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
With inputs (all optional):
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
approved-design:
|
|
238
|
+
uses: SYW-Apps/Waffle-AIron/.github/workflows/lock-check.yml@v6.0.0
|
|
239
|
+
with:
|
|
240
|
+
working-directory: packages/api # where the .wai/ tree lives (default: .)
|
|
241
|
+
wairon-version: '6.0.0' # version or npm dist-tag, 6.0.0 or later (default: latest)
|
|
242
|
+
strict: true # fail when nothing was approved — use it once you have locked (default: false)
|
|
243
|
+
node-version: '20' # (default: '20')
|
|
244
|
+
runs-on: ubuntu-latest # (default: ubuntu-latest)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Pin `@<ref>` to a **tag**, never to `main` or `dev`. A moving branch means the
|
|
248
|
+
check that gates your merges can change under you between two runs of the same
|
|
249
|
+
commit — and this one decides whether code merges. Pin `wairon-version` too, to
|
|
250
|
+
the version you lock with: the gate identity's algorithm can change between
|
|
251
|
+
releases, and the default `latest` moves.
|
|
252
|
+
|
|
253
|
+
On a `pull_request` event the default checkout is the merge commit, so what the
|
|
254
|
+
gate judges is literally the design that would land.
|
|
255
|
+
|
|
256
|
+
**A failing job does not block a merge on its own.** A workflow can only fail;
|
|
257
|
+
making a failing job stop a merge is a branch-protection / ruleset setting on
|
|
258
|
+
your repository ("Require status checks to pass" → add this job). No workflow
|
|
259
|
+
can declare that for itself.
|
|
260
|
+
|
|
261
|
+
### `wairon doctor [--fix] [-y, --yes] [--global] [--report <section>]`
|
|
262
|
+
Health check: flags stale generated guides/skills, an unregistered MCP server,
|
|
263
|
+
spec-tree issues and the member/reference migration still pending. `--fix`
|
|
264
|
+
regenerates stale in-project guides/context/skills, registers the MCP server and
|
|
265
|
+
applies the spec repairs, then the member migration once confirmed (`--yes`
|
|
266
|
+
answers that confirmation in a script). A write outside the project root (a
|
|
267
|
+
machine-wide MCP config) also needs `--global`; each file it replaces is backed
|
|
268
|
+
up beside itself. `--report <section>` prints one section's report and writes
|
|
269
|
+
nothing: `chaining` (the member/reference migration plan) or
|
|
270
|
+
`composed-validation` (what the family run changes about each member's
|
|
271
|
+
findings); it never combines with `--fix`.
|
|
272
|
+
|
|
273
|
+
Among its spec repairs, `--fix` rewrites every stored type position that is an
|
|
274
|
+
alias of its canonical spelling (`string[]` becomes `list<string>`, `boolean`
|
|
275
|
+
becomes `bool`, `T | null` becomes `T?`, `Promise<T>` becomes `async T`) — what
|
|
276
|
+
any later save would write. It never guesses: a `number` position whose name
|
|
277
|
+
says a whole number (`count`, `maxDepth`, `port`) gets `int` *proposed* and
|
|
278
|
+
listed, and every position no rewrite can settle (an inline function type, a
|
|
279
|
+
literal union, a union mixing in a primitive, a `number` with no proposal) is
|
|
280
|
+
listed with its replacement for an author. Plain `wairon doctor` prints the same
|
|
281
|
+
plan without writing it.
|
|
282
|
+
|
|
283
|
+
### `wairon list` (alias `ls`) / `wairon show <id>`
|
|
284
|
+
List, or show full details of, the agents resolved from the spec tree
|
|
285
|
+
(`system-architect`, `<subsystem>-owner`, owners for free-standing domains, and
|
|
286
|
+
a `<component>-implementer` per component when
|
|
287
|
+
`rules.generateComponentImplementers` is on).
|
|
288
|
+
|
|
289
|
+
### `wairon agent brief <id>` / `wairon agent customize <id>`
|
|
290
|
+
`brief` prints the live delegation brief for one agent — the same composition
|
|
291
|
+
`sdd_get_agent_brief` returns, rendered from the current spec tree on every
|
|
292
|
+
call. `customize` scaffolds the user-owned guidance file `.wai/agents/<id>.md`,
|
|
293
|
+
which later briefs for that agent include.
|
|
294
|
+
|
|
295
|
+
### `wairon rules list`
|
|
296
|
+
List the SDD conformance rule registry — every rule group, the issue codes it
|
|
297
|
+
can emit, default severities, and any per-project overrides from
|
|
298
|
+
`rules.sddRuleSeverity`. The gate is a documented architecture linter: each
|
|
299
|
+
rule is a self-contained module in `src/core/rules/`. Rules injected by
|
|
300
|
+
extension packs are listed too, tagged with their pack.
|
|
301
|
+
|
|
302
|
+
### `wairon execution show` (alias `ls`) / `wairon execution set-tier <tier>`
|
|
303
|
+
Execution budgets — the resource axis of the derived topology. Where authority
|
|
304
|
+
says which paths an agent owns, a budget says what its work costs to do:
|
|
305
|
+
a capability tier, a reasoning effort, a turn ceiling, a tool class, and
|
|
306
|
+
whether it may delegate further or load MCP servers.
|
|
307
|
+
|
|
308
|
+
`show` lists every agent's allowance next to the rationale that produced it
|
|
309
|
+
(component stereotype, owned-path breadth, role), so a tier choice is auditable
|
|
310
|
+
rather than magic. It also flags any agent an override has pinned to the
|
|
311
|
+
`frontier` tier — derivation never selects it.
|
|
312
|
+
|
|
313
|
+
`set-tier` moves the aggressiveness dial and states what the new tier costs:
|
|
314
|
+
|
|
315
|
+
| Tier | What it does |
|
|
316
|
+
|------|--------------|
|
|
317
|
+
| `off` (default) | No budgets derived. Output is byte-identical to before this feature existed. |
|
|
318
|
+
| `free` | Structural constraints only — tool class, MCP access, nested delegation. No model or effort selection, so no quality tradeoff at all. |
|
|
319
|
+
| `default` | Adds capability-tier selection per role and turn ceilings. Mechanical work (Store, Index, Registry, Adapter) runs smaller; work carrying decisions (Orchestrator, Supervisor) keeps the capable tier. |
|
|
320
|
+
| `trade` | Adds effort reduction on mechanical work and steps standard work down a tier. Real but bounded quality cost. |
|
|
321
|
+
| `aggressive` | Small tier for everything but deep reasoning, halved turn ceilings. Expect partial results and worse judgment. |
|
|
322
|
+
|
|
323
|
+
Raising the dial can only tighten a budget, so it is safe to turn without
|
|
324
|
+
auditing every agent. Per-agent overrides live in `.wai/project.yaml` under
|
|
325
|
+
`execution.overrides`.
|
|
326
|
+
|
|
327
|
+
### `wairon pack …`
|
|
328
|
+
Extension packs — plain config files (YAML, or a JS module for programmatic
|
|
329
|
+
rules) injecting custom profiles, language/platform tables, and conformance
|
|
330
|
+
rules (see [Extending wairon](extending-wairon.md)). `wairon packs` is a
|
|
331
|
+
deprecated alias of `wairon pack`.
|
|
332
|
+
|
|
333
|
+
| Command | Description |
|
|
334
|
+
|---------|-------------|
|
|
335
|
+
| `wairon pack init <name> [--kind declarative\|code] [--dir <path>] [--skill]` | Scaffold a new pack project |
|
|
336
|
+
| `wairon pack build [source] [--out <file>]` | Build an installable `.wpack` archive from a pack directory |
|
|
337
|
+
| `wairon pack install <source> [-y]` | Install a pack (`.wpack`/`.zip` or a directory) into this machine's pack store. It applies to nothing until a project selects it |
|
|
338
|
+
| `wairon pack uninstall <name>[@version]` | Remove a pack from the store |
|
|
339
|
+
| `wairon pack use <name>[@version] [--source <url>] [--pin] [--bundle] [-y]` | Select an installed pack for this project, recorded in `.wai/project.yaml` |
|
|
340
|
+
| `wairon pack unuse <name> [-y]` | Deselect a pack for this project (it stays installed) |
|
|
341
|
+
| `wairon pack impact <name>[@version]` | What a pack changes here, writing nothing (below) |
|
|
342
|
+
| `wairon pack sync` | Install every declared-but-missing pack from the source its selection records — what a fresh machine or CI runner needs |
|
|
343
|
+
| `wairon pack bundle [name] [--all]` | Commit a copy of a selected pack under `.wai/packs/`, so a clone and CI need no pack store |
|
|
344
|
+
| `wairon pack which <name>` | Which installed pack a name resolves to: version, path, digest, origin |
|
|
345
|
+
| `wairon pack list` | Global and project packs and what each provides, with load errors inline |
|
|
346
|
+
| `wairon pack add <source> [--global]` / `wairon pack remove <name> [--global]` | Vendor a pack file or directory into `.wai/packs/` (or install it machine-wide), or remove it |
|
|
347
|
+
|
|
348
|
+
The vendoring commands in detail:
|
|
349
|
+
|
|
350
|
+
- `add <source>`: verify the pack loads, then vendor it into `.wai/packs/`
|
|
351
|
+
and register it in `project.yaml → extensions.packs` (commit `.wai/` so CI
|
|
352
|
+
and every clone enforce it). With `--global`, install machine-wide into
|
|
353
|
+
`WAIRON_PACKS_DIR` / `~/.wairon/packs` — auto-loaded for every project on
|
|
354
|
+
this machine (project packs win on collision; a project opts out via
|
|
355
|
+
`extensions.useGlobalPacks: false`). A source may be a file or a directory
|
|
356
|
+
with a pack entry file (`pack.yaml` | `pack.cjs` | `index.cjs` | …).
|
|
357
|
+
- `list`: global + project packs and what each provides (profiles,
|
|
358
|
+
languages, rules), with load errors inline.
|
|
359
|
+
- `remove <name>`: deregister by pack name and delete vendored files under
|
|
360
|
+
`.wai/packs/` (files elsewhere are left in place); `--global` removes a
|
|
361
|
+
machine-wide pack.
|
|
362
|
+
|
|
363
|
+
The store and selection commands (`install | uninstall | which | use | unuse |
|
|
364
|
+
impact | bundle | sync`) are described in
|
|
365
|
+
[Extending wairon](extending-wairon.md#installing-and-selecting-packs).
|
|
366
|
+
|
|
367
|
+
`wairon patterns list` lists the reusable, versioned architecture patterns the
|
|
368
|
+
loaded packs declare; `wairon variants list` lists the component variants (each
|
|
369
|
+
anchored on a base block, with its implementation guidance).
|
|
370
|
+
|
|
371
|
+
### `wairon pack impact <name>[@version]` — and confirm before every pack write
|
|
372
|
+
Packs exist to adjust wairon's checks and behaviour, and a pack may loosen or
|
|
373
|
+
remove them by design (an automation platform cannot hold every concept a full
|
|
374
|
+
backend can). So installing a pack is never judged — but it is always
|
|
375
|
+
intentional, and its impact is shown first.
|
|
376
|
+
|
|
377
|
+
`wairon pack impact` shows, writing nothing, what a pack changes on this
|
|
378
|
+
project:
|
|
379
|
+
|
|
380
|
+
- its **doctrine against wairon's defaults**, by the profile that carries each
|
|
381
|
+
change: rules loosened, raised, turned off or added; profiles added or a
|
|
382
|
+
builtin redefined; stereotypes removed or discouraged, edges licensed,
|
|
383
|
+
patterns, guarantee tokens and language tables added;
|
|
384
|
+
- the **profiles of it that would govern** here (the `projectType` and each
|
|
385
|
+
subsystem profile that names one);
|
|
386
|
+
- the **findings that change** on this project — introduced, resolved and
|
|
387
|
+
regraded, from a dry validate of the current and the candidate
|
|
388
|
+
configuration — with both totals.
|
|
389
|
+
|
|
390
|
+
A pack this project does not apply (or applies at another version) is measured
|
|
391
|
+
as applied; one it applies exactly as asked is measured as removed, so the
|
|
392
|
+
report reads as what the pack accounts for now. Nothing in the report is a
|
|
393
|
+
finding.
|
|
394
|
+
|
|
395
|
+
Every command that selects, updates or removes a project's pack — `pack use`,
|
|
396
|
+
`pack unuse`, `pack add`, `pack remove`, and `pack install` when it moves this
|
|
397
|
+
project's floating selection — **shows the same report and asks before it
|
|
398
|
+
writes**; anything but yes writes nothing. In a script, pass `-y, --yes`: the
|
|
399
|
+
write happens without the report, and the command says it applied without
|
|
400
|
+
showing it. A run with no terminal to ask on (CI, a pipe) behaves the same way.
|
|
401
|
+
|
|
402
|
+
### `wairon diagram [--format <fmt>] [--subsystem <id>] [--sequence <component:method>] [--depth <n>] [--all] [--out <path>] [--no-health]`
|
|
403
|
+
Generate architecture diagrams derived from the spec tree — living
|
|
404
|
+
documentation from the same source of truth as the conformance gate. Every
|
|
405
|
+
format writes a file (by default under `.wai/docs/diagrams/`; `--out` names
|
|
406
|
+
another file or directory); nothing opens automatically.
|
|
407
|
+
|
|
408
|
+
- **default (no flags): the interactive canvas** (`canvas.html`).
|
|
409
|
+
- `--format mermaid` (or `--subsystem <id>`): system-wide or scoped Mermaid
|
|
410
|
+
**component diagram** as a markdown file (subsystems as subgraphs,
|
|
411
|
+
`dependsOn` edges, thick edges for boundary hops, dashed `owns`
|
|
412
|
+
containment, bold border on the published surface). Use a `.mmd` `--out`
|
|
413
|
+
path for raw Mermaid.
|
|
414
|
+
- `--subsystem <id>`: scope to one subsystem plus its directly-connected
|
|
415
|
+
external neighbors.
|
|
416
|
+
- `--sequence <component:method>`: a **sequence diagram** derived from the
|
|
417
|
+
method's L5 narrative, recursively expanding `call` steps (cycle-guarded,
|
|
418
|
+
`--depth` limits expansion; default 3).
|
|
419
|
+
- `--canvas`: an **interactive HTML canvas** — the whole system on one page.
|
|
420
|
+
Subsystems as containers, pattern compounds nested inside, components laid
|
|
421
|
+
out in dependency layers (entrypoints left → data right), subsystems in
|
|
422
|
+
topological order (callers left of providers) with barycenter
|
|
423
|
+
crossing-reduction so links stay short and untangled. Pan/zoom,
|
|
424
|
+
double-click a boundary to collapse it (its external edges aggregate into
|
|
425
|
+
labeled "tubes"), click anything for a spec-derived detail panel
|
|
426
|
+
(description, interfaces, methods, endpoints, narratives, dependencies,
|
|
427
|
+
trusted links), search, and a validation-issue overlay. The computed
|
|
428
|
+
layout is locked by default — enable "rearrange" to drag, "reset layout"
|
|
429
|
+
to undo — and "export PNG" renders the full graph to a high-res image for
|
|
430
|
+
Miro/docs/slides. Fully self-contained (Cytoscape.js embedded inline,
|
|
431
|
+
works offline).
|
|
432
|
+
- `--format <mermaid|canvas|drawio|excalidraw>`: friendly alias for the flags below/above.
|
|
433
|
+
- `--drawio` / `--excalidraw`: **editable** exports in open formats
|
|
434
|
+
(draw.io / diagrams.net XML, Excalidraw scene JSON) with the exact same
|
|
435
|
+
computed layout as the canvas — import into diagrams.net, excalidraw.com,
|
|
436
|
+
VS Code extensions, or any whiteboard tool that accepts these formats.
|
|
437
|
+
- `--all`: write the full set (system, per-subsystem, one sequence per
|
|
438
|
+
entrypoint method with a narrative, plus `canvas.html`,
|
|
439
|
+
`architecture.drawio`, and `architecture.excalidraw`) into
|
|
440
|
+
`.wai/docs/diagrams/` (or `--out`).
|
|
441
|
+
- `--no-health`: skip comparing each consumption relation with its live
|
|
442
|
+
producer; the canvas draws those edges as not checked.
|
|
443
|
+
|
|
444
|
+
The canvas is a small single-page app with **C4-style scoped navigation**:
|
|
445
|
+
each view renders one scope's direct children (System → subsystems →
|
|
446
|
+
components → pattern members, infinitely deep by ownership); double-click
|
|
447
|
+
drills in, the breadcrumb navigates back, "Internals" previews children
|
|
448
|
+
inside boxes, "Externals" shows out-of-scope references as ghosts. Plus
|
|
449
|
+
SYW/light themes, presentation mode, per-view layout persistence,
|
|
450
|
+
narrative flowcharts with call drill-down, and an Export menu
|
|
451
|
+
(PNG / draw.io / Excalidraw) capturing the current view and layout.
|
|
452
|
+
|
|
453
|
+
### `wairon export [--out <file>]`
|
|
454
|
+
The whole design, resolved, as one JSON document for generators,
|
|
455
|
+
translators and documentation tools (format `wairon-design`; the
|
|
456
|
+
[format page](design-export.md) describes every field, the compatibility
|
|
457
|
+
promise and how a consumer follows renames).
|
|
458
|
+
|
|
459
|
+
- It first decides the approval verdict exactly as `wairon lock-check` does
|
|
460
|
+
(not strict) and stamps it as `source.approval` (`locked`, `stale` or
|
|
461
|
+
`unlocked`) and `source.approved`. An unapproved tree is exported too,
|
|
462
|
+
carrying its state.
|
|
463
|
+
- Without `--out` it prints the JSON to stdout and nothing else, so it pipes
|
|
464
|
+
cleanly (`wairon export | jq .components`). With `--out` it writes the file
|
|
465
|
+
and reports the path and the approval state.
|
|
466
|
+
- Member projects are listed under `dependencies`, never inlined: export each
|
|
467
|
+
from its own root. So the top of a family that holds no design of its own
|
|
468
|
+
exports no components. It names its members (on stderr without `--out`), so
|
|
469
|
+
that does not read as an empty design. A part's subsystems are the project's
|
|
470
|
+
own and are exported with it.
|
|
471
|
+
- Deterministic: the same tree and verdict give byte-identical output.
|
|
472
|
+
- A tree without an L0 is refused. The JSON Schema ships in the package as
|
|
473
|
+
`schemas/design-export-1.json`, and the library twin is `exportDesign()`.
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Members
|
|
478
|
+
|
|
479
|
+
A project's **members** are declared in its `.wai/project.yaml`, each by one
|
|
480
|
+
location key:
|
|
481
|
+
|
|
482
|
+
```yaml
|
|
483
|
+
members:
|
|
484
|
+
scheduler: services/scheduler # contained
|
|
485
|
+
admin: ../admin # a sibling checkout
|
|
486
|
+
payments: git@host:acme/payments.git#<full commit> # a git repository at a commit
|
|
487
|
+
ledger: { source: services/ledger, description: The books, as: project } # long form
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
A member is a **part** or a **project**, and what it is follows from its
|
|
491
|
+
content — `as:` only asserts it (a contradiction is `MEMBER_KIND_MISMATCH`):
|
|
492
|
+
|
|
493
|
+
- A **part** (the default) is a piece of this project stored in another folder
|
|
494
|
+
or repository. Its subsystems are this project's own: written by local id,
|
|
495
|
+
judged by the ordinary subsystem rules, covered by this project's lock, and
|
|
496
|
+
generated and briefed with this project's agents. It holds only a specs folder
|
|
497
|
+
— plus, when it is not contained, a `project.yaml` holding just
|
|
498
|
+
`partOf: { project: <parent id>, path: <parent root> }`.
|
|
499
|
+
- A **project** is an independent boundary: its tree declares an id, holds an L0
|
|
500
|
+
or carries a lock. It is reached only as `alias::name` through its L0 exports,
|
|
501
|
+
judged by its own gate and locked at its own root. A project stored outside
|
|
502
|
+
this project (`../x`, git, `hosted:`) is a **referenced** project: this
|
|
503
|
+
project's gate judges it against its pin (`wairon externals pin`), and the
|
|
504
|
+
family run opens it where it is stored and composes it per use.
|
|
505
|
+
|
|
506
|
+
Boundaries are earned: grow a system from subsystems, to parts when a piece
|
|
507
|
+
needs its own folder or repository, to projects only when it needs its own team,
|
|
508
|
+
release, approval or public surface.
|
|
509
|
+
|
|
510
|
+
A git member is pinned at a full commit; it is read from the fetch cache
|
|
511
|
+
(`WAIRON_CACHE_DIR`, else the OS user cache directory), immutable and offline
|
|
512
|
+
once fetched. A git part is read-only here — change it in its own repository,
|
|
513
|
+
then move the pin with `wairon member update`. An uncached git part offline is
|
|
514
|
+
`PART_UNAVAILABLE`; an uncached git project falls back to its pin and is
|
|
515
|
+
`EXTERNAL_CHECK_UNAVAILABLE` — never a pass. Every cross-project reference is
|
|
516
|
+
`alias::name`: the alias is one of the referring project's project members or
|
|
517
|
+
declared `externals`, the name a public name in that project's L0 export table.
|
|
518
|
+
An id without `::` is local — a part's subsystems included.
|
|
519
|
+
|
|
520
|
+
| Command | Description |
|
|
521
|
+
|---------|-------------|
|
|
522
|
+
| `wairon member add <alias> <source> [--project] [--description <text>]` | Create a member at `<source>` (`path`, `../path` or `git-url[#commit]`) and declare it by the shorthand. A **part** by default: a specs folder (and, outside this project, its `partOf`). With `--project` a project: its `project.yaml` declaring `<alias>` as its id and its L0, each only when absent. A git member is never scaffolded: it is pinned at the commit given, else the default branch head, and its content decides what it is |
|
|
523
|
+
| `wairon member promote <alias> [--id <id>]` | Make a part an independent project **in place**: its id, an L0 exporting exactly what this project uses of it, references across the new boundary respelled `alias::name` (`<parent id>::name` the other way), the parent declared as its external, pins on both sides. Refused for a part fetched from git, a trustedLink that would cross the boundary, a component used across it that its subsystem does not publish, or an id already taken |
|
|
524
|
+
| `wairon member demote <alias> [--home <subsystem>] [--packs adopt\|drop]` | Make a project member a part **in place** — promote's inverse: its own metadata goes home (as `internalize` sends it), its L0, lock and pins end, references across the old boundary become local ids. Refused while another family project consumes it, or for a member fetched from git |
|
|
525
|
+
| `wairon member update <alias> [--ref <ref> \| --commit <sha>] [--report]` | Move a git member's pinned commit to its ref's head (or the ref or commit given), printing the spec files it adds, changes and removes; `--report` writes nothing. The only way a git member's content changes; this project's lock reads stale until re-locked when the content moved |
|
|
526
|
+
| `wairon member move <alias> <path>` | Move a contained member's directory and point its `members` entry there (a legacy L1 mount is moved into `members` first) |
|
|
527
|
+
| `wairon member attach <alias> <path> [--description <text>]` | Make the **existing** project at `<path>` a member, keeping its L0, subsystems, packs and lock; its id is declared when it only defaulted one (the id its lock approved, else its effective id). Refused when its id collides with a family project's |
|
|
528
|
+
| `wairon member detach <alias> [--widen]` | Take a member out of the family: this project and every family consumer reach it as an external by `source.path`, this project's pinned. Refused (`audience-too-narrow`, each export named with its users) while a family project uses a name the member exports only to the family; `--widen` instead widens exactly those used exports to `instance` in the member's L0, each shown in the plan |
|
|
529
|
+
| `wairon member adopt <alias>` | Make this project's external found by a path inside it a member again — detach's inverse |
|
|
530
|
+
| `wairon member rename-alias <old> <new>` | Rename one alias of this project (a member or an external) and respell this project's references through it; no member or sibling changes |
|
|
531
|
+
| `wairon member internalize <alias> [--into <subsystem>] [--packs adopt\|drop] [--export <name>…]` | Fold a member into this project's own specs folder. A **part** is a storage move: its subsystems move in and nothing else changes. A **project** is demoted first — its own metadata goes to a home (its L0 vision onto the `--into` subsystem, its boundaries, requirements and databases into this L0, its language, profile and depth onto the moved subsystems, its members and externals into this configuration, its packs adopted or dropped), what has no home (its lock, pins, derived outputs) is deleted and listed, and every family project that consumed it is re-pointed here |
|
|
532
|
+
| `wairon project rename <new-id> [--project <alias path>]` | Move a project's id — this project's, or a member's named by its alias path — and every reference to the old id family-wide; the old id is kept in `previousIds`. Lists every project it writes to re-lock |
|
|
533
|
+
| `wairon subsystem externalize <id> --path <dir> [--as part\|project]` | Move an internal subsystem's specs into a **part** at `<dir>` (a new one, declared under the subsystem id, or the existing part there) — a storage move: no reference, export or pin changes, the same verdict. With `--as project` the move is followed by a promote, family-wide: references across the new boundary become `alias::name`, what crosses it is exported and imported, and every other family project's names are checked to keep resolving. You move the source code |
|
|
534
|
+
|
|
535
|
+
On a hosted instance a project member is a project record of its own and
|
|
536
|
+
inherits access from its parent, while a **part is part of its parent's record**:
|
|
537
|
+
it gets no record, and a demote retires the member's record (disabled as "part of
|
|
538
|
+
`<parent>`", audited `member.retired`, its own-scope settings kept) and a promote
|
|
539
|
+
re-enables it; the record's `disabledReason` (shown in the web app and `host
|
|
540
|
+
project list`) says why. Hosted roots are isolated, so a member with a `../` or git source
|
|
541
|
+
is refused there; a `hosted:` project member is its own top-level record.
|
|
542
|
+
`sdd_detach_member` also **moves** the member to an isolated root of its own
|
|
543
|
+
(writing `source: { hosted: <id> }` for its consumers) and `sdd_adopt_member`
|
|
544
|
+
with `path` moves it back. A `source.hosted` producer outside a hosted server is
|
|
545
|
+
reported as "hosted-only producer `<id>`: available only through the hosted
|
|
546
|
+
server".
|
|
547
|
+
|
|
548
|
+
**A part on its own.** A part stored outside its parent can be validated in its
|
|
549
|
+
own repository's CI: run `wairon externals pin` at the part (with the parent on
|
|
550
|
+
disk at `partOf.path`) to pin the excerpt of the parent it uses; `wairon validate`
|
|
551
|
+
there then judges the part against that pin (`PART_JUDGED_ALONE`). Unpinned, it
|
|
552
|
+
says "part of `<parent>`; validate from the parent" (`PART_UNPINNED`).
|
|
553
|
+
|
|
554
|
+
**Family migrations.** `attach`, `detach`, `adopt`, `promote`, `demote`,
|
|
555
|
+
`rename-alias`, `internalize`, `project rename` and `subsystem externalize` all run
|
|
556
|
+
one flow: plan, print the plan (each project's edits, every refusal, the notes,
|
|
557
|
+
the file changes, the projects to re-lock), then apply it all or nothing.
|
|
558
|
+
`--report` prints the plan and writes nothing; otherwise the command asks —
|
|
559
|
+
`--yes` answers, and a shell with no terminal and no `--yes` writes nothing. A
|
|
560
|
+
refused plan exits non-zero and writes nothing. The plan is computed by running
|
|
561
|
+
the verb's writes on a private copy of the family's `.wai` trees (its parts'
|
|
562
|
+
included); applying it stages every change with a backup under each project's
|
|
563
|
+
`.wai/transactions/<id>/` (never committed) and swaps them in, restoring every
|
|
564
|
+
backup on any failure. A crash mid-swap leaves a journal: `wairon status` and
|
|
565
|
+
`wairon validate` show it as a notice, and `wairon doctor --fix` rolls it back.
|
|
566
|
+
No verb ever locks — each names the projects to re-lock. A member stored in a
|
|
567
|
+
`../` sibling checkout on the same volume joins the same transaction: its files
|
|
568
|
+
change all-or-nothing with the family's, the coordinator's journal names every
|
|
569
|
+
owner (so `wairon doctor --fix` recovers from either root), and the plan says
|
|
570
|
+
plainly "files: all-or-nothing; commits: one per repository" — commit each
|
|
571
|
+
repository yourself; no commit across repositories is ever atomic. A sibling on
|
|
572
|
+
another volume is refused (`cross-volume`), and a git member always is (its files
|
|
573
|
+
are a read-only cache). `member demote` names every L0 export entry it removes.
|
|
574
|
+
|
|
575
|
+
**Required packs.** A project may require packs of the members below it with
|
|
576
|
+
`composition.requirePolicies` (see
|
|
577
|
+
[Extending wairon](extending-wairon.md#governance--what-a-pack-changes-and-what-a-parent-requires)).
|
|
578
|
+
`wairon member add --project` — and `wairon init` run in a subdirectory when you
|
|
579
|
+
answer yes on a terminal, which creates a project member the same way — writes those packs into the new member's selection once,
|
|
580
|
+
each pinned to the highest installed version its range admits, sets the
|
|
581
|
+
`projectType` a requirement names, and prints what it applied and each
|
|
582
|
+
requirement nothing installed satisfies. Scaffolding is an unattended pack write,
|
|
583
|
+
so it shows no impact report; run `wairon pack impact <name>` at the member's
|
|
584
|
+
root to see one.
|
|
585
|
+
|
|
586
|
+
**Deprecated forms.** For one release wairon still reads, and reports: a leading
|
|
587
|
+
`::` (`::shared::money`), `super::` (`super::sibling`), a member path
|
|
588
|
+
(`billing::invoice::invoice_portal`) and an L1 subsystem carrying
|
|
589
|
+
`projectPath` (`DEPRECATED_MOUNT_FORM`), and a member's long-form `path` key
|
|
590
|
+
(`{ path: services/x }`; the one location key is `source`). `wairon doctor
|
|
591
|
+
--fix` rewrites them to `members`, the one location key and `alias::name`.
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## Domains
|
|
596
|
+
|
|
597
|
+
A domain is a unit of agent ownership. Subsystem-derived domains come from the
|
|
598
|
+
spec tree (read-only); free-standing domains live in `.wai/topology.yaml`.
|
|
599
|
+
|
|
600
|
+
| Command | Description |
|
|
601
|
+
|---------|-------------|
|
|
602
|
+
| `wairon domains list` | List all domains (subsystem-derived + free-standing) |
|
|
603
|
+
| `wairon domains scan [--add]` | Detect physical directory candidates; `--add` adds selected ones as free-standing domains |
|
|
604
|
+
| `wairon domains add [--path] [--id]` | Manually add a free-standing domain |
|
|
605
|
+
| `wairon domains remove <id>` | Remove a free-standing domain (subsystem-derived domains cannot be removed here) |
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Skills
|
|
610
|
+
|
|
611
|
+
The five SDD skills (`sdd-architect`, `sdd-narrative`, `sdd-auditor`,
|
|
612
|
+
`sdd-implement`, `sdd-delegate`) drive the spec-driven workflow inside your AI
|
|
613
|
+
tool.
|
|
614
|
+
|
|
615
|
+
| Command | Description |
|
|
616
|
+
|---------|-------------|
|
|
617
|
+
| `wairon skills list` | List the built-in SDD skills |
|
|
618
|
+
| `wairon skills install` (alias `sync`) | Install/refresh the skills into each active target's skills dir |
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## MCP
|
|
623
|
+
|
|
624
|
+
The wairon MCP server exposes topology and `sdd_*` tools so AI tools can query
|
|
625
|
+
and author specs directly.
|
|
626
|
+
|
|
627
|
+
| Command | Description |
|
|
628
|
+
|---------|-------------|
|
|
629
|
+
| `wairon mcp serve` | Start the MCP server (stdio transport) |
|
|
630
|
+
| `wairon mcp install [--global] [--config-dir <path>] [--backend claude\|gemini]` | Register the server. Default: project-local. `--global` uses the home config (respects `CLAUDE_CONFIG_DIR`/`GEMINI_CONFIG_DIR`); `--config-dir` installs into an explicit, validated config dir (requires `--backend`) |
|
|
631
|
+
| `wairon mcp install --hosted <url> [--project <id>] [--token <token>]` | Register a **hosted** entry against an instance instead of the local stdio server; the token defaults to the credential stored by `wairon login` |
|
|
632
|
+
| `wairon mcp status` | Show whether the server is registered |
|
|
633
|
+
|
|
634
|
+
A project-local registration (`.mcp.json`, `.gemini/settings.json`) is committed with the
|
|
635
|
+
project, so it holds nothing machine-specific: it runs `wairon mcp serve` from the PATH (or
|
|
636
|
+
`node ./<path>` when the CLI lives inside the project, as in a checkout of wairon itself),
|
|
637
|
+
and the server attaches to the project it is started in. A `--global` registration is
|
|
638
|
+
machine-wide and names the running CLI by its absolute path.
|
|
639
|
+
|
|
640
|
+
The local server offers 37 tools:
|
|
641
|
+
|
|
642
|
+
| Group | Tools |
|
|
643
|
+
|-------|-------|
|
|
644
|
+
| Topology (read) | `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig` |
|
|
645
|
+
| Authoring | `sdd_initialize_system`, `sdd_add_subsystem`, `sdd_set_public_interfaces`, `sdd_add_component`, `sdd_define_interface`, `sdd_set_endpoints`, `sdd_write_narrative`, `sdd_add_type`, `sdd_update_spec`, `sdd_delete_spec` |
|
|
646
|
+
| Reading and checking | `sdd_get_spec`, `sdd_get_status`, `sdd_validate_tree` |
|
|
647
|
+
| Renames and moves (in this tree) | `sdd_rename_component`, `sdd_rename_method`, `sdd_rename_type`, `sdd_move_methods` |
|
|
648
|
+
| Members and family migrations (each takes `dryRun`) | `sdd_add_member`, `sdd_move_member`, `sdd_externalize_subsystem`, `sdd_promote_member`, `sdd_demote_member`, `sdd_internalize_member`, `sdd_attach_member`, `sdd_detach_member`, `sdd_adopt_member`, `sdd_rename_project`, `sdd_rename_member_alias` |
|
|
649
|
+
| Externals | `sdd_pin_externals`, `sdd_get_externals_status` |
|
|
650
|
+
| Packs | `sdd_pack_impact` |
|
|
651
|
+
| Delegation | `sdd_get_agent_brief` (the live brief for one agent; also served as the `wairon-agent://` resource) |
|
|
652
|
+
|
|
653
|
+
A hosted server adds its own `sdd_host_*` and `sdd_landscape_*` tools; see the
|
|
654
|
+
[hosted server guide](design/hosted-mcp-server.md).
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
## Hosting (self-hosted server)
|
|
659
|
+
|
|
660
|
+
`wairon serve` runs wairon as an HTTP server that hosts many **fully-isolated**
|
|
661
|
+
projects behind one endpoint — a public **data plane** (the `sdd_*` tools over
|
|
662
|
+
streamable HTTP, per-project API-key scoped) and an admin **control plane**
|
|
663
|
+
(project & key lifecycle, state-scoped lock). See the
|
|
664
|
+
[hosted server guide](design/hosted-mcp-server.md) for the architecture, Docker
|
|
665
|
+
self-hosting, auth, and sizing.
|
|
666
|
+
|
|
667
|
+
### `wairon serve [--host <h>] [--port <p>] [--admin-host <h>] [--admin-port <p>] [--data-dir <path>] [--no-auth]`
|
|
668
|
+
Start the hosting server. Data plane on `0.0.0.0:8080` (`POST /mcp`, `/healthz`,
|
|
669
|
+
`/readyz`); admin plane on `127.0.0.1:8081` (`/admin/*`). Auth is **on by
|
|
670
|
+
default** and requires `WAIRON_ADMIN_TOKEN` (the master credential) — the server
|
|
671
|
+
refuses to start without it; `--no-auth` disables data-plane auth for a trusted
|
|
672
|
+
network. `--data-dir` (or `WAIRON_DATA_DIR`, default `~/.wairon/data`) is the data
|
|
673
|
+
root holding `projects/` and `auth/`. `WAIRON_AUDIT_POLICY` (optional) sets the
|
|
674
|
+
durable audit policy as a JSON object of overrides laid over the secure default
|
|
675
|
+
(capture `info` and above, keep events 90 days and `security` events 365, skip read
|
|
676
|
+
events, redacted metadata) — e.g. `{"retentionDays":30,"includeReadEvents":true}`.
|
|
677
|
+
Its fields are `enabled` and `includeReadEvents` (booleans), `retentionDays` and
|
|
678
|
+
`securityRetentionDays` (non-negative numbers), `minimumLevel` (`debug`, `info`,
|
|
679
|
+
`warning`, `error` or `security`) and `metadataMode` (`none` or `redacted`; the
|
|
680
|
+
retired `full-redacted` still loads as `redacted`, with a deprecation warning); an unknown field, a wrong type or invalid JSON stops the server at
|
|
681
|
+
startup with a message naming the variable. `WAIRON_QUOTA_POLICY` (optional) sets the
|
|
682
|
+
advisory quota policy the same way, as a JSON object of overrides laid over the
|
|
683
|
+
disabled default (`enabled: false`, mode `observe`, no limits) — e.g.
|
|
684
|
+
`{"enabled":true,"mode":"warn","maxProjectsPerUser":20}`. Its fields are `enabled`
|
|
685
|
+
(boolean), `mode` (`observe` or `warn`, the label an exceeded limit is reported
|
|
686
|
+
with; quotas never block or throttle yet, so the retired `block` loads as `observe`,
|
|
687
|
+
with a deprecation warning) and the non-negative integer
|
|
688
|
+
limits `maxProjectsPerUser`, `maxMcpRequestsPerMinute`, `maxProjectBytes` and
|
|
689
|
+
`maxAuditEventsPerDay`; it is refused at startup exactly as the audit policy is.
|
|
690
|
+
|
|
691
|
+
### `wairon host …` — control plane
|
|
692
|
+
Runs **in-process** (no running server needed), so it works over SSH /
|
|
693
|
+
`docker exec`. Reads the master credential from `WAIRON_ADMIN_TOKEN`.
|
|
694
|
+
|
|
695
|
+
Every project lives in an **organization unit**, and access is a grid of
|
|
696
|
+
**permission assignments** (subject × scope × capability). An API key acts as its
|
|
697
|
+
**owner's** live permissions, narrowed to the projects it names. The first
|
|
698
|
+
project on a new instance:
|
|
699
|
+
|
|
700
|
+
```sh
|
|
701
|
+
wairon host unit create --slug acme # a root unit (business_entity)
|
|
702
|
+
wairon host project create --id shop --unit acme
|
|
703
|
+
wairon host permission set --user alice --capability project:read --project shop
|
|
704
|
+
wairon host permission set --user alice --capability project:write --project shop
|
|
705
|
+
wairon host key mint --project shop --owner alice # → wk_… (shown once)
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
`project:write` does not include `project:read`: a key whose owner can only
|
|
709
|
+
write is refused every read tool. `host permission set` warns when it grants
|
|
710
|
+
`project:write` to a user who cannot read that scope, and `host key mint --owner`
|
|
711
|
+
warns when the owner cannot read the project — each naming the grant that fixes
|
|
712
|
+
it. An owner needs no user record — an id with no record acts as a service
|
|
713
|
+
principal.
|
|
714
|
+
|
|
715
|
+
| Command | Description |
|
|
716
|
+
|---------|-------------|
|
|
717
|
+
| `wairon host unit create --slug <slug> [--kind <kind>] [--name <name>] [--parent <unitId>]` | Create an organization unit. A root unit (no `--parent`) must be a `business_entity`, which is its default kind; under `--parent` the default is `team`, and `department`, … are allowed where the hierarchy admits them |
|
|
718
|
+
| `wairon host project create --id <id> --unit <unitId>` | Provision a new isolated project (its own `.wai/` tree) in a unit |
|
|
719
|
+
| `wairon host project list` | List hosted projects |
|
|
720
|
+
| `wairon host project destroy --id <id>` | Remove a project and its tree |
|
|
721
|
+
| `wairon host demo [--id <id>] [--unit <unitId>] [--force]` | Provision a project seeded with an example spec tree (default id and unit `demo`; the unit is created if absent), so the canvas has content |
|
|
722
|
+
| `wairon host permission set --user <userId> --capability <cap> [--value yes\|approval\|no\|inherit] [--project <id> [--subsystem <id>] \| --unit <unitId> \| --instance]` | Set one assignment. Capabilities: `project:read`, `project:create`, `project:write`, `project:admin`, `approval:decide`. The scope defaults to the instance |
|
|
723
|
+
| `wairon host permission list [--user <userId>] [scope flags]` / `remove --id <assignmentId>` | List or remove assignments |
|
|
724
|
+
| `wairon host key mint --project <id\|*> --owner <userId> [--label <label>]` | Mint an API key acting as the owner's live permissions (plaintext shown once). The project must exist (`*` aside); a token naming a project covers its members. Without `--owner` (the legacy `--role editor\|admin` mint) the key resolves to **zero** permissions and the command warns |
|
|
725
|
+
| `wairon host doctor [--fix]` | Inspect the data dir and, with `--fix`, migrate it: roll back a transaction a crash left unfinished there, apply the permission-model migration, then register every hosted family's members as records of their own (no grant written — access is inherited through the parent chain — and every member-qualified key entry rewritten to a record id), all or nothing, audited |
|
|
726
|
+
| `wairon host key list [--project <id>]` | List API keys |
|
|
727
|
+
| `wairon host key revoke --id <id>` | Revoke a key |
|
|
728
|
+
| `wairon host lock --project <id>` | The same lock flow as `wairon lock` (design gate, `members`, `code` beside the claim, format 2) against the hosted project |
|
|
729
|
+
| `wairon host git enable \| disable \| sync \| commit \| status \| sync-config` | Bind a hosted project to its real repository (wairon commits only `.wai/`) |
|
|
730
|
+
| `wairon host producer configure \| produce \| remove \| list` | Project a hosted project to Notion or Miro |
|
|
731
|
+
| `wairon host secret set \| list` | Set integration secrets at runtime (`git-token`, `notion-token`, `miro-token`, `signing-secret`) — no restart |
|
|
732
|
+
| `wairon host packs list [--project <id>]` | List the server-global packs, or one hosted project's |
|
|
733
|
+
| `wairon host packs install --file <pack.yaml> [--name <n>] [--project <id>] [-y]` | Install a declarative pack server-wide, or into a hosted project. Into a project it first shows the pack's impact on that project (measured by the host, writing nothing) and asks; `--yes`, or no terminal, installs without the report and says so |
|
|
734
|
+
| `wairon host packs remove --name <n> [--project <id>] [-y]` | Remove a pack; from a project it first shows what the pack accounts for there and asks, as install does |
|
|
735
|
+
|
|
736
|
+
The hosted API and web UI preview the same way:
|
|
737
|
+
`POST /admin/projects/{id}/packs/{name}/impact` (a declarative pack's YAML as
|
|
738
|
+
`content`, or no body to preview adopting the server-global pack of that name)
|
|
739
|
+
and `POST /admin/projects/{id}/packs/{name}/removal-impact`; both need
|
|
740
|
+
`project:read` and write nothing. The hosted MCP data plane offers the same
|
|
741
|
+
preview as `sdd_host_pack_impact`. The unattended hosted policy writes —
|
|
742
|
+
reconcile, a project type change, and a policy-governed project creation —
|
|
743
|
+
return the impact of every pack they applied in their results.
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
## Connecting to a hosted instance
|
|
748
|
+
|
|
749
|
+
| Command | Description |
|
|
750
|
+
|---------|-------------|
|
|
751
|
+
| `wairon login <url> [--token <token>] [--project <id>]` | Store a bearer credential for a hosted instance on this machine (`--project` verifies it against that project first; the token defaults to `WAIRON_REMOTE_TOKEN`) |
|
|
752
|
+
| `wairon logout [url]` | Forget a stored credential (local only — it does not revoke it), or list what is stored |
|
|
753
|
+
| `wairon remote push \| pull [--url] [--project] [--token] [--unit <id>] [--force] …` | Migrate a spec tree to or from a hosted project (`--unit` creates the destination project first) |
|
|
754
|
+
| `wairon remote attach \| detach \| status` | Bind this checkout to a hosted project; while attached, `wairon lock` locks the hosted project |
|
|
755
|
+
|
|
756
|
+
## Surfaces, externals and producers
|
|
757
|
+
|
|
758
|
+
| Command | Description |
|
|
759
|
+
|---------|-------------|
|
|
760
|
+
| `wairon externals pin [alias…] \| status \| list [--json]` | Pin declared externals into `.wai/externals/<alias>.yaml`; `status` compares each pin with its live producer per used member; `list` shows what is declared |
|
|
761
|
+
| `wairon surface export \| import \| list [--audience <level>] [--format native\|openapi] [--portal <id>] [--out <path>] [--source <path>]` | Exchange a public surface document: export this project's (native snapshot or one OpenAPI document per portal), import one, or list them |
|
|
762
|
+
| `wairon produce <notion\|miro> [--page <id>] [--token <token>]` | Project the local spec tree to Notion or Miro (the token comes from `--token`, the environment, else a prompt; nothing is stored) |
|
|
763
|
+
|
|
764
|
+
## Tooling
|
|
765
|
+
|
|
766
|
+
| Command | Description |
|
|
767
|
+
|---------|-------------|
|
|
768
|
+
| `wairon dev [--port <port>] [--open]` | A local single-project dev server: the wairon web UI over the current project, no login or tenancy (loopback only) |
|
|
769
|
+
| `wairon update [--check] [--channel <name>]` | Check/install the latest release; switch channel |
|
|
770
|
+
| `wairon aliases list` | Show command aliases (`wai`) and their status |
|
|
771
|
+
| `wairon aliases enable <name>` / `disable <name>` | Create / remove an alias |
|