ten2mushi

kiutils

0
0
# Install this skill:
npx skills add ten2mushi/kiutils_skill

Or install specific skill: npx add-skill https://github.com/ten2mushi/kiutils_skill

# Description

|

# SKILL.md


name: kiutils
description: |
KiCad file manipulation with kiutils Python library for offline parsing and modification. Use when:
- Parsing/modifying .kicad_pcb (PCB boards) without running KiCad
- Parsing/modifying .kicad_sch (schematics) without running KiCad
- Creating/editing .kicad_mod (footprints) programmatically
- Creating/editing .kicad_sym (symbol libraries)
- Managing sym_lib_table / fp_lib_table (library tables)
- Modifying .kicad_dru (design rules)
- Editing .kicad_wks (worksheet templates)
- Batch processing KiCad files without GUI
- Scripted design rule checks or modifications
- Automated BOM generation from schematics
- Programmatic footprint/symbol library management
Triggers: "kiutils", "kicad file", "kicad_pcb", "kicad_sch", "kicad_mod", "kicad_sym", "parse kicad", "offline kicad", "kicad scripting"


kiutils - KiCad File Manipulation Library

kiutils is a Python library for parsing, creating, and modifying KiCad design files without requiring a running KiCad instance. It works directly with the S-expression file formats used by KiCad 6+.

Environment Setup

IMPORTANT: kiutils is installed in a Poetry virtual environment. Use the venv Python interpreter directly to run scripts from any project directory:

# Define the kiutils Python interpreter
KIUTILS_PYTHON="/path/to/kiutils"

# Run a script from your current project directory
$KIUTILS_PYTHON my_kicad_script.py

# Run inline Python (works from any directory)
$KIUTILS_PYTHON -c "from kiutils.board import Board; board = Board.from_file('project.kicad_pcb'); print(len(board.footprints))"

# Interactive shell with kiutils available
$KIUTILS_PYTHON

The kiutils environment is located at: /path/to/kiutils/

Note: File paths in your scripts are relative to your current working directory, not the venv location.

Quick Start

The universal pattern for all kiutils operations:

from kiutils.board import Board

# Load file
board = Board.from_file("project.kicad_pcb")

# Modify (add track, footprint, etc.)
# ...

# Save (overwrites original, or specify new path)
board.to_file()
# or: board.to_file("output.kicad_pcb")

This pattern applies to ALL kiutils classes:
- Board.from_file() / board.to_file()
- Schematic.from_file() / schematic.to_file()
- Footprint.from_file() / footprint.to_file()
- SymbolLib.from_file() / symbollib.to_file()
- DesignRules.from_file() / designrules.to_file()
- FootprintTable.from_file() / fptable.to_file()
- SymbolTable.from_file() / symtable.to_file()
- WorkSheet.from_file() / worksheet.to_file()

Critical Concepts

Units: Millimeters Throughout

kiutils uses millimeters for all dimensions. No unit conversion needed:

from kiutils.items.common import Position

# Position is in millimeters, angle in degrees
pos = Position(X=10.0, Y=20.0, angle=45.0)

Compare to kipy (KiCad IPC) which uses nanometers:
- kiutils: x = 10.0 (10mm)
- kipy: x = 10_000_000 (10mm in nm)

Net References: Numbers Not Objects

Critical gotcha: Track segments, vias, and pads reference nets by NUMBER, not by Net object:

# CORRECT: Find net, use its number
gnd_net = next((n for n in board.nets if n.name == "GND"), None)
segment.net = gnd_net.number  # Use the number!

# WRONG: This won't work
segment.net = gnd_net  # Don't assign the Net object

Layers: String Names

Layers are referenced by their string names:

# Copper layers
"F.Cu", "B.Cu", "In1.Cu", "In2.Cu"

# Technical layers
"F.SilkS", "B.SilkS", "F.Mask", "B.Mask", "F.Paste", "B.Paste"

# Mechanical layers
"Edge.Cuts", "Dwgs.User", "Cmts.User", "F.CrtYd", "B.CrtYd", "F.Fab", "B.Fab"

