austere-axi 0.1.1 → 0.3.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 +149 -102
- package/dist/austere.js +500 -154
- package/dist/catalog.json +1 -0
- package/dist/chrome/chrome.css +1 -1
- package/dist/chrome/chrome.js +5 -4
- package/dist/cli.mjs +5384 -488
- package/dist/pack-kit.mjs +284 -0
- package/dist/packs/mermaid/catalog.json +53 -0
- package/dist/packs/mermaid/pack.js +25 -0
- package/dist/prompt.md +22 -26
- package/dist/spec.json +1 -1
- package/package.json +3 -2
- package/skills/austere/SKILL.md +2 -0
- package/skills/austere-setup/SKILL.md +83 -0
package/README.md
CHANGED
|
@@ -1,95 +1,102 @@
|
|
|
1
1
|
# austere-axi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/austere-axi)
|
|
4
|
+
[](https://www.npmjs.com/package/austere-axi)
|
|
5
|
+
[](https://www.jsdelivr.com/package/npm/austere-axi)
|
|
6
|
+
[](https://skills.sh/mandalorianbatman/austere-axi)
|
|
7
|
+
[](https://github.com/MandalorianBatman/austere-axi/actions/workflows/ci.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/austere-axi)
|
|
9
|
+
[](LICENSE)
|
|
4
10
|
|
|
5
|
-
The agent writes a short
|
|
6
|
-
|
|
7
|
-
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.
|
|
8
12
|
|
|
9
13
|
```
|
|
10
14
|
root = Stack([intro, flow, options, decide], "column", "l")
|
|
11
15
|
intro = Card([CardHeader("Session storage migration", "Redis → Postgres"), introText])
|
|
12
16
|
introText = MarkDownRenderer("Sessions are lost on failover. **Goal:** durable sessions, no forced logout.")
|
|
13
17
|
flow = Card([CardHeader("Request path"), flowDiagram])
|
|
14
|
-
flowDiagram =
|
|
15
|
-
|
|
18
|
+
flowDiagram = Architecture([client, api, pg], [toApi, toPg])
|
|
19
|
+
client = Node("Client", "external")
|
|
20
|
+
api = Node("API", "backend", "Node :8080")
|
|
21
|
+
pg = Node("Postgres", "database", "sessions table", "new")
|
|
22
|
+
toApi = Edge(client, api, "HTTPS", "emphasis")
|
|
23
|
+
toPg = Edge(api, pg, "SQL")
|
|
16
24
|
options = Card([CardHeader("Rollout options"), optionTable])
|
|
17
25
|
optionTable = Table([Col("Option", ["Big bang", "Dual-write"]), Col("Risk", [Tag("high", null, "sm", "danger"), Tag("low", null, "sm", "success")])])
|
|
18
26
|
decide = Form("rollout", Buttons([Button("Send decision", @ToAssistant("Decision: rollout"), "primary")]), [strategyField])
|
|
19
27
|
strategyField = FormControl("Rollout", RadioGroup("strategy", [RadioItem("Big bang", "", "bigbang"), RadioItem("Dual-write", "", "dualwrite")]))
|
|
20
28
|
```
|
|
21
29
|
|
|
22
|
-
[`examples/session-migration.html`](examples/session-migration.html) is a full page
|
|
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.
|
|
23
31
|
|
|
24
32
|
## Why
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
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.
|
|
29
35
|
|
|
30
36
|
| | lavish-axi | austere-axi |
|
|
31
37
|
|---|---|---|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
| Feedback
|
|
35
|
-
| Validation |
|
|
36
|
-
| Look |
|
|
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 |
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
agent writes plan.html ──► austere-axi plan.html ──► local server + browser review
|
|
42
|
-
│ reviewer annotates, picks options,
|
|
43
|
-
│ types, presses Send
|
|
44
|
-
agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-poll returns the batch)
|
|
45
|
-
│
|
|
46
|
-
└─► page updates in place and flashes the changed statements
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
- **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.
|
|
50
|
-
- **The runtime** bundles the OpenUI React renderer with a curated component library: 58 of the stock components, plus `Mermaid`, `Diff` and `FileTree`. Every rendered statement is tagged with its name.
|
|
51
|
-
- **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.
|
|
52
|
-
- **Buttons and forms** are feedback channels. `@ToAssistant("…")` queues a message along with the form's values. A button with no action sends its label.
|
|
53
|
-
- **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.
|
|
54
45
|
|
|
55
46
|
## Install
|
|
56
47
|
|
|
57
|
-
|
|
48
|
+
You need Node.js 20 or later.
|
|
58
49
|
|
|
59
|
-
**
|
|
50
|
+
**Let your agent do it.** Install the setup skill, then ask your agent to "set up austere-axi":
|
|
60
51
|
|
|
61
52
|
```sh
|
|
62
|
-
npx
|
|
53
|
+
npx skills add MandalorianBatman/austere-axi --skill austere-setup
|
|
63
54
|
```
|
|
64
55
|
|
|
65
|
-
|
|
56
|
+
It checks Node, installs the `austere` skill, offers the optional items below, and opens a test page.
|
|
66
57
|
|
|
67
|
-
**Or
|
|
58
|
+
**Or do it yourself.** Agents can run the CLI with no install:
|
|
68
59
|
|
|
69
60
|
```sh
|
|
70
|
-
|
|
71
|
-
austere-axi --help
|
|
61
|
+
npx -y austere-axi --help
|
|
72
62
|
```
|
|
73
63
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
**Agent skill (optional):**
|
|
64
|
+
`-y` skips the npm prompt, so an agent does not stop on it. Optional:
|
|
77
65
|
|
|
78
66
|
```sh
|
|
79
|
-
|
|
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
|
|
80
69
|
```
|
|
81
70
|
|
|
82
|
-
The
|
|
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`.
|
|
83
72
|
|
|
84
73
|
## Quick start
|
|
85
74
|
|
|
86
75
|
```sh
|
|
87
|
-
npx -y austere-axi spec # component reference
|
|
76
|
+
npx -y austere-axi spec # component reference (read once per session)
|
|
88
77
|
npx -y austere-axi plan.html # validate and open the page for review
|
|
89
|
-
npx -y austere-axi poll plan.html # wait
|
|
78
|
+
npx -y austere-axi poll plan.html # wait until the reviewer presses Send
|
|
90
79
|
```
|
|
91
80
|
|
|
92
|
-
`--help` prints the file skeleton and the full
|
|
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` is a bundled [pack](#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 review UI has a mobile layout.
|
|
93
100
|
|
|
94
101
|
## CLI
|
|
95
102
|
|
|
@@ -99,99 +106,139 @@ austere-axi <file> validate + open a review [--no-open] [--reope
|
|
|
99
106
|
austere-axi poll <file> long-poll for feedback [--agent-reply "..."] [--takeover]
|
|
100
107
|
austere-axi reply <file> post a reply without polling
|
|
101
108
|
austere-axi end <file> end the review as the agent
|
|
102
|
-
austere-axi
|
|
109
|
+
austere-axi feedback <file> feedback no agent has answered yet [--all] [--peek]
|
|
110
|
+
austere-axi check <file> validate the program (no browser) [--fix]
|
|
103
111
|
austere-axi patch <file> merge statements from stdin into the program
|
|
104
|
-
austere-axi spec [group...]
|
|
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
|
|
105
114
|
austere-axi export <file> one self-contained HTML file, works offline
|
|
115
|
+
austere-axi hub on|off|status|list|open persistent server with an index of every page
|
|
106
116
|
austere-axi stop stop the background server
|
|
107
117
|
```
|
|
108
118
|
|
|
109
|
-
Output is compact YAML
|
|
119
|
+
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.
|
|
120
|
+
|
|
121
|
+
Session rules:
|
|
122
|
+
|
|
123
|
+
- One poll listens on a session at a time.
|
|
124
|
+
- Feedback sent while no agent listens waits for the next poll.
|
|
125
|
+
- A review the user ended does not open again unless you pass `--reopen`.
|
|
126
|
+
- 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).
|
|
127
|
+
- `feedback <file>` returns notes no agent has answered, also from earlier days. Reading them marks them received; `reply` marks them answered.
|
|
128
|
+
|
|
129
|
+
## Components and packs
|
|
130
|
+
|
|
131
|
+
`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`:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
austere-axi components # every component, by group
|
|
135
|
+
austere-axi components search scatter # search by name, group, description
|
|
136
|
+
austere-axi components show AreaChart # signature, notes, example
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**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).
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
austere-axi components add austere-pack-gantt --trust # npm name, folder, or .tgz
|
|
143
|
+
austere-axi check --fix plan.html # adds the pack tag to the page
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
A page names its packs in the head, so it renders wherever you open it:
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<meta name="austere-packs" content="austere-pack-gantt@1.2.0 sha384-…">
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- 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.
|
|
153
|
+
- A pack component with no pack tag is a `missing-pack` error. `check --fix` repairs it.
|
|
154
|
+
- Mermaid ships as a bundled pack. It loads only when the page calls `Mermaid(...)` and needs no tag.
|
|
155
|
+
|
|
156
|
+
**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.
|
|
157
|
+
|
|
158
|
+
**Write a pack:**
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
austere-axi components new austere-pack-timeline
|
|
162
|
+
cd austere-pack-timeline && npm install && npm run build
|
|
163
|
+
austere-axi components add .
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
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`.
|
|
110
167
|
|
|
111
|
-
|
|
168
|
+
## Hub
|
|
169
|
+
|
|
170
|
+
The server starts when needed and stops after 30 idle minutes. `hub on` keeps it running and indexes every page you open or check:
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
austere-axi hub on # http://127.0.0.1:4388/: pages by project and day, with search
|
|
174
|
+
austere-axi hub list # the same, for an agent: [words] [--project dir]
|
|
175
|
+
austere-axi hub off # back to on-demand; the index stays
|
|
176
|
+
```
|
|
112
177
|
|
|
113
|
-
|
|
114
|
-
- Feedback sent while no agent is listening is queued until the next poll.
|
|
115
|
-
- A review the user ended is not reopened unless `--reopen` is passed.
|
|
116
|
-
- 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.
|
|
178
|
+
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. The hub needs Node 22.13 or later (`node:sqlite`).
|
|
117
179
|
|
|
118
|
-
|
|
180
|
+
## Environment
|
|
119
181
|
|
|
120
182
|
| Variable | Default | |
|
|
121
183
|
|---|---|---|
|
|
122
184
|
| `AUSTERE_AXI_PORT` | `4388` | server port |
|
|
123
185
|
| `AUSTERE_AXI_STATE_DIR` | `~/.austere-axi` | sessions, server log |
|
|
124
|
-
| `AUSTERE_AXI_NO_OPEN` | | `1` = never
|
|
125
|
-
| `AUSTERE_AXI_HOST` | | extra
|
|
186
|
+
| `AUSTERE_AXI_NO_OPEN` | | `1` = never open a browser |
|
|
187
|
+
| `AUSTERE_AXI_HOST` | | extra bind address (never `0.0.0.0`) |
|
|
126
188
|
| `AUSTERE_AXI_NO_TAILSCALE` | | `1` = loopback only |
|
|
127
|
-
| `AUSTERE_AXI_LINK_HOST` | | host
|
|
189
|
+
| `AUSTERE_AXI_LINK_HOST` | | host in the printed remote link |
|
|
128
190
|
| `AUSTERE_AXI_ALLOWED_HOSTS` | | extra `Host` headers to accept (reverse proxy) |
|
|
129
|
-
| `AUSTERE_AXI_DISCONNECT_GRACE_MS` | `300000` | how long a dropped connection
|
|
130
|
-
| `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle
|
|
191
|
+
| `AUSTERE_AXI_DISCONNECT_GRACE_MS` | `300000` | how long a dropped connection can stay away before `poll` returns; `0` disables |
|
|
192
|
+
| `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle shutdown; `0` disables (the hub never idles out) |
|
|
193
|
+
| `AUSTERE_AXI_NPM_REGISTRY` | `npm_config_registry`, else npmjs | where `components add` gets packs |
|
|
131
194
|
|
|
132
195
|
## Security
|
|
133
196
|
|
|
134
|
-
The server binds to loopback, plus the Tailscale IP when Tailscale is up. It never binds to
|
|
197
|
+
The server binds to loopback, plus the Tailscale IP when Tailscale is up. It never binds to all interfaces.
|
|
135
198
|
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
199
|
+
- `/api/*` answers only loopback callers and refuses browser requests.
|
|
200
|
+
- `Host` headers must be on an allowlist (DNS-rebinding defense).
|
|
201
|
+
- Browser routes that change state refuse a foreign `Origin`.
|
|
202
|
+
- The page runs in a sandboxed iframe with no same-origin access. Packs run there too.
|
|
203
|
+
- The server serves files only from the page's own directory, with no dotfiles.
|
|
204
|
+
- Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
|
|
141
205
|
|
|
142
|
-
|
|
206
|
+
OpenUI's Node side has PostHog telemetry. The CLI sets `OPENUI_TELEMETRY_DISABLED=1`. The browser runtime sends no telemetry.
|
|
143
207
|
|
|
144
208
|
## Not in v1
|
|
145
209
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
- **Themes:** there is one dark theme. Per-project accents via OpenUI's `ThemeProvider` are an easy follow-up.
|
|
151
|
-
- **Hosted sharing:** use `export` to produce a single offline file instead.
|
|
210
|
+
- Image attachments in feedback.
|
|
211
|
+
- Live data: `Query` and `Mutation` are off, so pages are static.
|
|
212
|
+
- Themes: one dark theme only.
|
|
213
|
+
- Hosted sharing: use `export` to make one offline file.
|
|
152
214
|
|
|
153
215
|
## Development
|
|
154
216
|
|
|
155
217
|
```sh
|
|
156
218
|
npm install
|
|
157
|
-
npm run build # dist/:
|
|
219
|
+
npm run build # dist/: runtime, packs, pack kit, Mermaid, spec, catalog, review UI, cli.mjs
|
|
158
220
|
npm test
|
|
159
221
|
node dist/cli.mjs examples/session-migration.html
|
|
160
222
|
```
|
|
161
223
|
|
|
162
224
|
| Path | Contents |
|
|
163
225
|
|---|---|
|
|
164
|
-
| `src/runtime/components/` | Custom components (`
|
|
165
|
-
| `src/runtime/stock.js` | Which stock
|
|
166
|
-
| `
|
|
167
|
-
| `src/
|
|
168
|
-
| `src/
|
|
169
|
-
| `src/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
- every component sits in a prompt group
|
|
174
|
-
- the component reference stays within its token budget
|
|
175
|
-
- released prop orders are append-only (`npm run signatures`)
|
|
176
|
-
- every example passes `check`
|
|
177
|
-
|
|
178
|
-
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:
|
|
179
|
-
|
|
180
|
-
- every component and example renders at desktop and phone width without errors or sideways scrolling
|
|
181
|
-
- every clickable part reaches `poll` correctly
|
|
182
|
-
|
|
183
|
-
Before committing, [`.agents/skills/review-change`](.agents/skills/review-change/SKILL.md) runs these checks plus three review agents from `.agents/agents/`:
|
|
184
|
-
|
|
185
|
-
- **change-reviewer:** an independent code review
|
|
186
|
-
- **visual-qa:** looks at screenshots, including stress inputs
|
|
187
|
-
- **cold-start-author:** writes a page from `--help` and `spec` alone, to test the agent-facing docs
|
|
226
|
+
| `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `FileTree`), one file each |
|
|
227
|
+
| `src/runtime/stock.js` | Which stock components ship, which stay out of `spec`, their groups |
|
|
228
|
+
| `packs/` | Packs bundled with austere-axi (`mermaid`) |
|
|
229
|
+
| `src/runtime/pack-api.js`, `src/pack-kit/` | `austere-axi/pack` and the `components build` pipeline |
|
|
230
|
+
| `src/shared/catalog.js` | Component catalog and prompt tiers |
|
|
231
|
+
| `src/runtime/` | Library, annotation layer, review bridge, styles |
|
|
232
|
+
| `src/chrome/` | Review UI around the page iframe |
|
|
233
|
+
| `src/cli/` | CLI, server, sessions, `check`, `export`, packs, hub |
|
|
234
|
+
| `skills/` | Published skills: `austere`, `austere-setup` |
|
|
188
235
|
|
|
189
|
-
|
|
236
|
+
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`.
|
|
190
237
|
|
|
191
|
-
|
|
238
|
+
Visual checks: `npm run gallery`, `npm run shot`, and `npm run test:browser` (real review loop in headless Chrome; needs Chrome or `CHROME_PATH`).
|
|
192
239
|
|
|
193
|
-
|
|
240
|
+
**CI** builds and tests every push to `main` and every pull request (Node 20, 22, 24 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.
|
|
194
241
|
|
|
195
242
|
## License
|
|
196
243
|
|
|
197
|
-
MIT. Builds on [OpenUI](https://github.com/thesysdev/openui) (MIT)
|
|
244
|
+
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).
|