uisnap - UI state snapshot for coding agents
Pulling in the full DOM and console logs is not a good use of tokens. Capture the browser state once, then let your agent write queries against it.
npm install -g uisnap
npx playwright install chromium
uisnap setup-skill # optional: installs Claude Code skill
Requires Node.js 18, 20, or 22 LTS
The package offers two sets of commands: capture commands and analyze commands.
Capture Commands
-
uisnap snapshot https://example.dev [options] Capture full page state: accessibility tree, console logs, network requests, web vitals, Chrome performance trace
Options: --console-filter, -cf <pattern> Filter console logs by regex --wait-for-console, -wc <pattern> Wait for message before capture Examples: uisnap snapshot https://example.com uisnap snapshot https://myapp.com -wc "Data loaded" uisnap snapshot https://myapp.com -cf "MyApp" -wc "Ready" 📂 Output directory: .uisnap/example.dev/2026-01-02-14-30-45/ Navigating to https://example.dev... Page loaded, capturing state... ✓ Snapshot saved to .uisnap/example.dev/2026-01-02-14-30-45/ - Accessibility tree: 2847 bytes (YAML) - Console messages: 3 - Network requests: 12 - Page title: "uisnap - AI-First Frontend Debugging" - Web vitals: FCP=234ms, LCP=456ms, TTI=890ms, CLS=0.01 - Chrome trace: chrome-trace.json + chrome-trace.db -
uisnap exec script.js https://example.dev Execute a custom Playwright script. Useful for multi-step flows and performance traces.
📂 Output directory: .uisnap/example.dev/login-flow-2026-01-02/ Executing script: examples/login-flow.js ✓ Step 1: goto-login ✓ Step 2: fill-email ✓ Step 3: click-submit Script completed! -
uisnap exec --template my-script.js Generate a starter script template with examples
✓ Created template: my-script.js Run it with: uisnap exec my-script.js https://example.com
Sample Script
// my-script.js - Test a login flow
await page.goto(args[0] || 'https://myapp.com/login');
await utils.captureStep(page, 'initial', async () => {
// State captured after page loads
});
await utils.captureStep(page, 'fill-form', async () => {
await page.fill('input[name="email"]', '[email protected]');
await page.fill('input[name="password"]', 'password123');
});
await utils.captureStep(page, 'submit', async () => {
await page.click('button[type="submit"]');
await page.waitForURL('**/dashboard');
});
Captured data is stored in timestamped snapshot directories for later analysis.
Analyze Commands
-
uisnap diagnose .uisnap/example.dev/latest/ High-level overview: web vitals, console errors, network failures, performance issues
=== Web Vitals === FCP: 234ms ✓ LCP: 456ms ✓ TTI: 890ms ✓ CLS: 0.01 ✓ Console: 1 errors, 2 warnings Network: 12 requests, 1 failed, 0 slow (>1s) Performance: 3 long tasks (>50ms), 456ms total JS time For details, use: --errors Show 1 console error(s) --network Show network details (1 failed, 0 slow) --performance Show performance breakdown --full Show all details -
uisnap analyze-console .uisnap/example.dev/latest/console.jsonl Analyze console logs with grouping by error type
=== Console Summary === Total messages: 3 - error: 1 - warning: 1 - log: 1 === Errors (1) === Failed to load resource: net::ERR_BLOCKED_BY_CLIENT Locations: https://example.com/tracker.js:1 -
uisnap analyze-network .uisnap/example.dev/latest/network.jsonl Analyze network requests by status, timing, and type
=== Network Summary === Total requests: 12 By status: 200 (11), 404 (1) By type: document (1), script (4), stylesheet (2), fetch (5) === Failed Requests (1) === 404 GET https://uisnap.dev/missing.js -
uisnap query-a11y .uisnap/example.dev/latest/a11y.yaml "Submit" Query accessibility tree by role or text content
Found 1 match: - button "Submit" -
uisnap trace-analyze .uisnap/example.dev/latest/chrome-trace.db Analyze browser performance: long tasks, JS execution, layout/paint
=== Trace Summary === Total tasks: 1847 Total duration: 2341ms Long tasks (>50ms): 3 === Long Tasks === EvaluateScript - 156ms at 1234ms Layout - 89ms at 2100ms Paint - 67ms at 2200ms Run with --js-time or --layout-paint for details
Why?
Context efficiency, basically. I got tired of copying from the console. Tried Playwright MCP, which worked great, but super wasteful on tokens. Why read the whole thing when the agent can grep/jq or write SQL?
Manual
User: There was an error.
Agent: What's the stack trace?
User: [copies stack trace from browser]
Agent: Can you check if analytics.js loaded?
User: [checks Network tab, reports back]
Agent: What's the order of script tags?
User: [inspects DOM, copies HTML]
→ User is stuck in the loop, manually gathering data.
Playwright MCP
User: "Fix the console errors on myapp.com"
Agent: Let me fetch the page...
→ Loads full DOM (15,000 tokens)
→ Parses all console logs (8,000 tokens)
→ Analyzes network tab (5,000 tokens)
... that's a lot of tokens
With uisnap
User: "Fix the console errors on myapp.com"
Agent: Capturing page state...
$ uisnap snapshot https://myapp.com
$ uisnap analyze-console .uisnap/myapp.com/latest/console.jsonl
→ Agent sees: ERR_BLOCKED_BY_CLIENT in tracker.js
Agent: Let me check the script load order...
$ grep -A5 "tracker.js" .uisnap/myapp.com/latest/network.jsonl
Agent: And find the analytics script tag...
$ grep "analytics" .uisnap/myapp.com/latest/a11y.yaml
Agent: Found the issue. Script order is wrong.
[Makes edit to index.html]
Agent: Verifying the fix...
$ uisnap snapshot https://myapp.com
$ uisnap analyze-console .uisnap/myapp.com/latest/console.jsonl
→ Agent used grep/jq/CLI tools autonomously
→ Each query: ~200 tokens (local files)
→ Total: ~2,000 tokens
Claude Code Skill
Install the skill to teach Claude Code when and how to use uisnap automatically.
Installation
uisnap setup-skill # project-level (recommended)
uisnap setup-skill --global # user-level
Activation
Once installed, Claude automatically uses uisnap when you mention:
- "Debug console errors on https://myapp.com"
- "Help me test the login flow"
- "Why is the submit button not working?"
- "Page is slow/laggy"
- "Check network requests on this page"
Decision Tree
Functional issues? → Snapshot workflow
Button doesn't work, form fails, console errors, API 404s
Performance issues? → Trace workflow
Page slow, scrolling janky, animations stutter, UI freezes
Not sure? → Start with snapshot, escalate to trace if needed
Key Features
- 17x token reduction - Capture once, query many times
- Auto-organized snapshots - Timestamped directories, never overwrites
- Multi-step flows - JavaScript with captureStep helper
- Local analysis - Fast grep/SQL queries on captured state
Script API Reference
Scripts run in an async context with access to Playwright and these helpers:
Playwright Objects
page- Playwright Page instance (docs)context- Browser context for CDP access, cookies, etc.browser- Browser instanceargs- Remaining CLI arguments after URLfs,path- Node.js modules
Capture Helpers (utils.*)
-
captureStep(page, name, action?)Capture accessibility tree + metadata after action. Createsstep-N-{name}/directory. -
startChromeTrace(cdp)Start Chrome DevTools performance trace. Use withcontext.newCDPSession(page). -
stopChromeTraceAndImport(cdp, path)Stop trace and import to DuckDB. Returns.dbpath for SQL queries.
File Helpers (utils.*)
-
writeJson(filepath, data)Write JSON with formatting -
writeJsonl(filepath, items)Write array as newline-delimited JSON -
appendJsonl(filepath, item)Append single item to JSONL file -
baseOutputDirAuto-generated output directory path