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 mapCore Workflow
Every browser automation follows this pattern:
- Navigate:
bl open <url> - Map:
bl map(get element refs like@e1,@e2) - Interact: Use refs to click, fill, select — e.g.
bl click @e1 - 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:
- Try
bldirectly — works if it's on yourPATH(the normal install). - 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 mapWhen 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 preferbl 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 browserbl stop— stop the current session while keeping the shared browser warmbl session new <name>— create a named session (isolated cookies/storage).--dialog accept|dismissmakes the browser answer native dialogs itself, so a click that opens one returns normally; the defaultignoreleaves the dialog open forbl dialog *but BLOCKS the triggering command until it is answered. Also coversbeforeunload.bl session list— list active sessionsbl session close <name>— close a named sessionbl 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 tabsbl tabs --tree— show each tab with its nested iframe hierarchybl tab new [url]— open a new tabbl tab switch <index|url>— switch tabbl tab close [index]— close tab
Page
bl open <url>— open a page (aliases:go,goto,navigate; barebl openjust launches a browser)bl back— go back in historybl forward— go forward in historybl reload— reload the current page (--ignore-cache,--wait none|interactive|complete)bl url— print current URLbl title— print page titlebl text— get all page textbl text "<selector>"— get text of a specific elementbl html— get page HTML (use--outerfor outerHTML)bl a11y-tree— accessibility tree (--everythingfor all nodes)bl eval "<js>"— run JavaScript and print result (--stdinto 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 sessionbl 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 pagebl 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 subtreebl find "<selector>"— find element, return@e1ref (clickable withbl click @e1)bl find "<selector>" --all— find all matching elements →@e1,@e2, ... (--limit N)bl find text "Sign In"— find element by text content →@e1bl find label "Email"— find input by label →@e1bl find placeholder "Search"— find by placeholder →@e1bl find testid "submit-btn"— find by data-testid →@e1bl find xpath "//div[@class]"— find by XPath →@e1bl find alt "Logo"— find by alt attribute →@e1bl find title "Settings"— find by title attribute →@e1bl find role <role>— find element by ARIA role →@e1(--namefor accessible name filter)bl count "<selector>"— count matching elementsbl highlight "<selector>"— highlight element visually (3 seconds; visual debugging)
Act
bl click "<selector>"— click an element (also accepts@reffrom map)bl dblclick "<selector>"— double-click an elementbl 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 elementbl focus "<selector>"— focus an elementbl hover "<selector>"— hover over an elementbl scroll [direction]— scroll page (--amount N,--selector)bl scroll into-view "<selector>"— scroll element into view (centered)bl select "<selector>" "<value>"— pick a dropdown optionbl 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 eventsbl keyboard inserttext "<text>"— insert at the current focus without key eventsbl keyboard down <key>— press and hold a keybl 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 coordinatesbl 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 valuebl attr "<selector>" "<attribute>"— get HTML attribute valuebl 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 URLbl expect title contains|equals "<text>"— assert the page titlebl expect text contains|equals "<text>"— assert page text (--selectorto scope to one element)bl expect visible "<selector>"— assert element is visiblebl expect hidden "<selector>"— assert element is absent or not visiblebl expect enabled "<selector>"— assert element is enabledbl expect checked "<selector>"— assert checkbox/radio is checked (--notfor unchecked)bl expect value "<selector>" contains|equals "<text>"— assert a form element's valuebl expect count "<selector>" <n>— assert the number of matching elementsbl 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
ignorehandler the command that OPENS a dialog blocks until it is answered, so drive it from a second command, or create the session withbl session new <name> --dialog acceptto 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 sessionbl screencast stop— stop it and print the browser-written video file path (-o pathcopies 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 cookiesbl cookies <name> <value>— set a cookiebl cookies clear— clear all cookiesbl storage save <name>— snapshot the session's cookies + localStorage + sessionStoragebl storage load <name>— restore a saved snapshot into the sessionbl storage list/bl storage show <name>— list snapshots / show redacted metadatabl storage delete <name>— remove a snapshotbl storage export <name> -o <path>/bl storage import <path> --name <name>— move snapshots between machinesbl download dir <path>— set download directorybl download wait [--timeout <ms>]— wait for the next download to finish
Emulation
bl viewport— get current viewport dimensionsbl viewport <width> <height>— set viewport size (--dprfor device pixel ratio)bl viewport reset— reset viewport size and device pixel ratio overridesbl media— override whatmatchMedia()reports (--color-scheme,--reduced-motion,--forced-colors,--contrast,--media). Note this does NOT re-evaluate CSS:@mediarules andgetComputedStyleare unaffected, and the override is lost on navigation. Use it for pages whose theming readsmatchMedia()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.4194bl emulate environment— set session-scoped browsing environment overrides (--locale,--timezone,--user-agent,--offline,--no-scripting); flags can be combined and new tabs inherit thembl 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 — preferbl wait)
Common Patterns
Ref-based workflow (recommended for AI)
bl open https://example.com
bl map
bl click @e1
bl map # re-map after interactionVerify action worked
bl map
bl click @e3
bl diff map # see what changedRead a page
bl open https://example.com && bl textFill 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.pngAssert 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 assertionScoped 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 controlsSemantic 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/dashboardExtract 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-treeRemote browser
bl start ws://remote-host:9515/session
bl open https://example.com
bl map
bl stopMulti-tab workflow
bl tab new https://docs.example.com
bl text "h1"
bl tab switch 0Annotated screenshot
bl screenshot -o annotated.png --annotateInspect 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.5Eval / 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() };
}));
EOFJSON 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.
| Flag | Scope | Description |
|---|---|---|
-v, --verbose | Global — valid on any command | Debug logging |
--json | Per-command, after the subcommand | Output as JSON |
--json is absent on the few setup commands that emit no result (completion, mcp, add-skill).
bl map --jsonTips
- All click/type/hover/fill actions auto-wait for the element to be actionable
- All selector arguments also accept
@reffrombl map - Use
bl mapbefore interacting to discover interactive elements - Use
bl map --selectorto reduce noise on large pages - Use
bl fillto replace a field's value,bl typeto append to it - Use
bl find text/find label/find testidfor semantic element lookup (more reliable than CSS selectors) - Use
bl find rolefor ARIA-role-based lookup - Use
bl a11y-treeto understand page structure without visual rendering - Use
bl text "<selector>"to read specific sections - Use
bl diff mapafter interactions to see what changed;bl diff snapshotcatches text/structure changesmapcan't see - Use
bl expectto turn a page check into a pass/fail exit code —waitblocks until a state arrives,expectverdicts the state now bl evalis the escape hatch for complex DOM queriesbl check/bl uncheckare idempotent — safe to call without checking state first- Screenshots save to the current directory by default (
-oto change) - Use
bl storage save/bl storage loadto persist auth across sessions