Agent recipes
A playbook for driving browserlane from an AI agent — the map/act/re-map loop, ref hygiene, token-efficient reading, and verification.
This is the field guide for an agent driving browserlane — whether you call the
bl CLI directly or go through the MCP server. The commands below are written as
CLI verbs, but each one corresponds to an MCP tool (for example bl map →
browser_map, bl go → browser_navigate). If you're connected over MCP, use
the tool; the patterns are identical. See the
CLI ↔ MCP mapping for the full correspondence
and the MCP reference for every tool's parameters.
The core loop: map → act → re-map
Almost every task is this loop. Don't act blind — discover the page first, act on what you found, then re-discover after the page changes.
- Map —
bl maplists interactive elements as refs (@e1,@e2, …). - Act — use a ref:
bl click @e1,bl fill @e2 "text". - Re-map — after the page changes,
bl mapagain for fresh refs.
bl go https://example.com
bl map # → @e1, @e2, ...
bl click @e1
bl map # re-map: the previous refs no longer applyRef hygiene: refs go stale
Refs point at a snapshot of the page. They are invalidated by navigation, form submission, and dynamic content (dropdowns, modals, client-side renders). The single most common agent mistake is reusing a ref across a page change.
Re-map after anything that changes the page
Always run bl map again after: clicking a link or button that navigates,
submitting a form, or any interaction that loads or replaces content. A ref
from before the change may now point at the wrong element — or nothing.
CSS selectors and bl find are re-evaluated every time you use them, so they
don't go stale the way refs do. When a page is volatile, preferring bl find /
selectors over cached refs is more robust.
Chaining: && vs separate commands
Use && to run a known sequence back-to-back; the chain stops on the first
error:
bl go https://example.com && bl map && bl click @e1 && bl diff mapDon't chain a command whose next step depends on reading the output. If you
need to look at bl map to decide which ref to click, run it on its own, read
the result, then issue the click. Chaining past a decision point means acting on
guesses.
- Chain: navigate → act → verify, when each step is predetermined.
- Separate: anything where you must parse output before the next move (reading a map, extracting data, branching on page state).
Read token-efficiently
Pulling the entire page is expensive and noisy. Read the minimum that answers the question:
bl text "main" # text of one region, not the whole page
bl map --selector "nav" # map only the elements inside <nav>
bl a11y-tree # compact structural view, no visual renderingbl text "<selector>"scopes reading to one element — far cheaper thanbl texton the whole document.bl map --selector "<css>"cuts a large page's map down to the subtree you care about (form,#sidebar,nav).bl a11y-treegives you the page's structure and roles without the markup — often the cheapest way to understand a page's shape.- Prefer
bl count "<selector>"orbl attrwhen you need one number or one attribute rather than a block of text.
Prefer semantic bl find for reliability
When you can describe an element the way a person would, bl find is more robust
than a brittle CSS selector — and it returns a ref you act on like any other:
bl find text "Sign In" # → @e1 [button] "Sign In"
bl find label "Email" # → @e1 [input]
bl find role button --name "Submit"
bl click @e1bl find also matches placeholder, testid, alt, title, and xpath. Reach
for it before resorting to a hand-built CSS selector.
Verify your actions worked
Don't assume an action succeeded — check. bl diff map shows what changed
between the last map and now, which is the quickest confirmation that a click did
something:
bl map
bl click @e3
bl diff map # see exactly what changed on the pageOther cheap verifications:
bl wait url "/dashboard" # confirm a navigation actually happened
bl wait text "Success" # confirm a confirmation message appeared
bl is checked "#terms" # confirm a checkbox is in the expected state
bl value "input[name=q]" # read a field back to confirm it filledbl eval is the escape hatch
When no command fits — a complex DOM query, a computed value, a bit of
mutation — drop into JavaScript with bl eval. Make the final expression the
value you want back:
bl eval "document.querySelectorAll('li').length"
bl eval "JSON.stringify([...document.querySelectorAll('a')].map(a => ({ text: a.textContent.trim(), href: a.href })))"For longer scripts, pipe them in with --stdin to dodge shell-quoting issues —
see Scrape structured data for the heredoc
form.
Persist auth instead of logging in every run
Logging in on every task is slow and flaky. Authenticate once, save the state, and restore it next time:
# Once: log in, then snapshot cookies + localStorage + sessionStorage
bl go 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 -o auth.json
# Later: restore and skip the login entirely
bl storage restore auth.json
bl go https://app.example.com/dashboardSee Persist a login for the full workflow and the security caveats around the state file.
A complete recipe, end to end
Navigate, read to decide, act, and verify — with re-maps at every page change:
bl go https://app.example.com/search
bl find label "Search" # → @e1
bl fill @e1 "wireless headphones"
bl keys Enter
bl wait text "results" # results loaded
bl map # re-map: the page changed
# read the map output, pick the first result's ref, then:
bl click @e1
bl wait load
bl diff map # confirm we landed on a product page
bl text "h1" # read the product titleRelated
- CLI ↔ MCP mapping — which MCP tool each
blverb corresponds to. - MCP reference — every tool and its parameters.
- Wire up an agent (MCP) — point your agent at the MCP server.
- Selectors vs @refs & the map → act → re-map loop and Auto-waiting vs. explicit waits.