active/agent-browser/SKILL.md
Automate browser navigation, interaction, screenshots, and extraction.
npx skillsauth add kevinslin/skills agent-browserInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Every browser automation follows this pattern:
agent-browser open <url>agent-browser snapshot -i (get element refs like @e1, @e2)agent-browser open https://example.com/form
agent-browser snapshot -i
# Output: @e1 [input type="email"], @e2 [input type="password"], @e3 [button] "Submit"
agent-browser fill @e1 "[email protected]"
agent-browser fill @e2 "password123"
agent-browser click @e3
agent-browser wait --load networkidle
agent-browser snapshot -i # Check result
Before testing behavior in a logged-in app or local dev app, confirm the environment is valid:
agent-browser session listagent-browser --session app get urlcontenteditable or ProseMirror, not a plain textareaDo this before clicking around. Many false negatives come from testing the wrong browser state.
# Navigation
agent-browser open <url> # Navigate (aliases: goto, navigate)
agent-browser close # Close browser
# Snapshot
agent-browser snapshot -i # Interactive elements with refs (recommended)
agent-browser snapshot -i -C # Include cursor-interactive elements (divs with onclick, cursor:pointer)
agent-browser snapshot -s "#selector" # Scope to CSS selector
# Interaction (use @refs from snapshot)
agent-browser click @e1 # Click element
agent-browser fill @e2 "text" # Clear and type text
agent-browser type @e2 "text" # Type without clearing
agent-browser select @e1 "option" # Select dropdown option
agent-browser check @e1 # Check checkbox
agent-browser press Enter # Press key
agent-browser scroll down 500 # Scroll page
# Get information
agent-browser get text @e1 # Get element text
agent-browser get url # Get current URL
agent-browser get title # Get page title
# Wait
agent-browser wait @e1 # Wait for element
agent-browser wait --load networkidle # Wait for network idle
agent-browser wait --url "**/page" # Wait for URL pattern
agent-browser wait 2000 # Wait milliseconds
# Capture
agent-browser screenshot # Screenshot to temp dir
agent-browser screenshot --full # Full page screenshot
agent-browser pdf output.pdf # Save as PDF
agent-browser open https://example.com/signup
agent-browser snapshot -i
agent-browser fill @e1 "Jane Doe"
agent-browser fill @e2 "[email protected]"
agent-browser select @e3 "California"
agent-browser check @e4
agent-browser click @e5
agent-browser wait --load networkidle
# Login once and save state
agent-browser open https://app.example.com/login
agent-browser snapshot -i
agent-browser fill @e1 "$USERNAME"
agent-browser fill @e2 "$PASSWORD"
agent-browser click @e3
agent-browser wait --url "**/dashboard"
agent-browser state save auth.json
# Reuse in future sessions
agent-browser state load auth.json
agent-browser open https://app.example.com/dashboard
# Auto-save/restore cookies and localStorage across browser restarts
agent-browser --session myapp open https://app.example.com/login
# ... login flow ...
agent-browser --session myapp close # State auto-saved to ~/.agent-browser/sessions/
# Next time, state is auto-loaded
agent-browser --session myapp open https://app.example.com/dashboard
# Encrypt state at rest
export AGENT_BROWSER_ENCRYPTION_KEY=$(openssl rand -hex 32)
agent-browser --session secure open https://app.example.com
# Manage saved states
agent-browser state list
agent-browser state show myapp-default.json
agent-browser state clear myapp
agent-browser state clean --older-than 7
agent-browser open https://example.com/products
agent-browser snapshot -i
agent-browser get text @e5 # Get specific element text
agent-browser get text body > page.txt # Good for static pages; too noisy for app-state classification
# JSON output for parsing
agent-browser snapshot -i --json
agent-browser get text @e1 --json
For interactive apps, prefer a narrow container or stable selector over get text body. Full-body text often mixes shell UI, scripts, and transient loading text with the real result.
agent-browser --session site1 open https://site-a.com
agent-browser --session site2 open https://site-b.com
agent-browser --session site1 snapshot -i
agent-browser --session site2 snapshot -i
agent-browser session list
# Auto-discover running Chrome with remote debugging enabled
agent-browser --auto-connect open https://example.com
agent-browser --auto-connect snapshot
# Or with explicit CDP port
agent-browser --cdp 9222 snapshot
# Launch once with headed/profile flags
agent-browser --session demo --headed open https://example.com
# Follow-up commands should reuse only the session name
agent-browser --session demo highlight @e1
agent-browser --session demo record start demo.webm
If the user asked for a visible/headed browser, verify the window is actually on screen and frontmost. On macOS, the app may appear as Google Chrome for Testing rather than Google Chrome.
If recording is requested, prefer a fresh session when possible. Recording mixed with persistent-profile debugging is more fragile than plain headed automation.
# Open local files with file:// URLs
agent-browser --allow-file-access open file:///path/to/document.pdf
agent-browser --allow-file-access open file:///path/to/page.html
agent-browser screenshot output.png
# List available iOS simulators
agent-browser device list
# Launch Safari on a specific device
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com
# Same workflow as desktop - snapshot, interact, re-snapshot
agent-browser -p ios snapshot -i
agent-browser -p ios tap @e1 # Tap (alias for click)
agent-browser -p ios fill @e2 "text"
agent-browser -p ios swipe up # Mobile-specific gesture
# Take screenshot
agent-browser -p ios screenshot mobile.png
# Close session (shuts down simulator)
agent-browser -p ios close
Requirements: macOS with Xcode, Appium (npm install -g appium && appium driver install xcuitest)
Real devices: Works with physical iOS devices if pre-configured. Use --device "<UDID>" where UDID is from xcrun xctrace list devices.
The default Playwright timeout is 60 seconds for local browsers. For slow websites or large pages, use explicit waits instead of relying on the default timeout:
# Wait for network activity to settle (best for slow pages)
agent-browser wait --load networkidle
# Wait for a specific element to appear
agent-browser wait "#content"
agent-browser wait @e1
# Wait for a specific URL pattern (useful after redirects)
agent-browser wait --url "**/dashboard"
# Wait for a JavaScript condition
agent-browser wait --fn "document.readyState === 'complete'"
# Wait a fixed duration (milliseconds) as a last resort
agent-browser wait 5000
When dealing with consistently slow websites, use wait --load networkidle after open to ensure the page is fully loaded before taking a snapshot. If a specific element is slow to render, wait for it directly with wait <selector> or wait @ref.
When running multiple agents or automations concurrently, always use named sessions to avoid conflicts:
# Each agent gets its own isolated session
agent-browser --session agent1 open site-a.com
agent-browser --session agent2 open site-b.com
# Check active sessions
agent-browser session list
Launch-only flags such as --profile and --headed belong on the initial open command for a session. After the browser daemon is running, reuse only --session for follow-up commands. Do not repeat launch flags on later commands; they can be ignored or produce warnings like --profile ignored: daemon already running.
# Initial launch
agent-browser --session self-test --profile ~/.agent-browser/profiles/self-test --headed open http://localhost:3000
# Follow-up commands
agent-browser --session self-test wait --load networkidle
agent-browser --session self-test snapshot -i
agent-browser --session self-test get url
If you need a different profile or different launch flags, close the running session first and relaunch:
agent-browser --session self-test close
agent-browser --session self-test --profile ~/.agent-browser/profiles/other --headed open http://localhost:3000
Always close your browser session when done to avoid leaked processes:
agent-browser close # Close default session
agent-browser --session agent1 close # Close specific session
If a previous session was not closed properly, the daemon may still be running. Use agent-browser close to clean it up before starting new work.
For chat apps, approval flows, and other long-running actions, separate submission from observation:
# Submit first
agent-browser --session chat snapshot -i
agent-browser --session chat fill @e2 "hello"
agent-browser --session chat click @e3
# Then watch a narrow region or stable selector
agent-browser --session chat wait 2000
agent-browser --session chat snapshot -i -s '[data-testid="conversation"]'
Guidelines:
snapshot -s "<selector>" or stable test ids over get text bodyConnecting to app, Stop streaming, and similar shell text as weak signals, not final proofRefs (@e1, @e2, etc.) are invalidated when the page changes. Always re-snapshot after:
agent-browser click @e5 # Navigates to new page
agent-browser snapshot -i # MUST re-snapshot
agent-browser click @e1 # Use new refs
When refs are unavailable or unreliable, use semantic locators:
agent-browser find text "Sign In" click
agent-browser find label "Email" fill "[email protected]"
agent-browser find role button click --name "Submit"
agent-browser find placeholder "Search" type "query"
agent-browser find testid "submit-btn" click
Use eval to run JavaScript in the browser context. Shell quoting can corrupt complex expressions -- use --stdin or -b to avoid issues.
# Simple expressions work with regular quoting
agent-browser eval 'document.title'
agent-browser eval 'document.querySelectorAll("img").length'
# Complex JS: use --stdin with heredoc (RECOMMENDED)
agent-browser eval --stdin <<'EVALEOF'
JSON.stringify(
Array.from(document.querySelectorAll("img"))
.filter(i => !i.alt)
.map(i => ({ src: i.src.split("/").pop(), width: i.width }))
)
EVALEOF
# Alternative: base64 encoding (avoids all shell escaping issues)
agent-browser eval -b "$(echo -n 'Array.from(document.querySelectorAll("a")).map(a => a.href)' | base64)"
Why this matters: When the shell processes your command, inner double quotes, ! characters (history expansion), backticks, and $() can all corrupt the JavaScript before it reaches agent-browser. The --stdin and -b flags bypass shell interpretation entirely.
Rules of thumb:
eval 'expression' with single quotes is fineeval --stdin <<'EVALEOF'eval -b with base64| Reference | When to Use | |-----------|-------------| | references/commands.md | Full command reference with all options | | references/snapshot-refs.md | Ref lifecycle, invalidation rules, troubleshooting | | references/session-management.md | Parallel sessions, state persistence, concurrent scraping | | references/authentication.md | Login flows, OAuth, 2FA handling, state reuse | | references/video-recording.md | Recording workflows for debugging and documentation | | references/proxy-support.md | Proxy configuration, geo-testing, rotating proxies |
| Template | Description | |----------|-------------| | templates/form-automation.sh | Form filling with validation | | templates/authenticated-session.sh | Login once, reuse state | | templates/capture-workflow.sh | Content extraction with screenshots |
./templates/form-automation.sh https://example.com/form
./templates/authenticated-session.sh https://app.example.com/login
./templates/capture-workflow.sh https://example.com ./output
development
Resolve explicit shortcut triggers and usage. Always read this file at the start of a thread or when user mentions `trigger`.
testing
Improve skills from observed agent friction in sessions, PRs, or audits.
development
Generate incremental Slack digests for channels, topics, and categories.
testing
Audit an OpenClaw maturity-scorecard surface into an evidence-backed component score report. Use when given a surface from an OpenClaw maturity-scorecard.md and asked to score coverage, quality, readiness, or generate a detailed surface report plus per-component subreports.