KiCad 9 defaults to s-expression netlist export, not XML. Add
_parse_kicad_sexp() parser with pinfunction/pintype metadata, update
auto-detection to distinguish (export from <export by content.
Fix export_netlist: use kicad-cli's actual format names (kicadsexpr,
kicadxml) instead of invalid 'kicad', use --format instead of -f, and
treat non-zero exit with valid output as success with warnings.
Parses external netlist files into the component-pin-net graph that
verify_connectivity and apply_batch consume. Supports KiCad XML (.net)
exported by kicad-cli, and CSV/TSV with flexible column name matching.
Auto-detects format from file extension and content. Output is directly
compatible with verify_connectivity's expected parameter, closing the
loop between "I have a design" and "I can build it in KiCad."
Requested by ESP32-P4 project (agent thread message 028).
Hierarchical KiCad projects store sub-sheets in subdirectories (e.g.
sheets/). The flat os.listdir scan missed all of them. Use recursive
glob to find .kicad_sch files at any depth under the project directory.
Reported by ESP32-P4 project (agent thread message 025) — their 8
malformed property-private entries were all in sheets/ subdirectory.
validate_project now scans all .kicad_sch files for two classes of
silent rendering failure: quoted (property "private" ...) that should
be a bare keyword in KiCad 9, and lib_id references with no matching
lib_symbols entry. Both cause kicad-cli to blank entire pages during
export without any error message.
Also pass working_dir to run_kicad_command in export_pdf for robust
library path resolution.
Addresses feedback from ESP32-P4 project (agent thread message 021).
Power symbols at a pin coordinate create a direct electrical connection
without a wire. Note this in audit_wiring (explains wire_count: 0 for
connected pins) and add_power_symbol (explains why it draws a stub).
Group results by net name instead of per-pin, keeping the summary
compact enough to stay inline even for 100+ pin components. Add
anomaly detection (unconnected pins, high-fanout nets, auto-named
nets) and optional pin/net filters. Wire coordinates are now opt-in
via include_wires flag to avoid flooding the calling LLM with
coordinate noise.
Refactors _build_connectivity() into a two-layer state builder so the
union-find internals (pin_at, label_at, wire_segments) are accessible
to new analysis tools without duplicating the 200-line connectivity engine.
New tools:
- audit_wiring: trace all wires connected to a component, report per-pin
net membership with wire segment coordinates and connected pins
- remove_wires_by_criteria: bulk-remove wires by coordinate filters
(y, x, min/max ranges, tolerance) with dry_run preview support
- verify_connectivity: compare actual wiring against an expected
net-to-pin mapping, report matches/mismatches/missing nets
New sexp_parser utilities:
- parse_wire_segments: extract (wire ...) blocks with start/end/uuid
- remove_sexp_blocks_by_uuid: atomically remove blocks by UUID set
parse_lib_symbol_pins() now falls back to searching external .kicad_sym
library files when a symbol isn't embedded in the schematic's lib_symbols
section. Splits lib_id ("LibName:SymName") to locate the library file,
then parses pins using the bare symbol name.
Search order: schematic dir, libs/, ../libs/, project root, project
root/libs/, kicad/libs/, and sym-lib-table entries with ${KIPRJMOD}
substitution. Handles nonexistent directories gracefully.
Fixes add_power_symbol for script-generated schematics that reference
project library symbols without embedding them (e.g. D1/SMF5.0CA in
ESP32-P4-WIFI6-DEV-KIT library).
add_label bypasses kicad-sch-api serializer entirely — generates
s-expression strings and inserts them directly into the .kicad_sch
file via atomic write. Fixes two upstream bugs: global labels silently
dropped on save (serializer never iterates "global_label" key), and
local labels raising TypeError (parameter signature mismatch in
LabelCollection.add()).
add_power_symbol now falls back to sexp pin parsing when the API
returns None for custom library symbols (e.g. SMF5.0CA). Extracts
shared resolve_pin_position() utility used by both add_power_symbol
and batch operations.
Batch labels also fixed — collected as sexp strings during the batch
loop and inserted after sch.save() so the serializer can't overwrite
them.
Reduce _rc() and transform_pin_to_schematic() rounding from 3 to 2
decimal places to match KiCad's 0.01mm coordinate quantum — prevents
union-find misses when wire endpoints and sexp-parsed pin positions
differ at the sub-quantum level.
Use schematic stem as subdirectory inside .mckicad/ so multi-sheet
analysis outputs (connectivity.json, etc.) don't collide.
_build_connectivity() used sch.list_component_pins() which returns
empty for custom library symbols. Added a second pass that falls back
to parse_lib_symbol_pins() + transform_pin_to_schematic() for any
component that returned 0 pins from the API. This brings custom IC
pins (e.g. ESP32-P4's 105 pins) into the union-find net graph.
kicad-sch-api has two parsing gaps: get_symbol_definition() returns
None for non-standard library prefixes (e.g. Espressif:ESP32-P4),
and there is no sch.global_labels attribute for (global_label ...)
nodes. This adds a focused parser that reads directly from the raw
.kicad_sch file as a fallback, integrated into the connectivity
engine, pin extraction, and label counting tools.
Root cause: kicad-sch-api doesn't back-populate comp.pins or sch.nets
on loaded schematics. All data is accessible through alternative APIs.
Pin extraction: use comp.get_symbol_definition().pins for metadata and
sch.list_component_pins(ref) for schematic-transformed positions.
Connectivity: new wire-walking engine using union-find on coordinates.
Walks wires, pin positions, labels, and power symbols to reconstruct
the net graph. Replaces broken ConnectivityAnalyzer/sch.nets fallbacks.
Eliminates 'unhashable type: Net' crash.
Hierarchy: use sch.sheets.get_sheet_hierarchy() instead of the broken
sheets.data.get("sheet", []) raw dict approach.
Labels: supplement sch.get_statistics() with sch.labels.get_statistics()
and sch.hierarchical_labels for accurate counts.
99 tests passing, lint clean.
New modules:
- patterns/ library: decoupling bank, pull resistor, crystal oscillator
placement with power symbol attachment and grid math helpers
- tools/batch.py: atomic file-based batch operations with dry_run
- tools/power_symbols.py: add_power_symbol with auto #PWR refs
- tools/schematic_patterns.py: MCP wrappers for pattern library
- tools/schematic_edit.py: modify/remove components, title blocks, annotations
- resources/schematic.py: schematic data resources
43 new tests (99 total), lint clean.