@rulecad/cad 0.1.1 → 0.1.3
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/AGENTS.md +56 -0
- package/README.md +1 -0
- package/bin/cad.mjs +23 -0
- package/lib/versions.mjs +5 -4
- package/package.json +4 -3
package/AGENTS.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Designing with rulecad: notes for AI agents
|
|
2
|
+
|
|
3
|
+
This project designs physical things with the `cad` command (rulecad). Every measurement is a variable, and every
|
|
4
|
+
variable says which rule it comes from. Follow these steps; they're what makes a design traceable and printable.
|
|
5
|
+
(`npx cad help` lists the commands; the full guide is @rulecad/cad's README.)
|
|
6
|
+
|
|
7
|
+
## A new part
|
|
8
|
+
1. **Rules first.** Find the facts the part depends on: standards, the thing it fits, the printer. Put shared ones in
|
|
9
|
+
`rules/<set>.json` with `kind`, `source` and `checked`. Research them, don't guess. Ask the person to measure what
|
|
10
|
+
can't be looked up, and record it as `"kind": "measured"`; something nobody has published goes in as
|
|
11
|
+
`"kind": "estimate"`, and the check will flag it.
|
|
12
|
+
2. `npx cad new <project>/<object>`, then write the variables in the .json and the shape in the .js. No numbers in
|
|
13
|
+
the .js other than 0, 1 and 2: `cad check` fails on any other.
|
|
14
|
+
3. `npx cad check`, then `npx cad preview`, and **look at the preview image** before saying it's done. Check the views
|
|
15
|
+
from below for anything that fits onto something else.
|
|
16
|
+
4. Mark the variables that matter to the person `show` (six at most, short `label`s), and the ones other parts depend
|
|
17
|
+
on `interface` (versions follow them). Give sensible `tweak` ranges to the ones they may want to change.
|
|
18
|
+
5. `npx cad export` for printing; `npx cad link --md` for a link to share.
|
|
19
|
+
|
|
20
|
+
## Changing a design
|
|
21
|
+
1. `npx cad history <project>/<object>` first: read the print log. A failed print is the reason for most changes:
|
|
22
|
+
fix the rule or variable it points at (a measured fit beats a community rule).
|
|
23
|
+
2. Change, then check, preview and look, as for a new part.
|
|
24
|
+
3. `npx cad release <project>/<object> -m "what changed, for the person" --dry-run` shows the bump and why. If it's
|
|
25
|
+
major, tell the person which interface values changed: earlier prints may no longer fit.
|
|
26
|
+
4. Release only when the person agrees. Don't use `--force` without asking.
|
|
27
|
+
|
|
28
|
+
## Shapes
|
|
29
|
+
- Z is up, the bed is z = 0: model the part in its print orientation, and describe it in `orientation`.
|
|
30
|
+
- Walls are whole numbers of print lines (`x1c.line * 3`, or your printer's set), heights whole numbers of layers.
|
|
31
|
+
- Holes and slots get the printer's hole compensation; parts that slide together get its fit clearance. Chamfer
|
|
32
|
+
bottom edges.
|
|
33
|
+
- An overhang warning means supports: say so in `orientation`, or change the shape or orientation.
|
|
34
|
+
- Something printed under the part and removed afterwards is a named `support` part (`return { cap, pillar }`, both
|
|
35
|
+
described in the .json's `parts`), not part of the shape. Look at `cad preview --explode` too.
|
|
36
|
+
|
|
37
|
+
## Alternatives, layouts, colours
|
|
38
|
+
- To try an alternative, add a **variant** (named inputs in the .json) rather than a copy of the object; `cad compare`
|
|
39
|
+
shows two side by side. Remove trial variants before a release.
|
|
40
|
+
- What differs key by key on a keyboard belongs in a **layout file**, read through `per`. `cad import-kle` brings
|
|
41
|
+
one in from keyboard-layout-editor.com.
|
|
42
|
+
- A part in another colour is a part with a `filament` from `project.json`: the 3MF puts it on its own AMS slot.
|
|
43
|
+
- Fine detail a screen doesn't need goes in `display` settings (fewer segments, no legends).
|
|
44
|
+
|
|
45
|
+
## Fits
|
|
46
|
+
- To check how parts go together, make a `fit` assembly with the thing it fits as a `reference` object, and `fits`
|
|
47
|
+
with ranges from rules. An overlap or gap outside its range is a finding: report it with the numbers, and never
|
|
48
|
+
widen a range to make it pass without saying why.
|
|
49
|
+
- Build bigger things from checked assemblies used as items, rather than placing their objects again.
|
|
50
|
+
|
|
51
|
+
## Talking to the person
|
|
52
|
+
- Give links as one labelled line (`npx cad link <project>/<object> --md`), never a raw URL. If it says
|
|
53
|
+
"(not a release)", say so.
|
|
54
|
+
- Say what the checks found, with numbers. Ask before anything that leaves the computer: releases with `--push`,
|
|
55
|
+
ordering boards, publishing.
|
|
56
|
+
- Designs are code: only run designs you or the person trust. Links and rule files are data and never run.
|
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ npx cad rules bathroom/soap-tray # every variable, its rule and
|
|
|
35
35
|
npx cad check bathroom/soap-tray --set width=60 # change variables that have a tweak range
|
|
36
36
|
npx cad variants keyboard/keycap # named sets of inputs: keyboard/keycap:legend-f
|
|
37
37
|
npx cad compare keyboard/keycap dsa legend-f # two variants (or releases) side by side
|
|
38
|
+
npx cad import-kle layout.json keyboard/mine # a layout from keyboard-layout-editor.com, into layouts/
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
The keyboard examples below come from the project rulecad grew out of: a keycap, a switch plate, a case and their
|
package/bin/cad.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { execFileSync } from 'node:child_process';
|
|
6
6
|
import { fmt, loadRuleSet } from '../lib/rules.mjs';
|
|
7
7
|
import { WS, TOOL, paths, config, printerOf } from '../lib/workspace.mjs';
|
|
8
|
+
import { fromKLE } from '@rulecad/keyboard-layout';
|
|
8
9
|
import { target, hasTarget } from '../lib/targets.mjs';
|
|
9
10
|
import { locate, build, pick, lint, readObject, printer, pcbEngine } from '../lib/design.mjs';
|
|
10
11
|
import { git, tags, bump, bigger, newer, compare, snapshot, materialise, files, prints, printsFile, engineId } from '../lib/versions.mjs';
|
|
@@ -34,6 +35,8 @@ const VIEW = process.env.CAD_VIEW_URL || config.viewUrl;
|
|
|
34
35
|
const HELP = `cad: JSCAD designs you can trace, check, share and print (rulecad)
|
|
35
36
|
|
|
36
37
|
cad new <project>/<object> start a design from the template (makes the project if needed)
|
|
38
|
+
cad import-kle <file> <project>/<name> a keyboard layout from keyboard-layout-editor.com, into layouts/
|
|
39
|
+
cad agents AGENTS.md: how an AI agent should design in this workspace
|
|
37
40
|
cad rules <project>/<object> every variable: value, the rule behind it, its source
|
|
38
41
|
cad check <project>/<object> raw numbers in the design, then printability (exit 1 on a failure)
|
|
39
42
|
cad preview <project>/<object> four views as a PNG in .preview/ (prints the path)
|
|
@@ -96,6 +99,26 @@ async function main() {
|
|
|
96
99
|
return console.log(`made projects/${project}/objects/${name}.{js,json}`);
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
if (cmd === 'agents') { // the notes for AI agents, into this workspace (AGENTS.md, and CLAUDE.md pointing at it)
|
|
103
|
+
const to = path.join(WS, 'AGENTS.md');
|
|
104
|
+
fs.copyFileSync(path.join(TOOL, 'AGENTS.md'), to);
|
|
105
|
+
const claude = path.join(WS, 'CLAUDE.md');
|
|
106
|
+
if (!fs.existsSync(claude)) fs.writeFileSync(claude, '@AGENTS.md\n');
|
|
107
|
+
else if (!fs.readFileSync(claude, 'utf8').includes('@AGENTS.md')) fs.appendFileSync(claude, '\n@AGENTS.md\n');
|
|
108
|
+
return console.log(`wrote ${path.relative(process.cwd(), to) || 'AGENTS.md'}: how an AI agent should design here (CLAUDE.md includes it)`);
|
|
109
|
+
}
|
|
110
|
+
if (cmd === 'import-kle') { // a layout from keyboard-layout-editor.com into a project's layouts/
|
|
111
|
+
const [project, name] = String(args[2] || '').split('/');
|
|
112
|
+
if (!ref || !fs.existsSync(ref) || !project || !/^[a-z][a-z0-9-]*$/.test(name || '')) throw new Error('usage: cad import-kle <layout.json> <project>/<name> (the JSON download, or a file with its Raw data)');
|
|
113
|
+
const { layout, notes } = fromKLE(fs.readFileSync(ref, 'utf8'));
|
|
114
|
+
const to = path.join(paths.project(project), 'layouts', `${name}.json`);
|
|
115
|
+
if (fs.existsSync(to) && !opt('force')) throw new Error(`${path.relative(WS, to)} already exists (--force to replace it)`);
|
|
116
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
117
|
+
fs.writeFileSync(to, `${JSON.stringify({ title: layout.title || name, ...layout }, null, 1)}\n`);
|
|
118
|
+
console.log(`made ${path.relative(WS, to)}: ${layout.rows.length} rows, ${layout.rows.flat().filter((k) => !k.gap).length} keys`);
|
|
119
|
+
for (const n of notes) console.log(` note: ${n}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
99
122
|
if (cmd === 'status') return status(ref);
|
|
100
123
|
if (!ref) throw new Error(`usage: cad ${cmd} <project>/<object>`);
|
|
101
124
|
if (cmd === 'release') return ref.includes('/') ? releaseObject(locate(ref)) : opt('all') ? releaseAll(ref) : releaseProject(ref);
|
package/lib/versions.mjs
CHANGED
|
@@ -65,15 +65,16 @@ export function materialise(d, version) {
|
|
|
65
65
|
// ---------- what changed: compare() is @rulecad/versions' (exported above) ----------
|
|
66
66
|
|
|
67
67
|
// The board engine's identity: its code and the tscircuit it runs on. A board's release tag records it ("engine: …").
|
|
68
|
-
// A package's version, from the package.json above its entry point (some packages don't export their package.json)
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
// A package's version, from the package.json above its entry point (some packages don't export their package.json).
|
|
69
|
+
// from: where to look from (a file URL), when it's another package's dependency, as tscircuit is the board engine's
|
|
70
|
+
export function packageVersion(name, from = import.meta.url) {
|
|
71
|
+
for (let dir = path.dirname(createRequire(from).resolve(name)); dir !== path.dirname(dir); dir = path.dirname(dir)) {
|
|
71
72
|
const f = path.join(dir, 'package.json');
|
|
72
73
|
if (fs.existsSync(f)) { const pj = JSON.parse(fs.readFileSync(f, 'utf8')); if (pj.name === name) return pj.version; }
|
|
73
74
|
}
|
|
74
75
|
return '';
|
|
75
76
|
}
|
|
76
|
-
const tscircuitVersion = () => packageVersion('tscircuit');
|
|
77
|
+
const tscircuitVersion = () => packageVersion('tscircuit', import.meta.resolve('@rulecad/pcb'));
|
|
77
78
|
const engineHash = (code) => crypto.createHash('sha256').update(`${code.trimEnd()}\n${tscircuitVersion()}`).digest('hex').slice(0, 16);
|
|
78
79
|
export const engineId = () => engineHash(fs.readFileSync(new URL(import.meta.resolve('@rulecad/pcb')), 'utf8'));
|
|
79
80
|
// From a tag's message, or for releases before tags recorded it, from the engine's code in the repo at the tag
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulecad/cad",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The cad command: JSCAD designs from traceable rules, checked for printing and fit, versioned by what fits, shared as links and exported as Bambu 3MF.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"lib",
|
|
23
23
|
"templates",
|
|
24
24
|
"README.md",
|
|
25
|
-
"LICENSE"
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"AGENTS.md"
|
|
26
27
|
],
|
|
27
28
|
"license": "MIT",
|
|
28
29
|
"keywords": [
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@rulecad/variables": "^0.1.0",
|
|
46
|
-
"@rulecad/keyboard-layout": "^0.1.
|
|
47
|
+
"@rulecad/keyboard-layout": "^0.1.2",
|
|
47
48
|
"@rulecad/printcheck": "^0.1.0",
|
|
48
49
|
"@rulecad/fit": "^0.1.0",
|
|
49
50
|
"@rulecad/model-link": "^0.1.0",
|