Install
$ agentstack add skill-avatarsd-kiskill-kicad-schematic ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
KiCad schematic generation
Engine: $(kx root)/kicad_lib/ — lossless sexp parser (sexp.py, token-equal round-trip proven on 2.3 MB boards; byte-identity only for .kicad_mod — sch re-serializes with ~0.5% whitespace drift) and the kx CLI (python3 -m kicad_lib.cli probe|check FILE from the repo root). Prefer kx probe over ad-hoc grep for inventory; prefer sexp.py over regex surgery for edits (kx is on PATH via ~/.local/bin). Persist with sexp.save_file(path, root) — PATH FIRST. Python imports need PYTHONPATH= or cwd=; the package is not pip-installed. Sibling skills: kicad-project (state machine), kicad-component (find/fetch parts), kicad-review (diff/ERC), kicad-improve (self-improvement loop). Design: doc/DESIGN.md in that repo.
Battle-tested workflow for writing .kicad_sch/.kicad_pcb files from Python without KiCad GUI. The engine and worked examples ship in this repo (kx root):
kicad_lib/ops.py— standalone sheet, scaffold symbols, full-wire
layout, driven by the built-in geometric verifier (kicad_lib/verify.py).
kicad_lib/live_ops.py— inject a block into an EXISTING schematic using
real library symbols (extends-flattening, multi-unit parts, mirrors), label-stitched islands.
kicad_lib/pcb.py— stage footprints in the PCB, path-linked to
schematic symbols so Update-PCB adopts them. See tests/test_*.py for runnable end-to-end usage against tests/fixtures/.
0. The loop (never skip a stage)
- Probe target file + libraries (uuids, pin geometry, occupancy, refs).
- Generate with explicit coordinates; run the geometric verifier.
- ERC headless; compare against a baseline run as a violation set.
- Render SVG → PNG → look at it. Geometry that passes checks can still
be unreadable (text collisions, symbol overlap). Iterate.
- Commit per slice. Checkpoint-commit the project BEFORE first modification.
1. Environment
KiCad 10 is a flatpak (org.kicad.KiCad); there is no host kicad-cli:
flatpak run --command=kicad-cli org.kicad.KiCad sch erc --output erc.rpt --severity-error FILE.kicad_sch
flatpak run --command=kicad-cli org.kicad.KiCad sch export svg --output OUTDIR --no-background-color FILE.kicad_sch
flatpak run --command=kicad-cli org.kicad.KiCad sch export netlist --output out.net FILE.kicad_sch
flatpak run --command=kicad-cli org.kicad.KiCad pcb export svg --output out.svg --layers F.Cu,B.Cu,Edge.Cuts,F.SilkS FILE.kicad_pcb
- Flatpak
/tmpis sandboxed — outputs must land under$HOME. - Official libs:
~/.local/share/flatpak/runtime/org.kicad.KiCad.Library.Symbols/x86_64/stable/active/files/symbols/*.kicad_sym ~/.local/share/flatpak/runtime/org.kicad.KiCad.Library.Footprints/x86_64/stable/active/files/footprints/*.pretty/*.kicad_mod
- Rasterize SVG with
cairosvg(installed), crop with PIL, then Read the PNG. - If a project lock file (
~NAME.kicad_pro.lck) exists, KiCad is open —
warn the user to close/reload before and after edits.
2. File format essentials (.kicad_sch, format 20260306, generator "eeschema" 10.0)
Top-level order: (kicad_sch (version) (generator) (generator_version) (uuid) (paper) (lib_symbols ...) (sheet_instances ...) (embedded_fonts no)). Order of body items is loose.
- Preserve the existing file uuid (it's the root sheet uuid; instance
paths reference it). For a sub-sheet keep the uuid KiCad created.
- Every placed symbol needs `(instances (project "NAME" (path "/"
(reference "R1") (unit 1)))). Sub-sheet instance paths are //`.
lib_symbolsis a cache of full symbol definitions named"Lib:Name".
Wires connect ONLY at exact pin endpoints — geometry comes from the cache.
- Wire: `(wire (pts (xy x1 y1) (xy x2 y2)) (stroke (width 0) (type default))
(uuid ...)). Junction: (junction (at x y) (diameter 0) (color 0 0 0 0) (uuid ...))`.
- Labels: local
(label "net" (at x y rot) ...), global `(global_label
"net" (shape input|output) ...), hierarchical (hierarchical_label ...)` (sub-sheets only; on a root sheet use local/global).
- Paren-balance check before writing; strings may contain parens — the
balanced extractor must be quote-aware.
3. Coordinates, rotation, mirror
World coords: mm, +Y down. Library pin coords: +Y up. Everything (symbol origins AND wire endpoints) must sit on the 1.27 mm grid or wires will not connect.
Pin world position for a symbol at (x, y, rot), lib pin offset (u, v):
def rot_xy(u, v, rot, mirror=""):
if mirror == "x": v = -v # use mirror only with rot 0 (unambiguous)
if rot == 0: return (u, -v)
if rot == 90: return (-v, -u)
if rot == 180: return (-u, v)
if rot == 270: return (v, u)
# world = (x + dx, y + dy)
Device:Ris vertical at rot 0 (pin1 top); rot 90 → pin1 left.- KiCad PNPs draw emitter at bottom;
(mirror x)flips it up (idiomatic). - Property text angle is RELATIVE to symbol rotation — for a rot-90/270
symbol set property angle 90 so the text renders horizontal.
- Real symbol heights differ from scratch-built ones — transistor circles
are ~11 mm; totem pairs need ≥15.24 mm center spacing.
4. Sourcing symbols
Real library symbols (preferred — "select real components"):
- Extract
(symbol "NAME" ...)blocks from the lib with a balanced parser. - Resolve
(extends "BASE"): take the base body, apply the derived
symbol's property overrides, rename BASE_X_Y sub-units to NAME_X_Y, then prefix the top name to "Lib:NAME" for the cache.
- Cache
Valueproperty must equal the bare symbol name, else ERC reports
lib_symbol_mismatch against the configured library.
- Parse pin positions per unit from
(pin ... (at x y a) ... (number "N")). - Multi-unit parts (LM393 = unit1 A, unit2 B, unit3 power): one reference,
several (symbol ...) instances each with its own (unit N); all units carry identical properties. One dual comparator serves two channels.
- Watch coincident pins (e.g. DMP3013SFV has 3 drain pins at one point —
all get the same net, emit all pin uuid entries).
Scaffold symbols (standalone sheets / quick drafts): define your own minimal R/C/Q/M/opamp boxes in lib_symbols so pin geometry is fully under your control. Sub-unit names inside a definition are bare ("R_0_1", never "lib:R_0_1"). Power symbol header is (power global). If KiCad will open the project standalone, also emit scaffold.kicad_sym + a project sym-lib-table entry (URI ${KIPRJMOD}/scaffold.kicad_sym) or ERC warns.
5. Placement and routing
- Describe nets as polylines of waypoints, where a waypoint is either a
literal point or ("ref","pin"); consecutive points become axis-parallel wire segments. Skip zero-length segments.
- Full wires for a self-contained sheet; label-stitched islands
(driver island / power island / comparator island joined by short local labels) when injecting into a crowded page — labels merge nets with the existing drawing (out1, Vin_p, VCC...) for free.
- Probe free space BEFORE placing: collect
(at ...)/(xy ...)coords into
a coarse occupancy grid — but remember it misses label TEXT extents and sheet boxes; always confirm against a render. Probe (sheet (at)(size)) blocks explicitly. Title block ≈ bottom-right 110×30 mm — keep clear.
- If the page is full, grow the paper (A4 → A3); existing content keeps
coordinates.
- T-junctions: a wire endpoint or pin touching another wire's interior DOES
connect; emit a (junction ...) there. Pins may sit pin-on-pin or pin-on-wire-interior — both connect.
- GND/VCC: one
power:GND/power:VCCsymbol per drop, short stub wire to
the pin. Standalone sheets need one PWR_FLAG (power_out pin) on each passive-driven rail or ERC raises power_pin_not_driven.
6. Geometric verifier (run before every write)
Implemented: kicad_lib/verify.py (Seg/Pin/verify()) + kicad_lib/geom.py (rot_xy, pin_world, snap) — mutation-tested by tests/test_verify.py. Build your net model with these; do NOT rewrite the verifier per-generator.
Mandatory checks over all generated segments + pins:
- Everything on the 1.27 mm grid; no diagonal segments.
- No two segments of DIFFERENT nets may touch, cross, T-touch, or overlap
collinearly (treat all GND*/VCC* stub nets as one net each).
- Same-net collinear overlap (double-draw) and same-net crossing without a
shared endpoint are errors too.
- No pin of net A lying on a wire of net B.
- Every declared pin claimed by exactly one net; every label anchor on a
segment of its net.
- (Scaffold symbols) no wire through a symbol body bbox.
- Junction emission: ≥3 connection items at a point, or endpoint/pin on a
same-net segment interior.
Float hygiene: round(coord, 3) everywhere — 160.02 + 7.62 produces 167.64000000000001 which the clash checks will flag against 167.64. Mutation-test the verifier once (plant a crossing, expect failure).
7. References and ERC
- References are global across ALL sheets of a project. Collision-check
against every .kicad_sch in the project dir, not just the target file. Duplicate refs silently cross-merge multi-unit symbols and corrupt ERC.
- GOTCHA:
kicad-cli sch ercdoes NOT catch duplicate designators or
unannotated symbols (R?) — even at --severity-all. They are Annotation-tool checks (SCH_REFERENCE_LIST), not ERC violations, so a headless ERC pass stays 0/0 green while two R1s cross-merge. Verify refs with kx ref-audit FILE (multi-unit aware: U1A/U1B/U1C sharing U1 is fine; a repeated unit or two lib_ids on one ref is the real dup) or the GUI Annotate dialog — never trust headless ERC for annotation.
#PWR0NNNrefs: pick an unused high block.- ERC totals are noisy. Always: run ERC on the pre-edit baseline
(git show HEAD:file > baseline.kicad_sch), normalize each violation to type: @locations, and set-diff new vs baseline. Judge only the NEW entries; report regrouping moves pre-existing items around.
- Expected-benign classes:
isolated_pin_labelon block I/O, undriven
inputs awaiting later wiring, pre-existing unwired-MCU noise.
missing_unit/missing_input_pin: EVERY unit of a multi-unit part
(dual/quad opamp, gate pack) must be placed, AND a spare you don't use still goes on a sheet + tied off (opamp: in+ → GND, in− → out; logic: inputs to a defined level) or its inputs throw missing_input_pin. kx unit-audit FILE lists placed-vs-expected units per ref (reads the count from lib_symbols) and front-runs ERC; ERC is the authority on tie-off. (kicad.info unused-pin threads; KLC S4.5)
- No-connect discipline: a genuinely unused pin needs a
(no_connect …)
flag to silence pin_not_connected — DOCUMENT the intent, never lower severity. Inverse trap no_connect_connected: a NC flag on a pin/node that IS wired (flag and wiring contradict) — delete the flag OR the wire, not both. NC means "nothing else attaches here"; one ERC entry lands at the flag, one at the connected pin. (kicad.info t/46229, t/21294)
- The exported netlist is the ground truth for connectivity: parse
(net (name)(node (ref)(pin))) blocks and assert each designed net has exactly the expected members. Do this at least once per block.
- **
power_pin_not_driven("Input power pin not driven by output power
pins") is a missing DRIVER DECLARATION, not a wiring fault.** Each power/ground rail needs one driver: a power_out pin (regulator) OR a power:PWR_FLAG. Fix by adding PWRFLAG — NOT a ground symbol like PWRGND (name trap: PWRGND is just a GND graphic; PWRFLAG declares the net driven) — at the rail's PASSIVE source (connector / battery / regulator INPUT). Never flag a regulator OUTPUT (already power_out; flagging it can trip "two outputs"). One flag per rail.
- GOTCHA:
kicad-cli sch export netlist**drops PWR_FLAG and power-symbol
nodes**, so a flagged and a flag-stripped schematic have IDENTICAL netlist driver content (both zero power_out) — the netlist cannot see this class of error. NEVER audit power drivers from the netlist; use kx power-audit FILE (reads PWR_FLAG instances from the schematic) plus kicad-cli ERC (the authority). (kicad.info t/35552, t/57016)
8. Visual inspection (non-negotiable)
flatpak run --command=kicad-cli org.kicad.KiCad sch export svg --output svg_out --no-background-color FILE.kicad_sch
python3 -c "import cairosvg; cairosvg.svg2png(url='svg_out/FILE.svg', write_to='out.png', output_width=2600)"
# crop: px = x_mm/paper_w*img_w (A4 297x210, A3 420x297), then Read the PNG
Look for: symbol body overlap, value/ref text collisions (move per-ref via a TEXT_POS override table), labels overlapping existing drawing text, junction dots present, pins facing their wires. Fix and re-render until clean. Matplotlib net-colored previews of the segment model help debug the generator, but only the KiCad render proves the file.
9. Injecting into an existing schematic
- Checkpoint-commit first; idempotency =
git checkout -- filethen rerun
the generator (uuids regenerate — re-derive anything uuid-dependent after).
- Splice lib symbols right after
(lib_symbols, body before
(sheet_instances; skip lib defs already cached.
- Reuse existing nets by label name (
out1,Vin_p,VCC) and existing
cached symbols (e.g. the project's own cap symbol) where possible.
- Bind MCU pins by adding a 5.08 mm stub wire + local label at the pin
endpoint (get pin world pos from the cached MCU symbol + instance (at)). Only bind pins the design doc justifies; leave the rest to the user.
10. PCB footprint staging (.kicad_pcb)
Goal: place footprints so KiCad's Update PCB from Schematic adopts them (no duplicates) and fills nets:
- Export the netlist; for each ref take
(tstamps "")from its
(comp ...) block — that uuid IS the link.
- Load
.kicad_mod, take the balanced(footprint ...)block, rename to
"Lib:Name", strip (version)/(generator*) headers, insert after the layer clause: (uuid), (at X Y), (path "/"), (sheetname "/"), (sheetfile "..."); set Reference/Value properties; refresh all item uuids. Leave pads netless — sync fills them.
- Stage in a grid clear of the board outline (probe Edge.Cuts extent).
- Parse-check via
pcb export svg, render, eyeball, commit.
11. Live IPC editing (KiCad v11 nightly, GUI open)
When kx env reports ipc_alive: true + scope pcb+sch, edit the schematic INSIDE the running eeschema instead of the file (the file is LOCKED then — never write it). kipy master via repo .venv (bootstrap: tools/bootstrap_kipy.sh); worked example tests/test_live_ipc.py:
from kipy import KiCad import kipy.schematictypes as st from kipy.geometry import Vector2 # nanometers: mm * 1e6 k = KiCad(socketpath="ipc:///tmp/kicad/api.sock") sch = k.getschematic() # the OPEN document refs = [s.referencefield.text.value for s in sch.getsymbols()] t = st.SchematicText(); t.value = "note" t.position = Vector2.fromxy(50800000, 25400000) c = sch.begincommit(); sch.createitems(t); sch.push_commit(c, "msg")
- Every push_commit is a single undo step in the GUI — small definitive
steps map 1:1 onto user-visible, user-revertable edits.
- removeitems() deletes live. getlines/getlabels/gettext mirror reads.
- PERSISTENCE GAP (nightly 10.99 standalone): save/revert are unhandled
over IPC — the USER saves. Verify expected state on disk afterwards with kx probe / kx check. Full handler map: kicad-project skill.
kx live wraps this as VERIFIED atomic ops (kicadlib/liveops.py, worked example tests/testliveops.py — re-execs the .venv itself):
kx live snap inventory + violations (mm, with ids) kx live check geometric verifier on the live model
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: AvatarSD
- Source: AvatarSD/KiSkill
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.