copperhead 0.3.0

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 (81) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +87 -0
  3. package/dist/agent/filetools.js +118 -0
  4. package/dist/agent/filetools.js.map +1 -0
  5. package/dist/agent/ledger.js +43 -0
  6. package/dist/agent/ledger.js.map +1 -0
  7. package/dist/agent/loop.js +279 -0
  8. package/dist/agent/loop.js.map +1 -0
  9. package/dist/agent/prompts.js +47 -0
  10. package/dist/agent/prompts.js.map +1 -0
  11. package/dist/agent/providers/anthropic.js +78 -0
  12. package/dist/agent/providers/anthropic.js.map +1 -0
  13. package/dist/agent/providers/openai.js +74 -0
  14. package/dist/agent/providers/openai.js.map +1 -0
  15. package/dist/agent/tools.js +439 -0
  16. package/dist/agent/tools.js.map +1 -0
  17. package/dist/agent/transcript.js +60 -0
  18. package/dist/agent/transcript.js.map +1 -0
  19. package/dist/agent/types.js +2 -0
  20. package/dist/agent/types.js.map +1 -0
  21. package/dist/cli.js +185 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands/check.js +64 -0
  24. package/dist/commands/check.js.map +1 -0
  25. package/dist/commands/create.js +91 -0
  26. package/dist/commands/create.js.map +1 -0
  27. package/dist/commands/sync.js +144 -0
  28. package/dist/commands/sync.js.map +1 -0
  29. package/dist/config.js +64 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/kicad/cli.js +91 -0
  32. package/dist/kicad/cli.js.map +1 -0
  33. package/dist/kicad/report.js +48 -0
  34. package/dist/kicad/report.js.map +1 -0
  35. package/dist/kicad/sexp.js +291 -0
  36. package/dist/kicad/sexp.js.map +1 -0
  37. package/dist/memory/constraints.js +43 -0
  38. package/dist/memory/constraints.js.map +1 -0
  39. package/dist/memory/drift.js +81 -0
  40. package/dist/memory/drift.js.map +1 -0
  41. package/dist/memory/scaffold.js +215 -0
  42. package/dist/memory/scaffold.js.map +1 -0
  43. package/dist/openspec/cli.js +31 -0
  44. package/dist/openspec/cli.js.map +1 -0
  45. package/dist/util/env.js +68 -0
  46. package/dist/util/env.js.map +1 -0
  47. package/dist/util/git.js +53 -0
  48. package/dist/util/git.js.map +1 -0
  49. package/dist/util/paths.js +25 -0
  50. package/dist/util/paths.js.map +1 -0
  51. package/dist/util/redact.js +21 -0
  52. package/dist/util/redact.js.map +1 -0
  53. package/dist/util/retry.js +27 -0
  54. package/dist/util/retry.js.map +1 -0
  55. package/package.json +73 -0
  56. package/src/agent/filetools.ts +136 -0
  57. package/src/agent/ledger.ts +71 -0
  58. package/src/agent/loop.ts +320 -0
  59. package/src/agent/prompts.ts +58 -0
  60. package/src/agent/providers/anthropic.ts +81 -0
  61. package/src/agent/providers/openai.ts +80 -0
  62. package/src/agent/tools.ts +481 -0
  63. package/src/agent/transcript.ts +78 -0
  64. package/src/agent/types.ts +32 -0
  65. package/src/cli.ts +188 -0
  66. package/src/commands/check.ts +85 -0
  67. package/src/commands/create.ts +125 -0
  68. package/src/commands/sync.ts +182 -0
  69. package/src/config.ts +77 -0
  70. package/src/kicad/cli.ts +106 -0
  71. package/src/kicad/report.ts +81 -0
  72. package/src/kicad/sexp.ts +327 -0
  73. package/src/memory/constraints.ts +73 -0
  74. package/src/memory/drift.ts +97 -0
  75. package/src/memory/scaffold.ts +241 -0
  76. package/src/openspec/cli.ts +43 -0
  77. package/src/util/env.ts +65 -0
  78. package/src/util/git.ts +63 -0
  79. package/src/util/paths.ts +25 -0
  80. package/src/util/redact.ts +20 -0
  81. package/src/util/retry.ts +33 -0
