@verirun/cli 0.2.4 → 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 CHANGED
@@ -4,6 +4,8 @@ Record a browser workflow, replay it, and capture the Supabase, Vercel + Railway
4
4
  your backend produced during the replay. Plus `verirun map`: a static
5
5
  interaction graph of your React/TypeScript app — every page, what you can click
6
6
  on it, and which backend calls those clicks make — without running anything.
7
+ And `verirun scan <url>`: a read-only scan of any live site for JS errors,
8
+ failed requests, dead links and missing anchors, with no source or logs needed.
7
9
 
8
10
  ## Install
9
11
 
@@ -30,17 +32,34 @@ your `MANPATH` (`man verirun`) on macOS, Linux, and WSL / Git Bash. Native
30
32
  Windows (cmd, PowerShell) has no `man` — use `--help`, or `man -l` the file
31
33
  under a POSIX shell. From a `npm link` checkout: `man -l man/verirun.1`.
32
34
 
35
+ ### Output and exit codes
36
+
37
+ Output is compact by default: a verdict on the first line (`PASS` / `FAIL`),
38
+ then only the evidence for it. Commands that report something take `--json`
39
+ (one object on one line, with a `schemaVersion`) and `--verbose` for the full
40
+ detail. With `--json`, progress and warnings go to stderr, so stdout is the
41
+ result and nothing else.
42
+
43
+ | Exit code | Meaning |
44
+ | --- | --- |
45
+ | `0` | The command did what it was asked and the check, if any, passed. |
46
+ | `1` | It ran, and what it checks failed: a replay step (`run`), findings (`scan`), errors in a workflow file (`workflow check`), no usable license (`whoami`), a recording with no actions (`record`). |
47
+ | `2` | It could not run: bad usage, missing setup, no license, an unreachable target. With `--json` the message is also printed on stdout as `{"status":"error","error":{"message":"…"}}`. |
48
+
49
+ Before 0.3.0 every failure exited `1`. A script that only tests for non-zero is
50
+ unaffected.
51
+
33
52
  ---
34
53
 
35
54
  ## Account
36
55
 
37
- `record` and `run` need a connected Verirun account. First run on a machine:
56
+ `record`, `run`, `map` and `scan` need a connected Verirun account. First run on a machine:
38
57
 
39
58
  ```bash
40
59
  verirun login
41
60
  ```
42
61
 
