Skip to content

tonyhschu/uisnap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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:

  1. 🔴 Load the full page HTML (~15K tokens)
  2. 🔴 Parse console logs (~8K tokens)
  3. 🔴 Analyze network requests (~5K tokens)
  4. 🔴 Repeat for every question

uisnap changes this:

  1. ✅ Capture once: ~2K tokens
  2. ✅ Query many times: ~200 tokens each
  3. 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 chromium

2. 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 --global

Once 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 snapshot
  • console.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 instances
  • utils.captureStep(page, name, action) - Auto-capture steps with numbering
  • utils.writeJson, writeJsonl, appendJsonl - Data writing helpers
  • fs, path - Node.js modules
  • args - 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.jsonl

Iterative 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/ --performance

Continuous 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('test@example.com');
});

// 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/example

Console 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.sh

Local 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.sh

This 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:

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors