← Commit history

issue #10: rebase retired wiki-ufypy5dpx93o.adom.cloud -> wiki.adom.inc (CONTRIBUTING x2, CI workflow, page.json readme); resync page.json readme from the live README (was a 10KB snapshot from v0.8.2); drop the dead release-bridge.sh flow

John Lauer ·3802ba959f ·2mo ago ·parent af1082a
3 files changed +72−4
.github/workflows/validate.ymladded+68
@@ -0,0 +1,68 @@+name: Validate bridge.json + version bump++# PR-only trigger. We don't run on direct pushes to main because the only+# pushes today are maintainer ones (already validated locally), and running+# on every push generates failed-run emails when the org's GH Actions+# billing isn't current. The real value of this workflow is gating+# community PRs before they reach main.++on:+  pull_request:+    branches: [main]++jobs:+  validate:+    runs-on: ubuntu-latest+    steps:+      - uses: actions/checkout@v4+        with:+          fetch-depth: 0  # need main + PR head for version-bump check++      - name: Validate bridge.json is parseable JSON+        run: |+          python3 -c "import json; d = json.load(open('bridge.json')); print(f'Parsed bridge.json: {d[\"name\"]} v{d[\"version\"]}')"++      - name: Check required fields exist+        run: |+          python3 - <<'PY'+          import json, sys+          d = json.load(open('bridge.json'))+          required = ['manifest_version', 'name', 'displayName', 'version', 'spawn', 'verbPrefixes']+          missing = [k for k in required if k not in d]+          if missing:+              print(f'ERROR: bridge.json missing required fields: {missing}')+              sys.exit(1)+          spawn = d['spawn']+          for k in ['kind', 'entrypoint']:+              if k not in spawn:+                  print(f'ERROR: bridge.json spawn missing required field: {k}')+                  sys.exit(1)+          # v1.8.31+: spawn.port should be 0 (dynamic) or warn+          if spawn.get('port', 0) != 0:+              print(f'WARN: spawn.port = {spawn["port"]} — dynamic port (0) is recommended for v1.8.31+. Hardcoded ports collide with sibling apps. See https://wiki.adom.inc/apps/adom-desktop-bridges')+          # Verb prefixes should end in underscore+          for p in d.get('verbPrefixes', []):+              if not p.endswith('_'):+                  print(f'WARN: verbPrefix "{p}" should end in underscore (convention)')+          print('bridge.json schema check passed')+          PY++      - name: Verify BRIDGE_VERSION matches bridge.json.version+        run: |+          BV=$(cat BRIDGE_VERSION | tr -d '[:space:]')+          JV=$(python3 -c "import json; print(json.load(open('bridge.json'))['version'])")+          if [ "$BV" != "$JV" ]; then+              echo "ERROR: BRIDGE_VERSION ($BV) != bridge.json version ($JV) — bump them together"+              exit 1+          fi+          echo "BRIDGE_VERSION matches bridge.json: $BV"++      - name: Verify version was bumped vs. main+        run: |+          MAIN_VER=$(git show origin/main:BRIDGE_VERSION 2>/dev/null | tr -d '[:space:]' || echo "0.0.0")+          PR_VER=$(cat BRIDGE_VERSION | tr -d '[:space:]')+          if [ "$MAIN_VER" = "$PR_VER" ]; then+              echo "ERROR: BRIDGE_VERSION not bumped (still $MAIN_VER). Bump patch/minor/major in BRIDGE_VERSION AND bridge.json."+              exit 1+          fi+          echo "Version bumped: $MAIN_VER → $PR_VER"
CONTRIBUTING.md+3−3
@@ -12,13 +12,13 @@ This bridge is part of the [Adom Desktop](https://github.com/adom-inc/adom-deskt 1. **Develop locally.** Clone this repo, edit, test. The bridge runs alongside an installed [Adom Desktop](https://github.com/adom-inc/adom-desktop); use `adom-desktop bridge_install` with a local `file://` manifest URL to test your changes against a real GUI. 2. **Open a PR** against `main`. The CI in `.github/workflows/validate.yml` checks that `bridge.json` is valid + verb prefixes look right + the BRIDGE_VERSION was bumped vs. main. 3. **Maintainer reviews + merges.** No squash-merge — we preserve your authored commits.-4. **Maintainer tags + ships.** A tag `v<X.Y.Z>` triggers `release-bridge.sh` on a maintainer's box, which uploads the new zip + manifest to [the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges). Anyone running Adom Desktop v1.7.19+ picks it up within 4 hours or on the next `adom-desktop refresh_bridges`.+4. **Maintainer tags + ships.** A maintainer bumps the version in lockstep (`bridge.json` / `BRIDGE_VERSION` / the manifest), builds the runtime zip and publishes it as a **Release asset** with `adom-wiki release create` + `release upload`, alongside the manifest on [the Adom Wiki](https://wiki.adom.inc/adom/adom-desktop-bridges). Anyone running Adom Desktop v1.7.19+ picks it up within 4 hours or on the next `adom-desktop refresh_bridges`. -We DON'T auto-ship from GitHub Actions today. Reason: `release-bridge.sh` needs an Adom Wiki API token, and we keep that secret on maintainer machines rather than in GH Actions secrets. The community trust model around auto-publishing community PRs to a shared wiki space is part of a separate effort ("Chunk D" in adom-desktop's TODO.md) — comment on an issue if you want to help design that.+We DON'T auto-ship from GitHub Actions today. Reason: publishing needs an Adom Wiki API token (`adom-wiki` auth), and we keep that secret on maintainer machines rather than in GH Actions secrets. The community trust model around auto-publishing community PRs to a shared wiki space is part of a separate effort ("Chunk D" in adom-desktop's TODO.md) — comment on an issue if you want to help design that.  ## Bridge.json schema -See [the SDK guide on the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges) for the authoritative schema. Quick summary:+See [the SDK guide on the Adom Wiki](https://wiki.adom.inc/adom/adom-desktop-bridges) for the authoritative schema. Quick summary:  - `manifest_version: 1` - `name` — must match the directory name in adom-desktop's `plugins/` (when bundled)
page.json+1−1
@@ -86,7 +86,7 @@     "install_hint": "",     "version_cmd": ""   },-  "readme": "# Reference bridge: KiCad\r\n\r\n## Source code + contributing (2026-05)\r\n\r\nThe 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.\r\n\r\n### Where the source lives\r\n\r\nThe 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.\r\n\r\nIn the meantime, the full source — code, history, contributing guide, license — is **mirrored on this wiki page**:\r\n\r\n| Download | What it is | How to use |\r\n|---|---|---|\r\n| [`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/` |\r\n| [`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` |\r\n\r\nNo GitHub account needed to download either — both URLs are public.\r\n\r\n### How to contribute\r\n\r\nWe accept community improvements. Most useful targets right now:\r\n\r\n- **KiCad 11 support** — when KiCad 11 ships, the `kicad_detect.py` discovery + the in-process bridge plugin both need updates. Fork-friendly.\r\n- **macOS / Linux ports** — bridge currently Windows-only; cross-platform PRs welcome.\r\n- **New verbs** — anything KiCad exposes via `pcbnew.*` / `kicad-cli` that you'd find useful.\r\n\r\nTwo contribution paths, depending on your access:\r\n\r\n**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.\r\n\r\n**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.\r\n\r\nSee `CONTRIBUTING.md` inside the tarball/bundle for the full PR + ship workflow.\r\n\r\n### How shipping works after merge\r\n\r\n1. Maintainer merges your PR into `main`\r\n2. Maintainer tags `v<X.Y.Z>` + runs `bash scripts/release-bridge.sh kicad` on their box\r\n3. New zip + manifest published to this wiki page (replaces current version)\r\n4. Every Adom Desktop install running v1.7.19+ auto-picks up your change within 4 hours OR immediately via `adom-desktop refresh_bridges`\r\n\r\nNo NSIS installer rebuild required. Your code reaches real users in ~30s after merge.\r\n\r\n### License\r\n\r\nMIT — see the `LICENSE` file in the tarball.\r\n\r\n---\r\n\r\n\r\nThe 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).\r\n\r\nIf 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.\r\n\r\n## Quick facts\r\n\r\n| | |\r\n|---|---|\r\n| Name | `kicad` |\r\n| Display | KiCad EDA |\r\n| Spawn kind | Python (`server.py`) |\r\n| Port | 8772 |\r\n| Verb prefix | `kicad_*` (23 verbs) |\r\n| Watcher | yes (`*.kicad_pcb`, `*.kicad_sch`, `*.kicad_pro`) |\r\n| Source zip | [`kicad-bridge-v0.8.2.zip`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-v0.8.2.zip) |\r\n| Manifest | [`kicad-bridge-manifest.json`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-manifest.json) |\r\n| User-facing skill | [`kicad-SKILL.md`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-SKILL.md) |\r\n\r\n## What it does\r\n\r\n- **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.\r\n- **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.\r\n- **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.\r\n\r\n## `bridge.json`\r\n\r\n```json\r\n{\r\n  \"manifest_version\": 1,\r\n  \"name\": \"kicad\",\r\n  \"displayName\": \"KiCad EDA\",\r\n  \"version\": \"0.8.2\",\r\n  \"description\": \"Reverse bridge for KiCad — board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.\",\r\n  \"homepage\": \"https://github.com/adom-inc/adom-desktop\",\r\n  \"author\": \"Adom Inc.\",\r\n  \"license\": \"MIT\",\r\n  \"spawn\": {\r\n    \"kind\": \"python\",\r\n    \"entrypoint\": \"server.py\",\r\n    \"port\": 8772,\r\n    \"healthEndpoint\": \"http://127.0.0.1:8772/status\",\r\n    \"stopMethod\": \"kill\",\r\n    \"killImageName\": \"python.exe\"\r\n  },\r\n  \"verbPrefixes\": [\"kicad_\"],\r\n  \"verbs\": [\r\n    \"kicad_open_board\", \"kicad_open_schematic\", \"kicad_open_symbol_editor\",\r\n    \"kicad_open_footprint_editor\", \"kicad_open_3d_viewer\",\r\n    \"kicad_list_versions\",\r\n    \"kicad_run_drc\", \"kicad_run_erc\",\r\n    \"kicad_lint_board\", \"kicad_lint_schematic\", \"kicad_lint_library\",\r\n    \"kicad_format_upgrade\",\r\n    \"kicad_install_plugin\", \"kicad_install_symbol\", \"kicad_install_footprint\",\r\n    \"kicad_bridge_status\", \"kicad_bridge_call\",\r\n    \"kicad_open_editors\", \"kicad_window_info\", \"kicad_screenshot_all\",\r\n    \"kicad_send_key\", \"kicad_click\", \"kicad_close\"\r\n  ],\r\n  \"dependencies\": { \"python\": \">=3.11\", \"kicad\": \">=10.0\" },\r\n  \"category\": \"EDA\",\r\n  \"tags\": [\"pcb\", \"schematic\", \"open-source\", \"kicad-10\", \"kicad-11-ready\"],\r\n  \"watcher\": {\r\n    \"supports\": true,\r\n    \"displayName\": \"Project Watch\",\r\n    \"description\": \"Monitors a KiCad project folder for file changes and syncs them to Docker.\",\r\n    \"defaultGlobs\": [\"*.kicad_pcb\", \"*.kicad_sch\", \"*.kicad_pro\"],\r\n    \"configKey\": \"project_watch\"\r\n  }\r\n}\r\n```\r\n\r\nThe `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.\r\n\r\n## Architecture highlights\r\n\r\n```\r\nkicad/\r\n├── server.py              ← HTTP entry point; routes /command to handlers/\r\n├── handlers/              ← One module per logical verb group\r\n│   ├── board_ops.py       ← kicad_run_drc, kicad_lint_board, kicad_open_board, …\r\n│   ├── schematic_ops.py\r\n│   ├── library_ops.py\r\n│   ├── window_automation.py  ← Win32 SendInput, PrintWindow, EnumWindows\r\n│   ├── bridge_call.py     ← Reverse bridge dispatch into the in-process plugin\r\n│   └── …\r\n├── kicad_detect.py        ← Find installed KiCad versions, exe paths, USER_SITE\r\n├── adom_library.py        ← Symbol/footprint discovery in sym-lib-table.txt\r\n├── lib_table.py           ← Add/remove rows in *-lib-table files\r\n├── plugin_install.py      ← Copy adom_bridge.py into USER_SITE/usercustomize.py\r\n├── plugin_payload/\r\n│   └── adom_bridge.py     ← Reverse-bridge plugin loaded INSIDE every KiCad GUI exe\r\n├── parsers/               ← .kicad_sym, .kicad_mod, .kicad_pcb tokenizers\r\n└── start.bat              ← Windows launcher for local dev\r\n```\r\n\r\nThe 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.\r\n\r\n## Reverse-bridge example\r\n\r\n```bash\r\n# From cloud Docker: list every open KiCad GUI exe + which board it has loaded\r\nadom-desktop kicad_open_editors\r\n\r\n# Direct in-process call: ask the running pcbnew.exe what board it has open\r\nadom-desktop kicad_bridge_call '{\"method\":\"get_board_info\"}'\r\n\r\n# Run a DRC IN-PROCESS — no file write, no kicad-cli detour\r\nadom-desktop kicad_bridge_call '{\"method\":\"run_drc\",\"args\":{\"severity\":\"error\"}}'\r\n```\r\n\r\nThe bridge dispatches these to `adom_bridge.py` running inside pcbnew. The handler executes against the live `pcbnew.GetBoard()` and returns JSON over the websocket.\r\n\r\n## Reference: install the source locally to read\r\n\r\n```bash\r\n# Pull the zip (no auth needed — wiki static URL)\r\ncurl -L https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-v0.8.2.zip -o /tmp/kicad-bridge.zip\r\n\r\n# Extract\r\nmkdir -p /tmp/kicad-bridge && cd /tmp/kicad-bridge && python3 -c \"import zipfile; zipfile.ZipFile('/tmp/kicad-bridge.zip').extractall()\"\r\nls -la\r\n```\r\n\r\nYou'll see `server.py`, `bridge.json`, `handlers/`, `plugin_payload/`, etc. — fork the layout for your own bridge.\r\n\r\n## Related\r\n\r\n- **[Bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges)** — `bridge.json` schema, packaging recipe, lifecycle\r\n- **[Puppeteer bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-puppeteer-bridge)** — simpler architecture, single-instance, Node + Chrome for Testing\r\n- **[Fusion 360 bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-fusion-bridge)** — passthrough-to-add-in pattern\r\n- **[adom-desktop main page](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop)** — install the parent app first\r\n\r\n",+  "readme": "# Adom Desktop — KiCad Bridge\n\nA **reverse bridge** that lets Adom Desktop (AD) — and the AI driving it — control the user's own\nKiCad on Windows: open and drive every editor, install symbol/footprint libraries, place parts, run\nDRC/ERC, export manufacturing files, and screenshot any window back to the AI.\n\nKiCad is the user's **host app**. The bridge *detects* an existing KiCad and, if none is present,\n*installs* one for them — it never asks the user to download or click through anything by hand.\n\n- **Page / docs:** https://wiki.adom.inc/adom/adom-desktop-kicad-bridge\n- **Bridge SDK:** https://wiki.adom.inc/adom/adom-desktop-bridges\n- **Verb namespace:** `kicad_*` &nbsp;·&nbsp; **Status verb:** `kicad_bridge_status` &nbsp;·&nbsp; **Health:** `GET /status`\n\n> Every screenshot in this README was captured on **ADOMBASELINE**, a stock Hyper-V VM with **no\n> GPU**, driven end-to-end through the bridge — install → libraries → editors → 2D → 3D. If it\n> renders there, it renders on a real laptop.\n\n---\n\n## Demo — narrated live editor tour (real screen recording)\n\n[![KiCad bridge live demo](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-bridge-demo-poster.png)](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-bridge-demo.mp4)\n\n**▶ [Watch the demo](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-bridge-demo.mp4)** (48s, 🔊 narrated) — the bridge driving real KiCad on a Windows VM: **select** a footprint and **zoom** the copper in the PCB editor, **rotate** the assembled board in 3D, then tour the schematic, symbol, and footprint editors. Genuine screen recording of the live windows (zoom / select / 3D rotation), narrated section by section — not animated stills.\n\n\n---\n\n## The demo verb — `kicad_demo`\n\nOne verb that shows the whole bridge off, built for the AI that demos Adom Desktop\nduring Hydrogen Desktop's install. Six beats, in the order a hardware person thinks:\n\n| Beat | Shows |\n|---|---|\n| 1 | the **schematic symbol** in the user's own Symbol Editor |\n| 2 | the **footprint** for that same part |\n| 3 | that part in **3D** |\n| 4 | a **schematic** using it (U1 + two 10k resistors) |\n| 5 | the **2D board layout** |\n| 6 | the finished **board in 3D** |\n\n```bash\nadom-desktop kicad_demo '{}'                      # start: prepares, warms KiCad, opens beat 1\nadom-desktop kicad_demo '{\"step\":\"footprint\"}'    # ...then follow data.nextCall each time\nadom-desktop kicad_demo '{\"all\":true}'            # bulk (non-interactive; slower than one request budget)\nadom-desktop kicad_close '{\"force\":true}'         # tear down\n```\n\n**It narrates itself.** Every beat returns `say` (a line to speak), `pointOut`\n(what's actually on screen), `window` (match it in `kicad_screenshot_all`) and\n`nextCall`. One beat per call is the default precisely so the AI can talk, screenshot,\nthen move on.\n\n**It ships nothing.** The two demo parts, the schematic and the board are all\ngenerated in code, and the 3D bodies come from KiCad's *own* bundled\n`3dmodels/*.3dshapes` — so the runtime zip stays small and the 3D views are real.\nParts install into the user's real `Adom` library; the project lands in\n`Documents/adom-kicad-demo`.\n\n**No KiCad? It offers to install it.** `kicad_demo` returns an offer instead of an\nerror; `kicad_demo '{\"installKiCad\": true}'` silently installs the official build\n(per-user, no UAC) and then runs the tour. Watching the AI install your EDA tool is\nitself a good demo beat.\n\n**It never takes your screen.** Every window opens in the background and all input is\nwindow-targeted (UIA / `PostMessage`), so you can keep working while it runs.\n\n---\n\n## How it fits together\n\nThe bridge is a `spawn.kind: python` process AD launches on the user's machine (`entrypoint:\nserver.py`, `port: 0` — AD picks the port and passes `ADOM_BIND_HOST`). It speaks HTTP\n(`POST /command`, `GET /status`) and reaches KiCad through **four control surfaces**, picking the\nlightest one that can do the job.\n\n![Control surfaces](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/control-surfaces.svg)\n\n| # | Surface | Used for | Where |\n|---|---------|----------|-------|\n| 1 | **kicad-cli** (subprocess) | headless DRC/ERC, gerber/pdf/svg/step/bom export, format upgrade | `handlers/export.py`, `run_drc`, `run_erc`, `lint_*` |\n| 2 | **KiCad IPC (kipy)** | board/schematic introspection, confirmed footprint placement (KiCad 9+) | `handlers/place_footprint.py` |\n| 3 | **Embedded Python** | JSON-RPC *inside* each KiCad process, dispatched on its wx UI thread | `plugin_payload/adom_bridge.py`, `handlers/bridge_client.py` |\n| 4 | **Win32 / UIA / SendKeys** | open editors, screenshots, clicks/keys, dialog handling, window management | `handlers/kicad_ui.py`, `handlers/close_windows.py` |\n\n---\n\n## The window tour\n\nEverything below was opened and captured on the GPU-less VM. Sample data is the **RP2040 breakout**\ngenerated by `tour-pack-rp2040/` (an `AdomRP2040` symbol + `QFN-56_AdomRP2040` footprint + a board).\n\n### Schematic editor — `kicad_open_schematic`\n\nThe RP2040 breakout: the MCU (U1), USB-C, a 12 MHz crystal, decoupling, and mounting pins.\n\n![Schematic editor](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/schematic-editor.png)\n\n### Symbol editor — `kicad_open_symbol_editor`\n\nOpens straight to a symbol (`kicad_install_symbol` puts it in a user library first).\n\n![Symbol editor](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/symbol-editor.png)\n\n### Footprint editor — `kicad_open_footprint_editor`\n\n`{\"footprintName\":\"QFN-56_AdomRP2040\",\"library\":\"AdomRP2040\"}` loads the part directly — 56 pins +\nthermal pad, 7×7 mm, 0.4 mm pitch, courtyard and silkscreen.\n\n![Footprint editor](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/footprint-editor.png)\n\n### PCB editor (2D) — `kicad_open_board`\n\nThe routed breakout — copper, silkscreen, the QFN-56 land pattern, mounting holes.\n\n![PCB editor 2D](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/pcb-editor-2d.png)\n\n### 3D viewer — `kicad_open_3d_viewer {\"editor\":\"pcb\"}`\n\nThe full board in 3D — board body, the USB-C connector's 3D model, the QFN chip body, SMD parts,\nplated through-holes. **Rendered on the CPU via the software-OpenGL fallback (below); reload 1.7 s.**\n\n![3D viewer](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/3d-viewer.png)\n\n---\n\n## Install & upgrade — zero manual steps\n\nThe bridge never tells the user to go download KiCad. `kicad_upgrade` fetches the official installer\nand runs it silently, picking the scope automatically:\n\n- **Elevated AD** → `/allusers /S` (system-wide, `%ProgramFiles%\\KiCad`).\n- **Non-elevated AD** → `/currentuser /S` (`%LocalAppData%\\Programs\\KiCad`, **no UAC prompt**).\n\n`kicad_readiness` reports whether KiCad is installed and ready without side effects; the AI routes on\nit before offering to install. Hard-won install details (all handled for you):\n\n- KiCad 10's NsisMultiUser installer **requires** a scope flag — bare `/S` errors `rc=666660`.\n- AD's portable Python has **no CA bundle**; the bridge ships `certs/cacert.pem` and uses it for TLS.\n- Downloads are size-checked against `Content-Length` (a truncated installer otherwise fails at NSIS).\n- `%APPDATA%/kicad/<ver>/` lib tables don't exist until first launch — the bridge **bootstraps** them\n  so `install_library`/`install_footprint` work on a never-opened KiCad.\n- `kicad_upgrade {\"diagnoseOnly\":true}` reports token-elevation type + `EnableLUA` without installing.\n\n---\n\n## Dialogs & error handling — the bridge clears the pointless ones\n\nKiCad throws modal dialogs that stall automation. The bridge **scans every window owned by a running\nKiCad process** (by PID — reliable, unlike title matching), auto-expires the benign ones, screenshots\nwhat it dismissed, and returns a hint so the AI can decide what (if anything) to tell the user.\n\nAuto-expired benign dialogs include:\n\n- **\"Could not use OpenGL / falling back to software rendering\"** (GPU-less hosts).\n- **\"Welcome to KiCad — starting for the first time\"** first-run wizard (dismissing accepts defaults).\n- **\"This file was created by an older version of KiCad\"** conversion notice.\n\n![First-run wizard, auto-dismissed](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/dialog-firstrun-wizard.png)\n\nVerbs:\n\n- `kicad_window_info` — lists windows and **self-heals** (auto-expires benign dialogs) by default.\n- `kicad_dismiss_dialogs {\"all\":true}` — clear everything blocking; `{\"screenshot\":true}` returns\n  images of each; `{\"forceSoftwareCanvas\":true}` persists Cairo canvas; `{\"debug\":true}` dumps the\n  raw PID-based scan.\n- `kicad_screenshot_all` — one call returns **every** open KiCad window, so the AI can spot an error\n  dialog it didn't expect and read the message.\n\n---\n\n## Software-OpenGL fallback (last resort)\n\n`kicad_enable_software_opengl` deploys Mesa's `llvmpipe` (a CPU OpenGL rasterizer) into KiCad's `bin`\nso a box with **no usable GPU** — Hyper-V, RDP, headless CI — can still render the editors and the 3D\nviewer. This is how every 3D shot above exists.\n\n> ⚠️ It renders on the CPU and is **slow**. It is a worst-case fallback only — a real GPU (or GPU-P /\n> DDA passthrough) is vastly better. The bridge keeps it so a GPU-less box isn't a dead end; it is\n> never suggested proactively.\n\n---\n\n## Verb reference\n\nAll 41 verbs (prefix `kicad_`). Call `kicad_describe` for the live catalog with per-verb hints,\nrelated verbs, and pitfalls.\n\n**Windows & UI** — `open_board`, `open_schematic`, `open_symbol_editor`, `open_footprint_editor`,\n`open_3d_viewer`, `open_editors`, `close_symbol_editor`, `close_footprint_editor`, `close_3d_viewer`,\n`close`, `window_info`, `dismiss_dialogs`, `screenshot_all`, `send_key`, `click`, `fix_keyboard`\n\n**Libraries & parts** — `install_library`, `install_symbol`, `install_footprint`, `install_plugin`,\n`place_footprint`, `adom_library_status`\n\n**Checks & export** — `run_drc`, `run_erc`, `lint_board`, `lint_schematic`, `lint_library`,\n`format_upgrade`, `export_gerber`, `export_pdf`, `export_svg`, `export_step`, `export_bom_csv`\n\n**Detect, install & meta** — `list_versions`, `readiness`, `describe`, `enable_software_opengl`,\n`check_for_updates`, `upgrade`, `bridge_status`, `bridge_call`\n\n---\n\n## Running / dependencies\n\nAD provisions everything; there is nothing to install by hand.\n\n- **Python** ≥ 3.11 (AD provisions it) — stdlib only, plus the bundled `certs/cacert.pem`.\n- **KiCad** ≥ 7.0 (host app; `kicad_upgrade` installs it if absent).\n- **OS:** Windows (macOS/Linux detection stubs exist; the GUI surfaces are Windows-first).\n\nThe bridge binds `ADOM_BIND_HOST` on an AD-assigned port — never `0.0.0.0`. Auto-updates via\n`updateManifestUrl` in `bridge.json`.\n\n---\n\n*Developer & publish notes live in `dev-skills/` and `publish-skills/` (source-only, never shipped to\na user install).*\n",   "author": {     "name": "Kyle Bergstedt",     "email": "[email protected]"