43
- This prints a short code and a link. Open the link, sign in (a 7 day free trial
62
+ This prints a short code and a link. Open the link, sign in (a 3-day free trial
44
63
  starts automatically if you are new), and enter the code. The CLI then holds a
45
64
  device token for that machine.
46
65
 
@@ -49,6 +68,11 @@ verirun whoami # account, plan, subscription status
49
68
  verirun logout # disconnect this machine
50
69
  ```
51
70
 
71
+ `verirun whoami` exits `0` when the gated commands would run and `1` when they
72
+ would not, and `verirun whoami --json` prints the same status as JSON
73
+ (`schemaVersion` 1, with a `licensed` boolean), so a script can check the
74
+ license without parsing text.
75
+
52
76
  | | |
53
77
  | --- | --- |
54
78
  | `~/.verirun/config.json` | Holds the device token. Per user, not per project. Restricted to your account (0600, or an `icacls` ACL on Windows). |
@@ -114,6 +138,8 @@ Opens a headed browser. Perform the workflow by hand — click, type, navigate.
114
138
  | `--user-data-dir <dir>` | Record in a persistent profile directory, so the session is already logged in and stays logged in for later runs. The real browser using that profile must be closed first. |
115
139
  | `--test-id-attribute <attr>` | Attribute preferred for test-id selectors. Defaults to `data-testid`; also settable as `testIdAttribute` in config. |
116
140
  | `-f, --force` | Overwrite an existing workflow of the same name. Without it, recording onto an existing name errors. |
141
+ | `--json` | Print the result as JSON (`schemaVersion` 1: `name`, `saved`, `steps`, `path`). Everything else goes to stderr. |
142
+ | `--verbose` | List every recorded step after saving. By default `record` prints one line: how many steps it saved and where. |
117
143
 
118
144
  **Name rules.** `<name>` becomes a filename: letters, digits, dot, dash,
119
145
  underscore; must start with a letter or digit; 64 chars max. Two Windows rules
@@ -147,6 +173,20 @@ what you recorded is what runs.
147
173
  into a field and close the browser without clicking or tabbing away, that last
148
174
  value is not recorded. Tab out before closing.
149
175
 
176
+ **Dialogs.** Answer `alert` / `confirm` / `prompt` boxes in the recording
177
+ browser as you normally would. Codegen notes *that* a step opened one but not
178
+ which button you pressed, so after you close the browser `record` asks, per such
179
+ step, `[O]K / [c]ancel` (Enter = OK; without a terminal every answer is OK). The
180
+ answer is saved on the step and `run` replays it:
181
+
182
+ ```json
183
+ { "index": 4, "action": "click", "selector": "…", "dialog": { "action": "dismiss" } }
184
+ ```
185
+
186
+ `action` is `accept` (OK) or `dismiss` (Cancel). For a `prompt()`, add
187
+ `"promptText": "…"` — without it, accepting submits the prompt's default value.
188
+ Every dialog the step opens gets the same answer.
189
+
150
190
  **Nothing recorded.** If you close the browser without doing anything, nothing
151
191
  is saved and the command says so.
152
192
 
@@ -208,6 +248,26 @@ Replays the recorded steps — headless, in the same browser kind the workflow w
208
248
  recorded in — then captures logs for the wall-clock window the replay occupied
209
249
  and writes `.verirun/runs/<name>-<timestamp>/logs.json`.
210
250
 
251
+ What it prints is a summary, verdict first:
252
+
253
+ ```text
254
+ FAIL checkout step 2 failed: click button "Pay now" (2/4 steps replayed, 2.3s)
255
+ locator.click: Timeout 2000ms exceeded.
256
+ waiting for locator('role=button[name="Pay now"i]')
257
+ selector: role=button[name="Pay now"i]
258
+ logs 90 entries, 34 errors, 6 warnings (Vercel 90)
259
+ x30 vercel/lambda error 500 Error: connect ECONNREFUSED 10.0.3.7:5432
260
+ x3 vercel/lambda error 500 TypeError: Cannot read properties of undefined (reading 'total')
261
+ x1 vercel/lambda error 500 Unhandled rejection: payment provider returned 402 for order ord_99812
262
+ full log: .verirun/runs/checkout-2026-11-13T13-10-02-468Z/logs.json
263
+ ```
264
+
265
+ The `logs` lines are the error-level entries (and any carrying an HTTP 5xx
266
+ status) from the run window. Lines that differ only by ids or numbers are
267
+ folded into one with a count, most frequent first, 8 groups at most. A `PASS`
268
+ with error lines under `logs` is the case this tool exists for: the screen said
269
+ done and the backend threw. Everything else is in `logs.json`.
270
+
211
271
  | Flag | Meaning |
212
272
  | --- | --- |
213
273
  | `--slow-mo <ms>` | Pause before each action. Defaults to **500 ms when `--headed`** so you can follow the run by eye, and to 0 otherwise. `--slow-mo 0` watches at full speed; a bigger number follows a fast form more easily. |
@@ -217,6 +277,25 @@ and writes `.verirun/runs/<name>-<timestamp>/logs.json`.
217
277
  | `--user-data-dir <dir>` | Replay in a persistent profile directory, so it runs already logged in. The profile must not be open in another browser. |
218
278
  | `--cdp <url>` | Attach to a browser already running with remote debugging and drive it. See below. |
219
279
  | `--user <id>` | Keep only log entries whose message or raw payload contains this string, case-insensitive — e.g. a user's email. Overrides `config.userFilter`. Recorded in `logs.json` as `"filter": { "user": … }`; `entries` and the per-source counts are post-filter. If everything is filtered out, `run` warns. |
280
+ | `--dialog <answer>` | How to answer a browser dialog opened by a step with no saved `dialog` answer: `accept` (OK, the default) or `dismiss` (Cancel). A step's own `dialog` always wins. |
281
+ | `--json` | Print the summary as one JSON object instead of text (below). `logs.json` is written either way. |
282
+ | `--verbose` | Also print each step as it replays, every distinct error line, and the full error text. |
283
+
284
+ `run --json` (`schemaVersion` 1) is the summary, not the entries:
285
+
286
+ ```jsonc
287
+ { "schemaVersion": 1, "tool": "verirun-run", "workflow": "checkout", "status": "ok" | "failed",
288
+ "durationMs": 2300, "window": { "startedAt": "<ISO>", "endedAt": "<ISO>" },
289
+ "steps": { "total": 4, "replayed": 2, "failedAt": 2 },
290
+ "failedStep": { "index": 2, "action": "click", "selector": "…", "description": "…" }, // on failure
291
+ "error": "<full message>", // on failure
292
+ "dialogs": 0,
293
+ "sources": [ { "id": "vercel", "name": "Vercel", "entries": 90 } ],
294
+ "logs": { "entries": 90, "problems": 34, "warnings": 6, "groupsOmitted": 0,
295
+ "groups": [ { "source": "vercel", "stream": "lambda", "level": "error", "status": 500,
296
+ "message": "<first line>", "count": 30, "firstAt": "<ISO>", "lastAt": "<ISO>" } ] },
297
+ "logsPath": ".verirun/runs/checkout-<timestamp>/logs.json" }
298
+ ```
220
299
 
221
300
  **Where the user filter runs.** For **Supabase** it goes into the log query's
222
301
  `WHERE` (matched against the rendered message and the JSON of `metadata`), so
@@ -263,6 +342,11 @@ frontmost.
263
342
  whatever logs already landed are still written, and the exit code is non-zero.
264
343
  - Popups and newly opened tabs become the active target as they open. A workflow
265
344
  that drives several tabs at once is not supported yet.
345
+ - Browser dialogs (`alert`, `confirm`, `prompt`, `beforeunload`) are answered
346
+ automatically: with the step's saved `dialog` if it has one, otherwise with
347
+ `--dialog` (default `accept`). Each one is printed under its step and listed
348
+ in `logs.json` `dialogs`. With `--cdp`, only tabs the run itself drives or
349
+ opens are touched.
266
350
 
267
351
  ### Sequencing
268
352
 
@@ -282,6 +366,9 @@ warned about and contributes no entries.
282
366
  "window": { "startedAt": "2026-08-31T13:01:50.998Z", "endedAt": "2026-08-31T13:01:51.499Z" },
283
367
  "steps": { "total": 12, "replayed": 12 },
284
368
  "filter": { "user": "ada@example.com" },
369
+ "dialogs": [
370
+ { "step": 7, "type": "confirm", "message": "Delete this item?", "action": "accept", "at": "2026-08-31T13:01:51.102Z" }
371
+ ],
285
372
  "sources": [{ "id": "vercel", "name": "Vercel", "entries": 8 }],
286
373
  "entries": [
287
374
  {
@@ -300,12 +387,100 @@ warned about and contributes no entries.
300
387
  (the step index) and the file carries a top-level `error` string.
301
388
  - `filter` is present only when `--user` / `config.userFilter` was in effect;
302
389
  `entries` and every `sources[].entries` count are then post-filter.
390
+ - `dialogs` lists every browser dialog the replay answered, in order: the
391
+ `step` running when it opened, its `type` and `message`, the prompt's
392
+ `defaultValue` (prompts only), the `action` taken, and the `promptText`
393
+ submitted to an accepted prompt. Empty when none opened.
303
394
  - `entries` merges every source, sorted by `timestamp`. `stream` is the
304
395
  sub-stream within a source (`lambda`, `edge`, `postgres_logs`, …). `raw` is
305
396
  the provider's untouched payload (for Supabase that now includes the row's
306
397
  `metadata`, so a `--user` filter can match a nested email field).
307
- - With no log sources configured, `run` still replays and warns; `entries` is
308
- empty.
398
+ - With no log sources configured, `run` still replays and says so in its
399
+ summary; `entries` is empty.
400
+ - The file's schema is unchanged by the compact summary: `run` prints less, it
401
+ does not store less.
402
+
403
+ ---
404
+
405
+ ## `verirun scan <url>`
406
+
407
+ ```bash
408
+ verirun scan https://example.com
409
+ verirun scan https://example.com --md # paste-ready Markdown
410
+ verirun scan https://example.com --json # the full report
411
+ ```
412
+
413
+ An outside-in scan of a live site. It needs no source code, no recording and
414
+ no log source: point it at a public URL. It crawls same-origin links breadth
415
+ first (20 pages, 2 hops from the start URL by default) in headless Chromium,
416
+ loading up to 4 pages at a time with a 500 ms pause before each request on
417
+ each connection. Pages listed in the site's sitemap (the `Sitemap:` lines in
418
+ `robots.txt`, else `/sitemap.xml`) join the crawl after the start page's own
419
+ links, so a site whose navigation is JavaScript rather than `<a href>` is still
420
+ covered. A request that gets no response at all (a reset connection, not a
421
+ timeout) is asked once more before it is reported. A `429` means the site is
422
+ rate-limiting the scanner, not that the URL is broken, so it is counted rather
423
+ than reported; a link served as a download is treated as a file. It reports,
424
+ per page:
425
+
426
+ - JavaScript exceptions and console errors
427
+ - failed requests and HTTP 4xx / 5xx responses
428
+ - dead internal links
429
+ - in-page `#anchor` and skip-link targets that do not exist
430
+ - a missing main landmark
431
+
432
+ Only the site's own problems are kept: its own origin, or an API it calls.
433
+ Analytics, ad, chat-widget and other third-party noise is dropped. The same
434
+ finding on several pages is reported once, with the pages and a count.
435
+
436
+ ```text
437
+ FAIL https://example.com/ 5 findings on 6 pages (browser, 9.4s)
438
+ JS exception TypeError: Cannot read properties of undefined (reading 'call')
439
+ on /
440
+ HTTP error 500 GET /api/data
441
+ on /
442
+ dead link /broken-page returned 404
443
+ on /
444
+ …
445
+ 14 internal links checked
446
+ never requested: 2 destructive-looking links, 1 URL disallowed by robots.txt
447
+ ```
448
+
449
+ **It is strictly read-only.**
450
+
451
+ - It only navigates. It sends `GET` and `HEAD`, never clicks a button, never
452
+ submits a form.
453
+ - It never requests a link that looks destructive: `logout`, `signout`,
454
+ `delete`, `remove`, `unsubscribe`, `cancel`, `deactivate`, `revoke` and
455
+ similar, in the URL or the link text.
456
+ - It identifies itself honestly:
457
+ `Mozilla/5.0 (compatible; VerirunScan/1.0; +https://get-verirun.duely.in/scanner)`.
458
+ That page explains the scanner and how to block it.
459
+ - It obeys `robots.txt` (the `VerirunScan` group, else `*`), including
460
+ `Crawl-delay`; a site that sets one gets one request at a time. If the start
461
+ URL is disallowed, the scan does not run.
462
+
463
+ Scan sites you own or have permission to scan.
464
+
465
+ | Flag | Meaning |
466
+ | --- | --- |
467
+ | `--max-pages <n>` | Stop after this many pages. Default 20. |
468
+ | `--depth <n>` | Follow same-origin links this many hops from the start URL. Default 2. |
469
+ | `--delay <ms>` | Pause before each page load, on each of the 4 parallel connections. Default 500; a larger `Crawl-delay` in robots.txt wins and also drops the scan to one connection. |
470
+ | `--timeout <ms>` | Per-page load timeout. Default 20000. |
471
+ | `--no-browser` | Plain HTTP instead of headless Chromium. Nothing is executed, so JS exceptions and console errors are not checked, and a page that renders entirely client-side shows little. The report says so. |
472
+ | `--json` | The report as one JSON object, `schemaVersion` 1: `status` (`clean` / `findings` / `error`), `summary`, `pages`, and `findings` (`kind`, `message`, `evidence`, `pages`, `count`). |
473
+ | `--md` | The report as Markdown, ready to paste into a reply or an issue. |
474
+ | `--verbose` | Every finding with its evidence, and every page scanned. |
475
+
476
+ Exit code `0` when nothing was found, `1` when there are findings, `2` when the
477
+ scan could not run (unreachable, disallowed by robots.txt, not HTML).
478
+
479
+ **What an outside scan cannot see.** It sees what a visitor's browser sees. It
480
+ does not know which backend call a button makes (`verirun map` reads that from
481
+ your source) and it cannot see your backend logs (`verirun run` captures those
482
+ for a recorded flow). It does not log in, so anything behind a login is out of
483
+ reach.
309
484
 
310
485
  ---
311
486
 
@@ -392,7 +567,7 @@ browser, no server, no recording.
392
567
 
393
568
  ```bash
394
569
  verirun map # per-page summary
395
- verirun map --json # the whole graph
570
+ verirun map --json # the whole graph, on one line (--verbose indents it)
396
571
  verirun map --report # graph + coverage.md into .verirun/map
397
572
  verirun map --bfs /account --depth 2 # what is reachable from one page
398
573
  verirun map --reach # how much of each page was captured
@@ -422,17 +597,34 @@ element that gets you there:
422
597
  $ verirun map --bfs /account --depth 2
423
598
  page:/account
424
599
  └─ nav "verirun" → page:/
425
- └─ effect "revoke" [if !(tokens.length === 0)] → effect:POST ?
600
+ └─ effect "revoke" [if window.confirm("Revoke this terminal's access? …")] → effect:POST ?
601
+ opens confirm "Revoke this terminal's access? …" — the answer decides what the click does
426
602
  └─ nav "Log in" → page:/login
427
603
  # frontier at depth 2: page:/login, page:/signup
428
604
  ```
429
605
 
606
+ **Browser dialogs.** An element whose handler calls the native `alert()`,
607
+ `confirm()` or `prompt()` (bare, or on `window` / `globalThis`; a locally
608
+ imported `confirm` is a custom modal, not a dialog) carries
609
+ `dialogs: [{ type, message, messageStatic, gates, … }]` in `--json`. `gates` is
610
+ true when the handler reads the answer, so OK and Cancel lead to different
611
+ outcomes. An early `if (!window.confirm(…)) return;` also shows up as the
612
+ `guardSource` of every backend call after it, which is how you see that Cancel
613
+ skips the `POST`. The per-page summary counts the elements that open a dialog,
614
+ `--bfs` prints each one under its edge, and `coverage.md` lists them all. A
615
+ workflow step that clicks one answers it with its `dialog` field (see
616
+ [`verirun run`](#verirun-run-name)); `workflow check` warns when that field is
617
+ missing (see below).
618
+
430
619
  **Limits, honestly.** It is static analysis: a target computed at runtime shows
431
620
  as `unresolved`, an endpoint passed through a variable as `?`, and Supabase
432
621
  `.auth.*` / `.storage.*` calls are not yet classified as effects. `--reach`
433
622
  tells you what fraction of each page it actually attached, so you can see when
434
623
  it is missing something rather than guessing.
435
624
 
625
+ `--bfs` and `--reach` also take `--json`, for the same result as one JSON
626
+ object (`schemaVersion` 1).
627
+
436
628
  ---
437
629
 
438
630
  ## `verirun workflow check <name>`
@@ -451,6 +643,15 @@ verirun workflow check login --json # machine-readable findings
451
643
  verirun workflow check ./some/file.json
452
644
  ```
453
645
 
646
+ If `verirun map --report` has saved `.verirun/map/interaction-tree.json`,
647
+ `check` also warns about a step that clicks an element the graph says opens a
648
+ browser dialog but has no `dialog` answer. Replay would press OK, which is
649
+ usually what you want, but when the handler reads the answer, Cancel is a
650
+ different path and should be chosen on purpose. Matching is by exact selector, so it catches
651
+ workflows written from `map --json`. A selector that elements *without* a dialog
652
+ also match (a bare `role=button`) is skipped rather than guessed at. Re-run
653
+ `map --report` when the app changes; the saved graph goes stale.
654
+
454
655
  Static only: it never opens a browser, so it cannot tell you whether a selector
455
656
  matches the live page. Only `verirun run` can. Non-zero exit if anything is an
456
657
  error.
@@ -477,7 +678,9 @@ an agent — to write `.verirun/workflows/<name>.json` directly:
477
678
  Actions and required `params`: `navigate` (`url`, absolute), `click` /
478
679
  `dblclick`, `fill` (`text`), `press` (`key`), `check` / `uncheck`, `select`
479
680
  (`values`), `setInputFiles` (`files`). Everything but `navigate` needs a
480
- `selector`, which is handed to `page.locator()` verbatim.
681
+ `selector`, which is handed to `page.locator()` verbatim. A step that opens a
682
+ browser dialog can carry `"dialog": { "action": "accept" | "dismiss",
683
+ "promptText"?: "…" }`; without it `run` accepts.
481
684
 
482
685
  A recorded workflow is ground truth; a written one is a guess until `run`
483
686
  confirms it. Don't overwrite a recording with a generated file — use a new name.
@@ -486,18 +689,30 @@ confirms it. Don't overwrite a recording with a generated file — use a new nam
486
689
 
487
690
  ## `verirun agent`
488
691
 
489
- Prints a single self-contained reference for an AI coding agent: every command
490
- and flag (read from the CLI itself, so it can't drift), the `.verirun/` layout,
491
- the `logs.json` schema, the recommended record-then-run loop, and a snapshot of
492
- **this** checkout — resolved repo root, configured log sources, recorded
493
- workflows, recent runs.
692
+ Prints a compact reference for an AI coding agent: what the tool does, the loop
693
+ to follow, how to read a result, the exit codes, the commands an agent drives
694
+ with their flags (read from the CLI itself, so they can't drift), and a
695
+ snapshot of **this** checkout — resolved repo root, configured log sources,
696
+ recorded workflows, recent runs.
697
+
698
+ The default is deliberately short, because an agent reads it on every task.
699
+ Three longer sections are printed only when asked for:
700
+
701
+ | `--topic` | What it covers |
702
+ | --- | --- |
703
+ | `authoring` | Writing a workflow file yourself from `map --json`: format, actions, selectors, dialogs. |
704
+ | `signed-in` | Testing a flow behind a login: the browser profile, Windows paths. |
705
+ | `schemas` | The full shapes of `run --json`, `logs.json` and the scan report. |
494
706
 
495
707
  ```bash
496
- verirun agent > .verirun/AGENT.md # save it as context for your agent
708
+ verirun agent # the short reference
709
+ verirun agent --topic authoring # one section
710
+ verirun agent --full > .verirun/AGENT.md # everything, saved as context for your agent
497
711
  verirun agent --json # machine-readable form
498
712
  verirun agent --skill --install # .claude/skills/verirun/SKILL.md
499
713
  verirun agent --skill --install cursor # .cursor/rules/verirun.mdc
500
- verirun agent --skill --install all # both
714
+ verirun agent --skill --install codex # .agents/skills/verirun/SKILL.md + an AGENTS.md section
715
+ verirun agent --skill --install all # all three
501
716
  ```
502
717
 
503
718
  `--skill` emits the same knowledge as an agent skill — frontmatter with a
@@ -509,14 +724,23 @@ looks:
509
724
  |---|---|---|
510
725
  | `claude` (default) | `.claude/skills/verirun/SKILL.md` | automatically, when a task looks like app testing |
511
726
  | `cursor` | `.cursor/rules/verirun.mdc` | when the task matches its description (an agent-requested rule, not always-on) |
512
- | `all` | both | |
727
+ | `codex` | `.agents/skills/verirun/SKILL.md`, plus a short section in `AGENTS.md` | the skill when the task matches its description; the `AGENTS.md` section (a pointer to the skill) on every request |
728
+ | `all` | all three | |
729
+
730
+ Codex reads skills from `.agents/skills/` between the working directory and the
731
+ repo root. `AGENTS.md` is shared with other tools and with people, so verirun only
732
+ writes between `<!-- verirun:start -->` and `<!-- verirun:end -->`: it creates the
733
+ file if there is none, appends the section if there is no marked one, and
734
+ replaces just that section on a re-run. Everything outside the markers is left as
735
+ it was.
513
736
 
514
737
  Re-run it after upgrading verirun; the file is generated, not hand-maintained.
515
738
 
516
739
  For any other agent, paste the output into `CLAUDE.md`, an `AGENTS.md`, or pipe it
517
740
  straight into a prompt. No account needed. The agent then knows to run
518
- `verirun run <name>`, read exit code + `.verirun/runs/<name>-<newest>/logs.json`,
519
- and act on `status` / `error` / `entries` without you explaining the tool.
741
+ `verirun run <name>`, branch on the exit code, and act on the `PASS` / `FAIL`
742
+ summary without you explaining the tool, opening `logs.json` only when it needs
743
+ an entry the summary does not show.
520
744
 
521
745
  ---
522
746
 
@@ -663,6 +887,67 @@ run this often.
663
887
 
664
888
  ## Changelog
665
889
 
890
+ ### 0.3.0
891
+
892
+ - **`verirun scan <url>`.** A read-only, outside-in scan of any live site: JS
893
+ exceptions, console errors, failed requests, HTTP 4xx / 5xx, dead internal
894
+ links, missing `#anchor` and skip-link targets, a missing main landmark. No
895
+ source and no logs needed. `--json`, `--md`, `--no-browser`. It never clicks
896
+ or submits, skips logout / delete style links, sends an honest user agent and
897
+ obeys `robots.txt`. Needs a connected account, like `record` and `run`.
898
+ It also crawls the pages a site's sitemap lists, so JavaScript-only
899
+ navigation is covered, and asks once more before reporting a request that
900
+ got no response, so one dropped connection does not fail the scan.
901
+ A rendered page bloated with inline base64 images no longer produces
902
+ missing-anchor findings past the read limit (data URIs are compacted, and a
903
+ page still too large skips its absence checks); `429`s and download links
904
+ are no longer reported as failures.
905
+ - **Compact output.** `run` now prints a verdict first, then the failed step
906
+ and the error-level log lines from the run window with repeats folded into a
907
+ count, instead of a step list and a pointer to a file. On the same run, what
908
+ an agent has to read to learn why it failed drops from the whole `logs.json`
909
+ to a few lines. `workflow check`, `whoami` and `record` lead with their
910
+ result too. `--verbose` brings the detail back.
911
+ - **`--json` everywhere it was missing**: `run`, `record`, `whoami`, `map --bfs`
912
+ and `map --reach`. Each is one object on one line with a `schemaVersion`.
913
+ `map --json` and `workflow check --json` are now one line too (`map
914
+ --verbose` indents); `workflow check --json` gained `schemaVersion`.
915
+ - **Exit codes mean something.** `0` ok, `1` the check failed, `2` could not
916
+ run (usage, setup, license, unreachable). Before, every failure was `1`.
917
+ `whoami` now exits `1` when the gated commands would not run.
918
+ - **`verirun agent --skill --install codex`.** Writes the skill to
919
+ `.agents/skills/verirun/SKILL.md`, where Codex looks for skills, and a short
920
+ marked section in `AGENTS.md` pointing at it, without touching the rest of
921
+ that file. `--install all` now covers Claude Code, Cursor and Codex.
922
+ - **`verirun agent` is shorter.** The default reference carries what an agent
923
+ needs on every task; the workflow-authoring, signed-in and schema sections
924
+ moved behind `--topic`, and `--full` prints everything.
925
+ - `logs.json` and workflow files are unchanged (both still `schemaVersion` 1).
926
+
927
+ ### 0.2.5
928
+
929
+ - **`verirun run` answers browser dialogs instead of cancelling them.** With no
930
+ handler, Playwright dismissed every `alert` / `confirm` / `prompt`, so each
931
+ `confirm()` in a workflow came back as Cancel and the action it guarded
932
+ quietly never happened. Replay now answers each one: with the step's saved
933
+ `dialog` (`accept` / `dismiss`, plus `promptText` for a prompt), otherwise
934
+ with the new `--dialog accept|dismiss` (default `accept`). Each dialog is
935
+ printed under its step and listed in `logs.json` under `dialogs`. With
936
+ `--cdp`, only the tabs the run drives are touched.
937
+ - **`verirun record` asks which button you pressed.** Playwright's recorder notes
938
+ that a step opened a dialog but not the answer, so after you close the
939
+ browser `record` asks `[O]K / [c]ancel` for each such step and saves it on the
940
+ step.
941
+ - **`verirun map` shows the dialogs an element opens.** `--json` carries a
942
+ `dialogs` list per element (`type`, `message`, and `gates` when the handler
943
+ reads the answer). An early `if (!window.confirm(…)) return;` now shows up as
944
+ the `guardSource` of the backend calls after it, so you can see that Cancel
945
+ skips them. The summary counts them, `--bfs` prints them under their edge,
946
+ and `coverage.md` lists them.
947
+ - **`verirun workflow check` validates the `dialog` field.** After `map
948
+ --report`, it also warns about a step that clicks a dialog-opening element
949
+ without saying OK or Cancel.
950
+
666
951
  ### 0.2.4
667
952
 
668
953
  - **`verirun map` reads the import aliases your project actually declares.**