@huaqiu/dsh-hqsch 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/cordis.patch.yml +13 -0
  4. package/lib/index.d.mts +98 -0
  5. package/lib/index.mjs +159 -0
  6. package/package.json +40 -0
  7. package/skills/hqsch-scenario-b/AGENTS.md +209 -0
  8. package/skills/hqsch-scenario-b/CLAUDE.md +209 -0
  9. package/skills/hqsch-scenario-b/INSTALL.md +140 -0
  10. package/skills/hqsch-scenario-b/SKILL.md +107 -0
  11. package/skills/hqsch-scenario-b/SYSTEM-PROMPT.md +209 -0
  12. package/skills/hqsch-scenario-b/docs/circuit-pattern-layout.md +304 -0
  13. package/skills/hqsch-scenario-b/docs/editing-a-circuit.md +167 -0
  14. package/skills/hqsch-scenario-b/docs/part-search.md +244 -0
  15. package/skills/hqsch-scenario-b/docs/placement-conventions.md +714 -0
  16. package/skills/hqsch-scenario-b/docs/property-conventions.md +225 -0
  17. package/skills/hqsch-scenario-b/docs/quickstart.md +99 -0
  18. package/skills/hqsch-scenario-b/docs/reading-a-circuit.md +317 -0
  19. package/skills/hqsch-scenario-b/docs/rpc/canvas-auto-connect-objects-by-id.md +98 -0
  20. package/skills/hqsch-scenario-b/docs/rpc/canvas-delete-objects-by-ids.md +94 -0
  21. package/skills/hqsch-scenario-b/docs/rpc/canvas-find-object-by-property.md +101 -0
  22. package/skills/hqsch-scenario-b/docs/rpc/canvas-list-wire-segments.md +94 -0
  23. package/skills/hqsch-scenario-b/docs/rpc/canvas-mirror-objects-by-ids.md +96 -0
  24. package/skills/hqsch-scenario-b/docs/rpc/canvas-rotate-objects-by-ids.md +94 -0
  25. package/skills/hqsch-scenario-b/docs/rpc/canvas-set-object-property.md +99 -0
  26. package/skills/hqsch-scenario-b/docs/rpc/canvas-zoom-all.md +92 -0
  27. package/skills/hqsch-scenario-b/docs/rpc/find-find-object.md +96 -0
  28. package/skills/hqsch-scenario-b/docs/rpc/kernel-get-snapshot.md +104 -0
  29. package/skills/hqsch-scenario-b/docs/rpc/netlist-get-active-page-net-list.md +87 -0
  30. package/skills/hqsch-scenario-b/docs/rpc/netlist-get-port-pairing.md +34 -0
  31. package/skills/hqsch-scenario-b/docs/rpc/obj-place-place-pin-stub-wire-and-net-alias.md +114 -0
  32. package/skills/hqsch-scenario-b/docs/rpc/pattern-layout-apply-circuit-pattern.md +125 -0
  33. package/skills/hqsch-scenario-b/docs/rpc/pattern-layout-get-page-occupancy.md +100 -0
  34. package/skills/hqsch-scenario-b/docs/rpc/pattern-layout-list-circuit-patterns.md +97 -0
  35. package/skills/hqsch-scenario-b/docs/rpc/placement-list-part-libraries.md +94 -0
  36. package/skills/hqsch-scenario-b/docs/rpc/placement-list-symbol-libraries.md +94 -0
  37. package/skills/hqsch-scenario-b/docs/rpc/placement-place-kicad-symbol.md +105 -0
  38. package/skills/hqsch-scenario-b/docs/rpc/placement-place-part-from-library.md +106 -0
  39. package/skills/hqsch-scenario-b/docs/rpc/placement-place-symbol-from-library.md +108 -0
  40. package/skills/hqsch-scenario-b/docs/rpc/project-get-active-project.md +96 -0
  41. package/skills/hqsch-scenario-b/docs/rpc/project-list-open-projects.md +99 -0
  42. package/skills/hqsch-scenario-b/docs/rpc-availability.md +97 -0
  43. package/skills/hqsch-scenario-b/docs/rpc-index/canvas.md +81 -0
  44. package/skills/hqsch-scenario-b/docs/rpc-index/find.md +36 -0
  45. package/skills/hqsch-scenario-b/docs/rpc-index/kernel.md +36 -0
  46. package/skills/hqsch-scenario-b/docs/rpc-index/netlist.md +38 -0
  47. package/skills/hqsch-scenario-b/docs/rpc-index/obj-place.md +76 -0
  48. package/skills/hqsch-scenario-b/docs/rpc-index/pattern-layout.md +38 -0
  49. package/skills/hqsch-scenario-b/docs/rpc-index/placement.md +48 -0
  50. package/skills/hqsch-scenario-b/docs/rpc-index/project.md +47 -0
  51. package/skills/hqsch-scenario-b/docs/serialization.md +60 -0
  52. package/skills/hqsch-scenario-b/docs/skill-dependencies.md +93 -0
  53. package/skills/hqsch-scenario-b/skill.json +13 -0
  54. package/skills/hqsch-scenario-b/template/package.json +16 -0
  55. package/skills/hqsch-scenario-b/template/scripts/edit-property-by-ref.ts +44 -0
  56. package/skills/hqsch-scenario-b/template/scripts/fix-designators.ts +52 -0
  57. package/skills/hqsch-scenario-b/template/scripts/hello.ts +46 -0
  58. package/skills/hqsch-scenario-b/template/scripts/read-circuit.ts +357 -0
  59. package/skills/hqsch-scenario-b/template/scripts/replace-part-by-ref.ts +44 -0
  60. package/skills/hqsch-scenario-b/template/scripts/rewire-pin-by-ref.ts +42 -0
  61. package/skills/hqsch-scenario-b/template/tsconfig.json +11 -0
  62. package/src/config.ts +25 -0
  63. package/src/index.ts +186 -0
  64. package/src/paths.ts +48 -0
