LuYanFCP/dsh-kicad

dsh-kicad

A DeepSeek Harness plugin for AI-driven PCB design in KiCad. A plugin that lets DeepSeek Harness design PCBs with KiCad.

未分類

dsh-kicad

English | 中文

KiCad for DeepSeek Harness (dsh): a native dsh plugin bundle that lets the agent read, edit and check KiCad schematics, with KiCad itself (kicad-cli) deciding whether the design is correct.

Developer preview. Built and tested against dsh 0.2.1-rc.2 and KiCad 10.0.7. dsh's plugin APIs are pre-stable, so every package pins engines.dsh to >=0.2.1-rc.2 <0.3.0-0.

What you get

Surface · What it does Tools (kicad_*) · doctor (is this machine ready: kicad-cli and KiCad 8+, libraries, Python, with fixes and install commands), read_schematic, list_components, get_symbol_pins, list_nets (KiCad netlist), search_symbols (installed libraries), erc, drc, render (SVG, PNG attached as an image for image-capable models; for boards KiCad's 3D view from any side or angle), export (netlist/BOM), simulate (SPICE verification with ngspice: operating point, transient and AC runs, with requirement assertions judged on the numbers, see Simulation), sim_model (find the SPICE models in a model file or folder, with their pins), check_mcu (static check of how a microcontroller is wired, see MCU wiring check), simulate_firmware (run an ATtiny or ESP32 firmware against the board, see Firmware against the board), export_fab (Gerber/drill/position/BOM zip with 3D previews, refused until KiCad finds the board ready, see Fab output), fab_preset (JLCPCB/PCBWay design rules), create_schematic, and edit_schematic — an atomic batch of edits (add/move/remove symbols, set values/footprints/properties, wires, labels, power symbols, junctions, no-connects, pin-to-pin wires routed around parts and other nets, pin-to-net connections, plus layout by function: titled block frames, moving a part or a whole block together with its wiring, page size and title block); for boards read_pcb (outline, layers, rules and net classes, footprints with courtyards and pad nets, zones, DRC's unrouted count) and edit_pcb — an atomic batch (board outline and mounting holes, moving, flipping and locking footprints, design rules and net classes in the .kicad_pro, tracks with pad-to-pad endpoints, vias, zones, keepouts, silkscreen text and reference designators, removal by uuid); and update_pcb — Update PCB from Schematic, headless: a new board gets KiCad's empty board, a default stackup and a placeholder outline; on an existing board placed footprints stay put, new ones are staged beside the outline, and nets, values and fields follow the schematic; it refuses with the REF:PINs to fix, and KiCad's schematic parity check judges the result; and autoroute — routes the board with Freerouting, keeping hand-routed (locked) and existing tracks, refilling copper pours, with KiCad's DRC as the verdict (see Routing). Untouched file text stays byte-identical. Policy · Design edits go through dsh approval (ask); writes are confined to the session's working directory (a monotonic guard); after every successful edit KiCad ERC (schematic) or DRC (board edit or fab preset, zones refilled; with the schematic parity count after update_pcb) runs and its verdict is appended to the tool result; the system prompt gets stable KiCad working rules and a dynamic "KiCad project in this directory + last ERC/DRC" context. Skills · kicad-agent-workflow, kicad-erc-triage, kicad-drc-triage, kicad-schematic-design, kicad-simulate, kicad-mcu-check. Commands · /erc, /drc, /bom, /simulate, /mcu-check and /mcu-sim (model-free, run on the project's root design), /kicad-fab (fab zip), /kicad-render, /kicad-doctor (environment check), /kicad-install (install KiCad, the Freerouting autorouter or Espressif's QEMU for ESP32 firmware simulation, see below). Agent preset · eda — a standard-like tool set with an electronics-design persona. The KiCad tools are global, so every preset can use them. Web UI · Right-sidebar viewer for *.kicad_sch / *.kicad_pcb with two modes: Interactive — the embedded KiCanvas viewer (hierarchical sheets, symbol/footprint/net inspection, layer toggles, KiCad or Witch Hazel theme, reloads when the file changes) — and SVG (kicad-cli render, pan/zoom, multi-sheet, Render button, re-renders a board by itself when it changes; also the automatic fallback); boards also get 3D, modelled on KiCad's 3D Viewer (see 3D view); conversation cards for ERC/DRC, schematic edits and the environment check; settings pages for the sidecar (kicad-cli path, Python, plus the KiCad environment check and Install KiCad) and the policy (approval, auto-ERC, auto-render). The viewer and failed tool cards show a notice with Install KiCad when KiCad is missing or too old. Standalone MCP server · The Python sidecar speaks MCP over stdio, so Claude Code or an RL environment can use the same tools without dsh. KiCad side (optional) · An IPC plugin with an "Open in DSH" toolbar action.

Architecture

dsh-kicad-bundle  (cordis.patch.yml: global rows + presets/eda.patch.yml)
├─ dsh-kicad-bridge     Host  ctx.kicad service; runs the Python sidecar through ctx.subprocess, speaks MCP to it
│    └─ sidecar/        Python ≥3.10, stdlib-only core: lossless .kicad_sch S-expression editing, symbol libraries,
│                       pin geometry, kicad-cli (ERC/DRC/netlist/BOM/SVG), fab output; optional extras: png (cairosvg), ipc (kicad-python)
├─ dsh-tool-kicad       Host  kicad_* tools (defineTool), paths resolved against the session working directory
├─ dsh-kicad-policy     Host  tools.guard + tools/pre-execute (ask/deny) + tools/post-execute (auto ERC/DRC) + prompt section/context
├─ dsh-kicad-skills     Host  skill provider + /erc /drc /bom /kicad-fab /kicad-render
└─ dsh-client-ui-kicad  Client sidebarRightTabs type + tool.call.toolview rows + plugins.row.config pages
kicad-plugin/           KiCad IPC plugin ("Open in DSH")
market/                 plugin-market index example

Design decisions:

  • Files first, headless. Schematics are edited as files (.kicad_sch S-expressions) and judged with kicad-cli; no KiCad GUI is needed. The IPC API (kipy) is only an optional live channel (ipc_status), because it needs a running GUI and admits one client at a time.
  • Python sidecar, not Node → IPC: KiCad's IPC is NNG + protobuf with an official binding only for Python.
  • KiCad decides. ERC/DRC verdicts come from kicad-cli, never from the model.
  • Narrow dsh coupling. Each package touches dsh only through one adapter module (src/dsh.ts, src/client/dsh.ts); the Client uses public seams only (sidebarRightTabs, keyed slots, remote.workspaceFiles, remote.commands, config forms) and imports no dsh Client package, as dsh asks of third-party UI plugins.

Requirements

  • dsh >=0.2.1-rc.2 =22.19, pnpm 11 (for building from source)
  • Python >=3.10 on PATH (python3; python on Windows) — no Python packages needed for the core
  • KiCad 8+ with kicad-cli (tested with 10.0.7). Found via the setting, $KICAD_CLI, PATH, macOS/Windows install dirs, or Flatpak org.kicad.KiCad
  • Optional: rsvg-convert, ImageMagick or the png extra for PNG renders; the ipc extra for the IPC channel; ngspice (ngspice on PATH, or $NGSPICE) for kicad_simulate

Check your setup and install KiCad

Plugins → KiCad → KiCad bridge shows the environment check: each item (kicad-cli, KiCad version ≥ 8, symbol and footprint libraries, Python ≥ 3.10, optional KiCad 3D models, PNG rasterizer and kicad-python) with its status and how to fix it. /kicad-doctor prints the same in a conversation, and the model has kicad_doctor; a tool that fails because KiCad is missing names the install command.

When KiCad is missing or too old, the page (and the viewer's notice) lists the ways to install it on this machine:

Platform · Option · Runs from dsh? Fedora · sudo dnf install -y kicad · no: needs root, shown with Copy command Ubuntu and derivatives · KiCad PPA: sudo add-apt-repository -y ppa:kicad/kicad-10.0-releases && sudo apt-get update && sudo apt-get install -y kicad · no (root) Debian · sudo apt-get install -y kicad · no (root) Arch · sudo pacman -S --needed kicad kicad-library · no (root) openSUSE · sudo zypper install kicad · no (root) any Linux with flatpak · flatpak remote-add --user … flathub + flatpak install --user flathub org.kicad.KiCad · yes, per user, no root (about 1.5 GB) macOS · brew install --cask kicad · yes, if Homebrew is installed Windows · winget install --id KiCad.KiCad -e · yes, if winget is available (Windows may ask for permission) everywhere · installer from kicad.org · link

Nothing runs until you click Install… and confirm the exact command. dsh never runs sudo: root-only options are shown for you to run in a terminal, then Check again. A running install shows its log and can be stopped; when it finishes the bridge restarts its sidecar and checks again, so the new kicad-cli (including the Flatpak one, via flatpak run --command=kicad-cli org.kicad.KiCad) is used without restarting dsh. From a conversation: /kicad-install lists the options, /kicad-install flatpak --yes runs one, /kicad-install status follows it.

The settings page has no session of its own: it runs these checks through your most recent session, so open or start one first.

Install

From source (works today)

git clone https://github.com/LuYanFCP/dsh-kicad && cd dsh-kicad
pnpm install && pnpm run build
dsh plugin --profile web add "$PWD/packages/kicad-bundle"
dsh web

dsh plugin … add links the directory; leaf packages resolve through this checkout's node_modules, and dsh maps @deepseek-ai/* imports to the running installation. Rebuild and restart dsh after code changes. In the Web UI you can also use Plugins → Install with the same absolute path.

Paste the repository URL (simplest)

In dsh, Plugins → Add plugin, enter https://github.com/LuYanFCP/dsh-kicad and install (or dsh plugin --profile web add https://github.com/LuYanFCP/dsh-kicad). The repository root is itself the dsh-kicad bundle; it loads the prebuilt dist/dsh-kicad. Append # to pin a version.

From the plugin market (search inside dsh)

dsh finds plugins through plugin markets: JSON indexes you add once. This repository publishes one.

  1. Plugins → Experimental plugins → More → Custom plugin markets: switch it on. A Discover tab appears.
  2. Discover → Add index source: https://raw.githubusercontent.com/LuYanFCP/dsh-kicad/main/market/index.json
  3. Search, e.g. kicad, 原理图, 电路板, circuit board, then Install → Enable now. No restart needed.

The market entry installs dist/dsh-kicad, a single self-contained package built by pnpm run dist (scripts/pack-dist.mjs), from a pinned Git commit. The same package installs directly:

dsh plugin --profile web add "git+https://github.com/LuYanFCP/dsh-kicad.git#<commit>&path:/dist/dsh-kicad"

To publish a new version: pnpm run dist, commit, then add a version to market/index.json with that commit and check it with node market/validate.mjs /node_modules/@deepseek-ai/dsh-plugin-market-index.

Use

  1. Open a session whose workspace contains a KiCad project (sidebar Add workspace). Optionally pick the EDA agent preset.
  2. Ask, e.g. *"Add a 100 nF decoupling capacitor between +3V3 and GND next to U1 and make ERC pass."* The agent reads the design, sends one kicad_edit_schematic batch (you approve it), and reads the ERC verdict that the policy appends.
  3. Open the .kicad_sch from Workspace files: the KiCad tab shows the render, refreshed after each agent edit; Render re-renders on demand.
  4. /erc, /drc, /bom work without the model.
  5. Plugins → KiCad → KiCad bridge / KiCad policy hold the settings.

Board placement

After kicad_update_pcb creates a board, one kicad_edit_pcb operation, auto_place, lays it out from the schematic's functional blocks (the add_block frames; without them its sheets, else net connectivity): each block stays together, connectors sit on the board edge facing out (a footprint's "PCB Edge" mark lands on the edge), decoupling capacitors go next to their IC's power pins and crystals next to their IC, power parts get extra room, and the outline is sized to fit (a drawn outline is kept and filled instead). place_block lays out one block in a given spot or rectangle. kicad_read_pcb then reports courtyard overlaps, footprints outside the outline and the total ratsnest length, and kicad_render with view: "3d" shows the result. On the usb-attiny85 board it gives no courtyard overlap, everything inside the outline, the USB-C on the edge, and a ratsnest of 214 mm against 345 mm for random placement (details).

Routing

kicad_autoroute {pcb, nets?, exclude_nets?, passes?, timeout?} writes the board as a Specctra design, runs Freerouting headless, and adds only the new tracks and vias; footprints never move. Every track and via already on the board is fixed wiring, so the agent routes critical nets by hand first (power, USB or differential pairs, a crystal: kicad_edit_pcb add_track / add_via with locked: true) and the router completes the rest. Copper zones (a GND pour on both layers) are refilled and saved afterwards by kicad-cli pcb drc --refill-zones --save-board, whose verdict (unrouted count, DRC errors, schematic parity) is the result. The run can be cancelled from dsh and stops at timeout (default 300 s), keeping what it routed; the design, session and log stay in /.dsh-kicad/autoroute/. When connections stay open, the result suggests routing them by hand or, on an auto_placed board, clearing the routes (remove_tracks) and placing again with more room (gap: 2, group_gap: 4): that routes the usb-attiny85 example completely.

Freerouting (GPL-3.0) is a separate Java program and is not bundled. /kicad-doctor checks for Java and Freerouting; /kicad-install freerouting --yes downloads the pinned release jar (2.5.0, about 65 MB, SHA-256 checked) into your data directory (~/.local/share/dsh-kicad/freerouting on Linux), never with sudo. Freerouting 2.5.0 needs Java 25+: the doctor lists the package-manager command or the Temurin JRE. The bridge's java and freerouting settings point at an existing Java or jar.

Simulation

kicad_simulate {path, analyses?, assertions?, probes?, timeout?} checks what a schematic *does*, not only how it is wired. KiCad exports the SPICE netlist (kicad-cli sch export netlist --format spice, which reads the symbols' Sim.* properties, so a design simulated here also simulates in KiCad's own Simulator), ngspice runs it headless, and each requirement is an assertion the tool judges on the numbers, as ERC and DRC are judged by KiCad: the model never decides.

  • Analyses: op (DC operating point, the default), tran {stop, step?}, ac {start, stop, points?}; one of each per call.
  • Probes: V(net), V(a,b), I(R1|C1|L1|D1|V1). Assertions: {probe, min?, max?, equals? + tol | tol_pct, abs?, measure?, at?, from?, to?, ref?} with SPICE-style numbers (10k, 100n, 3.3, 20m). Measures: final, initial, max, min, mean, rms, pp, at (transient); gain_db, phase_deg, peak_gain_db, lowpass_cutoff_hz, highpass_cutoff_hz (AC, ref for gain relative to the input).
  • Example: a 3.3 V rail {probe: "V(3V3)", min: 3.2, max: 3.4}, an LED {probe: "I(D1)", min: "5m", max: "15m", abs: true}, a low-pass {probe: "V(OUT)", analysis: "ac", measure: "lowpass_cutoff_hz", equals: 1000, tol_pct: 5}.
  • The circuit needs a ground net, sources from Simulation_SPICE (VDC, VSIN, VPULSE, IDC) and a model for every part that matters: R, C and L simulate from their value; a diode or LED needs set_sim_model {reference, device: "D", params} (hidden Sim.Device) (the tool refuses otherwise, because without it KiCad wires the diode backwards); ICs, regulators and transistors need a vendor model (below). Parts without a model are left out and listed in the result.
  • Vendor models. kicad_sim_model {path, name?} lists the .subckt (with their pins and the comment above them) and .model definitions in a model file, or in every model file below a folder. kicad_edit_schematic set_sim_model {reference, library, name, pins?} binds one to a symbol: it writes the hidden Sim.Library (relative to the project), Sim.Name, Sim.Device and Sim.Pins, matches symbol pins to model pins by name, and refuses with both pin lists when it cannot (give pins: {"1": "GND", "2": "OUT", "3": "IN"}), because KiCad trusts a wrong Sim.Pins and simulates the wrong circuit. {reference, device: "D", params} sets a built-in device without a file. Models come from files you provide; none are bundled, and the agent is told not to invent any. Subcircuit internals stay out of the result.
  • Tolerances. monte_carlo: {runs?, seed?, tolerances?, distribution?, min_pass_rate?} repeats the run with R, C and L values (defaults ±5 %, ±10 %, ±10 %; tolerances: {R: 1, C1: "20%", V1: 2} in percent by part letter or reference, V and I sources by DC level) drawn at random, uniform within the tolerance or gaussian with 3σ at the tolerance, and reports per assertion how many runs held, the min, mean, max and the first failing draw. Seeded, so repeatable; min_pass_rate (default every run) accepts a share below 100 %. Only plain R, C, L values and source levels vary, not the parameters inside vendor models.
  • Requirements stay with the project. save_spec: true writes the analyses, assertions and monte_carlo of a call to .sim.json (plain JSON: analyses, assertions, probes, monte_carlo, timeout; spec names another file). Later kicad_simulate calls and /simulate use that file when it exists, so a change is re-verified against every saved requirement without retyping them; arguments named in a call replace the same key of the file.
  • ngspice is a separate install: /kicad-doctor checks for it and lists the command for this machine (sudo dnf install ngspice, sudo apt-get install ngspice, brew install ngspice, ...). The netlist, deck, rawfiles and log are kept in /.dsh-kicad/sim//. /simulate runs the project's saved requirements (an operating point when there are none) without the model; the kicad-simulate skill teaches the agent to turn requirements into assertions and iterate.
  • What it cannot say: anything about firmware or a part without a SPICE model, signal integrity, EMC or heat.

MCU wiring check

kicad_check_mcu {path, reference?, requirements?} checks how each microcontroller is wired, from KiCad's netlist and a per-family pin table (an MCU *provider*), and judges the result itself. It is a static check, not a firmware simulation: it cannot know what the firmware does with a pin.

  • Findings (error / warning / info, each with the pin, the net and a fix). For the classic ESP32: a strapping pin held at the wrong level at reset (GPIO0 tied to ground, GPIO12 high or strongly pulled up, which selects 1.8 V flash and stops the chip booting, GPIO2 pulled up), reserved flash or PSRAM pins in use (GPIO6-11, and 16/17 on PICO, WROVER and the -R2 modules), an input-only GPIO (34-39) that nothing can drive or that floats behind a button, an unconnected or unpulled EN, less than 10 µF on the supply. For the ATtiny85: RESET unconnected or relying on the weak internal pull-up, less than 100 nF on VCC; its peripherals sit on fixed pins, so requirements like i2c_sda, i2c_scl, spi_*, pwm, adc are judged against them (an I²C SDA on PB4 fails).
  • Requirements say what the firmware needs from the board: [{net: "VBAT_SENSE", needs: "adc1"}, {pin: "U1:IO25", needs: ["output", "dac"]}]; needs is output (not input-only), input, adc / adc1 (usable with WiFi) / adc2, dac, touch or boot_free (not a strapping pin). Each is judged against the GPIO the signal is on; the agent is told to choose GPIOs for them before wiring.
  • Cannot verify, never passing. An MCU no provider covers (ESP32-S3, STM32, ATtiny, a custom symbol whose pins the table cannot read, ...) is reported as cannot_verify with the reason; the verdict is then neither passed nor failed, and the requirements on it are ?.
  • Providers are data. mcu_data/esp32.json (classic ESP32 modules and PICO, and attiny85.json for the 8-pin ATtiny25/45/85; the tables were transcribed from Espressif's datasheet and hardware design guidelines, so check it against your part's datasheet) says which symbols it covers and, per GPIO, what matters. A project adds its own family as .mcu.json next to the schematic (id, name, match, gpio_pin_pattern, gpios; same structure, same rules); the project's file wins for the same id. /mcu-check runs the check on the project schematic without the model; the kicad-mcu-check skill teaches the agent to set requirements first and fix the findings.

Firmware against the board

kicad_simulate_firmware {path, firmware?, stimuli?, assertions?, duration?, clock_hz?, ...} runs a firmware image on a simulated ATtiny25/45/85 or classic ESP32 wired as the schematic says and judges what the board does, so "pressing the button lights the LED within 20 ms" is checked rather than assumed. The firmware is one you built: for the ATtiny a .hex or .elf (avr-gcc, PlatformIO, Arduino; an ELF also carries the initial EEPROM), for the ESP32 a merged flash image (see below); the plugin never builds, writes or invents one.

  • The chip is a pure-Python AVR simulator (no dependencies) that follows the datasheet (Atmel 2586Q, read in full for this): the ATtiny25/45/85 instruction set and memory sizes; GPIO; the external, pin-change and every peripheral interrupt in vector order; Timer0 and Timer1 (normal, CTC, phase-correct and fast PWM, toggle outputs; compare values latched at TOP/BOTTOM as the hardware does; Timer1 with the 64 MHz PLL clock, OCR1C as TOP and the complementary outputs); the ADC (single-ended and differential channels with gain, unipolar and bipolar, the bandgap, the temperature sensor, Vcc / 1.1 V / 2.56 V / AREF references, free running, the 13/25-clock conversion time); the analog comparator; EEPROM (3.4 ms writes, erase-only and write-only modes, persisting across resets, loaded from an ELF's .eeprom); the watchdog (reset and interrupt mode, the timed-sequence rules, wake-up from power-down); resets (the RESET pin, the watchdog, the 64 ms start-up delay, MCUSR flags); the USI in three-wire mode (SPI as master or slave, software strobe or external clock); idle and power-down sleep. It is checked against programs compiled by avr-gcc at four optimisation levels (148 result bytes of arithmetic, sorting, CRC, soft-float compared with Python) and against known timings.
  • The board is a DC network: resistors, switches, the pin drivers (40 Ω) and internal pull-ups (35 kΩ), the RESET pin's own pull-up (45 kΩ), rails and ground. A button the scenario presses closes its switch (a switch from the RESET net to ground resets the chip); the chip reads whatever the network makes of the pin; an LED reports whether it would light (1.8 V forward); a PWM pin is high for its duty cycle and the network is solved