name: bridge-user-skill-template user-invocable: true description: "TEMPLATE for an Adom Desktop bridge's CONSUMER skill — copy it, then replace // and the verb glosses. It teaches a cloud Claude/Codex AI how to DRIVE on the user's desktop via the adom-desktop CLI: <one-line value prop — what the bridge controls and why>. Includes a FIRST-USE / COLD-START warm-up section so the AI doesn't panic on a fresh machine. Keep a trigger-words line like this one so the skill activates. Trigger words: , , , , _readiness, _prewarm, <2-3 plain-English phrases a user would say, e.g. 'open in ', ' screenshot'>."

Template — copy into your bridge's pkg as skills/<bridge>/SKILL.md, replace <bridge>/<prefix>.

— (with first-use warm-up)

Drive on the user's desktop from the cloud via the adom-desktop CLI. All verbs are Bash commands (NOT MCP tools):

adom-desktop <command> '<json_args>'

⭐ FIRST-TIME / COLD-START — read this BEFORE you panic that "_ did nothing"

On a brand-new machine (e.g. the user just installed the app that bundles Adom Desktop), something needs may NOT be pre-installed by the installer — a runtime, a large dependency, an external app, or a one-time download. The bridge handles this gracefully — your job is to read the structured response and follow it, not to give up.

When you run a <prefix>_<verb> on a cold machine, one of these happens — handle each:

Response What it means What you do
errorCode: "<dependency>_not_found" A required runtime/dependency isn't installed (the bridge can't start). Run the bridge's documented install verb (e.g. adom-desktop <prefix>_install_<dep> '{}'), wait for it, then retry the original verb.
errorCode: "<resource>_installing" (installing:true) A needed resource is missing; the bridge is auto-downloading/installing it now (one time). This is EXPECTED on a fresh machine — NOT a failure. Poll <prefix>_readiness until ready:true, THEN re-issue the exact same verb. You do NOT install anything yourself. Do NOT report failure to the user.
errorCode: "<resource>_install_failed" / <resource>_download_failed The download/install itself failed. Read readiness.lastError. It's usually no network / a proxy / low disk — NOT your fault. Retry <prefix>_prewarm; if it persists, surface lastError to the user, and use any documented fallback mode.
ok:true / a real session Everything was already warm. Proceed normally.

The clean cold-start recipe

# 1) (optional but nice) Warm the bridge WITHOUT doing real work first:
adom-desktop <prefix>_prewarm '{}'             # returns fast; kicks off any first-use install

# 2) Poll readiness until ready (the bridge sets itself up):
adom-desktop <prefix>_readiness '{}'           # → {ready:false, installing:true, installProgressPct:42, ...}
#    ...wait a few seconds, poll again until → {ready:true, ...}

# 3) Now do the real work — it's instant:
adom-desktop <prefix>_<verb> '{"sessionId":"<id>", ...}'

<prefix>_prewarm and <prefix>_readiness are the two cold-start verbs. <prefix>_prewarm '{"wait":true}' blocks until setup finishes instead of returning immediately. Always surface a verb's _hint field verbatim to the user when something isn't ready — it tells you (and them) exactly what's happening.

Concrete example (the Puppeteer/pup bridge): on a fresh PC, browser_open_window returns errorCode: "chrome_for_testing_installing" (installing:true) because Chrome for Testing (~150 MB) is auto-downloading. That is NOT a failure — you poll browser_readiness until {ready:true}, then re-issue the same browser_open_window. Before the bridge handled this, a cold PC failed with a generic "Failed to launch after 3 attempts" and the AI flailed. Don't reintroduce the panic; read the structured response and follow the table.

How to Run Commands

adom-desktop <prefix>_<verb>      '{"sessionId":"myjob", ...}'   # do a unit of work
adom-desktop <prefix>_screenshot  '{"sessionId":"myjob"}'        # capture current state (lossless PNG)
adom-desktop <prefix>_status      '{}'                           # list sessions + readiness

These are Bash commands, not MCP tools. If your verbs are session-scoped, always pass sessionId'{}' targets the active session, which may not be the one you mean.

Background by default — don't disrupt the user

is the AI's workspace, not the user's. Keep its windows/output OUT of the user's way unless they asked to watch.

  • After opening/driving something for automation (a screenshot, a scrape, a debug loop), send it to the back / leave it un-focused so it doesn't sit on top of what the user is doing (e.g. a <prefix>_lower_os_window or equivalent).
  • Never force your window to the foreground unless the user must SEE it — a recording, a demo, or a finished result you're explicitly showing them.
  • Use a non-intrusive nudge (flash the taskbar via a <prefix>_alert_window-style verb, or just report in chat) for "it's ready / done" instead of stealing focus.
  • For long-running output the user WILL look at, a dedicated window is fine — but still don't re-raise it on every update; nudge instead of grabbing focus.
adom-desktop <prefix>_<verb>          '{"sessionId":"job", ...}'
adom-desktop <prefix>_lower_os_window '{"sessionId":"job"}'   # get it off the user's screen; keep driving headlessly
# ...do work as needed (works while lowered/backgrounded)...
adom-desktop <prefix>_alert_window    '{"sessionId":"job"}'   # non-intrusive "done" when you want their attention

Core verbs

Command Description Key args
<prefix>_readiness Cold-start probe: is the bridge set up / still installing? none
<prefix>_prewarm Do first-use setup WITHOUT starting real work wait (optional bool)
<prefix>_open_window (or your "start a session" verb) Spawn a new session/window sessionId, profile, ...
<prefix>_<do-work> The bridge's primary action verb(s) sessionId, ...
<prefix>_screenshot Capture current state (lossless PNG) sessionId, fullPage?
<prefix>_status All sessions + readiness none
<prefix>_lower_os_window Send the window to the back (keep it off the user's screen) sessionId
<prefix>_raise_os_window / <prefix>_focus_window Bring to foreground (use sparingly — only to show the user) sessionId
<prefix>_alert_window Flash the taskbar (non-intrusive attention) sessionId
<prefix>_close_window Close one session + its window (destructive) sessionId
<prefix>_close Close ALL sessions none

Run adom-desktop <prefix>_describe '{}' for the full machine-readable verb catalog.

Sessions & persistence (if your bridge has sessions)

If one session = one long-lived context (a window, a connection, a document), say so here, and tell the AI how to reuse instead of re-open:

  • New context entirely → <prefix>_open_window (or your start verb).
  • Reuse an existing one → drive it directly (a <prefix>_navigate/equivalent), don't open a second.

Run <prefix>_status '{}' before opening a new session at the start of a conversation (especially after a context compaction) — if a session already matches what you're about to do, reuse it. Opening a duplicate leaves the user staring at two identical windows. Pick a stable profile/identifier across runs so any persisted state (logins, cookies, caches) survives — the user sets up once, reuses for months.

Verify what you did — ok:true is not enough

Many start/action verbs return ok:true as soon as the operation begins, not when it finished / rendered. Sanity-check with the cheapest signal: a <prefix>_eval/read verb that reflects real state (fastest), or a <prefix>_screenshot when a value isn't discriminating. Don't report success to the user on ok:true alone for anything that can silently land on an error/empty state.

Key rules

  • Always pass sessionId for session-scoped verbs. Never '{}' when you mean a specific session.
  • It's adom-desktop <prefix>_<cmd> <json> — there is no top-level <bridge> subcommand.
  • Surface _hint verbatim whenever a verb reports it isn't ready or failed — it's written for both you and the user.
  • Reuse, don't reflexively close+reopen — persisted state is keyed by profile/identifier; prefer a reload/navigate over a destroy+recreate.
  • Never broadly kill the underlying app/process — use the bridge's per-session close (<prefix>_close_window) so you don't nuke unrelated work.
  • Document any per-verb gotchas here (escaping rules, arg quirks, timeouts) — e.g. pup's "no semicolons in browser_eval — use the comma operator or an IIFE."