@@ -0,0 +1,304 @@
1
+ # Circuit Pattern Layout (HQSCH)
2
+
3
+ Parametric local-circuit layout. The agent identifies a recurring sub-circuit,
4
+ places the parts anywhere on the page (position may be rough), binds each part
5
+ to a **role**, and calls `ApplyCircuitPattern`. The engine then computes exact
6
+ positions / rotations from a LEFT_TO_RIGHT template, optionally adds GND/VCC,
7
+ routes intra-pattern nets as collinear stubs + rails, and returns one
8
+ `occupied_box` for later avoidance.
9
+
10
+ Coordinates are the same **external display** space as `Place*` RPCs: page
11
+ top-left origin, Y down. See [placement-conventions.md](placement-conventions.md).
12
+
13
+ > **After every `ApplyCircuitPattern`:** wire leftovers with the [routing mode gate](placement-conventions.md#routing-mode-gate-mandatory-before-every-net).
14
+ > **Measure first:** short → `autoConnect`; long / crowded → dual-end stub+NetAlias.
15
+ > Do **not** `autoConnect` far-apart modules without checking distance.
16
+
17
+ ## When to use a pattern vs hand layout
18
+
19
+ | Use a pattern | Hand-layout instead |
20
+ | --- | --- |
21
+ | Decoupling / bypass caps on one power pin | One-off geometry that matches no catalog entry |
22
+ | Pull-up / pull-down cluster | Cross-module nets after patterns are applied |
23
+ | MCU reset (pull-up + cap + optional button) | Simple single-pin pull-up only (use `pull_resistor`) |
24
+ | I2C (master + slaves + SDA/SCL pull-ups) | Long-distance leftover nets (stub + NetAlias) |
25
+ | SPI (master + slaves, CS lanes) | Parts that must stay exactly where the user put them |
26
+ | Crystal + load caps (+ feedback R) | |
27
+
28
+ ## Mixed-circuit decomposition
29
+
30
+ Most real sheets are **not** one catalog entry end-to-end. Before any hand
31
+ layout or `autoConnect` pass, **decompose** the design into subnets and classify
32
+ each subnet independently.
33
+
34
+ ```
35
+ Whole design
36
+ ├─ subnet A → matches catalog? → ApplyCircuitPattern
37
+ ├─ subnet B → matches catalog? → ApplyCircuitPattern
38
+ └─ subnet C → no catalog match → hand layout (gate: short autoConnect, long stub+NetAlias)
39
+ ```
40
+
41
+ ### Rules
42
+
43
+ 1. **Per-subnet, not per-sheet.** If subnet A matches `pull_resistor`, apply it
44
+ even when subnet B (e.g. a termination resistor between two bus wires, pin
45
+ short, cross-module net alias) has no pattern.
46
+ 2. **Do not skip a pattern because another part of the sheet is hand-only.**
47
+ Wrong: “this interface has no dedicated pattern → hand-layout everything
48
+ including obvious pull-ups.”
49
+ Right: apply `pull_resistor` / `decoupling_cap` / … for every matching
50
+ subnet; hand-layout only the leftovers.
51
+ 3. **One `ApplyCircuitPattern` call = one catalog entry = one local cluster.**
52
+ Do not try to bind termination resistors, bus transceivers, and pull-ups
53
+ into a single apply unless the catalog explicitly defines that combined
54
+ pattern.
55
+ 4. **Apply order:** pattern subnets first (each with its own anchor +
56
+ `occupied_box`), then hand leftovers. Re-read `get-page-occupancy` /
57
+ reserved boxes before the next apply.
58
+ 5. **After apply:** do not re-hand-wire intra-pattern nets listed in
59
+ `response.connections`. Hand work is only for subnets that never entered a
60
+ pattern.
61
+
62
+ ### Same part in multiple patterns
63
+
64
+ | Role kind | Moved by pattern? | Same part in multiple applies? |
65
+ | --- | --- | --- |
66
+ | `host`, `signal_host`, `master` | **No** (`movable=false`) | **Yes** — same IC in multiple applies **or** one apply with multiple `slotIndex` pairs (see [Pull resistor: multiple pins](#pull-resistor-multiple-pins)) |
67
+ | `cap`, `resistor`, `slave`, `xtal`, … | **Yes** | **No** — each physical part bound to **at most one** pattern |
68
+
69
+ **Forbidden:** bind the same **movable** part (e.g. one resistor) as `resistor`
70
+ in two different applies.
71
+
72
+ **Allowed:** the same MCU or transceiver as `signal_host` / `host` in several
73
+ applies, as long as each apply targets a **different pin or role terminal**
74
+ (e.g. pin A pull-up apply + pin B pull-down apply). When several pins share the
75
+ **same polarity and the same rail** (e.g. CC1 + CC2 both 5.1k→GND), prefer
76
+ **one apply with multiple slot pairs** instead of separate applies.
77
+
78
+ ### Classifying ambiguous subnets
79
+
80
+ | Electrical shape | Typical pattern | Notes |
81
+ | --- | --- | --- |
82
+ | One signal pin → one R → VCC | `pull_resistor` `polarity=up` | Bind `signal_host` + `resistor`; optional `rail` or auto-place VCC |
83
+ | One signal pin → one R → GND | `pull_resistor` `polarity=down` | Same; optional `rail` = GND |
84
+ | One power pin → bulk (+ optional bypass) → GND | `decoupling_cap` | See bulk/bypass bind table below |
85
+ | Master + slaves + SDA/SCL pull-ups | `i2c_bus` | Prefer over separate pull_resistor applies for the I2C cluster |
86
+ | Master + slaves + SPI lanes | `spi_bus` | Apply with **`route_mode=NONE`** (layout only); AI routes each `connections[]` entry |
87
+ | MCU xin/xout + crystal + load caps | `crystal` | |
88
+ | MCU reset + pull-up + filter cap (+ button) | `reset_circuit` | Host stub+`reset_net`; internal autoconnect |
89
+ | R between **two signal pins** (termination, series) | **Hand** | Not `pull_resistor` (that pattern is signal → R → rail) |
90
+ | Two control pins shorted (e.g. RE + DE) | **Hand** | `autoConnect` + NetAlias |
91
+ | Net alias to off-page / MCU port | **Hand** | stub + `placeNetAliasAt` after patterns |
92
+
93
+ When the catalog grows, call `list-circuit-patterns` again — do not rely only on
94
+ the static table below.
95
+
96
+ ## Catalog (call ListCircuitPatterns)
97
+
98
+ Always call `pattern-layout-list-circuit-patterns` first. The table below is a
99
+ shortcut; the RPC is the source of truth for roles / terminals / options.
100
+
101
+ | `pattern` | `id` | Required roles | Repeatable | Optional roles | Key options (defaults) |
102
+ | --- | --- | --- | --- | --- | --- |
103
+ | `CIRCUIT_PATTERN_DECOUPLING_CAP` | `decoupling_cap` | `host`(vcc), `cap[0]` bulk, optional `cap_bypass` or `cap[1]` | `cap` 1..8, `cap_bypass` 0..1 | `gnd`, `vcc` | `pitch=8`, `cap_gap=4`, `rail_offset=4`, `gnd_gap=4`, `power_net=VCC`, `power_symbol=VCC` |
104
+ | `CIRCUIT_PATTERN_PULL_RESISTOR` | `pull_resistor` | `signal_host`(sig), `resistor[]`(a/b) | both 1..8 | `rail` | `polarity=up\|down`, `pitch=6`, `lane_gap=4`, `rail_gap=4` |
105
+ | `CIRCUIT_PATTERN_I2C_BUS` | `i2c_bus` | `master`(sda/scl), `r_pullup_sda`, `r_pullup_scl` | `slave` 0..8 | `vcc` | `lane_gap=3`, `device_gap=10`, `pull_gap=4`, `pull_offset=4`, `sda_net=SDA`, `scl_net=SCL`, `lane_above=true` |
106
+ | `CIRCUIT_PATTERN_SPI_BUS` | `spi_bus` | `master`(sck/mosi/miso/cs), `slave[]` | `slave` 1..8 | — | `lane_gap=3`, `device_gap=10`, `share_cs=false`; **default `PATTERN_ROUTE_NONE`** — response `connections[]` lists pending nets (`note=pending AI routing`); agent chooses `autoConnect` vs dual-end `placePinStubWireAndNetAlias` per [placement-conventions.md](placement-conventions.md) |
107
+ | `CIRCUIT_PATTERN_CRYSTAL` | `crystal` | `host`(xin/xout), `xtal`(a/b), `cap_load_1`, `cap_load_2` | — | `r_feedback`, `gnd` | `cap_gap=4`, `xtal_offset=6`, `cap_spread=4` |
108
+ | `CIRCUIT_PATTERN_RESET_CIRCUIT` | `reset_circuit` | `host`(rst), `pull_up`(a/b), `filter_cap`(a/b) | — | `reset_sw`, `vcc`, `gnd` | `reset_net=RESET`, `pitch=6`, `branch_offset=4`, `branch_gap=4`, `sw_outward_gap=4`, `vcc_gap=4`, `gnd_gap=4`, `stub_length=3` |
109
+
110
+ `host` / `signal_host` are **not moved** (`movable=false`). Power symbols can be
111
+ bound (`power_mode=BOUND_ONLY`) or auto-created (`AUTO_PLACE`, default).
112
+
113
+ `anchor` is an external display point. Exact meaning is in
114
+ `descriptor.anchor_semantics` (typically the first device's bottom-left or the
115
+ leftmost cap).
116
+
117
+ `orientation` defaults to `LEFT_TO_RIGHT`. Other values are an affine transform
118
+ of the same template — do not rewrite roles.
119
+
120
+ ### Decoupling cap: bulk + optional bypass
121
+
122
+ Typical LDO/MCU input pairs: **10µF bulk** + **100nF bypass** on the same power
123
+ pin. The engine places both caps **vertical**, **same pin-row height**, **parallel
124
+ along the host pin outward direction** (e.g. VIN on the right → both caps sit to
125
+ the right of the pin, spaced by `cap_gap`).
126
+
127
+ | Bind | When |
128
+ | --- | --- |
129
+ | `cap` slot 0 | Always — bulk / filter cap (e.g. 10µF) |
130
+ | `cap_bypass` | When the design includes an HF bypass (e.g. 100nF). **Omit the role entirely** when there is no bypass cap. |
131
+ | `cap` slot 1 | Legacy alias for bypass (same layout as `cap_bypass`) |
132
+
133
+ After `ApplyCircuitPattern`, read `response.message`:
134
+
135
+ - `ok; bulk+bypass caps parallel outward …` — bypass was bound and laid out
136
+ - `ok; bulk filter cap only …` — no bypass role bound
137
+
138
+ Do **not** hand-wire between bulk and bypass after apply; autoconnect covers
139
+ `host.vcc → cap[0].a`, `cap.a → rail` (every cap), and `cap.b → GND` — one wire
140
+ per pin. Never wire `host.vcc → each cap.a` or `host.vcc → rail` by hand: the
141
+ first duplicates the cap pin and the second has to detour around the cap bodies.
142
+
143
+ ### Power rail: let the pattern place it
144
+
145
+ **Do not** hand-place `+5V` / `3V3` / `VCC` on a fixed page side before apply — the
146
+ host power pin may be on the opposite side (e.g. LDO VIN on the right). The engine
147
+ **auto-places the rail symbol above the cap cluster on the host pin outward side**
148
+ and autoconnects `host.vcc → cap[0].a` plus `cap.a → rail`.
149
+
150
+ | Option | Purpose |
151
+ | --- | --- |
152
+ | `power_net` | Net label on the auto rail (default `VCC`; use `+5V`, `3V3`, …) |
153
+ | `power_symbol` | Library symbol name (default `VCC`) |
154
+
155
+ Only bind optional role `vcc` when reusing an **existing** rail symbol; otherwise
156
+ **omit `vcc`** and let apply create one. Use `power_mode=BOUND_ONLY` with a bound
157
+ `gnd` if the design shares one ground symbol; rail is still auto-placed unless
158
+ `vcc` is bound.
159
+
160
+ ### Pull resistor: multiple pins {#pull-resistor-multiple-pins}
161
+
162
+ Up to **8** `(signal_host, resistor)` pairs per apply — same `slotIndex` on both
163
+ roles, one shared `polarity`, optional bound `rail` (VCC/GND).
164
+
165
+ | Case | Do |
166
+ | --- | --- |
167
+ | N signal pins, same pull direction, same rail | **One apply**, slots `0..N−1`; bind `rail` when reusing an existing power symbol (`power_mode=BOUND_ONLY`) |
168
+ | Mixed pull-up + pull-down, or different rails | **Separate applies** |
169
+ | Bus with paired pull-ups (e.g. I2C) | Matching **bus pattern** (`i2c_bus`, …) — not N separate `pull_resistor` applies |
170
+
171
+ One apply merges a single rail connection; repeated applies with `AUTO_PLACE` may
172
+ duplicate GND/VCC and hit `AREA_OCCUPIED` near the first cluster.
173
+
174
+ **Layout:** each resistor offsets from its own `sig` pin (`pitch`), outward from
175
+ the host edge (left/right/top/bottom). Multiple pins on the same edge use
176
+ `lane_gap` to stagger outward; the engine also nudges if bodies still overlap.
177
+
178
+ After apply, hand-finish only `response.connections` entries with `routed=false`;
179
+ do not re-wire segments already routed.
180
+
181
+ ### Reset circuit
182
+
183
+ Topology: **VCC → pull-up → reset net → (switch ∥ filter cap) → GND**. Layout extends
184
+ outward from the bound MCU `host.rst` pin, then stacks **pull-up → optional switch →
185
+ cap → GND** on one trunk column (avoids side loops). The reset button body is placed
186
+ **further outward** than the trunk (`sw_outward_gap`, default 4 grids) and clamped
187
+ clear of the host bbox so 4-pin switch pins do not overlap the MCU or trunk wires.
188
+ Optional `reset_sw` binds a momentary button to GND via the cap bottom node.
189
+
190
+ Debug log: `%TEMP%\pattern_layout.log` tags `[PL:reset]` and `[PL:stub]`.
191
+
192
+ | Wiring | Mechanism |
193
+ | --- | --- |
194
+ | Host reset pin | Engine places **short stub + net alias** (`reset_net`, default `RESET`) |
195
+ | Pull-up, cap, switch, rails | **autoconnect** inside the pattern |
196
+ | Host ↔ pull-up | autoconnect on `RESET` after host stub |
197
+
198
+ Bind `reset_sw` only when the design includes a reset button; omit the role for
199
+ cap-only RC debounce. Use `power_net` / bound `vcc` for the MCU rail name (`3V3`,
200
+ `VCC_3V3`, …). Do not hand-place a second stub on `host.rst` after apply.
201
+
202
+ ### I2C / SPI bus: autoconnect + spacing
203
+
204
+ Both patterns use **pin-to-pin autoconnect** (no manual coordinate wires). Slaves
205
+ are spaced by **`device_gap` (default 10 grids)** measured between bus pin columns,
206
+ not just bbox edges — increase it if MCU and slave bodies look cramped.
207
+
208
+ **I2C** topology:
209
+
210
+ - Pull-ups sit **between master and slave** (not on master pin X — SCL/SDA often share the same column)
211
+ - `r_pullup_scl/sda` vertical, bottom pin above bus by `pull_offset`; `vcc_offset` lifts 3V3 further
212
+ - **Hub topology**: `master.scl/sda → r_pullup_*`, `slave.scl/sda → same r_pullup_*` (not slave→master)
213
+ - Slave Y auto-aligns when SDA/SCL pin order is inverted vs master (MPU6050 vs STM32)
214
+
215
+ **SPI** topology:
216
+
217
+ - `slave.sck/mosi/miso → master.sck/mosi/miso` (star at master)
218
+ - `master.cs → slave.cs` when `share_cs=false`; `slave.cs → master.cs` when shared
219
+
220
+ Debug log tags: `[PL:i2c]`, `[PL:spi]`, `[PL:connect]`, `[PL:route]` in
221
+ `%TEMP%\pattern_layout.log`. Do not hand-wire SDA/SCL/SCK after apply.
222
+
223
+ ## Plan-then-apply
224
+
225
+ ```
226
+ list-circuit-patterns
227
+ → get-page-occupancy # page_box + existing bboxes
228
+ → place parts (rough pos) # keep out of finished occupied_box areas
229
+ → apply-circuit-pattern(plan_only=true, roles, anchor)
230
+ if AREA_OCCUPIED: pick another anchor from conflicting_object_ids
231
+ → apply-circuit-pattern(plan_only=false, route_mode=NONE for spi_bus)
232
+ → book-keep response.occupied_box
233
+ → for each connections[] with routed=false OR cross-block leftovers:
234
+ run routing gate per net
235
+ short (≤300 ext, low crossings) → autoConnect
236
+ long / crowded → dual-end placePinStubWireAndNetAlias (same netName)
237
+ → layout other leftovers (power rails: autoConnect if adjacent, stub+alias if far)
238
+ → kernel-get-snapshot or netlist-get-active-page-net-list
239
+ ```
240
+
241
+ Rules:
242
+
243
+ - **`spi_bus`:** use `PATTERN_ROUTE_NONE` — pattern moves parts only; `connections[]`
244
+ lists SCK/MOSI/MISO/CS intents (`routed=false`, `note=pending AI routing`).
245
+ - `plan_only=true` never mutates the canvas. Use it to test anchors.
246
+ - `ignore_area_conflict=false` (default) returns `AREA_OCCUPIED` plus
247
+ `conflicting_object_ids` when the planned box hits existing parts.
248
+ - `PATTERN_STATUS_PARTIAL` means layout was kept but some wires failed. Do not
249
+ undo; finish those nets with PlaceWire / stub+alias.
250
+ - `PATTERN_STATUS_LAYOUT_FAILED` aborted the undo group — the page is unchanged.
251
+ - After a successful apply, treat `occupied_box` as reserved. Later placements
252
+ and later patterns must stay outside it (plus a small grid margin).
253
+
254
+ ## Binding roles
255
+
256
+ ```typescript
257
+ await client.patternLayout.applyCircuitPattern({
258
+ context: projectCtx,
259
+ pattern: CircuitPatternId.CIRCUIT_PATTERN_I2C_BUS,
260
+ anchor: { x: 400n, y: 300n },
261
+ orientation: PatternOrientation.PATTERN_ORIENT_LEFT_TO_RIGHT,
262
+ routeMode: PatternRouteMode.PATTERN_ROUTE_FULL,
263
+ powerMode: PatternPowerMode.PATTERN_POWER_AUTO_PLACE,
264
+ planOnly: false,
265
+ roles: [
266
+ { role: "master", objectId: mcuId, slotIndex: 0, pins: [
267
+ { terminal: "sda", pinNumber: "21" },
268
+ { terminal: "scl", pinNumber: "22" },
269
+ ]},
270
+ { role: "slave", objectId: eepromId, slotIndex: 0, pins: [
271
+ { terminal: "sda", pinName: "SDA" },
272
+ { terminal: "scl", pinName: "SCL" },
273
+ ]},
274
+ { role: "r_pullup_sda", objectId: rSdaId, slotIndex: 0, pins: [] },
275
+ { role: "r_pullup_scl", objectId: rSclId, slotIndex: 0, pins: [] },
276
+ ],
277
+ options: { sda_net: "SDA", scl_net: "SCL", lane_gap: "3" },
278
+ });
279
+ ```
280
+
281
+ Pin resolution order: `pin_number` (string, e.g. `"1"`, `"A5"`) → `pin_name` →
282
+ two-terminal default (a=`"1"`, b=`"2"`) → power/port symbols use `"0"`.
283
+
284
+ Type-C CC pins: bind with `pinNumber: "A5"` / `"B5"` or `pinName: "CC1"` /
285
+ `"CC2"` — never coerce alphanumeric numbers to `int`.
286
+
287
+ ## Occupancy bookkeeping
288
+
289
+ `GetPageOccupancy` returns every part/symbol bbox (and wires if
290
+ `include_wires=true`) in external coordinates. Use it to:
291
+
292
+ 1. Floorplan several patterns before placing anything.
293
+ 2. Choose an `anchor` whose `typical_size` box is empty.
294
+ 3. After apply, merge `occupied_box` into the agent's reserved-rect list.
295
+
296
+ Empty `area` (all zeros) means the whole page.
297
+
298
+ ## Capability skill IDs
299
+
300
+ | Task | Skill |
301
+ | --- | --- |
302
+ | Catalog | `pattern-layout-list-circuit-patterns` |
303
+ | Occupancy | `pattern-layout-get-page-occupancy` |
304
+ | Dry-run / apply | `pattern-layout-apply-circuit-pattern` |
@@ -0,0 +1,167 @@
1
+ # Editing a circuit (local / surgical changes)
2
+
3
+ Use this when the task is **modify part of an existing design**, not redraw the
4
+ whole page: change a value, rewire one pin, replace one part, rename a net label.
5
+
6
+ **Flow C** — see [SYSTEM-PROMPT.md](../SYSTEM-PROMPT.md) §流程 C.
7
+
8
+ ## When to use Flow C vs A vs B
9
+
10
+ | User intent | Flow |
11
+ | --- | --- |
12
+ | Draw a new circuit from scratch | **A** — patterns + placement |
13
+ | "What does VR201 do?" / read-only | **B** — [reading-a-circuit.md](./reading-a-circuit.md) |
14
+ | Change C201 to 1 µF / move R5 net / swap U3 | **C** — this doc |
15
+
16
+ **Never** clear the page or redraw the whole schematic for a local edit.
17
+
18
+ ## Fixed workflow (mandatory order)
19
+
20
+ ```mermaid
21
+ flowchart TD
22
+ confirm[1. Confirm projectId]
23
+ snap[2. GetSnapshot locate targets]
24
+ scope[3. Define blast radius]
25
+ diff[4. Print diff ask user]
26
+ exec[5. Execute minimal RPCs]
27
+ verify[6. Snapshot + netList verify]
28
+ confirm --> snap --> scope --> diff --> exec --> verify
29
+ ```
30
+
31
+ ### 1. Confirm project
32
+
33
+ ```typescript
34
+ const active = await client.project.getActiveProject({ context: editorCtx });
35
+ const projectId = active.project?.projectId;
36
+ console.log("projectId:", projectId); // print every run
37
+ const ctx = client.createProjectContext(projectId!);
38
+ ```
39
+
40
+ ### 2. Locate targets (snapshot + find)
41
+
42
+ | Need | Source |
43
+ | --- | --- |
44
+ | Topology / neighbors | `kernel.GetSnapshot` — see [reading-a-circuit.md](./reading-a-circuit.md) |
45
+ | Canvas object id for edit | `canvasObjectId` from snapshot **or** `FindObjectByProperty("Reference", ref)` → `objectIds[0]` |
46
+ | Property keys | [property-conventions.md](./property-conventions.md) — set designator with **`Part Reference`**, not `Reference` |
47
+
48
+ Run the read template for context before editing:
49
+
50
+ ```bash
51
+ REF=VR201 npx tsx scripts/read-circuit.ts
52
+ REF=C201 TRACE=1 npx tsx scripts/read-circuit.ts # optional series traversal
53
+ ```
54
+
55
+ The script prints **canvas 编辑 id** block for the designator → edit bridge.
56
+
57
+ ### 3. Blast radius (修改集)
58
+
59
+ Before any write, list **only** what will change:
60
+
61
+ | Edit type | Typical blast radius |
62
+ | --- | --- |
63
+ | Change Value / Footprint | That object only |
64
+ | Rename designator | That object + verify find by new ref |
65
+ | Rewire one pin | That pin stub/alias **or** one `autoConnect`; avoid touching unrelated nets |
66
+ | Replace part | Old object + its wires (often delete old → place new → reconnect listed pins) |
67
+ | Bulk delete | **Forbidden** without explicit user confirmation |
68
+
69
+ Do **not** use `deleteObjectsByIds` on whole-page occupancy without user OK.
70
+
71
+ ### 4. Print diff → user confirmation
72
+
73
+ **Mandatory** for Flow C (unless user explicitly said "just do it"):
74
+
75
+ ```
76
+ 计划修改:
77
+ - C201 Value: 0.1 µF → 1 µF (objectId=…)
78
+ - VR201 pin5 仍接 N0261718,不改
79
+ 爆炸半径: 1 个对象,0 条 net 重布
80
+ ```
81
+
82
+ Wait for user confirmation before `SetObjectProperty` / delete / place.
83
+
84
+ Environment escape hatch: `CONFIRM=1` in script (document in script header).
85
+
86
+ ### 5. Execute (minimal RPC set)
87
+
88
+ | Recipe | RPCs | Template script |
89
+ | --- | --- | --- |
90
+ | Edit property | `FindObjectByProperty` → `SetObjectProperty` | `scripts/edit-property-by-ref.ts` |
91
+ | Rewire pin | Snapshot pin + `autoConnectObjectsById` **or** `placePinStubWireAndNetAlias` | `scripts/rewire-pin-by-ref.ts` |
92
+ | Replace part | Snapshot pose → `deleteObjectsByIds` → `placeKicadSymbol` → reconnect | `scripts/replace-part-by-ref.ts` |
93
+
94
+ Common rules:
95
+
96
+ - `SetObjectProperty`: `op: 1` (OBJ_PROP_SET), `commitUndo: true`
97
+ - Power symbol wiring: pin **`0`** on VCC/GND side
98
+ - After delete: poll occupancy until ids gone ([circuit-pattern-layout.md](./circuit-pattern-layout.md) § delete settle)
99
+ - After place: poll until new ids appear before wiring
100
+
101
+ **Rewire reality check:** there is no dedicated "disconnect this pin only" RPC in
102
+ the skill toolbox. Practical options:
103
+
104
+ 1. **Join existing net:** `autoConnectObjectsById` to a part already on the target net.
105
+ 2. **Name a net at pin:** `placePinStubWireAndNetAlias` with target `netName` (routing gate in [placement-conventions.md](./placement-conventions.md)).
106
+ 3. **True cut:** identify wire segments via `listWireSegments().wires` and delete only if user accepts risk — prefer (1)/(2) for surgical edits.
107
+
108
+ ### 6. Dual-source verification
109
+
110
+ After writes:
111
+
112
+ 1. **`GetSnapshot`** — target ref pin→net, no unexpected floating pins.
113
+ 2. **`netList.getProjectNetList`** or `getActivePageNetList` — net membership for changed nets.
114
+ 3. Re-run consistency checks from Flow B (e.g. `output_voltage` vs rail name).
115
+ 4. Optional `zoomAll` when user wants visual check (Flow B default skips viewport changes).
116
+
117
+ ## Safety rules (Flow C)
118
+
119
+ - **No** whole-page clear without confirmation.
120
+ - **No** Flow A pattern re-apply on existing design unless user asks to regenerate a subnet.
121
+ - **No** dependency on `GetConnectivity`, `selection.*`, `transaction.*` — see [rpc-availability.md](./rpc-availability.md).
122
+ - Prefer **one object / one net** per script; split large edits into reviewable steps.
123
+ - Report `projectId` and every `objectId` mutated in stdout.
124
+
125
+ ## Recipe: edit property by REF
126
+
127
+ Env: `REF=C201` `PROP=Value` `NEW=1 µF` `CONFIRM=1`
128
+
129
+ See `template/scripts/edit-property-by-ref.ts`.
130
+
131
+ ## Recipe: rewire pin by REF
132
+
133
+ Env: `REF=R201` `PIN=1` `TARGET_REF=U301` `TARGET_PIN=14` `CONFIRM=1`
134
+
135
+ Connects source pin to an existing part on the target net via `autoConnectObjectsById`.
136
+ For net-label-only targets, use `TARGET_NET=+3.3V` + stub/alias path in script comments.
137
+
138
+ See `template/scripts/rewire-pin-by-ref.ts`.
139
+
140
+ ## Recipe: replace part by REF
141
+
142
+ Env: `OLD_REF=U3` `NEW_MPN=…` `CONFIRM=1`
143
+
144
+ 1. Snapshot old position/rotation/mirrored + pin→net list.
145
+ 2. User confirms diff (old deleted, new placed, N pins reconnected).
146
+ 3. Delete old → place via part-search → reconnect pin-by-pin.
147
+
148
+ See `template/scripts/replace-part-by-ref.ts`.
149
+
150
+ ## From understanding (B) to editing (C)
151
+
152
+ After Flow B / `read-circuit.ts`:
153
+
154
+ 1. Copy `canvasObjectId` or `FindObject` id from output.
155
+ 2. Use in Flow C script — **do not** guess ids from designator strings alone for wiring.
156
+ 3. If `metadata.properties` was empty during read, still OK to edit Value/Footprint; do not invent MPN.
157
+
158
+ ## Engine improvements (optional, not blocking)
159
+
160
+ Documented in [rpc-availability.md](./rpc-availability.md) § Skill-side workarounds:
161
+
162
+ - True `GetConnectivity` graph → replace client-side series BFS.
163
+ - Per-pin associated wire ids → safer disconnect.
164
+ - `FindNet` explicit unimplemented instead of silent false.
165
+ - Selection API for "edit this region".
166
+
167
+ Skill ships with snapshot BFS + find-by-property until engine catches up.