File Preservation

kiutils attempts to preserve file structure but:
- Whitespace/formatting may change on save
- Comments in the original file are NOT preserved
- Version-specific tokens may be updated

Supported File Types

Extension Class Import
.kicad_pcb Board from kiutils.board import Board
.kicad_sch Schematic from kiutils.schematic import Schematic
.kicad_mod Footprint from kiutils.footprint import Footprint
.kicad_sym SymbolLib from kiutils.symbol import SymbolLib
.kicad_dru DesignRules from kiutils.dru import DesignRules
fp-lib-table FootprintTable from kiutils.libraries import FootprintTable
sym-lib-table SymbolTable from kiutils.libraries import SymbolTable
.kicad_wks WorkSheet from kiutils.wks import WorkSheet

Board Operations

Loading and Saving

from kiutils.board import Board

board = Board.from_file("project.kicad_pcb")
# ... modifications ...
board.to_file()  # Save to same file
board.to_file("modified.kicad_pcb")  # Save to new file

Working with Nets

# List all nets
for net in board.nets:
    print(f"Net {net.number}: {net.name}")

# Find a specific net
def get_net_number(board, net_name):
    """Get net number by name (required for assigning to traces)"""
    net = next((n for n in board.nets if n.name == net_name), None)
    return net.number if net else None

vcc_num = get_net_number(board, "VCC")

Adding Track Segments

from kiutils.items.brditems import Segment
from kiutils.items.common import Position

# Create a track segment
segment = Segment(
    start=Position(X=100.0, Y=50.0),
    end=Position(X=120.0, Y=50.0),
    width=0.25,
    layer="F.Cu",
    net=gnd_num  # Net NUMBER, not object
)
board.traceItems.append(segment)

Adding Arc Traces

from kiutils.items.brditems import Arc

arc = Arc(
    start=Position(X=100.0, Y=50.0),
    mid=Position(X=105.0, Y=45.0),  # Midpoint of arc
    end=Position(X=110.0, Y=50.0),
    width=0.25,
    layer="F.Cu",
    net=net_num
)
board.traceItems.append(arc)

Adding Vias

from kiutils.items.brditems import Via

via = Via(
    position=Position(X=110.0, Y=50.0),
    size=0.8,
    drill=0.4,
    layers=["F.Cu", "B.Cu"],  # Through-hole via
    net=net_num
)
board.traceItems.append(via)

Board Graphics

from kiutils.items.gritems import GrLine, GrRect, GrCircle, GrText, GrArc, GrPoly
from kiutils.items.common import Position, Stroke

# Line
line = GrLine(
    start=Position(X=0, Y=0),
    end=Position(X=10, Y=0),
    layer="Edge.Cuts",
    stroke=Stroke(width=0.1)
)
board.graphicItems.append(line)

# Rectangle
rect = GrRect(
    start=Position(X=0, Y=0),
    end=Position(X=50, Y=30),
    layer="Dwgs.User",
    stroke=Stroke(width=0.15)
)
board.graphicItems.append(rect)

# Circle
circle = GrCircle(
    center=Position(X=25, Y=15),
    end=Position(X=30, Y=15),  # Point on circumference
    layer="F.SilkS",
    stroke=Stroke(width=0.12)
)
board.graphicItems.append(circle)

# Text
from kiutils.items.common import Effects, Font

text = GrText(
    text="LABEL",
    position=Position(X=10, Y=5, angle=0),
    layer="F.SilkS",
    effects=Effects(font=Font(size=1.0, thickness=0.15))
)
board.graphicItems.append(text)

Working with Footprints

# Iterate footprints on board
for fp in board.footprints:
    print(f"{fp.entryName} at ({fp.position.X}, {fp.position.Y})")

    # Access pads
    for pad in fp.pads:
        print(f"  Pad {pad.number}: net {pad.net}")

# Move a footprint
fp = board.footprints[0]
fp.position.X += 10.0
fp.position.Y += 5.0

# Rotate a footprint
fp.position.angle = 90.0

