app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Drive your own KiCad from an AI, in the background: open every editor, read the whole board as data (the EDA Bridge Standard board model), stack-up, part heights, netlists, place, route, pour, DRC and exports.
← Commit history
Publish 1.0.148
6 files changed
+56−13
SKILL.md+1@@ -390,6 +390,7 @@ Two contract points shared by all of them. A removal is verified after the commi - **Placement memory (from 1.0.14).** The bridge remembers where the user keeps each editor kind (`~/.adom/kicad-bridge-placement.json`) and puts a new or restarted frame back there; replies say `placementRestored`. Do not re-tile windows yourself after a restart. - **`kicad_taskbar_repair` (from 1.0.30).** Standalone PCB and schematic editors showed a blank white taskbar icon because KiCad's installer leaves the identity blank on those Start Menu shortcuts. The bridge writes KiCad's OWN identity onto them at startup; the verb runs it on demand and reports what it changed. It never invents an identity of its own.+- **`backgroundGuard` (from 1.0.42, default OFF from 1.0.50).** On: a KiCad window the bridge is opening right now is sent behind the user's window; any window that already existed is the user's to raise. Off: the bridge moves no KiCad window at all. Off by default while it is tuned (wiki #114). `kicad_set_settings {"backgroundGuard": true}` or the dashboard's Keep KiCad in the background switch. - **`foregroundToastMinutes` (from 1.0.31, default 10, `0` = never).** When a verb the AI asked for must take the foreground, the user gets one toast per that many minutes, not one per call. Set it with `kicad_set_settings {"foregroundToastMinutes": 0}`; the 3 s on-screen caption on the driven box stays either way. - A failed editor close now answers with `dialogsSeen` and `editorCloseVerbs`: quote them when you report a stuck close.
dashboard/kicad_dashboard.py+1−1@@ -49,7 +49,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import unquote, urlparse -VERSION = "4.8.0"+VERSION = "4.8.1" AI_THREAD = "kicad-dashboard" DEFAULT_PORT = 8931 UI_DIR = Path(__file__).resolve().parent / "ui"
dashboard/ui/index.html+5−5@@ -446,14 +446,14 @@ main a:hover{border-bottom-color:var(--accent)} <section class="settings-card" id="card-guard"> <h3 class="card-title">Keep KiCad in the background <span class="card-status" id="stGuard"></span></h3>- <p class="card-desc">When Adom opens a KiCad window, KiCad tries to bring it to the front of whatever you are doing. With this on, the bridge sends the window it is opening right now behind your window. It only ever touches the window being opened at that moment: a KiCad window that is already open is yours to raise by clicking its taskbar button, Alt-Tab or anything else. With this off, the bridge never moves or pushes any KiCad window, so you see exactly what KiCad does by itself. Flip it to compare the two; the change applies at once and the bridge's focus log keeps recording what it would have done.</p>+ <p class="card-desc">When Adom opens a KiCad window, KiCad tries to bring it to the front of whatever you are doing. With this on, the bridge sends the window it is opening right now behind your window. It only ever touches the window being opened at that moment: a KiCad window that is already open is yours to raise by clicking its taskbar button, Alt-Tab or anything else. With this off, the bridge never moves or pushes any KiCad window, so you see exactly what KiCad does by itself. Off by default while it is still being tuned: with it on, KiCad can behave oddly (wiki #114). Flip it to compare the two; the change applies at once and the bridge's focus log keeps recording what it would have done.</p> <div class="wrapper"> <h4 class="name" title="backgroundGuard"><span class="prefix">Windows: </span><span class="title">Send new KiCad windows behind mine</span></h4> <div class="setting-row"> <button class="toggle" id="tglGuard" role="switch" aria-checked="true" aria-label="Send a KiCad window Adom is opening behind my window"><span class="toggle-track"><span class="toggle-thumb"></span></span></button> <span class="set-check-label">Send a KiCad window Adom is opening behind my window</span> </div>- <span class="default-hint">Default: On</span>+ <span class="default-hint">Default: Off</span> </div> </section> @@ -742,7 +742,7 @@ function sizeShotsToDevicePixels(){ }); } sizeShotsToDevicePixels();window.addEventListener('resize',sizeShotsToDevicePixels);-const SETTINGS_DEFAULTS={activityIndicator:'progress',overlayBadges:true,backgroundGuard:true};+const SETTINGS_DEFAULTS={activityIndicator:'progress',overlayBadges:true,backgroundGuard:false}; const MODE_LABEL={progress:'Progress bar',flash:'Attention flash',both:'Bar and flash',none:'Nothing'}; const dlg=$('#dlgSettings'); // Local mirror: the box's settings file is the truth, but the page reflects a@@ -757,7 +757,7 @@ function renderSettings(){ const on=settingsView.overlayBadges!==false; const t=$('#tglOverlay');t.classList.toggle('active',on);t.setAttribute('aria-checked',String(on)); $('#stOverlay').textContent=on?'on':'off';$('#stOverlay').className='card-status'+(on?' ok':'');- const gOn=settingsView.backgroundGuard!==false;+ const gOn=settingsView.backgroundGuard===true; const g=$('#tglGuard');g.classList.toggle('active',gOn);g.setAttribute('aria-checked',String(gOn)); $('#stGuard').textContent=gOn?'on':'off';$('#stGuard').className='card-status'+(gOn?' ok':''); $('#settingsBox').textContent=curTarget?('on '+curTarget):'';@@ -821,7 +821,7 @@ $('#tglOverlay').onclick=()=>{ saveSetting('set_overlay_badges',{overlayBadges:now},prev).then(ok=>{if(ok)toast('overlay badge '+(now?'on':'off'),'ok');}); }; $('#tglGuard').onclick=()=>{- const prev={backgroundGuard:settingsView.backgroundGuard!==false};+ const prev={backgroundGuard:settingsView.backgroundGuard===true}; const now=!prev.backgroundGuard; settingsView.backgroundGuard=now;rememberSettings();renderSettings(); saveSetting('set_background_guard',{backgroundGuard:now},prev).then(ok=>{if(ok)toast('keep KiCad in the background: '+(now?'on':'off'),'ok');});
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "kicad-bridge",- "version": "1.0.147",+ "version": "1.0.148", "type": "app", "description": "Skills for your container so your AI knows how to drive the KiCad bridge, and how to contribute a fix back (kicad-bridge-contributing). The bridge runtime itself is the release zip; Adom Bridge loads that.", "tags": [
page.json+36−5@@ -4,7 +4,7 @@ "slug": "kicad-bridge", "title": "KiCad - the KiCad Bridge", "brief": "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.",- "version": "1.0.111",+ "version": "1.0.148", "tags": [ "kicad", "pcb",@@ -53,7 +53,7 @@ "install_hint": "", "version_cmd": "" },- "readme": "# Adom Desktop \u2014 KiCad Bridge\n\nA **reverse bridge** that lets Adom Desktop (AD) \u2014 and the AI driving it \u2014 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 \u2014 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_*` \u00b7 **Status verb:** `kicad_bridge_status` \u00b7 **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 \u2014 install \u2192 libraries \u2192 editors \u2192 2D \u2192 3D. If it\n> renders there, it renders on a real laptop.\n\n---\n\n## Demo \u2014 narrated live editor tour (real screen recording)\n\n[](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-bridge-demo.mp4)\n\n**\u25b6 [Watch the demo](https://wiki.adom.inc/blob/app/adom-desktop-kicad-bridge/kicad-bridge-demo.mp4)** (48s, \ud83d\udd0a narrated) \u2014 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 \u2014 not animated stills.\n\n\n---\n\n## The demo verb \u2014 `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` \u2014 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` \u2014 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\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 \u2014 `kicad_open_schematic`\n\nThe RP2040 breakout: the MCU (U1), USB-C, a 12 MHz crystal, decoupling, and mounting pins.\n\n\n\n### Symbol editor \u2014 `kicad_open_symbol_editor`\n\nOpens straight to a symbol (`kicad_install_symbol` puts it in a user library first).\n\n\n\n### Footprint editor \u2014 `kicad_open_footprint_editor`\n\n`{\"footprintName\":\"QFN-56_AdomRP2040\",\"library\":\"AdomRP2040\"}` loads the part directly \u2014 56 pins +\nthermal pad, 7\u00d77 mm, 0.4 mm pitch, courtyard and silkscreen.\n\n\n\n### PCB editor (2D) \u2014 `kicad_open_board`\n\nThe routed breakout \u2014 copper, silkscreen, the QFN-56 land pattern, mounting holes.\n\n\n\n### 3D viewer \u2014 `kicad_open_3d_viewer {\"editor\":\"pcb\"}`\n\nThe full board in 3D \u2014 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\n\n---\n\n## Install & upgrade \u2014 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** \u2192 `/allusers /S` (system-wide, `%ProgramFiles%\\KiCad`).\n- **Non-elevated AD** \u2192 `/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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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\n\nVerbs:\n\n- `kicad_window_info` \u2014 lists windows and **self-heals** (auto-expires benign dialogs) by default.\n- `kicad_dismiss_dialogs {\"all\":true}` \u2014 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` \u2014 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** \u2014 Hyper-V, RDP, headless CI \u2014 can still render the editors and the 3D\nviewer. This is how every 3D shot above exists.\n\n> \u26a0\ufe0f It renders on the CPU and is **slow**. It is a worst-case fallback only \u2014 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** \u2014 `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** \u2014 `install_library`, `install_symbol`, `install_footprint`, `install_plugin`,\n`place_footprint`, `adom_library_status`\n\n**Checks & export** \u2014 `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** \u2014 `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** \u2265 3.11 (AD provisions it) \u2014 stdlib only, plus the bundled `certs/cacert.pem`.\n- **KiCad** \u2265 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 \u2014 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",+ "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_*` · **Status verb:** `kicad_bridge_status` · **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[](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\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\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\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\n\n### PCB editor (2D) — `kicad_open_board`\n\nThe routed breakout — copper, silkscreen, the QFN-56 land pattern, mounting holes.\n\n\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\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\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": "John Lauer", "email": "[email protected]"@@ -71,7 +71,7 @@ "skills": [], "author_name": "John Lauer", "source_visibility": "public",- "description": "Your real KiCad, driven by AI in the background: a native Rust bridge (Windows) with 110+ kicad_* verbs for editors, libraries, placement, routing, pours, DRC, exports and screenshots. This page's repo is the full source; fixes come back as wiki PRs (skill kicad-bridge-contributing).",+ "description": "Skills for your container so your AI knows how to drive the KiCad bridge, and how to contribute a fix back (kicad-bridge-contributing). The bridge runtime itself is the release zip; Adom Bridge loads that.", "dependencies": { "adom/adom-bridge": "^2.0.3" },@@ -87,9 +87,15 @@ "uninstall.sh", "skills/kicad-interaction/SKILL.md", "skills/kicad-3d-models/SKILL.md",+ "skills/kicad-autorouting/SKILL.md",+ "skills/kicad-place-route-loop/SKILL.md",+ "skills/kicad-copper-pours/SKILL.md", "skills/kicad-tour/SKILL.md", "skills/kicad-tour/tour_runner.py", "skills/kicad-web-control/SKILL.md",+ "skills/kicad-uia/catalog.json",+ "skills/kicad-uia/SKILL.md",+ "skills/kicad-bridge-contributing/SKILL.md", "skills/kicad-bridge-dev/SKILL.md", "skills/kicad-bridge-publish/SKILL.md", "skills/kicad-bridge-hero/SKILL.md",@@ -127,5 +133,30 @@ "binary_name": "adom-bridge-kicad", "authors": null, "author_handle": "john",- "authors_json": null-}+ "authors_json": null,+ "agent_permissions": {+ "allow_bash": [+ "adom-desktop-kicad-bridge"+ ],+ "allow": [+ "adom-desktop-kicad-bridge status, readiness, describe, diagnostics, window_info, state and open_editors are read-only queries against KiCad and the bridge; they change nothing.",+ "adom-desktop-kicad-bridge list_versions, list_footprints, list_symbols, list_design_rules and adom_library_status read library tables and files; they change nothing.",+ "adom-desktop-kicad-bridge plugin_diagnose, verb_times, progress, errors, log_tail, get_settings and check_for_updates read bridge logs and settings; they change nothing.",+ "adom-desktop-kicad-bridge screenshot_all captures KiCad windows to images; it changes nothing in KiCad.",+ "adom-desktop-kicad-bridge extract_netlist, trace_net, find_connections and analyze_connections read the open design; they change nothing.",+ "adom-desktop-kicad-bridge lint_board, lint_schematic, lint_library, run_drc and run_erc run KiCad's own checks and produce reports; they change nothing."+ ],+ "soft_deny": [+ "adom-desktop-kicad-bridge install_library, install_library_bundle, install_symbol, install_footprint, pcm_install and pcm_uninstall write to the user's KiCad libraries.",+ "adom-desktop-kicad-bridge adom_library_heal, set_design_rules, format_upgrade and set_settings rewrite library files, rules or settings.",+ "adom-desktop-kicad-bridge launch, close, close_symbol_editor, close_footprint_editor, close_3d_viewer and dismiss_dialogs start or stop KiCad windows.",+ "adom-desktop-kicad-bridge uninstall, install_plugin, upgrade and enable_software_opengl reconfigure the user's KiCad or the bridge.",+ "adom-desktop-kicad-bridge export_gerber, export_gerbers, export_pdf, export_svg, export_step, export_molecule, export_bom_csv and export_part write files.",+ "adom-desktop-kicad-bridge make_part_project and place_footprint edit or create designs; demo, send_key and click drive KiCad's windows and inject input."+ ],+ "environment": [+ "adom-desktop-kicad-bridge is the KiCad Bridge; every kicad verb also passes Adom Bridge's human-approval gate on the user's desktop (caller identity, per-call reason, Activity Log).",+ "adom-desktop-kicad-bridge container approval is the first of two gates; the desktop gate is the second."+ ]+ }+}
skills/kicad-bridge-test/run_verb_tests.py+12−1@@ -428,6 +428,12 @@ def main(): ab(a.target, "send_files", {"filePaths": [str(bz)], "dest": base}, timeout=120) fg_violations = []+ # The background guard is OFF by default from 1.0.50 (wiki #114): KiCad then comes to the front the+ # way it does on its own, and that is not a failure. Foreground findings become notes unless the+ # guard is on for this run.+ _gs = ab(a.target, "kicad_get_settings", {}, timeout=60)+ guard_on = bool(((_gs.get("settings") or _gs) or {}).get("backgroundGuard", False))+ print(f"[guard] backgroundGuard is {'ON: foreground steals fail the run' if guard_on else 'OFF: foreground findings are notes only'}") # STEALTH (John, 2026-08-14: "keep the kicad windows in the background when # you test — it doesn't have to be minimized, just background"). Capture the@@ -498,7 +504,10 @@ def main(): had_victim = (bool(before) and not any(k in before for k in _kw) and not any(before.startswith(k) for k in _kicad_dialogs) and before not in ("Program Manager",))- if had_victim:+ if had_victim and not guard_on:+ fg_notes.append({"after_verb": verb, "foreground": title, "before": before,+ "note": "guard OFF: KiCad came to the front by itself; expected, not counted"})+ elif had_victim: fg_violations.append({"after_verb": verb, "foreground": title, "before": before}) if verb in results and results[verb]["result"] == "PASS": results[verb]["result"] = "FAIL"@@ -886,6 +895,8 @@ def main(): if "steal" in act and float(e.get("t") or 0) >= t0 - 5: grabs.append({"action": act, "title": e.get("title"), "exe": e.get("exe"), "secondsSinceUserInput": e.get("secondsSinceUserInput")})+ if grabs and not guard_on:+ grabs = [] # guard off: nothing is bounced, so nothing to count if grabs: by = {} for g in grabs: