copperhead 0.5.0 → 0.7.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.
- package/README.md +34 -1
- package/dist/agent/loop.js +130 -15
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/prompts.js +2 -1
- package/dist/agent/prompts.js.map +1 -1
- package/dist/agent/providers/claude-code.js +466 -0
- package/dist/agent/providers/claude-code.js.map +1 -0
- package/dist/agent/providers/openai.js +30 -10
- package/dist/agent/providers/openai.js.map +1 -1
- package/dist/agent/recovery.js +148 -0
- package/dist/agent/recovery.js.map +1 -0
- package/dist/agent/render.js +17 -2
- package/dist/agent/render.js.map +1 -1
- package/dist/agent/response-cache.js +81 -0
- package/dist/agent/response-cache.js.map +1 -0
- package/dist/agent/tools.js +61 -4
- package/dist/agent/tools.js.map +1 -1
- package/dist/agent/transcript.js.map +1 -1
- package/dist/cli.js +47 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/create.js +486 -35
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/export.js +90 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/config.js +33 -6
- package/dist/config.js.map +1 -1
- package/dist/kicad/bom-export.js +240 -0
- package/dist/kicad/bom-export.js.map +1 -0
- package/dist/kicad/bootstrap.js +166 -0
- package/dist/kicad/bootstrap.js.map +1 -0
- package/dist/kicad/fab.js +94 -0
- package/dist/kicad/fab.js.map +1 -0
- package/dist/kicad/spice.js +306 -0
- package/dist/kicad/spice.js.map +1 -0
- package/dist/kicad/symlib.js +228 -0
- package/dist/kicad/symlib.js.map +1 -0
- package/dist/memory/bom-table.js +232 -0
- package/dist/memory/bom-table.js.map +1 -0
- package/dist/memory/drift.js +33 -27
- package/dist/memory/drift.js.map +1 -1
- package/dist/util/git.js +37 -1
- package/dist/util/git.js.map +1 -1
- package/dist/util/preflight.js +37 -0
- package/dist/util/preflight.js.map +1 -1
- package/dist/util/retry.js +23 -0
- package/dist/util/retry.js.map +1 -1
- package/dist/util/tmp.js +119 -0
- package/dist/util/tmp.js.map +1 -0
- package/package.json +6 -2
- package/src/agent/loop.ts +148 -15
- package/src/agent/prompts.ts +2 -1
- package/src/agent/providers/claude-code.ts +550 -0
- package/src/agent/providers/openai.ts +33 -16
- package/src/agent/recovery.ts +162 -0
- package/src/agent/render.ts +28 -1
- package/src/agent/response-cache.ts +80 -0
- package/src/agent/tools.ts +62 -4
- package/src/agent/transcript.ts +1 -0
- package/src/agent/types.ts +18 -0
- package/src/cli.ts +52 -2
- package/src/commands/create.ts +543 -38
- package/src/commands/export.ts +117 -0
- package/src/config.ts +54 -6
- package/src/kicad/bom-export.ts +321 -0
- package/src/kicad/bootstrap.ts +181 -0
- package/src/kicad/fab.ts +121 -0
- package/src/kicad/spice.ts +399 -0
- package/src/kicad/symlib.ts +248 -0
- package/src/memory/bom-table.ts +249 -0
- package/src/memory/drift.ts +42 -32
- package/src/util/git.ts +37 -1
- package/src/util/preflight.ts +44 -0
- package/src/util/retry.ts +29 -0
- package/src/util/tmp.ts +113 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-check the schematic's `lib_symbols` against the KiCad symbol libraries
|
|
3
|
+
* installed on the machine (I9).
|
|
4
|
+
*
|
|
5
|
+
* The create pipeline currently has the model hand-author every `lib_symbols`
|
|
6
|
+
* entry — pins, names, electrical types, geometry — under a `lib_id` that
|
|
7
|
+
* *claims* to be a canonical KiCad part (`Device:R`, `Connector:USB_C_...`).
|
|
8
|
+
* ERC only checks the net graph as drawn, so an entry whose pins silently
|
|
9
|
+
* diverge from the real library part (wrong pin count, a missing shield/CC pin,
|
|
10
|
+
* swapped numbers) passes every gate while being wrong. This module reads the
|
|
11
|
+
* real `(symbol …)` out of the installed `.kicad_sym` and reports divergences so
|
|
12
|
+
* the model — or a reviewer — can reconcile them.
|
|
13
|
+
*
|
|
14
|
+
* It is deliberately a *checker*, not an auto-replacer: KiCad renames symbols
|
|
15
|
+
* across versions (e.g. `USB_C_Receptacle_USB2.0` became `…_14P`/`…_16P` in
|
|
16
|
+
* KiCad 10), so blindly splicing by lib_id would fail on exactly the parts that
|
|
17
|
+
* matter most. When the exact name is absent, we surface close candidates
|
|
18
|
+
* instead of guessing.
|
|
19
|
+
*/
|
|
20
|
+
import { readFile, access } from 'node:fs/promises';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { parseSexp, children, child, isList } from './sexp.js';
|
|
23
|
+
const tag = (n) => (isList(n) && typeof n[0] === 'string' ? n[0] : null);
|
|
24
|
+
const atomAt = (node, idx) => {
|
|
25
|
+
const v = node?.[idx];
|
|
26
|
+
return typeof v === 'string' ? v : undefined;
|
|
27
|
+
};
|
|
28
|
+
// KiCad has two spellings for an unnamed pin: the legacy `~` sentinel and, in
|
|
29
|
+
// newer library format, an empty string. They are semantically identical, so
|
|
30
|
+
// normalize before comparing or the check floods with phantom `~` vs "" diffs.
|
|
31
|
+
const normPinName = (n) => (n === '~' ? '' : n);
|
|
32
|
+
/**
|
|
33
|
+
* Candidate directories holding KiCad's stock `.kicad_sym` libraries, most
|
|
34
|
+
* specific first. Env overrides win (KiCad exports these), then the standard
|
|
35
|
+
* install locations for Linux/macOS/Windows. Only existing dirs are returned.
|
|
36
|
+
*/
|
|
37
|
+
export async function symbolSearchDirs(env = process.env) {
|
|
38
|
+
const fromEnv = [
|
|
39
|
+
env.KICAD_SYMBOL_DIR,
|
|
40
|
+
env.KICAD10_SYMBOL_DIR,
|
|
41
|
+
env.KICAD9_SYMBOL_DIR,
|
|
42
|
+
env.KICAD8_SYMBOL_DIR,
|
|
43
|
+
].filter((v) => !!v);
|
|
44
|
+
const defaults = [
|
|
45
|
+
'/usr/share/kicad/symbols',
|
|
46
|
+
'/usr/local/share/kicad/symbols',
|
|
47
|
+
'/Applications/KiCad/KiCad.app/Contents/SharedSupport/symbols',
|
|
48
|
+
'C:/Program Files/KiCad/share/kicad/symbols',
|
|
49
|
+
];
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const dir of [...fromEnv, ...defaults]) {
|
|
52
|
+
try {
|
|
53
|
+
await access(dir);
|
|
54
|
+
if (!out.includes(dir))
|
|
55
|
+
out.push(dir);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// not present on this machine; skip
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/** Path to `<lib>.kicad_sym` in the first search dir that has it, or null. */
|
|
64
|
+
export async function findLibraryFile(lib, dirs) {
|
|
65
|
+
for (const dir of dirs) {
|
|
66
|
+
const p = path.join(dir, `${lib}.kicad_sym`);
|
|
67
|
+
try {
|
|
68
|
+
await access(p);
|
|
69
|
+
return p;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// try next dir
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
/** Collect pins (number, name, electrical type) from a `(symbol …)` node,
|
|
78
|
+
* including its nested unit sub-symbols. Same walk `libPinDefs` uses, plus the
|
|
79
|
+
* electrical-type atom that pin-position parsing does not need. */
|
|
80
|
+
export function pinsOfSymbolNode(sym) {
|
|
81
|
+
const pins = [];
|
|
82
|
+
const walk = (n) => {
|
|
83
|
+
if (!isList(n))
|
|
84
|
+
return;
|
|
85
|
+
if (tag(n) === 'pin') {
|
|
86
|
+
const num = atomAt(child(n, 'number'), 1);
|
|
87
|
+
if (num !== undefined) {
|
|
88
|
+
pins.push({
|
|
89
|
+
number: num,
|
|
90
|
+
name: atomAt(child(n, 'name'), 1) ?? '~',
|
|
91
|
+
type: typeof n[1] === 'string' ? n[1] : '?',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const c of n)
|
|
96
|
+
walk(c);
|
|
97
|
+
};
|
|
98
|
+
walk(sym);
|
|
99
|
+
return pins;
|
|
100
|
+
}
|
|
101
|
+
/** The top-level `(symbol "name" …)` entries of a parsed `.kicad_sym` root. */
|
|
102
|
+
function librarySymbols(root) {
|
|
103
|
+
const map = new Map();
|
|
104
|
+
for (const sym of children(root, 'symbol')) {
|
|
105
|
+
const name = atomAt(sym, 1);
|
|
106
|
+
if (name)
|
|
107
|
+
map.set(name, sym);
|
|
108
|
+
}
|
|
109
|
+
return map;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolve a `lib_id` (e.g. `Device:R`) to the real library part's pins.
|
|
113
|
+
* `extends` derived symbols inherit their base's pins, so we follow one such
|
|
114
|
+
* link (loop-guarded). Returns the pins, or — when the exact symbol is absent —
|
|
115
|
+
* the closest-named candidates so a caller can suggest the real name.
|
|
116
|
+
*/
|
|
117
|
+
export async function resolveLibrarySymbol(libId, dirs) {
|
|
118
|
+
const [lib, name] = libId.includes(':') ? [libId.slice(0, libId.indexOf(':')), libId.slice(libId.indexOf(':') + 1)] : ['', libId];
|
|
119
|
+
const file = await findLibraryFile(lib, dirs);
|
|
120
|
+
if (!file)
|
|
121
|
+
return { status: 'no-library' };
|
|
122
|
+
const root = parseSexp(await readFile(file, 'utf8'))[0];
|
|
123
|
+
if (root === undefined || !isList(root))
|
|
124
|
+
return { status: 'no-library' };
|
|
125
|
+
const symbols = librarySymbols(root);
|
|
126
|
+
let current = name;
|
|
127
|
+
const seen = new Set();
|
|
128
|
+
while (current && !seen.has(current)) {
|
|
129
|
+
seen.add(current);
|
|
130
|
+
const sym = symbols.get(current);
|
|
131
|
+
if (!sym)
|
|
132
|
+
break;
|
|
133
|
+
const pins = pinsOfSymbolNode(sym);
|
|
134
|
+
if (pins.length)
|
|
135
|
+
return { status: 'ok', pins };
|
|
136
|
+
// no pins of its own → follow an `extends` base if present
|
|
137
|
+
const base = atomAt(child(sym, 'extends'), 1);
|
|
138
|
+
if (!base)
|
|
139
|
+
return { status: 'ok', pins }; // genuinely pinless (e.g. a graphic)
|
|
140
|
+
current = base;
|
|
141
|
+
}
|
|
142
|
+
// exact name not found: offer near matches (case-insensitive substring both ways)
|
|
143
|
+
const q = name.toLowerCase();
|
|
144
|
+
const candidates = [...symbols.keys()]
|
|
145
|
+
.filter((k) => {
|
|
146
|
+
const lk = k.toLowerCase();
|
|
147
|
+
return lk.includes(q) || q.includes(lk);
|
|
148
|
+
})
|
|
149
|
+
.slice(0, 8);
|
|
150
|
+
return { status: 'no-symbol', candidates };
|
|
151
|
+
}
|
|
152
|
+
/** A schematic lib_symbols entry: its lib_id and the pins as authored. */
|
|
153
|
+
function schematicLibSymbols(root) {
|
|
154
|
+
const libs = child(root, 'lib_symbols');
|
|
155
|
+
if (!libs)
|
|
156
|
+
return [];
|
|
157
|
+
return children(libs, 'symbol').map((sym) => ({
|
|
158
|
+
libId: atomAt(sym, 1) ?? '',
|
|
159
|
+
pins: pinsOfSymbolNode(sym),
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Compare every lib_symbols entry in a schematic against the installed library.
|
|
164
|
+
* Returns one finding per divergence; an empty array means every resolvable
|
|
165
|
+
* symbol matched. A part whose library is not installed is reported once (so
|
|
166
|
+
* the model knows the check could not run for it) but never treated as a
|
|
167
|
+
* mismatch — absence of the library is not evidence of wrong pins.
|
|
168
|
+
*/
|
|
169
|
+
export async function verifySchematicSymbols(schPath, env = process.env) {
|
|
170
|
+
const dirs = await symbolSearchDirs(env);
|
|
171
|
+
const root = parseSexp(await readFile(schPath, 'utf8'))[0];
|
|
172
|
+
const findings = [];
|
|
173
|
+
if (root === undefined || !isList(root))
|
|
174
|
+
return { findings, checked: 0, skipped: 0 };
|
|
175
|
+
let checked = 0;
|
|
176
|
+
let skipped = 0;
|
|
177
|
+
for (const entry of schematicLibSymbols(root)) {
|
|
178
|
+
if (!entry.libId)
|
|
179
|
+
continue;
|
|
180
|
+
const resolved = await resolveLibrarySymbol(entry.libId, dirs);
|
|
181
|
+
if (resolved.status === 'no-library') {
|
|
182
|
+
skipped++;
|
|
183
|
+
findings.push({
|
|
184
|
+
libId: entry.libId,
|
|
185
|
+
kind: 'no-library',
|
|
186
|
+
detail: `library for "${entry.libId}" is not installed on this machine; cannot verify its pins`,
|
|
187
|
+
});
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (resolved.status === 'no-symbol') {
|
|
191
|
+
findings.push({
|
|
192
|
+
libId: entry.libId,
|
|
193
|
+
kind: 'no-symbol',
|
|
194
|
+
detail: resolved.candidates.length
|
|
195
|
+
? `"${entry.libId}" does not exist in the installed library — closest real symbols: ${resolved.candidates.join(', ')}. Use one of these lib_ids (KiCad renames symbols across versions).`
|
|
196
|
+
: `"${entry.libId}" does not exist in the installed library and no close match was found; confirm the lib_id.`,
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
checked++;
|
|
201
|
+
const real = resolved.pins;
|
|
202
|
+
const authored = entry.pins;
|
|
203
|
+
const realByNum = new Map(real.map((p) => [p.number, p]));
|
|
204
|
+
const authByNum = new Map(authored.map((p) => [p.number, p]));
|
|
205
|
+
if (real.length !== authored.length) {
|
|
206
|
+
findings.push({
|
|
207
|
+
libId: entry.libId,
|
|
208
|
+
kind: 'pin-count',
|
|
209
|
+
detail: `pin count differs: schematic has ${authored.length} pin(s) [${[...authByNum.keys()].join(',')}], the real ${entry.libId} has ${real.length} [${[...realByNum.keys()].join(',')}]`,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// per-pin name/type divergence on shared pin numbers
|
|
213
|
+
for (const [num, rp] of realByNum) {
|
|
214
|
+
const ap = authByNum.get(num);
|
|
215
|
+
if (!ap)
|
|
216
|
+
continue; // count mismatch already reported the gap
|
|
217
|
+
if (normPinName(ap.name) !== normPinName(rp.name) || ap.type !== rp.type) {
|
|
218
|
+
findings.push({
|
|
219
|
+
libId: entry.libId,
|
|
220
|
+
kind: 'pin-mismatch',
|
|
221
|
+
detail: `pin ${num}: schematic has (name "${ap.name}", ${ap.type}), real part has (name "${rp.name}", ${rp.type})`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { findings, checked, skipped };
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=symlib.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlib.js","sourceRoot":"","sources":["../../src/kicad/symlib.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared markdown-table parsing for BOM.md and PINOUT.md (design D9's
|
|
3
|
+
* fixed-column contract). Originally lived inline in drift.ts; pulled out so
|
|
4
|
+
* the supplier BOM export work (add-supplier-bom-export) can parse BOM.md
|
|
5
|
+
* once, the same way, instead of duplicating this.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parses every markdown pipe-table row out of a document, across however
|
|
9
|
+
* many tables the file contains, skipping separator rows (e.g. `|---|---|`).
|
|
10
|
+
* Malformed lines (stray `|` outside a real table) just become a row with
|
|
11
|
+
* whatever cells they split into — this function never throws.
|
|
12
|
+
*/
|
|
13
|
+
export function parseMarkdownTables(md) {
|
|
14
|
+
const rows = [];
|
|
15
|
+
for (const line of md.split('\n')) {
|
|
16
|
+
const t = line.trim();
|
|
17
|
+
if (!t.startsWith('|'))
|
|
18
|
+
continue;
|
|
19
|
+
const cells = t
|
|
20
|
+
.split('|')
|
|
21
|
+
.slice(1, -1)
|
|
22
|
+
.map((c) => c.trim());
|
|
23
|
+
if (cells.every((c) => /^:?-+:?$/.test(c)))
|
|
24
|
+
continue; // separator row
|
|
25
|
+
rows.push({ cells });
|
|
26
|
+
}
|
|
27
|
+
return rows;
|
|
28
|
+
}
|
|
29
|
+
/** True for a table's header row. BOM.md and PINOUT.md both lead with a
|
|
30
|
+
* Refdes or Pin column, so one check covers both doc types. */
|
|
31
|
+
export const isHeader = (row) => row.cells.some((c) => /^(refdes|pin)$/i.test(c));
|
|
32
|
+
/**
|
|
33
|
+
* Data rows of the CANONICAL table(s) only — those introduced by a Refdes/Pin
|
|
34
|
+
* header row (`isHeader`). BOM.md and PINOUT.md legitimately carry supporting
|
|
35
|
+
* tables (a quiescent-current roll-up, a net-meaning legend); their rows are
|
|
36
|
+
* NOT parts/pins and must never be compared against the schematic. The flat
|
|
37
|
+
* `parseMarkdownTables(md).filter(!isHeader)` does exactly that — it merges
|
|
38
|
+
* every table's rows — so a second table's first cell gets read as a refdes and
|
|
39
|
+
* flagged "not in schematic", which pushes the agent to degrade good docs into
|
|
40
|
+
* bullet lists just to appease the drift gate.
|
|
41
|
+
*
|
|
42
|
+
* This groups lines into tables (a run of pipe-rows, ended by any non-pipe
|
|
43
|
+
* line), keeps only the groups whose first row is a Refdes/Pin header, and
|
|
44
|
+
* returns those groups' data rows (header dropped). A table with no recognized
|
|
45
|
+
* header — including a bare data-only block — is ignored, preserving the
|
|
46
|
+
* fixed-column contract (design D9) while letting docs hold extra tables.
|
|
47
|
+
*/
|
|
48
|
+
export function parseCanonicalRows(md) {
|
|
49
|
+
return parseCanonicalTables(md).flatMap((t) => t.rows);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Like parseCanonicalRows, but keeps each kept table's header row so a caller
|
|
53
|
+
* can resolve columns by *name* instead of a fixed position. PINOUT.md's
|
|
54
|
+
* column count is not fixed in practice: the scaffold writes
|
|
55
|
+
* `Refdes | Pin | Name | Net | Notes`, but a hand- or LLM-authored table may
|
|
56
|
+
* legitimately drop the optional Name/Notes columns and write
|
|
57
|
+
* `Refdes | Pin | Net`. A fixed positional net index then reads the wrong cell
|
|
58
|
+
* and reports every pin as net "NC" against a doc that is in fact correct —
|
|
59
|
+
* a false drift the agent cannot diagnose (the doc plainly shows the net), so
|
|
60
|
+
* it loops on finish forever. Resolving by header name fixes that.
|
|
61
|
+
*/
|
|
62
|
+
export function parseCanonicalTables(md) {
|
|
63
|
+
const groups = [];
|
|
64
|
+
let current = null;
|
|
65
|
+
for (const line of md.split('\n')) {
|
|
66
|
+
const t = line.trim();
|
|
67
|
+
if (!t.startsWith('|')) {
|
|
68
|
+
current = null; // a blank or prose line terminates the current table
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const cells = t
|
|
72
|
+
.split('|')
|
|
73
|
+
.slice(1, -1)
|
|
74
|
+
.map((c) => c.trim());
|
|
75
|
+
if (cells.every((c) => /^:?-+:?$/.test(c)))
|
|
76
|
+
continue; // separator row: stays within the table
|
|
77
|
+
if (!current) {
|
|
78
|
+
current = [];
|
|
79
|
+
groups.push(current);
|
|
80
|
+
}
|
|
81
|
+
current.push({ cells });
|
|
82
|
+
}
|
|
83
|
+
const tables = [];
|
|
84
|
+
for (const g of groups) {
|
|
85
|
+
if (g.length && isHeader(g[0]))
|
|
86
|
+
tables.push({ header: g[0], rows: g.slice(1) });
|
|
87
|
+
}
|
|
88
|
+
return tables;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* PINOUT.md pin assignments, resolved by column *name* and tolerant of the
|
|
92
|
+
* optional Name/Notes columns (see parseCanonicalTables). Only the canonical
|
|
93
|
+
* table that carries both a Pin and a Net header is read; a supporting table
|
|
94
|
+
* (e.g. a `Net | Role` legend) is ignored. Net names are compared bare, so the
|
|
95
|
+
* common `` `VBUS` `` markdown-backtick styling is stripped — the schematic
|
|
96
|
+
* stores plain net names, and a backtick-only difference is not real drift.
|
|
97
|
+
*/
|
|
98
|
+
export function parsePinoutRows(md) {
|
|
99
|
+
const out = [];
|
|
100
|
+
const strip = (s) => (s ?? '').replace(/`/g, '').trim();
|
|
101
|
+
for (const { header, rows } of parseCanonicalTables(md)) {
|
|
102
|
+
const col = (re) => header.cells.findIndex((c) => re.test(c));
|
|
103
|
+
const refI = col(/^refdes$/i);
|
|
104
|
+
const pinI = col(/^pin$/i);
|
|
105
|
+
const netI = col(/^net$/i);
|
|
106
|
+
if (pinI < 0 || netI < 0)
|
|
107
|
+
continue; // not the pin-assignment table
|
|
108
|
+
for (const row of rows) {
|
|
109
|
+
out.push({
|
|
110
|
+
ref: refI >= 0 ? strip(row.cells[refI]) : '',
|
|
111
|
+
pin: strip(row.cells[pinI]),
|
|
112
|
+
net: strip(row.cells[netI]),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Fold the semantically-identical encodings that the model and KiCad render
|
|
120
|
+
* differently, so a value that differs only in *encoding* is not flagged as
|
|
121
|
+
* drift (#I11). A design that reached ERC-clean once churned for turns on
|
|
122
|
+
* `Ihold≥3A` vs `Ihold>=3A` and `0.1"` vs `0.1in` — byte differences with zero
|
|
123
|
+
* electrical meaning. Folded here: ≥/>=, ≤/<=, Ω/ohm(s), µ/μ/u, smart quotes,
|
|
124
|
+
* and the inch mark (`"` / `″` / a trailing `in`/`inch` after a number). NFKC
|
|
125
|
+
* first collapses width/compatibility variants; the explicit rules cover the
|
|
126
|
+
* cases NFKC leaves alone (≥, smart quotes, the ohm/inch words).
|
|
127
|
+
*/
|
|
128
|
+
export function foldEncodings(s) {
|
|
129
|
+
if (!s)
|
|
130
|
+
return '';
|
|
131
|
+
return s
|
|
132
|
+
.normalize('NFKC')
|
|
133
|
+
.replace(/≥/g, '>=')
|
|
134
|
+
.replace(/≤/g, '<=')
|
|
135
|
+
.replace(/[ΩΩ]/g, 'ohm') // ohm sign U+2126 / greek capital omega U+03A9
|
|
136
|
+
.replace(/\bohms\b/gi, 'ohm')
|
|
137
|
+
.replace(/[µμ]/g, 'u') // micro sign U+00B5 / greek small mu U+03BC
|
|
138
|
+
.replace(/[“”″]/g, '"') // smart double quotes and double-prime → "
|
|
139
|
+
.replace(/[‘’′]/g, "'") // smart single quotes and prime → '
|
|
140
|
+
.replace(/(?<=[\d.])\s*(?:inches|inch|in)\b/gi, '"') // 0.1in / 0.1 inch → 0.1"
|
|
141
|
+
.trim();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Value-cell equality key: `foldEncodings` plus case- and whitespace-folding.
|
|
145
|
+
* Used to compare BOM.md value/footprint cells against schematic symbol values
|
|
146
|
+
* so an encoding/case/spacing-only difference is not reported as drift.
|
|
147
|
+
*/
|
|
148
|
+
export function normalizeValue(s) {
|
|
149
|
+
return foldEncodings(s).replace(/\s+/g, '').toLowerCase();
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Footprint equality key: like `normalizeValue` but WITHOUT case-folding (F6).
|
|
153
|
+
* A footprint is a KiCad library reference (`Resistor_SMD:R_0603_1608Metric`)
|
|
154
|
+
* whose casing is significant — `R_0603` and `r_0603` are not the same library
|
|
155
|
+
* id — so lowercasing it would hide a real footprint difference. Encoding and
|
|
156
|
+
* spacing are still folded (a stray space or unicode variant is not real drift).
|
|
157
|
+
*/
|
|
158
|
+
export function normalizeFootprint(s) {
|
|
159
|
+
return foldEncodings(s).replace(/\s+/g, '');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Which of the canonical pin-assignment columns PINOUT.md actually provides.
|
|
163
|
+
* `checkDrift` uses this to emit ONE explicit "no Net column" message when the
|
|
164
|
+
* doc omits the column entirely, instead of silently checking nothing (a
|
|
165
|
+
* correct doc then looks unverified) or — the old positional bug (#I12) —
|
|
166
|
+
* reading the wrong cell and reporting every pin as a false `NC` mismatch.
|
|
167
|
+
* `hasTable` is false when the doc has no Refdes/Pin-headed table at all.
|
|
168
|
+
*/
|
|
169
|
+
export function pinoutColumnReport(md) {
|
|
170
|
+
let hasTable = false;
|
|
171
|
+
let pin = false;
|
|
172
|
+
let net = false;
|
|
173
|
+
let refdes = false;
|
|
174
|
+
for (const { header } of parseCanonicalTables(md)) {
|
|
175
|
+
hasTable = true;
|
|
176
|
+
const has = (re) => header.cells.some((c) => re.test(c));
|
|
177
|
+
if (has(/^pin$/i))
|
|
178
|
+
pin = true;
|
|
179
|
+
if (has(/^net$/i))
|
|
180
|
+
net = true;
|
|
181
|
+
if (has(/^refdes$/i))
|
|
182
|
+
refdes = true;
|
|
183
|
+
}
|
|
184
|
+
return { hasTable, pin, net, refdes };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Parses BOM.md's data rows into typed rows. Columns are resolved by header
|
|
188
|
+
* *name* (Refdes/Value/Footprint/MPN), falling back to the canonical position
|
|
189
|
+
* when a header is absent — the same header-name discipline `parsePinoutRows`
|
|
190
|
+
* uses (#I12), so a doc that reorders or drops an optional column is still read
|
|
191
|
+
* correctly instead of silently shifting every cell. Rows without a refdes are
|
|
192
|
+
* dropped rather than thrown on: a hand-edited doc with a ragged or partial
|
|
193
|
+
* table shouldn't crash `check` or `export bom`, it should just be skipped
|
|
194
|
+
* (drift/export callers report the gaps that matter against the schematic).
|
|
195
|
+
*/
|
|
196
|
+
export function parseBomTable(md) {
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const { header, rows } of parseCanonicalTables(md)) {
|
|
199
|
+
// Resolve by header name; -1 means "not found", so fall back to the
|
|
200
|
+
// canonical index for that column (Refdes 0, Value 1, Footprint 2, MPN 3).
|
|
201
|
+
const col = (re, fallback) => {
|
|
202
|
+
const i = header.cells.findIndex((c) => re.test(c));
|
|
203
|
+
return i >= 0 ? i : fallback;
|
|
204
|
+
};
|
|
205
|
+
const refI = col(/^refdes$/i, 0);
|
|
206
|
+
const valI = col(/^value$/i, 1);
|
|
207
|
+
const fpI = col(/^footprint$/i, 2);
|
|
208
|
+
const mpnI = col(/^mpn$/i, 3);
|
|
209
|
+
for (const row of rows) {
|
|
210
|
+
const refdes = row.cells[refI];
|
|
211
|
+
if (!refdes)
|
|
212
|
+
continue;
|
|
213
|
+
const value = row.cells[valI];
|
|
214
|
+
const footprint = row.cells[fpI];
|
|
215
|
+
const mpn = row.cells[mpnI];
|
|
216
|
+
const flags = [];
|
|
217
|
+
if (mpn === 'UNVERIFIED')
|
|
218
|
+
flags.push('UNVERIFIED');
|
|
219
|
+
else if (!mpn)
|
|
220
|
+
flags.push('MISSING_MPN');
|
|
221
|
+
out.push({
|
|
222
|
+
refdes,
|
|
223
|
+
value: value || undefined,
|
|
224
|
+
footprint: footprint || undefined,
|
|
225
|
+
mpn: mpn || undefined,
|
|
226
|
+
flags,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=bom-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bom-table.js","sourceRoot":"","sources":["../../src/memory/bom-table.ts"],"names":[],"mappings":"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"}
|
package/dist/memory/drift.js
CHANGED
|
@@ -2,23 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { listSymbols, pinNets } from '../kicad/sexp.js';
|
|
5
|
-
|
|
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));
|
|
5
|
+
import { parseCanonicalRows, parseBomTable, parsePinoutRows, pinoutColumnReport, normalizeValue, normalizeFootprint, } from './bom-table.js';
|
|
22
6
|
/**
|
|
23
7
|
* The zero-symbol carve-out in checkDrift is right for the create pipeline,
|
|
24
8
|
* but it would let `check` silently pass an established repo whose schematic
|
|
@@ -34,8 +18,7 @@ export async function emptySchematicWarning(repoRoot, docsDir, schematic) {
|
|
|
34
18
|
const bomPath = path.join(repoRoot, docsDir, 'BOM.md');
|
|
35
19
|
if (!existsSync(bomPath))
|
|
36
20
|
return null;
|
|
37
|
-
const refs =
|
|
38
|
-
.filter((r) => !isHeader(r))
|
|
21
|
+
const refs = parseCanonicalRows(await readFile(bomPath, 'utf8'))
|
|
39
22
|
.map((r) => r.cells[0])
|
|
40
23
|
.filter(Boolean);
|
|
41
24
|
if (!refs.length)
|
|
@@ -57,10 +40,11 @@ export async function checkDrift(repoRoot, docsDir, schematic) {
|
|
|
57
40
|
const byRef = new Map(symbols.map((s) => [s.ref, s]));
|
|
58
41
|
const bomPath = path.join(repoRoot, docsDir, 'BOM.md');
|
|
59
42
|
if (existsSync(bomPath)) {
|
|
60
|
-
|
|
43
|
+
// Resolve BOM columns by header name via the shared parseBomTable (F5), so
|
|
44
|
+
// the drift reader and `export bom` never disagree on a reordered table.
|
|
45
|
+
const rows = parseBomTable(await readFile(bomPath, 'utf8'));
|
|
61
46
|
const seen = new Set();
|
|
62
|
-
for (const
|
|
63
|
-
const [ref, value, footprint] = row.cells;
|
|
47
|
+
for (const { refdes: ref, value, footprint } of rows) {
|
|
64
48
|
if (!ref)
|
|
65
49
|
continue;
|
|
66
50
|
seen.add(ref);
|
|
@@ -69,10 +53,16 @@ export async function checkDrift(repoRoot, docsDir, schematic) {
|
|
|
69
53
|
mismatches.push({ doc: 'BOM.md', claim: `${ref} exists`, actual: `${ref} not in schematic` });
|
|
70
54
|
continue;
|
|
71
55
|
}
|
|
72
|
-
|
|
56
|
+
// Compare on the semantic value, not the byte-exact string: `Ihold≥3A` and
|
|
57
|
+
// `Ihold>=3A` are the same value written two ways (#I11). Raw `!==` flagged
|
|
58
|
+
// them as drift and whack-a-moled the agent across finish attempts.
|
|
59
|
+
if (value !== undefined && normalizeValue(value) !== normalizeValue(sym.value)) {
|
|
73
60
|
mismatches.push({ doc: 'BOM.md', claim: `${ref} value ${value}`, actual: `${ref} value ${sym.value}` });
|
|
74
61
|
}
|
|
75
|
-
|
|
62
|
+
// Footprint compare folds encoding/spacing but keeps case (F6): a footprint
|
|
63
|
+
// library id is case-sensitive, so lowercasing would hide a real mismatch.
|
|
64
|
+
if (footprint !== undefined &&
|
|
65
|
+
normalizeFootprint(footprint) !== normalizeFootprint(sym.footprint)) {
|
|
76
66
|
mismatches.push({
|
|
77
67
|
doc: 'BOM.md',
|
|
78
68
|
claim: `${ref} footprint ${footprint}`,
|
|
@@ -88,11 +78,27 @@ export async function checkDrift(repoRoot, docsDir, schematic) {
|
|
|
88
78
|
}
|
|
89
79
|
const pinoutPath = path.join(repoRoot, docsDir, 'PINOUT.md');
|
|
90
80
|
if (existsSync(pinoutPath)) {
|
|
81
|
+
const pinoutMd = await readFile(pinoutPath, 'utf8');
|
|
91
82
|
const nets = await pinNets(schPath);
|
|
92
83
|
const netOf = new Map(nets.map((p) => [`${p.ref}:${p.pinNumber}`, p.net]));
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
// If the doc has a Refdes/Pin table but no Net column, say so once and
|
|
85
|
+
// explicitly, rather than silently checking nothing (a correct doc then
|
|
86
|
+
// reads as unverified) — the counterpart to the old positional bug that
|
|
87
|
+
// reported every pin as a false NC (#I12). This tells the model what to fix
|
|
88
|
+
// (add the column) instead of leaving it guessing why nets aren't verified.
|
|
89
|
+
const cols = pinoutColumnReport(pinoutMd);
|
|
90
|
+
if (cols.hasTable && !cols.net) {
|
|
91
|
+
mismatches.push({
|
|
92
|
+
doc: 'PINOUT.md',
|
|
93
|
+
claim: 'the pin table has a Net column (expected header: Refdes | Pin | Net)',
|
|
94
|
+
actual: 'no Net column in the pin table, so pin-to-net assignments cannot be checked; add a Net column',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Resolve columns by header name, not position: the PINOUT table may be
|
|
98
|
+
// `Refdes | Pin | Net` or the scaffold's `Refdes | Pin | Name | Net | Notes`.
|
|
99
|
+
// A fixed net index read every pin as "NC" on the 3-column form (#I12).
|
|
100
|
+
const rows = parsePinoutRows(pinoutMd);
|
|
101
|
+
for (const { ref, pin: pinNumber, net } of rows) {
|
|
96
102
|
if (!ref || !pinNumber)
|
|
97
103
|
continue;
|
|
98
104
|
const k = `${ref}:${pinNumber}`;
|
package/dist/memory/drift.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/memory/drift.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,OAAO,EAAwB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/memory/drift.ts"],"names":[],"mappings":"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"}
|