aztrx-cli 0.4.5 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +325 -28
  2. package/dist/cli/help.d.ts +22 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +278 -81
  5. package/dist/core/auth.d.ts +33 -0
  6. package/dist/core/badge.d.ts +9 -0
  7. package/dist/core/browser.d.ts +3 -0
  8. package/dist/core/classifier.d.ts +41 -0
  9. package/dist/core/cloud/index.d.ts +62 -0
  10. package/dist/core/cloud/index.js +24 -4
  11. package/dist/core/devServer.d.ts +90 -0
  12. package/dist/core/devServer.js +253 -0
  13. package/dist/core/diagnose.d.ts +18 -0
  14. package/dist/core/diff.d.ts +31 -0
  15. package/dist/core/domWalker.d.ts +24 -0
  16. package/dist/core/domWalker.js +1 -1
  17. package/dist/core/eventBus.d.ts +59 -0
  18. package/dist/core/events.d.ts +42 -0
  19. package/dist/core/fixPr.d.ts +16 -0
  20. package/dist/core/fixPr.js +11 -1
  21. package/dist/core/fuzzer.d.ts +24 -0
  22. package/dist/core/fuzzer.js +1 -1
  23. package/dist/core/heal/apply.d.ts +28 -0
  24. package/dist/core/heal/boot.d.ts +74 -0
  25. package/dist/core/heal/boot.js +126 -23
  26. package/dist/core/heal/childEnv.d.ts +15 -0
  27. package/dist/core/heal/gates.d.ts +11 -0
  28. package/dist/core/heal/index.d.ts +19 -0
  29. package/dist/core/heal/index.js +53 -23
  30. package/dist/core/heal/llm.d.ts +43 -0
  31. package/dist/core/heal/redact.d.ts +22 -0
  32. package/dist/core/heal/sandbox.d.ts +54 -0
  33. package/dist/core/heal/sandbox.js +20 -3
  34. package/dist/core/heal/types.d.ts +102 -0
  35. package/dist/core/heal/verify.d.ts +37 -0
  36. package/dist/core/heal/verify.js +28 -14
  37. package/dist/core/httpFuzzer.d.ts +33 -0
  38. package/dist/core/init.d.ts +21 -0
  39. package/dist/core/init.js +5 -6
  40. package/dist/core/interceptor.d.ts +21 -0
  41. package/dist/core/llm.d.ts +36 -0
  42. package/dist/core/llm.js +0 -8
  43. package/dist/core/minimizer.d.ts +13 -0
  44. package/dist/core/modernize.d.ts +28 -0
  45. package/dist/core/modernize.js +2 -2
  46. package/dist/core/networkGuard.d.ts +13 -0
  47. package/dist/core/orchestrator.d.ts +86 -0
  48. package/dist/core/orchestrator.js +5 -2
  49. package/dist/core/patrol/loop.d.ts +46 -0
  50. package/dist/core/patrol/pr.d.ts +42 -0
  51. package/dist/core/patrol/pr.js +9 -6
  52. package/dist/core/patrol/record.d.ts +51 -0
  53. package/dist/core/patrol/state.d.ts +39 -0
  54. package/dist/core/pr.d.ts +3 -0
  55. package/dist/core/pr.js +15 -11
  56. package/dist/core/prompt.d.ts +14 -0
  57. package/dist/core/recorder.d.ts +23 -0
  58. package/dist/core/recorder.js +1 -1
  59. package/dist/core/renderMarkdown.d.ts +7 -0
  60. package/dist/core/replay.d.ts +33 -0
  61. package/dist/core/replay.js +9 -2
  62. package/dist/core/report.d.ts +8 -0
  63. package/dist/core/resolver.d.ts +44 -0
  64. package/dist/core/resolver.js +51 -13
  65. package/dist/core/rng.d.ts +2 -0
  66. package/dist/core/specCompiler.d.ts +17 -0
  67. package/dist/core/studio.d.ts +6 -0
  68. package/dist/core/studio.js +0 -3
  69. package/dist/core/summarize.d.ts +26 -0
  70. package/dist/core/swarm.d.ts +97 -0
  71. package/dist/core/telemetry/index.d.ts +33 -0
  72. package/dist/core/telemetry/index.js +22 -4
  73. package/dist/core/telemetry/sanitize.d.ts +19 -0
  74. package/dist/core/telemetry/types.d.ts +26 -0
  75. package/dist/core/types.d.ts +88 -0
  76. package/dist/core/ui.d.ts +23 -0
  77. package/dist/core/ui.js +0 -5
  78. package/dist/core/validator.d.ts +14 -0
  79. package/dist/core/version.d.ts +1 -0
  80. package/dist/hooks/index.d.ts +106 -0
  81. package/dist/hooks/index.js +349 -0
  82. package/dist/mcp/index.d.ts +58 -0
  83. package/dist/mcp/index.js +295 -0
  84. package/dist/mcp/install.d.ts +52 -0
  85. package/dist/mcp/install.js +204 -0
  86. package/dist/mcp/protocol.d.ts +106 -0
  87. package/dist/mcp/protocol.js +156 -0
  88. package/dist/mcp/tools.d.ts +107 -0
  89. package/dist/mcp/tools.js +621 -0
  90. package/dist/next/index.d.ts +70 -0
  91. package/dist/next/index.js +103 -0
  92. package/dist/plugins/scan.d.ts +85 -0
  93. package/dist/plugins/scan.js +188 -0
  94. package/dist/ui/app.d.ts +11 -0
  95. package/dist/vite/index.d.ts +60 -0
  96. package/dist/vite/index.js +56 -0
  97. package/package.json +34 -4
