Skip to main content
skill CX Codex

zener-language — Claude Code skill: install guide

Guides work on Zener HDL for schematics, components, package APIs, and dependencies. Use it when editing .zen files in PCB projects.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add diodeinc/pcb --skill zener-language -a claude-code
Open on GitHub

What zener-language does

This skill guides an agent through Zener, a Starlark-based language with modules for electrical connections, physical parts, layout, sourcing, and diagnostics. It covers load() and Module() imports, public io() and config() declarations, plus connection types such as Power, Ground, Net, I2c, Spi, and Usb2.

Public electrical connections should be flat top-level assignments, and public Power IO should carry a voltage range unless the API is deliberately generic. Configurations may alter values or dnp= state, but should not conditionally create, remove, or reconnect schematic instances. That matters when a configuration change would alter the schematic topology.

Before changing a package, the skill directs the agent to locate local sources with pcb list -m -json @stdlib or pcb list -m -json <package>[@<version>] and inspect its .zen files. Check installed CLI behaviour with --help; inspect the installed version and the pcb changelog when version history matters.

What it helps with

  • inspect a Zener package public API
  • define schematic module IO and configuration
  • import a local or remote .zen module
  • keep topology stable across configuration changes
  • enforce electrical connection constraints

Example prompts for the agent

  • "Read this .zen module and explain its public io() and config() API."
  • "Add an I2c interface to this module while keeping the existing schematic topology."
  • "Use pcb list -m -json @stdlib to locate the package API and inspect its .zen files."

How to install zener-language

1Skills CLI (Vercel)

npx skills add diodeinc/pcb --skill zener-language -a claude-code

2Manually: put the files in place

~/.claude/skills/zener-language/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/zener-language/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about zener-language

How do I install zener-language in Claude Code?

Run npx skills add diodeinc/pcb --skill zener-language -a claude-code, then run npx skills add diodeinc/pcb --skill zener-language -a claude-code again. The skill file is at ~/.claude/skills/zener-language/SKILL.md.

What does zener-language do?

It guides agents when reading or editing Zener HDL, including modules, physical components, electrical interfaces, and package dependencies. It also sets rules for configuration changes and schematic topology.

How should I inspect a Zener package API?

Use pcb list -m -json @stdlib or pcb list -m -json <package>[@<version>] to find package sources. Use --help on the installed CLI command to verify its behaviour.

Source text

SKILL.md · MIT
---
name: zener-language
description: Read or edit Zener HDL, package APIs, and tool-managed dependencies.
---

# Zener Language

Zener is Starlark plus PCB-specific modules, typed electrical connections, physical parts, sourcing, layout, and diagnostics.

## Discover Before Authoring

Use `pcb list -m -json @stdlib` or `pcb list -m -json <package>[@<version>]` to find a package's local source directory, then read its `.zen` files for the public API and exact behavior.

For CLI behavior, use the installed command's `--help`; do not invent or infer subcommands or flags. When version history matters, inspect the installed version and nearby entries in the [`pcb` changelog](https://github.com/diodeinc/pcb/blob/main/CHANGELOG.md) instead of relying on old examples.

## Modules and Components

A `.zen` file can be:

- a normal Starlark module imported with `load()`; or
- an instantiable schematic module imported with `Module()`.

Relative paths stay within the current package. Cross-package imports use full package URLs:

```zen
load("./helpers.zen", "helper")
LocalBlock = Module("./LocalBlock.zen")
RemoteBlock = Module("github.com/org/repo/modules/RemoteBlock.zen")
```

An instantiation passes `name=...`, its public `io()` and `config()` inputs, and optional `dnp` and `properties` arguments.

`name` establishes instance-path identity, not a fixed reference designator. Refdes-like names are only annotation hints; exact source-reference preservation is not supported.

Use refdes-like names only when the user explicitly asks. In that case, set `prefix=` on the underlying `Component()` to match the refdes prefix.

`Component()` creates a physical part from `name`, `symbol`, and `pins`. The selected symbol is the authority for its footprint, part identity, datasheet, and pins. Keep those properties correct in `.kicad_sym` rather than repeating them in Zener. Omit true `no_connect` pins; they are wired to `NotConnected()` automatically.

Use `Symbol(library, name=...)` for multi-symbol libraries. Use `Part(mpn=..., manufacturer=...)` only when the symbol does not already provide part identity.

`Project()` takes `layout = False` or `schematic = False` to disable either. Use one declaration per entrypoint with a config-independent path relative to its `.zen` file; use separate entrypoints for distinct projects.

## IO and Config

Define public electrical connections as flat top-level assignments:

```zen
VDD = io(Power(voltage="3.0V to 5.5V"))
GND = io(Ground)
EN = io(Net, optional=True)
```

Do not introduce legacy `Pins = struct(...)` wrappers in new or touched APIs.

With `optional=True`, an omitted IO receives an automatically generated net or interface.

`Net` is the base connection type. `Power`, `Ground`, `NotConnected`, and stdlib interfaces add constraints and semantics. Across an `io()` boundary, `NotConnected` can promote to any net type, a specialized net can demote to `Net`, and a plain `Net` does not automatically gain specialized semantics. Adapt intentionally with casts such as `Power(net, voltage=...)` or `Net(power_net)`.

Use stdlib interfaces such as `DiffPair`, `I2c`, `I3c`, `Spi`, `Qspi`, `Uart`, `Usart`, `Swd`, `Jtag`, `Usb2`, and `Usb3` when the grouped protocol semantics are meaningful.

