- name
- ck:chrome-devtools
- description
- Automate browsers with Puppeteer CLI scripts and persistent sessions. Use for screenshots, performance analysis, network monitoring, web scraping, form automation, JavaScript debugging.
- license
- Apache-2.0
- argument-hint
- [url or task]
- metadata
- {"author":"claudekit","version":"1.1.0"}
# Chrome DevTools Agent Skill
Browser automation via Puppeteer scripts with persistent sessions. All scripts output JSON.
## Skill Location
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
```bash
# Detect skill location (no cd needed - scripts use __dirname for paths)
SKILL_DIR=""
if [ -d ".opencode/skills/chrome-devtools/scripts" ]; then
SKILL_DIR=".opencode/skills/chrome-devtools/scripts"
elif [ -d "$HOME/.opencode/skills/chrome-devtools/scripts" ]; then
SKILL_DIR="$HOME/.opencode/skills/chrome-devtools/scripts"
fi
# Run scripts with full path: node "$SKILL_DIR/script.js" --args
```
## Choosing Your Approach
| Scenario | Approach |
|----------|----------|
| **Source-available sites** | Read source code first, write selectors directly |
| **Unknown layouts** | Use `aria-snapshot.js` for semantic discovery |
| **Visual inspection** | Take screenshots to verify rendering |
| **Debug issues** | Collect console logs, analyze with session storage |
| **Accessibility audit** | Use ARIA snapshot for semantic structure analysis |
## Automation Browsing Running Mode
Browser visibility is resolved automatically by `resolveHeadless()` in `lib/browser.js`:
| Environment | Default | Why |
|-------------|---------|-----|
| **macOS / Windows** | **Headed** (visible) | Better debugging, OAuth login support |
| **Linux / WSL** | **Headless** | Servers typically have no display |
| **CI** (`CI`, `GITHUB_ACTIONS`, `GITLAB_CI`, `JENKINS_URL` env vars) | **Headless** | No display available |
Override with `--headless true` or `--headless false` on any script.
- Run multiple scripts/sessions in parallel to simulate real user interactions.
- Run multiple scripts/sessions in parallel to simulate different device types (mobile, tablet, desktop).
## ARIA Snapshot (Element Discovery)
When page structure is unknown, use `aria-snapshot.js` to get a YAML-formatted accessibility tree with semantic roles, accessible names, states, and stable element references.
### Get ARIA Snapshot
```bash
# Generate ARIA snapshot and output to stdout
node "$SKILL_DIR/aria-snapshot.js" --url https://example.com
# Save to file in snapshots directory
node "$SKILL_DIR/aria-snapshot.js" --url https://example.com --output ./.opencode/chrome-devtools/snapshots/page.yaml
```
### Example YAML Output
```yaml
- banner:
- link "Hacker News" [ref=e1]
/url: https://news.ycombinator.com
- navigation:
- link "new" [ref=e2]
- link "past" [ref=e3]
- link "comments" [ref=e4]
- main:
- list:
- listitem:
- link "Show HN: My new project" [ref=e8]
- text: "128 points by user 3 hours ago"
- contentinfo:
- textbox [ref=e10]
/placeholder: "Search"
```
### Interpreting ARIA Notation
| Notation | Meaning |
|----------|---------|
| `[ref=eN]` | Stable identifier for interactive elements |
| `[checked]` | Checkbox/radio is selected |
| `[disabled]` | Element is inactive |
| `[expanded]` | Accordion/dropdown is open |
| `[level=N]` | Heading hierarchy (1-6) |
| `/url:` | Link destination |
| `/placeholder:` | Input placeholder text |
| `/value:` | Current input value |
### Interact by Ref
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
Use `select-ref.js` to interact with elements by their ref:
```bash
# Click element with ref e5
node "$SKILL_DIR/select-ref.js" --ref e5 --action click
# Fill input with ref e10
node "$SKILL_DIR/select-ref.js" --ref e10 --action fill --value "search query"
# Get text content
node "$SKILL_DIR/select-ref.js" --ref e8 --action text
# Screenshot specific element
node "$SKILL_DIR/select-ref.js" --ref e1 --action screenshot --output ./logo.png
# Focus element
node "$SKILL_DIR/select-ref.js" --ref e10 --action focus
# Hover over element
node "$SKILL_DIR/select-ref.js" --ref e5 --action hover
```
### Store Snapshots
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
Store snapshots for analysis in `<project>/.opencode/chrome-devtools/snapshots/`:
```bash
# Create snapshots directory
mkdir -p .opencode/chrome-devtools/snapshots
# Capture and store with timestamp
SESSION="$(date +%Y%m%d-%H%M%S)"
node "$SKILL_DIR/aria-snapshot.js" --url https://example.com --output .opencode/chrome-devtools/snapshots/$SESSION.yaml
```
### Workflow: Unknown Page Structure
1. **Get snapshot** to discover elements:
```bash
node "$SKILL_DIR/aria-snapshot.js" --url https://example.com
```
2. **Identify target** from YAML output (e.g., `[ref=e5]` for a button)
3. **Interact by ref**:
```bash
node "$SKILL_DIR/select-ref.js" --ref e5 --action click
```
4. **Verify result** with screenshot or new snapshot:
```bash
node "$SKILL_DIR/screenshot.js" --output ./result.png
```
## Local HTML Files
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
**IMPORTANT**: Never browse local HTML files via `file://` protocol. Always serve via local server:
**Why**: `file://` protocol blocks many browser features (CORS, ES modules, fetch API, service workers). Local server ensures proper HTTP behavior.
```bash
# Option 1: npx serve (recommended)
npx serve ./dist -p 3000 &
node "$SKILL_DIR/navigate.js" --url http://localhost:3000
# Option 2: Python http.server
python -m http.server 3000 --directory ./dist &
node "$SKILL_DIR/navigate.js" --url http://localhost:3000
```
**Note**: when port 3000 is busy, find an available port with `lsof -i :3000` and use a different one.
## Quick Start
```bash
# Install dependencies (one-time setup)
npm install --prefix "$SKILL_DIR"
# Test (browser stays running for session reuse)
node "$SKILL_DIR/navigate.js" --url https://example.com
# Output: {"success": true, "url": "...", "title": "..."}
```
**Linux/WSL only**: Run `"$SKILL_DIR/install-deps.sh"` first for Chrome system libraries.
## Session Persistence
Browser state persists across script executions via WebSocket endpoint file (`.browser-session.json`).
**Default behavior**: Scripts disconnect but keep browser running for session reuse.
```bash
# First script: launches browser, navigates, disconnects (browser stays running)
node "$SKILL_DIR/navigate.js" --url https://example.com/login
# Subsequent scripts: connect to existing browser, reuse page state
node "$SKILL_DIR/fill.js" --selector "#email" --value "user@example.com"
node "$SKILL_DIR/fill.js" --selector "#password" --value "secret"
node "$SKILL_DIR/click.js" --selector "button[type=submit]"
# Close browser when done
node "$SKILL_DIR/navigate.js" --url about:blank --close true
```
**Session management**:
- `--close true`: Close browser and clear session
- Default (no flag): Keep browser running for next script
## Available Scripts
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
All in `.opencode/skills/chrome-devtools/scripts/`:
| Script | Purpose |
|--------|---------|
| `navigate.js` | Navigate to URLs |
| `screenshot.js` | Capture screenshots (auto-compress >5MB via Sharp) |
| `click.js` | Click elements |
| `fill.js` | Fill form fields |
| `evaluate.js` | Execute JS in page context |
| `snapshot.js` | Extract interactive elements (JSON format) |
| `aria-snapshot.js` | Get ARIA accessibility tree (YAML format with refs) |
| `select-ref.js` | Interact with elements by ref from ARIA snapshot |
| `console.js` | Monitor console messages/errors |
| `network.js` | Track HTTP requests/responses |
| `performance.js` | Measure Core Web Vitals |
| `ws-debug.js` | Debug WebSocket connections (basic) |
| `ws-full-debug.js` | Debug WebSocket with full events/frames |
| `inject-auth.js` | Inject cookies/tokens for authentication |
| `import-cookies.js` | Import cookies from JSON/Netscape file |
| `connect-chrome.js` | Connect to Chrome with remote debugging |
## Workflow Loop
1. **Execute** focused script for single task
2. **Observe** JSON output
3. **Assess** completion status
4. **Decide** next action
5. **Repeat** until done
## Writing Custom Test Scripts
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
For complex automation, write scripts to `<project>/.opencode/chrome-devtools/tmp/`:
```bash
# Create tmp directory for test scripts
mkdir -p $SKILL_DIR/.opencode/chrome-devtools/tmp
# Write a test script
cat > $SKILL_DIR/.opencode/chrome-devtools/tmp/login-test.js << 'EOF'
import { getBrowser, getPage, disconnectBrowser, outputJSON } from '../scripts/lib/browser.js';
async function loginTest() {
const browser = await getBrowser();
const page = await getPage(browser);
await page.goto('https://example.com/login');
await page.type('#email', 'user@example.com');
await page.type('#password', 'secret');
await page.click('button[type=submit]');
await page.waitForNavigation();
outputJSON({
success: true,
url: page.url(),
title: await page.title()
});
await disconnectBrowser();
}
loginTest();
EOF
# Run the test
node $SKILL_DIR/.opencode/chrome-devtools/tmp/login-test.js
```
**Key principles for custom scripts**:
- Single-purpose: one script, one task
- Always call `disconnectBrowser()` at the end (keeps browser running)
- Use `closeBrowser()` only when ending session completely
- Output JSON for easy parsing
- Plain JavaScript only in `page.evaluate()` callbacks
## Screenshots
Skills can exist in **project-scope** or **user-scope**. Priority: project-scope > user-scope.
**IMPORTANT:** Invoke "/ck:project-organization" skill to organize the outputs.
Store screenshots for analysis in `<project>/.opencode/chrome-devtools/screenshots/`:
```bash
# Basic screenshot
node "$SKILL_DIR/screenshot.js" --url https://example.com --output ./.opencode/chrome-devtools/screenshots/page.png
# Full page
node "$SKILL_DIR/screenshot.js" --url https://example.com --output ./.opencode/chrome-devtools/screenshots/page.png --full-page true
# Specific element
node "$SKILL_DIR/screenshot.js" --url https://example.com --selector ".main-content" --output ./.opencode/chrome-devtools/screenshots/element.png
```
### Auto-Compression (Sharp)
Screenshots >5MB auto-compress using Sharp (4-5x faster than ImageMagick):
```bash
# Default: compress if >5MB
node "$SKILL_DIR/screenshot.js" --url https://example.com --output ./.opencode/chrome-devtools/screenshots/page.png
# Custom threshold (3MB)
node "$SKILL_DIR/screenshot.js" --url https://example.com --output ./.opencode/chrome-devtools/screenshots/page.png --max-size 3
# Disable compression
Ver en GitHub