name: pup user-invocable: true description: "Control Puppeteer browser windows on the user's desktop via the adom-desktop CLI. Use for: opening/closing browser windows, reloading pages, taking screenshots, evaluating JS, flashing taskbar alerts, navigating URLs, checking errors, managing multi-session Chrome. Trigger words: pup, puppeteer, browser window, browser reload, browser screenshot, pup reload, pup screenshot, pup alert, browser debug, visual debug, open in browser, reload browser, flash taskbar, browser_reload, browser_alert_window, browser_open_window, browser_screenshot, browser_eval."
Pup — Puppeteer Browser Control
Control Chrome browser windows on the user's desktop via the adom-desktop CLI.
How to Run Commands
All browser_* commands are run via the adom-desktop CLI using Bash:
adom-desktop <command> '<json_args>'
Examples:
adom-desktop browser_reload '{"sessionId":"dart2"}'
adom-desktop browser_alert_window '{"sessionId":"dart2"}'
adom-desktop browser_screenshot '{"sessionId":"dart2"}'
adom-desktop browser_open_window '{"sessionId":"myapp","profile":"myapp","url":"http://localhost:3000"}'
adom-desktop browser_eval '{"sessionId":"dart2","expr":"document.title"}'
IMPORTANT: These are NOT MCP tools. They are Bash commands. Use the Bash tool to run them.
Available Commands
Session Management
| Command | Description | Required Args |
|---|---|---|
browser_open_window | Open/navigate a browser tab | sessionId, profile, url |
browser_close_window | Close a specific session | sessionId |
browser_focus_window | Bring window to foreground | sessionId (optional) |
browser_alert_window | Flash taskbar icon (non-intrusive) | sessionId (optional) |
browser_switch_window | Switch global active session | sessionId |
browser_list_windows | List all open sessions | none |
Page Commands (all accept optional sessionId)
| Command | Description | Key Args |
|---|---|---|
browser_reload | Reload page + clear error log | sessionId |
browser_navigate | Navigate to new URL | url, sessionId |
browser_screenshot | Capture page screenshot (lossless PNG) | sessionId, maxWidth, fullPage |
browser_eval | Evaluate JS expression in page | expr, sessionId |
browser_errors | Get console/page errors | sessionId |
browser_wait | Wait for content to settle | ms (default 3000) |
browser_status | Check all sessions | none |
browser_close | Close ALL sessions | none |
Common Patterns
Reload + Alert (after code changes)
adom-desktop browser_reload '{"sessionId":"dart2"}' && \
adom-desktop browser_alert_window '{"sessionId":"dart2"}'
Debug Loop
- Edit code
browser_reloadbrowser_wait(3-5s for 3D tiles)browser_screenshotbrowser_errors- Analyze, repeat if needed
Check Connectivity
adom-desktop ping
Pick ONE surface — pup OR Hydrogen, never both
When you need a browser surface for a task, pick ONE: pup (your automation puppet) OR a Hydrogen webview tab (the user's interactive view). Never open the same content in both at once. User feedback 2026-04-26: "why the fuck did you open something in pup and some stuff in webview. pick one or the other."
Two costs of dual-opening:
- Two copies of the page → user sees changes in one but not the other → "why doesn't my upload show up?" or "which one is real?".
- Background WS / polling traffic doubles, which makes the Cloudflare-blipping problem worse.
The decision rule:
| Use case | Surface | Why |
|---|---|---|
| Automated test, screenshot, eval, ralph loop, scripted multi-step demo | pup | scriptable; doesn't compete with the user's tabs; pup's browser_eval works on every tab |
| Showing the user a finished result they should look at | Hydrogen webview tab (adom-cli hydrogen webview open-or-refresh) | the tab persists in their workspace; they can interact normally |
| Both at once for the SAME content | don't | pick whichever role the situation calls for and stick with it |
If you've opened the user's Hydrogen tab for a dashboard / viewer,
use that tab for screenshots too via adom-cli hydrogen screenshot panel --name "<tab-name>". Don't ALSO open a pup
window of the same URL. If you started in pup for testing and the
user wants to see the final state, route them to a Hydrogen tab
once and close the pup window.
Memory rule still applies for testing: pup is the testing surface, NOT Hydrogen. The point of THIS rule is: don't open both for the SAME content at the SAME time.
Multi-URL workflows — ONE window, MANY tabs
When you'd open more than one URL for the same user task (vendor fetch, multi-doc review, compare-pages workflows), put them in tabs of one window, not separate windows. User feedback 2026-04-26: "when you make all those pup windows, they clutter up the user's desktop, so you should open all of those as tabs in one pup window."
# 1) Create the window with the first URL.
adom-desktop browser_open_window '{
"sessionId":"my-task","profile":"my-task",
"url":"https://first.example.com"
}'
# 2) Add the rest as tabs in the SAME session.
adom-desktop browser_open_tab '{"sessionId":"my-task","url":"https://second.example.com"}'
adom-desktop browser_open_tab '{"sessionId":"my-task","url":"https://third.example.com"}'
# 3) Activate a specific tab when you want to screenshot or eval it.
adom-desktop browser_switch_tab '{"sessionId":"my-task","tabId":"tab-2"}'
adom-desktop browser_screenshot '{"sessionId":"my-task","maxWidth":1500}'
adom-desktop browser_eval '{"sessionId":"my-task","expr":"document.title"}'
browser_open_tab lives alongside the documented browser_*
verbs. Same JSON-args convention; same sessionId semantics.
Pick a stable profile name across runs of the same task so the
cookie jar persists — the user logs in once and reuses for 12+
months. Cross-vendor cookie blending is fine because cookies are
per-domain.
Verify what you opened — "ok": true is not enough
browser_open_window / browser_open_tab returns "ok": true as
soon as the navigation starts, NOT when the page actually
rendered useful content. Always sanity-check the result:
# Title check — fastest. 404s, anti-bot blocks, and login walls
# all change the title.
adom-desktop browser_eval '{"sessionId":"my-task","expr":"document.title"}'
# Screenshot — slowest, most thorough. Use when title isn't
# discriminating (e.g. SPA that updates title async).
adom-desktop browser_screenshot '{"sessionId":"my-task"}'
Never blindly template a part number into a vendor URL pattern
without verifying the page exists. The cost of a 404 you didn't
catch is the user spotting it instead. (Real example: shipped a
manufacturer-search-URL pattern that 404'd on ti.com/product/
and nxp.com/ for parts those vendors don't make. Using a
google.com/search?q=… link routes through Google's resolver
and gets a real product hit on the first result.)
Key Rules
- Always pass
sessionId— never use'{}'as args. The default targets the active session which may not be what you want. adom-desktop browser_<cmd> <json>— neveradom-desktop pup <cmd> --flags. There is nopupsubcommand at the top level; that pattern fails with "Invalid JSON args".- No semicolons in
browser_eval— use comma operator or IIFE instead. - Sessions persist — IndexedDB, cookies, localStorage survive restarts. Profile name is the persistence key.
- Each session = independent Chrome window — crash-isolated. Multiple tabs share one session and one cookie jar.
- Never kill Chrome broadly — use
browser_close_windowfor specific sessions. - Always reload + alert after browser-facing code changes — the user expects to see updates immediately.
Full Reference
For detailed docs (profiles, sleep/wake recovery, troubleshooting, first-time setup), see ~/.claude/skills/adom/guides/pup.md.