package/README.md CHANGED
@@ -2,20 +2,25 @@
2
2
 
3
3
  > **Catch the runtime crash your Error Boundary hid — and prove it with a test, not a log line.**
4
4
 
5
- [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green.svg?style=flat-square)](https://nodejs.org)
6
- [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](LICENSE)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D20-green.svg?style=flat-square)](https://nodejs.org)
6
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://github.com/Aztrx-AI/aztrx/blob/main/LICENSE)
7
7
 
8
8
  Aztrx AI finds **runtime** bugs, not security holes. It drives your web app like a hostile
9
9
  user and catches the crashes that ship to real users — *including ones a React Error Boundary
10
10
  swallows* (the errors `window.onerror` never sees). Each crash comes back as an exact source
11
- line plus an executable **Playwright repro** that fails `3/3` times. Then it fixes it.
11
+ line, and with `--repro` an executable **Playwright repro** that replays it. Then it fixes it.
12
12
 
13
13
  ```bash
14
- npx aztrx-cli run http://localhost:3000 # find crashes — zero setup, no key
15
- npx aztrx-cli run http://localhost:3000 --fix # fix them — free for common bugs
14
+ npx aztrx-cli # find crashes — no key, no config, no URL to look up
15
+ npx aztrx-cli --fix # fix them — free for common bugs
16
16
  ```
17
17
 
18
- ![aztrx demo](media/demo.gif)
18
+ Run it in your project and it works out what your app is, starts your dev server if
19
+ nothing is listening, scans, and stops the server again on the way out. Already have
20
+ one running? It attaches and leaves it alone. Pass a URL (`aztrx-cli run http://…`)
21
+ and it goes exactly there instead.
22
+
23
+ ![aztrx demo](https://raw.githubusercontent.com/Aztrx-AI/aztrx/main/media/demo.gif)
19
24
 
20
25
  ---
21
26
 
@@ -23,7 +28,7 @@ npx aztrx-cli run http://localhost:3000 --fix # fix them — free for common
23
28
 
24
29
  - **Sees swallowed errors.** Error Boundaries and `window.onerror` miss the errors your app *catches*. Aztrx reads the real throw-site stack off the `Error` object — a crash you've never seen in your logs becomes a finding you can't ignore.
25
30
  - **Explains the crash in one line.** Every crash/error ships with a one-sentence diagnosis — why it happened and what to change (e.g. `the value before `.cart` is undefined — guard with `?.`). Free, no key, right in the terminal and `report.html`.
26
- - **Proves, not reports.** Every crash ships with an executable `.spec.ts` repro and a flake-rate verdict — `[deterministic 3/3]`, `[flaky 3/5]`, or `[unreliable]`.
31
+ - **Proves, not reports.** With `--repro` (implied by `--fix`), every interaction crash ships with an executable `.spec.ts` repro and a flake-rate verdict — `[deterministic 3/3]`, `[flaky 3/5]`, or `[unreliable]`. A crash that can't be replayed reliably is reported as `[unreliable]` rather than dressed up as proof.
27
32
  - **Safe by default.** A deny-by-default network guard blocks off-origin calls, a destructive-action deny-list refuses to click "delete", "pay", or "logout", and nothing leaves your machine unless you opt in.
28
33
 
29
34
  ---
@@ -31,15 +36,17 @@ npx aztrx-cli run http://localhost:3000 --fix # fix them — free for common
31
36
  ## Quickstart
32
37
 
33
38
  ```bash
34
- npm i -g aztrx-cli # or use npx — no install needed
39
+ npm i -g aztrx-cli # or use npx — no install needed
35
40
 
36
- aztrx-cli run http://localhost:3000 # 1. find the crashes (no key, no account)
37
- aztrx-cli run http://localhost:3000 --repro # 2. prove them with an executable test
38
- aztrx-cli run http://localhost:3000 --fix # 3. fix them
41
+ aztrx-cli # 1. find the crashes (no key, no account)
42
+ aztrx-cli --repro # 2. prove them with an executable test
43
+ aztrx-cli --fix # 3. fix them
39
44
  ```
40
45
 
41
- Point it at any running dev server. It drives Chromium through Playwright (the first run
42
- downloads the browser automatically).
46
+ Run it from your project root it detects the framework, finds your dev server or
47
+ boots it (`npm run dev`), and tears the server down when it exits. To point it at a
48
+ server somewhere else, pass the URL: `aztrx-cli run http://localhost:3000`. It drives
49
+ Chromium through Playwright (the first run downloads the browser automatically).
43
50
 
44
51
  ---
45
52
 
@@ -69,8 +76,10 @@ export AZTRX_MODEL="anthropic/claude-sonnet-5"
69
76
  ```
70
77
 
71
78
  Every fix is redacted, sandboxed in a detached git worktree, compiler-checked, and gated on
72
- your test suite before you see it. Aztrx never commits. `--pr` opens a merge-ready PR;
73
- `--regression-test` drops the repro into your test dir so the bug can't come back.
79
+ your test suite before you see it. `--fix` writes a patch and stops — nothing is committed to
80
+ the branch you are on. `--pr` is the opt-in that goes further: it commits to a new
81
+ `aztrx/fix-…` branch and opens a merge-ready PR from it. `--regression-test` drops the repro
82
+ into your test dir so the bug can't come back.
74
83
 
75
84
  ---
76
85
 
@@ -86,11 +95,226 @@ your test suite before you see it. Aztrx never commits. `--pr` opens a merge-rea
86
95
  | `patrol <url>` | autonomous loop — re-scan, fix, open a PR per bug |
87
96
  | `modernize <file>` | rewrite legacy JS/TS into modern idiomatic syntax |
88
97
  | `studio` | live dashboard on `localhost:7331` |
98
+ | `hook install` | scan every `git push` — block the ones that crash |
99
+ | `mcp install` | put aztrx inside your editor's agent (Claude Code, Cursor, VS Code) |
89
100
 
90
101
  Full list: `aztrx-cli run --help`, or the [CLI reference](#cli-reference).
91
102
 
92
103
  ---
93
104
 
105
+ ## Scan while you develop
106
+
107
+ Add a few lines and your dev server reports its own runtime crashes as you work —
108
+ no second terminal, no separate command to remember. Same plugin either way; only
109
+ the hook differs.
110
+
111
+ ### Vite
112
+
113
+ ```ts
114
+ // vite.config.ts
115
+ import { aztrx } from "aztrx-cli/vite";
116
+
117
+ export default defineConfig({
118
+ plugins: [aztrx()],
119
+ });
120
+ ```
121
+
122
+ ```bash
123
+ npm run dev
124
+ # VITE v8.3.0 ready in 312 ms
125
+ # ➜ Local: http://localhost:5173/
126
+ # [aztrx] 1 crash — first: src/Report.tsx:42
127
+ # [aztrx] run `npx aztrx-cli` for the repro and the fix.
128
+ ```
129
+
130
+ ### Next.js
131
+
132
+ Next has no config object to hang a plugin off, so it uses `instrumentation.ts` —
133
+ the hook Next added for exactly this (Sentry and OpenTelemetry use it too):
134
+
135
+ ```ts
136
+ // instrumentation.ts, next to app/ or src/
137
+ export async function register() {
138
+ if (process.env.NEXT_RUNTIME === "nodejs") {
139
+ const { registerAztrx } = await import("aztrx-cli/next");
140
+ registerAztrx();
141
+ }
142
+ }
143
+ ```
144
+
145
+ ```bash
146
+ npm run dev
147
+ # ▲ Next.js 16.3.2 (Turbopack)
148
+ # - Local: http://localhost:3001
149
+ # [aztrx] 1 crash — first: app/page.tsx:18
150
+ # [aztrx] run `npx aztrx-cli` for the repro and the fix.
151
+ ```
152
+
153
+ The `NEXT_RUNTIME` guard is required, not decoration: Next compiles
154
+ `instrumentation.ts` for the edge runtime as well, which has no child processes.
155
+ The plugin itself does nothing outside `next dev` — a scan during `next build` or
156
+ `next start` would point a browser at a real deployment. It finds the port Next
157
+ actually bound, so it still works when 3000 was taken and Next moved to 3001.
158
+
159
+ ### Options and behaviour
160
+
161
+ ```ts
162
+ // Vite: plugins: [aztrx({ … })] Next.js: registerAztrx({ … }) — same options.
163
+ {
164
+ maxActions: 50, // shallower, faster walk
165
+ fuzz: true, // seeded chaos instead of the deterministic walk
166
+ onResult: (r) => {}, // wire findings into your own tooling
167
+ }
168
+ ```
169
+
170
+ Both plugins are a **sentinel, not a reporter**: silent until the scan finishes,
171
+ then one line. Run `aztrx-cli` for the full report, the executable repro, and the
172
+ fix. Findings also land in `.aztrx/` as usual.
173
+
174
+ The scan runs as a **separate process**, killed when the dev server stops. That is
175
+ deliberate: a scanner that crashes, hangs, or leaves a browser behind must never be
176
+ able to take your dev server down with it — and it dies with the parent even if the
177
+ parent is hard-killed. Set `AZTRX_DEV_SCAN=0` to skip a scan without editing the
178
+ config.
179
+
180
+ ---
181
+
182
+ ## Scan before you push (git hook)
183
+
184
+ One command and every `git push` scans your app first:
185
+
186
+ ```bash
187
+ aztrx-cli hook install
188
+ # ✓ pre-push hook installed.
189
+ # .git/hooks/pre-push
190
+ # Every `git push` now scans the app and blocks on a crash.
191
+ # Skip one push with `git push --no-verify`, or a run with AZTRX_HOOK_SKIP=1.
192
+ ```
193
+
194
+ A push that found something:
195
+
196
+ ```
197
+ [aztrx] 1 changed file: src/Cart.tsx. Scanning the app…
198
+ [aztrx] 1 crash · 1 warning — first: src/Cart.tsx:42
199
+ [aztrx] run `npx aztrx-cli` for the repro and the fix.
200
+ [aztrx] push blocked — 1 crash/error finding in your app.
201
+ [aztrx] fix them and push again; to push anyway, `git push --no-verify`.
202
+ ```
203
+
204
+ It scans **your app, not your diff** — a changed file is the *trigger*, not the scope. A crash
205
+ at `Cart.tsx:42` usually comes through a `useCart()` that changed three files away, and a
206
+ diff-shaped scan would look straight past it.
207
+
208
+ ### It stays out of the way
209
+
210
+ Push a README and it says so and gets out of the way in about a second:
211
+
212
+ ```
213
+ [aztrx] skipped — only docs, metadata or assets changed (1 file).
214
+ ```
215
+
216
+ The skip list is deliberately short and boring: `.github/`, `.vscode/`, `.aztrx/`, `LICENSE`,
217
+ `.gitignore`, `.md`, images. Anything arguable — lockfiles, config, `.mdx` — is **not** on it,
218
+ because a wrong skip is a missed crash, which is the one thing this hook exists to prevent.
219
+ When it cannot tell what changed (a branch the remote has never seen, a shallow clone) it
220
+ scans the whole app and says why.
221
+
222
+ | | |
223
+ | --- | --- |
224
+ | `git push --no-verify` | skip this push |
225
+ | `AZTRX_HOOK_SKIP=1 git push` | same, for scripts |
226
+ | `aztrx-cli hook uninstall` | remove it |
227
+
228
+ ### It cannot block you by accident
229
+
230
+ Aztrx blocks a push because it found a crash — never because it is broken:
231
+
232
+ - **Not installed, not on PATH** → the push proceeds.
233
+ - **The scan fails, or never finishes** → the push proceeds, with the reason printed.
234
+ `AZTRX_HOOK_TIMEOUT` (ms, default `300000`) caps a single scan.
235
+ - **A failed scan is never reported as clean.** "I could not look" is a different sentence
236
+ from "nothing there", and it stays that way.
237
+
238
+ A hook that blocks pushes when it is merely broken gets turned off, and then it catches
239
+ nothing.
240
+
241
+ The installed file is a short POSIX shim that calls back into the CLI, so `npm i -g
242
+ aztrx-cli@latest` upgrades the hook as well — nothing to reinstall. `hook install` is
243
+ idempotent, installs where git actually looks (so `core.hooksPath` — husky, Lefthook — is
244
+ respected), and refuses to overwrite a `pre-push` hook it did not write unless you pass
245
+ `--force`.
246
+
247
+ **Not handled:** under Yarn PnP there is no `node_modules/aztrx-cli` to find, so the hook
248
+ skips silently. Safe, but useless — use npm, pnpm, or Yarn with `nodeLinker: node-modules`.
249
+
250
+ ---
251
+
252
+ ## Use aztrx from your editor (MCP)
253
+
254
+ Your editor's agent can write code but cannot run it. It can compile, lint, and typecheck —
255
+ none of which open a page and click the button. So it will tell you a change works when all
256
+ it knows is that the change *parsed*.
257
+
258
+ `aztrx mcp` gives the agent the missing step: it drives the app in a real browser and hands
259
+ back the runtime crashes it produced, with an executable repro attached.
260
+
261
+ ```bash
262
+ npx aztrx-cli mcp install
263
+ # ✓ Claude Code — .mcp.json — added.
264
+ # ✓ Cursor — .cursor/mcp.json — added.
265
+ # • VS Code — no .vscode/ in this project
266
+ #
267
+ # Restart your editor — or reload its window — to pick the server up.
268
+ ```
269
+
270
+ It writes the config for the editors your project actually uses, and only ever adds one key
271
+ to a file it reads first. Your other MCP servers are left alone. If a config will not parse
272
+ it **refuses** rather than overwriting — a missing comma should never cost you your setup —
273
+ and `--force` is the escape hatch, which saves a `.bak` first. `mcp uninstall` removes only
274
+ our entry and keeps the file.
275
+
276
+ ### The three tools
277
+
278
+ | Tool | What it does | Costs |
279
+ | --- | --- | --- |
280
+ | `aztrx_scan` | Drives the app, reports crashes/errors with source locations. Boots the dev server itself if none is running. | tens of seconds |
281
+ | `aztrx_repro` | The minimized steps and the compiled Playwright spec for one finding. Reads the scan that already ran. | free |
282
+ | `aztrx_fix` | Patch → verify in a git worktree by replaying the repro → optionally write it into your tree. | one model call |
283
+
284
+ The scan returns a compact projection with a `scanId`; the detail — the action sequence, the
285
+ spec, the diff — is fetched by handle. A stack trace in the agent's context on every scan is
286
+ how a useful tool becomes an expensive one.
287
+
288
+ `aztrx_fix` needs a key (`ANTHROPIC_API_KEY`, or `AZTRX_API_BASE` + `AZTRX_API_KEY` +
289
+ `AZTRX_MODEL` for any other provider). Without one it returns `no-llm` and attempts nothing.
290
+ Scanning and proving never need a key. Aztrx never commits: `apply: true` writes working-tree
291
+ files, and `git diff` is the review.
292
+
293
+ ### What it will not do
294
+
295
+ - **No `--allow-destructive`, `--fuzz`, or `--http-fuzz` over MCP.** The tool drives a real
296
+ browser against an app you are working in. An agent should not be able to reach for
297
+ data-mutating controls because a prompt suggested it. If you want the fuzzer, that is a
298
+ deliberate `aztrx-cli run` in a terminal.
299
+ - **A scan that could not run is an error, never "no findings."** If the app would not boot
300
+ or the browser could not reach it, the tool returns `isError` with the reason — it does not
301
+ return an empty list. An agent that reads `0 crashes` from a scan that never happened will
302
+ tell you the code is fine.
303
+
304
+ ### Protocol support
305
+
306
+ MCP changed shape in its `2026-07-28` revision: the `initialize` handshake is gone, replaced
307
+ by per-request metadata. Editors are split across that line — Claude Code speaks the new
308
+ revision, and Cursor still speaks `2025-11-25`.
309
+
310
+ `aztrx mcp` speaks **both** and picks per request, which is why it works in both editors
311
+ today and will keep working as the others move. It is ~550 lines — the protocol layer and the
312
+ server — with zero new dependencies;
313
+ neither official SDK covers both revisions, and the legacy one pulls seventeen runtime
314
+ dependencies (express, cors, jose, ajv…) for a stdio server that needs none of them.
315
+
316
+ ---
317
+
94
318
  ## Autonomous patrol
95
319
 
96
320
  `aztrx patrol` is the looped version of `run --fix`: point it at a running app and it
@@ -113,6 +337,8 @@ aztrx-cli patrol http://localhost:3000 --batch # group a cycle's fixes into one
113
337
  | `--batch` | Group all of a cycle's fixes into one PR | one PR per bug |
114
338
  | `--once` | Run a single scan then exit | loop forever |
115
339
  | `--fuzz` / `--workers <n>` | Detection mode / parallelism (pass-through to `run`) | — |
340
+ | `--login` | Auto-login before each pass — needs `$AZTRX_AUTH_EMAIL` + `$AZTRX_AUTH_PASSWORD`, or `--login-email` / `--login-password` | off |
341
+ | `--storage-state <path>` | Playwright storage-state JSON for authenticated pages | — |
116
342
 
117
343
  Guardrails keep the loop from running away: it only stages the files a patch touched
118
344
  (never `git add -A`), dedups by crash fingerprint (a re-scan won't re-open the same PR),
@@ -123,7 +349,7 @@ backs off from unfixable bugs, and respects a session-wide LLM spend cap.
123
349
  ## Security
124
350
 
125
351
  - **Local-first.** Nothing leaves your machine unless you opt in.
126
- - **Never commits.** Fixes land in a detached worktree for your review.
352
+ - **Never touches the branch you are on.** `--fix` and `--heal` land a patch in a detached worktree for your review — `git diff` is the review. The two things that do commit are the two whose entire job is to open a PR: `--pr`, and `patrol`. Both commit to their own `aztrx/fix-…` branch, stage **only** the files a patch touched, and return you to the branch you started on.
127
353
  - **Redacted.** Secrets are stripped from the file, error, and stack before any LLM call.
128
354
  - **Deny-by-default network.** Off-origin calls are blocked; destructive clicks (delete/pay/logout) are refused.
129
355
  - **`.aztrx/` is gitignored** — repros, reports, and patches stay out of history.
@@ -132,9 +358,8 @@ backs off from unfixable bugs, and respects a session-wide LLM spend cap.
132
358
 
133
359
  ## Continuous Integration (GitHub Action)
134
360
 
135
- Runtime gate on every PR — boots your dev server, runs
136
- `aztrx-cli run --fail-on --repro --heal`, posts a comment with the repro + patch, and fails on
137
- a crash/error.
361
+ Runtime gate on every PR — runs `aztrx-cli run --fail-on --repro --heal`, posts a comment with
362
+ the repro + patch, and fails on a crash/error.
138
363
 
139
364
  ```yaml
140
365
  # .github/workflows/ci.yml — composite action, inline
@@ -142,30 +367,86 @@ on: pull_request
142
367
  jobs:
143
368
  aztrx:
144
369
  runs-on: ubuntu-latest
370
+ timeout-minutes: 30 # recommended — see below
145
371
  permissions: { contents: read, pull-requests: write }
146
372
  steps:
147
373
  - uses: actions/checkout@v4
148
- - uses: Aztrx-AI/aztrx@v0.4.3
374
+ - uses: Aztrx-AI/aztrx@v0.5.1
149
375
  with:
150
- url: http://localhost:3000
151
- start-command: npm run dev # optional — boot the app in the background
152
376
  token: ${{ github.token }}
153
377
  anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} # optional — enables --heal
154
378
  ```
155
379
 
380
+ That is the whole setup: aztrx detects the framework, boots your dev server, scans, and shuts
381
+ it down again. Already have a server running, or want to control how it starts? Pass a URL and
382
+ the action stays out of the way:
383
+
384
+ ```yaml
385
+ with:
386
+ url: http://localhost:3000
387
+ start-command: npm run dev # boot the app in the background first
388
+ wait-for: http://localhost:3000 # defaults to `url`
389
+ token: ${{ github.token }}
390
+ ```
391
+
156
392
  A status badge (`--badge`) and PR comment (`--pr-comment`) work the same way — regenerate in
157
393
  CI on every push.
158
394
 
395
+ ### What the check tells you
396
+
397
+ `uses: Aztrx-AI/aztrx@v0.5.1` runs **aztrx-cli 0.5.1** — the action reads its own version, so
398
+ the tag selects the engine, not just the wrapper.
399
+
400
+ A red check has two different meanings, and the message says which:
401
+
402
+ | | |
403
+ | --- | --- |
404
+ | `aztrx detected crash/error findings` | it scanned, and found something. Fix it, or run `npx aztrx-cli` locally for the repro. |
405
+ | `aztrx did not run (exit code: N)` | the scan never produced a result — a bad version pin, a registry failure, a broken config. **Not** a verdict about your app. The last lines of the run are printed to explain it. |
406
+
407
+ Set `timeout-minutes` on the job. The default `args` include `--repro` and `--heal`, which
408
+ drive your app repeatedly and can call a model — without a ceiling, one hung browser spends
409
+ GitHub's 6-hour job default finding nothing.
410
+
411
+ On a **fork pull request** the token is read-only, so the comment is skipped with a warning and
412
+ the check still reports the scan result. `--heal` needs `ANTHROPIC_API_KEY`, which forks do not
413
+ receive; it skips cleanly rather than failing.
414
+
415
+ Prefer not to wire it inline? The same thing is packaged as a reusable workflow:
416
+
417
+ ```yaml
418
+ jobs:
419
+ aztrx:
420
+ uses: Aztrx-AI/aztrx/.github/workflows/aztrx-pr.yml@v0.5.1
421
+ secrets:
422
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
423
+ ```
424
+
159
425
  ---
160
426
 
161
427
  ## CLI reference
162
428
 
163
429
  `aztrx-cli run --help` is grouped by intent (Detect / Prove / Fix / Report & ship / Auth);
164
430
  the table below is the complete reference — including flags hidden from `--help` (aliases and
165
- niche tuning knobs).
431
+ niche tuning knobs). `aztrx-cli --version` (or `-V`) prints the installed version, read from
432
+ the package's own `package.json` so it cannot drift from what npm shipped.
433
+
434
+ The commands that are not `run`:
435
+
436
+ | Command | What it does |
437
+ | --- | --- |
438
+ | `mcp` | Serve the [MCP server](#use-aztrx-from-your-editor-mcp) on stdio (this is what an editor's config runs) |
439
+ | `mcp install [--force]` | Add aztrx to `.mcp.json` / `.cursor/mcp.json` / `.vscode/mcp.json`, merging one key |
440
+ | `mcp uninstall` | Remove only that key |
441
+ | `init` | Scaffold `aztrx.config.ts` |
442
+ | `hook install \| uninstall \| run` | The [pre-push hook](#scan-before-you-push-git-hook) |
443
+ | `patrol [url]` | Autonomous scan → fix → PR loop |
444
+ | `modernize <file>` | Rewrite a legacy file with an LLM |
445
+ | `studio [--port n]` | Live dashboard (`7331`) |
166
446
 
167
447
  | Flag | Description | Default |
168
448
  | --- | --- | --- |
449
+ | `--no-boot` | Attach to a running dev server only — never start one yourself | boots when needed |
169
450
  | `--fuzz` | Seeded chaos fuzzing instead of the deterministic walk | — |
170
451
  | `--http-fuzz` | Server-side mutation fuzzing — hostile requests against the target origin | — |
171
452
  | `--http-fuzz-mutations` | With `--http-fuzz`: also send POST/PUT body mutations (default: GET-only) | — |
@@ -179,7 +460,7 @@ niche tuning knobs).
179
460
  | `--pr` | Open a merge-ready PR with the verified fixes (with `--fix`) | — |
180
461
  | `--lang <code>` | Language for the human-language summary (`en`, `ru`) | `en` |
181
462
  | `--upload` | Stream run findings to the cloud ingest backend | — |
182
- | `--api-key <key>` | Auth key for `--upload` / `--share-data` | `$AZTRX_API_KEY` |
463
+ | `--api-key <key>` | Auth key for `--upload` / `--share-data` — distinct from the model provider key | `$AZTRX_CLOUD_API_KEY` |
183
464
  | `--cloud-url <url>` | Ingest server base URL | `https://api.aztrx.app` |
184
465
  | `--max-actions <n>` | Max actions per pass | `100` |
185
466
  | `--seed <n>` | PRNG seed for deterministic fuzz | `42` |
@@ -194,7 +475,7 @@ niche tuning knobs).
194
475
  | `--start-command <cmd>` | Command to boot the app for server healing | `scripts.dev` → `scripts.start` |
195
476
  | `--pr-comment [path]` | Write a GitHub PR markdown comment | `.aztrx/pr-comment.md` |
196
477
  | `--badge [path]` | Write a self-contained SVG status badge | `.aztrx/badge.svg` |
197
- | `--regression-test [dir]` | Copy validated repro specs into the project test dir | `e2e/` or `tests/` |
478
+ | `--regression-test [dir]` | Copy validated repro specs into the project test dir | first of `e2e/`, `tests/`, `test/`, `__tests__/`; else `.aztrx/regression/` |
198
479
  | `--telemetry` | Collect anonymized tuples locally (opt-in) | — |
199
480
  | `--share-data` | Also upload the sanitized tuples (opt-in) | — |
200
481
  | `--repo <path>` | Root path for sourcemap → source resolution | cwd |
@@ -209,6 +490,17 @@ niche tuning knobs).
209
490
  | `--dry-run` | Log planned actions without executing them | — |
