Skip to main content
ZICQ

Skills ZICQ category:Agent Workflows browser-control

Browser Control

Drive the user's existing Chromium-family browser with deterministic Playwright. Use when asked to inspect, automate, test, or interact with a visible browser tab; continue an authenticated browser workflow; handle 2FA, passkeys, CAPTCHAs, or payment confirmation; record browser behavior; or capture an authenticated network flow.

732 installs

Official URL:skills.sh

What this skill does

Intro in this page language first. The official description stays in its original wording; we do not rewrite SKILL.md.

What it does

Drive the user's existing Chromium-family browser with deterministic Playwright

When to use it

asked to inspect, automate, test, or interact with a visible browser tab; continue an authenticated browser workflow; handle 2FA, passkeys, CAPTCHAs, or payment confirmation; record browser behavior; or capture an authenticated network flow

How agents load it

Per Agent Skills progressive disclosure: name and description load at startup (~100 tokens); the full SKILL.md body loads when the skill activates; scripts/, references/, and assets/ load only as needed. This file's sections: Browser Control; Core Workflow; 1. Run The Task Directly; 2. Choose The Page Deliberately; 3. Inspect, Act, Verify; 4. Continue Or Finish Cleanly. It includes spec-recommended sections: step-by-step instructions.

File analysis

File analysis: instruction-only skill (SKILL.md). The agent loads the full body when activated.

Browser ControlCore Workflow1. Run The Task Directly2. Choose The Page Deliberately3. Inspect, Act, Verify4. Continue Or Finish CleanlyCanonical Authenticated FlowPassword Manager PromptsInspection ToolsExecute InterfaceSafetyTypeScript Client

Source category:skills.sh agent-skill

SKILL.md & Agent activation

Official spec ↗
name
browser-control
description
Drive the user's existing Chromium-family browser with deterministic Playwright. Use when asked to inspect, automate, test, or interact with a visible browser tab; continue an authenticated browser workflow; handle 2FA, passkeys, CAPTCHAs, or payment confirmation; record browser behavior; or capture an authenticated network flow.
  1. DiscoverThe client exposes names and descriptions to the agent.
  2. ActivateYour request or the task context selects the skill and loads its instructions.
  3. Load resourcesReferenced scripts, documentation and assets are used when needed.

Invocation syntax and available tools depend on your Agent client. Client integration guide ↗

Install this skill

Skills CLI ↗

Choose the target agent and installation scope, keep referenced package files, then verify the skill appears in the client's catalog.

Ask your Agent to install

Copy these instructions to a compatible agent and confirm the target directory matches your client.

Install the agent skill "browser-control" into my project. The full SKILL.md and official description are at https://zicq.com/en/skills/skl-0549736a9762be33-Browser-Control.html
Save it as .cursor/skills/browser-control/SKILL.md or .claude/skills/browser-control/SKILL.md and keep the frontmatter name and description exactly as-is.

Full package on GitHub ↗

Install from the terminal · Skills CLI

Requires Node.js and npx. First inspect the repository's skill list to confirm the name.

npx skills add 'https://github.com/anomalyco/browser-control' --list

npx skills add 'https://github.com/anomalyco/browser-control' --skill 'browser-control'

The CLI lets you choose the agent interactively. The default scope is the project; use -g for user scope. Confirm package availability with the discovery command, then use npx skills list to inspect installed skills.