Zones (Copper Pours)

from kiutils.items.zones import Zone, ZoneFillSettings, ZoneKeepoutSettings

# Access existing zones
for zone in board.zones:
    print(f"Zone: net={zone.netName}, layer={zone.layer}")

# Zones have polygon outlines
zone = board.zones[0]
for point in zone.polygon.coordinates:
    print(f"  ({point.X}, {point.Y})")

Schematic Operations

Loading Schematics

from kiutils.schematic import Schematic

sch = Schematic.from_file("project.kicad_sch")

Working with Symbols

# Iterate schematic symbols
for sym in sch.schematicSymbols:
    ref = sym.property("Reference")
    value = sym.property("Value")
    print(f"{ref}: {value}")

# Access symbol position
for sym in sch.schematicSymbols:
    pos = sym.position
    print(f"Symbol at ({pos.X}, {pos.Y}), angle={pos.angle}")

Wires and Junctions

Wires are Connection objects stored in graphicalItems:

from kiutils.schematic import Schematic, Connection, Junction, Position, Stroke

# Access wires (Connection objects with type='wire')
for item in sch.graphicalItems:
    if isinstance(item, Connection) and item.type == 'wire':
        pts = item.points  # List of Position objects
        print(f"Wire: {[(p.X, p.Y) for p in pts]}")

# Create a wire
wire = Connection(
    type='wire',
    points=[Position(X=50.8, Y=50.8), Position(X=101.6, Y=50.8)],
    stroke=Stroke(width=0, type='default'),
    uuid="unique-id"
)
sch.graphicalItems.append(wire)

# Access junctions
for junction in sch.junctions:
    print(f"Junction at ({junction.position.X}, {junction.position.Y})")

# Create a junction
junction = Junction(
    position=Position(X=76.2, Y=50.8),
    diameter=0
)
sch.junctions.append(junction)

Labels

Labels are LocalLabel for local and GlobalLabel for global:

from kiutils.schematic import LocalLabel, GlobalLabel, Position, Effects

# Local labels (same sheet)
for label in sch.labels:
    print(f"Label: {label.text}")

# Create local label
label = LocalLabel(
    text="SIGNAL_NAME",
    position=Position(X=88.9, Y=50.8, angle=0),
    effects=Effects()
)
sch.labels.append(label)

# Global labels (cross-sheet)
for glabel in sch.globalLabels:
    print(f"Global: {glabel.text} ({glabel.shape})")

# Create global label (shapes: input, output, bidirectional, tri_state, passive)
glabel = GlobalLabel(
    text="VCC",
    position=Position(X=127.0, Y=50.8, angle=0),
    shape="input",
    effects=Effects()
)
sch.globalLabels.append(glabel)

# Hierarchical labels
for hlabel in sch.hierarchicalLabels:
    print(f"Hierarchical: {hlabel.text}")

No-Connect Markers

from kiutils.schematic import NoConnect, Position

# Add no-connect marker
nc = NoConnect(
    position=Position(X=50.8, Y=50.8)
)
sch.noConnects.append(nc)

Hierarchical Sheets

# Access sheet instances
for sheet in sch.sheets:
    print(f"Sheet: {sheet.sheetName}")
    for pin in sheet.pins:
        print(f"  Pin: {pin.name}")

BOM Generation

def generate_bom(schematic_path):
    """Generate simple BOM from schematic"""
    sch = Schematic.from_file(schematic_path)
    bom = []

    for sym in sch.schematicSymbols:
        ref = sym.property("Reference")
        value = sym.property("Value")
        footprint = sym.property("Footprint")

        # Skip power symbols
        if ref and not ref.startswith("#"):
            bom.append({
                "Reference": ref,
                "Value": value,
                "Footprint": footprint
            })

    return sorted(bom, key=lambda x: x["Reference"])

Footprint Operations

Loading/Creating Footprints

from kiutils.footprint import Footprint

# Load existing footprint
fp = Footprint.from_file("MyPart.kicad_mod")

