play

star 2

Run Playwright scripts and inspect durable browser sessions with flat agent-first commands

douglance By douglance schedule Updated 3/16/2026

name: play description: Run Playwright scripts and inspect durable browser sessions with flat agent-first commands context: fork

play

Run Playwright scripts with zero boilerplate. No imports, no browser lifecycle, no await keywords for the built-in globals.

Use play in two modes:

  1. One-shot scripts for quick navigation, assertions, screenshots, and extraction.
  2. Named sessions for multi-step browser work and real page-debugger control across commands.

Quick Start

play 'goto("https://example.com"); log(title())'

One-Shot Input Modes

# Inline code
play 'goto("https://example.com"); log(text("h1"))'

# Script file
play check-homepage.play.ts

# Pipe / heredoc
echo 'goto("https://example.com"); screenshot()' | play

play << 'EOF'
goto("https://example.com")
assert(text("h1") === "Example Domain", "heading correct")
EOF

Named Sessions

Use named sessions when the browser needs to stay alive across commands. Each session is also persisted under .play/sessions/<session-id>/, so inspection still works after the worker exits.

play do auto-opens the session if needed. Pass --headed and --devtools on the first do when you want a visible Chromium session.

play do agent1 'goto("https://example.com")' --headed --devtools
play console agent1
play events agent1 --limit 20
play net agent1 --limit 20
play dom agent1
play status
play close agent1

Use play open only when you want to start the browser before sending actions:

play open agent1 --headed --devtools
play do agent1 'goto("https://example.com"); click("More information")'
play close agent1

Capture And Probe

Use play capture to run one action batch and get only the console emitted during that batch:

play capture agent1 'goto("http://localhost:3000"); click("Save")'

Use play probe to install temporary session-lifetime instrumentation without editing app code:

play probe agent1 'window.__lastSave' --file app.js --line 120
play probes agent1
play unprobe agent1

Debugger Workflow

play debug still handles live page-debugger control for pause/step/eval/source work.

Key debugger commands:

  • play debug status <name>
  • play debug pause <name>
  • play debug continue <name>
  • play debug step-over <name>
  • play debug step-into <name>
  • play debug step-out <name>
  • play debug eval <name> '<expr>'
  • play debug query <name> '<expr>'
  • play debug source <name> [file start end]

Use -q with debugger status and step commands for one-line summaries instead of JSON blobs.

For inspection, prefer the flat commands:

  • play console <name>
  • play events <name>
  • play net <name>
  • play dom <name> [selector]
  • play capture <name> '<actions>'
  • play probe <name> ...
  • play probes <name>
  • play unprobe <name> [probeId]

Inside scripts, browser console output is buffered separately from log(...). Use consoleCursor(), consoleSince(cursor), capture(async () => ...), consoleLogs(limit?), and clearConsole() when you need precise temporary inspection.

Common Examples

# Navigate and extract
play 'goto("https://example.com"); log(url()); log(text("h1"))'

# Work against a detected local dev server
play 'goto(); click("Sign in"); wait("**/dashboard"); log(title())'

# Screenshot or PDF
play 'goto("https://example.com"); screenshot("homepage.png")'
play 'goto("https://example.com"); pdf("page.pdf")'

# Assertions with exit codes
play 'goto("https://example.com"); assert(visible("h1"), "heading visible")'

# Use the raw Playwright page object when needed
play 'goto("https://example.com"); const resp = await page.request.get("/api/health"); log(await resp.text())'

Globals

All built-in globals are auto-awaited.

Category Functions
Navigation goto(url?) reload() back() forward()
Interaction click(sel) dblclick(sel) fill(sel, val) type(sel, text) press(key) check(sel) uncheck(sel) select(sel, val) hover(sel) focus(sel) clear(sel) upload(sel, path) drag(from, to)
Extraction text(sel) texts(sel) attr(sel, name) html(sel) value(sel) count(sel) visible(sel) title() url() content() table(sel?) evaluate(fn)
Waiting wait(target) sleep(ms)
Media screenshot(path?) pdf(path?)
Output log(...args) capture(async () => ...) consoleCursor() consoleSince(cursor) consoleLogs(limit?) clearConsole()
Assertions assert(condition, message?)
Escape Hatches page browser context

Selector Resolution

click("Submit") and similar helpers resolve selectors in this order:

  1. CSS fast-path
  2. XPath
  3. ARIA role
  4. Label or placeholder
  5. Text
  6. Test ID
  7. Tag fallback

fill() prefers labels and placeholders. click() prefers roles.

Options

Flag Short Description
--headed -h Show browser window
--slow -s Slow motion delay in ms
--port -p Dev server port for goto()
--url -u Base URL for goto()
--timeout Action timeout in ms
--browser chromium, firefox, or webkit
--device Emulate a device such as "iPhone 14"
--devtools Auto-open Chromium DevTools
--quiet -q Suppress session status noise and prefer terse debugger output
Install via CLI
npx skills add https://github.com/douglance/play --skill play
Repository Details
star Stars 2
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator