---
slug: "dev-browser-claude-skill"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/wrsmith108/dev-browser-claude-skill@main/README.md"
repo: "https://github.com/wrsmith108/dev-browser-claude-skill"
source_file: "README.md"
branch: "main"
---
# Dev Browser Skill for Claude Code

> Browser automation with **persistent page state**. Write small, focused scripts that build on each other.

## Credits & Attribution

This skill is a fork of [**SawyerHood/dev-browser**](https://github.com/SawyerHood/dev-browser) by [Sawyer Hood](https://github.com/SawyerHood).

The original project provides the core architecture for persistent browser automation with Claude Code. This fork adds project-specific customizations and additional documentation.

## Why This Skill?

Unlike one-shot browser automation:
- **Pages persist** between script executions
- **State is preserved** — cookies, localStorage, DOM changes survive
- **Incremental workflows** — write small scripts, observe results, iterate

## Installation

### Option A: Claude Plugin (Recommended)

```bash
claude plugin add github:wrsmith108/dev-browser-claude-skill
```

### Option B: Manual

```bash
git clone https://github.com/wrsmith108/dev-browser-claude-skill ~/.claude/skills/dev-browser
cd ~/.claude/skills/dev-browser
bun install
```

## Quick Start

### 1. Start the Server

```bash
cd ~/.claude/skills/dev-browser
./server.sh &
# Wait for "Ready" message
```

Use `--headless` for headless mode (no visible browser window).

### 2. Write Scripts

```bash
cd ~/.claude/skills/dev-browser && bun x tsx <<'EOF'
import { connect, waitForPageLoad } from "@/client.js";

const client = await connect("http://localhost:9222");
const page = await client.page("main");

await page.goto("https://example.com");
await waitForPageLoad(page);

console.log({ title: await page.title(), url: page.url() });
await client.disconnect();
EOF
```

## Core Concepts

### Persistent Pages

Pages are named and persist on the server:

```typescript
const page = await client.page("checkout"); // Create or get named page
const pages = await client.list();           // List all pages
await client.close("checkout");              // Close a page
```

### ARIA Snapshots

Discover page elements without knowing the layout:

```typescript
const snapshot = await client.getAISnapshot("main");
console.log(snapshot);
// Output:
// - banner:
//   - link "Home" [ref=e1]
//   - button "Menu" [ref=e2]
// - main:
//   - heading "Welcome" [level=1]
//   - textbox [ref=e3] /placeholder: "Search"

// Interact by ref
const element = await client.selectSnapshotRef("main", "e2");
await element.click();
```

### Screenshots

```typescript
await page.screenshot({ path: "tmp/screenshot.png" });
await page.screenshot({ path: "tmp/full.png", fullPage: true });
```

## Workflow Pattern

1. **Write a script** — do ONE thing (navigate, click, fill)
2. **Run it** — observe output
3. **Evaluate** — did it work? What's the state?
4. **Iterate** — write next script based on results
5. **Repeat** until done

## Client API

```typescript
const client = await connect("http://localhost:9222");
const page = await client.page("name");          // Get or create page
const pages = await client.list();               // List all pages
await client.close("name");                      // Close a page
const snapshot = await client.getAISnapshot("name");     // ARIA tree
const el = await client.selectSnapshotRef("name", "e5"); // Get element
await client.disconnect();                       // Disconnect (pages persist)
```

The `page` object is a standard [Playwright Page](https://playwright.dev/docs/api/class-page).

## Key Principles

1. **Small scripts** — each does ONE thing
2. **Evaluate state** — always log results at the end
3. **Use page names** — descriptive like `"login"`, `"search-results"`
4. **Plain JS in evaluate** — no TypeScript inside `page.evaluate()`
5. **Disconnect to exit** — pages persist on server

## Waiting

```typescript
import { waitForPageLoad } from "@/client.js";

await waitForPageLoad(page);              // Full page load
await page.waitForSelector(".results");   // Wait for element
await page.waitForURL("**/success");      // Wait for URL
```

## Error Recovery

Pages persist after errors. Debug with:

```typescript
await page.screenshot({ path: "tmp/debug.png" });
console.log({
  url: page.url(),
  title: await page.title(),
});
```

## When to Use

| Use Case | Tool |
|----------|------|
| Interactive debugging | Dev Browser |
| CI/CD regression tests | `npm run test:e2e` (Playwright) |
| Visual inspection | Dev Browser screenshots |
| Form automation | Dev Browser |
| Complex multi-step flows | Dev Browser |

## Related Skills

- [docker-claude-skill](https://github.com/wrsmith108/docker-claude-skill) — Container-based development
- [clerk-claude-skill](https://github.com/wrsmith108/clerk-claude-skill) — Authentication patterns
- [varlock-claude-skill](https://github.com/wrsmith108/varlock-claude-skill) — Secrets management

## Original Project

This is a fork of [SawyerHood/dev-browser](https://github.com/SawyerHood/dev-browser). All credit for the original architecture goes to [Sawyer Hood](https://github.com/SawyerHood).

## License

MIT
