From jitx-skills
Wires up circuits, places components (resistors, capacitors), adds pours, and works with Circuit class, net operators, passive queries, voltage dividers, and copper geometry.
How this skill is triggered — by the user, by Claude, or both
Slash command
/jitx-skills:jitx-circuit-builderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
JITX was rewritten from Stanza to Python. Do not rely on prior JITX knowledge —
JITX was rewritten from Stanza to Python. Do not rely on prior JITX knowledge —
verify all imports with pyright before outputting code.
JITX uses two packages — know which one to import from:
jitx — Core framework. Circuit infrastructure, nets, ports, bundles, geometry.
jitx (top-level): Circuit, Net, Pour, Copper, currentjitx.common: Bundles — Power, GPIOjitx.net: Port system — Port, DiffPair, provide, Providejitx.toleranced: Tolerancedjitx.constraints: Tag, design_constraintjitx.layerindex: Sidejitxlib — Parts library. Components, queries, protocols, symbols, solvers.
jitxlib.parts: Resistor, Capacitor, Inductor, ResistorQuery, CapacitorQuery, InductorQueryjitxlib.protocols.serial: I2C, SPI, UARTjitxlib.symbols.net_symbols: GroundSymbol, PowerSymboljitxlib.voltage_divider: VoltageDividerConstraints, voltage_divider_from_constraintsThese modules DO NOT EXIST — NEVER import from them:
jitx.passives, jitx.passive, jitx.bundles, jitx.bundle, jitx.provide,
jitx.providers, jitx.symbols, jitx.si_units. There is no Device base class in jitx —
subclass Circuit; Device = MyCircuit is a module-level alias for your circuit (see Key Rules). Passives live in jitxlib.parts, bundles in jitx.common, protocols in
jitxlib.protocols.serial, provide in jitx.net.
When unsure, search the installed packages with your Grep tool (pattern class ClassName or def function_name, path .venv, glob *.py); it recurses and is OS-agnostic. Shell fallback: bash grep -rn "class ClassName" .venv/lib/python*/site-packages/jitx*/ (macOS/Linux); on Windows use the Grep tool, or Select-String over .venv\Lib\site-packages\jitx*.
Read the class definition to discover what pins a bundle has — use your Grep tool with context (pattern class Power / class SPI, path .venv, glob *.py, output mode content, ~10–20 lines); it recurses and is OS-agnostic. Shell fallback: bash grep -A 20 "class SPI" .venv/lib/python*/site-packages/jitxlib/protocols/serial.py (macOS/Linux); on Windows use the Grep tool, or Select-String .venv\Lib\site-packages\jitxlib\protocols\serial.py -Pattern "class SPI" -Context 0,20.
Do not hardcode pin names from memory — verify from source. Bundle constructors
may have optional pins (e.g., SPI(cs=True) to enable chip select).
from jitx import Circuit, Net
from jitx.common import Power
from jitx.net import Port
from jitxlib.parts import Resistor, Capacitor
class MyCircuit(Circuit):
"""Circuit subclass — follow this skeleton exactly."""
# 1. Ports are class-level attributes, NEVER assigned in __init__
power = Power()
signal = Port()
# 2. __init__ takes no super() call — Circuit handles setup internally
def __init__(self):
# 3. Named nets — name= is keyword-only (first positional arg is ports)
self.GND = Net(name="GND")
self.VCC = Net(name="VCC")
# 4. += stores the connection (net on LEFT, ports on right)
# bare `a + b` without storing on self silently drops the connection
self.VCC += self.power.Vp
self.GND += self.power.Vn
# 5. Components — ALWAYS assign to self, then insert
self.r1 = Resistor(resistance=10e3)
self.r1.insert(self.power.Vp, self.signal)
# 6. Bypass cap — must also be assigned to self
self.c_bypass = Capacitor(capacitance=100e-9)
self.c_bypass.insert(self.power.Vp, self.power.Vn, short_trace=True)
# Module-level alias for your Circuit — design/build-test code imports this name.
Device = MyCircuit
self.<name> — self.c1 = Capacitor(...) then self.c1.insert(...). Anonymous Capacitor().insert() passes pyright but fails at build time with "Reference to structural object lost during instantiation". Component instantiation should not be done at the class level.insert() belongs to the component — self.r1.insert(portA, portB). No self.insert() or self.add() on Circuit.class X(Circuit): — there is no framework Device or JITXDevice base class to subclass when defining a circuit. Expose the circuit under a module-level alias Device = MyCircuit at the end of the file (see the skeleton above). Downstream design/build-test code then imports and subclasses that alias (from .circuit import Device → class circuit(Device)) — that is expected, since Device is just your Circuit.__init__ — no circuit(), execute(), or build() methods.jitx.Component — import jitx then class MyIC(jitx.Component):.self.x = self.r1.p2 creates multiple parents and fails. To expose a connection point, wire to a class-level Port: self.r1.insert(gpio, self.output_port).For circuits that emit N parallel instances (per-lane fanout, per-row ballout, per-channel filter), construct the JITX objects directly inside the Circuit — do not build an intermediate list[dict[str, Any]] "spec" model and then walk it to emit JITX calls. If you need to batch parameters, use a frozen dataclass with named fields, not bare dict[str, Any]. See jitx/references/architectural-patterns.md §§ "Build the scene graph directly" and "Typed records over dict[str, Any]" before writing record-then-iterate code.
Likewise, don't add to a circuit from a free function (def add_x(circuit): circuit.xyz = ...) — compose a Container subclass holding the group and instantiate it as a member (self.my_x = MyX()). See jitx/references/architectural-patterns.md § "Compose members".
For a same-model self-critique pass on the circuit after writing (catches what these rules don't), invoke jitx-code-review. Optional for single-task use.
Nets can be named in the design when the net is defined. It is good practice to name the net so that the schematic and layout construction are easy to follow. Every power and ground net should carry a symbol definition (PowerSymbol() / GroundSymbol()) — at the top-level design only. This is not cosmetic: power/ground symbols are what connect a rail across the schematic without drawn wires, so the schematic stays legible instead of a rats-nest, and rails join correctly when a design spans multiple schematic pages (see jitx-component-modeler "Multi-Unit Symbols" for page splitting). PowerSymbol() / GroundSymbol() outside TOP_LEVEL_PATH (default designs/) is a hard-fail under scripts/grep_gates.py; the example below shows the top-level pattern.
# Top-level design (in <ns>/designs/...): symbols are legal here.
self.my_net = Net(self.a, name = "my_net")
self.VCC = Net(self.power.Vp, name = "VCC", symbol = PowerSymbol())
Every a + b expression creates a Net — it must be stored or the connection is lost.
# Named nets for power rails — use +=
self.VCC += self.power.Vp + self.ic.VIN
self.GND += self.ic.GND + self.power.Vn
# Group anonymous nets by function
self.feedback_nets = [self.fb_div.out + self.buck.FB]
self.i2c_nets = [
i2c.sda + self.sensor.SDA,
i2c.scl + self.sensor.SCL,
]
# >> topology operator for ordered routing (intermediate nodes are RoutingStructure instances)
self.topology = self.driver.out >> self.trace >> self.receiver.inp
from jitxlib.parts import Resistor, Capacitor, Inductor
# ALWAYS assign to self — anonymous Component().insert() fails at build time
self.r_sense = Resistor(resistance=0.1)
self.r_sense.insert(self.power.Vp, self.sense_out)
# Power-rail caps use short_trace=True (see "short_trace=True is the default
# for power-rail capacitors" below).
self.c_bypass = Capacitor(capacitance=100e-9)
self.c_bypass.insert(self.ic.VCC, self.ic.GND, short_trace=True)
# With extra parameters
self.c_bulk = Capacitor(capacitance=10e-6, rated_voltage=10.0, temperature_coefficient_code="X7R")
self.c_bulk.insert(self.ic.VCC, self.ic.GND, short_trace=True)
self.inductor = Inductor(inductance=4.7e-6, current_rating=3.0)
For all passive values, especially those that are calculated, use the eseries Python package to ensure that the value is legal. If not otherwise specified use the E96 range of values.
short_trace=True is the default for power-rail capacitorsEvery capacitor .insert(...) call on a power rail — decoupling, bypass, bulk, output filter — must pass short_trace=True. The router uses this to minimize the trace length between the cap and its connected ports, which is what makes the cap actually decouple. Without it, the router may place a 0402 100 nF cap 20 mm from the IC and route through vias, defeating the purpose.
# DEFAULT — every power-rail cap
self.c_bulk = Capacitor(capacitance=10e-6, rated_voltage=10.0)
self.c_bulk.insert(self.ic.VCC, self.GND, short_trace=True)
self.c_hf = Capacitor(capacitance=100e-9, rated_voltage=10.0)
self.c_hf.insert(self.ic.VCC, self.GND, short_trace=True)
Exceptions (caps where short_trace=True is NOT used — placement is part of the design):
The short_trace=True rule is gated at the Phase 2 → Phase 3 exit. python scripts/grep_gates.py <ns>/ flags every .insert(...) call missing short_trace= as review-required; the agent dispositions each: fix (add short_trace=True) for power-rail caps, accept-with-rationale (exception: AC coupling, exception: RC time constant, etc.) for non-power-rail caps, or N/A (not a capacitor — resistor insert).
The skill also documents ShortTrace(p1, p2) as an alternative connect-with-short-trace primitive — see https://docs.jitx.com/en/latest/api/jitx.net.html#jitx.net.ShortTrace.
For query refinement, voltage divider, pours, copper geometry, placement, and a complete application circuit example, see references/advanced-patterns.md.
For the deep treatment of physical layout authoring — custom shapes with shapely,
OverlappableCopper (antennas / filters / net-ties), pad features (soldermask / paste /
thermal pad), code-placed vias and routes, and layout-intent tags — invoke the
jitx-physical-layout subskill (the jitx-physical-layout skill). The
Pours / Copper Geometry / Placement sections below are the basics.
NEVER manually calculate resistor values for voltage dividers. Manual values like 8kΩ or 25kΩ
are often not standard E-series values and will fail with "No components meeting requirements".
Always use voltage_divider_from_constraints():
# WRONG — manual resistor values, 8k is not a standard E-series value
self.r_hi = Resistor(resistance=25e3)
self.r_lo = Resistor(resistance=8e3) # FAILS: not a real resistor value
# WRONG — Toleranced.exact() on v_out gives zero tolerance, solver WILL fail
VoltageDividerConstraints(v_out=Toleranced.exact(0.6), ...)
# CORRECT — always use Toleranced.percent() for v_out, always include prec_series
VoltageDividerConstraints(
v_in=Toleranced.exact(3.3),
v_out=Toleranced.percent(0.6, 2.0), # ±2% tolerance window (REQUIRED)
current=0.6 / 10e3,
prec_series=[1.00, 0.10], # precision grades (REQUIRED)
base_query=ResistorQuery(case=["0402"]),
)
For all @provide / @provide.one_of / @provide.subset_of / Provide() / require() patterns, see the jitx-pin-assignment skill. Invoke the jitx-pin-assignment skill.
net.symbol — Net SymbolsAnother option to provide a symbol on a net (if not done at Net() creation definition) is to assign to the .symbol attribute, never use insert() or +=. Same top-level restriction as above — only in TOP_LEVEL_PATH (default designs/):
# Top-level design only.
self.GND = Net(name="GND")
self.GND.symbol = GroundSymbol() # attribute assignment, NOT insert()
pyright path/to/circuit.py
Fix all import and type errors before proceeding. Ignore errors about .prebuilt_components relative imports — but always use the relative form (from .prebuilt_components import ...) since absolute imports fail at build time.
Create a test harness to verify the circuit builds with the JITX backend (utilizing the required virtual environment):
# design.py
from jitx.container import inline
from jitx.sample import SampleDesign
from jitxlib.parts import ResistorQuery, CapacitorQuery, InductorQuery
from .circuit import Device
class TestDesign(SampleDesign):
resistor_defaults = ResistorQuery(case=["0402", "0603", "0805"])
capacitor_defaults = CapacitorQuery(case=["0402", "0603", "0805", "1206"])
inductor_defaults = InductorQuery(mounting="smd")
@inline
class circuit(Device):
pass
jitx build <module>.design.TestDesign
Don't run parallel JITX builds against the same project — sequence them. See jitx/SKILL.md "Build Safety".
If a build_test helper is available (e.g., in the skill_eval package), use it instead:
python -m skill_eval.build_test path/to/circuit.py
If the build fails:
def method_name|class ClassName, path .venv, glob *.py); it recurses and is OS-agnostic. Shell fallback: bash grep -rn "def method_name\|class ClassName" .venv/lib/python*/site-packages/jitx*/ (macOS/Linux); on Windows use the Grep tool, or Select-String over .venv\Lib\site-packages\jitx*.Format all generated circuit code with ruff:
ruff format path/to/file.py
npx claudepluginhub jitx-inc/jitx-skills --plugin jitx-skillsGenerates JITX Python component code from datasheets, KiCad footprints, or user specifications with strict source authority enforcement for geometry and pinout.
Automates KiCad to EasyEDA to JLCPCB PCB workflow: project setup, LCSC part sourcing, pin-map fetching, fully-wired .kicad_pcb generation via pcbnew, and EasyEDA handoff for routing and ordering.
Generates publication-quality circuit diagrams (SVG/PDF/PNG) from natural language descriptions using the schemdraw Python library. Supports resistors, capacitors, ICs, opamps, logic gates, and more.