austere-axi 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,11 +8,7 @@
8
8
  [![node](https://img.shields.io/node/v/austere-axi)](https://www.npmjs.com/package/austere-axi)
9
9
  [![license](https://img.shields.io/npm/l/austere-axi)](LICENSE)
10
10
 
11
- Review artifacts for coding agents, written in [OpenUI Lang](https://github.com/thesysdev/openui) instead of hand-written HTML.
12
-
13
- The agent writes a short program: one named statement per line, built from a fixed set of components. The reviewer opens it in the browser, points at anything, and the agent gets the feedback back addressed by statement name. To revise, the agent edits only the statements the feedback names, and the page updates in place.
14
-
15
- It is the same loop as [lavish-axi](https://github.com/kunchenguid/lavish-axi). The difference is that the agent never writes CSS or layout.
11
+ Review pages for coding agents. The agent writes a short [OpenUI Lang](https://github.com/thesysdev/openui) program, not HTML. You open it in the browser, click on any part, and write a note. The agent gets each note with the name of the statement it points at. It edits only those statements, and the page updates in place.
16
12
 
17
13
  ```
18
14
  root = Stack([intro, flow, options, decide], "column", "l")
@@ -31,78 +27,76 @@ decide = Form("rollout", Buttons([Button("Send decision", @ToAssistant("Decision
31
27
  strategyField = FormControl("Rollout", RadioGroup("strategy", [RadioItem("Big bang", "", "bigbang"), RadioItem("Dual-write", "", "dualwrite")]))
32
28
  ```
33
29
 
34
- [`examples/session-migration.html`](examples/session-migration.html) is a full page: a KPI row, a diagram, a table, a chart, a diff, and a decision form. The whole file is about 1,000 tokens.
30
+ [`examples/session-migration.html`](examples/session-migration.html) is a full page (KPIs, diagram, table, chart, diff, decision form) in about 1,000 tokens.
35
31
 
36
32
  ## Why
37
33
 
38
- Lavish makes the agent author an entire HTML page every time, and re-author it after every round of feedback. That is slow, because output tokens are the slow part, and it is expensive.
39
-
40
- austere-axi changes three things:
34
+ The loop is the same as [lavish-axi](https://github.com/kunchenguid/lavish-axi). The difference: the agent never writes HTML, CSS or layout.
41
35
 
42
36
  | | lavish-axi | austere-axi |
43
37
  |---|---|---|
44
- | What the agent writes | Full HTML + CSS + inline SVG | OpenUI Lang statements |
45
- | Revising after feedback | Regenerate the file | Edit the named statements; the page patches in place and highlights what changed |
46
- | Feedback target | CSS selector + text | Statement name, its line in the file, plus cell / diff line / diagram node / selected text |
47
- | Validation | In-browser layout audit | `austere-axi check`: the renderer's own parser, in Node, no browser |
48
- | Look | Whatever the agent designs | One consistent dark design across agents |
49
-
50
- ## How it works
51
-
52
- ```
53
- agent writes plan.html ──► austere-axi plan.html ──► local server + browser review
54
- │ reviewer annotates, picks options,
55
- │ types, presses Send
56
- agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-poll returns the batch)
57
- │
58
- └─► page updates in place and flashes the changed statements
59
- ```
38
+ | Agent writes | Full HTML + CSS + SVG | OpenUI Lang statements |
39
+ | After feedback | Writes the file again | Edits the named statements; the page patches in place |
40
+ | Feedback points at | CSS selector + text | Statement name and line, plus table cell, diff line, diagram node or selected text |
41
+ | Validation | Layout audit in the browser | `austere-axi check`, in Node, no browser |
42
+ | Look | Different for each agent | One dark design |
60
43
 
61
- - **An artifact is a plain `.html` file.** It contains one `<script>` tag for the runtime and one `<script type="text/openui">` block holding the program. Opened directly, it renders from the CDN. Under review, the server points that tag at a local copy.
62
- - **The runtime** bundles the OpenUI React renderer with a curated component library: 58 of the stock components, plus `Architecture` (with `Node`, `Edge` and `Group`), `Mermaid`, `Diff` and `FileTree`. Every rendered statement is tagged with its name.
63
- - **Architecture diagrams** are laid out automatically ([dagre](https://github.com/dagrejs/dagre)): the agent writes nodes, edges and groups as statements and never a coordinate. Each node and edge is its own click target, so feedback on a diagram arrives as `annotation: api (Node)` with its line. The layout runs across when that stays legible at the page width and down otherwise, so a phone gets a tall diagram. For a system split by owner or tier, `lanes` turns each group into a column with right-angle lines between them (stacked as bands on a phone); `tone` tints a box or line to mark risk or a store family. Mermaid stays for sequences, state machines, ER and gantt.
64
- - **Annotations** resolve to the innermost named statement. They also carry precise detail where it exists: table row and column, `file:+line` for diff lines, the label of a diagram node, or the selected text. The popover shows the parent chain, so the reviewer can retarget a note to an enclosing section.
65
- - **Buttons and forms** are feedback channels. `@ToAssistant("…")` queues a message along with the form's values. A button with no action sends its label.
66
- - **Remote review.** When Tailscale is running, the server also listens on this machine's tailnet IP, and `open` prints a phone link. The review UI has a mobile layout with a conversation sheet.
44
+ Fewer output tokens means faster pages and lower cost.
67
45
 
68
46
  ## Install
69
47
 
70
- Requires Node.js 20 or later.
48
+ You need Node.js 22.18 or later.
71
49
 
72
- **Run it without installing** (what agents use):
50
+ **Let your agent do it.** Install the setup skill, then ask your agent to "set up austere-axi":
73
51
 
74
52
  ```sh
75
- npx -y austere-axi --help
53
+ npx skills add MandalorianBatman/austere-axi --skill austere-setup
76
54
  ```
77
55
 
78
- `npx` downloads the package into npm's cache and runs it. `-y` skips the install prompt, so a non-interactive agent never stalls on it. Prefix every command below the same way: `npx -y austere-axi spec`, `npx -y austere-axi plan.html`, and so on.
56
+ It checks Node, installs the `austere` skill, offers the optional items below, and opens a test page.
79
57
 
80
- **Or install the command globally:**
58
+ **Or do it yourself.** Agents can run the CLI with no install:
81
59
 
82
60
  ```sh
83
- npm install -g austere-axi
84
- austere-axi --help
61
+ npx -y austere-axi --help
85
62
  ```
86
63
 
87
- A global install stays on the version you installed until `npm update -g austere-axi`. Mixing the two is fine: a newer CLI replaces an older background server automatically.
88
-
89
- **Agent skill (optional):**
64
+ `-y` skips the npm prompt, so an agent does not stop on it. Optional:
90
65
 
91
66
  ```sh
92
- npx skills add MandalorianBatman/austere-axi --skill austere
67
+ npm install -g austere-axi # `austere-axi` command, no npx
68
+ npx skills add MandalorianBatman/austere-axi --skill austere # tells agents when to make a page; adds /austere
93
69
  ```
94
70
 
95
- The skill tells an agent when a page beats prose and adds an `/austere <request>` command. It is a stub that sends the agent to `npx -y austere-axi --help` and `spec`, so an installed copy cannot go stale. Agents that can run shell commands do not need it.
71
+ The `austere` skill is a stub that points at `--help` and `spec`, so it does not go out of date. A global install stays on its version until `npm update -g austere-axi`.
96
72
 
97
73
  ## Quick start
98
74
 
99
75
  ```sh
100
- npx -y austere-axi spec # component reference for the agent (read once per session)
76
+ npx -y austere-axi spec # component reference (read once per session)
101
77
  npx -y austere-axi plan.html # validate and open the page for review
102
- npx -y austere-axi poll plan.html # wait for the reviewer to press Send
78
+ npx -y austere-axi poll plan.html # wait until the reviewer presses Send
103
79
  ```
104
80
 
105
- `--help` prints the file skeleton and the full review loop.
81
+ `--help` prints the file skeleton and the full loop.
82
+
83
+ ## How it works
84
+
85
+ ```
86
+ agent writes plan.html ──► austere-axi plan.html ──► local server + browser review
87
+ │ reviewer annotates, picks options,
88
+ │ types, presses Send
89
+ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-poll returns the batch)
90
+ │
91
+ └─► page updates in place and flashes the changed statements
92
+ ```
93
+
94
+ - **A page is a plain `.html` file**: one runtime `<script>` tag and one `<script type="text/openui">` block. Opened directly, it loads the runtime from the CDN. Under review, the server serves a local copy.
95
+ - **Components**: 58 stock OpenUI components, plus `Architecture` (`Node`, `Edge`, `Group`), `Diff` and `FileTree`. `Mermaid` and nine shadcn-style components are bundled [packs](#components-and-packs).
96
+ - **Architecture diagrams** lay out automatically ([dagre](https://github.com/dagrejs/dagre)). The agent never writes coordinates. Each node and edge is a click target. `lanes` puts each group in a column; `tone` colors a box or line to show risk. Use Mermaid for sequences, state machines, ER and gantt.
97
+ - **Annotations** go to the smallest named statement under the click, with detail where it exists: table row and column, `file:+line` in a diff, a diagram node, or selected text. The reviewer can move a note to a parent section.
98
+ - **Buttons and forms** send feedback. `@ToAssistant("…")` sends a message with the form values. A button with no action sends its label.
99
+ - **Phone review**: when Tailscale runs, the server also listens on the tailnet IP and prints a phone link. The link carries your owner key; keep it to yourself. The review UI has a mobile layout.
106
100
 
107
101
  ## CLI
108
102
 
@@ -112,99 +106,171 @@ austere-axi <file> validate + open a review [--no-open] [--reope
112
106
  austere-axi poll <file> long-poll for feedback [--agent-reply "..."] [--takeover]
113
107
  austere-axi reply <file> post a reply without polling
114
108
  austere-axi end <file> end the review as the agent
115
- austere-axi check <file> validate the program (no browser)
109
+ austere-axi feedback <file> feedback no agent has answered yet [--all] [--peek]
110
+ austere-axi check <file> validate the program (no browser) [--fix]
116
111
  austere-axi patch <file> merge statements from stdin into the program
117
- austere-axi spec [group...] component reference (~6.5k tokens; groups narrow it)
112
+ austere-axi spec [group|pack...] component reference (~5.8k tokens; groups narrow it)
113
+ austere-axi components ... list | search | show | add | remove | new | build
118
114
  austere-axi export <file> one self-contained HTML file, works offline
115
+ austere-axi hub on|off|status|list|notes|open persistent server with an index of every page
116
+ austere-axi share <file> --as "<name>" invite link for a teammate on your tailnet [--auto]
117
+ austere-axi share list|revoke see invites, cut one off
119
118
  austere-axi stop stop the background server
120
119
  ```
121
120
 
122
- Output is compact YAML, written for agents to read. `poll` returns each item with the statement it points at and that statement's line in the file, so the agent can edit it without re-reading the file.
121
+ Output is compact YAML for agents. Each `poll` item has its statement and that statement's line, so the agent does not need to read the file again.
122
+
123
+ Session rules:
124
+
125
+ - One poll listens on a session at a time.
126
+ - Feedback sent while no agent listens waits for the next poll.
127
+ - A review the user ended does not open again unless you pass `--reopen`.
128
+ - If all review tabs stay closed, `poll` returns `browser_disconnected`: 10 s after a tab closes, or 5 min after a dropped connection (for example, a locked phone).
129
+ - `feedback <file>` returns notes no agent has answered, also from earlier days. Reading them marks them received; `reply` marks them answered.
130
+
131
+ ## Components and packs
132
+
133
+ `spec` covers the common components (~5.8k tokens). Rare ones (area and scatter charts, sliders, switches, editable tables, …) are in the library but not in `spec`. Find them with `components`:
134
+
135
+ ```sh
136
+ austere-axi components # every component, by group
137
+ austere-axi components search scatter # search by name, group, description
138
+ austere-axi components show AreaChart # signature, notes, example
139
+ ```
140
+
141
+ **Packs** add components. A pack is an npm package with `dist/pack.js` (the components, a few KB) and `dist/catalog.json` (signatures, examples, schema).
142
+
143
+ ```sh
144
+ austere-axi components add austere-pack-gantt --trust # npm name, folder, or .tgz
145
+ austere-axi check --fix plan.html # adds the pack tag to the page
146
+ ```
147
+
148
+ A page names its packs in the head, so it renders wherever you open it:
149
+
150
+ ```html
151
+ <meta name="austere-packs" content="austere-pack-gantt@1.2.0 sha384-…">
152
+ ```
153
+
154
+ - Under review, the server serves installed packs only while they match the hash in `~/.austere-axi/packs.json`. Opened directly, the page loads them from jsDelivr with Subresource Integrity. `export` inlines them.
155
+ - A pack component with no pack tag is a `missing-pack` error. `check --fix` repairs it.
156
+ - Mermaid ships as a bundled pack. It loads only when the page calls `Mermaid(...)` and needs no tag.
157
+ - The `shadcn` pack ships the same way. It adds `Progress`, `Avatar`, `AvatarGroup`, `Tooltip`, `HoverCard`, `Breadcrumb` (`BreadcrumbItem`), `Kbd`, `Skeleton`, `Calendar` and `Timeline` (`TimelineItem`). They are read-only. Find them with `austere-axi components search`.
158
+ ```
159
+ rollout = Progress(64, "Rows migrated")
160
+ freeze = Calendar("2026-11", ["2026-11-04 Code freeze"])
161
+ ```
123
162
 
124
- Session rules follow lavish:
163
+ **Trust.** Pack code runs inside your review pages, where notes and button presses also come from. Packs under `@austere-axi/` on npm install directly. Other npm packs and any `.tgz` need `--trust`; read them first. A local folder counts as your own. A page with third-party packs shows a warning, and `poll` tells the agent.
125
164
 
126
- - Only one poll listens on a session at a time.
127
- - Feedback sent while no agent is listening is queued until the next poll.
128
- - A review the user ended is not reopened unless `--reopen` is passed.
129
- - If every review tab stays closed, `poll` returns `browser_disconnected`. The wait is 10 s after a tab is closed, or 5 min after a dropped connection, such as a phone screen locking.
165
+ **Write a pack:**
166
+
167
+ ```sh
168
+ austere-axi components new austere-pack-milestones
169
+ cd austere-pack-milestones && npm install && npm run build
170
+ austere-axi components add .
171
+ ```
172
+
173
+ Each component uses `austereComponent` from `austere-axi/pack`: a PascalCase name, zod props in positional order (append-only after you publish), a one-line description and an example. The build loads the pack in Node, so build only code you trust. Publish with `npm publish`.
174
+
175
+ ## Hub
176
+
177
+ The server starts when needed and stops after 30 idle minutes. `hub on` keeps it running and indexes every page you open or check:
178
+
179
+ ```sh
180
+ austere-axi hub on # http://127.0.0.1:4388/: pages by project and day, with search
181
+ austere-axi hub list # the same, for an agent: [words] [--project dir]
182
+ austere-axi hub notes # every note, same for an agent: [words] [--project dir] [--unanswered]
183
+ austere-axi hub off # back to on-demand; the index stays
184
+ ```
130
185
 
131
- ### Environment
186
+ `/notes` lists every note on every page, newest first, with search, status and project filters. The home page shows which reviews are live and how much feedback the agent has answered. The index is SQLite (`~/.austere-axi/hub.db`), copied from the session files; the review loop does not need it.
187
+
188
+ ## Team review
189
+
190
+ Teammates on your tailnet can review a page and leave notes under their own name. The server still runs on your machine. There are no accounts and no hosted service.
191
+
192
+ ```sh
193
+ austere-axi share plan.html --as "Ana" # prints a link; notes wait for you
194
+ austere-axi share plan.html --as "Bo" --auto # Bo's notes go straight to the agent
195
+ austere-axi share list [--links] # who has a link
196
+ austere-axi share revoke Ana # name or id; takes effect at once
197
+ ```
198
+
199
+ Send the link to the teammate. The link needs Tailscale (or `AUSTERE_AXI_HOST`); without one, `share` stops with an error.
200
+
201
+ - **Hold (default):** a teammate's note waits in your side panel. Press **Send** on one note, or **Send all**. **Discard** removes it. Held notes are not in `poll` or `feedback`, so the agent does not see them until you send them.
202
+ - **Auto:** the note goes to the agent without your approval.
203
+ - **Author:** `poll` and `feedback` show `from: <name>` on each teammate's note. Your own notes have no `from`. The hub Notes view shows the author and can filter on it.
204
+ - **Reviewers can:** read the page and the conversation, and add notes. They cannot end the review, export, or send held notes.
205
+
206
+ Trust model: anyone who has the link, and can reach your machine on the tailnet, can post notes under that name until you revoke it. Share links only with people you trust, and revoke when the review is done. The link holds a random 192-bit token; the browser keeps it in a cookie and the address bar drops it.
207
+
208
+ Off loopback, a request without a valid invite or the owner key is refused. The hub and its Notes view are yours only. Your own phone link carries the owner key (`~/.austere-axi/owner.key`); do not forward it. To make all old phone links stop working, delete that file and restart the server. Behind a reverse proxy on the same machine, every request looks local, so it acts as you.
209
+
210
+ ## Environment
132
211
 
133
212
  | Variable | Default | |
134
213
  |---|---|---|
135
214
  | `AUSTERE_AXI_PORT` | `4388` | server port |
136
215
  | `AUSTERE_AXI_STATE_DIR` | `~/.austere-axi` | sessions, server log |
137
- | `AUSTERE_AXI_NO_OPEN` | | `1` = never launch a browser |
138
- | `AUSTERE_AXI_HOST` | | extra concrete bind address (never `0.0.0.0`) |
216
+ | `AUSTERE_AXI_NO_OPEN` | | `1` = never open a browser |
217
+ | `AUSTERE_AXI_HOST` | | extra bind address (never `0.0.0.0`) |
139
218
  | `AUSTERE_AXI_NO_TAILSCALE` | | `1` = loopback only |
140
- | `AUSTERE_AXI_LINK_HOST` | | host to print in the remote link |
219
+ | `AUSTERE_AXI_LINK_HOST` | | host in the printed remote link |
141
220
  | `AUSTERE_AXI_ALLOWED_HOSTS` | | extra `Host` headers to accept (reverse proxy) |
142
- | `AUSTERE_AXI_DISCONNECT_GRACE_MS` | `300000` | how long a dropped connection may stay away before `poll` returns; `0` disables |
143
- | `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle server shutdown; `0` disables |
221
+ | `AUSTERE_AXI_DISCONNECT_GRACE_MS` | `300000` | how long a dropped connection can stay away before `poll` returns; `0` disables |
222
+ | `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle shutdown; `0` disables (the hub never idles out) |
223
+ | `AUSTERE_AXI_NPM_REGISTRY` | `npm_config_registry`, else npmjs | where `components add` gets packs |
144
224
 
145
225
  ## Security
146
226
 
147
- The server binds to loopback, plus the Tailscale IP when Tailscale is up. It never binds to every interface.
227
+ The server binds to loopback, plus the Tailscale IP when Tailscale is up. It never binds to all interfaces.
148
228
 
149
- - **CLI control routes** (`/api/*`) answer only loopback callers.
150
- - **Host headers** are checked against an allowlist, as a DNS-rebinding defense.
151
- - **Mutating browser routes** reject foreign `Origin` headers.
152
- - **The artifact** runs in a sandboxed iframe with no same-origin access.
153
- - **File access** covers only the artifact's own directory, with no dotfiles.
229
+ - `/api/*` answers only loopback callers and refuses browser requests.
230
+ - `Host` headers must be on an allowlist (DNS-rebinding defense).
231
+ - Browser routes that change state refuse a foreign `Origin`.
232
+ - Invite tokens are random, compared in constant time, and kept out of the log. A reviewer link can only add notes to its own page. See [Team review](#team-review).
233
+ - The page runs in a sandboxed iframe with no same-origin access. Packs run there too.
234
+ - The server serves files only from the page's own directory, with no dotfiles.
235
+ - Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
154
236
 
155
- The Node side of OpenUI ships PostHog telemetry. The CLI sets `OPENUI_TELEMETRY_DISABLED=1` so nothing is sent; the browser runtime never sends telemetry.
237
+ OpenUI's Node side has PostHog telemetry. The CLI sets `OPENUI_TELEMETRY_DISABLED=1`. The browser runtime sends no telemetry.
156
238
 
157
239
  ## Not in v1
158
240
 
159
- These are left out of v1 on purpose:
160
-
161
- - **Image attachments** in feedback.
162
- - **Live data:** `Query` and `Mutation` are disabled, so artifacts are static.
163
- - **Themes:** there is one dark theme. Per-project accents via OpenUI's `ThemeProvider` are an easy follow-up.
164
- - **Hosted sharing:** use `export` to produce a single offline file instead.
241
+ - Image attachments in feedback.
242
+ - Live data: `Query` and `Mutation` are off, so pages are static.
243
+ - Themes: one dark theme only.
244
+ - Hosted sharing: use `export` to make one offline file.
165
245
 
166
246
  ## Development
167
247
 
168
248
  ```sh
169
249
  npm install
170
- npm run build # dist/: austere.js (runtime), mermaid.min.js, spec.json, prompt.md, chrome/, cli.mjs
250
+ npm run build # dist/: runtime, packs, pack kit, Mermaid, spec, catalog, review UI, cli.mjs
171
251
  npm test
252
+ npm run typecheck # tsc over src/cli, src/shared and the pack types
172
253
  node dist/cli.mjs examples/session-migration.html
173
254
  ```
174
255
 
175
256
  | Path | Contents |
176
257
  |---|---|
177
- | `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `Mermaid`, `FileTree`), one self-describing file each, collected automatically |
178
- | `src/runtime/stock.js` | Which stock OpenUI components ship, their prompt groups, renderer overrides |
179
- | `src/runtime/` | Library assembly, annotation layer, review bridge, styles |
180
- | `src/chrome/` | Review UI around the artifact iframe |
181
- | `src/cli/` | CLI, server, session store, `check`, `export` |
182
- | `src/shared/statements.js` | Statement splitter used by both sides |
183
-
184
- To add or change a component, follow [`.agents/skills/add-component/SKILL.md`](.agents/skills/add-component/SKILL.md). It is a maintainer-only skill and is not published; agents working in this repo pick it up through `AGENTS.md`. Tests enforce the mechanical rules:
185
-
186
- - every component sits in a prompt group
187
- - the component reference stays within its token budget
188
- - released prop orders are append-only (`npm run signatures`)
189
- - every example passes `check`
190
-
191
- For visual checks, `npm run gallery` and `npm run shot` render the components headlessly. `npm run test:browser` drives the real review loop in headless Chrome. It needs Chrome, or `CHROME_PATH` set to a browser, and it checks:
192
-
193
- - every component and example renders at desktop and phone width without errors or sideways scrolling
194
- - every clickable part reaches `poll` correctly
195
-
196
- Before committing, [`.agents/skills/review-change`](.agents/skills/review-change/SKILL.md) runs these checks plus three review agents from `.agents/agents/`:
197
-
198
- - **change-reviewer:** an independent code review
199
- - **visual-qa:** looks at screenshots, including stress inputs
200
- - **cold-start-author:** writes a page from `--help` and `spec` alone, to test the agent-facing docs
258
+ | `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `FileTree`), one file each |
259
+ | `src/runtime/stock.js` | Which stock components ship, which stay out of `spec`, their groups |
260
+ | `packs/` | Packs bundled with austere-axi (`mermaid`, `shadcn`) |
261
+ | `src/runtime/pack-api.js`, `src/pack-kit/` | `austere-axi/pack` and the `components build` pipeline |
262
+ | `src/shared/catalog.js` | Component catalog and prompt tiers |
263
+ | `src/runtime/` | Library, annotation layer, review bridge, styles |
264
+ | `src/chrome/` | Review UI around the page iframe |
265
+ | `src/cli/` | CLI, server, sessions, `check`, `export`, packs, hub |
266
+ | `skills/` | Published skills: `austere`, `austere-setup` |
201
267
 
202
- All of this is maintainer-only and is not published.
268
+ Maintainer guides (not published): [`AGENTS.md`](AGENTS.md), [`.agents/skills/add-component`](.agents/skills/add-component/SKILL.md) to add a component, [`.agents/skills/review-change`](.agents/skills/review-change/SKILL.md) before you commit. Tests enforce the component rules: every component has a group, `spec` stays in its token budget, released prop orders are append-only (`npm run signatures`), and every example passes `check`.
203
269
 
204
- CI (`.github/workflows/ci.yml`) builds and tests on every push to `main` and every pull request: Node 20, 22 and 24 on Linux, Node 24 on Windows, plus the browser suite. To release, bump `version` in `package.json`, push, and publish a GitHub Release. `.github/workflows/publish.yml` then tests again and publishes to npmjs.org through npm trusted publishing, with no token secret.
270
+ Visual checks: `npm run gallery`, `npm run shot`, and `npm run test:browser` (real review loop in headless Chrome; needs Chrome or `CHROME_PATH`).
205
271
 
206
- The runtime is about 3 MB minified. It is served locally and cached. `export` inlines it, and inlines Mermaid too when the artifact uses it.
272
+ **CI** builds and tests every push to `main` and every pull request (Node 22, 24, 26 on Linux; Node 24 on Windows; browser suite). **Release:** bump `version` in `package.json`, push, and publish a GitHub Release. `publish.yml` tests again and publishes to npm with trusted publishing.
207
273
 
208
274
  ## License
209
275
 
210
- MIT. Builds on [OpenUI](https://github.com/thesysdev/openui) (MIT) and borrows the review-loop design of [lavish-axi](https://github.com/kunchenguid/lavish-axi) (MIT).
276
+ MIT. Builds on [OpenUI](https://github.com/thesysdev/openui) (MIT). The review loop design comes from [lavish-axi](https://github.com/kunchenguid/lavish-axi) (MIT).