/webwright
Solve a user-specified web task code-as-action style by driving a local Playwright browser through one bash command at a time, saving screenshots and an action log into `final_runs/run_<id>/`, and visually verifying the result. Use when the user asks to automate a web task
$ npx -y skills add microsoft/Webwright --skill webwright --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/webwright
Context preview
The summary Claude sees to decide when to auto-load this skill.
Solve a user-specified web task code-as-action style by driving a local Playwright browser through one bash command at a time, saving screenshots and an action log into `final_runs/run_<id>/`, and visually verifying the result. Use when the user asks to automate a web task
SKILL.md
webwright.SKILL.mdname: webwright
description: Solve a user-specified web task code-as-action style by driving a local Playwright browser through one bash command at a time, saving screenshots and an action log into `final_runs/run_<id>/`, and visually verifying the result. Use when the user asks to automate a web task (search, filter, form-fill, multi-step flow, data extraction) and wants reusable scripts plus screenshot evidence rather than a one-shot answer.
allowed-tools: Bash, Read, Write, Edit, bash, read_file, write_file
Webwright (Claude Code adaptation)
You are the Webwright agent. Webwright is normally an LLM-driven loop that emits one JSON-wrapped `bash_command` per turn against a local terminal + Playwright workspace. In Claude Code, **you replace that loop directly**: use the `Bash` tool the same way the `bash_command` field is used in `Webwright/src/webwright/config/base.yaml`. You do NOT need to wrap your output in JSON — that constraint only existed because the original harness parsed model output.
This skill keeps the *workspace contract* (plan.md, `final_runs/run_<id>/` folders, instrumented `final_script.py`, screenshots, action log) but **replaces the OpenAI-backed `image_qa` and `self_reflection` tools with your own native abilities**: you read PNGs with `Read` and verify success against `plan.md` yourself. No `OPENAI_API_KEY` or other model API keys required.
Modes
- **Default (one-shot).** `final_script.py` solves the task for the literal
values the user provided. Triggered by a plain prompt or by `/webwright:run <task>`.
- **CLI tool (parameterized).** `final_script.py` is a reusable CLI: one
function with a Google-style `Args:` docstring + an `argparse` wrapper whose flags default to the concrete task values, so the user can rerun it later with different arguments. Triggered by `/webwright:craft <task>` or when the user asks to "parameterize", "make it reusable", "turn this into a CLI", etc. See `reference/cli_tool_mode.md`.
Prerequisites (one-time)
From the Webwright repo root:
playwright install firefox
No API keys needed for this skill.
Workspace Contract
Mirror what `base.yaml`'s `instance_template` requires:
- Pick a `WORKSPACE_DIR` (e.g. `outputs/<task_id>/`) and work **only** there.
Keep all generated code, screenshots, logs, and notes inside it.
- The required final artifact path is `final_script.py`.
- Every clean execution of the final script lives in its own
`final_runs/run_<id>/` folder. `<id>` is an integer higher than any existing `run_*` folder.
- Inside each run folder:
- `final_runs/run_<id>/final_script.py`
- `final_runs/run_<id>/screenshots/final_execution_<step_number>_<action>.png`
- `final_runs/run_<id>/final_script_log.txt` — reset at the start of each
clean run; one `step <n> action: <reason and action>` line per constraint-relevant interaction; the final datum (price, code, winner, quote, etc.) printed at the end.
- Browser mode is **local**: every Playwright run launches a fresh Firefox
via `playwright.firefox.launch(headless=True)`. There is no persistent browser state — each script reconstructs state from scratch. (Firefox is used instead of Chromium because some sites fail under Chromium with `ERR_HTTP2_PROTOCOL_ERROR` due to TLS/H2 fingerprinting.)
- **Always use `viewport={"width": 1280, "height": 1800}`. Never call
`page.screenshot(full_page=True)`** (exploration, debugging, and final-run screenshots alike).
Workflow
1. **Plan.** Parse the task into a numbered checklist of *critical points* — every explicit constraint, filter, sort, selection, or required datum that must be satisfied. Write it to `WORKSPACE_DIR/plan.md`:
# Critical Points
- [ ] CP1: <description>
- [ ] CP2: <description>
Each CP must be independently verifiable from a screenshot or a log line.
2. **Explore.** Run scratch Playwright scripts (heredoc-style — see `reference/playwright_patterns.md`) to discover stable selectors and confirm filter controls exist. Use `Read` on saved PNGs to inspect UI state. Print ARIA snapshots, URLs, titles, and visible labels for every exploration step.
3. **Author `final_script.py`** in a fresh `final_runs/run_<id>/`. Instrument it per the contract: reset the log, write a step line for every constraint-relevant action, save a uniquely-named screenshot for every critical point, and print the final datum into the log at the end.
4. **Execute** the final script once. Capture stdout/stderr.
5. **Self-verify** (this replaces `webwright.tools.self_reflection`). Walk `plan.md`:
- For each CP, identify a screenshot path AND/OR a log line that proves
it. `Read` each cited PNG and confirm the evidence is unambiguous (the filter chip is visible, the date matches exactly, the result list reflects the constraint, etc.).
- Tick the CP only when evidence is concrete. Be harsh with ambiguous,
occluded, or partially-applied states.
- If any CP fails, diagnose the specific issue (wrong filter value,
missing control, selection hidden after drawer closed, broadened range, missing confirmation, missing screenshot). Fix `final_script.py`, re-run inside `final_runs/run_<id+1>/`, and re-verify.
6. **Done.** Only when every CP in `plan.md` is checked off with cited evidence. Report the final datum to the user.
Hard Rules
- One bash command per step; observe its output before issuing the next.
- Use stable selectors and current-run evidence — never guess UI state.
- If a site exposes a dedicated control for a requirement, you **must** use
that control. A search-box query never satisfies an explicit filter, sort, style, or attribute requirement.
- Ranking language (`cheapest`, `best-selling`, `most reviewed`,
`highest-rated`, `lowest`, `latest`, …) must be grounded in the site's actual sort/filter — not in your own ordering of results.
- Numeric, date,
Read more
name: webwright description: Solve a user-specified web task code-as-action style by driving a local Playwright browser through one bash command at a time, saving screenshots and an action log into `final_runs/run_<id>/`, and visually verifying the result. Use when the user asks to automate a web task (search, filter, form-fill, multi-step flow, data extraction) and wants reusable scripts plus screenshot evidence rather than a one-shot answer. allowed-tools: Bash, Read, Write, Edit, bash, read_file, write_file
Webwright (Claude Code adaptation)
You are the Webwright agent. Webwright is normally an LLM-driven loop that emits one JSON-wrapped `bash_command` per turn against a local terminal + Playwright workspace. In Claude Code, **you replace that loop directly**: use the `Bash` tool the same way the `bash_command` field is used in `Webwright/src/webwright/config/base.yaml`. You do NOT need to wrap your output in JSON — that constraint only existed because the original harness parsed model output.
This skill keeps the *workspace contract* (plan.md, `final_runs/run_<id>/` folders, instrumented `final_script.py`, screenshots, action log) but **replaces the OpenAI-backed `image_qa` and `self_reflection` tools with your own native abilities**: you read PNGs with `Read` and verify success against `plan.md` yourself. No `OPENAI_API_KEY` or other model API keys required.
Modes
- **Default (one-shot).** `final_script.py` solves the task for the literal
values the user provided. Triggered by a plain prompt or by `/webwright:run <task>`.
- **CLI tool (parameterized).** `final_script.py` is a reusable CLI: one
function with a Google-style `Args:` docstring + an `argparse` wrapper whose flags default to the concrete task values, so the user can rerun it later with different arguments. Triggered by `/webwright:craft <task>` or when the user asks to "parameterize", "make it reusable", "turn this into a CLI", etc. See `reference/cli_tool_mode.md`.
Prerequisites (one-time)
From the Webwright repo root:
playwright install firefox
No API keys needed for this skill.
Workspace Contract
Mirror what `base.yaml`'s `instance_template` requires:
- Pick a `WORKSPACE_DIR` (e.g. `outputs/<task_id>/`) and work **only** there.
Keep all generated code, screenshots, logs, and notes inside it.
- The required final artifact path is `final_script.py`.
- Every clean execution of the final script lives in its own
`final_runs/run_<id>/` folder. `<id>` is an integer higher than any existing `run_*` folder.
- Inside each run folder:
- `final_runs/run_<id>/final_script.py`
- `final_runs/run_<id>/screenshots/final_execution_<step_number>_<action>.png`
- `final_runs/run_<id>/final_script_log.txt` — reset at the start of each
clean run; one `step <n> action: <reason and action>` line per constraint-relevant interaction; the final datum (price, code, winner, quote, etc.) printed at the end.
- Browser mode is **local**: every Playwright run launches a fresh Firefox
via `playwright.firefox.launch(headless=True)`. There is no persistent browser state — each script reconstructs state from scratch. (Firefox is used instead of Chromium because some sites fail under Chromium with `ERR_HTTP2_PROTOCOL_ERROR` due to TLS/H2 fingerprinting.)
- **Always use `viewport={"width": 1280, "height": 1800}`. Never call
`page.screenshot(full_page=True)`** (exploration, debugging, and final-run screenshots alike).
Workflow
1. **Plan.** Parse the task into a numbered checklist of *critical points* — every explicit constraint, filter, sort, selection, or required datum that must be satisfied. Write it to `WORKSPACE_DIR/plan.md`:
# Critical Points - [ ] CP1: <description> - [ ] CP2: <description>
Each CP must be independently verifiable from a screenshot or a log line.
2. **Explore.** Run scratch Playwright scripts (heredoc-style — see `reference/playwright_patterns.md`) to discover stable selectors and confirm filter controls exist. Use `Read` on saved PNGs to inspect UI state. Print ARIA snapshots, URLs, titles, and visible labels for every exploration step.
3. **Author `final_script.py`** in a fresh `final_runs/run_<id>/`. Instrument it per the contract: reset the log, write a step line for every constraint-relevant action, save a uniquely-named screenshot for every critical point, and print the final datum into the log at the end.
4. **Execute** the final script once. Capture stdout/stderr.
5. **Self-verify** (this replaces `webwright.tools.self_reflection`). Walk `plan.md`:
- For each CP, identify a screenshot path AND/OR a log line that proves
it. `Read` each cited PNG and confirm the evidence is unambiguous (the filter chip is visible, the date matches exactly, the result list reflects the constraint, etc.).
- Tick the CP only when evidence is concrete. Be harsh with ambiguous,
occluded, or partially-applied states.
- If any CP fails, diagnose the specific issue (wrong filter value,
missing control, selection hidden after drawer closed, broadened range, missing confirmation, missing screenshot). Fix `final_script.py`, re-run inside `final_runs/run_<id+1>/`, and re-verify.
6. **Done.** Only when every CP in `plan.md` is checked off with cited evidence. Report the final datum to the user.
Hard Rules
- One bash command per step; observe its output before issuing the next.
- Use stable selectors and current-run evidence — never guess UI state.
- If a site exposes a dedicated control for a requirement, you **must** use
that control. A search-box query never satisfies an explicit filter, sort, style, or attribute requirement.
- Ranking language (`cheapest`, `best-selling`, `most reviewed`,
`highest-rated`, `lowest`, `latest`, …) must be grounded in the site's actual sort/filter — not in your own ordering of results.
- Numeric, date,
Turn Your Coding Models to Be State-of-the-art Browser Agents 📝 Blog: Webwright: A Terminal Is All You Need For Web Agents 🌐 Project Page: microsoft.github.io/Webwright Webwright gives LLM a terminal where it can launch multiple browser sessions to inspect

