Download

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 Spawns a new pup window. Only use when there isn't an existing session of that name. 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

⚠ Don't double-open windows — browser_navigate reuses, browser_open_window spawns

If a session already exists (verify with browser_list_windows or browser_list_tabs), point an existing tab at a new URL with browser_navigate — passing tabId if you want to be explicit:

adom-desktop browser_navigate '{"sessionId":"chip-fetcher","tabId":"tab-1","url":"<new-url>"}'

Using browser_open_window against a session that's already alive spawns a second physical pup window with the same title. The new tab is tracked but the old window is orphaned — the user sees two windows showing the same content. Verified painful 2026-05-04: opened a chip-fetcher dashboard verification tab while the original UL Pro detail tab was still active; user noticed the duplicate before I did. Cleanup required desktop_close_window and a tracked-tab marker dance.

Decision tree:

  • Need a new session entirely (different sessionId) → browser_open_window.
  • Need to reuse an existing session's tab → browser_navigate (with tabId if multiple tabs).
  • Need a second tab in the same session → browser_open_tab (not browser_open_window).

⚠ Cross-sessionId duplicates — same app, two windows

The double-open trap above catches same-sessionId re-opens. There's a second flavor that catches you across conversation boundaries: a previous session left a pup window open under one sessionId, and the new conversation opens the same app under a different sessionId — adom-desktop happily creates a second window because the keys differ. The user sees two pup windows showing the same content.

Always run browser_status '{}' before browser_open_window at the start of a new conversation, especially after a context compaction. Look for any session whose URL maps to the app you're about to open. Reuse it (browser_navigate) instead of creating a peer.

Verified 2026-05-07 with chipsmith: an earlier session had sessionId: cs-v2 open at …/proxy/8872/. New conversation opened sessionId: chipsmith at the same port — two windows for the same chip. User noticed the duplicate first.

When closing a duplicate, prefer the OLDER one (lower cb= timestamp in URL); the newer one usually has the working state you just built up.

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

  1. Edit code
  2. browser_reload
  3. browser_wait (3-5s for 3D tiles)
  4. browser_screenshot
  5. browser_errors
  6. Analyze, repeat if needed

Check Connectivity

adom-desktop ping

Pick ONE surface — respect the user's window-mode preference

Hydrogen stores a per-session window mode preference: webview (default) or pup. Check it before opening a window:

adom-cli hydrogen probe  # → { ..., "window_mode": "webview" | "pup" }
  • webview → open as a Hydrogen webview tab (webview open-or-refresh)
  • pup → open via Adom Desktop (pup browser_open_window). If Adom Desktop isn't reachable, fall back to webview and warn.

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:

  1. 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?".
  2. Background WS / polling traffic doubles, which makes the Cloudflare-blipping problem worse.

When window-mode is webview, the decision rule:

Use case Surface Why
Automated test, screenshot, eval, ralph loop, scripted multi-step demo pup (even in webview mode — this is automation, not user-facing) 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

When window-mode is pup, everything goes through Adom Desktop — including user-facing results.

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> — never adom-desktop pup <cmd> --flags. There is no pup subcommand 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_window for 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.