Token-efficient browser verification for AI coding agents.
A persistent headless Chromium CLI. ~75 tokens per call vs ~1,500 with Playwright MCP. Same ARIA snapshot technology, 95% cheaper.
Works with Claude Code, Codex CLI, Cursor, Copilot, and Gemini CLI.
Inspired by gstack by Garry Tan.
Install globally:
npm install -g verdict-cliOr add to your project:
npm install verdict-cliChromium installs automatically via Playwright.
Requirements: Node.js 18+
verdict goto https://example.com
verdict snapshot -i
verdict click @e3
verdict fill @e5 "hello"
verdict snapshot -D
verdict stopThe server auto-starts on first call (~3s). Subsequent calls take ~100-200ms.
| Tool | Per call | 20 calls | Savings |
|---|---|---|---|
| Playwright MCP | ~1,500 tokens | ~30,000 tokens | — |
| Verdict | ~75 tokens | ~1,500 tokens | 95% |
verdict goto https://example.com
verdict back
verdict forward
verdict reload
verdict url
verdict titleTake an ARIA snapshot. Interactive elements get @e refs.
verdict snapshot # full ARIA tree
verdict snapshot -i # interactive elements only
verdict snapshot -D # diff against previous snapshot
verdict snapshot -a # annotated screenshot with ref labels
verdict snapshot -C # include cursor-clickable @c refsOutput looks like this:
@e1 - link "Home"
@e2 - button "Search"
@e3 - textbox "Email"
Use refs in any command: click @e2, fill @e3 "test".
verdict click @e3
verdict fill @e5 "user@test.com"
verdict select @e7 "Option A"
verdict hover @e2
verdict type @e5 "slow typing"
verdict press Enter
verdict scroll @e10
verdict wait 2000Verify an action changed the page:
verdict snapshot -i
verdict click @e2
verdict snapshot -D--- previous
+++ current
- @e5 - button "Submit"
+ @e5 - button "Loading..."
+ @e12 - text "Form submitted successfully"Read any computed CSS value:
verdict css @e3 padding
verdict css @e3 font-size
verdict css @e3 background-colorGet a full box model with 16 computed styles:
verdict inspect @e3Modify CSS live with undo support:
verdict style @e3 color red
verdict style @e3 padding 20px
verdict style --history
verdict style --undoScreenshot at mobile, tablet, and desktop in one command:
verdict responsive /tmpverdict screenshot /tmp/page.png
verdict screenshot /tmp/full.png --full
verdict viewport 375x812verdict js "document.title"
verdict console
verdict network
verdict perfSave and reload authenticated sessions. Encrypted with AES-256-CBC.
verdict goto https://app.com/login
verdict handoff # open visible Chrome
# ... log in manually (SSO, MFA, CAPTCHA) ...
verdict resume # back to headless
verdict auth-save myapp # save session encryptedReload in one command:
verdict goto-auth https://app.com/dashboard --profile myappManage profiles:
verdict auth-list
verdict auth-delete myappAuth profiles are Verdict's own encrypted store, for sessions you logged into by hand.
storageState is the interchange path: load auth you already have from Playwright.
# A file Playwright wrote, via context.storageState({ path }) or
# npx playwright codegen --save-storage=state.json
verdict storage-state-load ./state.json
# Or as a global flag, applied before the command runs
verdict --storage-state ./state.json goto https://app.com/dashboardThe flag composes with --session, and is re-applied if the server had to restart.
Three things to know:
- Loading replaces all cookies rather than merging into them.
- Seeding
localStoragemeans navigating to each origin in the file, so the load needs network and leaves the page on the last origin. Issue yourgotoafter it, not before. verdict cookieslists cookies for the current page's URL, so straight after a load it reportsNo cookies.until you navigate. The cookies are there; the listing is scoped.
The file holds cookies in plaintext. Auth profiles are encrypted; this is not.
verdict tabs
verdict newtab https://example.com
verdict tab 1
verdict closetab
verdict frame iframe#content
verdict frame-exitRun multiple commands in one call:
verdict chain '[["goto","https://example.com"],["snapshot","-i"],["console"]]'Compare two pages:
verdict diff https://example.com https://example.com/aboutverdict cookies
verdict cookie-set session abc123 example.com
verdict cookie-import example.comverdict status
verdict stopAdd to .claude/settings.json:
{
"permissions": {
"allow": [
"Bash(verdict*)",
"Bash(npx verdict*)"
]
}
}Create .claude/rules/verdict.md:
Use Verdict for all browser verification:
verdict goto <url>
verdict snapshot -i
verdict click @e3
Fall back to Playwright MCP for drag-and-drop only.Verdict works with any agent that can run Bash commands. Install globally and call verdict directly.
| Variable | Default | Description |
|---|---|---|
VERDICT_DATA_DIR |
.verdict-data/ |
Data directory for state and profiles |
VERDICT_IDLE_TIMEOUT |
1800000 (30 min) |
Server idle shutdown in ms |
VERDICT_PORT |
Random | Fixed port for the server |
AI Agent → Bash → CLI client (bin/browse.mjs)
↓ HTTP POST (localhost)
Server (src/server.mjs)
↓ Playwright API
Chromium (headless)
The CLI client is a thin HTTP wrapper (~1ms overhead). The server is a persistent Node.js daemon with random port, bearer token, and localhost-only binding. Auth profiles use AES-256-CBC with a scrypt-derived machine-specific key.
| Feature | Verdict | Playwright MCP | agent-browser |
|---|---|---|---|
| Token cost/call | ~75 | ~1,500 | ~similar |
| CSS inspection | Yes | No | No |
| Style mutation + undo | Yes | No | No |
| Responsive presets | Yes | No | No |
| Snapshot diff | Yes | No | Yes |
| Auth profiles (AES-256) | Yes | No | Yes |
| Handoff/resume | Yes | No | No |
| Batch commands | Yes | No | No |
| Persistent daemon | Yes | Per-session | Yes |
Fork the repo and create a pull request. Bug reports and feature requests welcome on GitHub Issues.