Agent Skill

The skill file

Browser automation for AI agents. Use when the user needs to navigate websites, read page content, fill forms, click elements, take screenshots, assert page state, or manage browser tabs.

This is the exact skill bl add-skill installs to ~/.claude/skills/browserlane/SKILL.md — the file a coding agent reads on demand. Generated verbatim from bl v0.1.14 (the YAML frontmatter and top heading are omitted here; the page title and description carry them).

The bl CLI automates Chrome via the command line. The browser auto-launches on first use, and a warm background process keeps it running between commands.

bl open <url> && bl map && bl click @e1 && bl map

Core Workflow

Every browser automation follows this pattern:

  1. Navigate: bl open <url>
  2. Map: bl map (get element refs like @e1, @e2)
  3. Interact: Use refs to click, fill, select — e.g. bl click @e1
  4. Re-map: After navigation or DOM changes, get fresh refs with bl map

Binary Resolution

Before running any commands, resolve the bl binary path once:

  1. Try bl directly — works if it's on your PATH (the normal install).
  2. Fall back to ./target/release/bl — a local release build in the project root.

Run bl --help (or the resolved path) to confirm it works, then use that path for every subsequent command.

Windows note: the binary is bl.exe. Use forward slashes in paths and quote any path that contains spaces.

Command Chaining

Chain commands with && to run them sequentially. The chain stops on first error:

bl open https://example.com && bl map && bl click @e3 && bl diff map

When to chain: Use && for sequences that should happen back-to-back (navigate → interact → verify). Run commands separately when you need to inspect output between steps.

When NOT to chain: Don't chain commands that depend on parsing the previous output (e.g. reading map output to decide what to click). Run those separately so you can analyze the result first.

Commands

Organized top-down along the containment hierarchy — a browser hosts sessions, a session holds tabs, a tab shows a page, a page contains frames and elements — followed by the scoped capabilities (dialogs, capture, state, emulation) and utilities.

The browser process itself is managed automatically: it launches on the first browser command, stays warm between commands, and shuts itself down after an idle period. There is nothing to start by hand.

Browser

  • bl quit — close the browser entirely: every session ends and the background process stops. Use when the task is done and nothing should linger; the next browser command starts fresh. In shared environments prefer bl stop — quit ends other agents' sessions too.

Session

  • bl start — start or resume a local browser session (usually automatic)
  • bl start <url> — start connected to a remote browser
  • bl stop — stop the current session while keeping the shared browser warm
  • bl session new <name> — create a named session (isolated cookies/storage). --dialog accept|dismiss makes the browser answer native dialogs itself, so a click that opens one returns normally; the default ignore leaves the dialog open for bl dialog * but BLOCKS the triggering command until it is answered. Also covers beforeunload.
  • bl session list — list active sessions
  • bl session close <name> — close a named session
  • bl session reset <name> — reset a named session (fresh storage + tab; also clears emulation overrides)
  • bl session id — print a deterministic session name for the current directory (for parallel agents)

Browser commands accept --session <name> (after the subcommand) to run against a named session instead of the default one.

Tabs

  • bl tabs — list open tabs
  • bl tabs --tree — show each tab with its nested iframe hierarchy
  • bl tab new [url] — open a new tab
  • bl tab switch <index|url> — switch tab
  • bl tab close [index] — close tab

Page

  • bl open <url> — open a page (aliases: go, goto, navigate; bare bl open just launches a browser)
  • bl back — go back in history
  • bl forward — go forward in history
  • bl reload — reload the current page (--ignore-cache, --wait none|interactive|complete)
  • bl url — print current URL
  • bl title — print page title
  • bl text — get all page text
  • bl text "<selector>" — get text of a specific element
  • bl html — get page HTML (use --outer for outerHTML)
  • bl a11y-tree — accessibility tree (--everything for all nodes)
  • bl eval "<js>" — run JavaScript and print result (--stdin to read from stdin)
  • bl console list — list buffered console messages and page errors (--level, --text, --type)
  • bl console wait — wait for the next matching console/page-error entry (--timeout ms, filters)
  • bl console clear — clear the buffered console history for this session
  • bl wait "<selector>" — wait for element (--state visible|hidden|attached, --timeout ms)
  • bl wait url "<pattern>" — wait until URL contains substring (--timeout ms)
  • bl wait load — wait until page is fully loaded (--timeout ms)
  • bl wait text "<text>" — wait until text appears on page (--timeout ms)
  • bl wait fn "<expression>" — wait until JS expression returns truthy (--timeout ms)

