austere-axi 0.4.0 → 0.5.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 +163 -23
- package/dist/austere.js +88 -88
- package/dist/catalog.json +1 -1
- package/dist/chrome/chrome.css +1 -1
- package/dist/chrome/chrome.js +8 -6
- package/dist/cli.mjs +2331 -1463
- package/dist/packs/mermaid/catalog.json +2 -2
- package/dist/packs/mermaid/pack.js +1 -1
- package/dist/packs/shadcn/catalog.json +2 -2
- package/dist/packs/shadcn/pack.js +1 -1
- package/package.json +8 -2
- package/skills/austere-setup/SKILL.md +5 -3
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
|
|
11
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.
|
|
12
12
|
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
<sub>The real loop, about 30 s: [MP4](docs/demo.mp4) · [WebM](docs/demo.webm).</sub>
|
|
16
|
+
|
|
13
17
|
```
|
|
14
18
|
root = Stack([intro, flow, options, decide], "column", "l")
|
|
15
19
|
intro = Card([CardHeader("Session storage migration", "Redis → Postgres"), introText])
|
|
@@ -29,6 +33,8 @@ strategyField = FormControl("Rollout", RadioGroup("strategy", [RadioItem("Big ba
|
|
|
29
33
|
|
|
30
34
|
[`examples/session-migration.html`](examples/session-migration.html) is a full page (KPIs, diagram, table, chart, diff, decision form) in about 1,000 tokens.
|
|
31
35
|
|
|
36
|
+
**Website:** [mandalorianbatman.github.io/austere-axi](https://mandalorianbatman.github.io/austere-axi/) has a [gallery](https://mandalorianbatman.github.io/austere-axi/gallery/) of example pages (postmortem, API review, PR summary, sprint plan) next to their source and token counts, and a [playground](https://mandalorianbatman.github.io/austere-axi/play/) where you edit OpenUI Lang and see the page with nothing installed.
|
|
37
|
+
|
|
32
38
|
## Why
|
|
33
39
|
|
|
34
40
|
The loop is the same as [lavish-axi](https://github.com/kunchenguid/lavish-axi). The difference: the agent never writes HTML, CSS or layout.
|
|
@@ -36,12 +42,18 @@ The loop is the same as [lavish-axi](https://github.com/kunchenguid/lavish-axi).
|
|
|
36
42
|
| | lavish-axi | austere-axi |
|
|
37
43
|
|---|---|---|
|
|
38
44
|
| Agent writes | Full HTML + CSS + SVG | OpenUI Lang statements |
|
|
39
|
-
| After feedback |
|
|
45
|
+
| After feedback | Edits the HTML; the page reloads | Edits the named statements; the page patches in place |
|
|
40
46
|
| Feedback points at | CSS selector + text | Statement name and line, plus table cell, diff line, diagram node or selected text |
|
|
41
47
|
| Validation | Layout audit in the browser | `austere-axi check`, in Node, no browser |
|
|
42
48
|
| Look | Different for each agent | One dark design |
|
|
43
49
|
|
|
44
|
-
Fewer output tokens means faster pages and lower cost.
|
|
50
|
+
Fewer output tokens means faster pages and lower cost. From the [benchmark](docs/benchmark.md):
|
|
51
|
+
|
|
52
|
+
<!-- bench:start -->
|
|
53
|
+
Across 36 paired runs (6 review pages, 2 models), lavish-axi spent a median **3.2×** the output tokens of austere-axi to produce the same page, took **2.4×** as long and cost **2.5×** as much. Answering one reviewer note took **2.7×** the output tokens.
|
|
54
|
+
<!-- bench:end -->
|
|
55
|
+
|
|
56
|
+
The benchmark also shows where lavish-axi does better; read its caveats before quoting the numbers.
|
|
45
57
|
|
|
46
58
|
## Install
|
|
47
59
|
|
|
@@ -70,6 +82,44 @@ npx skills add MandalorianBatman/austere-axi --skill austere # tells agents wh
|
|
|
70
82
|
|
|
71
83
|
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`.
|
|
72
84
|
|
|
85
|
+
## Install as a Claude Code plugin
|
|
86
|
+
|
|
87
|
+
The plugin bundles the `austere` skill (and `/austere`), the `austere-setup` skill and the MCP server below:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
claude plugin marketplace add MandalorianBatman/austere-axi
|
|
91
|
+
claude plugin install austere@austere-axi
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
In a session: `/plugin marketplace add MandalorianBatman/austere-axi`, then `/plugin install austere@austere-axi`. The MCP server runs `npx -y austere-axi mcp`, so it needs Node 22.18 or later and npm access. `claude plugin update austere` picks up new skill text; the CLI comes from npm each time.
|
|
95
|
+
|
|
96
|
+
## Use with MCP
|
|
97
|
+
|
|
98
|
+
`austere-axi mcp` is an MCP server on stdio. Its tools are `spec`, `check`, `open` (validate and open the review), `poll`, `reply`, `patch` and `end`. Each runs the CLI command of the same name and returns its output, so the review loop is the same as on the command line. `poll` returns `status: timeout` after 50 s by default, because many clients cancel longer tool calls; the agent polls again and nothing is lost. Pass `timeout_ms: 0` to wait until feedback arrives when your client allows long calls. `poll` and `reply` take the `cursor` a poll returned, as `--cursor` does.
|
|
99
|
+
|
|
100
|
+
**Claude Desktop** (`claude_desktop_config.json`) and **Cursor** (`~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project):
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"austere": {
|
|
106
|
+
"command": "npx",
|
|
107
|
+
"args": ["-y", "austere-axi", "mcp"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Codex** (`~/.codex/config.toml`):
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[mcp_servers.austere]
|
|
117
|
+
command = "npx"
|
|
118
|
+
args = ["-y", "austere-axi", "mcp"]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Or `codex mcp add austere -- npx -y austere-axi mcp`. On a machine with no screen, add `AUSTERE_AXI_NO_OPEN=1` to the server's `env` so `open` prints the link instead of launching a browser. The other [environment variables](#environment) work the same way. Pass absolute file paths: the server resolves relative ones against its own working directory, which depends on the client.
|
|
122
|
+
|
|
73
123
|
## Quick start
|
|
74
124
|
|
|
75
125
|
```sh
|
|
@@ -103,19 +153,24 @@ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-pol
|
|
|
103
153
|
```
|
|
104
154
|
austere-axi sessions + usage
|
|
105
155
|
austere-axi <file> validate + open a review [--no-open] [--reopen]
|
|
106
|
-
austere-axi poll <file> long-poll for feedback [--agent-reply "..."] [--takeover]
|
|
107
|
-
austere-axi reply <file> post a reply without polling
|
|
108
|
-
austere-axi end <file>
|
|
156
|
+
austere-axi poll <file> long-poll for feedback [--cursor n] [--agent-reply "..."] [--after n] [--takeover]
|
|
157
|
+
austere-axi reply <file> post a reply without polling [--cursor n]
|
|
158
|
+
austere-axi end <file> stop as the agent; the reviewer can still add notes
|
|
159
|
+
austere-axi close <file> close the review: no more notes until reopen
|
|
160
|
+
austere-axi reopen <file> reopen an ended or closed review
|
|
161
|
+
austere-axi login [file] single-use link that signs a browser in as you (SSH, --no-open)
|
|
109
162
|
austere-axi feedback <file> feedback no agent has answered yet [--all] [--peek]
|
|
110
163
|
austere-axi check <file> validate the program (no browser) [--fix]
|
|
111
|
-
austere-axi patch <file> merge statements from stdin into the program
|
|
164
|
+
austere-axi patch <file> merge statements from stdin into the program (reports dropped unreferenced ones)
|
|
112
165
|
austere-axi spec [group|pack...] component reference (~5.8k tokens; groups narrow it)
|
|
113
166
|
austere-axi components ... list | search | show | add | remove | new | build
|
|
114
167
|
austere-axi export <file> one self-contained HTML file, works offline
|
|
115
|
-
austere-axi hub on|off|status|list|notes|open
|
|
168
|
+
austere-axi hub on|off|status|list|notes|open home page with every page and note
|
|
116
169
|
austere-axi share <file> --as "<name>" invite link for a teammate on your tailnet [--auto]
|
|
117
170
|
austere-axi share list|revoke see invites, cut one off
|
|
118
171
|
austere-axi stop stop the background server
|
|
172
|
+
austere-axi server --persist run the server in the foreground and never idle out
|
|
173
|
+
austere-axi mcp MCP server on stdio (see Use with MCP)
|
|
119
174
|
```
|
|
120
175
|
|
|
121
176
|
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.
|
|
@@ -123,10 +178,14 @@ Output is compact YAML for agents. Each `poll` item has its statement and that s
|
|
|
123
178
|
Session rules:
|
|
124
179
|
|
|
125
180
|
- One poll listens on a session at a time.
|
|
181
|
+
- Every note is written to `~/.austere-axi/hub.db` before the page is told it was sent, so a note survives a crash or restart of the server.
|
|
126
182
|
- Feedback sent while no agent listens waits for the next poll.
|
|
127
|
-
- A
|
|
128
|
-
-
|
|
129
|
-
- `
|
|
183
|
+
- Feedback stays unread until the agent acknowledges it. `poll` prints a `cursor`. Passing it back (`--cursor n` on the next `poll` or `reply`), or replying at all, acknowledges everything up to it. A `poll` without either returns the same feedback again, so feedback lost on the way to the agent (a crash, a timed-out tool call) is delivered again. `--after n` replays from any point.
|
|
184
|
+
- Ending a review (the agent's `end`, or **End review** in the page) means the agent stopped. The reviewer can still send notes. The next `poll` returns every note the agent has not acknowledged, including ones sent before the end or released from hold after it, and reopens the review. A batch the reviewer sent with end stays final.
|
|
185
|
+
- Only the owner's **close** (`close <file>`, or **Close review** in the page) locks the composer. `reopen <file>` or **Reopen review** unlocks it.
|
|
186
|
+
- A review the user ended or closed does not open again unless you pass `--reopen`.
|
|
187
|
+
- 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). The agent stops polling but does not end the review, so notes left later are picked up by the next `poll`.
|
|
188
|
+
- `feedback <file>` returns notes no agent has answered, also from earlier days. Reading them acknowledges them; `reply` marks them answered.
|
|
130
189
|
|
|
131
190
|
## Components and packs
|
|
132
191
|
|
|
@@ -162,7 +221,7 @@ A page names its packs in the head, so it renders wherever you open it:
|
|
|
162
221
|
|
|
163
222
|
**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.
|
|
164
223
|
|
|
165
|
-
**Write a pack:**
|
|
224
|
+
**Write a pack:** [Writing a pack](docs/writing-a-pack.md) walks through one from scaffold to `npm publish`, using [austere-pack-gantt](https://github.com/MandalorianBatman/austere-pack-gantt), a Gantt chart pack built outside this repo, as the example. The short version:
|
|
166
225
|
|
|
167
226
|
```sh
|
|
168
227
|
austere-axi components new austere-pack-milestones
|
|
@@ -174,16 +233,38 @@ Each component uses `austereComponent` from `austere-axi/pack`: a PascalCase nam
|
|
|
174
233
|
|
|
175
234
|
## Hub
|
|
176
235
|
|
|
177
|
-
The server starts when needed and stops after 30 idle minutes. `hub on`
|
|
236
|
+
The server starts when needed and stops after 30 idle minutes. Every review is kept in `~/.austere-axi/hub.db` (SQLite) either way. `hub on` adds a home page over it and indexes every page you `check` or `patch`:
|
|
178
237
|
|
|
179
238
|
```sh
|
|
180
239
|
austere-axi hub on # http://127.0.0.1:4388/: pages by project and day, with search
|
|
181
240
|
austere-axi hub list # the same, for an agent: [words] [--project dir]
|
|
182
241
|
austere-axi hub notes # every note, same for an agent: [words] [--project dir] [--unanswered]
|
|
183
|
-
austere-axi hub off # back to
|
|
242
|
+
austere-axi hub off # back to the plain session list; the database stays
|
|
184
243
|
```
|
|
185
244
|
|
|
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.
|
|
245
|
+
`/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.
|
|
246
|
+
|
|
247
|
+
Session files from versions before 0.5 (`~/.austere-axi/sessions/*.json`) are imported into `hub.db` on first start and renamed to `*.imported`. One server runs per state dir; a second one refuses to start. `server.lock` names the running server's pid and port; a lock whose server no longer answers on that port is stale and is taken over.
|
|
248
|
+
|
|
249
|
+
### Always on, behind a reverse proxy
|
|
250
|
+
|
|
251
|
+
To serve review pages at a public address, run the server under a supervisor with `--persist` so it never idles out, and point the proxy at it:
|
|
252
|
+
|
|
253
|
+
```ini
|
|
254
|
+
# ~/.config/systemd/user/austere-axi.service
|
|
255
|
+
[Unit]
|
|
256
|
+
Description=austere-axi review server
|
|
257
|
+
|
|
258
|
+
[Service]
|
|
259
|
+
ExecStart=/usr/bin/env npx -y austere-axi server --persist
|
|
260
|
+
Environment=AUSTERE_AXI_LINK_URL=https://austere.example.com
|
|
261
|
+
Restart=on-failure
|
|
262
|
+
|
|
263
|
+
[Install]
|
|
264
|
+
WantedBy=default.target
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`systemctl --user enable --now austere-axi`. Commands on the same machine use the running server; a newer CLI that replaces an older server starts the new one with `--persist` too. `AUSTERE_AXI_PERSIST=1` does the same as `--persist`. Read the reverse-proxy note in [Team review](#team-review) first: behind a proxy on the same machine every request looks local.
|
|
187
268
|
|
|
188
269
|
## Team review
|
|
189
270
|
|
|
@@ -198,39 +279,88 @@ austere-axi share revoke Ana # name or id; takes effect at o
|
|
|
198
279
|
|
|
199
280
|
Send the link to the teammate. The link needs Tailscale (or `AUSTERE_AXI_HOST`); without one, `share` stops with an error.
|
|
200
281
|
|
|
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.
|
|
282
|
+
- **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. Each teammate can have up to 200 notes held at once; more are refused until you send or discard some.
|
|
202
283
|
- **Auto:** the note goes to the agent without your approval.
|
|
203
284
|
- **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
285
|
- **Reviewers can:** read the page and the conversation, and add notes. They cannot end the review, export, or send held notes.
|
|
205
286
|
|
|
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
|
|
287
|
+
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. Opening it shows a "Continue as <name>" button; pressing it (a same-origin POST) trades the token for a cookie and the address bar drops it. A link opened from another site therefore cannot switch your browser into a reviewer.
|
|
288
|
+
|
|
289
|
+
A request without a valid invite or the owner key is refused, on loopback too. 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 and owner cookies stop working, delete that file and restart the server. A reverse proxy on the same machine gets no owner rights from looking local.
|
|
290
|
+
|
|
291
|
+
## GitHub Action
|
|
207
292
|
|
|
208
|
-
|
|
293
|
+
Post an austere review page on every pull request. The action runs `austere-axi check` on the pages you commit, exports each one as a self-contained HTML file with `austere-axi export`, uploads them as a workflow artifact, and posts or updates **one** comment on the PR with a summary and a download link.
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
# .github/workflows/austere.yml
|
|
297
|
+
name: austere review
|
|
298
|
+
on: pull_request
|
|
299
|
+
|
|
300
|
+
permissions:
|
|
301
|
+
contents: read
|
|
302
|
+
pull-requests: write # to post the comment
|
|
303
|
+
|
|
304
|
+
jobs:
|
|
305
|
+
review:
|
|
306
|
+
runs-on: ubuntu-latest
|
|
307
|
+
steps:
|
|
308
|
+
- uses: actions/checkout@v7
|
|
309
|
+
- uses: MandalorianBatman/austere-axi/action@main # pin a release tag or SHA once you settle
|
|
310
|
+
with:
|
|
311
|
+
pages: reviews/*.html
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
| Input | Default | |
|
|
315
|
+
|---|---|---|
|
|
316
|
+
| `pages` | required | paths or globs, one per line, relative to the workspace |
|
|
317
|
+
| `version` | `latest` | austere-axi version from npm; pin it for reproducible runs |
|
|
318
|
+
| `artifact-name` | `austere-review` | workflow artifact that holds the exported pages |
|
|
319
|
+
| `retention-days` | repository default | artifact retention |
|
|
320
|
+
| `comment` | `true` | `false` leaves only the artifact and the job summary |
|
|
321
|
+
| `comment-tag` | `default` | names the sticky comment; use one per use of the action in the same PR |
|
|
322
|
+
| `github-token` | `github.token` | needs `pull-requests: write` to comment |
|
|
323
|
+
|
|
324
|
+
Outputs: `ok` (`true` when every page passed) and `artifact-url`.
|
|
325
|
+
|
|
326
|
+
- **A failing check fails the job.** The `check` output, with each error's statement and line, is in the log and in the job summary; pages that passed are still exported.
|
|
327
|
+
- **Fork PRs** get a read-only token, so the comment is skipped with a warning. The job summary and the artifact still carry the report.
|
|
328
|
+
- **Sticky comment:** the action updates only a marked comment posted by its own token's account (a bot account for `github.token`). A comment someone else writes with the same marker is left alone, and the action posts its own.
|
|
329
|
+
- **Triggers:** use `pull_request`. Do not use `pull_request_target` to run pages from a fork.
|
|
330
|
+
- **Artifact:** GitHub zips it and requires a login to download it. Unzip and open the HTML in a browser; it works offline. A Pages preview is not part of the action.
|
|
331
|
+
- The action lives in this repository, under `action/`, so one ref pins the action and the CLI contract it calls. `version` still picks the CLI.
|
|
209
332
|
|
|
210
333
|
## Environment
|
|
211
334
|
|
|
212
335
|
| Variable | Default | |
|
|
213
336
|
|---|---|---|
|
|
214
337
|
| `AUSTERE_AXI_PORT` | `4388` | server port |
|
|
215
|
-
| `AUSTERE_AXI_STATE_DIR` | `~/.austere-axi` | sessions, server log |
|
|
338
|
+
| `AUSTERE_AXI_STATE_DIR` | `~/.austere-axi` | sessions, owner key, server log (mode 0700 on macOS and Linux) |
|
|
216
339
|
| `AUSTERE_AXI_NO_OPEN` | | `1` = never open a browser |
|
|
217
340
|
| `AUSTERE_AXI_HOST` | | extra bind address (never `0.0.0.0`) |
|
|
218
341
|
| `AUSTERE_AXI_NO_TAILSCALE` | | `1` = loopback only |
|
|
219
|
-
| `AUSTERE_AXI_LINK_HOST` | | host in the printed remote link |
|
|
342
|
+
| `AUSTERE_AXI_LINK_HOST` | | host in the printed remote link (`http://<host>:<port>`) |
|
|
343
|
+
| `AUSTERE_AXI_LINK_URL` | | full origin of the printed remote link, e.g. `https://austere.example.com` behind a TLS proxy; the host is accepted without `ALLOWED_HOSTS` |
|
|
220
344
|
| `AUSTERE_AXI_ALLOWED_HOSTS` | | extra `Host` headers to accept (reverse proxy) |
|
|
221
345
|
| `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
|
|
346
|
+
| `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle shutdown; `0` disables |
|
|
347
|
+
| `AUSTERE_AXI_PERSIST` | | `1`: never idle out, as `server --persist` |
|
|
223
348
|
| `AUSTERE_AXI_NPM_REGISTRY` | `npm_config_registry`, else npmjs | where `components add` gets packs |
|
|
224
349
|
|
|
225
350
|
## Security
|
|
226
351
|
|
|
227
352
|
The server binds to loopback, plus the Tailscale IP when Tailscale is up. It never binds to all interfaces.
|
|
228
353
|
|
|
229
|
-
-
|
|
354
|
+
- Loopback is not the owner: other OS users on the machine can reach it too. The owner key in `owner.key` is.
|
|
355
|
+
- `/api/*` answers only loopback callers that send the owner key, and refuses browser requests. `/api/health` needs no key; it proves the server holds the key with an HMAC of its port and the CLI's nonce, so the CLI never talks to, or hands the key to, another program on its port.
|
|
356
|
+
- A browser signs in with a single-use login code: when the CLI opens your browser, it asks the server for a code and opens the page with it; the server swaps it for the owner cookie and drops it from the address bar. The key is never on a command line or in printed output. A code works once and for 60 seconds. Another local user who reads it from the process list in that time can use it first; your browser then shows "used already", and `austere-axi login` gives you a new one.
|
|
357
|
+
- A printed `url` works in a browser that already has the cookie. For any other browser (SSH with a forwarded port, `--no-open`, a second browser), run `austere-axi login [file]` and open the link it prints.
|
|
358
|
+
- The owner cookie is an HMAC of the key, not the key. Browsers share `127.0.0.1` cookies across ports, so any program listening on another local port that your browser visits can read it. With it, that program can open your pages and the hub as you in a browser, but not call `/api/*`, which needs the key.
|
|
359
|
+
- On macOS and Linux the state dir is made mode 0700 on every run, and new files in it (`owner.key`, `server.log`, and `hub.db`, which holds every note) are mode 0600. Files an older version created keep their old mode, behind the 0700 dir. On Windows, file modes do not apply: the state dir is protected only by your user profile's permissions.
|
|
230
360
|
- `Host` headers must be on an allowlist (DNS-rebinding defense).
|
|
231
361
|
- Browser routes that change state refuse a foreign `Origin`.
|
|
232
362
|
- 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.
|
|
363
|
+
- The page runs in a sandboxed iframe with no same-origin access. Packs run there too. Page files are also served with a `Content-Security-Policy: sandbox` header, so the page stays sandboxed when opened in a popup or its own tab. In review, links that open a new window are opened by the review page, and only if they are external `http(s):` or `mailto:` links. A popup the frame opens any other way stays sandboxed.
|
|
234
364
|
- The server serves files only from the page's own directory, with no dotfiles.
|
|
235
365
|
- Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
|
|
236
366
|
|
|
@@ -251,25 +381,35 @@ npm run build # dist/: runtime, packs, pack kit, Mermaid, spec, catalog, rev
|
|
|
251
381
|
npm test
|
|
252
382
|
npm run typecheck # tsc over src/cli, src/shared and the pack types
|
|
253
383
|
node dist/cli.mjs examples/session-migration.html
|
|
384
|
+
npm run site # _site/: the website (needs Chrome or CHROME_PATH); see site/ and examples/
|
|
254
385
|
```
|
|
255
386
|
|
|
256
387
|
| Path | Contents |
|
|
257
388
|
|---|---|
|
|
258
389
|
| `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `FileTree`), one file each |
|
|
259
390
|
| `src/runtime/stock.js` | Which stock components ship, which stay out of `spec`, their groups |
|
|
391
|
+
| `docs/` | [Writing a pack](docs/writing-a-pack.md) |
|
|
260
392
|
| `packs/` | Packs bundled with austere-axi (`mermaid`, `shadcn`) |
|
|
261
393
|
| `src/runtime/pack-api.js`, `src/pack-kit/` | `austere-axi/pack` and the `components build` pipeline |
|
|
262
394
|
| `src/shared/catalog.js` | Component catalog and prompt tiers |
|
|
263
395
|
| `src/runtime/` | Library, annotation layer, review bridge, styles |
|
|
264
396
|
| `src/chrome/` | Review UI around the page iframe |
|
|
265
397
|
| `src/cli/` | CLI, server, sessions, `check`, `export`, packs, hub |
|
|
398
|
+
| `action/` | The [GitHub Action](#github-action) (not published to npm) |
|
|
266
399
|
| `skills/` | Published skills: `austere`, `austere-setup` |
|
|
400
|
+
| `.claude-plugin/` | Claude Code marketplace and plugin manifest (in git, not in the npm package) |
|
|
267
401
|
|
|
268
402
|
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`.
|
|
269
403
|
|
|
270
404
|
Visual checks: `npm run gallery`, `npm run shot`, and `npm run test:browser` (real review loop in headless Chrome; needs Chrome or `CHROME_PATH`).
|
|
271
405
|
|
|
272
|
-
|
|
406
|
+
Website: `site/` holds the hand-written pages and CSS, `examples/` feeds the gallery (add a file there and it appears with a generated thumbnail), `scripts/site.mjs` builds `_site/`, and `.github/workflows/pages.yml` deploys it to GitHub Pages on every push to `main`. Examples need a `<meta name="description">` for the gallery blurb. None of it is published to npm.
|
|
407
|
+
|
|
408
|
+
Playground (`/play/`, source in `site/play/`): a textarea editor beside a sandboxed frame running `dist/austere.js`. It plays the part of the review chrome over the same `postMessage` bridge, so edits re-render in place and clicks arrive as the annotations `poll` prints. Problems come from `src/shared/check-program.js`, the same code as `austere-axi check`, bundled for the browser by `scripts/site.mjs` (with the spec, the component catalog and one template per example in `play/data.json`). The program travels in the URL hash (`#code=v1.<deflate-raw, base64url>`), so there is no server. `test/browser/play.browser.test.js` drives it in headless Chrome as part of `npm run test:browser`. Packs other than the bundled ones are not available there.
|
|
409
|
+
|
|
410
|
+
Benchmark against lavish-axi: `npm run bench` (needs model keys; see [docs/benchmark.md](docs/benchmark.md)). `npm run bench:report` rebuilds its tables from the committed results.
|
|
411
|
+
|
|
412
|
+
**CI** builds and tests every push to `main` and every pull request (Node 22, 24, 26 on Linux; Node 24 on Windows; browser suite). `action.yml` runs the GitHub Action on fixture pages (`test/fixtures/action/`). **Release:** bump `version` in `package.json`, push, and publish a GitHub Release. `publish.yml` tests again and publishes to npm with trusted publishing.
|
|
273
413
|
|
|
274
414
|
## License
|
|
275
415
|
|