Readable layout
--- name: browser-control description: Drive the user's existing Chromium-family browser with deterministic Playwright. Use when asked to inspect, automate, test, or interact with a visible browser tab; continue an authenticated browser workflow; handle 2FA, passkeys, CAPTCHAs, or payment confirmation; record browser behavior; or capture an authenticated network flow. --- # Browser Control Browser Control is a **driver**, not an agent. The calling agent decides what to do; Browser Control runs deterministic Playwright code in the user's visible browser. Use one loop throughout: **inspect, act, verify**. Inspect the real page before choosing locators, act through the narrowest stable control, then verify the result through a URL or fresh page read. Never treat a successful click or human acknowledgment as proof that the task succeeded. ## Core Workflow ### 1. Run The Task Directly Start with the requested browser work. Relay-backed commands start the detached relay and wait for the extension; do not start `browser-control serve` first. ```bash browser-control execute 'return { url: page.url(), title: await page.title() }' ``` Use `browser-control doctor` only when setup or runtime behavior is unclear. `status` and `doctor` are observational and never start the relay. MCP startup, tool discovery, `skill`, and `session_current` do not contact the relay. The first operational tool call starts it if needed; relay-backed observational tools report unavailability instead of starting it. Ordinary CLI/MCP/SDK calls never replace a running relay. On a build mismatch, coordinate with other agents before running `browser-control relay restart`. It preserves browser tabs and durable sessions but resets JavaScript state and snapshot refs. A busy or timed-out drain leaves the old relay running; finish recordings/captures and disconnect raw CDP clients rather than forcing a stop. ```bash browser-control doctor browser-control status --json ``` Completion: one execute returns a page result and a readable session id, or `doctor` identifies the concrete setup failure. ### 2. Choose The Page Deliberately A bare CLI execute creates a fresh session-owned page and prints the exact `--session ` continuation command. Every later CLI call must pass that id or set `BROWSER_CONTROL_SESSION`; bare execute never guesses from human-shell current state. ```bash browser-control execute 'return page.url()' browser-control execute --session cosmic-otter-866 'return page.url()' ``` MCP keeps one implicit process session. Omit `session` for that normal path, or call `session_new` and pass an explicit id when one MCP process needs multiple sessions. To control a tab already open in the user's browser, ask the user to click the Browser Control toolbar button on that tab. Select it for one execute or adopt it for sticky reuse: ```bash browser-control execute --target-url github.com 'return page.url()' browser-control session adopt --target-url github.com --session github ``` `targetUrl` and `targetIndex` select existing attached pages; they never navigate. A URL selector must match exactly one page, and URL and index selectors cannot be combined. Adoption makes that tab the session default, closes the session's previous relay-created page, and is exclusive to one Browser Control session. Reset or delete releases an adopted user tab without closing it. Prefer adoption for authenticated browser state rather than reproducing login in a fresh page. Each relay controls one browser/profile at a time. A second extension connection cannot replace a healthy active connection. If `status` or `doctor` reports rejected competing connections, keep the extension enabled only in the intended browser/profile. To switch browsers, disconnect the incumbent extension first; creating a new execute session does not switch browsers. Completion: the selected page URL is the intended page, and later work either retains the returned session id or intentionally uses the MCP process session. ### 3. Inspect, Act, Verify Inspect before guessing roles or selectors: ```js return await snapshot() ``` Then act from the returned structure and verify the destination: ```js await ref("e12").click() await page.getByRole("heading", { name: "Settings" }).waitFor() if (!page.url().includes("/settings")) { throw new Error(`Unexpected destination: ${page.url()}`) } return { url: page.url(), heading: await page.getByRole("heading").first().innerText() } ``` Use normal Playwright first. Keep dependent interactions in one execute when they rely on transient UI such as an open menu, selected rows, hover state, or an in-progress form. If native `locator.fill()` hangs because a browser extension interferes with focus, use the explicit `input`/`textarea` fallback: ```js await fillInput(page.getByPlaceholder("Username"), "standard_user") ``` Completion: the final return value contains evidence of the requested outcome, not merely evidence that an action was attempted. ### 4. Continue Or Finish Cleanly Named sessions preserve their default page across short-lived CLI and MCP processes. They also survive relay restarts: Browser Control restores the id, read-only mode, and exact default target. JavaScript `state` and snapshot refs are process-local and reset after a relay restart with an explicit warning. ```bash browser-control session list browser-control session reset github browser-control session delete github ``` Deletion is idempotent for an explicit session id, so cleanup can be safely retried when that session is already absent. Every execute is journaled under `~/.browser-control/sessions//journal.jsonl`. The journal records code, status, duration, URL movement, warnings, handoffs, and bounded diagnostics. Never place credentials directly in execute source. ```bash browser-control journal --session github --limit 50 ``` Completion: retain the session only when follow-up work is expected; otherwise reset or delete session-owned pages and report any warnings that affect later work. ## Canonical Authenticated Flow The distinguishing Browser Control workflow is an authenticated tab plus a human-only prompt: Attach and adopt the existing tab, inspect its real UI, fill ordinary fields, then register `handoff` before triggering WebAuthn, 2FA, CAPTCHA, or payment UI. After the user completes it, verify the authenticated destination. The same session can continue after an MCP process or relay restart. When the prompt-triggering action may itself block, put only that action in `start`. Browser Control presents and acknowledges WAIT before invoking it: ```js await handoff("Complete the security-key prompt, then continue", { timeoutMs: 600_000, start: () => page .getByRole("button", { name: /passkey|security key|sign in/i }) .click({ timeout: 600_000 }), }) await page.waitForURL((url) => !url.pathname.startsWith("/login")) await page.getByRole("heading", { name: /account|dashboard/i }).waitFor() return { authenticatedUrl: page.url(), title: await page.title() } ``` After a resolved handoff, Browser Control waits through transient destination context replacement before returning, so this verification can remain in the same execute. For a handoff on another page, pass `{ page: otherPage }`. Readiness checks that page, not the session default. If a non-default page was replaced or closed, inspect the remaining pages rather than assuming an old Playwright reference now identifies its replacement. Tell the user what action is waiting. Human acknowledgment is not verification: always assert the expected URL or stable element after `handoff`. If the action was already completed and only the human step remains, call `handoff(message)` without `start`. The default timeout is ten minutes. Completion: the prompt was presented only after WAIT was registered, the action settled, and the authenticated result was independently verified. ### Password Manager Prompts Ordinary webpage fields and accessible open-shadow-root controls remain usable. 1Password's inline menus are extension-owned iframes, not ordinary webpage DOM. Chromium blocks one extension from debugging another extension's pages; toolbar popups and native unlock, Touch ID, and Windows Hello prompts are not supported Playwright control surfaces. `target/cross-extension-page` means a permission boundary. Ask the user to finish or dismiss the prompt and retry; do not reset the page, read vault contents, or weaken browser security to get around it. Register `handoff` on the originating webpage before triggering a human-only prompt when possible. If the prompt already prevents attachment, give the user the required action directly rather than assuming an in-page handoff can be displayed. Verify the intended webpage state after the prompt is completed. ## Inspection Tools Use the least expensive view that answers the question: - `snapshot()` is the compact read-before-act default. It prioritizes semantic groups, alerts, lists, tables, headings, links, and controls. Text input and textarea values are omitted. - `ref("e12")` resolves a control from the latest snapshot. Refs fail closed after navigation or incompatible DOM drift. - `snapshot({ diff: true })` reports semantic changes from the compatible prior baseline. A diff invalidates earlier refs and exposes refs only for added or changed current lines. - `ariaSnapshot(target?, { timeout })` returns Playwright's detailed YAML aria tree when the compact snapshot omits needed structure. Native text-control values, custom ARIA range values, and editable content are omitted so they do not enter tool output. Await it separately; do not run other operations on the same page concurrently. - `screenshotWithLabels({ page, path? })` adds visual labels and metadata when layout matters. - `screenshotDiff({ baseline, path?, threshold?, fullPage? })` compares a saved PNG (absolute path or Buffer) with the current session page at CSS-pixel scale. It returns `matches`, `changedPixels`, `changedRatio` (0..1), dimensions, and a red-highlighted PNG. Omit `path` to return the image as execute media; otherwise supply a fresh absolute `.png` path. Existing output files are never overwritten. ```js return await snapshot({ within: "main", maxItems: 200 }) // When layout matters, return the image through MCP so it can be inspected. return await screenshotWithLabels({ page }) ``` Saving an image and returning only `"ok"` proves file creation, not visual correctness. Return screenshot buffers through MCP when visual evidence matters. For visual regression checks, save a baseline before changing the UI: ```ts await page.screenshot({ path: "/absolute/before.png", scale: "css" }) // After the intended UI change, in the same viewport: return await screenshotDiff({ baseline: "/absolute/before.png" }) ``` The threshold defaults to 0.1 and controls per-pixel color tolerance, not the allowed changed area. Set it to 0 for exact pixels. Antialiasing changes count. Settle animations yourself and use the same viewport and `fullPage` setting for both captures. Dimension mismatches fail explicitly; images are never resized. Comparisons are limited to PNGs of 32 MiB / 16 megapixels each. Screenshots and diffs include visible page content: inspect for private information before sharing. ## Execute Interface Execute code can use `page`, `context`, `browser`, persistent `state`, selected Node modules through `modules` and aliases such as `fs` and `path`, plus the Browser Control helpers documented here. Single expressions auto-return; multi-statement scripts need `return`. Use `--file` for longer scripts: ```bash browser-control execute --session github --file ./perform-flow.js ``` Human CLI output includes logs, warnings, and a concise aftermath. Use `--json` when another command needs to branch on `ok`, `value`, `error`, `warnings`, or `aftermath`: ```bash browser-control execute --json --session github '({ url: page.url() })' | jq .value.url ``` Playwright downloads are unavailable through extension-backed tabs because Chromium blocks download artifact control through `chrome.debugger`. If the page exposes the payload through fetch or an API response, read the bytes in the page and write them with `fs`. Do not retry `page.waitForEvent("download")`. ## Safety Browser Control blocks CDP commands that would destroy shared browser state, including browser close and cookie/cache clearing. Never work around those guardrails. For inspect-only work, use a read-only session: ```bash browser-control session new inspect-prod --read-only browser-control execute --session inspect-prod 'await page.goto("https://example.com"); return page.title()' ``` Read-only sessions reject `Input.*`, so they cannot click or type through Playwright. `page.evaluate` can still mutate the DOM; read-only prevents trusted mistakes, not malicious code. For destructive UI work, use a two-phase **read, confirm, verify** flow: 1. Read candidates and return exact stable identifiers or row text. 2. Obtain user approval for those exact items. 3. Re-select only approved items and assert the selected count. 4. Read the confirmation dialog and throw unless it matches the approved action. 5. Confirm, then verify through a fresh page read or independent CLI/API path. Do not discover and confirm destructive candidates in one script unless the user already approved exact stable identifiers. Never globally auto-accept native dialogs; wait for the expected dialog and assert its type and message before accepting it. ## TypeScript Client Applications can import `BrowserControlClient` for schema-decoded, same-origin requests authenticated by a session page. Use `sensitive: true` for token-bearing responses and reveal them through Browser Control's API, not the application's own Effect `Redacted` import; package-manager layouts may resolve separate Effect runtimes. ```ts import { BrowserControlClient } from "@opencode-ai/browser-control" const sensitive = yield* origin.json({ path: "/api/session", method: "POST", body: {}, response: SessionResponse, sensitive: true, }) const session = BrowserControlClient.reveal(sensitive) ``` ## Authenticated Network Capture Use network capture when the browser is needed to authenticate or discover a workflow, but repeated direct HTTP calls would be faster and more reliable. Capture each flow at least twice with different inputs so constants and parameters can be distinguished. ```bash browser-control network start --session github --url /api/ \ --resource-type fetch --resource-type xhr browser-control execute --session github --file ./perform-flow.js browser-control network status --session github browser-control network stop --session github \ --output ./github.har --secrets github ``` Written artifacts replace credential-bearing headers, cookies, query fields, and structured body fields with stable references such as `${BC_SECRET_1}`. `--secrets github` stores lossless values separately in a mode-`0600` Secret Profile. Never copy profile values into source, output, diagnostics, or journals, and never deliberately return or log credentials. Inspect the redacted artifact offline, generate one typed function per observed flow, then verify each function with a harmless live request. Run generated clients without exposing values: ```bash browser-control secrets status github browser-control secrets run github -- ./github-cli repositories ``` Generated TypeScript applications can own that wrapper internally through the public SDK: ```ts import { SecretProfile } from "@opencode-ai/browser-control" import { Effect } from "effect" const result = await Effect.runPromise(SecretProfile.run({ name: "github", command: process.execPath, args: ["./github-cli.js", "repositories"], })) ``` The trusted worker receives `BC_SECRET_N` variables and its bounded output is redacted. The public SDK exposes profile metadata but never raw profile values. Refresh credentials normally renewed by a page reload with: ```bash browser-control secrets refresh github --session github --url /api/ ``` If refresh requires login or a human prompt, reauthenticate in the adopted tab and repeat capture with the same profile. MCP exposes equivalent `network_*` and `secrets_*` tools. Completion: the artifact contains references rather than credential values, the generated operation passes a harmless live check, and no secret value appears in source or output. ## Recording Record an attached or session-owned tab with: ```bash browser-control recording start ./tmp/demo.mp4 --session github --mode cdp browser-control recording status --session github browser-control recording stop --session github ``` Start, stop, and status accept `--json`. CDP stop/status results include a `quality` receipt: output dimensions/rate, source and retained-image counts/rates, coalesced/dropped frames, and `screenshotFallback`. The fallback means no compositor frames arrived and the video holds one stop-time screenshot; do not present that as recorded motion. Source counters count compositor events, not visually distinct frames. Low rates can be normal on a static page. Tab capture and older relays omit quality telemetry rather than inventing measurements. Explicit frame rates must be integers from 1 through 60 in either mode; invalid values fail instead of silently clamping. The start result reports the chosen rate. `--mode auto` uses tab capture for user-owned tabs and CDP for relay-owned tabs. Tab capture can include audio; CDP requires `ffmpeg` and has no audio. Use the command's `--help` for format and cursor options. CDP recordings preserve the starting CSS viewport (not a fixed 720p canvas), use high-quality source frames, and default to 60 fps. Use `--frame-rate 30` for smaller files. Actual motion still depends on Chrome delivering new frames; 60 fps output does not guarantee 60 distinct frames. Larger viewports cost more CPU, transport bandwidth, and storage. Set the viewport before recording and do not change viewport/emulation mid-recording. Odd dimensions round down to even. Inspect an encoded frame at native size before sharing: the whole viewport must fill the frame, small text must be readable, and motion must not be a repeated still image. Do not crop and upscale a low-resolution capture to call it HD. On an older installed relay that shrinks the page into a padded corner, record the defect and coordinate a recorder update; changing the file's resolution is not a repair. Completion: stop the recorder, inspect the resulting media rather than only its existence, and report the viewport, state, and interaction path actually tested. ## Troubleshooting 1. Run `browser-control doctor`; it checks package metadata, CLI/relay build identity, extension protocol compatibility, sessions, targets, and artifacts. 2. Use `status --json` to inspect exact sessions and target ownership. 3. Reproduce once with the smallest execute before changing code. Common diagnoses: - `connected:false`: run a relay-backed command and allow the extension startup or alarm wake-up to reconnect. Reload the unpacked extension only if that loop does not recover. - Incompatible extension protocol: update either the extension or npm package; exact extension and relay release versions do not need to match. - Competing browser/profile connections: the active browser is preserved and additional connections are rejected. Use one browser/profile per relay; repeatedly creating sessions or resetting tabs does not switch browsers. - Stale relay build: inspect `doctor`, then coordinate an explicit `browser-control relay restart`. It requires an exact managed instance and safe shutdown protocol 2. Legacy relays need a one-time coordinated manual stop; foreground/source or newer relays are never force-killed or downgraded. MCP observational tools remain available on a mismatch. - Unexpected restart: inspect private endpoint-scoped `~/.browser-control/relays//lifecycle.jsonl` for requester/build/instance metadata. Preparing or selecting a development candidate must not restart the daemon. Use isolated `runtime:prepare` / `runtime:select`, not a live checkout link, when developing Browser Control itself. - `Target not found`: attach the intended tab, then select or adopt it using a unique URL substring or explicit index. - All targets disappeared: dismissing Chromium's debugging banner detaches every tab. Reattach through the toolbar. - Relay restarted: named sessions reclaim exact targets, but JavaScript `state` and snapshot refs reset. Continue after the warning. - Reset/delete after an extension update may wait briefly for target re-announcement; if the old relay-owned target is absent from the completed inventory, Browser Control forgets the dead identity without closing a guessed tab. - Repeated execution-context errors: run one short follow-up so Browser Control can health-check the page. It may recreate a relay-owned page, but it never replaces an unhealthy adopted user tab; reset or re-adopt that tab. - Fill timeout on login fields: inspect first, then try `fillInput` after confirming the selector or locator resolves. String selectors search open shadow roots recursively; closed shadow roots remain unavailable. - Download wait fails: use fetch plus `fs`; extension-backed Playwright cannot retain a native download artifact. - Hover on an infinitely animated target: Playwright may never consider the element stable. Read its current `getBoundingClientRect()` and use `page.mouse.move()` when coordinate input is appropriate. - Chromium-protected pages such as the Chrome Web Store developer dashboard may detach `chrome.debugger`. Do not retry or bypass that boundary; open the page for manual operation. For deeper relay diagnosis, restart with `BROWSER_CONTROL_DEBUG=1`. Debug traces must never include expressions, arguments, results, headers, cookies, or form values. Whenever Browser Control fails, wedges, replaces a page/session, or behaves unexpectedly, create or update a `browser-control` project todo with the Browser Control version, safe session/page context, exact error, deterministic reproduction, expected versus actual behavior, and recovery attempted. Never include credentials, form values, or private account data.

Related skills

Agent Workflows

Skill Creator

Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that exte…

Agent Workflows

Clawdhub

Use the ClawdHub CLI to search, install, update, and publish agent skills from clawdhub.com. Use when you need to fetch new skills on the fl…

Agent Workflows

Agent Team Orchestration

Orchestrate multi-agent teams with defined roles, task lifecycles, handoff protocols, and review workflows. Use when: (1) Setting up a team …

Agent Workflows

Superpowers

Spec-first, TDD, subagent-driven software development workflow. Use when: (1) building any new feature or app — triggers brainstorm → plan →…