Adom Desktop - KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge — multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
name: adom-desktop-kicad public: true description: "Launch and drive KiCad on the user's laptop (KiCad 7/8/9/10 supported, multi-version side-by-side) from this container via the adom-desktop CLI. Open schematics/boards/symbols/footprints, run DRC, install sym-lib-table / fp-lib-table libraries, show the 3D viewer, capture KiCad window screenshots, send keyboard shortcuts and click coordinates. Trigger words: kicad, open schematic, open board, open pcb, open symbol, open footprint, KiCad symbol editor, KiCad footprint editor, KiCad 3D viewer, run DRC, install KiCad library, sym-lib-table, fp-lib-table, KiCad screenshot, kicad_send_key, kicad_click, kicad_window_info, kicad_screenshot_all, close KiCad, kicad versions, kicad_list_versions, KiCad bridge."
adom-desktop — KiCad bridge
All commands dispatch to the KiCad Python bridge running alongside the desktop app. Invoke from this container via Bash:
adom-desktop kicad_<action> '<json_args>'
The leading kicad_ routes the call to the KiCad plugin; the <action> names below are the plugin-side command names.
FIRST-TIME / COLD-START — read before you panic
A fresh machine can be missing KiCad, or the reverse-bridge plugin auto-installs on your first call. These states are EXPECTED — don't declare defeat. Branch on the stable errorCode (not the prose), and relay the returned _hint to the user verbatim.
Signal (errorCode / field) |
What it means | What YOU do |
|---|---|---|
host_app_not_installed (generic; hostApp:"KiCad". Legacy alias kicad_not_installed in errorCodeLegacy) |
KiCad (the user's host app) isn't on this machine. AD detects host apps but never auto-installs them. | Don't send the user to download it. Offer to install it, then run kicad_upgrade '{}' — the agent installs the official build from scratch. Then retry. |
kicad_version_not_installed |
The kicadVersion you asked for isn't installed. |
Run kicad_list_versions '{}'; retry with an installed version or omit kicadVersion to use the newest. |
pluginAutoInstall present in a response |
Your first kicad_* call auto-installed the reverse-bridge plugin into KiCad (idempotent). |
Nothing — expected. Subsequent calls are near-zero cost. |
| bridge not responding on the first call | AD is lazy-spawning the bridge (+ ~9s KiCad detection). | Wait a beat and retry; it binds once and stays up. |
Readiness before you drive:
kicad_readiness '{}'— read-only, zero side effects: is KiCad installed/ready? ({detected, ready, versionCount, defaultVersion}.) There's no prewarm — KiCad is a host app you install on request viakicad_upgrade, not a downloaded runtime asset.kicad_describe '{}'— the full machine-readable verb catalog (the completeness escape hatch; the per-call_hints are what carry you in practice).- For "what EDA tools do I have" across all bridges, use AD's
bridge_readiness(see below).
Installing KiCad on a fresh machine (hard-won, v0.9.26+)
kicad_upgrade downloads the official downloads.kicad.org installer and silent-installs it. Two things that WILL bite on a truly fresh machine, both handled for you now:
- Install scope is mandatory in silent mode. KiCad 10's installer is NsisMultiUser: bare
/Sexits instantly withrc=666660("invalid command-line parameters") and installs nothing. The bridge auto-picks the scope:/alluserswhen the AD process is elevated (installs to Program Files), else/currentuser(installs to%LOCALAPPDATA%\Programs\KiCad— no admin, no UAC prompt at all). Force it withkicad_upgrade '{"scope":"user"}'— that's your answer to "install KiCad without a UAC prompt." (Elevation note: on a VM/kiosk the AD process may already be elevated even though nobody clicked UAC — an elevated installer that launches the app hands down its admin token.kicad_upgrade '{"diagnoseOnly":true}'reports the real token/UAC facts.) - Library installs work before KiCad's first launch.
%APPDATA%/kicad/<ver>/(the per-user lib tables) doesn't exist until KiCad's first GUI run, sokicad_install_library/_footprintused to fail "config directory not found." The bridge now bootstraps that config (seeding the lib tables from the install template) automatically — installs just work on a never-launched KiCad.
GPU-less hosts (VM/RDP/server): KiCad pops a blocking "Could not use OpenGL" dialog and the heavy editors may not render. The bridge auto-expires that dialog and switches KiCad to the Cairo software canvas — see kicad-interaction for the dialog/canvas details.
Multi-version (KiCad 9 + KiCad 10 side-by-side)
Users can have multiple KiCad versions installed at the same time. The bridge auto-discovers everything under C:/Program Files/KiCad/<version>/ and defaults to the newest installed version if you don't specify one.
# Discover what's installed (run this first if unsure)
adom-desktop kicad_list_versions '{}'
# → {"versions":[{"version":"10.0", "default":true, ...}, {"version":"9.0", ...}], "default":"10.0", "count":2}
Every kicad_open_* (and install_*, run_drc) command accepts an optional kicadVersion arg to pin a specific install:
# Open in KiCad 10 (newest — default if you omit kicadVersion)
adom-desktop kicad_open_board '{"filePath":"C:/designs/foo.kicad_pcb"}'
# Open in KiCad 9 explicitly (e.g. to check that an older project still works)
adom-desktop kicad_open_board '{"filePath":"C:/designs/foo.kicad_pcb","kicadVersion":"9.0"}'
If you pass a version that's not installed, the bridge returns a hint listing the installed versions. The success response includes kicadVersionUsed so you can confirm which install actually ran.
Note on libraries / config: each KiCad version has its own
sym-lib-tableandfp-lib-table(in%APPDATA%/kicad/<version>/).kicad_install_librarywrites to the table of whichever version was selected — so if the user installs a library and then asks "open it in KiCad 9", you may need to install it under both versions.
"What EDA tools do I have?" — readiness & host-app detection
KiCad is the user's HOST APP. Adom Desktop detects it — it won't silently auto-install it as a prewarm/dependency (AD v1.9.47+). But if KiCad isn't found, do NOT hand the user a manual download — offer to install it for them and run kicad_upgrade '{}'. That verb installs the latest stable from the official downloads.kicad.org installer (silent /S, Windows only; may raise a UAC prompt) and installs from scratch, not just upgrades. The distinction: AD-core never auto-installs a host app, but a user-requested, agent-run install is exactly right.
- To answer "what EDA tools / host apps do I have?" across every bridge, call the AD-level verb
adom-desktop bridge_readiness '{}'— AD aggregates each bridge's host-app detection (KiCad, Fusion 360, …) into one report. Start here for any "do I have X installed?" question. - For KiCad specifically,
adom-desktop kicad_readiness '{}'is a zero-side-effect read-only probe:{hostApp, detected, ready, versionCount, defaultVersion, versions[]}. It does not install the plugin or spawn subprocesses, and works even when KiCad is absent (detected:false). Usekicad_list_versionswhen you need the full per-version detail (paths, IPC API, lib tables).
Commands
| CLI form | Action | Purpose | Key args |
|---|---|---|---|
kicad_list_versions |
list_versions |
Enumerate every installed KiCad version with paths and which one is the default | — |
kicad_readiness |
readiness |
Read-only host-app detection: is KiCad installed/ready? Zero side effects (no plugin load, no subprocess); works even when KiCad is absent. For cross-bridge "what do I have", use AD's bridge_readiness |
— |
kicad_check_for_updates |
check_for_updates |
Read-only: installed KiCad vs latest STABLE (from downloads.kicad.org / KiCad GitLab tags); flags when the install predates the modern IPC/kipy automation API and should be upgraded | — |
kicad_upgrade |
upgrade |
Download the official KiCad installer from downloads.kicad.org and silent-install it (/S) — not winget (winget lags the real release). May raise a UAC prompt to approve |
optional version, force |
kicad_open_schematic |
open_schematic |
Open a .kicad_sch in the Schematic Editor |
filePath, optional kicadVersion |
kicad_open_board |
open_board |
Open a .kicad_pcb in the PCB Editor |
filePath, optional kicadVersion |
kicad_open_symbol_editor |
open_symbol_editor |
Launch Symbol Editor (optional: open specific library/symbol) | libraryName, symbolName, optional kicadVersion |
kicad_open_footprint_editor |
open_footprint_editor |
Launch Footprint Editor | libraryName, footprintName, optional kicadVersion |
kicad_open_3d_viewer |
open_3d_viewer |
Show the board (or footprint) in 3D viewer | editor ("auto"/"pcb"/"fp" — "auto" prefers the board when both editors are open, no need to close sub-editors), optional kicadVersion |
kicad_close_symbol_editor |
close_symbol_editor |
Close Symbol Editor | — |
kicad_close_footprint_editor |
close_footprint_editor |
Close Footprint Editor | — |
kicad_close_3d_viewer |
close_3d_viewer |
Close 3D viewer | — |
kicad_close |
close |
Close KiCad (all editors) | — |
kicad_window_info |
window_info |
Enumerate open KiCad windows (HWND, title, bounds, editor type) | — |
kicad_install_library |
install_library |
Register a .kicad_sym + .pretty/ pair in sym-lib-table + fp-lib-table |
libraryPath, libraryType, libraryName, optional kicadVersion |
kicad_install_symbol |
install_symbol |
Add a single symbol to an existing library | fileName, fileContent, optional kicadVersion |
kicad_install_footprint |
install_footprint |
Add a single footprint to an existing library | library, footprint_path, optional kicadVersion |
kicad_run_drc |
run_drc |
Run DRC on the current board, return violations | filePath, optional kicadVersion |
kicad_fix_keyboard |
fix_keyboard |
Workaround stuck-modifier bug after screen lock | — |
kicad_screenshot_all |
screenshot_all |
Capture every open KiCad window (lossless PNGs) | — |
kicad_send_key |
send_key |
Single key to an EXACT hwnd (e.g. Enter/Escape to a dialog). For modifier CHORDS ("ctrl+s", "alt+3") use AD's desktop_press_key — don't route chords here. |
hwnd, key |
kicad_click |
click |
Send a click at (x, y) in a KiCad window | hwnd, x, y |
kicad_adom_library_status |
adom_library_status |
Report whether the Adom shared library is registered | — |
Quick examples
# Open a schematic
adom-desktop kicad_open_schematic '{"path":"C:/designs/foo.kicad_sch"}'
# List open KiCad windows
adom-desktop kicad_window_info '{}'
# Screenshot every KiCad window
adom-desktop kicad_screenshot_all '{}'
# Run DRC
adom-desktop kicad_run_drc '{"board_path":"C:/designs/foo.kicad_pcb"}'
# Install a library pair
adom-desktop kicad_install_library '{
"name":"my-lib",
"sym_path":"C:/libs/my-lib.kicad_sym",
"fp_path":"C:/libs/my-lib.pretty"
}'
Error shape
Every failing response includes a _hint field with a human-readable next step — surface it to the user verbatim when a KiCad command fails.
Bridge architecture (v1.8.31+) — you don't need to know any port
Earlier versions of the KiCad bridge listened on hardcoded port 8772. As of v1.8.31, bridges use OS-assigned ephemeral ports — every spawn gets a new free port. You never need to know it.
- The CLI (
adom-desktop kicad_*) automatically routes through adom-desktop's direct API (default port 47200 as of v1.8.33, with auto-fallback to 47201-47209 if 47200 is taken). adom-desktop then forwards to the bridge's actual runtime port. - The bridge port is internal plumbing and can change every spawn.
bridge_listreports it asspawn.runtimePortfor debugging only — do NOT hardcode that anywhere. - Why: this lets adom-desktop coexist with Hydrogen Desktop (which used to claim 8772 itself) and any other tool that grabs ports in the 8000 range. Side-by-side bridges from different apps no longer collide.
- The relay path (Docker → wss proxy → Windows GUI) is unchanged — same
adom-desktop kicad_*verbs, same JSON shape.
If you ever find yourself wanting to talk directly to the KiCad bridge process: don't. Go through the verb dispatch. If the verb you need doesn't exist, request a feature in adom-inc/adom-desktop.
Related
adom-desktop-discovery(gallia) — ensures the CLI + relay are installed.adom-desktop-fusion— sibling bridge for Fusion 360.adom-desktop-direct-api— the direct API contract (port 47200 + 47201-47209 fallback, discovery file at~/.adom/direct-api-port).- Repo:
adom-inc/adom-desktop/plugins/kicad/
---
name: adom-desktop-kicad
public: true
description: "Launch and drive KiCad on the user's laptop (KiCad 7/8/9/10 supported, multi-version side-by-side) from this container via the adom-desktop CLI. Open schematics/boards/symbols/footprints, run DRC, install sym-lib-table / fp-lib-table libraries, show the 3D viewer, capture KiCad window screenshots, send keyboard shortcuts and click coordinates. Trigger words: kicad, open schematic, open board, open pcb, open symbol, open footprint, KiCad symbol editor, KiCad footprint editor, KiCad 3D viewer, run DRC, install KiCad library, sym-lib-table, fp-lib-table, KiCad screenshot, kicad_send_key, kicad_click, kicad_window_info, kicad_screenshot_all, close KiCad, kicad versions, kicad_list_versions, KiCad bridge."
---
# adom-desktop — KiCad bridge
All commands dispatch to the KiCad Python bridge running alongside the desktop app. Invoke from this container via Bash:
```bash
adom-desktop kicad_<action> '<json_args>'
```
The leading `kicad_` routes the call to the KiCad plugin; the `<action>` names below are the plugin-side command names.
## FIRST-TIME / COLD-START — read before you panic
A fresh machine can be missing KiCad, or the reverse-bridge plugin auto-installs on your first call. These states are **EXPECTED** — don't declare defeat. Branch on the stable `errorCode` (not the prose), and relay the returned `_hint` to the user verbatim.
| Signal (`errorCode` / field) | What it means | What YOU do |
|---|---|---|
| `host_app_not_installed` (generic; `hostApp:"KiCad"`. Legacy alias `kicad_not_installed` in `errorCodeLegacy`) | KiCad (the user's host app) isn't on this machine. AD detects host apps but never auto-installs them. | **Don't send the user to download it.** Offer to install it, then run `kicad_upgrade '{}'` — the agent installs the official build from scratch. Then retry. |
| `kicad_version_not_installed` | The `kicadVersion` you asked for isn't installed. | Run `kicad_list_versions '{}'`; retry with an installed version or omit `kicadVersion` to use the newest. |
| `pluginAutoInstall` present in a response | Your first `kicad_*` call auto-installed the reverse-bridge plugin into KiCad (idempotent). | Nothing — expected. Subsequent calls are near-zero cost. |
| bridge not responding on the first call | AD is lazy-spawning the bridge (+ ~9s KiCad detection). | Wait a beat and retry; it binds once and stays up. |
**Readiness before you drive:**
- `kicad_readiness '{}'` — read-only, zero side effects: is KiCad installed/ready? (`{detected, ready, versionCount, defaultVersion}`.) There's **no prewarm** — KiCad is a host app you install on request via `kicad_upgrade`, not a downloaded runtime asset.
- `kicad_describe '{}'` — the full machine-readable verb catalog (the completeness escape hatch; the per-call `_hint`s are what carry you in practice).
- For **"what EDA tools do I have"** across all bridges, use AD's `bridge_readiness` (see below).
## Installing KiCad on a fresh machine (hard-won, v0.9.26+)
`kicad_upgrade` downloads the official downloads.kicad.org installer and silent-installs it. Two things that WILL bite on a truly fresh machine, both handled for you now:
- **Install scope is mandatory in silent mode.** KiCad 10's installer is NsisMultiUser: bare `/S` exits instantly with **`rc=666660`** ("invalid command-line parameters") and installs nothing. The bridge auto-picks the scope: **`/allusers`** when the AD process is elevated (installs to Program Files), else **`/currentuser`** (installs to `%LOCALAPPDATA%\Programs\KiCad` — **no admin, no UAC prompt at all**). Force it with `kicad_upgrade '{"scope":"user"}'` — that's your answer to *"install KiCad without a UAC prompt."* (Elevation note: on a VM/kiosk the AD process may already be elevated even though nobody clicked UAC — an elevated installer that launches the app hands down its admin token. `kicad_upgrade '{"diagnoseOnly":true}'` reports the real token/UAC facts.)
- **Library installs work before KiCad's first launch.** `%APPDATA%/kicad/<ver>/` (the per-user lib tables) doesn't exist until KiCad's first GUI run, so `kicad_install_library`/`_footprint` used to fail *"config directory not found."* The bridge now bootstraps that config (seeding the lib tables from the install template) automatically — installs just work on a never-launched KiCad.
**GPU-less hosts (VM/RDP/server):** KiCad pops a blocking *"Could not use OpenGL"* dialog and the heavy editors may not render. The bridge auto-expires that dialog and switches KiCad to the Cairo software canvas — see **[kicad-interaction](skills/kicad-interaction/SKILL.md)** for the dialog/canvas details.
## Multi-version (KiCad 9 + KiCad 10 side-by-side)
Users can have multiple KiCad versions installed at the same time. The bridge auto-discovers everything under `C:/Program Files/KiCad/<version>/` and **defaults to the newest installed version** if you don't specify one.
```bash
# Discover what's installed (run this first if unsure)
adom-desktop kicad_list_versions '{}'
# → {"versions":[{"version":"10.0", "default":true, ...}, {"version":"9.0", ...}], "default":"10.0", "count":2}
```
Every `kicad_open_*` (and `install_*`, `run_drc`) command accepts an optional **`kicadVersion`** arg to pin a specific install:
```bash
# Open in KiCad 10 (newest — default if you omit kicadVersion)
adom-desktop kicad_open_board '{"filePath":"C:/designs/foo.kicad_pcb"}'
# Open in KiCad 9 explicitly (e.g. to check that an older project still works)
adom-desktop kicad_open_board '{"filePath":"C:/designs/foo.kicad_pcb","kicadVersion":"9.0"}'
```
If you pass a version that's not installed, the bridge returns a hint listing the installed versions. The success response includes `kicadVersionUsed` so you can confirm which install actually ran.
> **Note on libraries / config**: each KiCad version has its own `sym-lib-table` and `fp-lib-table` (in `%APPDATA%/kicad/<version>/`). `kicad_install_library` writes to the table of whichever version was selected — so if the user installs a library and then asks "open it in KiCad 9", you may need to install it under both versions.
## "What EDA tools do I have?" — readiness & host-app detection
**KiCad is the user's HOST APP. Adom Desktop *detects* it — it won't silently auto-install it as a prewarm/dependency** (AD v1.9.47+). But if KiCad isn't found, **do NOT hand the user a manual download** — offer to install it for them and run `kicad_upgrade '{}'`. That verb installs the latest stable from the official downloads.kicad.org installer (silent `/S`, Windows only; may raise a UAC prompt) and **installs from scratch, not just upgrades**. The distinction: AD-core never auto-installs a host app, but a user-requested, agent-run install is exactly right.
- To answer **"what EDA tools / host apps do I have?"** across every bridge, call the AD-level verb **`adom-desktop bridge_readiness '{}'`** — AD aggregates each bridge's host-app detection (KiCad, Fusion 360, …) into one report. Start here for any "do I have X installed?" question.
- For **KiCad specifically**, `adom-desktop kicad_readiness '{}'` is a zero-side-effect read-only probe: `{hostApp, detected, ready, versionCount, defaultVersion, versions[]}`. It does not install the plugin or spawn subprocesses, and works even when KiCad is absent (`detected:false`). Use `kicad_list_versions` when you need the full per-version detail (paths, IPC API, lib tables).
## Commands
| CLI form | Action | Purpose | Key args |
|---|---|---|---|
| `kicad_list_versions` | `list_versions` | Enumerate every installed KiCad version with paths and which one is the default | — |
| `kicad_readiness` | `readiness` | Read-only host-app detection: is KiCad installed/ready? Zero side effects (no plugin load, no subprocess); works even when KiCad is absent. For cross-bridge "what do I have", use AD's `bridge_readiness` | — |
| `kicad_check_for_updates` | `check_for_updates` | Read-only: installed KiCad vs latest STABLE (from downloads.kicad.org / KiCad GitLab tags); flags when the install predates the modern IPC/kipy automation API and should be upgraded | — |
| `kicad_upgrade` | `upgrade` | Download the **official** KiCad installer from downloads.kicad.org and silent-install it (`/S`) — **not winget** (winget lags the real release). May raise a UAC prompt to approve | optional `version`, `force` |
| `kicad_open_schematic` | `open_schematic` | Open a `.kicad_sch` in the Schematic Editor | `filePath`, optional `kicadVersion` |
| `kicad_open_board` | `open_board` | Open a `.kicad_pcb` in the PCB Editor | `filePath`, optional `kicadVersion` |
| `kicad_open_symbol_editor` | `open_symbol_editor` | Launch Symbol Editor (optional: open specific library/symbol) | `libraryName`, `symbolName`, optional `kicadVersion` |
| `kicad_open_footprint_editor` | `open_footprint_editor` | Launch Footprint Editor | `libraryName`, `footprintName`, optional `kicadVersion` |
| `kicad_open_3d_viewer` | `open_3d_viewer` | Show the board (or footprint) in 3D viewer | `editor` ("auto"/"pcb"/"fp" — "auto" prefers the board when both editors are open, no need to close sub-editors), optional `kicadVersion` |
| `kicad_close_symbol_editor` | `close_symbol_editor` | Close Symbol Editor | — |
| `kicad_close_footprint_editor` | `close_footprint_editor` | Close Footprint Editor | — |
| `kicad_close_3d_viewer` | `close_3d_viewer` | Close 3D viewer | — |
| `kicad_close` | `close` | Close KiCad (all editors) | — |
| `kicad_window_info` | `window_info` | Enumerate open KiCad windows (HWND, title, bounds, editor type) | — |
| `kicad_install_library` | `install_library` | Register a `.kicad_sym` + `.pretty/` pair in sym-lib-table + fp-lib-table | `libraryPath`, `libraryType`, `libraryName`, optional `kicadVersion` |
| `kicad_install_symbol` | `install_symbol` | Add a single symbol to an existing library | `fileName`, `fileContent`, optional `kicadVersion` |
| `kicad_install_footprint` | `install_footprint` | Add a single footprint to an existing library | `library`, `footprint_path`, optional `kicadVersion` |
| `kicad_run_drc` | `run_drc` | Run DRC on the current board, return violations | `filePath`, optional `kicadVersion` |
| `kicad_fix_keyboard` | `fix_keyboard` | Workaround stuck-modifier bug after screen lock | — |
| `kicad_screenshot_all` | `screenshot_all` | Capture every open KiCad window (lossless PNGs) | — |
| `kicad_send_key` | `send_key` | Single key to an EXACT `hwnd` (e.g. Enter/Escape to a dialog). For modifier CHORDS ("ctrl+s", "alt+3") use AD's `desktop_press_key` — don't route chords here. | `hwnd`, `key` |
| `kicad_click` | `click` | Send a click at (x, y) in a KiCad window | `hwnd`, `x`, `y` |
| `kicad_adom_library_status` | `adom_library_status` | Report whether the Adom shared library is registered | — |
## Quick examples
```bash
# Open a schematic
adom-desktop kicad_open_schematic '{"path":"C:/designs/foo.kicad_sch"}'
# List open KiCad windows
adom-desktop kicad_window_info '{}'
# Screenshot every KiCad window
adom-desktop kicad_screenshot_all '{}'
# Run DRC
adom-desktop kicad_run_drc '{"board_path":"C:/designs/foo.kicad_pcb"}'
# Install a library pair
adom-desktop kicad_install_library '{
"name":"my-lib",
"sym_path":"C:/libs/my-lib.kicad_sym",
"fp_path":"C:/libs/my-lib.pretty"
}'
```
## Error shape
Every failing response includes a `_hint` field with a human-readable next step — surface it to the user verbatim when a KiCad command fails.
## Bridge architecture (v1.8.31+) — you don't need to know any port
Earlier versions of the KiCad bridge listened on hardcoded port `8772`. **As of v1.8.31, bridges use OS-assigned ephemeral ports** — every spawn gets a new free port. You never need to know it.
- The CLI (`adom-desktop kicad_*`) automatically routes through adom-desktop's direct API (default port 47200 as of v1.8.33, with auto-fallback to 47201-47209 if 47200 is taken). adom-desktop then forwards to the bridge's actual runtime port.
- The bridge port is internal plumbing and can change every spawn. `bridge_list` reports it as `spawn.runtimePort` for debugging only — do NOT hardcode that anywhere.
- Why: this lets adom-desktop coexist with Hydrogen Desktop (which used to claim 8772 itself) and any other tool that grabs ports in the 8000 range. Side-by-side bridges from different apps no longer collide.
- The relay path (Docker → wss proxy → Windows GUI) is unchanged — same `adom-desktop kicad_*` verbs, same JSON shape.
If you ever find yourself wanting to talk directly to the KiCad bridge process: don't. Go through the verb dispatch. If the verb you need doesn't exist, request a feature in `adom-inc/adom-desktop`.
## Related
- `adom-desktop-discovery` (gallia) — ensures the CLI + relay are installed.
- `adom-desktop-fusion` — sibling bridge for Fusion 360.
- `adom-desktop-direct-api` — the direct API contract (port 47200 + 47201-47209 fallback, discovery file at `~/.adom/direct-api-port`).
- Repo: `adom-inc/adom-desktop/plugins/kicad/`