← Commit history

v0.9.38: auto-dismiss KiCad first-run wizard + older-version notice; README window-tour overhaul (VM screenshots, control-surfaces SVG, install/dialog/software-OpenGL sections)

John Lauer ·17c839f49b ·3mo ago ·parent 70830c7
4 files changed +187−194
README.md+168−188
@@ -1,188 +1,168 @@-# Reference bridge: KiCad
-
-## Source code + contributing (2026-05)
-
-The KiCad bridge moved out of `adom-inc/adom-desktop`'s monorepo into its own dedicated repo so the community can extend it without coordinating with Adom Desktop's release cycle.
-
-### Where the source lives
-
-The canonical repo is **[`adom-inc/adom-bridge-kicad`](https://github.com/adom-inc/adom-bridge-kicad)** on GitHub. It's private today and will go public after a review pass.
-
-In the meantime, the full source — code, history, contributing guide, license — is **mirrored on this wiki page**:
-
-| Download | What it is | How to use |
-|---|---|---|
-| [`adom-bridge-kicad-source.tar.gz`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop-kicad-bridge/adom-bridge-kicad-source.tar.gz) | Clean source tree, no `.git` directory. ~165 KB. | `tar xzf adom-bridge-kicad-source.tar.gz && cd adom-bridge-kicad/` |
-| [`adom-bridge-kicad.bundle`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop-kicad-bridge/adom-bridge-kicad.bundle) | Git bundle with full commit history. ~240 KB. | `git clone https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop-kicad-bridge/adom-bridge-kicad.bundle adom-bridge-kicad && cd adom-bridge-kicad && git log` |
-
-No GitHub account needed to download either — both URLs are public.
-
-### How to contribute
-
-We accept community improvements. Most useful targets right now:
-
-- **KiCad 11 support** — when KiCad 11 ships, the `kicad_detect.py` discovery + the in-process bridge plugin both need updates. Fork-friendly.
-- **macOS / Linux ports** — bridge currently Windows-only; cross-platform PRs welcome.
-- **New verbs** — anything KiCad exposes via `pcbnew.*` / `kicad-cli` that you'd find useful.
-
-Two contribution paths, depending on your access:
-
-**With a GitHub account (preferred):** wait until we make the repo public, then fork on GitHub and open a PR. We're aiming to flip the repo public in the next maintenance pass.
-
-**Without GitHub access (patch-by-email-style, available right now):** Clone the bundle, make your changes, generate a patch with `git format-patch origin/main..HEAD --stdout > my-changes.patch`, and email it to **`[email protected]`**. Adom maintainers will review and apply.
-
-See `CONTRIBUTING.md` inside the tarball/bundle for the full PR + ship workflow.
-
-### How shipping works after merge
-
-1. Maintainer merges your PR into `main`
-2. Maintainer tags `v<X.Y.Z>` + runs `bash scripts/release-bridge.sh kicad` on their box
-3. New zip + manifest published to this wiki page (replaces current version)
-4. Every Adom Desktop install running v1.7.19+ auto-picks up your change within 4 hours OR immediately via `adom-desktop refresh_bridges`
-
-No NSIS installer rebuild required. Your code reaches real users in ~30s after merge.
-
-### License
-
-MIT — see the `LICENSE` file in the tarball.
-
----
-
-
-The KiCad bridge is one of three reference implementations bundled with Adom Desktop. It's the most complex of the three — a multi-instance Python server that runs alongside KiCad GUI processes and provides both a forward path (CLI → bridge → kicad-cli) and a reverse path (CLI → bridge → in-process Python plugin inside KiCad).
-
-If you're authoring a new third-party bridge, see the [bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges) first. Use this page for architectural reference once you've grasped the basics.
-
-## Quick facts
-
-| | |
-|---|---|
-| Name | `kicad` |
-| Display | KiCad EDA |
-| Spawn kind | Python (`server.py`) |
-| Port | 8772 |
-| Verb prefix | `kicad_*` (23 verbs) |
-| Watcher | yes (`*.kicad_pcb`, `*.kicad_sch`, `*.kicad_pro`) |
-| Source zip | [`kicad-bridge-v0.8.2.zip`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-v0.8.2.zip) |
-| Manifest | [`kicad-bridge-manifest.json`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-manifest.json) |
-| User-facing skill | [`kicad-SKILL.md`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-SKILL.md) |
-
-## What it does
-
-- **Forward path** (CLI → bridge → kicad-cli): board/schematic linting (`kicad_lint_board`, `kicad_lint_schematic`, `kicad_lint_library`), DRC/ERC (`kicad_run_drc`, `kicad_run_erc`), format upgrades, exports.
-- **Window automation** (CLI → bridge → Win32): launch editors (`kicad_open_board`, `kicad_open_schematic`, …), list open editors, capture screenshots of all KiCad windows, send keystrokes, click at coords.
-- **Reverse path** (CLI → bridge → in-process plugin inside KiCad): `kicad_bridge_call` invokes a Python handler running INSIDE the running KiCad GUI exe, with access to `pcbnew.*` / future `kipy`. This gives Docker-side Claude live introspection of the actually-open board without a round-trip through file exports.
-
-## `bridge.json`
-
-```json
-{
-  "manifest_version": 1,
-  "name": "kicad",
-  "displayName": "KiCad EDA",
-  "version": "0.8.2",
-  "description": "Reverse bridge for KiCad — board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",
-  "homepage": "https://github.com/adom-inc/adom-desktop",
-  "author": "Adom Inc.",
-  "license": "MIT",
-  "spawn": {
-    "kind": "python",
-    "entrypoint": "server.py",
-    "port": 8772,
-    "healthEndpoint": "http://127.0.0.1:8772/status",
-    "stopMethod": "kill",
-    "killImageName": "python.exe"
-  },
-  "verbPrefixes": ["kicad_"],
-  "verbs": [
-    "kicad_open_board", "kicad_open_schematic", "kicad_open_symbol_editor",
-    "kicad_open_footprint_editor", "kicad_open_3d_viewer",
-    "kicad_list_versions",
-    "kicad_run_drc", "kicad_run_erc",
-    "kicad_lint_board", "kicad_lint_schematic", "kicad_lint_library",
-    "kicad_format_upgrade",
-    "kicad_install_plugin", "kicad_install_symbol", "kicad_install_footprint",
-    "kicad_bridge_status", "kicad_bridge_call",
-    "kicad_open_editors", "kicad_window_info", "kicad_screenshot_all",
-    "kicad_send_key", "kicad_click", "kicad_close"
-  ],
-  "dependencies": { "python": ">=3.11", "kicad": ">=10.0" },
-  "category": "EDA",
-  "tags": ["pcb", "schematic", "open-source", "kicad-10", "kicad-11-ready"],
-  "watcher": {
-    "supports": true,
-    "displayName": "Project Watch",
-    "description": "Monitors a KiCad project folder for file changes and syncs them to Docker.",
-    "defaultGlobs": ["*.kicad_pcb", "*.kicad_sch", "*.kicad_pro"],
-    "configKey": "project_watch"
-  }
-}
-```
-
-The `watcher` field declares that this bridge supports project-watching — the GUI renders a 👁 pip on the chip when watching is active. See the [bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges#bridgejson-schema) for the full schema.
-
-## Architecture highlights
-
-```
-kicad/
-├── server.py              ← HTTP entry point; routes /command to handlers/
-├── handlers/              ← One module per logical verb group
-│   ├── board_ops.py       ← kicad_run_drc, kicad_lint_board, kicad_open_board, …
-│   ├── schematic_ops.py
-│   ├── library_ops.py
-│   ├── window_automation.py  ← Win32 SendInput, PrintWindow, EnumWindows
-│   ├── bridge_call.py     ← Reverse bridge dispatch into the in-process plugin
-│   └── …
-├── kicad_detect.py        ← Find installed KiCad versions, exe paths, USER_SITE
-├── adom_library.py        ← Symbol/footprint discovery in sym-lib-table.txt
-├── lib_table.py           ← Add/remove rows in *-lib-table files
-├── plugin_install.py      ← Copy adom_bridge.py into USER_SITE/usercustomize.py
-├── plugin_payload/
-│   └── adom_bridge.py     ← Reverse-bridge plugin loaded INSIDE every KiCad GUI exe
-├── parsers/               ← .kicad_sym, .kicad_mod, .kicad_pcb tokenizers
-└── start.bat              ← Windows launcher for local dev
-```
-
-The reverse-bridge plugin (`plugin_payload/adom_bridge.py`) is what makes the kicad bridge special. KiCad's bundled Python interpreter overrides `site.USER_SITE` to `%USERPROFILE%\Documents\KiCad\10.0\3rdparty\Python311\site-packages\`, and the `usercustomize.py` import hook fires on every KiCad GUI exe start (`kicad.exe`, `pcbnew.exe`, `eeschema.exe`, `kicad-cli.exe`). Our plugin uses this hook to launch a tiny websocket client that connects to the bridge — turning every running KiCad into a remote-controllable instance.
-
-## Reverse-bridge example
-
-```bash
-# From cloud Docker: list every open KiCad GUI exe + which board it has loaded
-adom-desktop kicad_open_editors
-
-# Direct in-process call: ask the running pcbnew.exe what board it has open
-adom-desktop kicad_bridge_call '{"method":"get_board_info"}'
-
-# Run a DRC IN-PROCESS — no file write, no kicad-cli detour
-adom-desktop kicad_bridge_call '{"method":"run_drc","args":{"severity":"error"}}'
-```
-
-The bridge dispatches these to `adom_bridge.py` running inside pcbnew. The handler executes against the live `pcbnew.GetBoard()` and returns JSON over the websocket.
-
-## Reference: install the source locally to read
-
-```bash
-# Pull the zip (no auth needed — wiki static URL)
-curl -L https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-v0.8.2.zip -o /tmp/kicad-bridge.zip
-
-# Extract
-mkdir -p /tmp/kicad-bridge && cd /tmp/kicad-bridge && python3 -c "import zipfile; zipfile.ZipFile('/tmp/kicad-bridge.zip').extractall()"
-ls -la
-```
-
-You'll see `server.py`, `bridge.json`, `handlers/`, `plugin_payload/`, etc. — fork the layout for your own bridge.
-
-## Related
-
-- **[Bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges)** — `bridge.json` schema, packaging recipe, lifecycle
-- **[Puppeteer bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-puppeteer-bridge)** — simpler architecture, single-instance, Node + Chrome for Testing
-- **[Fusion 360 bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-fusion-bridge)** — passthrough-to-add-in pattern
-- **[adom-desktop main page](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop)** — install the parent app first---![How the KiCad bridge works](arch.png)--![KiCad verbs Claude can call](verbs.png)--![What you can ask Claude to do](outcomes.png)+# Adom Desktop — KiCad Bridge++A **reverse bridge** that lets Adom Desktop (AD) — and the AI driving it — control the user's own+KiCad on Windows: open and drive every editor, install symbol/footprint libraries, place parts, run+DRC/ERC, export manufacturing files, and screenshot any window back to the AI.++KiCad is the user's **host app**. The bridge *detects* an existing KiCad and, if none is present,+*installs* one for them — it never asks the user to download or click through anything by hand.++- **Page / docs:** https://wiki.adom.inc/adom/adom-desktop-kicad-bridge+- **Bridge SDK:** https://wiki.adom.inc/adom/adom-desktop-bridges+- **Verb namespace:** `kicad_*` &nbsp;·&nbsp; **Status verb:** `kicad_bridge_status` &nbsp;·&nbsp; **Health:** `GET /status`++> Every screenshot in this README was captured on **ADOMBASELINE**, a stock Hyper-V VM with **no+> GPU**, driven end-to-end through the bridge — install → libraries → editors → 2D → 3D. If it+> renders there, it renders on a real laptop.++---++## How it fits together++The bridge is a `spawn.kind: python` process AD launches on the user's machine (`entrypoint:+server.py`, `port: 0` — AD picks the port and passes `ADOM_BIND_HOST`). It speaks HTTP+(`POST /command`, `GET /status`) and reaches KiCad through **four control surfaces**, picking the+lightest one that can do the job.++![Control surfaces](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/control-surfaces.svg)++| # | Surface | Used for | Where |+|---|---------|----------|-------|+| 1 | **kicad-cli** (subprocess) | headless DRC/ERC, gerber/pdf/svg/step/bom export, format upgrade | `handlers/export.py`, `run_drc`, `run_erc`, `lint_*` |+| 2 | **KiCad IPC (kipy)** | board/schematic introspection, confirmed footprint placement (KiCad 9+) | `handlers/place_footprint.py` |+| 3 | **Embedded Python** | JSON-RPC *inside* each KiCad process, dispatched on its wx UI thread | `plugin_payload/adom_bridge.py`, `handlers/bridge_client.py` |+| 4 | **Win32 / UIA / SendKeys** | open editors, screenshots, clicks/keys, dialog handling, window management | `handlers/kicad_ui.py`, `handlers/close_windows.py` |++---++## The window tour++Everything below was opened and captured on the GPU-less VM. Sample data is the **RP2040 breakout**+generated by `tour-pack-rp2040/` (an `AdomRP2040` symbol + `QFN-56_AdomRP2040` footprint + a board).++### Schematic editor — `kicad_open_schematic`++The RP2040 breakout: the MCU (U1), USB-C, a 12 MHz crystal, decoupling, and mounting pins.++![Schematic editor](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/schematic-editor.png)++### Symbol editor — `kicad_open_symbol_editor`++Opens straight to a symbol (`kicad_install_symbol` puts it in a user library first).++![Symbol editor](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/symbol-editor.png)++### Footprint editor — `kicad_open_footprint_editor`++`{"footprintName":"QFN-56_AdomRP2040","library":"AdomRP2040"}` loads the part directly — 56 pins ++thermal pad, 7×7 mm, 0.4 mm pitch, courtyard and silkscreen.++![Footprint editor](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/footprint-editor.png)++### PCB editor (2D) — `kicad_open_board`++The routed breakout — copper, silkscreen, the QFN-56 land pattern, mounting holes.++![PCB editor 2D](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/pcb-editor-2d.png)++### 3D viewer — `kicad_open_3d_viewer {"editor":"pcb"}`++The full board in 3D — board body, the USB-C connector's 3D model, the QFN chip body, SMD parts,+plated through-holes. **Rendered on the CPU via the software-OpenGL fallback (below); reload 1.7 s.**++![3D viewer](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/3d-viewer.png)++---++## Install & upgrade — zero manual steps++The bridge never tells the user to go download KiCad. `kicad_upgrade` fetches the official installer+and runs it silently, picking the scope automatically:++- **Elevated AD** → `/allusers /S` (system-wide, `%ProgramFiles%\KiCad`).+- **Non-elevated AD** → `/currentuser /S` (`%LocalAppData%\Programs\KiCad`, **no UAC prompt**).++`kicad_readiness` reports whether KiCad is installed and ready without side effects; the AI routes on+it before offering to install. Hard-won install details (all handled for you):++- KiCad 10's NsisMultiUser installer **requires** a scope flag — bare `/S` errors `rc=666660`.+- AD's portable Python has **no CA bundle**; the bridge ships `certs/cacert.pem` and uses it for TLS.+- Downloads are size-checked against `Content-Length` (a truncated installer otherwise fails at NSIS).+- `%APPDATA%/kicad/<ver>/` lib tables don't exist until first launch — the bridge **bootstraps** them+  so `install_library`/`install_footprint` work on a never-opened KiCad.+- `kicad_upgrade {"diagnoseOnly":true}` reports token-elevation type + `EnableLUA` without installing.++---++## Dialogs & error handling — the bridge clears the pointless ones++KiCad throws modal dialogs that stall automation. The bridge **scans every window owned by a running+KiCad process** (by PID — reliable, unlike title matching), auto-expires the benign ones, screenshots+what it dismissed, and returns a hint so the AI can decide what (if anything) to tell the user.++Auto-expired benign dialogs include:++- **"Could not use OpenGL / falling back to software rendering"** (GPU-less hosts).+- **"Welcome to KiCad — starting for the first time"** first-run wizard (dismissing accepts defaults).+- **"This file was created by an older version of KiCad"** conversion notice.++![First-run wizard, auto-dismissed](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/dialog-firstrun-wizard.png)++Verbs:++- `kicad_window_info` — lists windows and **self-heals** (auto-expires benign dialogs) by default.+- `kicad_dismiss_dialogs {"all":true}` — clear everything blocking; `{"screenshot":true}` returns+  images of each; `{"forceSoftwareCanvas":true}` persists Cairo canvas; `{"debug":true}` dumps the+  raw PID-based scan.+- `kicad_screenshot_all` — one call returns **every** open KiCad window, so the AI can spot an error+  dialog it didn't expect and read the message.++---++## Software-OpenGL fallback (last resort)++`kicad_enable_software_opengl` deploys Mesa's `llvmpipe` (a CPU OpenGL rasterizer) into KiCad's `bin`+so a box with **no usable GPU** — Hyper-V, RDP, headless CI — can still render the editors and the 3D+viewer. This is how every 3D shot above exists.++> ⚠️ It renders on the CPU and is **slow**. It is a worst-case fallback only — a real GPU (or GPU-P /+> DDA passthrough) is vastly better. The bridge keeps it so a GPU-less box isn't a dead end; it is+> never suggested proactively.++---++## Verb reference++All 41 verbs (prefix `kicad_`). Call `kicad_describe` for the live catalog with per-verb hints,+related verbs, and pitfalls.++**Windows & UI** — `open_board`, `open_schematic`, `open_symbol_editor`, `open_footprint_editor`,+`open_3d_viewer`, `open_editors`, `close_symbol_editor`, `close_footprint_editor`, `close_3d_viewer`,+`close`, `window_info`, `dismiss_dialogs`, `screenshot_all`, `send_key`, `click`, `fix_keyboard`++**Libraries & parts** — `install_library`, `install_symbol`, `install_footprint`, `install_plugin`,+`place_footprint`, `adom_library_status`++**Checks & export** — `run_drc`, `run_erc`, `lint_board`, `lint_schematic`, `lint_library`,+`format_upgrade`, `export_gerber`, `export_pdf`, `export_svg`, `export_step`, `export_bom_csv`++**Detect, install & meta** — `list_versions`, `readiness`, `describe`, `enable_software_opengl`,+`check_for_updates`, `upgrade`, `bridge_status`, `bridge_call`++---++## Running / dependencies++AD provisions everything; there is nothing to install by hand.++- **Python** ≥ 3.11 (AD provisions it) — stdlib only, plus the bundled `certs/cacert.pem`.+- **KiCad** ≥ 7.0 (host app; `kicad_upgrade` installs it if absent).+- **OS:** Windows (macOS/Linux detection stubs exist; the GUI surfaces are Windows-first).++The bridge binds `ADOM_BIND_HOST` on an AD-assigned port — never `0.0.0.0`. Auto-updates via+`updateManifestUrl` in `bridge.json`.++---++*Developer & publish notes live in `dev-skills/` and `publish-skills/` (source-only, never shipped to+a user install).*
adom-bridge-kicad-manifest.json+18−5
@@ -1,5 +1,18 @@-{ "manifest_version": 1, "name": "kicad", "version": "0.9.37",-  "url": "https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/adom-bridge-kicad-v0.9.37.zip",-  "sha256": "3082569f00135e0656aa7ec3ef30a3fbc13ade7aaf366dc8eef8927be967b36b", "size": 628912, "verbPrefixes": ["kicad_"], "healthEndpoint": "/status",-  "released_at": "2026-07-04T14:44:55Z", "hero": "https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-hero.png",-  "languages": ["Python","PowerShell"] }+{+  "manifest_version": 1,+  "name": "kicad",+  "version": "0.9.38",+  "url": "https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/adom-bridge-kicad-v0.9.38.zip",+  "sha256": "217b3ccb1bd640abd14ad3c7b33b86c9a8a9a60112ca795332c2414199a4b604",+  "size": 622873,+  "verbPrefixes": [+    "kicad_"+  ],+  "healthEndpoint": "/status",+  "released_at": "2026-07-04T14:44:55Z",+  "hero": "https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-hero.png",+  "languages": [+    "Python",+    "PowerShell"+  ]+}
adom-bridge-kicad-v0.9.38.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.37",+  "version": "0.9.38",   "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",   "homepage": "https://wiki.adom.inc/adom/adom-desktop",   "author": "Adom Inc.",