@@ -0,0 +1,91 @@
1
+ import { execa } from 'execa';
2
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { normalizeReport } from './report.js';
6
+ export class KicadCliMissingError extends Error {
7
+ constructor() {
8
+ super('kicad-cli not found on PATH. Install KiCad ≥ 8 (https://www.kicad.org/download/) and ensure kicad-cli is available.');
9
+ this.name = 'KicadCliMissingError';
10
+ }
11
+ }
12
+ export async function kicadCliVersion() {
13
+ try {
14
+ const { stdout } = await execa('kicad-cli', ['version']);
15
+ return stdout.trim();
16
+ }
17
+ catch (err) {
18
+ if (err.code === 'ENOENT')
19
+ throw new KicadCliMissingError();
20
+ throw err;
21
+ }
22
+ }
23
+ async function runCheck(kind, filePath, extraArgs = []) {
24
+ const dir = await mkdtemp(path.join(tmpdir(), 'copperhead-'));
25
+ const out = path.join(dir, `${kind}.json`);
26
+ const sub = kind === 'erc' ? ['sch', 'erc'] : ['pcb', 'drc'];
27
+ try {
28
+ await execa('kicad-cli', [...sub, '--format', 'json', '--exit-code-violations', '--output', out, ...extraArgs, filePath], { reject: false }).then((res) => {
29
+ if (res.failed && res.code === 'ENOENT') {
30
+ throw new KicadCliMissingError();
31
+ }
32
+ });
33
+ const raw = JSON.parse(await readFile(out, 'utf8'));
34
+ return normalizeReport(raw, kind);
35
+ }
36
+ finally {
37
+ await rm(dir, { recursive: true, force: true });
38
+ }
39
+ }
40
+ export function runErc(schPath) {
41
+ return runCheck('erc', schPath);
42
+ }
43
+ export function runDrc(pcbPath) {
44
+ return runCheck('drc', pcbPath);
45
+ }
46
+ /**
47
+ * Export the fabrication package (SPEC §2.5 outputs): gerbers + drill, DXF and
48
+ * STEP outline, SVG renders. Each artifact fails independently with a reason so
49
+ * a missing STEP exporter never sinks the rest of the package.
50
+ */
51
+ export async function exportFab(pcbPath, schPath, outDir) {
52
+ const result = { produced: [], failed: [] };
53
+ const jobs = [
54
+ { artifact: 'gerbers', args: ['pcb', 'export', 'gerbers', '--output', path.join(outDir, 'gerbers'), pcbPath] },
55
+ { artifact: 'drill', args: ['pcb', 'export', 'drill', '--output', path.join(outDir, 'gerbers'), pcbPath] },
56
+ { artifact: 'outline.dxf', args: ['pcb', 'export', 'dxf', '--output', path.join(outDir, 'outline.dxf'), '--layers', 'Edge.Cuts', pcbPath] },
57
+ { artifact: 'board.step', args: ['pcb', 'export', 'step', '--output', path.join(outDir, 'board.step'), pcbPath] },
58
+ { artifact: 'board.svg', args: ['pcb', 'export', 'svg', '--output', path.join(outDir, 'board.svg'), '--layers', 'F.Cu,B.Cu,Edge.Cuts', pcbPath] },
59
+ ];
60
+ if (schPath) {
61
+ jobs.push({ artifact: 'schematic.svg', args: ['sch', 'export', 'svg', '--output', path.join(outDir, 'renders'), schPath] });
62
+ }
63
+ for (const job of jobs) {
64
+ try {
65
+ await execa('kicad-cli', job.args);
66
+ result.produced.push(job.artifact);
67
+ }
68
+ catch (err) {
69
+ if (err.code === 'ENOENT')
70
+ throw new KicadCliMissingError();
71
+ result.failed.push({ artifact: job.artifact, reason: String(err.stderr ?? err.message).slice(0, 200) });
72
+ }
73
+ }
74
+ return result;
75
+ }
76
+ /** Export an SVG render of a schematic or board; returns the output directory. */
77
+ export async function exportSvg(kind, filePath, outDir) {
78
+ const args = kind === 'sch'
79
+ ? ['sch', 'export', 'svg', '--output', outDir, filePath]
80
+ : ['pcb', 'export', 'svg', '--output', path.join(outDir, 'board.svg'), '--layers', 'F.Cu,B.Cu,Edge.Cuts', filePath];
81
+ try {
82
+ await execa('kicad-cli', args);
83
+ }
84
+ catch (err) {
85
+ if (err.code === 'ENOENT')
86
+ throw new KicadCliMissingError();
87
+ throw err;
88
+ }
89
+ return outDir;
90
+ }
91
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/kicad/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ function normViolation(v, sheet) {
2
+ return {
3
+ severity: v.severity ?? 'error',
4
+ type: v.type ?? 'unknown',
5
+ description: v.description ?? '',
6
+ ...(sheet !== undefined ? { sheet } : {}),
7
+ items: (v.items ?? []).map((i) => ({
8
+ description: i.description ?? '',
9
+ ...(i.pos?.x !== undefined ? { x: i.pos.x } : {}),
10
+ ...(i.pos?.y !== undefined ? { y: i.pos.y } : {}),
11
+ })),
12
+ };
13
+ }
14
+ /**
15
+ * Normalize kicad-cli ERC and DRC JSON reports into one shape. ERC nests
16
+ * violations per sheet; DRC has top-level `violations` plus `unconnected_items`
17
+ * and `schematic_parity`. Tolerant of missing fields across KiCad versions.
18
+ */
19
+ export function normalizeReport(raw, source) {
20
+ const r = raw;
21
+ const violations = [];
22
+ for (const sheet of r.sheets ?? []) {
23
+ for (const v of sheet.violations ?? [])
24
+ violations.push(normViolation(v, sheet.path));
25
+ }
26
+ for (const v of r.violations ?? [])
27
+ violations.push(normViolation(v));
28
+ for (const v of r.unconnected_items ?? [])
29
+ violations.push(normViolation(v));
30
+ for (const v of r.schematic_parity ?? [])
31
+ violations.push(normViolation(v));
32
+ return { ok: violations.length === 0, source, violations };
33
+ }
34
+ export function formatViolations(report) {
35
+ if (report.ok)
36
+ return `${report.source.toUpperCase()}: clean`;
37
+ const lines = [`${report.source.toUpperCase()}: ${report.violations.length} violation(s)`];
38
+ for (const v of report.violations) {
39
+ const where = v.sheet ? ` [sheet ${v.sheet}]` : '';
40
+ lines.push(` ${v.severity} ${v.type}${where}: ${v.description}`);
41
+ for (const i of v.items) {
42
+ const pos = i.x !== undefined ? ` @ (${i.x}, ${i.y})` : '';
43
+ lines.push(` - ${i.description}${pos}`);
44
+ }
45
+ }
46
+ return lines.join('\n');
47
+ }
48
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/kicad/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,291 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export function parseSexp(text) {
4
+ const tokens = [];
5
+ let i = 0;
6
+ while (i < text.length) {
7
+ const c = text[i];
8
+ if (c === '(' || c === ')') {
9
+ tokens.push(c);
10
+ i++;
11
+ }
12
+ else if (c === '"') {
13
+ let j = i + 1;
14
+ let s = '';
15
+ while (j < text.length && text[j] !== '"') {
16
+ if (text[j] === '\\' && j + 1 < text.length) {
17
+ s += text[j + 1];
18
+ j += 2;
19
+ }
20
+ else {
21
+ s += text[j];
22
+ j++;
23
+ }
24
+ }
25
+ tokens.push(JSON.stringify(s));
26
+ i = j + 1;
27
+ }
28
+ else if (/\s/.test(c)) {
29
+ i++;
30
+ }
31
+ else {
32
+ let j = i;
33
+ while (j < text.length && !/[\s()"]/.test(text[j]))
34
+ j++;
35
+ tokens.push(text.slice(i, j));
36
+ i = j;
37
+ }
38
+ }
39
+ let pos = 0;
40
+ function parseNode() {
41
+ const tok = tokens[pos++];
42
+ if (tok === '(') {
43
+ const list = [];
44
+ while (pos < tokens.length && tokens[pos] !== ')')
45
+ list.push(parseNode());
46
+ pos++; // consume ')'
47
+ return list;
48
+ }
49
+ return tok.startsWith('"') ? JSON.parse(tok) : tok;
50
+ }
51
+ const roots = [];
52
+ while (pos < tokens.length)
53
+ roots.push(parseNode());
54
+ return roots;
55
+ }
56
+ export const isList = (n) => Array.isArray(n);
57
+ const tag = (n) => (isList(n) && typeof n[0] === 'string' ? n[0] : null);
58
+ export function children(node, name) {
59
+ if (!isList(node))
60
+ return [];
61
+ return node.filter((c) => tag(c) === name);
62
+ }
63
+ export function child(node, name) {
64
+ return children(node, name)[0];
65
+ }
66
+ function atomAt(node, idx) {
67
+ const v = node?.[idx];
68
+ return typeof v === 'string' ? v : undefined;
69
+ }
70
+ function property(sym, key) {
71
+ for (const p of children(sym, 'property')) {
72
+ if (atomAt(p, 1) === key)
73
+ return atomAt(p, 2);
74
+ }
75
+ return undefined;
76
+ }
77
+ async function loadSheets(rootSch) {
78
+ const seen = new Set();
79
+ const out = [];
80
+ async function load(file, sheetName) {
81
+ const abs = path.resolve(file);
82
+ if (seen.has(abs))
83
+ return;
84
+ seen.add(abs);
85
+ const text = await readFile(abs, 'utf8');
86
+ const root = parseSexp(text)[0];
87
+ if (root === undefined || !isList(root)) {
88
+ throw new Error(`not a KiCad s-expression file: ${file}`);
89
+ }
90
+ out.push({ filePath: abs, sheetName, root });
91
+ for (const sheet of children(root, 'sheet')) {
92
+ const sub = property(sheet, 'Sheetfile') ?? property(sheet, 'Sheet file');
93
+ const name = property(sheet, 'Sheetname') ?? property(sheet, 'Sheet name') ?? 'sheet';
94
+ if (sub)
95
+ await load(path.resolve(path.dirname(abs), sub), name);
96
+ }
97
+ }
98
+ await load(rootSch, '/');
99
+ return out;
100
+ }
101
+ /** Pin definitions per lib symbol name, in symbol coordinates. */
102
+ function libPinDefs(root) {
103
+ const map = new Map();
104
+ const libs = child(root, 'lib_symbols');
105
+ if (!libs)
106
+ return map;
107
+ for (const sym of children(libs, 'symbol')) {
108
+ const name = atomAt(sym, 1);
109
+ if (!name)
110
+ continue;
111
+ const pins = [];
112
+ const walk = (n) => {
113
+ if (!isList(n))
114
+ return;
115
+ if (tag(n) === 'pin') {
116
+ const at = child(n, 'at');
117
+ const num = atomAt(child(n, 'number'), 1);
118
+ const pinName = atomAt(child(n, 'name'), 1);
119
+ if (at && num !== undefined) {
120
+ pins.push({
121
+ number: num,
122
+ name: pinName ?? '~',
123
+ x: parseFloat(atomAt(at, 1) ?? '0'),
124
+ y: parseFloat(atomAt(at, 2) ?? '0'),
125
+ });
126
+ }
127
+ }
128
+ for (const c of n)
129
+ walk(c);
130
+ };
131
+ walk(sym);
132
+ map.set(name, pins);
133
+ }
134
+ return map;
135
+ }
136
+ /** Symbol-space → schematic-space transform (schematic Y grows downward). */
137
+ export function pinAbsolute(symAt, mirror, pin) {
138
+ let px = pin.x;
139
+ let py = pin.y;
140
+ if (mirror === 'y')
141
+ px = -px;
142
+ if (mirror === 'x')
143
+ py = -py;
144
+ const theta = (symAt.rot * Math.PI) / 180;
145
+ const rx = px * Math.cos(theta) - py * Math.sin(theta);
146
+ const ry = px * Math.sin(theta) + py * Math.cos(theta);
147
+ return { x: round(symAt.x + rx), y: round(symAt.y - ry) };
148
+ }
149
+ const round = (n) => Math.round(n * 10000) / 10000;
150
+ const key = (x, y) => `${round(x)},${round(y)}`;
151
+ class UnionFind {
152
+ parent = new Map();
153
+ find(k) {
154
+ let p = this.parent.get(k);
155
+ if (p === undefined) {
156
+ this.parent.set(k, k);
157
+ return k;
158
+ }
159
+ if (p !== k) {
160
+ p = this.find(p);
161
+ this.parent.set(k, p);
162
+ }
163
+ return p;
164
+ }
165
+ union(a, b) {
166
+ this.parent.set(this.find(a), this.find(b));
167
+ }
168
+ }
169
+ function symbolsOf(sheet) {
170
+ const out = [];
171
+ for (const s of children(sheet.root, 'symbol')) {
172
+ const libId = atomAt(child(s, 'lib_id'), 1);
173
+ if (!libId)
174
+ continue; // lib_symbols entries have no lib_id child
175
+ const at = child(s, 'at');
176
+ const mirrorAtom = atomAt(child(s, 'mirror'), 1);
177
+ out.push({
178
+ node: s,
179
+ mirror: mirrorAtom === 'x' || mirrorAtom === 'y' ? mirrorAtom : null,
180
+ sym: {
181
+ ref: property(s, 'Reference') ?? '?',
182
+ value: property(s, 'Value') ?? '',
183
+ footprint: property(s, 'Footprint') ?? '',
184
+ libId,
185
+ sheet: sheet.sheetName,
186
+ at: {
187
+ x: parseFloat(atomAt(at, 1) ?? '0'),
188
+ y: parseFloat(atomAt(at, 2) ?? '0'),
189
+ rot: parseFloat(atomAt(at, 3) ?? '0'),
190
+ },
191
+ uuid: atomAt(child(s, 'uuid'), 1) ?? '',
192
+ },
193
+ });
194
+ }
195
+ return out;
196
+ }
197
+ const isPowerSymbol = (libId) => libId.startsWith('power:');
198
+ /** One row per real component (power symbols excluded), across all sheets. */
199
+ export async function listSymbols(rootSch) {
200
+ const sheets = await loadSheets(rootSch);
201
+ const out = [];
202
+ for (const sheet of sheets) {
203
+ for (const { sym } of symbolsOf(sheet)) {
204
+ if (!isPowerSymbol(sym.libId))
205
+ out.push(sym);
206
+ }
207
+ }
208
+ return out.sort((a, b) => a.ref.localeCompare(b.ref, undefined, { numeric: true }));
209
+ }
210
+ /** All net names visible via labels and power symbols, across all sheets. */
211
+ export async function listNets(rootSch) {
212
+ const sheets = await loadSheets(rootSch);
213
+ const names = new Set();
214
+ for (const sheet of sheets) {
215
+ for (const kind of ['label', 'global_label', 'hierarchical_label']) {
216
+ for (const l of children(sheet.root, kind)) {
217
+ const name = atomAt(l, 1);
218
+ if (name)
219
+ names.add(name);
220
+ }
221
+ }
222
+ for (const { sym } of symbolsOf(sheet)) {
223
+ if (isPowerSymbol(sym.libId))
224
+ names.add(sym.value);
225
+ }
226
+ }
227
+ return [...names].sort();
228
+ }
229
+ /**
230
+ * Geometric connectivity per sheet: pins, labels, and wire endpoints that share
231
+ * coordinates (or are joined by wires) form a group; a group's net name comes
232
+ * from its labels or power symbols. Good enough for docs scaffolding and drift
233
+ * checks; not a full netlister.
234
+ */
235
+ export async function pinNets(rootSch) {
236
+ const sheets = await loadSheets(rootSch);
237
+ const out = [];
238
+ for (const sheet of sheets) {
239
+ const pinDefs = libPinDefs(sheet.root);
240
+ const uf = new UnionFind();
241
+ const netNameAt = new Map();
242
+ for (const w of children(sheet.root, 'wire')) {
243
+ const pts = children(child(w, 'pts') ?? [], 'xy').map((xy) => ({
244
+ x: parseFloat(atomAt(xy, 1) ?? '0'),
245
+ y: parseFloat(atomAt(xy, 2) ?? '0'),
246
+ }));
247
+ for (let i = 1; i < pts.length; i++) {
248
+ uf.union(key(pts[0].x, pts[0].y), key(pts[i].x, pts[i].y));
249
+ }
250
+ }
251
+ for (const kind of ['label', 'global_label', 'hierarchical_label']) {
252
+ for (const l of children(sheet.root, kind)) {
253
+ const name = atomAt(l, 1);
254
+ const at = child(l, 'at');
255
+ if (!name || !at)
256
+ continue;
257
+ const k = key(parseFloat(atomAt(at, 1) ?? '0'), parseFloat(atomAt(at, 2) ?? '0'));
258
+ uf.find(k);
259
+ netNameAt.set(k, name);
260
+ }
261
+ }
262
+ const symPins = [];
263
+ for (const { sym, mirror } of symbolsOf(sheet)) {
264
+ const defs = pinDefs.get(sym.libId) ?? [];
265
+ for (const pin of defs) {
266
+ const abs = pinAbsolute(sym.at, mirror, pin);
267
+ const k = key(abs.x, abs.y);
268
+ uf.find(k);
269
+ if (isPowerSymbol(sym.libId)) {
270
+ netNameAt.set(k, sym.value);
271
+ }
272
+ else {
273
+ symPins.push({ sym, pin, k });
274
+ }
275
+ }
276
+ }
277
+ const groupNet = new Map();
278
+ for (const [k, name] of netNameAt)
279
+ groupNet.set(uf.find(k), name);
280
+ for (const { sym, pin, k } of symPins) {
281
+ out.push({
282
+ ref: sym.ref,
283
+ pinNumber: pin.number,
284
+ pinName: pin.name,
285
+ net: groupNet.get(uf.find(k)) ?? null,
286
+ });
287
+ }
288
+ }
289
+ return out;
290
+ }
291
+ //# sourceMappingURL=sexp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sexp.js","sourceRoot":"","sources":["../../src/kicad/sexp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ export function constraintsPath(repoRoot) {
5
+ return path.join(repoRoot, '.copperhead', 'constraints.json');
6
+ }
7
+ export async function loadConstraints(repoRoot) {
8
+ const p = constraintsPath(repoRoot);
9
+ if (!existsSync(p))
10
+ return {};
11
+ return JSON.parse(await readFile(p, 'utf8'));
12
+ }
13
+ export async function saveConstraint(repoRoot, key, constraint) {
14
+ const registry = await loadConstraints(repoRoot);
15
+ registry[key] = constraint;
16
+ const p = constraintsPath(repoRoot);
17
+ await mkdir(path.dirname(p), { recursive: true });
18
+ await writeFile(p, JSON.stringify(registry, null, 2) + '\n', 'utf8');
19
+ return registry;
20
+ }
21
+ /**
22
+ * Mechanical validation where possible (SPEC §2.6): forbidden pins against the
23
+ * pinout, numeric budget keys surfaced for the doc-level checks. Geometry
24
+ * checks are out of scope for Phase 1.
25
+ */
26
+ export function checkForbiddenPins(registry, pinNets) {
27
+ const violations = [];
28
+ for (const [key, c] of Object.entries(registry)) {
29
+ if (!c.forbidden?.length)
30
+ continue;
31
+ for (const pn of pinNets) {
32
+ if (pn.net && c.forbidden.includes(pn.pinName)) {
33
+ violations.push({
34
+ key,
35
+ description: `${pn.ref} pin ${pn.pinName} is connected to net ${pn.net} but is forbidden by ${key}`,
36
+ source: c.source,
37
+ });
38
+ }
39
+ }
40
+ }
41
+ return violations;
42
+ }
43
+ //# sourceMappingURL=constraints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../src/memory/constraints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAkB7B,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,kBAAkB,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAuB,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,GAAW,EACX,UAAsB;IAEtB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;IACjD,QAAQ,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;IAC3B,MAAM,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACrE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAA4B,EAC5B,OAA+D;IAE/D,MAAM,UAAU,GAA0B,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM;YAAE,SAAS;QACnC,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YACzB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/C,UAAU,CAAC,IAAI,CAAC;oBACd,GAAG;oBACH,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,OAAO,wBAAwB,EAAE,CAAC,GAAG,wBAAwB,GAAG,EAAE;oBACnG,MAAM,EAAE,CAAC,CAAC,MAAM;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,81 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { listSymbols, pinNets } from '../kicad/sexp.js';
5
+ export function parseMarkdownTables(md) {
6
+ const rows = [];
7
+ for (const line of md.split('\n')) {
8
+ const t = line.trim();
9
+ if (!t.startsWith('|'))
10
+ continue;
11
+ const cells = t
12
+ .split('|')
13
+ .slice(1, -1)
14
+ .map((c) => c.trim());
15
+ if (cells.every((c) => /^:?-+:?$/.test(c)))
16
+ continue; // separator row
17
+ rows.push({ cells });
18
+ }
19
+ return rows;
20
+ }
21
+ const isHeader = (row) => row.cells.some((c) => /^(refdes|pin)$/i.test(c));
22
+ export async function checkDrift(repoRoot, docsDir, schematic) {
23
+ const mismatches = [];
24
+ const schPath = path.join(repoRoot, schematic);
25
+ const symbols = await listSymbols(schPath);
26
+ const byRef = new Map(symbols.map((s) => [s.ref, s]));
27
+ const bomPath = path.join(repoRoot, docsDir, 'BOM.md');
28
+ if (existsSync(bomPath)) {
29
+ const rows = parseMarkdownTables(await readFile(bomPath, 'utf8')).filter((r) => !isHeader(r));
30
+ const seen = new Set();
31
+ for (const row of rows) {
32
+ const [ref, value, footprint] = row.cells;
33
+ if (!ref)
34
+ continue;
35
+ seen.add(ref);
36
+ const sym = byRef.get(ref);
37
+ if (!sym) {
38
+ mismatches.push({ doc: 'BOM.md', claim: `${ref} exists`, actual: `${ref} not in schematic` });
39
+ continue;
40
+ }
41
+ if (value !== undefined && value !== sym.value) {
42
+ mismatches.push({ doc: 'BOM.md', claim: `${ref} value ${value}`, actual: `${ref} value ${sym.value}` });
43
+ }
44
+ if (footprint !== undefined && footprint !== '' && footprint !== sym.footprint) {
45
+ mismatches.push({
46
+ doc: 'BOM.md',
47
+ claim: `${ref} footprint ${footprint}`,
48
+ actual: `${ref} footprint ${sym.footprint}`,
49
+ });
50
+ }
51
+ }
52
+ for (const sym of symbols) {
53
+ if (!seen.has(sym.ref)) {
54
+ mismatches.push({ doc: 'BOM.md', claim: `${sym.ref} absent`, actual: `${sym.ref} (${sym.value}) in schematic` });
55
+ }
56
+ }
57
+ }
58
+ const pinoutPath = path.join(repoRoot, docsDir, 'PINOUT.md');
59
+ if (existsSync(pinoutPath)) {
60
+ const nets = await pinNets(schPath);
61
+ const netOf = new Map(nets.map((p) => [`${p.ref}:${p.pinNumber}`, p.net]));
62
+ const rows = parseMarkdownTables(await readFile(pinoutPath, 'utf8')).filter((r) => !isHeader(r));
63
+ for (const row of rows) {
64
+ const [ref, pinNumber, , net] = row.cells;
65
+ if (!ref || !pinNumber)
66
+ continue;
67
+ const k = `${ref}:${pinNumber}`;
68
+ if (!netOf.has(k)) {
69
+ mismatches.push({ doc: 'PINOUT.md', claim: `${k} exists`, actual: `${k} not in schematic` });
70
+ continue;
71
+ }
72
+ const actual = netOf.get(k) ?? 'NC';
73
+ const claimed = net === undefined || net === '' ? 'NC' : net;
74
+ if (claimed !== actual) {
75
+ mismatches.push({ doc: 'PINOUT.md', claim: `${k} net ${claimed}`, actual: `${k} net ${actual}` });
76
+ }
77
+ }
78
+ }
79
+ return mismatches;
80
+ }
81
+ //# sourceMappingURL=drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/memory/drift.ts"],"names":[],"mappings":"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"}