@huaqiu/dsh-tool-kicad-schematic-gen 0.6.7 → 0.6.9
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 +7 -1
- package/lib/builder.d.ts +22 -1
- package/lib/builder.js +32 -5
- package/lib/client.js +34 -59
- package/lib/{flat--E2zWV_r.js → grid-check-BgWU7a8j.js} +109 -4
- package/lib/index.js +59 -7
- package/package.json +3 -3
- package/skills/kicad-schematic-design/SKILL.md +15 -4
- package/skills/kicad-schematic-design/assets/runtime.mjs +3 -0
- package/skills/kicad-schematic-design/references/drawing-patterns.md +1 -1
- package/skills/kicad-schematic-design/references/flat-reuse.md +2 -0
- package/skills/kicad-schematic-design/references/hierarchy.md +1 -0
- package/skills/kicad-schematic-design/references/layout-delivery.md +35 -0
- package/skills/kicad-schematic-design/references/module-layout.md +1 -1
- package/skills/kicad-schematic-design/references/power-symbols.md +1 -0
- package/skills/kicad-schematic-design/references/programming.md +7 -3
- package/skills/kicad-schematic-design/scripts/run-design.mjs +26 -6
- package/skills/kicad-schematic-design/templates/project.kicad_pro +1 -0
- package/src/builder.ts +2 -0
- package/src/client/index.ts +9 -25
- package/src/client/turn-overview.ts +37 -0
- package/src/prompt.ts +3 -0
- package/src/schematic/builder.ts +29 -3
- package/src/schematic/design-plan.ts +41 -0
- package/src/schematic/flat-input.ts +6 -4
- package/src/schematic/grid-check.ts +31 -0
- package/src/schematic/grid.ts +11 -0
- package/src/schematic/project.ts +1 -1
- package/src/schematic/tools.ts +27 -4
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
One installable plugin containing twelve tools and the kicad-schematic-design skill. Internal modules preserve the former component-library, module-catalog and schematic-tool contracts. Component search is provided by the KiCad built-in plugin @huaqiu/dsh-tool-part-search and its search_hqsch_parts tool.
|
|
6
6
|
|
|
7
|
+
布局交付:简单项目默认首版、一次集中修复、验证并交付;首版达标可直接交付。额外修复必须有具体阻塞缺陷,真实错误不受轮数限制。不为装饰偏好或清零保守警告重复重建。流程和验收条件见 [布局修复与交付停止条件](skills/kicad-schematic-design/references/layout-delivery.md),启动器摘要返回 nextAction 提示,不自动宣称方案或电气验证通过。
|
|
8
|
+
|
|
7
9
|
Configuration is grouped under components, modules.search, modules.download and schematic. Templates ship under skills/kicad-schematic-design; the public scripting API is available at ./builder. Module downloads resolve the current HQ Edge host at execution time, so a missing host does not prevent other tools or the skill from loading.
|
|
8
10
|
|
|
9
11
|
Imports remain workspace scoped and use DSH sandbox policy. Model inspection reports pin/pad summaries; paired import checks unique pin/pad number sets, but does not verify electrical suitability or package dimensions. Placement and routing are explicitly authored in design scripts. Datasheet extraction preserves missing/truncated status.
|
|
@@ -18,6 +20,10 @@ Power symbols: declare actual shared nets with the builder's `requiredPowerSymbo
|
|
|
18
20
|
|
|
19
21
|
Standalone common-only symbols with pins (e.g. SW-PTB-2_0_1) are automatically normalized in project copies to unit 1 without changing pins or graphics. Model summaries and import results report builderCompatibility.normalization=common_only_to_unit_1. Inherited, empty, malformed or alternate-only definitions are not normalized. Unsupported imports report part, symbol, sections and source URL.
|
|
20
22
|
|
|
21
|
-
最终概览卡片:正式预览传入 `delivery: true` 和包含全部本地库的 `projectDir
|
|
23
|
+
最终概览卡片:正式预览传入 `delivery: true` 和包含全部本地库的 `projectDir`,待本轮正常完成后,在最终回复后的轮次尾部显示页面切换、缩放、ZIP 下载;工具执行期间不展示正式卡片。正式概览必须是本轮最后一个工具操作,之后调用其他工具或普通预览时须重新正式交付;失败、取消、中断的轮次不显示正式卡片。卡片从所属轮次的工具记录恢复,切换会话无需全局缓存。复用已有 PNG,不重新生成电路,无重新生成/详情按钮。需 KiCad Harness 的 webServer、conversation.chat.turnTail 和 useChat(ChatSnapshot 的 locations/nodes)支持;不可用时仍返回本地预览路径。快照保存在插件 `.overview-cache/`,重启后可读取;移除该目录后历史卡片下载将失效。ZIP 为工程目录内相关设计文件与库的快照,不收集脚本、预览或外部库。
|
|
22
24
|
|
|
23
25
|
Module analysis queries use `analyze_kicad_module({schematicPath, view, reference?, pin?, net?, offset?, kicadCliPath?})`, with explicit `view`. Downloads already return a summary: query only needed components, grouped nets or grouped BOM. CLI/project options are resolved internally; optional absolute `kicadCliPath` is for recovery using the existing `get_eda_host_info` result. On `CLI_START_FAILED`, retry analysis with that path and `view:summary`, without downloading again. Successful paths are reused; default CLI command names preserve automatic discovery. Probe errors return `error:{code,executable}` and retain causes; capability failures are distinct from startup failures. Matching same-stem projects and downloaded `.source.kicad_pro` files are discovered. Detail output contains `status`, `items`, and optional `nextOffset`/`warnings`; missing `nextOffset` means completion. Component custom fields require a reference filter. Partial results are reused for detail queries; after fixing configuration use `view:summary` to retry. Full local artifacts are preserved. This replaces the previous analysis query arguments and response contract; existing callers must migrate. See [module analysis](skills/kicad-schematic-design/references/module-analysis.md).
|
|
26
|
+
|
|
27
|
+
方案先行:先保存硬件方案,用 `scripts/run-design.mjs --read-plan <方案.md>` 读回,再按文档编写脚本。运行时必须传 `--plan <path> --plan-sha256 <sha256>`;构建库直接调用也要求 `designPlan:{path,sha256}`。平铺 create 与层次生成工具同样检查方案。缺失、空文档或版本变化时阻止生成;只读模块分析和预览不受影响。构建报告保存方案全文、方案 hash 和脚本 hash。此检查保证文件和版本前置,不代替方案电气完整性及文图一致性审查。
|
|
28
|
+
|
|
29
|
+
电气栅格:默认 50 mil(1.27 mm)。构建器吸附器件放置、导线坐标、标签和模块平移量;`gridPoint/snapGrid` 可用于脚本规划。引脚库必须兼容同一格点,原理图生成与正式交付检查实际引脚及连接点,偏离时阻止交付。已有工程需从脚本重新构建,不通过独立舍入引脚/连线修补。
|
package/lib/builder.d.ts
CHANGED
|
@@ -180,6 +180,17 @@ interface PowerPlanningOptions extends Pick<PowerLabelSpec, 'libraryPath' | 'net
|
|
|
180
180
|
maxMoveMm?: number;
|
|
181
181
|
}
|
|
182
182
|
//#endregion
|
|
183
|
+
//#region src/schematic/design-plan.d.ts
|
|
184
|
+
interface DesignPlanReference {
|
|
185
|
+
path: string;
|
|
186
|
+
sha256: string;
|
|
187
|
+
}
|
|
188
|
+
interface DesignPlan extends DesignPlanReference {
|
|
189
|
+
text: string;
|
|
190
|
+
}
|
|
191
|
+
declare function readDesignPlan(path: string): DesignPlan;
|
|
192
|
+
declare function loadDesignPlan(input?: DesignPlanReference): DesignPlan;
|
|
193
|
+
//#endregion
|
|
183
194
|
//#region src/schematic/builder.d.ts
|
|
184
195
|
/** Explicit instance placement in millimetres; library files must be trusted project inputs. */
|
|
185
196
|
interface SymbolPlacement {
|
|
@@ -200,6 +211,8 @@ interface SymbolPlacement {
|
|
|
200
211
|
}
|
|
201
212
|
/** Explicit deployment templates and page choice; no implicit file discovery. */
|
|
202
213
|
interface SchematicOptions {
|
|
214
|
+
/** Saved plan version reviewed before generation; runner supplies this via environment. */
|
|
215
|
+
designPlan?: DesignPlanReference;
|
|
203
216
|
projectName: string;
|
|
204
217
|
templateDir: string;
|
|
205
218
|
paper: 'A4' | 'A3' | 'A2' | 'A1' | 'A0';
|
|
@@ -312,6 +325,7 @@ declare class Schematic {
|
|
|
312
325
|
private readonly objectModules;
|
|
313
326
|
private readonly requiredPowerSymbols;
|
|
314
327
|
private readonly templates;
|
|
328
|
+
private readonly designPlan;
|
|
315
329
|
/** Load validated templates. @param options Project identity, page and template directory. */
|
|
316
330
|
constructor(options: SchematicOptions);
|
|
317
331
|
/** Reserve a non-overlapping fixed module frame before placing its circuit.
|
|
@@ -419,6 +433,7 @@ declare class Schematic {
|
|
|
419
433
|
projectPath: string;
|
|
420
434
|
frames: ResolvedFlatFrame[];
|
|
421
435
|
layoutWarnings: PowerLayoutWarning[];
|
|
436
|
+
designPlan: DesignPlanReference;
|
|
422
437
|
};
|
|
423
438
|
}
|
|
424
439
|
//#endregion
|
|
@@ -483,4 +498,10 @@ declare function attachPinLabels(target: LabelTarget, spec: {
|
|
|
483
498
|
})[];
|
|
484
499
|
};
|
|
485
500
|
//#endregion
|
|
486
|
-
|
|
501
|
+
//#region src/schematic/grid.d.ts
|
|
502
|
+
/** Electrical schematic coordinates use KiCad's 50 mil connection grid. */
|
|
503
|
+
declare const SCHEMATIC_GRID_MM = 1.27;
|
|
504
|
+
declare function snapGrid(n: number): number;
|
|
505
|
+
declare function gridPoint(p: readonly number[]): [number, number];
|
|
506
|
+
//#endregion
|
|
507
|
+
export { ModuleRegion, ModuleRegionSpec, PowerLabelEntry, PowerLabelReplacement, PowerLabelSpec, PowerLayoutWarning, type PowerPlacementPlan, type PowerPlanningOptions, PowerSymbolStatus, SCHEMATIC_GRID_MM, Schematic, SchematicOptions, SymbolPlacement, attachPinLabel, attachPinLabels, gridPoint, loadDesignPlan, readDesignPlan, resolveKiCadPaths, snapGrid };
|
package/lib/builder.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as required, D as children, E as applyEdits, N as resolveKiCadPaths, O as parse, S as alignVendorSymbolIds, T as normalizeCommonUnits, a as snapGrid, c as loadDesignPlan, d as buildFlatCopy, f as listFlatObjects, h as flatTextSize, i as onGrid, j as value, k as quote, n as SCHEMATIC_GRID_MM, p as loadFlatTemplates, r as gridPoint, t as assertSchematicGrid, u as readDesignPlan, v as validatePreviewSchematic, y as assignFootprintText } from "./grid-check-BgWU7a8j.js";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -402,7 +402,7 @@ var ModuleRegion = class {
|
|
|
402
402
|
};
|
|
403
403
|
function point(at) {
|
|
404
404
|
if (at.length !== 2 || at.some((n) => !Number.isFinite(n))) throw new Error("coordinates require two finite millimetre values");
|
|
405
|
-
return at;
|
|
405
|
+
return gridPoint(at);
|
|
406
406
|
}
|
|
407
407
|
function definition(source, name, chain = []) {
|
|
408
408
|
const root = parse(source);
|
|
@@ -549,9 +549,19 @@ var Schematic = class Schematic {
|
|
|
549
549
|
objectModules = /* @__PURE__ */ new Map();
|
|
550
550
|
requiredPowerSymbols;
|
|
551
551
|
templates;
|
|
552
|
+
designPlan;
|
|
552
553
|
/** Load validated templates. @param options Project identity, page and template directory. */
|
|
553
554
|
constructor(options) {
|
|
554
555
|
this.options = options;
|
|
556
|
+
const { path, sha256 } = loadDesignPlan(options.designPlan);
|
|
557
|
+
if (options.designPlan && process.env.DSH_DESIGN_BRIEF_PATH) {
|
|
558
|
+
const runnerPlan = loadDesignPlan();
|
|
559
|
+
if (path !== runnerPlan.path || sha256 !== runnerPlan.sha256) throw new Error("DESIGN_PLAN_MISMATCH: script and runner must use the same saved plan version");
|
|
560
|
+
}
|
|
561
|
+
this.designPlan = {
|
|
562
|
+
path,
|
|
563
|
+
sha256
|
|
564
|
+
};
|
|
555
565
|
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(options.projectName)) throw new Error("projectName must be an ASCII filename without an extension");
|
|
556
566
|
if (![
|
|
557
567
|
"A4",
|
|
@@ -606,9 +616,14 @@ var Schematic = class Schematic {
|
|
|
606
616
|
* @returns Reference map, translated label anchors, net names and object UUIDs for frames.
|
|
607
617
|
*/
|
|
608
618
|
importModule(spec) {
|
|
619
|
+
spec = {
|
|
620
|
+
...spec,
|
|
621
|
+
offset: gridPoint(spec.offset)
|
|
622
|
+
};
|
|
609
623
|
if (this.importedModules.has(spec.name)) throw new Error(`duplicate imported module: ${spec.name}`);
|
|
610
624
|
const used = new Set([...this.references.keys()].map((key) => key.slice(0, key.lastIndexOf(":"))));
|
|
611
625
|
const sourceText = readFileSync(spec.sourcePath, "utf8");
|
|
626
|
+
assertSchematicGrid(sourceText);
|
|
612
627
|
const prepared = prepareFlatImport(sourceText, spec, this.options.projectName, this.rootId, used);
|
|
613
628
|
const footprints = {};
|
|
614
629
|
if (spec.footprintMappingPath) {
|
|
@@ -656,6 +671,14 @@ var Schematic = class Schematic {
|
|
|
656
671
|
const symbolName = spec.libId.slice(separator + 1);
|
|
657
672
|
const raw = normalizeCommonUnits(definition(readFileSync(spec.libraryPath, "utf8"), symbolName)), form = parse(raw);
|
|
658
673
|
const sections = children(form, "symbol");
|
|
674
|
+
for (const section of sections) {
|
|
675
|
+
const suffix = /_(\d+)_(\d+)$/u.exec(value(section));
|
|
676
|
+
if (!suffix || ![0, unit].includes(Number(suffix[1])) || ![0, 1].includes(Number(suffix[2]))) continue;
|
|
677
|
+
for (const pin of children(section, "pin")) {
|
|
678
|
+
const at = children(pin, "at")[0];
|
|
679
|
+
if (!at || ![Number(value(at, 1)), Number(value(at, 2))].every(onGrid)) throw new Error(`SYMBOL_PIN_OFF_GRID: ${spec.libId} pin ${value(children(pin, "number")[0])} requires a 50 mil (1.27 mm) compatible library symbol. Do not round pin geometry independently of the circuit.`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
659
682
|
if (!sections.some((s) => {
|
|
660
683
|
const m = /_(\d+)_(\d+)$/u.exec(value(s));
|
|
661
684
|
return m && Number(m[1]) === unit && [0, 1].includes(Number(m[2]));
|
|
@@ -741,7 +764,8 @@ var Schematic = class Schematic {
|
|
|
741
764
|
this.endpoint(from),
|
|
742
765
|
...via.map(point),
|
|
743
766
|
this.endpoint(to)
|
|
744
|
-
];
|
|
767
|
+
].filter((p, i, all) => i === 0 || p[0] !== all[i - 1][0] || p[1] !== all[i - 1][1]);
|
|
768
|
+
if (points.length < 2) throw new Error("wire collapses on the 50 mil (1.27 mm) grid; increase the lead length");
|
|
745
769
|
const segments = [], ids = [];
|
|
746
770
|
for (let i = 1; i < points.length; i++) {
|
|
747
771
|
const a = points[i - 1], b = points[i];
|
|
@@ -1004,7 +1028,9 @@ var Schematic = class Schematic {
|
|
|
1004
1028
|
* @returns Schematic text, project JSON text, resolved frames and edit counts.
|
|
1005
1029
|
*/
|
|
1006
1030
|
render() {
|
|
1031
|
+
loadDesignPlan(this.designPlan);
|
|
1007
1032
|
this.checkPowerSymbols();
|
|
1033
|
+
assertSchematicGrid(this.source());
|
|
1008
1034
|
if (!this.instances.length) throw new Error("place at least one symbol before rendering");
|
|
1009
1035
|
return buildFlatCopy(this.source(), this.options.projectName, {
|
|
1010
1036
|
removeReferences: [],
|
|
@@ -1030,7 +1056,8 @@ var Schematic = class Schematic {
|
|
|
1030
1056
|
schematicPath,
|
|
1031
1057
|
projectPath,
|
|
1032
1058
|
frames: result.resolvedFrames,
|
|
1033
|
-
layoutWarnings: this.powerLayoutWarnings()
|
|
1059
|
+
layoutWarnings: this.powerLayoutWarnings(),
|
|
1060
|
+
designPlan: { ...this.designPlan }
|
|
1034
1061
|
};
|
|
1035
1062
|
}
|
|
1036
1063
|
};
|
|
@@ -1131,4 +1158,4 @@ function attachPinLabels(target, spec) {
|
|
|
1131
1158
|
};
|
|
1132
1159
|
}
|
|
1133
1160
|
//#endregion
|
|
1134
|
-
export { ModuleRegion, Schematic, attachPinLabel, attachPinLabels, resolveKiCadPaths };
|
|
1161
|
+
export { ModuleRegion, SCHEMATIC_GRID_MM, Schematic, attachPinLabel, attachPinLabels, gridPoint, loadDesignPlan, readDesignPlan, resolveKiCadPaths, snapGrid };
|
package/lib/client.js
CHANGED
|
@@ -47,6 +47,32 @@
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
//#endregion
|
|
50
|
+
//#region src/client/turn-overview.ts
|
|
51
|
+
const NO_OVERVIEW = { kind: "review" };
|
|
52
|
+
/** Read only this completed turn's persisted tool nodes; no global/session cache or DOM timing. */
|
|
53
|
+
function overviewForTurn(turn, chat) {
|
|
54
|
+
if (turn?.status !== "closed" || turn.end?.data.reason.kind !== "completed" || !chat) return NO_OVERVIEW;
|
|
55
|
+
const calls = [];
|
|
56
|
+
const visited = /* @__PURE__ */ new Set();
|
|
57
|
+
function walk(value, depth = 0) {
|
|
58
|
+
if (!value || typeof value !== "object" || visited.has(value) || depth > 64) return;
|
|
59
|
+
visited.add(value);
|
|
60
|
+
const block = value;
|
|
61
|
+
if (block.kind === "tool-result" && !block.subCalls?.length && typeof block.seq === "number") calls.push({
|
|
62
|
+
seq: block.seq,
|
|
63
|
+
block,
|
|
64
|
+
name: block.call?.name
|
|
65
|
+
});
|
|
66
|
+
if (Array.isArray(block.subCalls)) for (const child of block.subCalls) walk(child, depth + 1);
|
|
67
|
+
}
|
|
68
|
+
for (const key of chat.locations.getTurn(turn.turn)) {
|
|
69
|
+
const node = chat.nodes.get(key);
|
|
70
|
+
if (node?.kind === "tool-call") walk(node.data?.root);
|
|
71
|
+
}
|
|
72
|
+
const latest = calls.sort((a, b) => a.seq - b.seq).at(-1);
|
|
73
|
+
return latest?.name === "render_kicad_module_preview" ? parseCard(latest.block) : NO_OVERVIEW;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
50
76
|
//#region src/client/place.ts
|
|
51
77
|
function placementHost(get) {
|
|
52
78
|
try {
|
|
@@ -71,18 +97,6 @@
|
|
|
71
97
|
if (result?.ok === false) throw new Error(result.message ?? "编辑器拒绝放置。");
|
|
72
98
|
}
|
|
73
99
|
//#endregion
|
|
74
|
-
//#region src/client/latest-overview.ts
|
|
75
|
-
/** Choose the newest mounted snapshot for each session/project; never compare titles. */
|
|
76
|
-
function supersededOverviews(rows) {
|
|
77
|
-
const latest = /* @__PURE__ */ new Map();
|
|
78
|
-
rows.forEach((row, index) => {
|
|
79
|
-
if (!row.session || !row.project) return;
|
|
80
|
-
const key = JSON.stringify([row.session, row.project]), previous = latest.get(key);
|
|
81
|
-
if (previous === void 0 || row.createdAt >= rows[previous].createdAt) latest.set(key, index);
|
|
82
|
-
});
|
|
83
|
-
return rows.map((row, index) => !!row.session && !!row.project && latest.get(JSON.stringify([row.session, row.project])) !== index);
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
86
100
|
//#region ../dsh-sidebar-entry/lib/index.mjs
|
|
87
101
|
/** Find the sidebar shell root element, or undefined while not yet mounted. */
|
|
88
102
|
function sidebarRoot() {
|
|
@@ -262,11 +276,11 @@
|
|
|
262
276
|
* family row means adding its selector here (and to no plugin-local list).
|
|
263
277
|
*/
|
|
264
278
|
const HUAQIU_SIDEBAR_ENTRY_FAMILY = [
|
|
265
|
-
"[data-hq-plugin-update-entry]",
|
|
266
279
|
"[data-hqcg-footprint-entry]",
|
|
267
280
|
"[data-hqcg-symbol-entry]",
|
|
268
281
|
"[data-hqcg-schematic-entry]",
|
|
269
|
-
"[data-hq-dummy-entry]"
|
|
282
|
+
"[data-hq-dummy-entry]",
|
|
283
|
+
"[data-hq-plugin-update-entry]"
|
|
270
284
|
];
|
|
271
285
|
/**
|
|
272
286
|
* Read the host UI language from `<html lang>`.
|
|
@@ -1057,52 +1071,14 @@
|
|
|
1057
1071
|
}
|
|
1058
1072
|
//#endregion
|
|
1059
1073
|
//#region src/client/index.ts
|
|
1060
|
-
/** Harness compacts finished tool calls; keep only our delivery card visible. */
|
|
1061
|
-
function keepDeliveryVisible() {
|
|
1062
|
-
if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {};
|
|
1063
|
-
let frame = 0, disposed = false;
|
|
1064
|
-
const reveal = () => {
|
|
1065
|
-
frame = 0;
|
|
1066
|
-
if (disposed) return;
|
|
1067
|
-
const cards = Array.from(document.querySelectorAll(".dsh-local-overview"));
|
|
1068
|
-
const hidden = supersededOverviews(cards.map((card) => ({
|
|
1069
|
-
session: card.dataset.overviewSession ?? "",
|
|
1070
|
-
project: card.dataset.overviewProject ?? "",
|
|
1071
|
-
createdAt: Number(card.dataset.overviewCreated ?? 0)
|
|
1072
|
-
})));
|
|
1073
|
-
for (const [index, card] of cards.entries()) {
|
|
1074
|
-
if (card.hidden !== hidden[index]) card.hidden = hidden[index];
|
|
1075
|
-
if (hidden[index]) continue;
|
|
1076
|
-
let parent = card.parentElement;
|
|
1077
|
-
while (parent) {
|
|
1078
|
-
if (parent.hasAttribute("data-turn-process-hidden")) parent.removeAttribute("hidden");
|
|
1079
|
-
parent = parent.parentElement;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
};
|
|
1083
|
-
const observer = new MutationObserver(() => {
|
|
1084
|
-
if (!frame) frame = requestAnimationFrame(reveal);
|
|
1085
|
-
});
|
|
1086
|
-
observer.observe(document.documentElement, {
|
|
1087
|
-
subtree: true,
|
|
1088
|
-
childList: true,
|
|
1089
|
-
attributes: true,
|
|
1090
|
-
attributeFilter: ["hidden", "data-turn-process-hidden"]
|
|
1091
|
-
});
|
|
1092
|
-
reveal();
|
|
1093
|
-
return () => {
|
|
1094
|
-
disposed = true;
|
|
1095
|
-
observer.disconnect();
|
|
1096
|
-
if (frame) cancelAnimationFrame(frame);
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
1074
|
window.__ModuleLoader__.load({
|
|
1100
1075
|
id: "@huaqiu/dsh-tool-kicad-schematic-gen",
|
|
1101
1076
|
factory: (require) => {
|
|
1102
1077
|
const { createElement: h, useState, useEffect } = require("react");
|
|
1103
1078
|
let getHost;
|
|
1104
1079
|
function Card(props) {
|
|
1105
|
-
const
|
|
1080
|
+
const chat = props.useChat?.((snapshot) => snapshot);
|
|
1081
|
+
const state = chat ? overviewForTurn(props.turn, chat) : NO_OVERVIEW, result = state.result;
|
|
1106
1082
|
const [page, setPage] = useState(0), [zoom, setZoom] = useState(1), [message, setMessage] = useState(""), [busy, setBusy] = useState(false);
|
|
1107
1083
|
useEffect(() => {
|
|
1108
1084
|
setPage(0);
|
|
@@ -1247,7 +1223,6 @@
|
|
|
1247
1223
|
inject: ["slots", "inputTriggers"],
|
|
1248
1224
|
apply: (ctx) => {
|
|
1249
1225
|
getHost = () => ctx.get?.("hqEdge");
|
|
1250
|
-
const stopVisibility = keepDeliveryVisible();
|
|
1251
1226
|
const source = registerSchematicGenSourceWhenReady(() => ctx.get?.("inputTriggers"));
|
|
1252
1227
|
try {
|
|
1253
1228
|
console.debug("[schematic-gen:client] apply", {
|
|
@@ -1283,12 +1258,12 @@
|
|
|
1283
1258
|
try {
|
|
1284
1259
|
console.debug("[schematic-gen:client] sidebar row", { rows: document.querySelectorAll("[data-hqcg-schematic-entry]").length });
|
|
1285
1260
|
} catch {}
|
|
1286
|
-
const dispose = ctx.slots.inject("
|
|
1287
|
-
name: "
|
|
1288
|
-
|
|
1261
|
+
const dispose = ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
|
|
1262
|
+
name: "conversation.chat.turnTail",
|
|
1263
|
+
id: "huaqiu-schematic-overview",
|
|
1264
|
+
order: 100
|
|
1289
1265
|
}, Card));
|
|
1290
1266
|
ctx.effect?.(() => () => {
|
|
1291
|
-
stopVisibility();
|
|
1292
1267
|
source.dispose();
|
|
1293
1268
|
stopEntry();
|
|
1294
1269
|
if (typeof dispose === "function") dispose();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { delimiter, dirname, join } from "node:path";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/runtime-paths.ts
|
|
5
5
|
const isDefaultCommand = (value) => !value || /^kicad-cli(?:\.exe)?$/i.test(value.trim());
|
|
6
6
|
/** Explicit paths override discovery; default command names still discover the host and PATH. */
|
|
@@ -602,7 +602,8 @@ function buildProject(edited, projectName, sheetName, expectedPorts, drivenInput
|
|
|
602
602
|
schematic: {
|
|
603
603
|
legacy_lib_dir: "",
|
|
604
604
|
legacy_lib_list: [],
|
|
605
|
-
...imported.schematic
|
|
605
|
+
...imported.schematic,
|
|
606
|
+
connection_grid_size: 50
|
|
606
607
|
},
|
|
607
608
|
sheets: [],
|
|
608
609
|
text_variables: {},
|
|
@@ -1504,4 +1505,108 @@ function buildFlatCopy(source, projectName, plan, templates) {
|
|
|
1504
1505
|
};
|
|
1505
1506
|
}
|
|
1506
1507
|
//#endregion
|
|
1507
|
-
|
|
1508
|
+
//#region src/schematic/design-plan.ts
|
|
1509
|
+
/** Required, version-bound design document input for schematic generation. */
|
|
1510
|
+
const DESIGN_PLAN_MAX_BYTES = 1048576;
|
|
1511
|
+
function designPlanReference(input) {
|
|
1512
|
+
const plan = input;
|
|
1513
|
+
if (!plan || typeof plan.path !== "string" || !plan.path.trim() || typeof plan.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(plan.sha256)) throw new Error("DESIGN_PLAN_REQUIRED: save the hardware design document, read it with run-design.mjs --read-plan <path>, then supply designPlan:{path,sha256}. Do not generate first and write the document afterwards.");
|
|
1514
|
+
return {
|
|
1515
|
+
path: plan.path,
|
|
1516
|
+
sha256: plan.sha256
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
/** Structural check only; the model must still review engineering completeness. */
|
|
1520
|
+
function inspectDesignPlan(path, text) {
|
|
1521
|
+
if (Buffer.byteLength(text, "utf8") > 1048576 || text.trim().length < 200 || !/^#\s+\S/m.test(text) || !/^##\s+\S/m.test(text)) throw new Error("DESIGN_PLAN_INCOMPLETE: provide a saved Markdown hardware plan with a title, substantive sections, and at least 200 characters. Review requirements, parts, circuits, connections and budgets before generation.");
|
|
1522
|
+
return {
|
|
1523
|
+
path,
|
|
1524
|
+
sha256: createHash("sha256").update(text).digest("hex"),
|
|
1525
|
+
text
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function matchDesignPlan(reference, text) {
|
|
1529
|
+
const plan = inspectDesignPlan(reference.path, text);
|
|
1530
|
+
if (plan.sha256 !== reference.sha256) throw new Error("DESIGN_PLAN_CHANGED: the document changed after it was read. Read the latest document and update the design script/connection plan before rebuilding with its new sha256.");
|
|
1531
|
+
return plan;
|
|
1532
|
+
}
|
|
1533
|
+
function readDesignPlan(path) {
|
|
1534
|
+
try {
|
|
1535
|
+
const canonical = realpathSync(path), info = statSync(canonical);
|
|
1536
|
+
if (!info.isFile() || info.size > 1048576) throw new Error("expected a document file no larger than 1 MiB");
|
|
1537
|
+
return inspectDesignPlan(canonical, new TextDecoder("utf-8", { fatal: true }).decode(readFileSync(canonical)));
|
|
1538
|
+
} catch (error) {
|
|
1539
|
+
throw new Error(`DESIGN_PLAN_UNAVAILABLE: ${path}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function loadDesignPlan(input) {
|
|
1543
|
+
const reference = designPlanReference(input ?? {
|
|
1544
|
+
path: process.env.DSH_DESIGN_BRIEF_PATH,
|
|
1545
|
+
sha256: process.env.DSH_DESIGN_BRIEF_SHA256
|
|
1546
|
+
});
|
|
1547
|
+
const plan = readDesignPlan(reference.path);
|
|
1548
|
+
return matchDesignPlan({
|
|
1549
|
+
...reference,
|
|
1550
|
+
path: plan.path
|
|
1551
|
+
}, plan.text);
|
|
1552
|
+
}
|
|
1553
|
+
//#endregion
|
|
1554
|
+
//#region src/schematic/grid.ts
|
|
1555
|
+
/** Electrical schematic coordinates use KiCad's 50 mil connection grid. */
|
|
1556
|
+
const SCHEMATIC_GRID_MM = 1.27;
|
|
1557
|
+
function snapGrid(n) {
|
|
1558
|
+
if (!Number.isFinite(n)) throw new Error("GRID_COORDINATE_INVALID: expected finite millimetres");
|
|
1559
|
+
return Number((Math.round(n / SCHEMATIC_GRID_MM) * SCHEMATIC_GRID_MM).toFixed(6));
|
|
1560
|
+
}
|
|
1561
|
+
function gridPoint(p) {
|
|
1562
|
+
if (p.length !== 2) throw new Error("grid point requires two coordinates");
|
|
1563
|
+
return [snapGrid(p[0]), snapGrid(p[1])];
|
|
1564
|
+
}
|
|
1565
|
+
function onGrid(n) {
|
|
1566
|
+
return Number.isFinite(n) && Math.abs(n - snapGrid(n)) < 1e-6;
|
|
1567
|
+
}
|
|
1568
|
+
//#endregion
|
|
1569
|
+
//#region src/schematic/grid-check.ts
|
|
1570
|
+
/** Never round an existing circuit's wires and library pins independently: that can break nets. */
|
|
1571
|
+
function assertSchematicGrid(source) {
|
|
1572
|
+
const failures = [];
|
|
1573
|
+
const root = parse(source), sheets = children(root, "sheet");
|
|
1574
|
+
const check = (name, p) => {
|
|
1575
|
+
if (!p.every(onGrid) && failures.length < 16) failures.push(`${name} (${p.join(", ")})`);
|
|
1576
|
+
};
|
|
1577
|
+
const flat = applyEdits(source, sheets.map((sheet) => ({
|
|
1578
|
+
start: sheet.start,
|
|
1579
|
+
end: sheet.end,
|
|
1580
|
+
text: ""
|
|
1581
|
+
})));
|
|
1582
|
+
for (const object of listFlatObjects(flat)) if (object.type === "symbol") {
|
|
1583
|
+
if (object.pinError) throw new Error(`GRID_PIN_UNRESOLVED: ${object.reference}: ${object.pinError}`);
|
|
1584
|
+
if (object.at) check(object.reference ?? "symbol", object.at);
|
|
1585
|
+
for (const pin of object.pins ?? []) check(`${object.reference}.${pin.number}`, pin.at);
|
|
1586
|
+
} else if (["wire", "bus"].includes(object.type)) {
|
|
1587
|
+
if (object.from) check(`${object.type} start`, object.from);
|
|
1588
|
+
if (object.to) check(`${object.type} end`, object.to);
|
|
1589
|
+
} else if ([
|
|
1590
|
+
"label",
|
|
1591
|
+
"global_label",
|
|
1592
|
+
"hierarchical_label",
|
|
1593
|
+
"junction",
|
|
1594
|
+
"no_connect"
|
|
1595
|
+
].includes(object.type) && object.at) check(object.type, object.at);
|
|
1596
|
+
for (const sheet of sheets) {
|
|
1597
|
+
for (const at of children(sheet, "at")) check("sheet", [Number(value(at, 1)), Number(value(at, 2))]);
|
|
1598
|
+
for (const pin of children(sheet, "pin")) for (const at of children(pin, "at")) check(`sheet pin ${value(pin)}`, [Number(value(at, 1)), Number(value(at, 2))]);
|
|
1599
|
+
}
|
|
1600
|
+
for (const kind of ["wire", "bus"]) for (const wire of children(root, kind)) for (const pts of children(wire, "pts")) for (const xy of children(pts, "xy")) check(kind, [Number(value(xy, 1)), Number(value(xy, 2))]);
|
|
1601
|
+
for (const entry of children(root, "bus_entry")) {
|
|
1602
|
+
const at = children(entry, "at")[0], size = children(entry, "size")[0];
|
|
1603
|
+
if (at && size) {
|
|
1604
|
+
const x = Number(value(at, 1)), y = Number(value(at, 2));
|
|
1605
|
+
check("bus entry", [x, y]);
|
|
1606
|
+
check("bus entry end", [x + Number(value(size, 1)), y + Number(value(size, 2))]);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
if (failures.length) throw new Error(`SCHEMATIC_OFF_GRID: expected 50 mil (1.27 mm). ${failures.join("; ")}. Fix source placement/routing or select a 50 mil compatible symbol; do not round library pins and connected wires separately.`);
|
|
1610
|
+
}
|
|
1611
|
+
//#endregion
|
|
1612
|
+
export { required as A, buildProject as C, children as D, applyEdits as E, getKiCadPaths as M, resolveKiCadPaths as N, parse as O, alignVendorSymbolIds as S, normalizeCommonUnits as T, previewExportError as _, snapGrid as a, auditFootprints as b, loadDesignPlan as c, buildFlatCopy as d, listFlatObjects as f, PreviewError as g, flatTextSize as h, onGrid as i, value as j, quote as k, matchDesignPlan as l, summarizeFlatLayout as m, SCHEMATIC_GRID_MM as n, DESIGN_PLAN_MAX_BYTES as o, loadFlatTemplates as p, gridPoint as r, designPlanReference as s, assertSchematicGrid as t, readDesignPlan as u, validatePreviewSchematic as v, isCommonOnlyUnit as w, footprintRequirements as x, assignFootprintText as y };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as required, C as buildProject, D as children, E as applyEdits, M as getKiCadPaths, O as parse, T as normalizeCommonUnits, _ as previewExportError, b as auditFootprints, d as buildFlatCopy, f as listFlatObjects, g as PreviewError, j as value, k as quote, l as matchDesignPlan, m as summarizeFlatLayout, o as DESIGN_PLAN_MAX_BYTES, p as loadFlatTemplates, r as gridPoint, s as designPlanReference, t as assertSchematicGrid, v as validatePreviewSchematic, w as isCommonOnlyUnit, x as footprintRequirements, y as assignFootprintText } from "./grid-check-BgWU7a8j.js";
|
|
2
2
|
import { getLogger } from "@huaqiu/dsh-plugin-log";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
@@ -5218,7 +5218,7 @@ function text(value, field, max = 300) {
|
|
|
5218
5218
|
}
|
|
5219
5219
|
function point(value, field) {
|
|
5220
5220
|
if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "number" || !Number.isFinite(item) || Math.abs(item) > 2e3)) throw new Error(`${field} must be two finite coordinates in millimetres, each within +/-2000`);
|
|
5221
|
-
return value;
|
|
5221
|
+
return gridPoint(value);
|
|
5222
5222
|
}
|
|
5223
5223
|
function endpoint(value, field) {
|
|
5224
5224
|
if (Array.isArray(value)) return point(value, field);
|
|
@@ -5255,7 +5255,8 @@ function parseFlatRequest(args, listLimit) {
|
|
|
5255
5255
|
"setValues",
|
|
5256
5256
|
"setLabels",
|
|
5257
5257
|
"addWires",
|
|
5258
|
-
"frames"
|
|
5258
|
+
"frames",
|
|
5259
|
+
"designPlan"
|
|
5259
5260
|
], "edit_kicad_flat_schematic");
|
|
5260
5261
|
const sourcePath = requiredString(input, "sourcePath");
|
|
5261
5262
|
if (input.action === "connections") {
|
|
@@ -5394,7 +5395,8 @@ function parseFlatRequest(args, listLimit) {
|
|
|
5394
5395
|
sourcePath,
|
|
5395
5396
|
targetDir: requiredString(input, "targetDir"),
|
|
5396
5397
|
projectName: requiredString(input, "projectName"),
|
|
5397
|
-
plan
|
|
5398
|
+
plan,
|
|
5399
|
+
designPlan: designPlanReference(input.designPlan)
|
|
5398
5400
|
};
|
|
5399
5401
|
}
|
|
5400
5402
|
//#endregion
|
|
@@ -5646,6 +5648,30 @@ function previewConfig(config, kicadCliPath) {
|
|
|
5646
5648
|
//#endregion
|
|
5647
5649
|
//#region src/schematic/tools.ts
|
|
5648
5650
|
/** Read, preview, and generate KiCad schematic copies through workspace-scoped DSH tools. */
|
|
5651
|
+
const designPlanSchema = {
|
|
5652
|
+
type: "object",
|
|
5653
|
+
description: "Required saved hardware design document and exact version returned by run-design.mjs --read-plan. Read and implement this plan before generation.",
|
|
5654
|
+
additionalProperties: false,
|
|
5655
|
+
properties: {
|
|
5656
|
+
path: { type: "string" },
|
|
5657
|
+
sha256: { type: "string" }
|
|
5658
|
+
},
|
|
5659
|
+
required: ["path", "sha256"]
|
|
5660
|
+
};
|
|
5661
|
+
/** Read the exact plan version through workspace policy before any generation writes. */
|
|
5662
|
+
async function readWorkspaceDesignPlan(fs, workspace, input, signal) {
|
|
5663
|
+
const reference = designPlanReference(input);
|
|
5664
|
+
const root = await fs.resolve(workspace, { signal }), target = await fs.resolve(reference.path, {
|
|
5665
|
+
cwd: workspace,
|
|
5666
|
+
signal
|
|
5667
|
+
});
|
|
5668
|
+
if (!fs.contains(root, target) || (await fs.stat(target, signal))?.type !== "file") throw new Error("DESIGN_PLAN_UNAVAILABLE: designPlan.path must be an existing document inside the workspace");
|
|
5669
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(await fs.readBytes(target, signal, DESIGN_PLAN_MAX_BYTES));
|
|
5670
|
+
return matchDesignPlan({
|
|
5671
|
+
...reference,
|
|
5672
|
+
path: target.displayPath
|
|
5673
|
+
}, text);
|
|
5674
|
+
}
|
|
5649
5675
|
async function writeOutputs(fs, targets, policy, signal) {
|
|
5650
5676
|
const written = [];
|
|
5651
5677
|
for (const { target, content } of targets) {
|
|
@@ -6106,6 +6132,7 @@ function registerSchematicTools(ctx, config) {
|
|
|
6106
6132
|
});
|
|
6107
6133
|
for (const item of sources) {
|
|
6108
6134
|
const sheet = item.schematicPath === source.path ? source : await readWorkspaceSchematic(ctx.fs, workspace, item.schematicPath, config.maxSchematicBytes, exec.signal);
|
|
6135
|
+
assertSchematicGrid(sheet.text);
|
|
6109
6136
|
const footprints = await auditFootprints(sheet.text, resolved.displayPath, ctx.fs, exec.signal);
|
|
6110
6137
|
if (footprints.status !== "ready") throw new Error(`FOOTPRINTS_UNRESOLVED: ${footprints.unresolved.length} of ${footprints.total} physical instances in ${item.schematicPath} lack project-local footprints. Run localize_kicad_module_footprints check/import and persist script mappings before delivery.`);
|
|
6111
6138
|
}
|
|
@@ -6155,6 +6182,7 @@ function registerSchematicTools(ctx, config) {
|
|
|
6155
6182
|
type: "object",
|
|
6156
6183
|
additionalProperties: false,
|
|
6157
6184
|
properties: {
|
|
6185
|
+
designPlan: designPlanSchema,
|
|
6158
6186
|
projectName: {
|
|
6159
6187
|
type: "string",
|
|
6160
6188
|
description: "ASCII filename stem: letters, digits, underscores, and hyphens; starts with a letter."
|
|
@@ -6202,7 +6230,8 @@ function registerSchematicTools(ctx, config) {
|
|
|
6202
6230
|
"projectName",
|
|
6203
6231
|
"title",
|
|
6204
6232
|
"targetDir",
|
|
6205
|
-
"sheets"
|
|
6233
|
+
"sheets",
|
|
6234
|
+
"designPlan"
|
|
6206
6235
|
]
|
|
6207
6236
|
},
|
|
6208
6237
|
output: {
|
|
@@ -6210,6 +6239,7 @@ function registerSchematicTools(ctx, config) {
|
|
|
6210
6239
|
type: "object",
|
|
6211
6240
|
additionalProperties: false,
|
|
6212
6241
|
properties: {
|
|
6242
|
+
designPlan: designPlanSchema,
|
|
6213
6243
|
projectPath: { type: "string" },
|
|
6214
6244
|
rootPath: { type: "string" },
|
|
6215
6245
|
symbolLibraryPath: { type: "string" },
|
|
@@ -6276,7 +6306,8 @@ function registerSchematicTools(ctx, config) {
|
|
|
6276
6306
|
"projectName",
|
|
6277
6307
|
"title",
|
|
6278
6308
|
"targetDir",
|
|
6279
|
-
"sheets"
|
|
6309
|
+
"sheets",
|
|
6310
|
+
"designPlan"
|
|
6280
6311
|
], "create_kicad_hierarchy");
|
|
6281
6312
|
const projectName = requiredString(input, "projectName", 64);
|
|
6282
6313
|
if (!/^[A-Za-z][A-Za-z0-9_-]*$/u.test(projectName)) throw new Error("projectName must start with an ASCII letter and contain only letters, digits, underscores, or hyphens");
|
|
@@ -6284,6 +6315,7 @@ function registerSchematicTools(ctx, config) {
|
|
|
6284
6315
|
const targetDir = requiredString(input, "targetDir");
|
|
6285
6316
|
if (!Array.isArray(input.sheets) || input.sheets.length < 1 || input.sheets.length > 9) throw new Error("sheets must contain 1 to 9 modules");
|
|
6286
6317
|
const workspace = workspaceRoot(exec.agent);
|
|
6318
|
+
const designPlan = await readWorkspaceDesignPlan(ctx.fs, workspace, input.designPlan, exec.signal);
|
|
6287
6319
|
const workspacePath = await ctx.fs.resolve(workspace, { signal: exec.signal });
|
|
6288
6320
|
const directory = await ctx.fs.resolve(targetDir, {
|
|
6289
6321
|
cwd: workspace,
|
|
@@ -6311,8 +6343,14 @@ function registerSchematicTools(ctx, config) {
|
|
|
6311
6343
|
};
|
|
6312
6344
|
}));
|
|
6313
6345
|
const files = buildAssembly(projectName, title, sheets);
|
|
6346
|
+
assertSchematicGrid(files.root);
|
|
6347
|
+
for (const child of files.children) assertSchematicGrid(child.text);
|
|
6314
6348
|
const paths = await publishAssembly(ctx.fs, workspace, targetDir, projectName, files, void 0, exec.signal);
|
|
6315
6349
|
return {
|
|
6350
|
+
designPlan: {
|
|
6351
|
+
path: designPlan.path,
|
|
6352
|
+
sha256: designPlan.sha256
|
|
6353
|
+
},
|
|
6316
6354
|
projectPath: paths.projectPath,
|
|
6317
6355
|
rootPath: paths.rootPath,
|
|
6318
6356
|
symbolLibraryPath: paths.symbolLibraryPath,
|
|
@@ -6343,6 +6381,10 @@ function registerSchematicTools(ctx, config) {
|
|
|
6343
6381
|
type: "object",
|
|
6344
6382
|
additionalProperties: false,
|
|
6345
6383
|
properties: {
|
|
6384
|
+
designPlan: {
|
|
6385
|
+
...designPlanSchema,
|
|
6386
|
+
description: "Required for action:create. Saved and reviewed hardware plan path and exact sha256 returned by run-design.mjs --read-plan. Not used for list/connections."
|
|
6387
|
+
},
|
|
6346
6388
|
action: {
|
|
6347
6389
|
type: "string",
|
|
6348
6390
|
enum: [
|
|
@@ -6512,6 +6554,7 @@ function registerSchematicTools(ctx, config) {
|
|
|
6512
6554
|
type: "object",
|
|
6513
6555
|
additionalProperties: false,
|
|
6514
6556
|
properties: {
|
|
6557
|
+
designPlan: designPlanSchema,
|
|
6515
6558
|
sha256: { type: "string" },
|
|
6516
6559
|
connection: { type: "object" },
|
|
6517
6560
|
status: { type: "string" },
|
|
@@ -6581,7 +6624,9 @@ function registerSchematicTools(ctx, config) {
|
|
|
6581
6624
|
nextAction: "Reuse the requested information; fetch nextOffset only if needed. Planning reads do not require an edited copy. Only when implementing edits, use these UUIDs/coordinates and prefer frames autoBounds:true. Image sizes are in mm; bounds describe layout, not electrical connectivity."
|
|
6582
6625
|
};
|
|
6583
6626
|
}
|
|
6627
|
+
const designPlan = await readWorkspaceDesignPlan(ctx.fs, workspace, request.designPlan, exec.signal);
|
|
6584
6628
|
const files = buildFlatCopy(source.text, request.projectName, request.plan, flatTemplates);
|
|
6629
|
+
assertSchematicGrid(files.schematic);
|
|
6585
6630
|
const directory = await ctx.fs.resolve(request.targetDir, {
|
|
6586
6631
|
cwd: workspace,
|
|
6587
6632
|
signal: exec.signal
|
|
@@ -6599,6 +6644,10 @@ function registerSchematicTools(ctx, config) {
|
|
|
6599
6644
|
}));
|
|
6600
6645
|
await writeOutputs(ctx.fs, targets, void 0, exec.signal);
|
|
6601
6646
|
return {
|
|
6647
|
+
designPlan: {
|
|
6648
|
+
path: designPlan.path,
|
|
6649
|
+
sha256: designPlan.sha256
|
|
6650
|
+
},
|
|
6602
6651
|
status: "created",
|
|
6603
6652
|
sourcePath: source.path,
|
|
6604
6653
|
schematicPath: targets[0].target.displayPath,
|
|
@@ -7450,9 +7499,12 @@ const SCHEMATIC_GEN_TOKEN = "@schematic-gen";
|
|
|
7450
7499
|
function schematicGenSectionText() {
|
|
7451
7500
|
return [
|
|
7452
7501
|
`Schematic Gen: the ${SCHEMATIC_GEN_TOKEN} token in a user message is the Schematic Gen chip — the user explicitly chose it for that request. Produce the design with the \`kicad-schematic-design\` skill, which starts from the hardware design brief when none exists yet.`,
|
|
7502
|
+
"For every new schematic project, first show the requirements summary, scope, key unknowns and recommended choices. Ask with ask_user_question and wait for an actual user reply BEFORE writing formal design documents or generation scripts. If the tool is unavailable, ask in chat and end the turn to wait. Silence, timeout, cancellation or a preselected option is not confirmation. Reuse an already confirmed requirements baseline for the same project; do not ask again unless core requirements change.",
|
|
7503
|
+
"Before writing or running a schematic generation script, save the hardware design document and read it back with run-design.mjs --read-plan. Implement that document; pass its path and sha256 to generation. Never postpone the plan until after drawing. ",
|
|
7453
7504
|
"Never hand-write .kicad_sch files or KiCad project files yourself; go through the skill and its tools.",
|
|
7454
7505
|
"Use the user's own words as the design description — if they have not described the circuit yet, ask for the requirements instead of assuming them.",
|
|
7455
|
-
"Follow-up messages in the same conversation continue that work without repeating the token."
|
|
7506
|
+
"Follow-up messages in the same conversation continue that work without repeating the token.",
|
|
7507
|
+
"For a simple schematic, plan layout before the first build, collect concrete readability defects and fix them together once, then verify and deliver. Stop layout edits when connections, grid, readability, page fit and project files meet the delivery criteria. Further iterations require an identified blocking defect; do not rebuild for cosmetic preferences or to drive conservative layout warnings to zero. Real errors must still be fixed, regardless of iteration count."
|
|
7456
7508
|
].join("\n");
|
|
7457
7509
|
}
|
|
7458
7510
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huaqiu/dsh-tool-kicad-schematic-gen",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
4
4
|
"description": "KiCad 原理图设计:电路模块搜索、器件模型与手册、符号导入、编辑预览、脚本构建及设计技能",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"pdfjs-dist": "6.3.289",
|
|
33
33
|
"sharp": "0.35.4",
|
|
34
34
|
"undici": "^8.10.0",
|
|
35
|
-
"@huaqiu/dsh-plugin-log": "0.6.
|
|
36
|
-
"@huaqiu/dsh-sidebar-entry": "0.6.
|
|
35
|
+
"@huaqiu/dsh-plugin-log": "0.6.9",
|
|
36
|
+
"@huaqiu/dsh-sidebar-entry": "0.6.9"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.2",
|