create-zudo-circuit-doc 0.1.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/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/bin/create-zudo-circuit-doc.js +6 -0
- package/dist/args.d.ts +21 -0
- package/dist/args.js +71 -0
- package/dist/cli.d.ts +27 -0
- package/dist/cli.js +101 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +7 -0
- package/dist/git.d.ts +11 -0
- package/dist/git.js +69 -0
- package/dist/help.d.ts +2 -0
- package/dist/help.js +21 -0
- package/dist/install.d.ts +3 -0
- package/dist/install.js +17 -0
- package/dist/next-steps.d.ts +10 -0
- package/dist/next-steps.js +24 -0
- package/dist/plan.d.ts +17 -0
- package/dist/plan.js +59 -0
- package/dist/prompt.d.ts +3 -0
- package/dist/prompt.js +12 -0
- package/dist/scaffold.d.ts +22 -0
- package/dist/scaffold.js +187 -0
- package/dist/shell-quote.d.ts +2 -0
- package/dist/shell-quote.js +7 -0
- package/dist/validate.d.ts +18 -0
- package/dist/validate.js +85 -0
- package/dist/version.d.ts +6 -0
- package/dist/version.js +13 -0
- package/package.json +49 -0
- package/templates/default/.claude/skills/circuit-spec-integration/SKILL.md +22 -0
- package/templates/default/.claude/skills/circuit-spec-integration/references/rules.json +4 -0
- package/templates/default/.claude/skills/component-spec-audit/SKILL.md +36 -0
- package/templates/default/.claude/skills/component-spec-audit/references/contract.md +21 -0
- package/templates/default/.claude/skills/component-spec-audit/references/direct-routing.json +5 -0
- package/templates/default/.claude/skills/component-spec-audit/references/external-vendor-qualifiers.json +4 -0
- package/templates/default/.claude/skills/component-spec-audit/references/inventory.json +11 -0
- package/templates/default/.claude/skills/component-spec-audit/references/new-component-workflow.md +113 -0
- package/templates/default/AGENTS.md +7 -0
- package/templates/default/CLAUDE.md +7 -0
- package/templates/default/README.md +49 -0
- package/templates/default/ZUDO_DEPS_PINS.md +48 -0
- package/templates/default/_gitignore +25 -0
- package/templates/default/circuit/WORKFLOW.md +361 -0
- package/templates/default/circuit/agent-task-examples.md +178 -0
- package/templates/default/circuit/checks/README.md +37 -0
- package/templates/default/circuit/generated/preflight.json +625 -0
- package/templates/default/circuit/publication/assets.json +9 -0
- package/templates/default/circuit/publication/selection.json +13 -0
- package/templates/default/circuit/templates/README.md +33 -0
- package/templates/default/circuit/templates/cad-asset-receipt.json +58 -0
- package/templates/default/circuit/templates/cad-asset-receipt.md +33 -0
- package/templates/default/circuit/templates/project-docs/architecture/interfaces.mdx +52 -0
- package/templates/default/circuit/templates/project-docs/architecture/overview.mdx +55 -0
- package/templates/default/circuit/templates/project-docs/decisions/decision.mdx +66 -0
- package/templates/default/circuit/templates/project-docs/decisions/sourcing.mdx +57 -0
- package/templates/default/circuit/templates/project-docs/project/change-impact.mdx +61 -0
- package/templates/default/circuit/templates/project-docs/project/index.mdx +62 -0
- package/templates/default/circuit/templates/project-docs/project/next-actions.mdx +58 -0
- package/templates/default/circuit/templates/project-docs/project/task-request.mdx +56 -0
- package/templates/default/circuit/templates/project-docs/research/component-candidate.mdx +60 -0
- package/templates/default/circuit/templates/project-docs/verification/bring-up.mdx +55 -0
- package/templates/default/circuit.config.ts +36 -0
- package/templates/default/doc/package.json +37 -0
- package/templates/default/doc/pages/docs/[[...slug]].tsx +68 -0
- package/templates/default/doc/pages/index.tsx +6 -0
- package/templates/default/doc/pages/lib/_circuit-doc-islands.ts +4 -0
- package/templates/default/doc/public/favicon-16x16.png +0 -0
- package/templates/default/doc/public/favicon-32x32.png +0 -0
- package/templates/default/doc/public/favicon.ico +0 -0
- package/templates/default/doc/public/favicon.svg +4 -0
- package/templates/default/doc/scripts/check-links.js +969 -0
- package/templates/default/doc/src/chrome-bindings.tsx +11 -0
- package/templates/default/doc/src/content/docs/architecture/index.mdx +11 -0
- package/templates/default/doc/src/content/docs/architecture/overview.mdx +55 -0
- package/templates/default/doc/src/content/docs/components/catalog/index.mdx +12 -0
- package/templates/default/doc/src/content/docs/components/index.mdx +49 -0
- package/templates/default/doc/src/content/docs/components/integration/index.mdx +34 -0
- package/templates/default/doc/src/content/docs/components/records/index.mdx +14 -0
- package/templates/default/doc/src/content/docs/decisions/index.mdx +9 -0
- package/templates/default/doc/src/content/docs/project/how-we-work.mdx +67 -0
- package/templates/default/doc/src/content/docs/project/index.mdx +60 -0
- package/templates/default/doc/src/content/docs/project/next-actions.mdx +58 -0
- package/templates/default/doc/src/content/docs/research/index.mdx +9 -0
- package/templates/default/doc/src/content/docs/verification/index.mdx +9 -0
- package/templates/default/doc/src/styles/global.css +31 -0
- package/templates/default/doc/tsconfig.json +13 -0
- package/templates/default/doc/zfb.config.ts +78 -0
- package/templates/default/package.json +23 -0
- package/templates/default/pnpm-workspace.yaml +9 -0
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { CliError } from "./errors.js";
|
|
5
|
+
// Files carrying these extensions are copied byte-for-byte: they are either
|
|
6
|
+
// binary (previews, 3D models, fonts) or, in the case of .svg, listed
|
|
7
|
+
// explicitly by the spec as skipped even though it is text (spec #6).
|
|
8
|
+
const BINARY_EXTENSIONS = new Set([
|
|
9
|
+
".png",
|
|
10
|
+
".jpg",
|
|
11
|
+
".jpeg",
|
|
12
|
+
".gif",
|
|
13
|
+
".ico",
|
|
14
|
+
".svg",
|
|
15
|
+
".wrl",
|
|
16
|
+
".step",
|
|
17
|
+
".stp",
|
|
18
|
+
".pdf",
|
|
19
|
+
".zip",
|
|
20
|
+
".gz",
|
|
21
|
+
".woff",
|
|
22
|
+
".woff2",
|
|
23
|
+
".ttf",
|
|
24
|
+
".otf",
|
|
25
|
+
".eot",
|
|
26
|
+
]);
|
|
27
|
+
const PLACEHOLDER_TOKENS = [
|
|
28
|
+
"__PROJECT_NAME__",
|
|
29
|
+
"__SITE_TITLE__",
|
|
30
|
+
"__LIBRARY_NAME__",
|
|
31
|
+
];
|
|
32
|
+
export function placeholdersFromPlan(plan) {
|
|
33
|
+
return {
|
|
34
|
+
__PROJECT_NAME__: plan.name,
|
|
35
|
+
__SITE_TITLE__: plan.title,
|
|
36
|
+
__LIBRARY_NAME__: plan.library,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Throws a CliError, unchanged, if the destination exists and is not empty (spec #4). There is no force flag. */
|
|
40
|
+
export function checkDestinationCollision(destinationPath) {
|
|
41
|
+
let stat;
|
|
42
|
+
try {
|
|
43
|
+
stat = fs.statSync(destinationPath);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return; // Missing destination is fine.
|
|
47
|
+
}
|
|
48
|
+
if (!stat.isDirectory()) {
|
|
49
|
+
throw new CliError(`Cannot create project: "${destinationPath}" already exists and is not a directory.`);
|
|
50
|
+
}
|
|
51
|
+
const entries = fs.readdirSync(destinationPath);
|
|
52
|
+
if (entries.length > 0) {
|
|
53
|
+
throw new CliError(`Cannot create project: "${destinationPath}" already exists and is not empty.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function resolveTemplateDirPath(templateDir) {
|
|
57
|
+
return typeof templateDir === "string" ? templateDir : fileURLToPath(templateDir);
|
|
58
|
+
}
|
|
59
|
+
function substitutePlaceholders(text, values) {
|
|
60
|
+
let result = text;
|
|
61
|
+
for (const token of PLACEHOLDER_TOKENS) {
|
|
62
|
+
result = result.replaceAll(token, values[token]);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function copyTemplateTree(sourceDir, targetDir, values, beforeCopyFile, relPath) {
|
|
67
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
68
|
+
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
69
|
+
const entryRelPath = relPath ? `${relPath}/${entry.name}` : entry.name;
|
|
70
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
71
|
+
const targetName = entry.name === "_gitignore" ? ".gitignore" : entry.name;
|
|
72
|
+
const targetPath = path.join(targetDir, targetName);
|
|
73
|
+
if (entry.isDirectory()) {
|
|
74
|
+
copyTemplateTree(sourcePath, targetPath, values, beforeCopyFile, entryRelPath);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!entry.isFile()) {
|
|
78
|
+
throw new Error(`Unsupported template entry (not a file or directory): ${entryRelPath}`);
|
|
79
|
+
}
|
|
80
|
+
beforeCopyFile?.(entryRelPath);
|
|
81
|
+
const stat = fs.statSync(sourcePath);
|
|
82
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
83
|
+
if (BINARY_EXTENSIONS.has(ext)) {
|
|
84
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const contents = fs.readFileSync(sourcePath, "utf8");
|
|
88
|
+
fs.writeFileSync(targetPath, substitutePlaceholders(contents, values), "utf8");
|
|
89
|
+
}
|
|
90
|
+
fs.chmodSync(targetPath, stat.mode);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function assertNoPlaceholdersRemain(dir, relPath = "") {
|
|
94
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
95
|
+
const entryRelPath = relPath ? `${relPath}/${entry.name}` : entry.name;
|
|
96
|
+
const entryPath = path.join(dir, entry.name);
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
assertNoPlaceholdersRemain(entryPath, entryRelPath);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
102
|
+
if (BINARY_EXTENSIONS.has(ext))
|
|
103
|
+
continue;
|
|
104
|
+
const contents = fs.readFileSync(entryPath, "utf8");
|
|
105
|
+
for (const token of PLACEHOLDER_TOKENS) {
|
|
106
|
+
if (contents.includes(token)) {
|
|
107
|
+
throw new Error(`Placeholder ${token} was not replaced in generated file: ${entryRelPath}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const RUNTIME_PACKAGE_NAME = "@takazudo/zudo-circuit-doc";
|
|
113
|
+
const RUNTIME_SPEC_MANIFESTS = ["package.json", "doc/package.json"];
|
|
114
|
+
const DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
|
|
115
|
+
/**
|
|
116
|
+
* Overwrites the `@takazudo/zudo-circuit-doc` dependency spec in both
|
|
117
|
+
* manifests with `runtimeSpec` (spec #58's `--runtime-spec`). Both manifests
|
|
118
|
+
* are required to already declare the dependency — every shipped template
|
|
119
|
+
* does — so a missing one is a template bug, not a user error.
|
|
120
|
+
*/
|
|
121
|
+
function applyRuntimeSpec(stagingDir, runtimeSpec) {
|
|
122
|
+
for (const relPath of RUNTIME_SPEC_MANIFESTS) {
|
|
123
|
+
const manifestPath = path.join(stagingDir, ...relPath.split("/"));
|
|
124
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
125
|
+
let found = false;
|
|
126
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
127
|
+
const dependencies = manifest[field];
|
|
128
|
+
if (dependencies && typeof dependencies === "object" && RUNTIME_PACKAGE_NAME in dependencies) {
|
|
129
|
+
dependencies[RUNTIME_PACKAGE_NAME] = runtimeSpec;
|
|
130
|
+
found = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!found) {
|
|
134
|
+
throw new Error(`${relPath} has no "${RUNTIME_PACKAGE_NAME}" dependency to apply --runtime-spec to.`);
|
|
135
|
+
}
|
|
136
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** claude removes AGENTS.md, codex removes CLAUDE.md, none removes both, both removes neither (spec #6). `.claude/skills/**` is never touched here, so it always stays. */
|
|
140
|
+
function applyAgentFilter(dir, agent) {
|
|
141
|
+
const removeIfExists = (name) => {
|
|
142
|
+
const target = path.join(dir, name);
|
|
143
|
+
if (fs.existsSync(target))
|
|
144
|
+
fs.rmSync(target, { force: true });
|
|
145
|
+
};
|
|
146
|
+
if (agent === "claude")
|
|
147
|
+
removeIfExists("AGENTS.md");
|
|
148
|
+
else if (agent === "codex")
|
|
149
|
+
removeIfExists("CLAUDE.md");
|
|
150
|
+
else if (agent === "none") {
|
|
151
|
+
removeIfExists("AGENTS.md");
|
|
152
|
+
removeIfExists("CLAUDE.md");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Composes the project into a sibling staging directory, then renames it
|
|
157
|
+
* onto the destination in one atomic step (spec #6). On any failure the
|
|
158
|
+
* staging directory is removed and the destination is left untouched.
|
|
159
|
+
*/
|
|
160
|
+
export function composeProject(params) {
|
|
161
|
+
const { plan, templateDir, randomSuffix, beforeCopyFile } = params;
|
|
162
|
+
const sourceDir = resolveTemplateDirPath(templateDir);
|
|
163
|
+
// An existing (empty) destination is filled in place: renaming over it would
|
|
164
|
+
// swap the directory inode out from under a shell whose cwd is that directory.
|
|
165
|
+
const destinationExists = fs.existsSync(plan.destinationPath);
|
|
166
|
+
const stagingDir = path.join(destinationExists ? plan.destinationPath : path.dirname(plan.destinationPath), `.create-zudo-circuit-doc-staging-${randomSuffix()}`);
|
|
167
|
+
try {
|
|
168
|
+
copyTemplateTree(sourceDir, stagingDir, placeholdersFromPlan(plan), beforeCopyFile, "");
|
|
169
|
+
assertNoPlaceholdersRemain(stagingDir);
|
|
170
|
+
applyAgentFilter(stagingDir, plan.agent);
|
|
171
|
+
if (plan.runtimeSpec !== undefined)
|
|
172
|
+
applyRuntimeSpec(stagingDir, plan.runtimeSpec);
|
|
173
|
+
if (destinationExists) {
|
|
174
|
+
for (const entry of fs.readdirSync(stagingDir)) {
|
|
175
|
+
fs.renameSync(path.join(stagingDir, entry), path.join(plan.destinationPath, entry));
|
|
176
|
+
}
|
|
177
|
+
fs.rmdirSync(stagingDir);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
fs.renameSync(stagingDir, plan.destinationPath);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
185
|
+
throw new CliError(`Failed to create project at "${plan.destinationPath}": ${error.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const SAFE_UNQUOTED_RE = /^[A-Za-z0-9_.\-/]+$/;
|
|
2
|
+
/** Quotes `value` for a POSIX shell only when it contains characters that would need it. */
|
|
3
|
+
export function shellQuote(value) {
|
|
4
|
+
if (SAFE_UNQUOTED_RE.test(value))
|
|
5
|
+
return value;
|
|
6
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Throws a CliUsageError if `name` does not satisfy the npm package-name grammar (spec #3). */
|
|
2
|
+
export declare function validateName(name: string): void;
|
|
3
|
+
/** Throws a CliUsageError if `title` is not 1-80 printable characters excluding quoting-sensitive symbols (spec #3). */
|
|
4
|
+
export declare function validateTitle(title: string): void;
|
|
5
|
+
/** Throws a CliUsageError if `library` does not match the KiCad library-name grammar (spec #3). */
|
|
6
|
+
export declare function validateLibrary(library: string): void;
|
|
7
|
+
/**
|
|
8
|
+
* Throws a CliUsageError if `spec` is not usable as the
|
|
9
|
+
* `@takazudo/zudo-circuit-doc` dependency spec (spec #58). Accepts a semver
|
|
10
|
+
* range/dist-tag, or a `file:` spec whose path is absolute. A relative
|
|
11
|
+
* `file:` path is rejected rather than rebased: `doc/package.json` sits one
|
|
12
|
+
* directory deeper than `package.json`, so one relative string cannot be
|
|
13
|
+
* correct in both places without silently pointing `doc/` at the wrong
|
|
14
|
+
* ancestor — pass an absolute path instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateRuntimeSpec(spec: string): void;
|
|
17
|
+
/** "my-thing" -> "My Thing". Used as the default --title derived from --name. */
|
|
18
|
+
export declare function titleCaseFromName(name: string): string;
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { CliUsageError } from "./errors.js";
|
|
3
|
+
const UNSCOPED_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
|
|
4
|
+
const SCOPED_NAME_RE = /^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/;
|
|
5
|
+
/** Throws a CliUsageError if `name` does not satisfy the npm package-name grammar (spec #3). */
|
|
6
|
+
export function validateName(name) {
|
|
7
|
+
if (name.length < 1 || name.length > 214) {
|
|
8
|
+
throw new CliUsageError(`Invalid --name "${name}": must be 1-214 characters long.`);
|
|
9
|
+
}
|
|
10
|
+
if (name === "node_modules" || name.includes("node_modules")) {
|
|
11
|
+
throw new CliUsageError(`Invalid --name "${name}": must not be or contain "node_modules".`);
|
|
12
|
+
}
|
|
13
|
+
if (!UNSCOPED_NAME_RE.test(name) && !SCOPED_NAME_RE.test(name)) {
|
|
14
|
+
throw new CliUsageError(`Invalid --name "${name}": must match the npm package-name grammar (lowercase, optionally scoped).`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Excluded because the title is substituted verbatim into TS string literals
|
|
18
|
+
// and MDX frontmatter — these characters would break that quoting.
|
|
19
|
+
const TITLE_FORBIDDEN_RE = /["'`\\$<>{}]/;
|
|
20
|
+
// eslint-disable-next-line no-control-regex
|
|
21
|
+
const CONTROL_CHAR_RE = /[\u0000-\u001f\u007f]/;
|
|
22
|
+
/** Throws a CliUsageError if `title` is not 1-80 printable characters excluding quoting-sensitive symbols (spec #3). */
|
|
23
|
+
export function validateTitle(title) {
|
|
24
|
+
if (title.length < 1 || title.length > 80) {
|
|
25
|
+
throw new CliUsageError(`Invalid --title "${title}": must be 1-80 characters long.`);
|
|
26
|
+
}
|
|
27
|
+
if (CONTROL_CHAR_RE.test(title)) {
|
|
28
|
+
throw new CliUsageError(`Invalid --title "${title}": must not contain control characters.`);
|
|
29
|
+
}
|
|
30
|
+
if (TITLE_FORBIDDEN_RE.test(title)) {
|
|
31
|
+
throw new CliUsageError(`Invalid --title "${title}": must not contain any of " ' \` \\ $ < > { }.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const LIBRARY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
35
|
+
/** Throws a CliUsageError if `library` does not match the KiCad library-name grammar (spec #3). */
|
|
36
|
+
export function validateLibrary(library) {
|
|
37
|
+
if (!LIBRARY_RE.test(library)) {
|
|
38
|
+
throw new CliUsageError(`Invalid --library "${library}": must match ${LIBRARY_RE.source}.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line no-control-regex
|
|
42
|
+
const RUNTIME_SPEC_CONTROL_CHAR_RE = /[\u0000-\u001f\u007f]/;
|
|
43
|
+
// Permissive semver-range grammar (npm dist-tags like "latest"/"next" also
|
|
44
|
+
// match): digits, dots, the usual range operators, hyphen ranges and OR.
|
|
45
|
+
// This is deliberately not a full semver-range parser — it only rejects
|
|
46
|
+
// values that could not possibly be a range or a "file:" spec.
|
|
47
|
+
const SEMVER_RANGE_RE = /^[0-9A-Za-z.\-+^~*<>=|\s]+$/;
|
|
48
|
+
/**
|
|
49
|
+
* Throws a CliUsageError if `spec` is not usable as the
|
|
50
|
+
* `@takazudo/zudo-circuit-doc` dependency spec (spec #58). Accepts a semver
|
|
51
|
+
* range/dist-tag, or a `file:` spec whose path is absolute. A relative
|
|
52
|
+
* `file:` path is rejected rather than rebased: `doc/package.json` sits one
|
|
53
|
+
* directory deeper than `package.json`, so one relative string cannot be
|
|
54
|
+
* correct in both places without silently pointing `doc/` at the wrong
|
|
55
|
+
* ancestor — pass an absolute path instead.
|
|
56
|
+
*/
|
|
57
|
+
export function validateRuntimeSpec(spec) {
|
|
58
|
+
// 1024, not 200 like the other options: a "file:" spec carries a full
|
|
59
|
+
// absolute path (e.g. verify-pack's tmpdir tarball path), which can run
|
|
60
|
+
// considerably longer than a semver range on some platforms/CI runners.
|
|
61
|
+
if (spec.length < 1 || spec.length > 1024) {
|
|
62
|
+
throw new CliUsageError(`Invalid --runtime-spec "${spec}": must be 1-1024 characters long.`);
|
|
63
|
+
}
|
|
64
|
+
if (RUNTIME_SPEC_CONTROL_CHAR_RE.test(spec)) {
|
|
65
|
+
throw new CliUsageError(`Invalid --runtime-spec "${spec}": must not contain control characters.`);
|
|
66
|
+
}
|
|
67
|
+
if (spec.startsWith("file:")) {
|
|
68
|
+
const filePath = spec.slice("file:".length);
|
|
69
|
+
if (filePath.length === 0 || !path.isAbsolute(filePath)) {
|
|
70
|
+
throw new CliUsageError(`Invalid --runtime-spec "${spec}": a "file:" spec must use an absolute path (relative "file:" paths are not supported, since doc/package.json sits one directory deeper than package.json).`);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!SEMVER_RANGE_RE.test(spec)) {
|
|
75
|
+
throw new CliUsageError(`Invalid --runtime-spec "${spec}": must be a semver range/dist-tag or a "file:" spec with an absolute path.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** "my-thing" -> "My Thing". Used as the default --title derived from --name. */
|
|
79
|
+
export function titleCaseFromName(name) {
|
|
80
|
+
return name
|
|
81
|
+
.split(/[-_]+/)
|
|
82
|
+
.filter((word) => word.length > 0)
|
|
83
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
84
|
+
.join(" ");
|
|
85
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the package version from package.json at runtime, resolved relative
|
|
3
|
+
* to `baseUrl` (pass `import.meta.url` from src or dist — both sit one
|
|
4
|
+
* directory below the package root, spec #9).
|
|
5
|
+
*/
|
|
6
|
+
export declare function readPackageVersion(baseUrl: string | URL): string;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
/**
|
|
4
|
+
* Reads the package version from package.json at runtime, resolved relative
|
|
5
|
+
* to `baseUrl` (pass `import.meta.url` from src or dist — both sit one
|
|
6
|
+
* directory below the package root, spec #9).
|
|
7
|
+
*/
|
|
8
|
+
export function readPackageVersion(baseUrl) {
|
|
9
|
+
const packageJsonUrl = new URL("../package.json", baseUrl);
|
|
10
|
+
const contents = readFileSync(fileURLToPath(packageJsonUrl), "utf8");
|
|
11
|
+
const pkg = JSON.parse(contents);
|
|
12
|
+
return pkg.version;
|
|
13
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-zudo-circuit-doc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Create a new zudo-circuit-doc project.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Takazudo/zudo-circuit-doc.git",
|
|
10
|
+
"directory": "packages/create-zudo-circuit-doc"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Takazudo/zudo-circuit-doc/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"create",
|
|
17
|
+
"scaffold",
|
|
18
|
+
"circuit",
|
|
19
|
+
"zudo-circuit-doc",
|
|
20
|
+
"cli"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22.18.0"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"create-zudo-circuit-doc": "./bin/create-zudo-circuit-doc.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"bin",
|
|
33
|
+
"dist",
|
|
34
|
+
"templates",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"CHANGELOG.md",
|
|
38
|
+
"!dist/**/*.test.*"
|
|
39
|
+
],
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.9.0",
|
|
42
|
+
"@types/node": "^22.0.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.build.json",
|
|
46
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
47
|
+
"test": "node --experimental-strip-types --test \"test/**/*.test.ts\""
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: circuit-spec-integration
|
|
3
|
+
description: Audit behavior that spans more than one exact component, such as shared supplies, signal boundaries, startup and reset states, protection, sensing, thermal coupling, firmware assumptions and as-built state. Use whenever a question, substitution or design change involves several component records or a stage beyond a single datasheet.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Circuit specification integration
|
|
7
|
+
|
|
8
|
+
Individually valid components do not prove the whole circuit. This skill handles the cross-component rules in `references/rules.json`. The canonical workflow is [circuit/WORKFLOW.md](../../../circuit/WORKFLOW.md).
|
|
9
|
+
|
|
10
|
+
1. Run `pnpm exec zudo-circuit-doc validate` (or `pnpm circuit:check`) and note pre-existing failures.
|
|
11
|
+
2. Read `references/rules.json` and load every exact owner bundle named by each matching rule, through the component-spec-audit skill. Resolve manufacturer, MPN and order codes independently; reject conflicts, same-name wrong-vendor parts and ambiguous bare names. Load subordinate records directly.
|
|
12
|
+
3. Use the listed fact IDs with their exact conditions and locators. An `UNSOURCED` fact cannot close a domain, and design connectivity cannot prove programmed, assembled or measured state.
|
|
13
|
+
4. For a substitution or design change, report the affected rule, the raw facts, recalculated margins with their dependencies, the evidence stages still missing, and one honest verdict. Do not change design files silently.
|
|
14
|
+
5. Recompute conditioned calculations from their inputs. The validator checks the arithmetic, not the units or the reading of the source table; check those yourself.
|
|
15
|
+
|
|
16
|
+
## Adding a rule
|
|
17
|
+
|
|
18
|
+
The rule list may be empty; `{ "schema_version": 1, "rules": [] }` is valid. Add a rule **only when a real interaction exists** between exact components. A rule names its `rule_id`, `domain`, `record_ids`, `fact_ids` (owned by those records), `conditions`, `verdict`, `refusal` text and any `conditioned_calculations`.
|
|
19
|
+
|
|
20
|
+
An evidence chain lists stages in order, from the official source and the conditioned requirement through the netlist, symbol and footprint, PCB orientation, BOM and placement, as-built and programmed state, to bench results. Every stage stays `OPEN` with empty `fact_ids` until real evidence exists. A completed early stage never implies a later one, and a generated netlist is never `CONFIRMED`.
|
|
21
|
+
|
|
22
|
+
After editing rules, update the integration count lock `expect.integrationRules` in `circuit/publication/selection.json`, then run `pnpm circuit:generate` and `pnpm check`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: component-spec-audit
|
|
3
|
+
description: Audit exact electronic-component identities and evidence, and run the end-to-end workflow for adding or replacing a component. Use whenever circuit, schematic, PCB, BOM, firmware, bring-up, substitution or documentation work could depend on a component rating, pin, package, source, CAD asset, publication or interaction.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Component spec audit
|
|
7
|
+
|
|
8
|
+
Protect the design from plausible-looking but wrong component claims. Manufacturer documents are the authority for component behavior; the inventory is the list of orderable identities; each owner bundle is the evidence for one exact component. The canonical workflow is [circuit/WORKFLOW.md](../../../circuit/WORKFLOW.md); this skill is its entry for component work.
|
|
9
|
+
|
|
10
|
+
## Audit existing components
|
|
11
|
+
|
|
12
|
+
1. Run the validator before relying on any record:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pnpm exec zudo-circuit-doc validate
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
(`pnpm circuit:check` runs the same command.) Note pre-existing failures before editing, and read the `SCOPE:` line: with the manual inventory provider, placements are declared, not bound to a schematic.
|
|
19
|
+
2. Resolve the component through `references/inventory.json` by exact manufacturer and complete MPN, supplier order code, function or placement, then load the owner bundle named by the line's `owner_skill`. A bare base name is not an identity. Load subordinate records directly; never answer a subordinate query from its parent.
|
|
20
|
+
3. Read the owner's `manifest.json`, `sources.json`, `facts.json`, `coverage.json`, `routing.json`, `interactions.json` and `pin-map.json`. Standalone and subordinate records get the same rigor.
|
|
21
|
+
4. Keep the contract's distinctions: source authority versus availability, fact class, provenance, conditions, calculation dependencies and the six verdicts, spelled exactly. The frozen prose is the package's [contract.md](../../../node_modules/@takazudo/zudo-circuit-doc/contract/contract.md).
|
|
22
|
+
5. Cross-check claims against the pin map and the current design state. For effects that span components, also load [circuit-spec-integration](../circuit-spec-integration/SKILL.md).
|
|
23
|
+
6. If an authoritative source cannot be retrieved, or its retained extract does not support the claim, report `SOURCE UNAVAILABLE` and `UNSOURCED`. Never reconstruct a fact from memory or from a same-name or family part.
|
|
24
|
+
7. Report exact fact IDs, source IDs, locators, conditions, calculations and one allowed verdict. Keep proposed design changes separate from the audit result.
|
|
25
|
+
|
|
26
|
+
## Add or replace a component
|
|
27
|
+
|
|
28
|
+
Follow [the new-component workflow](references/new-component-workflow.md) in order. It covers identity, the owner bundle, the manual inventory profile, optional CAD, the publication selection, regeneration and checks, and it is the only onboarding procedure; do not create a separate catalog-update skill.
|
|
29
|
+
|
|
30
|
+
Start a new owner bundle with:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pnpm exec zudo-circuit-doc new-component SUFFIX
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Replace every placeholder value the template contains; the validator fails on any that remain. Keep downloads in the ignored `.circuit-cache/sources/` directory, retain short normalized extracts in `sources.json`, and use `pnpm exec zudo-circuit-doc validate --online` or `--refresh-source SOURCE_ID` only for an explicit source refresh.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Frozen component-spec contract (version 1)
|
|
2
|
+
|
|
3
|
+
The central inventory is generated-spec identity truth; an owner record is evidence truth. One inventory line represents one nonblank LCSC/orderable identity and may have many placements. Bare-copper pogo and test pads remain explicit exclusions.
|
|
4
|
+
|
|
5
|
+
Every standalone or subordinate record uses the same files and schema. Subordinates receive their own record, source, fact, interaction, routing, coverage, and pin-map IDs; parentage changes organization, never rigor.
|
|
6
|
+
|
|
7
|
+
A subordinate's `parent_record_id` resolves to a standalone record in the same bundle. Each record lists exactly all and only its assigned source, fact, and interaction IDs, has direct positive/negative routing plus coverage and pin-map data, and owns no unexplained orphan data. Each open domain is a named entry matched one-for-one by `OPEN` coverage with an explicit reason.
|
|
8
|
+
|
|
9
|
+
Every coverage entry also carries a machine-readable `blocking_fact_ids` array. For an `OPEN` entry it names the exact same-record facts whose non-PASS verdicts keep the domain open; each member must carry a verdict of `UNSOURCED` or `NEEDS BENCH`, or cite a `SOURCE UNAVAILABLE` source. The converse is enforced too: whenever any fact listed in `fact_ids` meets that same blocking test, `blocking_fact_ids` must name at least one of them. It may be empty only when no listed fact addresses the domain in that sense — a `NOT APPLICABLE` fact never blocks, so an `OPEN` entry whose only evidence is `NOT APPLICABLE` correctly keeps the array empty. Independently, a reason whose text claims evidence is unavailable, lower-authority, or `UNSOURCED` must cite those facts through `blocking_fact_ids` rather than leave the claim unverifiable.
|
|
10
|
+
|
|
11
|
+
## Evidence retention
|
|
12
|
+
|
|
13
|
+
A source lock records document title/number, revision/date, primary and optional alternate authoritative URLs, retrieval date, authority class, SHA-256, physical PDF page index, printed label, and exact section/table/figure/row locator. Retain a normalized, minimal evidence extract beside the locator so a reviewer can audit a critical claim without redistributing a PDF. An inaccessible source is `SOURCE UNAVAILABLE`; its SHA-256 is always the all-zero sentinel. Its extract may remain for audit history but cannot promote a new claim or invite a memory-based fallback.
|
|
14
|
+
|
|
15
|
+
Fact classes are `ABSOLUTE_MAXIMUM`, `RECOMMENDED_OPERATION`, `GUARANTEED_ELECTRICAL`, `TYPICAL_CURVE`, `TRANSIENT`, `PROTECTION_STANDOFF`, `PROTECTION_BREAKDOWN`, `PROTECTION_CLAMP`, `THERMAL_SOA`, and `PROJECT_STATE`. Provenance is `PRIMARY-SPEC`, narrowly scoped `DISTRIBUTOR-IDENTITY`, `REFERENCE-DESIGN`, `CALCULATED`, `PROJECT-CHOICE`, `BENCH-OBSERVED`, or `UNVERIFIED`. Every quantitative fact carries an explicit unit and conditions; textual facts use unit `NONE`. Calculated facts list raw fact IDs and an evaluable arithmetic expression. Cycles are invalid.
|
|
16
|
+
|
|
17
|
+
Only these verdicts are valid: `PASS - primary-source confirmed`, `CONFIRMED - distributor identity only`, `BLOCKER - deterministic spec violation`, `NEEDS BENCH`, `UNSOURCED`, and `NOT APPLICABLE`. Distributor identity confirmation is valid only for an `AVAILABLE` `DISTRIBUTOR_IDENTITY` source and a `PROJECT_STATE` identity fact; it cannot support electrical/thermal limits, calculations, primary PASS, or deterministic BLOCKER.
|
|
18
|
+
|
|
19
|
+
`PRIMARY-SPEC` may receive a PASS only from an `AVAILABLE` `MANUFACTURER_PRIMARY` source. A calculated PASS is trusted only when every raw leaf in its dependency closure is such a primary-source PASS. Its expression names exactly the transformed fact IDs in `depends_on`; undeclared, unused, self, missing, or cyclic dependencies fail.
|
|
20
|
+
|
|
21
|
+
Unknown identity, source, open harness/mechanical domains, unavailable URLs, and unexplained coverage gaps stay explicit. Generic-family, distributor, or same-name cross-vendor data never silently stands in for the exact orderable.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"contract": "For each line, the validator constructs literal positive queries for exact MPN, exact LCSC, manufacturer plus MPN, and function plus MPN; every query must resolve directly and uniquely. The negative query must resolve no line.",
|
|
4
|
+
"cases": []
|
|
5
|
+
}
|
package/templates/default/.claude/skills/component-spec-audit/references/new-component-workflow.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# New component workflow
|
|
2
|
+
|
|
3
|
+
Run these steps from the project root, in order. This checklist owns a new or replacement component from its exact identity through the committed, published reference. It is the detailed form of Workflow B in [circuit/WORKFLOW.md](../../../../circuit/WORKFLOW.md). It does not authorize an unresolved electrical or firmware design change.
|
|
4
|
+
|
|
5
|
+
## Preconditions
|
|
6
|
+
|
|
7
|
+
Resolve before editing:
|
|
8
|
+
|
|
9
|
+
- manufacturer, complete MPN (suffix and package variant), supplier order codes;
|
|
10
|
+
- intended function, placements (board and reference designator) and population (fitted, not fitted, hand-fitted, external);
|
|
11
|
+
- the source that will establish identity and behavior;
|
|
12
|
+
- whether the part needs CAD assets on a board, and whether its page and previews will be published.
|
|
13
|
+
|
|
14
|
+
Do not invent a reference designator, pin, fact, order code or document kind to make the workflow progress. Stop and record the missing design or review decision in next actions instead.
|
|
15
|
+
|
|
16
|
+
Run `pnpm circuit:check` first and note what already fails.
|
|
17
|
+
|
|
18
|
+
## 1. Lock the identity
|
|
19
|
+
|
|
20
|
+
The configured inventory provider (`inventoryProvider` in `circuit.config.ts`) decides how identity is locked.
|
|
21
|
+
|
|
22
|
+
### Manual provider (the default)
|
|
23
|
+
|
|
24
|
+
Edit `.claude/skills/component-spec-audit/references/inventory.json`:
|
|
25
|
+
|
|
26
|
+
1. Add one line per orderable identity, with the same keys as existing lines (`line_id`, `mpn`, `manufacturer`, `lcsc`, `package`, `dnp`, `owner_skill`, `identity_state`, `source_state`, `function`, `placements`; `mounting` defaults to `pcb`). The validator names any key that is missing.
|
|
27
|
+
2. Identity is a unique `line_id` plus a unique (manufacturer, complete MPN) pair. Two manufacturers may share an MPN; each line then resolves only with its manufacturer qualifier.
|
|
28
|
+
3. `lcsc` must be present. Use `""` unless you read a C-number from the supplier's listing for this exact part. Never fabricate, guess or reuse a C-number from a similar part.
|
|
29
|
+
4. Optional `suppliers: [{ "supplier": "...", "order_code": "..." }]` records other order codes. They are display-only and create no routing aliases.
|
|
30
|
+
5. `placements` may be `[]`. Placements are **declared, not verified**: the manual provider does not bind them to a schematic, and the `SCOPE:` line printed by the validator says so. Repeat that limit in your report.
|
|
31
|
+
6. `identity_state` is `VERIFIED` or `UNRESOLVED`; `source_state` is `AVAILABLE` or `SOURCE UNAVAILABLE`. Keep them consistent with the owner bundle; the validator warns when the summary lags the evidence.
|
|
32
|
+
7. Update the reviewed `assertions` counts (`orderable_lines`, `fitted_lines`, `dnp_or_hand_fit_lines`) by hand. Do not change a count only to make a check pass.
|
|
33
|
+
|
|
34
|
+
### Generator provider (optional)
|
|
35
|
+
|
|
36
|
+
When the project configures a generator-based provider, the generator specification is the identity lock: add the exact part to the configured spec, regenerate the schematic with the project's generator, commit the spec and its output together, and then reconcile the inventory line against it. Do not hand-edit an inventory line to disagree with its generator.
|
|
37
|
+
|
|
38
|
+
## 2. Build the evidence owner
|
|
39
|
+
|
|
40
|
+
1. Create the bundle from the package template:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pnpm exec zudo-circuit-doc new-component SUFFIX
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This creates `.claude/skills/component-SUFFIX/` and refuses an existing directory. Use a suffix derived from the exact part.
|
|
47
|
+
2. Replace **every** placeholder in all eight files. Fill `manifest.json`, `sources.json`, `facts.json`, `coverage.json`, `routing.json`, `interactions.json` and `pin-map.json` from audited sources (Workflow C for acquisition). Record units, conditions, provenance, verdicts, calculation dependencies, open domains with their `blocking_fact_ids`, routing cases and the real pin map. Retain short normalized extracts, not whole documents.
|
|
48
|
+
3. Keep the bundle `SKILL.md` frontmatter valid: `name` equals the directory name, and `description` is at least 80 characters and says when to use it.
|
|
49
|
+
4. The record's `mpn`, `manufacturer`, `lcsc` and `package` must equal the inventory line; `owner_skill` on the line names this bundle.
|
|
50
|
+
5. Add the line's positive and negative cases to `.claude/skills/component-spec-audit/references/direct-routing.json`. Add a vendor qualifier to `external-vendor-qualifiers.json` only when routing needs one.
|
|
51
|
+
6. When the part affects another component or domain, add or update a rule in `.claude/skills/circuit-spec-integration/references/rules.json` (see the circuit-spec-integration skill). Evidence-chain stages stay `OPEN` until real evidence exists.
|
|
52
|
+
|
|
53
|
+
Run `pnpm circuit:check` now; fix every failure the new bundle introduced.
|
|
54
|
+
|
|
55
|
+
## 3. CAD assets (optional)
|
|
56
|
+
|
|
57
|
+
Only when the part goes on a board and `cad.enabled` is `true` in `circuit.config.ts`. With CAD disabled, the pin-asset check is reported as SKIPPED; say so in the report rather than implying pins were checked.
|
|
58
|
+
|
|
59
|
+
1. Acquire the symbol, footprint and model (Workflow D): a pinned KiCad library release tag, the manufacturer, or `easyeda2kicad` for an LCSC-listed part. Import into `.circuit-cache/cad/` first, never straight into the libraries.
|
|
60
|
+
2. Merge only this part's symbol into the configured symbol library. Never overwrite a shared multi-symbol library with importer output.
|
|
61
|
+
3. Place the footprint in the configured footprint roots. If the config names both a master root and a library root, keep the two `.kicad_mod` copies byte-identical (`cmp -s`).
|
|
62
|
+
4. Place the WRL model in the configured model root — it is required and is what gets published. A STEP file is optional; if you add one, it must share the WRL's basename (a mismatched pair fails the check). Review the footprint's model reference and its offset, rotation and scale. The web viewer renders WRL only; if no WRL exists for the pinned library release, record the model as unavailable rather than converting one.
|
|
63
|
+
5. The pin map's `symbol` and `footprint` must exist in the configured libraries, and symbol pin numbers, footprint pad numbers and pin-map pins must be identical sets. Check them against the datasheet yourself; the validator checks only agreement.
|
|
64
|
+
6. Write the receipt `circuit/cad-receipts/ASSET_ID.receipt.json` from `circuit/templates/cad-asset-receipt.json`, with the fidelity class (`exact-vendor`, `family`, `derived`, `unavailable`) and the evidence for it.
|
|
65
|
+
|
|
66
|
+
## 4. Choose what becomes public
|
|
67
|
+
|
|
68
|
+
Edit `circuit/publication/selection.json` in the same change. Nothing is published unless it is listed.
|
|
69
|
+
|
|
70
|
+
1. Add the record to `recordIds`, every public source to `sourceIds`, and every approved outbound URL to `linkableSourceIds`.
|
|
71
|
+
2. Add exactly one entry to `documentSelections` for the record, with its audited document source and a truthful `documentKind` (`datasheet`, `specification` or `drawing`). Inspect the retrieved content first: a product page or HTML denial is not a datasheet because its URL ends in `.pdf`. The bytes must start with `%PDF-`, and the title and part list must cover the exact MPN.
|
|
72
|
+
3. Update the reviewed `expect` locks: `records`, `sources`, `integrationRules` and `packages` (the number of selected footprint/model packages; `0` is valid). Keep them explicit; never infer them.
|
|
73
|
+
4. A file deliberately published under `doc/public/` (for example a redistributable drawing) needs an entry in `circuit/publication/assets.json` with its path and reason. Raw sources and CAD files otherwise stay out of `doc/public/`.
|
|
74
|
+
|
|
75
|
+
## 5. Regenerate, review, commit
|
|
76
|
+
|
|
77
|
+
1. If CAD previews were selected, regenerate and check them:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
pnpm previews:generate
|
|
81
|
+
pnpm exec zudo-circuit-doc footprints check
|
|
82
|
+
pnpm exec zudo-circuit-doc models
|
|
83
|
+
pnpm exec zudo-circuit-doc models --check
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Restart a running dev server after adding public assets.
|
|
87
|
+
2. Regenerate and check:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
pnpm circuit:generate
|
|
91
|
+
pnpm circuit:check
|
|
92
|
+
pnpm check
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
3. Build and run the post-build checks:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
pnpm build
|
|
99
|
+
pnpm check:site
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
4. Commit the evidence, inventory, routing, selection, receipts, generated pages, `circuit/generated/preflight.json` and previews together. Never hand-edit generated files. Review the generated diff with:
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
git add --intent-to-add -A -- doc/src/content/docs/components circuit/generated doc/public/assets/component-previews
|
|
106
|
+
git diff -- doc/src/content/docs/components circuit/generated doc/public/assets/component-previews
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
5. Open the new record page in the built site or dev server. A passing link check proves the route exists, not that it reads correctly.
|
|
110
|
+
|
|
111
|
+
## External (off-board) components
|
|
112
|
+
|
|
113
|
+
A purchased, hand-wired component that is not soldered to a PCB (a panel switch, a connector on a cable) is not a board part. Set inventory `mounting: external`, keep its footprint empty, and record its physical terminal numbers in the pin map's `footprint_pad`. Do not fabricate PCB pads or model geometry for it. Verify system wiring and its absence from the PCB assembly separately; both remain explicit checks.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Agent instructions
|
|
2
|
+
|
|
3
|
+
Read [circuit/WORKFLOW.md](circuit/WORKFLOW.md) first. It is the canonical workflow for this circuit project; this file only points to it.
|
|
4
|
+
|
|
5
|
+
- Exact-component evidence lives in the owner bundles under `.claude/skills/component-*/`, indexed by `.claude/skills/component-spec-audit/references/inventory.json`. Cross-component rules live in `.claude/skills/circuit-spec-integration/references/rules.json`.
|
|
6
|
+
- Run `pnpm circuit:check` before editing and again after.
|
|
7
|
+
- Never hand-edit the generated pages under `doc/src/content/docs/components/`; change the evidence and run `pnpm circuit:generate`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Agent instructions
|
|
2
|
+
|
|
3
|
+
Read [circuit/WORKFLOW.md](circuit/WORKFLOW.md) first. It is the canonical workflow for this circuit project; this file only points to it.
|
|
4
|
+
|
|
5
|
+
- Exact-component evidence lives in the owner bundles under `.claude/skills/component-*/`, indexed by `.claude/skills/component-spec-audit/references/inventory.json`. Cross-component rules live in `.claude/skills/circuit-spec-integration/references/rules.json`.
|
|
6
|
+
- Run `pnpm circuit:check` before editing and again after.
|
|
7
|
+
- Never hand-edit the generated pages under `doc/src/content/docs/components/`; change the evidence and run `pnpm circuit:generate`.
|