Use bl console list / bl console wait to observe console.log / console.error and uncaught page errors without digging into raw BiDi subscriptions.

Frames

  • bl frames — list all iframes on the page
  • bl frame "<nameOrUrl>" — find a frame by name or URL substring

Elements

Find & map

  • bl map — map interactive elements with @refs (recommended before interacting)
  • bl map --selector "nav" — scope map to elements within a CSS subtree
  • bl find "<selector>" — find element, return @e1 ref (clickable with bl click @e1)
  • bl find "<selector>" --all — find all matching elements → @e1, @e2, ... (--limit N)
  • bl find text "Sign In" — find element by text content → @e1
  • bl find label "Email" — find input by label → @e1
  • bl find placeholder "Search" — find by placeholder → @e1
  • bl find testid "submit-btn" — find by data-testid → @e1
  • bl find xpath "//div[@class]" — find by XPath → @e1
  • bl find alt "Logo" — find by alt attribute → @e1
  • bl find title "Settings" — find by title attribute → @e1
  • bl find role <role> — find element by ARIA role → @e1 (--name for accessible name filter)
  • bl count "<selector>" — count matching elements
  • bl highlight "<selector>" — highlight element visually (3 seconds; visual debugging)

Act

  • bl click "<selector>" — click an element (also accepts @ref from map)
  • bl dblclick "<selector>" — double-click an element
  • bl type "<selector>" "<text>" — type into an input (appends to existing value)
  • bl fill "<selector>" "<text>" — clear field and type new text (replaces value)
  • bl press <key> [selector] — press a key on element or focused element
  • bl focus "<selector>" — focus an element
  • bl hover "<selector>" — hover over an element
  • bl scroll [direction] — scroll page (--amount N, --selector)
  • bl scroll into-view "<selector>" — scroll element into view (centered)
  • bl select "<selector>" "<value>" — pick a dropdown option
  • bl check "<selector>" — check a checkbox/radio (idempotent)
  • bl uncheck "<selector>" — uncheck a checkbox (idempotent)
  • bl upload "<selector>" <files...> — set files on input[type=file]

Keyboard primitives

  • bl keyboard type "<text>" — type at the current focus with key events
  • bl keyboard inserttext "<text>" — insert at the current focus without key events
  • bl keyboard down <key> — press and hold a key
  • bl keyboard up <key> — release a held key

Mouse primitives

  • bl mouse click [x] [y] — click at coordinates or current position (--button 0|1|2)
  • bl mouse move <x> <y> — move mouse to coordinates
  • bl mouse down — press mouse button (--button 0|1|2)
  • bl mouse up — release mouse button (--button 0|1|2)
  • bl drag "<source>" "<target>" — drag from one element to another

Element state

  • bl value "<selector>" — get input/textarea/select value
  • bl attr "<selector>" "<attribute>" — get HTML attribute value
  • bl is visible "<selector>" — check if element is visible (true/false)
  • bl is enabled "<selector>" — check if element is enabled (true/false)
  • bl is checked "<selector>" — check if checkbox/radio is checked (true/false)
  • bl is actionable "<selector>" — print an actionability breakdown (Visible, Stable, ReceivesEvents, Enabled, Editable)

Assertions

Each expect prints PASS … and exits 0 when the assertion holds, or prints the actual value and exits 1 — use it to verify a flow reached the expected state.

  • bl expect url contains|equals "<text>" — assert the current URL
  • bl expect title contains|equals "<text>" — assert the page title
  • bl expect text contains|equals "<text>" — assert page text (--selector to scope to one element)
  • bl expect visible "<selector>" — assert element is visible
  • bl expect hidden "<selector>" — assert element is absent or not visible
  • bl expect enabled "<selector>" — assert element is enabled
  • bl expect checked "<selector>" — assert checkbox/radio is checked (--not for unchecked)
  • bl expect value "<selector>" contains|equals "<text>" — assert a form element's value
  • bl expect count "<selector>" <n> — assert the number of matching elements
  • bl expect js "<expression>" — assert a JS expression is truthy

Dialogs

  • bl dialog wait — wait for a dialog to open, print its type/message (--timeout ms)

    Note: with the default ignore handler the command that OPENS a dialog blocks until it is answered, so drive it from a second command, or create the session with bl session new <name> --dialog accept to have the browser answer automatically.

  • bl dialog info — show the currently open dialog, or "No dialog open"

  • bl dialog accept [text] — accept dialog (optionally with prompt text)

  • bl dialog dismiss — dismiss dialog