Use `UartPair()` and `UsartPair()` when a point-to-point link should cross-connect the two endpoints.

Declare a voltage range on every public `Power` IO unless the API is intentionally generic.

Define non-electrical choices with typed `config()`:

```zen
output_voltage = config(Voltage, default="3.3V", allowed=["3.3V", "5V"])
```

Load physical value types from `@stdlib/units.zen`: `Voltage`, `Current`, `Resistance`, `Capacitance`, `Inductance`, `Impedance`, `Frequency`, `Temperature`, `Time`, and `Power`. String defaults and allowed values auto-convert to the declared physical type. Use enums for non-physical modes and strategies. Expose application-level choices rather than internal passive values or implementation details.

Physical constructors accept point values, engineering notation, ranges, and tolerances. Arithmetic tracks units. Equality between two physical values is strict; use `.matches(...)` for coercive comparison with a string or scalar. Inspect the installed API for other operations.

Enum defaults use the selected string value:

```zen
Mode = enum("PULLUP", "PULLDOWN")
mode = config(Mode, default="PULLUP")
```

## Stable Topology

Configs may change values and `dnp=` state, but should not add, remove, or reconnect schematic instances. Stable instance and net identity preserves layout and reviewability.

- Compute a selected value on one component when the nets do not change.
- Instantiate every mutually exclusive strap or option and DNP the inactive alternatives.
- Do not use conditional instantiation to change topology.
- Use an IC's internal pull-up or pull-down for its default mode when appropriate; add external bias components with `dnp=` only for populated alternatives.

Put non-trivial electrical calculations in named functions. Cite the relevant datasheet equation or table and snap calculated values with the appropriate stdlib E-series helper.

The available E-series helpers in `@stdlib/utils.zen` are `e3`, `e6`, `e12`, `e24`, `e48`, `e96`, and `e192`.

Use `check`, `warn`, `error`, and stdlib checks for enforceable electrical constraints rather than documenting them only in comments.

For reusable power-rail boundary checks, inspect and prefer `voltage_within(...)` from `@stdlib/checks.zen` instead of duplicating the constraint.

## Public Compatibility

Reusable-package compatibility includes the public `io()` and `config()` API, entrypoints, behavior, layout, and physical integration assumptions. A build of the current package does not prove existing consumers remain compatible.

If consumers must change to adopt an update, treat it as breaking. Document the migration and use a breaking commit.

## Schematic Position State

Inspect the root declaration and its flags:

- `Board(..., schematic = True)` uses `layout_path`; `Project(...)` uses `path` with schematics enabled by default. These KiCad files are persistent state.
- Without a linked schematic, preserve legacy `# pcb:sch <ID> ...` placement comments, add new code above the block, and do not use the `agent-schema` CLI. On rename or deletion, update only the corresponding records; do not hand-edit coordinates outside requested schematic layout work.

For a requested legacy migration, first run `pcb-sch export-kicad <root.zen> --output <fresh-directory>`. Preserve the exported placement and existing layout when linking the KiCad files: enable `schematic = True` on the existing `Board()`, or replace a module's `Layout()` with `Project()`. Run `pcb apply schematic --no-open <root.zen>` twice; the second must report `schematic unchanged`.

## Packages and Dependencies

The stdlib is toolchain-managed and does not belong in `[dependencies]`. Other packages are declared by their `load()` or `Module()` imports, and the dependency state in `pcb.toml`, including indirect entries, is tool-managed.

Use `pcb list -m -u` to inspect compatible and breaking updates. Use `pcb add -u` for compatible updates and `pcb list -m -versions <url>` plus `pcb add <url>@<version>` for a specific or breaking version. Do not hand-edit resolved versions or use the legacy `pcb update` workflow.

Board roots contain workspace and board metadata. Registry roots contain reusable component and module members without a root board. Reusable packages contain their own direct dependencies and optional default parts.

## Style

Match the surrounding Zener code. Keep declarations concise, use comments for evidence or non-obvious judgment, and avoid decorative section banners or prose that restates the code. These cleanup rules never apply to `# pcb:sch` records.

Use established naming:

- public `io()` names: uppercase;
- `config()` names: lowercase;
- component instances: uppercase functional names; and
- differential signals: `_P` and `_N` suffixes (KiCad pairs nets by these suffixes alone).

Prefer stdlib generics for common passives, discretes, connectors, test points, and mechanical features. Inspect the current stdlib package rather than relying on a memorized inventory. Use `Rectifier`, `Zener`, or `Tvs` instead of the deprecated generic `Diode`.

For ordinary boards, prefer `Board(..., layers=<count>)`; standard defaults exist for 2, 4, 6, 8, and 10 layers. Customize them with `outer_copper_weight`, `copper_finish`, `solder_mask_color`, `track_widths`, and `via_dimensions`. Use explicit stackup and design-rule records only when those defaults are insufficient; an explicit `config` merges over the layers-derived defaults.

## Completion Evidence

Use each supported primitive for its own purpose:

- after changing imports or dependencies, run `pcb sync` from the relevant workspace or package;
- run `pcb fmt` on changed Zener;
- run `pcb build <path>` for affected entrypoints to evaluate the design and collect diagnostics; for registry package curation, use `pcb build -Wstyle <path>` to promote style advice to warnings; and
- use `pcb bom <entrypoint>.zen -f json` only when sourceability or part selection is relevant.

Use the applicable checks and engineering evidence for the changed API, circuit, or dependencies. Preserve schematic position state and report unverified work.

© diodeinc · MIT · diodeinc/pcb

Similar skills

All skills for Programming languages →

From the same set4

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor