A Playwright REPL for driving Chromium from the command line, made to be shared by you and your AI agents.
pw-repl connects to a Chromium over CDP (the Chrome DevTools Protocol), in the browser you already use or
in one it starts itself. You type commands at a pw> prompt. Agents send the same commands from a shell
with pw-repl send. Every command shows in the REPL, whoever sent it, so you always see what an agent
is doing in your browser, and the agent can see what you did.
pw-repl-demo.webm
NOTE: The REPL window in the above video is simulated. The actual tool runs in a terminal, and your browser will look the same as it usually does. The demo uses the virtual REPL to emphasize what's happening in the
pw-repltool as it's working.
A 90-second demo, recorded by pw-repl itself from one script of commands. The command panel on the right is part of the demo page, showing what the agent sends.
pw> tab new http://localhost:3000/cart
pw> snapshot # the page by role and name, with [ref=eN] labels
pw> click e7 # click by ref, or by any Playwright selector
pw> requests # what the page requested
pw> body 4 # what one request got back
pw> route **/api/cart 500 {"error":"boom"} # fake a failing backend
pw> reload
pw> console error # and see how the page copes
Quick start
You need Node.js 20 or newer. pw-repl can use a Chromium-based browser you already have (Chrome,
Chromium, ...), or Playwright's own: npx playwright-core install chromium downloads it.
npx pw-repl@latest run --launch https://example.com
--launch starts a private headless Chromium for the REPL, in a 1280x800 window, and stops it when
the REPL quits. Add --headed to see that browser and click in it yourself. The page opens, and a
pw> prompt waits:
pw> help # common tasks and topics; help <topic>, help <command>, help --all
pw> snapshot
pw> click e3
pw> screenshot
pw> quit
Commands act on the selected tab. tab lists the tabs, with * on the selected one.
Install
| How | Setup | Then run |
|---|---|---|
| Without | nothing | npx pw-repl@latest <command> |
| Global | npm install -g pw-repl |
pw-repl <command> |
| From a clone | npm install (and npm link for pw-repl) |
bin/pw-repl.js <command> |
With npx, use npx pw-repl@latest the same way every time, so every command runs the same version.
Why use it
- Your browser, as it is. It connects to a running Chromium, with your tabs, logins and state, and leaves it running when it stops. Nothing is reset to a fresh profile.
- One session, seen by everyone. You and any number of agents drive the same browser, each with a selected tab of its own, and every command shows in the REPL.
- What you did, for an agent to read.
watchrecords each step you take in a tab, with the requests it caused. Reproduce a bug by hand and the agent sees exactly what happened.watch savekeeps the steps as a file of commands that runs them again. - Breaking things on purpose. Fake, patch, delay or fail responses, cut or slow the network, emulate a phone, dark mode, reduced motion, a locale or a timezone. All of it applies to one tab, so your other tabs are left alone.
- Videos that explain themselves. Record a tab with a pointer that glides to each click, captions on screen, boxes around what matters, and pacing for a viewer.
What it can do
- Read the page: accessibility snapshots with clickable refs, text, HTML, attributes, links, form controls, event listeners, screenshots (viewport, full page, one element with or without a margin around it, or a rectangle).
- Act on it: click, double-click, fill, type, press or hold keys, focus, select, check, drag and drop, upload files, click or drag at a point, scroll the wheel, answer dialogs.
- Watch the network: every request and response body, console messages and page errors, all recorded as they happen.
- Change the network: fake a response (
route), patch its JSON, delay it, fail it, send it to another URL or redirect it, or cut or slow the whole tab's network. - Emulate: a phone, dark mode, reduced motion, forced colors, contrast, print styles, a locale, a timezone, a viewport size.
- Run code:
evalany JavaScript in the page, a line or a file of it, or send a raw CDP command withcdp; read what a Copy button put on the clipboard (clipboard). - Wait: for an element, some text, a response or a page load.
- Record: what someone does in a tab (
watch), a timeline of requests and console (capture), or a video of the tab (record), with a virtual pointer (cursor), captions (toast) and highlights (highlight). - Profile: what keeps a page's main thread busy (
profile): long tasks and the function behind each, forced style and layout, each click's or key's time to the next paint, and dropped frames. - Share: many clients on one REPL, each with its own tab; a command server on a Unix socket or a local port, with an HTTP+JSON protocol.
Use your own browser
Start Chrome with remote debugging (on Linux it may be google-chrome or chromium), then run the REPL
without --launch:
chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.config/chrome-debug" pw-repl run # connects to localhost:9222
Use a separate --user-data-dir: recent versions of Chrome do not open the debugging port on the default
profile. To check that it is up: curl http://localhost:9222/json/version. To reach a browser on
another port or machine, set PW_CDP_URL=http://host:9222.
A headless Chrome works the same way (chrome --headless=new --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-headless). Nobody can click a headless browser's dialogs, so answer them
with dialog accept or dialog dismiss. Its pages start at about 800x600; viewport 1280x800 changes
that.
Browser on the host, REPL in a container: with host networking, localhost:9222 reaches the host's
browser directly. Otherwise, set PW_CDP_URL to the host's address as the container sees it (for
example the Docker bridge gateway, ip route | awk '/default/ {print $3}').
Ways to run it
| Command | What you get |
|---|---|
pw-repl run |
a prompt in this terminal; send reaches it only through a tmux session pw-repl |
pw-repl serve |
a prompt (pw[serve]>), plus a command server on /tmp/pw-repl.sock |
pw-repl serve --background |
no terminal; output in /tmp/pw-repl.log |
pw-repl attachshows a background REPL and takes commands, from any terminal.pw-repl stopstops it.pw-repl wheresays what is running and howsendwill reach it.- For
sendto reachrun, start it in tmux:tmux new -s pw-repl, thenpw-repl runthere. - A URL after the command opens in a new tab:
pw-repl serve http://localhost:3000. --launchworks with each of them. Flags after--go to that Chromium (flags only; a start URL goes before--).- A REPL of your own goes on a socket of its own:
-e /tmp/mine.sockon every command,serveincluded, or setPW_SOCKET.serve <port>listens on TCP, on 127.0.0.1 only.
Common tasks
| I want to… | Commands |
|---|---|
| see where I am | tab, info |
| see what is on the page | snapshot, screenshot |
| do something on it | click, fill, type, press, drag |
| click what has no selector (a canvas) | mouse click <x> <y>, mouse dblclick <x> <y> |
| run some JavaScript in the page | eval document.title; eval --file=check.js for a long one |
| wait for a page or element | wait load, wait <selector> [--gone], wait text <text> |
| choose a file in a file input | upload <selector> <file> |
| see it as a phone, or in dark mode | emulate mobile, emulate dark |
| tap, swipe or pinch as a finger | touch tap <x> <y>, touch pinch <x> <y> <scale> |
| see what the page requested | requests, then body <#> for what one got back |
| see console messages and errors | console |
| show an agent what I do | watch on, click around, then watch |
| see each step as I click | watch on --live |
| watch a tab someone will open | watch on --next-tab [url-part] |
| replay what I clicked through | watch save flow.txt, then pw-repl send --file flow.txt |
| record requests and console together | capture on, then capture off |
| find what makes the page slow | profile on, then profile off; profile reload for a load |
| fake a backend response | route <glob> <status> <json>, route <glob> abort |
| change or slow an API response | route <glob> patch <json>, route <glob> delay <time> |
| cut or slow the whole network | network off, network slow, then network on |
| record a video of the page | record on, then record off (help video for a take) |
| show a pointer where the agent acts | cursor on, then cursor off |
| say what is happening on screen | toast <text>, then toast off |
| point something out on the page | highlight <ref>, then highlight off |
| clean up | modes off |
help <topic> lists the rest, and help <command> has usage and caveats.
Modes
Some commands turn on a mode that stays on until you turn it off: watch, capture, profile,
record, route, network off or slow, emulate, viewport, highlight, cursor and toast.
While any are on in the selected tab, the prompt shows them: (watch network:off routes:2) pw>. modes
lists them for every tab, and modes off turns them all off. Each of these commands on its own shows
its state and what you can run next.
For agents and scripts
A skill for agents
pw-repl skill prints an agent skill (SKILL.md) that teaches an agent to use the REPL: how to start
it, send commands, and share the browser with the user. Save it as pw-repl/SKILL.md in the folder
your agent reads skills from:
mkdir -p <skills folder>/pw-repl npx pw-repl@latest skill > <skills folder>/pw-repl/SKILL.md
It is stamped with the version it came from and a hash of its text. pw-repl where names the skill of
the pw-repl being run, so an agent can tell when its saved copy is out of date and offer to update it
(keeping its Custom rules section).
send, clients and where
pw-repl send info pw-repl send help # the REPL's commands; works without a running REPL pw-repl where # which REPL send would reach
pw-repl send sends one command and prints its result, through the server when one is running, and
through the pw-repl tmux session otherwise. Chain several in the shell: && stops at the first that
fails or is not confirmed (exit 1 or 2), and ; carries on. -t is how long it waits for the answer
(default 20s). Times, there and in every command, are in milliseconds, as in Playwright, or in seconds
with an s: -t 90000 or -t 90s. A send that gives up cancels its command: one still queued never
runs, and one running is no longer waited for.
Several agents can share one REPL. pw-repl send -c <name> (or PW_CLIENT=<name>) sends as a client
with a selected tab of its own, shown in the REPL as [server:<name>]. modes says which client turned
each mode on, and modes off --mine turns off only the sender's own.
send --file take.txt runs a file of commands, one per line, and stops at the first that fails: a
repro to keep, or a video take.
Passwords: type one as a variable, so it is never in the command, a file of commands or the REPL's
log: PW_PASSWORD=... pw-repl send fill '#password' "{{ PW_PASSWORD }}" (watch save writes them that
way). What reads the page back can still show it: snapshot, html and attrs hide a password field's
value; eval on the field does not.
The HTTP protocol
With pw-repl serve, the server speaks HTTP with JSON, for clients of your own:
curl --unix-socket /tmp/pw-repl.sock -H 'Content-Type: application/json' \ -d '{"command": "info"}' http://localhost/run
The body may also have "client": "<name>", and "vars": {"PW_NAME": "<value>"} with the values of the
{{ PW_NAME }} variables in the command (one line only, else 400). It returns {"status": "ok" | "error", "output": "..."}, plus "unconfirmed": true when the command may or may not have done what it
was sent to do (it timed out, or the REPL quit while it ran and it was not read-only). While the REPL
starts, /run answers 503 with "starting": true: retry. GET /health says "status": "starting" until
it serves, then "ok". A client that closes its connection before the answer cancels the command, as
send does when it gives up. pw-repl serve <port> serves TCP on 127.0.0.1; other addresses are refused.
How it differs from playwright-cli
playwright-cli is Microsoft's command line for agents. It
gives each agent a browser session of its own, while pw-repl shares one browser between users and agents.
playwright-cli's command names and most of their options work here too (help playwright-cli lists
them). Where pw-repl differs, it is mostly because someone else may be using the browser:
openandcloseopen a tab and turn off your modes; they never start, close or wipe a browser.closealso lists the files you wrote that are still there.- Routes, network and emulation apply to one tab, not the whole browser.
- File pickers are not caught, since they may be the user's:
uploadnames the input instead. - There is no snapshot after every command, since the user reads the REPL too;
snapshotshows one. - Instead of
-s=<session>, an agent is a client of its own (send -c <name>). watchrecords what the user does as steps with their requests, andwatch savewrites them as pw-repl commands to run again, not as Playwright code.highlightdraws its box without Playwright's locator label, which would cover what is below it and put selectors in screenshots and videos.highlight --labels onshows it.
For a browser of its own for each agent, isolated sessions, traces, PDFs or generated test code,
playwright-cli is the better fit. record records a tab's page; to record the shared browser as you see
it, with your real pointer, use a screen recorder (OBS, ffmpeg -f x11grab, macOS screencapture -v).
Contributing
AGENTS.md describes how the code is laid out, how to add a command, and how releases are cut.
The tests need Node.js 21 or newer. They run against a private headless Chromium they start themselves
(PW_TEST_CHROME, or the one --launch would use: Playwright's, then one on the PATH) and a local test
site. The browser tests are skipped when no Chromium is found.
License
MIT. See LICENSE.md.