← Commit history

Add dev-skills + publish-skills (SDK 3-skill set; source-only, not in the user pkg)

John Lauer ·86bd62e9e3 ·3mo ago ·parent e9ff5f0
2 files changed +109
dev-skills/kicad-bridge-dev.mdadded+46
@@ -0,0 +1,46 @@+---+name: kicad-bridge-dev+description: DEVELOPER skill for building/debugging the Adom Desktop KiCad bridge. NOT shipped to users (source-only). Read before editing bridge code — architecture, the 4 control surfaces, how to add a verb, and every fresh-machine gotcha this bridge has hit. Trigger words — kicad bridge dev, edit kicad bridge, add kicad verb, kicad bridge architecture, debug kicad bridge, kicad control surfaces, kicad fresh machine.+---++# kicad-bridge-dev++Developer notes for the `adom-desktop-kicad-bridge`. Source-only (belongs in the repo, never shipped to a user install). Read `adom-desktop-bridge-sdk` first for the AD-core-vs-author boundary.++## What it is++A reverse bridge AD spawns on the user's laptop (`spawn.kind: python`, `entrypoint: server.py`, `port: 0`). It adds the `kicad_*` verb namespace. AD provisions Python (Runtime contract v1.9.63+) and passes `ADOM_BIND_HOST` — bind that, never `0.0.0.0`.++## The 4 control surfaces (how a verb reaches KiCad)++1. **kicad-cli** (`subprocess` → `kicad-cli.exe`) — headless: `pcb drc`, `sch erc`, `pcb/sch export` (gerber/pdf/svg/step/bom), `pcb/sch upgrade`. No GUI. Handlers: `run_drc`, `run_erc`, `lint_*`, `export.py`, `format_upgrade`, `bom.py`, `netlist.py`.+2. **KiCad IPC API (kipy)** — KiCad 9+. Board/schematic introspection + automation (e.g. `place_footprint` confirms via `kipy get_footprints()`). Expands with KiCad 11.+3. **Embedded-Python reverse bridge** (`plugin_payload/adom_bridge.py`) — `usercustomize.py` auto-injects at KiCad's USER_SITE → runs an HTTP server INSIDE each KiCad process, writes a `%TEMP%` discovery file; container-side `handlers/bridge_client.py` POSTs JSON-RPC to `/rpc`, dispatched onto KiCad's wx UI thread via `wx.CallAfter`. Methods: `ping`, `list_frames`, `get_menu_ids`, `wm_command`. Verbs: `bridge_status`, `bridge_call`.+4. **Win32 / UIA / SendKeys** (`handlers/kicad_ui.py`, ctypes/user32) — the last resort: `WM_COMMAND` (open editors), `SendInput`/`keybd_event` (`alt+3`, `ctrl+s`), `PrintWindow`+`EnumWindows` (screenshots), `GetWindowRect`. For opening the 3D viewer, screenshots, clicks, keystrokes, window management.++## Adding a verb (3 places, keep in sync)++1. Write `handle_<verb>` in a handler; return a dict with `success` + a rich **`_hint`** (mandatory — the AI reads verb OUTPUT, not this skill).+2. Register in `server.py` `COMMAND_HANDLERS`.+3. Add `kicad_<verb>` to `bridge.json` `verbs[]` AND an entry (summary/hint/related/pitfalls) to `_VERB_CATALOG` in `server.py` (the `kicad_describe` catalog). A test checks these three stay aligned.++**Dispatcher special cases** (`dispatch_command`): `readiness`, `describe`, `check_for_updates` return BEFORE the plugin auto-install + the KiCad-not-installed pre-check (they must work with KiCad absent). `upgrade` also bypasses that pre-check (it INSTALLS KiCad — do not gate the installer behind "KiCad not installed") and refreshes the detection cache on success.++## Fresh-machine gotchas (all hard-won, all fixed — don't regress them)++- **Install scope is mandatory.** KiCad 10's NsisMultiUser installer: bare `/S` = `rc=666660` (invalid params). Pass `/allusers /S` (elevated) or `/currentuser /S` (per-user, `%LOCALAPPDATA%\Programs\KiCad`, **no UAC**). See `handlers/upgrade.py`.+- **TLS on the portable Python.** AD's provisioned Python has no CA bundle → `CERTIFICATE_VERIFY_FAILED`. We bundle `certs/cacert.pem` (certifi) and load it in `_ssl_context()`. Any new https fetch must use that context.+- **Truncated downloads** read as EOF and pass MZ/size checks, then NSIS fails at install. `upgrade.py` HEADs `Content-Length` and refuses/deletes a size-mismatched file.+- **Config doesn't exist pre-first-launch.** `%APPDATA%/kicad/<ver>/` (lib tables) is created on KiCad's first GUI run. `kicad_detect.ensure_win_user_config()` bootstraps it (seeds tables from the install template) so `install_library`/`_footprint` work on a never-launched KiCad.+- **Detection cache is built once at startup.** After `kicad_upgrade` installs KiCad, `dispatch_command` re-runs `detect_all_kicad_versions()` so subsequent verbs see it without a bridge restart. Detect paths include `%LocalAppData%\Programs\KiCad` (per-user installs).+- **Blocking dialogs stall everything.** `handlers/close_windows.py` scans EVERY window owned by a running KiCad process (by PID — `_win_scan_kicad_dialogs`, reliable; title/owner matching is NOT), auto-expires benign ones (OpenGL notice), screenshots them, and returns hints. `window_info` self-heals by default; `kicad_dismiss_dialogs {all|forceSoftwareCanvas}`.+- **GPU-less hosts.** No OpenGL → the notice pops and pcbnew/3D may not render. On expiring that notice we persist `graphics.canvas_type=2` (Cairo) to `kicad_common.json`. NOTE: a truly minimal VM (Hyper-V, no GPU) may still not render the heavy editors even in Cairo — that's the hardware. Validate GUI/screenshot work on a real-GPU machine.++## Diagnostics you can call++- `kicad_upgrade {"diagnoseOnly":true}` — token elevation type + UAC `EnableLUA` + cached-installer/truncation facts (no install).+- `kicad_dismiss_dialogs {"debug":true}` — raw process-based dialog scan (PIDs + every KiCad window + class + isDialog).++## Testing on a fresh AD / VM++`refresh_bridges {name:kicad}` pulls the new cache; the RUNNING instance keeps its old code until it dies — `bridge_kill {name:kicad}` forces a fresh spawn that loads the new version and re-detects. The first verb after a kill returns a `bridge_starting` envelope — retry. Confirm the running version via `bridge_log_read` ("starting v0.9.xx").
publish-skills/kicad-bridge-publish.mdadded+63
@@ -0,0 +1,63 @@+---+name: kicad-bridge-publish+description: PUBLISH skill — the exact recipe to ship a new version of the Adom Desktop KiCad bridge to wiki.adom.inc. NOT shipped to users (source-only). Covers version lockstep, the zip build, adom-wiki repo push + release create, the skills pkg, pushing to a target AD, and verify steps. Trigger words — publish kicad bridge, release kicad bridge, ship kicad bridge, bump kicad bridge, kicad bridge manifest, kicad bridge pkg.+---++# kicad-bridge-publish++How to cut and publish a KiCad-bridge release. **Use the `adom-wiki` CLI for ALL wiki interactions — never raw curl.** Two independent artifacts: the RUNTIME (zip + manifest, on the page `/files`) and the SKILLS PKG (`adom/adom-desktop-kicad-bridge`, container-side docs).++## 1. Version lockstep (bump ALL three together)++- `bridge.json` → `"version"`+- `BRIDGE_VERSION` (plain text)+- the manifest JSON → `"version"`++They must match. `bridge.json` also carries `updateManifestUrl` (durable auto-update), `docs` (your page), `homepage` (a **wiki.adom.inc** URL, NEVER github), `detect` (host-app paths incl. `%LocalAppData%\Programs\KiCad`), `verbPrefixes`, complete `verbs[]`, `statusVerb`.++## 2. Build the zip++Zip the repo dir CONTENTS (bridge.json at the zip root). Exclude: `__pycache__`, `*.pyc`, `.git`, `.github`, `node_modules`, `.DS_Store`, and the generated `adom-bridge-kicad-manifest.json`. Include `certs/cacert.pem`. Compute `sha256` + byte `size` for the manifest.++Manifest fields: `manifest_version:1, name:"kicad", version, url` (the `/blob/app/.../adom-bridge-kicad-v<ver>.zip`), `sha256, size, verbPrefixes:["kicad_"], healthEndpoint:"/status", released_at, hero, languages`.++## 3. Publish the runtime (CLI)++```bash+adom-wiki repo push adom/adom-desktop-kicad-bridge \+  --files adom-bridge-kicad-v<ver>.zip adom-bridge-kicad-manifest.json bridge.json \+  -m "v<ver>: <what changed>"+adom-wiki release create adom/adom-desktop-kicad-bridge <ver> \+  --title "KiCad bridge v<ver>" --changelog "..." \+  --binary-path adom-bridge-kicad-v<ver>.zip+```++`repo push` writes the files to the page `/files` (same place AD's bridge_cache fetches from). `release create` also registers a proper Release (SDK Artifact 1). For a field with no dedicated verb use `adom-wiki api -X PUT /api/v1/pages/... --body ...`, not curl.++## 4. Publish the skills pkg (container docs)++Stage a dir with `package.json` (name/version/`files[]`), a ROOT `SKILL.md`, and `skills/<name>/SKILL.md` per user skill. **DEV + PUBLISH skills go in `dev-skills/`/`publish-skills/` in the REPO only — they are auto-excluded from the pkg tarball; never ship them to users.** Then:++```bash+cd <staging> && adom-wiki pkg pack --out /tmp/v.tgz   # verify: tar lists every skills/<x>/SKILL.md, 0 binaries+adom-wiki pkg publish --org adom --skip-lint --yes     # moves dist-tag latest+```++The runtime SKILL.md (`adom-desktop-kicad`, ships in the zip) and the pkg's root SKILL.md are DIFFERENT docs — update both when a change affects users.++## 5. Push to a running desktop + verify++```bash+adom-desktop --target <name> refresh_bridges '{"name":"kicad"}'   # pulls new cache+adom-desktop --target <name> bridge_kill '{"name":"kicad"}'       # force fresh spawn (old instance keeps old code)+adom-desktop --target <name> bridge_check_updates '{}'            # current == latest?+adom-wiki repo show adom/adom-desktop-kicad-bridge adom-bridge-kicad-manifest.json  # served version matches?+```++Verify the served bridge.json (`repo show ... bridge.json`) has the fields you expect and the zip's sha matches. A fresh verb call after a kill returns `bridge_starting` — retry.++## 6. Standing rules++- Commit source to `main` in lockstep with each publish (drift = the bug that once dropped `updateManifestUrl`).+- After ANY wiki change, refresh the wiki pup window showing the page.+- The changelog must be real (≥2 words, no placeholders) — the server enforces it.