{
  "schema_version": 1,
  "type": "skill",
  "slug": "adom-desktop-bridge-author",
  "title": "Author a third-party Adom Desktop bridge",
  "brief": "Chat-driven scaffold for writing a new adom-desktop bridge — pick a name + verb prefix, fork the hello sample, wire bridge.json, implement the HTTP server, test locally, ship to the wiki. Walks throug",
  "version": "1.0.0",
  "tags": [],
  "license": "MIT",
  "discovery_triggers": [
    "write a bridge",
    "author a bridge",
    "build a third-party bridge",
    "adom-desktop bridge SDK",
    "scaffold adom-desktop bridge",
    "fork hello sample bridge",
    "new bridge skeleton",
    "integrate altium with adom",
    "integrate matlab with adom",
    "integrate solidworks with adom",
    "integrate eagle with adom",
    "custom adom-desktop verb",
    "add a CLI verb",
    "extend adom-desktop",
    "ship a bridge to the wiki",
    "bridge_install",
    "package a bridge zip",
    "bridge manifest"
  ],
  "discovery_pitch": "Chat-driven scaffold for writing a new adom-desktop bridge — pick a name + verb prefix, fork the hello sample, wire bridge.json, implement the HTTP server, test locally, ship to the wiki. Walks through every step from blank dir to installable manifest URL.",
  "sample_prompts": [
    {
      "label": "Scaffold an Altium bridge",
      "prompt": "Help me start a new adom-desktop bridge for Altium Designer using this skill"
    },
    {
      "label": "Add a verb to an existing bridge",
      "prompt": "Use this skill to help me add a new browser_extract_pdfs verb to the puppeteer bridge"
    },
    {
      "label": "Walk me through hello",
      "prompt": "Use this skill to walk me through forking the hello sample bridge from scratch"
    },
    {
      "label": "Build a MATLAB bridge",
      "prompt": "Scaffold a third-party bridge for MATLAB — figure out the right pattern"
    },
    {
      "label": "Ship a bridge to the wiki",
      "prompt": "I have a working bridge dir; use this skill to package + publish it to the wiki"
    }
  ],
  "source_path": "SKILL.md",
  "readme": "---\r\nname: adom-desktop-bridge-author\r\ndescription: Use this skill when the user says they want to write/author/build a third-party bridge for adom-desktop, integrate a new desktop app (Altium, MATLAB, SolidWorks, an oscilloscope, etc.) with cloud Claude, fork the hello sample bridge, ship a bridge to the wiki, or asks how custom CLI verbs get routed into adom-desktop. Walks them through scaffolding a fresh bridge from the canonical template, wiring `bridge.json`, writing the HTTP server, packaging the zip, hosting it, and verifying install via `adom-desktop bridge_install`.\r\n---\r\n\r\n# Authoring an Adom Desktop bridge\r\n\r\nA bridge is a small HTTP server (Python, Node, or any executable) that adom-desktop spawns on demand and proxies cloud CLI requests to. Once installed, every `adom-desktop <verb> '<json>'` whose verb starts with your bridge's prefix routes to your server. Adom Desktop v1.8.0+ supports dynamic bridges installed at runtime from a wiki manifest URL — no fork of adom-desktop required.\r\n\r\n## When to use this skill\r\n\r\nThe user wants to write a bridge if they say any of:\r\n- \"I want to integrate Altium / MATLAB / Eagle / SolidWorks / OrCAD / my oscilloscope / our internal CAD tool with adom-desktop\"\r\n- \"Help me build a third-party adom-desktop bridge\"\r\n- \"How do I add a new CLI verb to adom-desktop?\" (the answer is almost always \"ship it as a bridge, not by forking adom-desktop\")\r\n- \"Fork the hello sample bridge\"\r\n- \"Make my bridge installable via the install modal\"\r\n\r\nIf they want to USE existing bridges (kicad, fusion, puppeteer), that's the `adom-desktop` skill, not this one.\r\n\r\n## Prerequisites\r\n\r\nThe user needs adom-desktop installed + running on their laptop and reachable from this container:\r\n\r\n```bash\r\nadom-desktop ping\r\n```\r\n\r\nIf that fails, walk them through the install flow at https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop first.\r\n\r\n## Workflow\r\n\r\n### Step 1 — pick a bridge name + verb prefix\r\n\r\nAsk the user:\r\n- What's the host app this bridge integrates? (e.g. \"Altium Designer\")\r\n- What lowercase one-word name should the bridge use? (e.g. `altium`) — this is the cache directory name + the manifest key\r\n- What verb prefix should they own? Conventionally `<name>_*`, e.g. `altium_open_project`, `altium_export_step`. Prefixes are first-come-first-served across the registry; check existing bridges with `adom-desktop bridge_list` so you don't collide.\r\n\r\nReserved prefixes that are already taken:\r\n- `kicad_*` (kicad bridge)\r\n- `browser_*`, `desktop_recorder_*`, `desktop_record_*` (puppeteer bridge)\r\n- `fusion_*` (fusion360 bridge)\r\n- `hello_*` (sample bridge)\r\n\r\nReserved bridge ports: 8770–8779 (Adom-owned), 8851 (puppeteer). Pick something in 8800–9000 for new bridges.\r\n\r\n### Step 2 — scaffold from the hello sample\r\n\r\nThe hello sample bridge is the minimum viable example — a ~80-line stdlib-Python HTTP server. Have the user download + extract it:\r\n\r\n```bash\r\n# On the user's laptop (run via adom-desktop shell_execute or have them run it themselves):\r\ncurl -L https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-bridge-v1.0.0.zip \\\r\n  -o /tmp/hello-bridge.zip\r\nmkdir -p ~/my-bridge && cd ~/my-bridge\r\npython3 -c \"import zipfile; zipfile.ZipFile('/tmp/hello-bridge.zip').extractall()\"\r\nls -la\r\n```\r\n\r\nThey now have `bridge.json`, `BRIDGE_VERSION`, `server.py`, `README.md`. Rename the directory + edit each file to match the new bridge name/verbs.\r\n\r\nFor Node-based bridges, look at the puppeteer bridge zip instead:\r\n`https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/puppeteer-bridge-v1.0.0.zip`\r\n\r\nFor app-with-plugin-API bridges (Altium / SolidWorks / etc.), the fusion360 bridge is the right reference — it has both a bridge process AND an in-host add-in:\r\n`https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/fusion360-bridge-v1.0.0.zip`\r\n\r\n### Step 3 — edit `bridge.json`\r\n\r\nThe schema is documented at https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges#bridgejson-schema. Minimum required fields:\r\n\r\n```json\r\n{\r\n  \"manifest_version\": 1,\r\n  \"name\": \"<lowercase-name>\",\r\n  \"displayName\": \"<human-readable>\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"One-line description that shows up in bridge_list.\",\r\n  \"spawn\": {\r\n    \"kind\": \"python\",\r\n    \"entrypoint\": \"server.py\",\r\n    \"port\": 8XXX,\r\n    \"healthEndpoint\": \"http://127.0.0.1:8XXX/status\",\r\n    \"stopMethod\": \"kill\",\r\n    \"killImageName\": \"python.exe\"\r\n  },\r\n  \"verbPrefixes\": [\"<name>_\"],\r\n  \"verbs\": [\"<name>_verb1\", \"<name>_verb2\"]\r\n}\r\n```\r\n\r\nKeep `verbs` accurate — `bridge_list` advertises this to discovery. Don't list verbs you haven't implemented.\r\n\r\nIf your bridge supports project-watching (file watcher → auto-resync to Docker), add a `watcher` block:\r\n```json\r\n\"watcher\": {\r\n  \"supports\": true,\r\n  \"displayName\": \"<Watcher name>\",\r\n  \"defaultGlobs\": [\"*.your-ext\"],\r\n  \"configKey\": \"project_watch\"\r\n}\r\n```\r\n\r\nThe GUI renders an eye-pip on the chip when watching is active.\r\n\r\n### Recommended: `docs` URL + `platforms` map (v1.8.14+)\r\n\r\nThese two optional fields make your bridge a first-class citizen of the GUI:\r\n\r\n```json\r\n\"docs\": \"https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/your-bridge-name\",\r\n\"platforms\": {\r\n  \"windows\": { \"supported\": true },\r\n  \"macos\":   { \"supported\": false, \"reason\": \"Mac port pending — Win32 deps in handlers/window_automation.py need a Quartz equivalent.\" },\r\n  \"linux\":   { \"supported\": false, \"reason\": \"Same as macOS.\" }\r\n}\r\n```\r\n\r\n- **`docs`**: surfaces in the chip's right-click context menu as \"View on wiki\" (top item). Use it. If you don't have a wiki page yet, publish one first (see `adom-wiki page publish` via the `adom-wiki` skill).\r\n- **`platforms`**: an honest declaration of which OSes the bridge actually works on. Keys are `windows` / `macos` / `linux`. The GUI shows the support status in the tooltip + warns (non-blocking) in the install modal if the user is on a `supported: false` OS. Provide a `reason` string when something doesn't work — the user sees it verbatim, so explain why.\r\n\r\nTell the user **don't lie in `platforms`**. If they haven't tested macOS, leave it as `{\"supported\": false, \"reason\": \"Untested on macOS so far\"}` rather than claiming support. The whole point of the field is honesty — better to set expectations than burn a user who installs a bridge that silently fails.\r\n\r\n### Step 4 — implement the server\r\n\r\nThe HTTP contract is two endpoints:\r\n\r\n- `GET /status` → `{\"ok\": true, \"version\": \"...\"}` — used by adom-desktop to confirm the bridge is alive\r\n- `POST /command` body `{\"verb\": \"<name>_<verb>\", \"args\": {...}}` → `{\"success\": true, \"output\": \"...\"}` or `{\"success\": false, \"error\": \"...\"}`\r\n\r\nThe hello sample's `server.py` is the canonical shape — fork it and replace the verb handlers with yours.\r\n\r\nWhen implementing handlers:\r\n- Validate `args` strictly. Return `{\"success\": false, \"error\": \"...\"}` for invalid input; don't 500.\r\n- Keep responses small. If returning large payloads (file contents, binary blobs), write to disk first and return the path; Docker will use `pull_file` to fetch.\r\n- Long-running operations (>5s) should return immediately with a job handle, then expose a `<name>_status` verb for polling.\r\n\r\n### Step 5 — test locally via a localhost manifest\r\n\r\nBefore publishing to the wiki, test with a local HTTP server:\r\n\r\n```bash\r\n# Build the zip\r\ncd ~/my-bridge\r\npython3 -c \"\r\nimport zipfile, os, fnmatch\r\nEXCLUDE = ['*/__pycache__/*', '*.pyc', '.history/*']\r\nwith zipfile.ZipFile('../my-bridge-v1.0.0.zip', 'w', zipfile.ZIP_DEFLATED) as zf:\r\n    for root, dirs, files in os.walk('.'):\r\n        for f in files:\r\n            rel = os.path.relpath(os.path.join(root, f), '.')\r\n            if any(fnmatch.fnmatch(rel.replace(os.sep, '/'), g) for g in EXCLUDE):\r\n                continue\r\n            zf.write(os.path.join(root, f), rel)\r\n\"\r\n\r\n# Compute sha256 + size\r\nSHA=$(sha256sum ../my-bridge-v1.0.0.zip | awk '{print $1}')\r\nSIZE=$(stat -c%s ../my-bridge-v1.0.0.zip)\r\n\r\n# Write the manifest\r\ncat > ../my-bridge-manifest.json <<EOF\r\n{\r\n  \"manifest_version\": 1,\r\n  \"version\": \"1.0.0\",\r\n  \"url\": \"http://127.0.0.1:9999/my-bridge-v1.0.0.zip\",\r\n  \"sha256\": \"$SHA\",\r\n  \"size\": $SIZE,\r\n  \"released_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\r\n}\r\nEOF\r\n\r\n# Serve them on a local HTTP port\r\ncd .. && python3 -m http.server 9999 &\r\nLOCAL_SERVE_PID=$!\r\n\r\n# Install through the localhost URL\r\nadom-desktop bridge_install '{\"manifestUrl\":\"http://127.0.0.1:9999/my-bridge-manifest.json\"}'\r\n\r\n# Try a verb\r\nadom-desktop <name>_<verb> '{...}'\r\n\r\n# Stop the local server when done\r\nkill $LOCAL_SERVE_PID\r\n```\r\n\r\nThis roundtrip exercises the entire install path (download + sha256 verify + extract + registry rescan + spawn-on-first-call) without uploading anything yet.\r\n\r\n### Step 6 — ship to the wiki (recommended) or any HTTPS host\r\n\r\nTwo options:\r\n\r\n**Option A — Adom Wiki** (recommended for bridges they want the community to find):\r\n\r\n```bash\r\nadom-wiki asset upload apps/adom-desktop \\\r\n  --asset-type file \\\r\n  --file ../my-bridge-v1.0.0.zip \\\r\n  --caption \"my-bridge v1.0.0\"\r\n\r\n# Then upload a manifest pointing at that zip's wiki URL\r\n# (Replace the local-server URL with the wiki static URL the upload command prints.)\r\nadom-wiki asset upload apps/adom-desktop \\\r\n  --asset-type file \\\r\n  --file ../my-bridge-manifest.json \\\r\n  --caption \"my-bridge manifest v1.0.0\"\r\n```\r\n\r\nSuggest the user also write a dedicated `apps/<my-bridge>-bridge` wiki page documenting their bridge — same shape as the [reference bridges](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges#reference-implementations). Use the `adom-wiki` skill for the publish flow.\r\n\r\n**Option B — any HTTPS host** (GitHub Releases, your own CDN, S3):\r\n\r\n```bash\r\n# Upload both files anywhere reachable by HTTPS, then:\r\nadom-desktop bridge_install '{\"manifestUrl\":\"https://your-host/my-bridge-manifest.json\"}'\r\n```\r\n\r\nCloud users can install from any reachable URL — they're not locked to the wiki.\r\n\r\n### Step 7 — verify end-to-end + iterate\r\n\r\n```bash\r\n# See it in the registry\r\nadom-desktop bridge_list\r\n\r\n# Trigger your verbs\r\nadom-desktop <name>_<verb> '{...}'\r\n\r\n# Inspect the install dir (right-click the chip in the GUI → Open install folder)\r\n# OR programmatically:\r\nadom-desktop shell_execute '{\"command\":\"powershell -NoProfile -Command \\\"Get-ChildItem $env:LOCALAPPDATA\\\\\\\\\\\\\\\"Adom Desktop\\\\\\\\\\\\\\\"\\\\\\\\bridges-cache\\\\\\\\<name>\\\"\",\"timeout_secs\":5}'\r\n\r\n# After edits, bump bridge.json's version + BRIDGE_VERSION, rebuild the zip, re-upload, then:\r\nadom-desktop refresh_bridges\r\n```\r\n\r\nTo ship a breaking change without leaving users on the old version: bump the manifest's `version` field. On next launch + refresh, adom-desktop fetches the new manifest, compares versions, downloads + verifies the new zip, atomically swaps the cache dir.\r\n\r\n## Lifecycle controls available to your bridge users\r\n\r\nThese verbs are built into adom-desktop — you don't implement them, just know they exist so you can mention them when users ask:\r\n\r\n| Verb | What it does |\r\n|---|---|\r\n| `bridge_list` | List all installed bridges + their metadata |\r\n| `bridge_install` | Install from a manifest URL |\r\n| `bridge_uninstall` | Remove from cache (returns to the bundled version if there was one) |\r\n| `bridge_pause` | Stop routing verbs (process stays running, verbs return \"paused\") |\r\n| `bridge_resume` | Resume routing |\r\n| `refresh_bridges` | Re-poll all known manifest URLs for updates |\r\n\r\nFrom the Adom Desktop GUI: right-click any chip in the bridge bar for Pause / Resume / Open install folder / Uninstall.\r\n\r\n## Reference pages on the wiki\r\n\r\nThese pages contain full architecture writeups for the three bundled bridges. Use them when the user is stuck on a pattern you've already seen one of these solve:\r\n\r\n- **[Bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges)** — schema, packaging, lifecycle, trust model\r\n- **[KiCad bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-kicad-bridge)** — multi-instance, reverse-bridge into the host app\r\n- **[Puppeteer bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-puppeteer-bridge)** — single-instance Node + session-profile isolation + recording\r\n- **[Fusion 360 bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-fusion-bridge)** — passthrough-to-add-in pattern (use for Altium, SolidWorks, Eagle)\r\n\r\n## Trust + security gotchas to warn users about\r\n\r\n- The trust model is SHA256-verification of the zip against the manifest. **Users install with their own privileges** — bridges can read/write any file the user can. Tell them to install only from sources they trust.\r\n- If you publish to the wiki, anyone in the org can see it. For internal-only bridges, host the manifest behind auth (HTTPS basic, signed URLs, etc.) — adom-desktop's `bridge_install` honors HTTP redirects + auth headers passed via `--auth-header`.\r\n- Don't put secrets in the bridge zip. The unpacked cache dir is readable by anyone on the same machine. Read secrets at runtime from env vars / OS keychain / a `.env` next to the bridge cache that the user populates manually.\r\n\r\n## When something goes wrong\r\n\r\n1. **`bridge_install` says SHA mismatch** — the zip you uploaded doesn't match the manifest's `sha256`. Recompute and re-upload the manifest.\r\n2. **Verb returns \"bridge not found\"** — the prefix you declared doesn't match the verb. Check `adom-desktop bridge_list` to see what prefixes you actually registered.\r\n3. **Bridge process won't start** — adom-desktop reports the spawn failure. Common causes: wrong `entrypoint`, `spawn.kind` mismatch (Python file as `node`), missing dependencies. Check the GUI's activity log.\r\n4. **Bridge runs but verbs return \"process exited\"** — your handler probably crashed. Add `try/except` around your dispatcher and log to stderr; adom-desktop forwards stderr lines to the GUI log.\r\n5. **Open install folder shows a \"Location is not available\" dialog** — the cache dir doesn't exist on the user's real filesystem (probably installed from a sandboxed context). Uninstall + reinstall from a clean session. See https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges for the full troubleshooting.\r\n",
  "author": {
    "id": "695820315b5f1e4db2fcf602",
    "name": "Kyle Bergstedt",
    "email": "[email protected]"
  },
  "visibility": {
    "public": true
  },
  "hero": null,
  "metadata": {},
  "created_at": "2026-05-28T05:29:31.282Z",
  "updated_at": "2026-05-28T05:29:31.282Z",
  "sub_skills": [],
  "parent_app": null
}