When the dialog's content matters (e.g. deciding whether to confirm), inspect it first: bl dialog wait (or bl dialog info if it's already open) reports the type (alert/confirm/prompt/beforeunload), message, and prompt default value — then accept or dismiss.

Capture

  • bl screenshot -o file.png — capture screenshot (--full-page, --annotate)
  • bl pdf -o file.pdf — save page as PDF (--background, --orientation portrait|landscape, --scale, --no-shrink-to-fit, --margin[-top|-right|-bottom|-left] cm, --page-width/--page-height cm, repeatable --page-range)
  • bl record start — start recording (--screenshots, --snapshots, --name)
  • bl record stop — stop recording and save ZIP (-o path)
  • bl screencast start — start a native BiDi video recording of the page (--mime-type, --width, --height, --frame-rate, --audio); requires browser support, one active screencast per session
  • bl screencast stop — stop it and print the browser-written video file path (-o path copies the file there; the source file is kept)
  • bl diff map — compare current vs last map (see what changed)
  • bl diff snapshot — compare current a11y snapshot vs last (structure/text changes; --baseline <file> to diff against a saved snapshot)
  • bl diff screenshot --baseline <file> — pixel-compare the page against a baseline image (visual regression)
  • bl diff url <url1> <url2> — snapshot-diff two pages (e.g. prod vs staging; browser ends on url2)

State

  • bl cookies — list all cookies
  • bl cookies <name> <value> — set a cookie
  • bl cookies clear — clear all cookies
  • bl storage save <name> — snapshot the session's cookies + localStorage + sessionStorage
  • bl storage load <name> — restore a saved snapshot into the session
  • bl storage list / bl storage show <name> — list snapshots / show redacted metadata
  • bl storage delete <name> — remove a snapshot
  • bl storage export <name> -o <path> / bl storage import <path> --name <name> — move snapshots between machines
  • bl download dir <path> — set download directory
  • bl download wait [--timeout <ms>] — wait for the next download to finish

Emulation

  • bl viewport — get current viewport dimensions
  • bl viewport <width> <height> — set viewport size (--dpr for device pixel ratio)
  • bl viewport reset — reset viewport size and device pixel ratio overrides
  • bl media — override what matchMedia() reports (--color-scheme, --reduced-motion, --forced-colors, --contrast, --media). Note this does NOT re-evaluate CSS: @media rules and getComputedStyle are unaffected, and the override is lost on navigation. Use it for pages whose theming reads matchMedia() in JS; it cannot verify a print stylesheet.
  • bl geolocation <lat> <lng> — override geolocation (--accuracy); negative longitudes work unquoted, e.g. bl geolocation 37.7749 -122.4194
  • bl emulate environment — set session-scoped browsing environment overrides (--locale, --timezone, --user-agent, --offline, --no-scripting); flags can be combined and new tabs inherit them
  • bl emulate reset <locale|timezone|user-agent|network|scripting|environment> — clear one override or restore the whole environment; bl session reset <name> also clears these overrides

Utilities

  • bl sleep <ms> — pause execution (max 30000ms; last resort — prefer bl wait)

Common Patterns

bl open https://example.com
bl map
bl click @e1
bl map  # re-map after interaction

Verify action worked

bl map
bl click @e3
bl diff map  # see what changed

Read a page

bl open https://example.com && bl text

Fill a form (end-to-end)

bl open https://example.com/login
bl map
# Look at map output to identify form fields
bl fill @e1 "user@example.com"
bl fill @e2 "secret"
bl click @e3
bl wait url "/dashboard"
bl screenshot -o after-login.png

Assert a flow passed (exit-code testing)

bl open https://app.example.com/login
bl fill "input[name=email]" "user@example.com"
bl fill "input[name=password]" "secret"
bl click "button[type=submit]"
bl wait url "/dashboard"
bl expect url contains "/dashboard"
bl expect text contains "Welcome" --selector "h1"
bl expect hidden ".error"
# each expect exits 0 on pass / 1 on failure, so a && chain (or a CI script) stops at the first failed assertion

Scoped map (large pages)

bl map --selector "nav"        # Only map elements in <nav>
bl map --selector "#sidebar"   # Only map elements in #sidebar
bl map --selector "form"       # Only map form controls

Semantic find (no CSS selectors needed)

bl find text "Sign In"             # → @e1 [button] "Sign In"
bl find label "Email"              # → @e1 [input] placeholder="Email"
bl click @e1                       # Click the found element
bl find placeholder "Search..."    # → @e1 [input] placeholder="Search..."
bl find testid "submit-btn"        # → @e1 [button] "Submit"
bl find alt "Company logo"         # → @e1 [img] alt="Company logo"
bl find title "Close"              # → @e1 [button] title="Close"
bl find xpath "//a[@href='/about']"  # → @e1 [a] "About"

