feat: add hierarchy tools (hierarchical label + sheet pin)

Two new tools for managing hierarchical schematic connections:

- add_schematic_hierarchical_label: create sheet interface ports on
  sub-sheet schematics. These are the sub-sheet side of hierarchical
  connections, linking to sheet pins on the parent.

- add_sheet_pin: add pins to sheet symbol blocks on the parent
  schematic. Targets the correct sheet by matching the Sheetname
  property. The pinName must match a hierarchical_label in the
  sub-sheet.

Both tools use text-based S-expression insertion (not sexpdata
round-trip) to preserve KiCad's native file formatting. Labels
include proper justification based on orientation: left-justify for
rightward labels (0°), right-justify for leftward labels (180°).

Wire manager additions:
- _find_insertion_point(): locates sheet_instances block or final paren
- _text_insert(): inserts formatted S-expression text at the right position
- _make_hierarchical_label_text(): generates hierarchical_label S-expression
- _make_sheet_pin_text(): generates sheet pin S-expression
- WireManager.add_hierarchical_label(): static method for label insertion
- WireManager.add_sheet_pin(): static method for pin insertion into
  named sheet blocks

12 unit tests covering insertion, orientation/justification mapping,
parameter validation, multi-sheet targeting, and error handling.
This commit is contained in:
Leah Armstrong
2026-04-15 12:32:09 -04:00
parent eea43d18f1
commit 101b4e1dad
4 changed files with 749 additions and 0 deletions

View File

@@ -1524,4 +1524,125 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp
}
},
);
// Add hierarchical label to a sub-sheet
server.tool(
"add_schematic_hierarchical_label",
"Add a hierarchical label (sheet interface port) to a sub-sheet schematic. " +
"Hierarchical labels are the connection points that link a sub-sheet to its " +
"parent via sheet pins. The label text must exactly match the corresponding " +
"sheet pin name.",
{
schematicPath: z.string().describe("Path to the sub-sheet .kicad_sch file"),
text: z
.string()
.describe("Label text (e.g. 'SD_CLK') — must match the sheet pin name"),
position: z
.array(z.number())
.length(2)
.describe("Position [x, y] in mm"),
shape: z
.enum(["input", "output", "bidirectional"])
.describe("Signal direction from the sub-sheet's perspective"),
orientation: z
.number()
.optional()
.describe(
"Rotation in degrees: 0=label points right, 180=label points left (default: 0)",
),
},
async (args: {
schematicPath: string;
text: string;
position: number[];
shape: "input" | "output" | "bidirectional";
orientation?: number;
}) => {
const result = await callKicadScript("add_schematic_hierarchical_label", args);
if (result.success) {
return {
content: [
{
type: "text" as const,
text: result.message || `Added hierarchical label '${args.text}'`,
},
],
};
}
return {
content: [
{
type: "text" as const,
text: `Failed to add hierarchical label: ${result.message || "Unknown error"}`,
},
],
};
},
);
// Add sheet pin to a sheet block on the parent schematic
server.tool(
"add_sheet_pin",
"Add a pin to a sheet symbol block on the parent schematic. Sheet pins are the " +
"parent-side connection points that correspond to hierarchical labels in the " +
"sub-sheet. The pinName must exactly match a hierarchical_label in the sub-sheet.",
{
schematicPath: z.string().describe("Path to the PARENT .kicad_sch file"),
sheetName: z
.string()
.describe(
"Sheet name as it appears in the Sheetname property (e.g. 'Storage')",
),
pinName: z
.string()
.describe("Pin name — must match a hierarchical_label in the sub-sheet"),
pinType: z
.enum(["input", "output", "bidirectional"])
.describe(
"Signal direction (should match the sub-sheet hierarchical label shape)",
),
position: z
.array(z.number())
.length(2)
.describe(
"Pin position [x, y] in mm — must be on the sheet block boundary",
),
orientation: z
.number()
.optional()
.describe(
"Pin orientation: 0=right edge of sheet box, 180=left edge (default: 0)",
),
},
async (args: {
schematicPath: string;
sheetName: string;
pinName: string;
pinType: "input" | "output" | "bidirectional";
position: number[];
orientation?: number;
}) => {
const result = await callKicadScript("add_sheet_pin", args);
if (result.success) {
return {
content: [
{
type: "text" as const,
text:
result.message ||
`Added sheet pin '${args.pinName}' to sheet '${args.sheetName}'`,
},
],
};
}
return {
content: [
{
type: "text" as const,
text: `Failed to add sheet pin: ${result.message || "Unknown error"}`,
},
],
};
},
);
}