| name | brw |
| description | Automates browser interactions via Chrome DevTools Protocol. Screenshots, clicks, types, navigates, reads page accessibility trees, extracts text, and executes JavaScript in web pages. Use when the user asks to interact with a website, test a web app, fill web forms, scrape web content, or automate browser tasks. |
brw — Browser Automation
Setup
Step 1 — one-time bootstrap (the only command that uses a node path):
node "${SKILL_DIR}/scripts/brw.js" install
This symlinks brw into ~/.local/bin. If ~/.local/bin is not yet on your PATH, the command prints a nextSteps block containing an exportLine — append that exportLine to your shell profile (the printed profile path), then restart the session so brw resolves.
Step 2 — HARD ASSERT before any brw use (checks both that brw is on PATH and that it is this exact build — an older/shadowing brw earlier on PATH would silently run stale code):
[ "$(brw --version 2>/dev/null)" = "$(node "${SKILL_DIR}/scripts/brw.js" --version 2>/dev/null)" ] && echo BRW_OK || echo BRW_STALE
If this prints BRW_STALE (bare brw is missing, or an older/different brw is winning on PATH), run node "${SKILL_DIR}/scripts/brw.js" install and apply its nextSteps (it reports shadowedBy / onPathVersion and how to fix), restart the session, then re-run this check. DO NOT proceed using node or absolute paths for real work — once BRW_OK, every command MUST be the bare brw ....
Prerequisites: Node.js 18+, a Chromium-based browser (Chrome, Chromium, Edge, or Brave).
Proxy Lifecycle
The proxy is a per-user daemon listening on a private Unix domain socket (mode 0600) at ~/.config/brw/proxy.sock — there is no TCP port. It starts automatically on first command and stays running for 4 hours of idle time (configurable via BRW_IDLE_TIMEOUT). You do not need to restart it between commands. Intercept rules, console captures, and network captures all persist within a session. Chrome is driven over --remote-debugging-pipe (no debug port is opened).
brw server status
brw server start
brw server start --clean
brw server stop
brw server restart
brw server clean
server restart relaunches Chrome, so any open tabs are lost. Login state and cookies persist because they live in the on-disk Chrome profile directory, not in the running process.
Workflow
- Screenshot to see current state:
brw screenshot
- Read page to understand structure:
brw read-page or brw read-page --filter interactive
- Interact via click/type/key/navigate — each returns an auto-screenshot
- Read the screenshot to verify the result, then repeat
This screenshot-act-verify loop is the core pattern. Every mutation command returns a screenshot path — read it to see what happened.
Output Format
All commands return JSON:
{"ok": true, "screenshot": "~/.config/brw/screenshots/123.png", "page": {"url": "...", "title": "...", "contentLength": 48230}}
{"ok": true, "tree": "...", "refCount": 42}
{"ok": false, "error": "Tab not found", "code": "TAB_NOT_FOUND", "hint": "Available tabs: 1, 2, 3"}
Check page.url between commands to detect unexpected navigations. On error, read code and hint for recovery guidance.
Core Commands
Navigation
brw navigate <url>
brw navigate back
brw navigate forward
brw navigate <url> --wait network
brw navigate <url> --wait render
Screenshot
brw screenshot
brw screenshot --full-page
brw screenshot --ref ref_3
brw screenshot --region 0,0,500,300
Click
brw click <x> <y>
brw click --ref ref_5
brw click --selector "button.submit"
brw click --text "Save and Continue"
brw click --label "Email"
brw click --text "Submit" --wait
brw click <x> <y> --right
brw click <x> <y> --double
Type & Key
brw type "hello world"
brw type "new text" --clear
brw type "hello" --text "Search"
brw type "test@email.com" --label "Email" --clear
brw key Enter
brw key "cmd+a"
brw key Tab --repeat 3
Read Page (Accessibility Tree)
brw read-page
brw read-page --filter interactive
brw read-page --search "Submit"
brw read-page --ref ref_5
brw read-page --depth 2
brw read-page --frame 0
brw read-page --limit 50
brw read-page --include-hidden
The tree includes ref IDs (like ref_1, ref_2) that can be used with --ref in other commands. Refs persist until navigation.
Get Text
brw get-text
brw get-text --max-chars 500
Form Input
Set form values programmatically (triggers change/input events):
brw form-input --ref ref_3 --value "test@example.com"
brw form-input --ref ref_7 --value true
brw form-input --ref ref_9 --value "option2"
brw form-input --label "Password" --value "secret"
brw form-input --text "Username" --value "john"
brw form-input --selector "#email" --value "test@example.com"
JavaScript
brw js "document.title"
brw js --file /tmp/script.js
brw js "await fetch('/api').then(r => r.json())"
brw js "document.title" --frame 0
brw js - <<'JS'
document.querySelectorAll('a').forEach(a => console.log(a.href))
JS
For complex or multi-line JS, use heredoc (brw js - <<'JS') or --file to avoid shell quoting issues. await in heredoc/file input is auto-wrapped in an async IIFE — no manual wrapping needed. Note: multi-line heredoc input requires explicit return for the last value (single-line expressions auto-return).
Scroll
brw scroll down
brw scroll down --amount 5
brw scroll up
brw scroll down --at 200,400
brw scroll-to --ref ref_12
Hover & Drag
brw hover <x> <y>
brw hover --ref ref_3
brw drag 100 100 300 300
brw drag --from-ref ref_1 --to-ref ref_5
Wait
brw wait --duration 2
brw wait-for --selector ".modal"
brw wait-for --text "Success"
brw wait-for --url "*/dashboard*"
brw wait-for --js "window.loaded"
brw wait-for --network-idle
wait-for returns matched: true/false — it does not error on timeout.
Tabs
brw tabs
brw new-tab "https://example.com"
brw new-tab "https://example.com" --wait dom
brw switch-tab <id>
brw close-tab <id>
brw name-tab inbox
brw name-tab docs 2
Named tabs can be used anywhere --tab is accepted (e.g., --tab inbox).
Dialog Handling
brw dialog
brw dialog accept
brw dialog dismiss
brw dialog accept --text "response"
Dialogs auto-dismiss after 5 seconds if not handled explicitly.
Advanced Commands
Console & Network
brw console
brw console --errors-only
brw network
brw network --url-pattern "api"
brw network --full
brw network-request <request_id>
brw network-body <request_id>
Script Run & Generate
For data extraction or batch workflows, drive the app's APIs from the page context rather than clicking through the DOM. The script runs in the active tab's runtime, so fetch(..., { credentials: 'include' }) reuses the page's cookies, CSRF tokens, and session.
brw script run /tmp/x.js [--param k=v] [--timeout 120] [--output /tmp/x.json]
brw script run --inline "return document.title"
brw script run - <<'JS' ... JS
brw script gen --url-pattern <subs> --output /tmp/x.js
Discovery companions:
brw auth-tokens --tab <alias> [--probe <url>]
brw network --url-pattern A --url-pattern B --status 4xx --with-body-preview 300 --tab <alias>
brw network-request <id> --tab <alias>
Globals injected inside script run: args, log(...), sleep(ms), cookie(name), xssiUnwrap(text), gjson(responseOrText). Top-level await and return <value> work.
Full guide: see references/SCRIPT-WORKFLOW.md for the 5-step discovery loop, auth/pagination/response-shape taxonomy, gotchas, and DOM fallback recipe.
File Upload
brw file-upload --ref ref_3 --files /path/to/file.txt
brw file-upload --ref ref_3 --files /tmp/a.txt /tmp/b.txt
Cookies & Storage
brw cookies
brw cookies --all-domains
brw cookies get "session_id"
brw cookies set "name" "value"
brw storage get "key"
brw storage set "key" "value"
Network Interception
brw intercept add "*/api/data" --status 200 --body '{"mock": true}'
brw intercept add "*analytics*" --block
brw intercept list
brw intercept remove <rule_id>
brw intercept clear
Other
brw new-tab <url> --window
brw arrange
brw window-bounds
brw resize 800 600
brw pdf --output report.pdf
brw emulate --device "iPhone 15"
brw perf
brw gif start
brw gif stop
brw gif export --output demo.gif
brw server status
brw server stop
brw log
brw log --lines 100
Quick Mode
Chain multiple simple actions in one call to reduce round-trips:
brw quick "N https://example.com
W
C 500 300
T hello world
K Enter"
Ref-based commands are also available: CR ref_5 (click ref), FR ref_3 value (form-input ref), R (read-page), WF --text "Done" (wait-for).
Text-based commands: CT Submit (click by text), FT Email test@example.com (form-input by label).
Returns a screenshot after the final command. See references/QUICK-MODE.md for the full command table.
Tips
- Semantic targeting: Use
--text/--label instead of --ref when element text is stable — skips the read-page step. Add --wait for dynamic content.
- Multi-page wizards: Chain
CT + WF across pages in one quick call: CT Next\nWF --text "Step 2". For JS-heavy forms, J document.querySelector('form').submit() + WF --url */next* skips coordinate resolution. Use W 3 for fixed pauses between pages.
- Refs over coordinates: Prefer
--ref ref_X over coordinate clicks — refs are more reliable and survive scrolling.
- Skip auto-screenshot: Use
--no-screenshot when chaining actions before a manual screenshot. Saves time.
- SPAs: Use
--wait render for SPAs. For heavy apps (Gmail), prefer --wait dom + wait-for --selector.
- Iframes: Use
--frame 0 (by index) or --frame "name" for iframe content. read-page returns iframes: N when iframes exist.
- Multi-agent: Each agent uses its own tab. Use
new-tab <url> --wait dom --alias <name> for atomic tab creation + naming.
- Complex pages: If
--search errors, narrow with --scope "main" first or use --filter interactive --limit 50.
- Canvas apps:
read-page returns a hint when canvas is detected. Use screenshot + js instead.
- Hidden overlays (Gmail compose): Use
--include-hidden or --scope "[role='dialog']".
- Global flags:
--tab <alias> targets a specific tab, --no-screenshot skips screenshots.
Most tips are also returned as contextual hint fields in CLI responses when relevant (e.g., REF_NOT_FOUND, canvas pages, search failures).
Configuration
Set via environment variables (BRW_*), .claude/brw.json (per-repo), or ~/.config/brw/config.json (user). Run brw config to see resolved values.
Key variables: BRW_HEADLESS, BRW_CHROME_PATH, BRW_SOCKET, BRW_SCREENSHOT_DIR, BRW_ALLOWED_URLS, BRW_AUTO_SCREENSHOT (set to false to disable auto-screenshots on mutation commands — useful for automation loops).
Security
brw blocks dangerous protocols (file://, javascript:, data:, etc.) and cloud metadata endpoints by default. Cookies are scoped to the current tab's domain. Run brw server status to see the active security posture. See references/SECURITY.md for the full threat model, recommended configs, and per-command security notes.
App Profiles
Profiles package app-specific automation (selectors, JS scripts, multi-step actions) into reusable config directories. Instead of repeating workarounds in every prompt, call profile actions directly:
brw profile list
brw profile show google-docs
brw run google-docs:read-content
brw run google-docs:type-text --param text="Hello"
Profiles live in .claude/brw/profiles/<name>/ (repo) or ~/.config/brw/profiles/<name>/ (user). See references/PROFILES.md for authoring details.
References
- Script workflow:
references/SCRIPT-WORKFLOW.md — DOM → API conversion playbook; auth/pagination/response-shape patterns; per-app cheat sheet
- Full command reference:
references/COMMANDS.md — all flags, output fields, and edge cases
- Security reference:
references/SECURITY.md — threat model, default protections, recommended configs
- Quick mode reference:
references/QUICK-MODE.md — command table and multi-step examples
- App profiles reference:
references/PROFILES.md — profile format, discovery, authoring guide