Authentication with state persistence

# Log in once and save state
bl open https://app.example.com/login
bl fill "input[name=email]" "user@example.com"
bl fill "input[name=password]" "secret"
bl click "button[type=submit]"
bl wait url "/dashboard"
bl storage save auth

# Restore in a later session (skips login)
bl storage load auth
bl open https://app.example.com/dashboard

Extract structured data

bl open https://example.com
bl eval "JSON.stringify([...document.querySelectorAll('a')].map(a => ({text: a.textContent.trim(), href: a.href})))"

Check page structure without rendering

bl open https://example.com && bl a11y-tree

Remote browser

bl start ws://remote-host:9515/session
bl open https://example.com
bl map
bl stop

Multi-tab workflow

bl tab new https://docs.example.com
bl text "h1"
bl tab switch 0

Annotated screenshot

bl screenshot -o annotated.png --annotate

Inspect an element

bl attr "a" "href"
bl value "input[name=email]"
bl is visible ".modal"

Save as PDF

bl open https://example.com && bl pdf -o page.pdf
bl pdf -o report.pdf --orientation landscape --background
bl pdf -o invoice.pdf --page-range 1 --margin 0.5

Eval / JavaScript

bl eval is the escape hatch for any DOM query or mutation the CLI doesn't cover directly.

Simple expressions — use single quotes:

bl eval 'document.title'
bl eval 'document.querySelectorAll("li").length'

Complex scripts — use --stdin with a heredoc:

bl eval --stdin <<'EOF'
const rows = [...document.querySelectorAll('table tbody tr')];
JSON.stringify(rows.map(r => {
  const cells = r.querySelectorAll('td');
  return { name: cells[0].textContent.trim(), price: cells[1].textContent.trim() };
}));
EOF

JSON output — use --json to get machine-readable output:

bl eval --json 'JSON.stringify({url: location.href, title: document.title})'

Important: eval returns the expression result. If your script doesn't return a value, you'll get null. Always make sure the last expression evaluates to the data you want.

Timeouts and Waiting

All interaction commands (click, fill, type, etc.) auto-wait for the target element to be actionable. You usually don't need explicit waits.

Use explicit waits when:

  • Waiting for navigation: bl wait url "/dashboard" — after clicking a link that navigates
  • Waiting for content: bl wait text "Success" — after form submission, wait for confirmation
  • Waiting for element: bl wait ".modal" — wait for a modal to appear
  • Waiting for page load: bl wait load — after navigation to a slow page
  • Waiting for JS condition: bl wait fn "window.appReady === true" — wait for app initialization
  • Fixed delay (last resort): bl sleep 2000 — only when no better signal exists (max 30s)

All wait commands accept --timeout <ms> (default varies by command).

Ref Lifecycle

Refs (@e1, @e2) are invalidated when the page changes. Always re-map after:

  • Clicking links or buttons that navigate
  • Form submissions
  • Dynamic content loading (dropdowns, modals)

Flags

Per-command flags go AFTER the subcommand (bl map --json works; bl --json map is rejected). The one exception is -v, --verbose — the only true global flag, valid anywhere in the invocation.

FlagScopeDescription
-v, --verboseGlobal — valid on any commandDebug logging
--jsonPer-command, after the subcommandOutput as JSON

--json is absent on the few setup commands that emit no result (completion, mcp, add-skill).

bl map --json

Tips

  • All click/type/hover/fill actions auto-wait for the element to be actionable
  • All selector arguments also accept @ref from bl map
  • Use bl map before interacting to discover interactive elements
  • Use bl map --selector to reduce noise on large pages
  • Use bl fill to replace a field's value, bl type to append to it
  • Use bl find text / find label / find testid for semantic element lookup (more reliable than CSS selectors)
  • Use bl find role for ARIA-role-based lookup
  • Use bl a11y-tree to understand page structure without visual rendering
  • Use bl text "<selector>" to read specific sections
  • Use bl diff map after interactions to see what changed; bl diff snapshot catches text/structure changes map can't see
  • Use bl expect to turn a page check into a pass/fail exit code — wait blocks until a state arrives, expect verdicts the state now
  • bl eval is the escape hatch for complex DOM queries
  • bl check/bl uncheck are idempotent — safe to call without checking state first
  • Screenshots save to the current directory by default (-o to change)
  • Use bl storage save / bl storage load to persist auth across sessions

On this page