From brw
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.
npx claudepluginhub sshh12/claude-plugins --plugin brwThis skill uses the workspace's default tool permissions.
Run any command once to auto-create the `/tmp/brw` shortcut:
Verifies tests pass on completed feature branch, presents options to merge locally, create GitHub PR, keep as-is or discard; executes choice and cleans up worktree.
Guides root cause investigation for bugs, test failures, unexpected behavior, performance issues, and build failures before proposing fixes.
Writes implementation plans from specs for multi-step tasks, mapping files and breaking into TDD bite-sized steps before coding.
Run any command once to auto-create the /tmp/brw shortcut:
node "${SKILL_DIR}/scripts/brw.js" --help
Then use /tmp/brw for all subsequent commands. Prerequisites: Node.js 18+, a Chromium-based browser (Chrome, Chromium, Edge, or Brave).
The proxy server auto-starts on first command. Chrome launches headed by default (set BRW_HEADLESS=true for headless).
The proxy is a persistent daemon — 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.
/tmp/brw server status # Check if proxy is running
/tmp/brw server start # Manually start (usually not needed)
/tmp/brw server start --clean # Kill all debug-mode browsers, then start fresh
/tmp/brw server stop # Stop proxy and Chrome
/tmp/brw server restart # Restart proxy only (keeps Chrome and tabs alive)
/tmp/brw server clean # Kill all debug-mode browsers and clean up state
If Chrome crashes, the proxy auto-relaunches on the next command. Sessions, cookies, and tabs survive proxy restarts because the proxy reconnects to an existing Chrome if one is already running on the CDP port. Use server restart to recover from an unresponsive proxy without losing tabs.
/tmp/brw screenshot/tmp/brw read-page or /tmp/brw read-page --filter interactiveThis screenshot-act-verify loop is the core pattern. Every mutation command returns a screenshot path — read it to see what happened.
All commands return JSON:
// Mutation commands (click, type, navigate, etc.)
{"ok": true, "screenshot": "/tmp/brw-screenshots/123.png", "page": {"url": "...", "title": "...", "contentLength": 48230}}
// Read-only commands (read-page, get-text, tabs, etc.)
{"ok": true, "tree": "...", "refCount": 42}
// Errors
{"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.
/tmp/brw navigate <url> # Go to URL (auto-prepends https://)
/tmp/brw navigate back # Go back
/tmp/brw navigate forward # Go forward
/tmp/brw navigate <url> --wait network # Wait for network idle (default: dom)
/tmp/brw navigate <url> --wait render # Full SPA render wait (network + layout stable + paint)
/tmp/brw screenshot # Current viewport
/tmp/brw screenshot --full-page # Entire scrollable page
/tmp/brw screenshot --ref ref_3 # Single element
/tmp/brw screenshot --region 0,0,500,300 # Crop to region
/tmp/brw click <x> <y> # Left click at coordinates
/tmp/brw click --ref ref_5 # Click element by ref ID
/tmp/brw click --selector "button.submit" # Click by CSS selector
/tmp/brw click --text "Save and Continue" # Click by visible text (no read-page needed)
/tmp/brw click --label "Email" # Click form input by label
/tmp/brw click --text "Submit" --wait # Wait up to 10s for element, then click
/tmp/brw click <x> <y> --right # Right click
/tmp/brw click <x> <y> --double # Double click
/tmp/brw type "hello world" # Type into focused element
/tmp/brw type "new text" --clear # Clear field first, then type
/tmp/brw type "hello" --text "Search" # Focus element by text, then type
/tmp/brw type "test@email.com" --label "Email" --clear # Focus by label, clear, type
/tmp/brw key Enter # Press Enter
/tmp/brw key "cmd+a" # Keyboard shortcut
/tmp/brw key Tab --repeat 3 # Press Tab 3 times
/tmp/brw read-page # Full a11y tree with ref IDs
/tmp/brw read-page --filter interactive # Only interactive elements (inputs, buttons, links)
/tmp/brw read-page --search "Submit" # Search for elements by text
/tmp/brw read-page --ref ref_5 # Subtree rooted at ref
/tmp/brw read-page --depth 2 # Limit tree depth
/tmp/brw read-page --frame 0 # Read iframe content
/tmp/brw read-page --limit 50 # Cap at 50 ref elements (use --search to narrow)
/tmp/brw read-page --include-hidden # Include aria-hidden="true" elements (overlays, compose UIs)
The tree includes ref IDs (like ref_1, ref_2) that can be used with --ref in other commands. Refs persist until navigation.
/tmp/brw get-text # Extract main content text
/tmp/brw get-text --max-chars 500 # Limit output length
Set form values programmatically (triggers change/input events):
/tmp/brw form-input --ref ref_3 --value "test@example.com" # Text input
/tmp/brw form-input --ref ref_7 --value true # Checkbox
/tmp/brw form-input --ref ref_9 --value "option2" # Select dropdown
/tmp/brw form-input --label "Password" --value "secret" # Find by label text
/tmp/brw form-input --text "Username" --value "john" # Find by accessible name
/tmp/brw form-input --selector "#email" --value "test@example.com"
/tmp/brw js "document.title" # Evaluate expression
/tmp/brw js --file /tmp/script.js # Read JS from file
/tmp/brw js "await fetch('/api').then(r => r.json())" # Async expression
/tmp/brw js "document.title" --frame 0 # Execute in iframe
/tmp/brw js - <<'JS' # Heredoc (recommended for multi-line)
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).
/tmp/brw scroll down # Scroll down (default amount)
/tmp/brw scroll down --amount 5 # Scroll down 5 ticks
/tmp/brw scroll up # Scroll up
/tmp/brw scroll down --at 200,400 # Scroll element at coordinates
/tmp/brw scroll-to --ref ref_12 # Scroll element into view
/tmp/brw hover <x> <y> # Hover at coordinates
/tmp/brw hover --ref ref_3 # Hover over element
/tmp/brw drag 100 100 300 300 # Drag from (100,100) to (300,300)
/tmp/brw drag --from-ref ref_1 --to-ref ref_5 # Drag between elements
/tmp/brw wait --duration 2 # Wait 2 seconds
/tmp/brw wait-for --selector ".modal" # Wait for element to appear
/tmp/brw wait-for --text "Success" # Wait for text on page
/tmp/brw wait-for --url "*/dashboard*" # Wait for URL change
/tmp/brw wait-for --js "window.loaded" # Wait for JS condition
/tmp/brw wait-for --network-idle # Wait for network to settle
wait-for returns matched: true/false — it does not error on timeout.
/tmp/brw tabs # List all tabs
/tmp/brw new-tab "https://example.com" # Open URL in new tab
/tmp/brw new-tab "https://example.com" --wait dom # Open and wait for page load
/tmp/brw switch-tab <id> # Switch to tab (accepts ID or alias)
/tmp/brw close-tab <id> # Close tab
/tmp/brw name-tab inbox # Name active tab "inbox"
/tmp/brw name-tab docs 2 # Name tab 2 "docs"
Named tabs can be used anywhere --tab is accepted (e.g., --tab inbox).
/tmp/brw dialog # Check for pending dialog
/tmp/brw dialog accept # Accept/OK
/tmp/brw dialog dismiss # Cancel/dismiss
/tmp/brw dialog accept --text "response" # Respond to prompt dialog
Dialogs auto-dismiss after 5 seconds if not handled explicitly.
/tmp/brw console # Read captured console messages
/tmp/brw console --errors-only # Only errors
/tmp/brw network # Read captured network requests
/tmp/brw network --url-pattern "api" # Filter by URL
/tmp/brw network-body <request_id> # Get response body
/tmp/brw file-upload --ref ref_3 --files /path/to/file.txt
/tmp/brw file-upload --ref ref_3 --files /tmp/a.txt /tmp/b.txt # Multiple files
/tmp/brw cookies # List cookies for current tab domain
/tmp/brw cookies --all-domains # List all cookies across domains
/tmp/brw cookies get "session_id" # Get specific cookie
/tmp/brw cookies set "name" "value" # Set cookie
/tmp/brw storage get "key" # Get localStorage value
/tmp/brw storage set "key" "value" # Set localStorage value
/tmp/brw intercept add "*/api/data" --status 200 --body '{"mock": true}'
/tmp/brw intercept add "*analytics*" --block
/tmp/brw intercept list
/tmp/brw intercept remove <rule_id>
/tmp/brw intercept clear
/tmp/brw new-tab <url> --window # Open in separate Chrome window
/tmp/brw arrange # Tile all windows in a grid
/tmp/brw window-bounds # Get/set window position and size
/tmp/brw resize 800 600 # Resize viewport
/tmp/brw pdf --output report.pdf # Save page as PDF
/tmp/brw emulate --device "iPhone 15" # Device emulation
/tmp/brw perf # Performance metrics
/tmp/brw gif start # Start GIF recording
/tmp/brw gif stop # Stop recording
/tmp/brw gif export --output demo.gif # Export animated GIF
/tmp/brw server status # Check proxy status
/tmp/brw server stop # Stop proxy and Chrome
/tmp/brw log # Show recent proxy log entries
/tmp/brw log --lines 100 # Show last 100 log lines
Chain multiple simple actions in one call to reduce round-trips:
/tmp/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.
--text/--label instead of --ref when element text is stable — skips the read-page step. Add --wait for dynamic content.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.--ref ref_X over coordinate clicks — refs are more reliable and survive scrolling.--no-screenshot when chaining actions before a manual screenshot. Saves time.--wait render for SPAs. For heavy apps (Gmail), prefer --wait dom + wait-for --selector.--frame 0 (by index) or --frame "name" for iframe content. read-page returns iframes: N when iframes exist.new-tab <url> --wait dom --alias <name> for atomic tab creation + naming.--search errors, narrow with --scope "main" first or use --filter interactive --limit 50.read-page returns a hint when canvas is detected. Use screenshot + js instead.--include-hidden or --scope "[role='dialog']".--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).
Set via environment variables (BRW_*), .claude/brw.json (per-repo), or ~/.config/brw/config.json (user). Run /tmp/brw config to see resolved values.
Key variables: BRW_HEADLESS, BRW_CHROME_PATH, BRW_PORT, BRW_SCREENSHOT_DIR, BRW_ALLOWED_URLS, BRW_AUTO_SCREENSHOT (set to false to disable auto-screenshots on mutation commands — useful for automation loops), BRW_CHROME_LAUNCH (set to false to connect to an existing Chrome instead of launching one).
brw blocks dangerous protocols (file://, javascript:, data:, etc.) and cloud metadata endpoints by default. Cookies are scoped to the current tab's domain. Run /tmp/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.
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:
/tmp/brw profile list # List available profiles
/tmp/brw profile show google-docs # Show profile actions/selectors
/tmp/brw run google-docs:read-content # Run a profile action
/tmp/brw run google-docs:type-text --param text="Hello" # Run with parameters
Profiles live in .claude/brw/profiles/<name>/ (repo) or ~/.config/brw/profiles/<name>/ (user). See references/PROFILES.md for authoring details.
references/COMMANDS.md — all flags, output fields, and edge casesreferences/SECURITY.md — threat model, default protections, recommended configsreferences/QUICK-MODE.md — command table and multi-step examplesreferences/PROFILES.md — profile format, discovery, authoring guide