# Create new footprint
fp = Footprint()
fp.entryName = "MyNewPart"
fp.layer = "F.Cu"

Adding Pads

from kiutils.items.fpitems import Pad, DrillDefinition
from kiutils.items.common import Position

# SMD pad
smd_pad = Pad(
    number="1",
    type="smd",
    shape="rect",
    position=Position(X=-1.27, Y=0),
    size=Position(X=1.5, Y=0.9),  # Size uses Position class
    layers=["F.Cu", "F.Paste", "F.Mask"]
)
fp.pads.append(smd_pad)

# Through-hole pad
th_pad = Pad(
    number="1",
    type="thru_hole",
    shape="circle",
    position=Position(X=0, Y=0),
    size=Position(X=1.7, Y=1.7),
    drill=DrillDefinition(diameter=1.0),
    layers=["*.Cu", "*.Mask"]
)
fp.pads.append(th_pad)

Pad Types and Shapes

# Pad types
"smd"          # Surface mount
"thru_hole"    # Through-hole
"np_thru_hole" # Non-plated through-hole
"connect"      # Edge connector

# Pad shapes
"circle"       # Round
"rect"         # Rectangle
"oval"         # Oval/oblong
"trapezoid"    # Trapezoid
"roundrect"    # Rounded rectangle
"custom"       # Custom shape

Footprint Graphics

from kiutils.items.fpitems import FpLine, FpRect, FpCircle, FpText, FpArc
from kiutils.items.common import Position, Stroke

# Silkscreen outline
line = FpLine(
    start=Position(X=-2.5, Y=-1.5),
    end=Position(X=2.5, Y=-1.5),
    layer="F.SilkS",
    stroke=Stroke(width=0.12)
)
fp.graphicItems.append(line)

# Courtyard
rect = FpRect(
    start=Position(X=-3.0, Y=-2.0),
    end=Position(X=3.0, Y=2.0),
    layer="F.CrtYd",
    stroke=Stroke(width=0.05)
)
fp.graphicItems.append(rect)

# Reference designator
ref_text = FpText(
    type="reference",
    text="REF**",
    position=Position(X=0, Y=-2.5),
    layer="F.SilkS"
)
fp.graphicItems.append(ref_text)

# Value
val_text = FpText(
    type="value",
    text="MyPart",
    position=Position(X=0, Y=2.5),
    layer="F.Fab"
)
fp.graphicItems.append(val_text)

3D Model Attachment

from kiutils.items.common import Coordinate

fp.models.append({
    "path": "${KICAD7_3DMODEL_DIR}/Package_SO.3dshapes/SOIC-8.wrl",
    "offset": Coordinate(X=0, Y=0, Z=0),
    "scale": Coordinate(X=1, Y=1, Z=1),
    "rotate": Coordinate(X=0, Y=0, Z=0)
})

Saving Footprints

fp.to_file("MyNewPart.kicad_mod")

Symbol Operations

Loading Symbol Libraries

from kiutils.symbol import SymbolLib, Symbol

# Load library
lib = SymbolLib.from_file("MySymbols.kicad_sym")

# Access symbols
for sym in lib.symbols:
    print(f"Symbol: {sym.entryName}")

Creating Symbols

from kiutils.symbol import Symbol, SymbolPin
from kiutils.items.syitems import SyRect, SyCircle, SyPolyline, SyText
from kiutils.items.common import Position

# Create new symbol
sym = Symbol(entryName="MySymbol")

# Add graphic body (rectangle)
body = SyRect(
    start=Position(X=-5.08, Y=5.08),
    end=Position(X=5.08, Y=-5.08),
    strokeWidth=0.254
)
sym.graphic.append(body)

Symbol Pins

# Add pins
pin1 = SymbolPin(
    electricType="input",
    graphicStyle="line",
    position=Position(X=-7.62, Y=2.54, angle=0),
    length=2.54,
    name="IN",
    number="1"
)
sym.pins.append(pin1)

pin2 = SymbolPin(
    electricType="output",
    graphicStyle="line",
    position=Position(X=7.62, Y=2.54, angle=180),
    length=2.54,
    name="OUT",
    number="2"
)
sym.pins.append(pin2)

