uiscout
START

Install and first run

Requirements#

  • Node 24 or later (uiscout runs its TypeScript directly with Node during development).
  • pnpm 10.
  • A web app: uiscout starts its dev server (see init below), or walks one already running (a dev server, vite preview, staging…).

Install uiscout#

git clone https://github.com/nguyentrinhquy1411/uiscout.git ~/dev/uiscout
cd ~/dev/uiscout
pnpm install                          # installs dependencies and builds dist/
pnpm exec playwright install chromium # the browser that walks the app
pnpm test                             # optional: about 120 tests, ~1 min

Three ways to run it#

A. Call it by path (fastest, no change to the app)#

cd ~/dev/my-app
node ~/dev/uiscout/src/cli.ts check --url http://localhost:5173/

An alias saves typing (add it to ~/.zshrc or ~/.bashrc):

alias uiscout="node ~/dev/uiscout/src/cli.ts"

The rest of this guide writes uiscout ….

B. Install it in the app as a dev dependency (needed for rules, adapters and the plugin)#

Rule files (*.rules.ts), adapters (*.adapter.ts) and the Vite plugin import uiscout/rules, uiscout/adapter and uiscout/vite. For those imports to resolve, install uiscout in the app:

cd ~/dev/my-app
pnpm add -D link:../uiscout          # points at the checkout next to it
pnpm exec uiscout check --url http://localhost:5173/

With link:, run pnpm build in the uiscout checkout after changing its code, so dist/ is current.

C. Install from GitHub#

pnpm add -D github:nguyentrinhquy1411/uiscout

D. Let Claude Code do it#

With the uiscout skill installed, ask Claude Code to "set up uiscout and find UI bugs": it installs, runs init, and walks the app.

Set up an app: uiscout init#

In the app's directory (in a monorepo, the web app's package):

cd ~/dev/my-app
uiscout init

It looks at the project and writes uiscout.config.json:

It reads It writes
The framework (Vite, Next.js, Remix, SvelteKit, Astro, Angular, CRA, webpack) and its port, from the dev script (--port, -p) or vite.config url
The package manager, from the nearest lockfile up to the repository root, and the dev (or start) script webServer: uiscout starts the app itself, and stops it afterwards
A TanStack Router route tree or a Next.js app/ or pages/ directory "seeds": ["auto"]: every static route gets walked, linked or not
AI, payment and analytics hosts in the code (OpenAI, Anthropic, Groq, DeepSeek, Gemini, Stripe, Google Analytics, PostHog, Segment, /api/ai) block: the walk never sends those requests

It also adds .uiscout/ to .gitignore and four scripts to package.json (scout, scout:quick, scout:graph, scout:accept), keeping any that exist. It prints every guess, and never overwrites an existing config without --force.

First run#

pnpm run scout:quick     # uiscout check --quick: a first look in seconds
pnpm run scout           # the full walk
pnpm run scout:graph     # the map of the app, in the browser

--quick walks one action deep, without the axe checks or the clock fast-forward. While it runs, each screen logs where the walk stands:

  node /docs (depth 0) · 3/10 screens · 12/250 actions · 20s · ~12s left

The config is found from any directory below it, up to the repository root, so uiscout check works from src/ too. Paths in it are read from its own directory.

Without init, point uiscout at a running app: uiscout check --url http://localhost:5173/.

Behind a sign-in#

Add the sign-in once to the config; uiscout runs it in a fresh browser before the walk, and every screen starts signed in:

"auth": {
  "steps": [
    { "goto": "/login" },
    { "fill": "Email", "text": "${SCOUT_EMAIL}" },
    { "fill": "Password", "text": "${SCOUT_PASSWORD}" },
    { "click": "Sign in" }
  ],
  "waitFor": "/"
}

${NAME} reads an environment variable, so the password stays out of the file. Details: Configuration.

What you get#

Output Where
Short report Printed, and saved to .uiscout/report.txt
Map of the app .uiscout/graph.html (opened by --open)
Raw data .uiscout/graph.json, findings.json, snapshots.json
A screenshot of each screen .uiscout/screens/*.jpg
Pull request comment .uiscout/report.md

.uiscout/ is the output of each run, not something to commit (init adds it to .gitignore).

Edit this page on GitHub