210
491
  | `--crash-test` | Throw a deliberate error to verify capture | — |
211
492
  | `--plain` / `--ui` | Force plain logs / force the live panel | — |
493
+ | `--json` | One JSON document on stdout, nothing else — for editors, plugins, and CI | — |
494
+
495
+ > **The hosted ingest is not live yet.** `--upload` and `--share-data` point at
496
+ > `https://api.aztrx.app`, which does not currently resolve, and the server in
497
+ > `server/` has no deployment. The routes are real and tested —
498
+ > `tests/server-ingest.test.ts` drives the actual `createIngestServer` over HTTP,
499
+ > including the fingerprint dedup — but there is no host behind the default URL.
500
+ > Until there is, point `--cloud-url` at your own instance of `server/`. A failed
501
+ > upload now says so on stderr instead of being discarded, and `--api-key` takes
502
+ > `$AZTRX_CLOUD_API_KEY`: it is deliberately *not* `$AZTRX_API_KEY`, which is your
503
+ > model provider key and has no business on the wire as an ingest credential.
212
504
 
213
505
  ---
214
506
 
@@ -241,12 +533,17 @@ one seeded runtime bug:
241
533
  | corpus | detection | deterministic repro |
242
534
  | --- | --- | --- |
243
535
  | 13 Next.js 16 apps | **13/13 · 100% recall** | **12/12 · 100%** |
244
- | 12 vanilla archetypes | **12/12 · 100% recall** | **10/11 · 91%** |
536
+ | 13 vanilla archetypes | **13/13 · 100% recall** | **11/12 · 91.7%** |
537
+
538
+ One archetype is `13-swallowed-boundary` — a crash caught by an Error Boundary and
539
+ logged via `console.error`, never rethrown. A `pageerror`-only detector scores
540
+ **0/1** on it. It is the README's headline claim, so it lives in the corpus where a
541
+ regression would fail the benchmark.
245
542
 
246
543
  Reproduce it yourself: `npm run bench` (archetypes) and `cd bench/frameworks && npm run bench`
247
544
  (Next.js corpus). Per-case results and scope notes live in
248
- [`bench/frameworks/RESULTS.md`](bench/frameworks/RESULTS.md) and
249
- [`bench/RESULTS.md`](bench/RESULTS.md).
545
+ [`bench/frameworks/RESULTS.md`](https://github.com/Aztrx-AI/aztrx/blob/main/bench/frameworks/RESULTS.md) and
546
+ [`bench/RESULTS.md`](https://github.com/Aztrx-AI/aztrx/blob/main/bench/RESULTS.md).
250
547
 
251
548
  ## Contributing
252
549
 
@@ -0,0 +1,22 @@
1
+ import { Command, Help, Option } from "commander";
2
+ /**
3
+ * Intent-grouped `--help` for the `run` command. Commander v12 has no native
4
+ * option grouping, so we tag each Option with a group and render the sections
5
+ * ourselves (reusing commander's own term/description/wrap helpers for a
6
+ * consistent look).
7
+ *
8
+ * The goal is a funnel the user can actually remember — Detect / Prove / Fix /
9
+ * Report & ship / Auth — with everything else demoted to a compact "Advanced
10
+ * options" block rather than a flat 40-line dump.
11
+ */
12
+ export type GroupName = "detect" | "prove" | "fix" | "ship" | "auth" | "advanced";
13
+ /** Build a commander Option tagged with a help group (defaults to "advanced"). */
14
+ export declare function opt(flags: string, description: string, group?: GroupName): Option;
15
+ /**
16
+ * Standalone `Help.formatHelp` override, registered via
17
+ * `runCommand.configureHelp({ formatHelp })`. Mirrors commander's built-in
18
+ * layout (Usage / Description / Arguments / Commands) but replaces the flat
19
+ * "Options:" list with named groups, and compresses the advanced flags into a
20
+ * single wrapped line of flag names.
21
+ */
22
+ export declare function formatHelp(cmd: Command, helper: Help): string;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};