Pin Electrical Types

"input"           # Input pin
"output"          # Output pin
"bidirectional"   # Bidirectional
"tri_state"       # Tri-state
"passive"         # Passive (resistors, caps)
"power_in"        # Power input (VCC, GND)
"power_out"       # Power output
"open_collector"  # Open collector
"open_emitter"    # Open emitter
"unconnected"     # No connect
"free"            # Unspecified

Pin Graphic Styles

"line"            # Standard line
"inverted"        # With bubble (active low)
"clock"           # Clock input
"inverted_clock"  # Inverted clock
"input_low"       # Active low input
"clock_low"       # Active low clock
"output_low"      # Active low output
"edge_clock_high" # Edge-triggered clock
"non_logic"       # Non-logic (power, etc.)

Saving Symbol Libraries

# Add symbol to library
lib.symbols.append(sym)

# Save
lib.to_file("MySymbols.kicad_sym")

Library Table Operations

Footprint Library Table

from kiutils.libraries import FootprintTable, LibraryEntry

# Load fp-lib-table
fp_table = FootprintTable.from_file("fp-lib-table")

# Add new library
new_lib = LibraryEntry(
    name="MyFootprints",
    type="KiCad",
    uri="${KIPRJMOD}/MyFootprints.pretty",
    options="",
    descr="My custom footprints"
)
fp_table.libs.append(new_lib)

# Save
fp_table.to_file()

Symbol Library Table

from kiutils.libraries import SymbolTable, LibraryEntry

# Load sym-lib-table
sym_table = SymbolTable.from_file("sym-lib-table")

# Add new library
new_lib = LibraryEntry(
    name="MySymbols",
    type="KiCad",
    uri="${KIPRJMOD}/MySymbols.kicad_sym",
    options="",
    descr="My custom symbols"
)
sym_table.libs.append(new_lib)

sym_table.to_file()

Design Rules Operations

from kiutils.dru import DesignRules

# Load design rules
dru = DesignRules.from_file("project.kicad_dru")

# Access rules
for rule in dru.rules:
    print(f"Rule: {rule.name}")
    for constraint in rule.constraints:
        print(f"  {constraint}")

# Modify and save
dru.to_file()

Common Gotchas

1. Net Assignment

# WRONG - assigning Net object
segment.net = board.nets[0]

# CORRECT - assigning net number
segment.net = board.nets[0].number

2. Position Object Required

# WRONG - tuple
segment.start = (10, 20)

# CORRECT - Position object
segment.start = Position(X=10, Y=20)

3. Size Uses Position Class

# Pad size uses Position(X=width, Y=height)
pad.size = Position(X=1.5, Y=0.9)

4. Layer Names Are Strings

# WRONG - using layer numbers
segment.layer = 0

# CORRECT - using layer names
segment.layer = "F.Cu"

5. Circle End Point

# Circle "end" is a point on the circumference, not diameter/radius
circle = GrCircle(
    center=Position(X=10, Y=10),
    end=Position(X=15, Y=10)  # Radius = 5mm
)

6. Symbol Properties

# Access symbol property values
ref = symbol.property("Reference")  # Returns value or None
val = symbol.property("Value")

# This searches the properties list for matching key

7. Append to Lists

# Items are stored in lists - append to add
board.traceItems.append(segment)
board.graphicItems.append(line)
fp.pads.append(pad)

Comparison with kipy

Aspect kiutils kipy
Requires KiCad No Yes (running instance)
Connection File I/O IPC socket
Units Millimeters Nanometers
Transactions None Commit required
File types All (.pcb, .sch, .mod, .sym) PCB only
Use case Batch processing, offline Live manipulation
Install pip install kiutils Included in KiCad

API Documentation

Load the appropriate API reference based on the task domain:

