uisnap
v0.2.3
Published
CLI debugging tool designed for AI coding agents. Captures browser state to disk for token-efficient analysis. Works with Claude Code, Cursor, and other AI assistants.
Maintainers
Readme
uisnap
CLI debugging tool designed for AI coding agents
Stop having your AI assistant repeatedly reload pages and parse megabytes of DOM. Capture browser state once, query efficiently many times.
Why uisnap?
When AI agents debug frontend issues, they typically:
- 🔴 Load the full page HTML (~15K tokens)
- 🔴 Parse console logs (~8K tokens)
- 🔴 Analyze network requests (~5K tokens)
- 🔴 Repeat for every question
uisnap changes this:
- ✅ Capture once: ~2K tokens
- ✅ Query many times: ~200 tokens each
- ✅ 17x token reduction
Philosophy
Capture once, query many - Expensive browser operations write to disk once. Cheap local queries analyze data repeatedly without re-running the browser.
Built for: Claude Code, Cursor, Aider, and other AI coding assistants.
Installation
Requirements
- Node.js 18, 20, or 22 LTS (recommended)
- Node.js 24 may work but is untested
- Node.js 25+ is not supported (duckdb lacks pre-built binaries)
1. Install the CLI tool
npm install -g uisnap
npx playwright install chromium2. Install Claude Code Skill (Recommended for Claude Code users)
Teach Claude when and how to use uisnap automatically:
# Project-level (recommended - commit to git for team sharing):
uisnap setup-skill
git add .claude/skills/
# Or user-level (personal use only):
uisnap setup-skill --globalOnce installed, Claude Code will automatically know:
- ✅ When to capture browser state vs. parse HTML
- ✅ How to use each analysis command
- ✅ Best practices for token-efficient debugging
Without the skill: You'll need to manually tell Claude which commands to use.
With the skill: Just say "debug console errors on example.com" and Claude handles the rest.
See SKILL-INSTALLATION.md for details.
Quick Start
# Capture page state (auto-generates timestamped directory)
uisnap snapshot https://example.com
# Analyze the latest snapshot using the "latest" symlink
uisnap analyze-console snapshots/example.com/latest/console.jsonl
uisnap analyze-network snapshots/example.com/latest/network.jsonl
uisnap query-a11y snapshots/example.com/latest/a11y.yaml --links
# Or specify explicit path for baselines/comparisons
uisnap snapshot https://example.com snapshots/baseline/Commands
Capture Commands
Snapshot - Capture full page state:
uisnap snapshot <url> [output-dir]If output-dir is omitted, auto-generates: snapshots/{hostname}/{timestamp}/
Chrome Performance Trace - Capture browser performance metrics:
uisnap exec chrome-perf-trace.js <url> [output-dir]Captures Chrome DevTools performance trace with automatic DuckDB import. Analyzes paint, layout, JavaScript execution, and other browser internals.
Auto-generated structure:
snapshots/
├── example.com/
│ ├── 2025-12-25-143052/
│ ├── 2025-12-25-144315/
│ └── latest -> 2025-12-25-144315/ (symlink to most recent)
└── myapp.com/
└── ...Each snapshot captures:
a11y.yaml- ARIA accessibility snapshotconsole.jsonl- Console messages (one JSON per line)network.jsonl- Network requests (one JSON per line)metadata.json- Page metadata (title, URL, viewport, timestamp)
Exec - Execute custom scripts for multi-step flows or custom data extraction:
uisnap exec <script.js> <url> [output-dir]If output-dir is omitted, auto-generates: snapshots/{hostname}/{script-name}-{timestamp}/
Scripts have access to:
page, context, browser- Playwright instancesutils.captureStep(page, name, action)- Auto-capture steps with numberingutils.writeJson, writeJsonl, appendJsonl- Data writing helpersfs, path- Node.js modulesargs- Remaining command-line arguments
Analysis Commands
Analyze Console:
uisnap analyze-console <console.jsonl>Provides:
- Message counts by type (error, warning, log)
- Grouped errors with counts and locations
- Grouped warnings with counts and locations
Analyze Network:
uisnap analyze-network <network.jsonl>Provides:
- Request counts by resource type, method, status
- Failed requests (4xx, 5xx)
- Slow requests (>1s)
- Requests by domain
Query A11y:
uisnap query-a11y <a11y.yaml> <query>Queries:
--buttons- List all buttons--links- List all links--headings- List all headings--inputs- List all input fields--interactive- List all interactive elements--images- List all images<text>- Search by text content
Analyze Chrome Trace:
uisnap trace-analyze <trace.db> [options]Options:
--long-tasks- Show tasks over threshold (default: 50ms)--js-time- Show JavaScript execution time breakdown--layout-paint- Show layout and paint performance--threshold=N- Set threshold in ms (default: 50)--full- Show all details
Progressive disclosure: Default shows summary with hints for next steps.
Import Chrome Trace (manual import if needed):
uisnap trace-import <trace.json> <output.db>Imports Chrome DevTools trace JSON into queryable DuckDB database. Usually not needed since chrome-perf-trace.js auto-imports.
Example Workflow
Basic Debugging
# 1. Capture page state (creates timestamped snapshot)
uisnap snapshot https://myapp.com
# 2. Check for errors (use "latest" for convenience)
uisnap analyze-console snapshots/myapp.com/latest/console.jsonl
# 3. Check network failures
uisnap analyze-network snapshots/myapp.com/latest/network.jsonl
# 4. Find the submit button
uisnap query-a11y snapshots/myapp.com/latest/a11y.yaml "Submit"
# 5. Make fixes, capture again - never overwrites!
uisnap snapshot https://myapp.com
# 6. Analyze latest results
uisnap analyze-console snapshots/myapp.com/latest/console.jsonlIterative Debugging
The auto-timestamped structure is perfect for iterative debugging:
# First attempt
uisnap snapshot https://myapp.com
# Creates: snapshots/myapp.com/2025-12-25-143052/
# Make code changes...
# Second attempt
uisnap snapshot https://myapp.com
# Creates: snapshots/myapp.com/2025-12-25-144315/
# "latest" always points to most recent
uisnap query-a11y snapshots/myapp.com/latest/a11y.yaml --buttons
# View history
ls snapshots/myapp.com/
# 2025-12-25-143052/
# 2025-12-25-144315/
# latest -> 2025-12-25-144315/Regression Testing
# Before deployment (use explicit path for baseline)
uisnap snapshot https://myapp.com snapshots/baseline/
# After deployment (auto-generated)
uisnap snapshot https://myapp.com
# Compare baseline vs latest (TODO: implement compare command)
# compare snapshots/baseline/ snapshots/myapp.com/latest/Performance Analysis
Initial page load:
# Snapshot captures everything including Chrome trace
uisnap snapshot https://myapp.com
uisnap diagnose snapshots/myapp.com/latest/ --performanceContinuous tracing (scroll, animations, interactions):
# Use the scroll-perf example or write your own
uisnap exec examples/scroll-perf-trace.js https://myapp.com
# 2. Start with summary
uisnap trace-analyze snapshots/myapp.com/latest/chrome-trace.db
# Shows: Total tasks, duration, hints for next steps
# 3. Identify long blocking tasks
uisnap trace-analyze snapshots/myapp.com/latest/chrome-trace.db --long-tasks
# Shows: Tasks over 50ms with timestamps
# 4. Analyze JavaScript execution
uisnap trace-analyze snapshots/myapp.com/latest/chrome-trace.db --js-time
# Shows: JS execution breakdown by type (total, avg, max)
# 5. Check layout/paint performance
uisnap trace-analyze snapshots/myapp.com/latest/chrome-trace.db --layout-paint
# Shows: Layout and paint operation counts and timings
# 6. See everything at once
uisnap trace-analyze snapshots/myapp.com/latest/chrome-trace.db --full
# 7. Custom queries with DuckDB (advanced)
duckdb snapshots/myapp.com/latest/chrome-trace.db
> SELECT name, dur_ms FROM tasks WHERE name LIKE '%Paint%' ORDER BY dur_ms DESC LIMIT 10;Custom Scripts
Multi-Step Flows
Use utils.captureStep() for multi-step interactions with automatic capture:
// login-flow.js
// Usage: uisnap exec login-flow.js https://myapp.com
// Step 1: Navigate
await utils.captureStep(page, 'goto-login', async () => {
await page.goto('https://myapp.com/login');
});
// Step 2: Fill email (using Playwright's role-based selectors)
await utils.captureStep(page, 'fill-email', async () => {
await page.getByLabel('Email').fill('[email protected]');
});
// Step 3: Submit
await utils.captureStep(page, 'click-submit', async () => {
await page.getByRole('button', { name: 'Submit' }).click();
});Creates:
snapshots/myapp.com/login-flow-2025-12-25-143052/
├── step-1-goto-login/
│ ├── a11y.yaml
│ └── metadata.json
├── step-2-fill-email/
│ └── ...
└── step-3-click-submit/
└── ...Simple Data Extraction
For custom data extraction without step capture:
// extract-data.js
await page.goto('https://example.com');
const data = await page.evaluate(() => {
return {
links: Array.from(document.links).map(l => l.href),
headings: Array.from(document.querySelectorAll('h1')).map(h => h.textContent),
};
});
utils.writeJson('data.json', data);Output Formats
ARIA Snapshot (YAML)
- heading "Example Domain" [level=1]
- paragraph: This domain is for use in examples.
- link "Learn more":
- /url: https://iana.org/domains/exampleConsole Logs (JSONL)
{"timestamp":"2025-12-25T10:30:00.000Z","type":"error","text":"Failed to load","location":{"url":"https://example.com/app.js","lineNumber":42}}
{"timestamp":"2025-12-25T10:30:01.000Z","type":"log","text":"User clicked submit","location":{"url":"https://example.com/app.js","lineNumber":100}}Network Requests (JSONL)
{"url":"https://api.example.com/users","status":200,"statusText":"OK","method":"GET","resourceType":"fetch","timing":{"startTime":1234.5,"responseEnd":1456.7}}
{"url":"https://api.example.com/submit","status":500,"statusText":"Internal Server Error","method":"POST","resourceType":"fetch"}Token Efficiency
Traditional approach:
- Full DOM: ~15,000 tokens
- Console logs: ~8,000 tokens
- Screenshots: ~10,000 tokens
- Total: ~40,000 tokens
This toolkit:
- Capture to disk: ~2,000 tokens (one-time)
- Analyze summaries: ~100 tokens
- Query results: ~200 tokens
- Total: ~2,350 tokens (~17x reduction)
Chrome performance traces:
- Raw 36MB trace JSON: ~9,000,000 tokens
- DuckDB import: ~100 tokens (one-time)
- Canned query results: ~100-500 tokens
- Total: ~600 tokens (~18,000x reduction)
Development
# Clone and install
git clone https://github.com/tonyhschu/uisnap
cd uisnap
npm install
# Build
npm run build
# Test locally
node dist/pw.js snapshot https://example.com test-output/
# Deploy skill to local Claude Code
./deploy-skill.shLocal Deployment Script
The deploy-skill.sh script copies the skill to ~/.claude/skills/ making it available to all Claude Code instances on your machine:
./deploy-skill.shThis removes any existing version and installs the latest skill instructions.
License
MIT
Contributing
See .claude/skills/uisnap/SKILL.md for guidance on working with this codebase in Claude Code.
For detailed reference:
- Snapshot debugging: snapshot-capture-and-analysis.md
- Performance debugging: trace-capture-and-analysis.md
