From safe-browser
Migrates browser-use (Python) browser-automation scripts to Stagehand v3 (TypeScript) on Browserbase, mapping agentic loops to deterministic primitives.
How this skill is triggered — by the user, by Claude, or both
Slash command
/safe-browser:browser-use-to-stagehandThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Convert a browser-use (Python) script into an idiomatic **Stagehand v3 (TypeScript)** script on
/browser-use-to-stagehand)Convert a browser-use (Python) script into an idiomatic Stagehand v3 (TypeScript) script on Browserbase, choosing the right level of determinism at each step rather than producing a one-to-one agentic copy.
Core principle: browser-use is agentic-by-default (the LLM decides every action). Stagehand lets you choose how much AI to use. A good migration replaces opaque agent loops with an inspectable, mostly-deterministic pipeline — using AI only where the page is genuinely unpredictable. This is a refactor with judgment, not a transpile.
Source of truth & versions. This skill's durable value is the judgment — the determinism spectrum and the decompose-vs-agent decision — not the API specifics, which drift every release. The code mappings here are a snapshot validated against
@browserbasehq/stagehand3.6.x and browser-use 0.13.x (2026-06). On any conflict, the live docs win — always verify against the installed package and these sources before emitting code:
- Stagehand v3: https://docs.stagehand.dev/v3 · installed types:
node_modules/@browserbasehq/stagehand- Browserbase: https://docs.browserbase.com
- browser-use: https://docs.browser-use.com
If the installed Stagehand major is not 3, treat this skill as conceptual only and follow the live docs for every signature.
references/api-mapping.md — the mechanical browser-use → Stagehand
mapping: variant detection, the full feature table, before/after code, Browserbase platform
options, and v3 version gotchas. Read this for any non-trivial construct.references/determinism.md — how to choose agent() vs
act/extract/observe vs cached observe→act. The decision tree. Read this when deciding
how to translate an Agent(task=…).references/trace-assisted.md — the optional "run it on
Browserbase, read the logs, then rewrite" workflow for opaque/flaky scripts.references/guide.md — the human migration guide: philosophy shift,
feature mapping, the determinism spectrum, and a recommended migration path.references/prompt.md — a self-contained, tool-agnostic version of this
skill; paste it into any AI assistant along with a browser-use script.EXAMPLES.md — before/after script pairs.Obtain the browser-use script(s). If the user only described a script, ask for the file(s). Note the target: TypeScript Stagehand on Browserbase unless they say otherwise.
First, gate on scope — is this even migratable? Not every browser-use file is an
Agent(task=…)script. If the source is browser-use running as an MCP server (uvx browser-use --mcp, amcpServersconfig) there is no Stagehand equivalent — flag it as out of scope, don't invent one (see api-mapping §3.7b). If the browser-use call is embedded in a larger app (a class/tool wrapper, web route, queue task), convert only the browser-use surface and preserve the surrounding app glue — see api-mapping §3.8.
Identify legacy (pre-0.12) vs stable vs Rust beta (only when imports come from browser_use.beta)
— see api-mapping §1. Note: the classic top-level from browser_use import Agent, ChatBrowserUse
surface is alive and well in 0.13.x — ChatBrowserUse alone is not a beta tell; only a
browser_use.beta import is. All variants translate identically, so when unsure, proceed with the
stable mapping. Normalize legacy names before translating. State which variant you found.
Extract a structured inventory before writing any TypeScript:
task= string(s); split each into its implied ordered steps.Chat* provider + model id.cdp_url/Browserbase; headless; proxies; user_data_dir/storage_state.output_model_schema Pydantic models.sensitive_data, env-var usage, login flows.allowed_domains, max_steps.@tools.action / Controller functions, and whether each is a deterministic
side-effect or an agent capability.initial_actions, secondary models (page_extraction_llm, planner_llm).For each step from the inventory, apply the decision tree in determinism.md:
page.goto(url) on the Stagehand page (no AI).act("…"); if it repeats, observe() once then replay act(action) (no LLM call).extract("…", zodSchema).stagehand.agent().execute(...) (tightened with maxSteps/systemPrompt).Default to decomposition when the flow is known; keep agent() only where it isn't. For a
first lift-and-shift, a faithful agent() translation is acceptable — say so and note the
optimization path.
First, verify the API. Before writing, confirm the exact signatures you're about to use against
the installed package (node_modules/@browserbasehq/stagehand types) or https://docs.stagehand.dev/v3.
The mappings below are a 3.6.x snapshot; if anything differs in the installed version, the installed
version wins. Then emit runnable TypeScript. Always:
import { Stagehand } from "@browserbasehq/stagehand"; and import { z } from "zod"; when extracting.const page = stagehand.context.pages()[0];.stagehand.act(...), stagehand.extract(...),
stagehand.observe(...) — never page.act(...)."provider/model" string.env: "BROWSERBASE"; show env: "LOCAL" as the dev option.variables and process.env, never hardcoded.await stagehand.init() at the start, await stagehand.close() in a finally.Include the project setup so it runs (see the templates below).
Alongside the code, produce a short summary:
allowed_domains guardrails,
custom-action logic, secondary-model intent, ambiguous task strings.If the source was one large opaque agent(task=…), was flaky, or your rewrite can't be confidently
mapped, offer the trace-assisted workflow (trace-assisted.md): run the original on Browserbase, pull
sessions.logs.list, and rewrite from observed behavior. Don't run anything without the user's go-ahead.
package.json
{
"name": "stagehand-migration",
"type": "module",
"scripts": { "start": "tsx index.ts" },
"dependencies": {
"@browserbasehq/stagehand": "^3.0.0",
"dotenv": "^16.0.0",
"zod": "^3.25.0"
},
"devDependencies": { "tsx": "^4.0.0", "typescript": "^5.0.0" }
}
Add
"ai": "^5.0.0"(Vercel AI SDK) only if a custom browser-use action maps to an agenttool. Pin v5, not v4 — Stagehand 3.6.x bundlesaiv5 and typesagent({ tools })as the v5ToolSet, where a tool's schema field isinputSchema. The v4tool()helper emitsparametersinstead and will fail to type-check against Stagehand's v5ToolSet. If you can't control the hoistedaiversion, skip thetool()helper and pass a plain object{ description, inputSchema: zodSchema, execute }— it satisfies the v5ToolSetregardless of whichaimajor resolves.
.env
BROWSERBASE_API_KEY=...
BROWSERBASE_PROJECT_ID=...
ANTHROPIC_API_KEY=... # or the provider matching your model string
index.ts skeleton (decomposed, the preferred shape)
import "dotenv/config";
import { Stagehand } from "@browserbasehq/stagehand";
import { z } from "zod";
async function main() {
const stagehand = new Stagehand({
env: "BROWSERBASE",
model: "anthropic/claude-sonnet-4-6",
});
await stagehand.init();
try {
const page = stagehand.context.pages()[0];
await page.goto("https://example.com"); // deterministic skeleton
await stagehand.act("…"); // AI where the page varies
const data = await stagehand.extract("…", z.object({ /* … */ })); // structured reads
console.log(data);
} finally {
await stagehand.close();
}
}
main().catch((err) => { console.error(err); process.exit(1); });
stagehand.act/extract/observe), not the page.stagehand.context.pages()[0]."provider/model" string; the matching provider key is in .env.extract uses a zod schema; zod is in dependencies.variables + process.env; nothing hardcoded.init() / close() present; close() in finally.page.act(), stagehand.page, modelName/modelClientOptions,
enableCaching) from old blog posts. Use v3 — see api-mapping "Version notes".act() — navigate with page.goto and cache repeatable steps via observe→act; don't spend an LLM call on every action.agent() — that just reproduces browser-use's non-determinism in a
new framework. Decompose where the flow is known.allowed_domains — Stagehand has no domain firewall; flag it for review.npx claudepluginhub browserbase/skills --plugin browseBuilds reliable browser automation skills through iterative testing — runs a task, reads the trace, and improves the strategy until it passes. Supports parallel multi-task runs via sub-agents.
Automates E2E testing and browser interactions with qa-use CLI. Create sessions, navigate pages, snapshot DOM for element refs, perform clicks/fills, and debug test failures.
Automates browser tasks with Playwright: tests pages, fills forms, screenshots, responsive design, UX validation, login flows, link checks. Auto-detects dev servers, runs visible browser.