Adom Desktop
Public Made by Adomby adom
Installs the adom-desktop CLI and its Claude skills INTO A CLOUD CONTAINER so an AI assistant there can understand and drive Adom Desktop over the relay. This is the container side, NOT the app itself. The Adom Desktop app is the signed Windows installer under Download below: run THAT on the PC you want the AI to control. Rule of thumb: pkg install here (in your Linux container), Download there (on your Windows PC). Once installed, the AI can drive file transfer, screenshots, notifications, KiCa
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(withtabIdif multiple tabs). - Need a second tab in the same session →
browser_open_tab(notbrowser_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
- 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 — 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:
- 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.
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>— 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.
Pup windows beat Hydrogen webviews for dashboards
For long-running dashboards (chip-fetcher progress, chipfit batch results, watchdog status, build watcher), prefer a dedicated pup browser window over a Hydrogen webview tab. Reasons:
- Hydrogen real estate — webviews occupy a panel slot. The user is already coding/browsing in Hydrogen; a permanent dashboard tab pushes other content off-screen.
- Two-pup pattern — works well: one pup window pinned as the dashboard, a second pup used for ad-hoc scraping/clicking. They don't fight each other.
- Persistence — pup windows survive Hydrogen reloads.
The pattern applies to chip-fetcher, chipfit, chipsmith, any tool that emits a continuous progress UI.
Reuse pup sessions — don't recreate
Each browser_open_window call with the same sessionId reuses the existing window if it's already open. Don't close + reopen reflexively; just browser_reload or navigate. Closing and reopening loses any auth state the window had.
Don't close tabs the user didn't ask to close
The agent owns the windows it opened (by session-id). The user's pre-existing tabs in those same windows are NOT yours to close. When in doubt: open a new session-id, work there, close that one when done. Past pattern: closed a user's research tab while cleaning up after a verification run.
---
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:
```bash
adom-desktop <command> '<json_args>'
```
**Examples:**
```bash
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:
```bash
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)
```bash
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
```bash
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:
```bash
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."*
```bash
# 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:
```bash
# 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`.
## Pup windows beat Hydrogen webviews for dashboards
For long-running dashboards (chip-fetcher progress, chipfit batch results, watchdog status, build watcher), prefer a dedicated pup browser window over a Hydrogen webview tab. Reasons:
1. **Hydrogen real estate** — webviews occupy a panel slot. The user is already coding/browsing in Hydrogen; a permanent dashboard tab pushes other content off-screen.
2. **Two-pup pattern** — works well: one pup window pinned as the dashboard, a second pup used for ad-hoc scraping/clicking. They don't fight each other.
3. **Persistence** — pup windows survive Hydrogen reloads.
The pattern applies to chip-fetcher, chipfit, chipsmith, any tool that emits a continuous progress UI.
## Reuse pup sessions — don't recreate
Each `browser_open_window` call with the same `sessionId` reuses the existing window if it's already open. Don't close + reopen reflexively; just `browser_reload` or navigate. Closing and reopening loses any auth state the window had.
## Don't close tabs the user didn't ask to close
The agent owns the windows it opened (by session-id). The user's pre-existing tabs in those same windows are NOT yours to close. When in doubt: open a new session-id, work there, close that one when done. Past pattern: closed a user's research tab while cleaning up after a verification run.