uiscout init reads the framework, port, routes and paid APIs to blockPoint it at your app. It walks every screen.
uiscout starts your app, signs in once, clicks every safe control, types into every field and follows every screen it can reach, then judges each step with deterministic checks. You get a map of the app and a list of real defects, each with the path that shows it. No spec to write, no model, the same result on every run.
--quick on a signed-in app: 26 screens from 12 routes, dev server start includedWhat it catches
The defects that slip past unit tests and nobody clicks through before a release: found by walking the app the way a curious user would, after every single step.
An exception on a menu nobody opens in tests, a library warning that means a broken component, an unhandled promise.
A 500 behind a button, a 404 from a stale link, a call that fails only from one screen.
A button under an invisible overlay, a link that goes nowhere, a click that can't land.
Overlapping controls, clipped labels, buttons with no accessible name.
A control that disappeared, a renamed action, a link that now leads somewhere else, all as a reviewable git diff.
"An empty cart can't be ordered", "a guest never reaches checkout": your rules, checked on every path walked.
From setup to a fix
Built to fit the loop you already have: one command to set up, a sign-in done once, and every finding turned into something you can act on.
uiscout init writes the config: how to start the app, its routes from TanStack Router or Next.js, and the AI, payment and analytics hosts the walk must never call. Works from any folder of a monorepo.
auth steps run in a fresh browser before the walk; every screen starts from that session. Passwords come from ${ENV} variables and never reach a report.
A one-line repro (open / → click "Cart" → click "Pay") and the control's source file and line. uiscout export 1 writes it as a Playwright test.
Fields get values they accept: emails, numbers within min and max, dates, values matching pattern. The same text every run.
check --watch reruns on save and, with a baseline, walks only the screens the changed files build.
A comment with the verdict and the graph diff, and uiscout site to host the graph for each pull request.
Three kinds of check, one walk
Every step the walker takes is judged three ways. The first needs nothing from you. The other two grow with the project.
Is the app broken?
Generic checks that hold for every web app.
scriptuncaught exceptions,console.errornetwork5xx, unexpected 4xx, failed callsdead-controla click that can't landlayoutoverlaps, clipped texta11yserious axe rules
Does it do the right thing?
Business rules in TypeScript, checked on every path. Product people write the sentences in intent files; the report shows which are covered.
alwaysan invariant in every statewhen().then()an implicationeventually().within()a deadline, on the app's clock
Is it different from yesterday?
An accepted run is committed as a lockfile. Every later run is compared to it, and git diff uiscout/ is the review.
- errora control gone, renamed, or leading elsewhere
- warninga control moved more than 16 px
- infoa new control
How fast it is
About 0.4–2 s per step: each step waits until the network is idle and the page has been still for 250 ms, never for a fixed timeout. Screens run four at a time, so a run takes about as long as its busiest screen.
| Run | What it walks | Time |
|---|---|---|
check --quick, signed in, seeds: ["auto"] | 26 screens, 82 steps, dev server start included | ~45 s |
check --depth 1 --no-a11y --fast-forward 0 | 11 screens, 186 steps | ~1 min |
check (depth 1, all checks) | 11 screens | ~2 min |
check --depth 2 | 31 screens, 431 steps | ~4 min |
check --affected origin/main | 5 of 10 screens, from a one-file change | 25 s (vs 42 s) |
check --depth 3, no config | a 10-route sample app, 19 clicks | 7 s |
- Deterministic. A fixed clock and recorded API responses give the same graph on every run of the same commit: a baseline reran with no diff at 31 screens and 352 edges.
- No backend in CI.
--mode replayanswers every API call from recordings, and fails closed: nothing reaches a real server. - No model, no API key. Every judgement is a deterministic check; AI agents can read the graph over MCP, but never decide a result.
Start in a minute
Any web app: uiscout starts its dev server, or walks one already running (a preview build, staging).
pnpm add -D github:nguyentrinhquy1411/uiscout pnpm exec playwright install chromium pnpm exec uiscout init # config, scripts, .gitignore pnpm run scout:quick # the first report
Using Claude Code? Install the skill and ask it to find and fix the bugs: /plugin marketplace add nguyentrinhquy1411/uiscout, then /plugin install uiscout@uiscout. More.
Then commit a baseline with uiscout check --update, and add the GitHub Actions job so every pull request gets a report comment.
Where it fits
Hand-written end-to-end tests check the paths someone thought of. Static graph tools read routes from code and stop at routers they don't support. uiscout watches what the running app actually does.
| uiscout | Playwright specs | uigraph | |
|---|---|---|---|
| Builds the graph by | Walking the running app | — | Static analysis, then verify |
| Works without a router adapter | Yes | Yes | No (0 screens on TanStack Router) |
| Finds defects with no test written | Yes | No | No |
| Catches regressions | Baseline lockfile | Per assertion | Graph diff |
| Drag, resize, canvas widgets | Adapters with invariants | Hand-written | — |
| Coding agents | MCP, quarantined proposals | — | MCP, quarantined proposals |
| Ranks gaps by real traffic | Yes | No | No |
Also compared with session replay (Meticulous), AI test writers, browser agents and Crawljax: How it compares.
The guide
In the order you'll need it: install, first run, reading the results, then the deeper checks.