How the Zig S-expression engine and typed KiCad models work, how they are exposed to Python (pyzig_sexp), and the invariants around parsing, formatting, and freeing. Use when working with KiCad file parsing, S-expression generation, or layout sync.
cd ~/.claude/skills
git clone https://github.com/atopile/atopile.git atopile mkdir -p ~/.claude/skills/sexp
curl -fsSL https://raw.githubusercontent.com/atopile/atopile/HEAD/.claude/skills/sexp/SKILL.md \
-o ~/.claude/skills/sexp/SKILL.md The sexp subsystem provides:
pyzig_sexp extension module.Source-of-truth docs and code:
src/faebryk/core/zig/README.md (high-level overview)src/faebryk/core/zig/src/sexp/* (tokenizer/AST/structure)src/faebryk/core/zig/src/python/sexp/sexp_py.zig (Python API + critical memory rules)from pathlib import Path
from faebryk.libs.kicad.fileformats import kicad
pcb = kicad.loads(kicad.pcb.PcbFile, Path("board.kicad_pcb"))
_text = kicad.dumps(pcb)
src/faebryk/core/zig/src/sexp/tokenizer.zig (tokenization + line/column tracking)src/faebryk/core/zig/src/sexp/ast.zig (SExp tree + KiCad pretty formatting)src/faebryk/core/zig/src/sexp/structure.zig (decode/encode + error context)src/faebryk/core/zig/src/sexp/kicad/* (typed KiCad models)src/faebryk/core/zig/src/python/sexp/init.zig (exports PyInit_pyzig_sexp)src/faebryk/core/zig/src/python/sexp/sexp_py.zig (module + type binding generation)src/faebryk/core/zig/gen/sexp/*.pyisrc/faebryk/libs/kicad/fileformats.py (namespaces modules + caching + loads/dumps)src/faebryk/libs/kicad/fileformats.py (primary integration layer)src/faebryk/exporters/pcb/kicad/*src/faebryk/exporters/pcb/layout/layout_sync.pysrc/atopile/kicad_plugin/*SExp parsing/formatting (tokenizer.zig, ast.zig)structure.zig + sexp/kicad/*.zig)pcb, netlist) with:
loads(data: str) -> Filedumps(file: File) -> strFile.free(...) for releasing Zig-owned allocationsfaebryk.libs.kicad.fileformats.kicad wraps these modules and provides kicad.loads(...)/kicad.dumps(...).src/faebryk/core/zig/src/sexp/*src/faebryk/core/zig/src/python/sexp/sexp_py.zigato dev compile (imports faebryk.core.zig)src/faebryk/core/zig/gen/sexp/*.pyi update accordinglysrc/faebryk/libs/kicad/fileformats.py if needed.kicad_pcb / .kicad_sch, dump it, and ensure KiCad accepts it (formatting-sensitive).zig test src/faebryk/core/zig/src/sexp/ast.zigzig test src/faebryk/core/zig/src/sexp/structure.zigfaebryk.libs.kicad.fileformats.kicad unless you explicitly need the raw module API.kicad.loads(...): path-based loads are cached and returned by reference (mutations are shared).The Python bindings duplicate the input S-expression string into a persistent allocator because parsed structs contain pointers into the input buffer.
Implications:
loads(...) of large files can grow memory if you never call free(...) on the returned *File.free(...) cached objects unless you also invalidate the cache.Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
Replace with description of the skill and when Claude should use it.
Automate 21risk tasks via Rube MCP (Composio). Always search tools first for current schemas.
Automate 2chat tasks via Rube MCP (Composio). Always search tools first for current schemas.
Automate Ably tasks via Rube MCP (Composio). Always search tools first for current schemas.
Automate Abstract tasks via Rube MCP (Composio). Always search tools first for current schemas.