Task Type File Extension Read First
Schematics .kicad_sch api/schematics_apis.md
Board/PCB .kicad_pcb api/board_apis.md
Footprints .kicad_mod api/footprint_apis.md
Symbols .kicad_sym api/symbol_apis.md
Design Rules .kicad_dru api/dru_apis.md
Worksheets .kicad_wks api/wks_apis.md
Library Tables fp_lib_table / sym_lib_table api/libraries_apis.md

Schematics API (api/schematics_apis.md)

  • Schematic class with all attributes
  • Schematic items: Junction, Connection, Labels (Local, Global, Hierarchical)
  • Graphical shapes: Text, TextBox, Rectangle, Circle, Arc
  • Hierarchical sheets and pins
  • Common patterns: BOM generation, wire networks, symbol search

Board/PCB API (api/board_apis.md)

  • Board class with all attributes
  • Trace items: Segment, Via, Arc, Target
  • Graphic items: GrText, GrLine, GrRect, GrCircle, GrArc, GrPoly, GrCurve
  • Zone classes: Zone, FillSettings, KeepoutSettings
  • Layer and stackup configuration
  • Common patterns: board outlines, copper pours, net lookup

Footprint API (api/footprint_apis.md)

  • Footprint class with all attributes (manufacturing, position, path)
  • Pad class: SMD, through-hole, drill definitions, shapes
  • Footprint graphics: FpText, FpLine, FpRect, FpCircle, FpArc, FpPoly, FpCurve
  • 3D model attachment
  • Common patterns: SMD resistor, through-hole connector, batch processing

Symbol API (api/symbol_apis.md)

  • SymbolLib and Symbol classes with all attributes
  • SymbolPin: electrical types, graphical styles, alternate pins
  • Symbol graphics: SyRect, SyCircle, SyArc, SyPolyLine, SyCurve, SyText, SyTextBox
  • Multi-unit symbol creation
  • Common patterns: IC symbols, power symbols, BOM from library

Design Rules API (api/dru_apis.md)

  • DesignRules and Rule classes
  • Constraint types: clearance, track_width, via_diameter, disallow, length, skew
  • Condition expressions: NetClass, NetName, Type, Layer, functions
  • Common patterns: high voltage clearance, differential pairs, BGA areas

Worksheet API (api/wks_apis.md)

  • WorkSheet and Setup classes
  • Drawing objects: TbText, Line, Rect, Polygon, Bitmap
  • Position with corner references (ltcorner, rbcorner, etc.)
  • Text variables: ${TITLE}, ${DATE}, ${REVISION}, ${#}, ${##}
  • Common patterns: title blocks, borders, company logos

Library Table API (api/libraries_apis.md)

  • LibTable and Library classes
  • Library types: KiCad, Legacy, Eagle, Altium
  • Path variables: ${KIPRJMOD}, ${KICAD7_FOOTPRINT_DIR}, etc.
  • Common patterns: add/remove libraries, migrate paths, merge tables

Items Package Reference

For detailed class-level API docs on shared item types used across boards, schematics, footprints, and symbols:

Use Case Read File
Positioning, styling, fonts, colors api/items/common_apis.md
PCB tracks, vias, arcs, stackup api/items/brditems_apis.md
Board/schematic graphics (text, lines, shapes) api/items/gritems_apis.md
Footprint graphics and pads api/items/fpitems_apis.md
Symbol body graphics api/items/syitems_apis.md
Schematic wires, junctions, labels, symbols api/items/schitems_apis.md
Copper zones and keepouts api/items/zones_apis.md
PCB dimension annotations api/items/dimensions_apis.md

When to Use Items APIs

Always start with the file-level API (api/board_apis.md, api/schematic_apis.md, etc.) for high-level operations. Then reference items APIs when you need detailed information about:

  • Creating specific item types (tracks, text, shapes)
  • Understanding attribute defaults and valid values
  • Working with styling (stroke, fill, effects)
  • Understanding coordinate/position conventions

# README.md

kiutils_skill

create KiCad schematic symbols, footprints, and more

you need to setup a directory with an environement where kiutils is installed and update the SKILL.md file with the path/to/directory in order for the agent to have access to it. (see Environment Setup in SKILL.md)

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.