forgecad 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.
Files changed (41) hide show
  1. package/dist/assets/{evalWorker-DuS4V-H5.js → evalWorker-1m873KWd.js} +107 -107
  2. package/dist/assets/{index-d60dJDhX.js → index-Dvz3nSDc.js} +342 -326
  3. package/dist/assets/{manifold-DEOPQqeC.js → manifold-C38sUiKu.js} +1 -1
  4. package/dist/assets/{manifold-cL7A7HeM.js → manifold-Dk2u-lhj.js} +1 -1
  5. package/dist/assets/{manifold-DbvY5u9x.js → manifold-rOWQW9fU.js} +1 -1
  6. package/dist/assets/{reportWorker-GZVKtf9S.js → reportWorker-Cj587shw.js} +129 -129
  7. package/dist/index.html +1 -1
  8. package/dist-cli/forgecad.js +204 -19
  9. package/dist-skill/SKILL.md +31 -4561
  10. package/dist-skill/docs/API/README.md +24 -0
  11. package/dist-skill/docs/API/guides/modeling-recipes.md +246 -0
  12. package/dist-skill/docs/API/internals/compiler.md +300 -0
  13. package/dist-skill/docs/API/internals/manifold.md +7 -0
  14. package/dist-skill/docs/API/model-building/README.md +31 -0
  15. package/dist-skill/docs/API/model-building/assembly.md +205 -0
  16. package/dist-skill/docs/API/model-building/coordinate-system.md +43 -0
  17. package/dist-skill/docs/API/model-building/entities.md +282 -0
  18. package/dist-skill/docs/API/model-building/geometry-conventions.md +104 -0
  19. package/dist-skill/docs/API/model-building/positioning.md +170 -0
  20. package/dist-skill/docs/API/model-building/reference.md +1936 -0
  21. package/dist-skill/docs/API/model-building/sheet-metal.md +180 -0
  22. package/dist-skill/docs/API/model-building/sketch-anchor.md +32 -0
  23. package/dist-skill/docs/API/model-building/sketch-booleans.md +101 -0
  24. package/dist-skill/docs/API/model-building/sketch-core.md +68 -0
  25. package/dist-skill/docs/API/model-building/sketch-extrude.md +57 -0
  26. package/dist-skill/docs/API/model-building/sketch-on-face.md +98 -0
  27. package/dist-skill/docs/API/model-building/sketch-operations.md +92 -0
  28. package/dist-skill/docs/API/model-building/sketch-path.md +70 -0
  29. package/dist-skill/docs/API/model-building/sketch-primitives.md +104 -0
  30. package/dist-skill/docs/API/model-building/sketch-transforms.md +60 -0
  31. package/dist-skill/docs/API/output/bom.md +53 -0
  32. package/dist-skill/docs/API/output/brep-export.md +82 -0
  33. package/dist-skill/docs/API/output/dimensions.md +62 -0
  34. package/dist-skill/docs/API/runtime/viewport.md +229 -0
  35. package/dist-skill/docs/CLI.md +672 -0
  36. package/dist-skill/docs/CODING.md +345 -0
  37. package/dist-skill/docs/PROGRAM-LEAD.md +180 -0
  38. package/dist-skill/docs/VISION.md +77 -0
  39. package/examples/api/import-group-assembly.forge.js +34 -0
  40. package/examples/api/import-group-source.forge.js +35 -0
  41. package/package.json +1 -1
@@ -0,0 +1,672 @@
1
+ # ForgeCAD CLI
2
+
3
+ ## Architecture
4
+
5
+ All CLI tools share the **same forge engine** as the browser UI. There is one source of truth for geometry logic — no code duplication.
6
+
7
+ ```
8
+ src/forge/headless.ts ← Single entry point for all contexts
9
+ ├── kernel.ts ← Manifold WASM wrapper (Shape, box, cylinder, sphere, etc.)
10
+ ├── runner.ts ← Script sandbox (Function() with full forge API injected)
11
+ ├── section.ts ← Plane intersection / projection
12
+ ├── sketch/ ← Complete 2D sketch system (primitives, transforms, booleans,
13
+ │ constraints, entities, topology, patterns, fillets, arc bridge)
14
+ ├── params.ts ← Parameter system
15
+ ├── library.ts ← Part library
16
+ ├── meshToGeometry.ts ← Manifold mesh → Three.js BufferGeometry
17
+ └── sceneBuilder.ts ← Three.js scene setup (lighting, camera, materials)
18
+ ```
19
+
20
+ **Browser** imports via `src/forge/index.ts` → re-exports from `headless.ts`.
21
+ **CLI tools** import directly from `src/forge/headless.ts`.
22
+
23
+ The key function is `runScript(code, fileName, allFiles)` — it wraps user code in a `Function()` sandbox with the entire forge API injected, and transpiles project files so standard JS `import` / `export` / `require(...)` work for shared utility modules. CLI scripts just call `init()` + `runScript()` and work with the results.
24
+
25
+ ## Install
26
+
27
+ Install the package and link the local binary once:
28
+
29
+ ```bash
30
+ npm install
31
+ npm link
32
+ ```
33
+
34
+ After that, use `forgecad ...` directly from your shell.
35
+
36
+ ### Shell Autocomplete
37
+
38
+ ForgeCAD now ships shell completion scripts in the usual modern-tool style:
39
+
40
+ ```bash
41
+ forgecad completion bash
42
+ forgecad completion zsh
43
+ forgecad completion fish
44
+ ```
45
+
46
+ Quick install:
47
+
48
+ ```bash
49
+ # bash
50
+ echo 'source <(forgecad completion bash)' >> ~/.bashrc
51
+
52
+ # zsh
53
+ mkdir -p ~/.zsh/completions
54
+ forgecad completion zsh > ~/.zsh/completions/_forgecad
55
+ echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
56
+ echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
57
+
58
+ # fish
59
+ mkdir -p ~/.config/fish/completions
60
+ forgecad completion fish > ~/.config/fish/completions/forgecad.fish
61
+ ```
62
+
63
+ The completions are contextual:
64
+
65
+ - nested subcommands such as `forgecad notebook view` and `forgecad export step`
66
+ - command-specific flags and common enum values
67
+ - ForgeCAD file suggestions where a command expects `.forge.js`, `.sketch.js`, or `.forge-notebook.json`
68
+
69
+ ## Available Commands
70
+
71
+ ### Notebook Cells (server-backed)
72
+
73
+ Forge notebooks live in `.forge-notebook.json` files and behave like lightweight Jupyter notebooks for ForgeCAD code cells.
74
+
75
+ The browser and CLI both use the Vite server for notebook execution. The CLI does not run Forge locally for notebook cells; it auto-starts or reuses the Forge server, sends the cell code, then prints the returned output summary.
76
+
77
+ Append a new code cell and run it immediately in one command:
78
+
79
+ ```bash
80
+ forgecad notebook examples/demo.forge-notebook.json --code "show(box(40, 20, 10));"
81
+ ```
82
+
83
+ If the target notebook file does not exist yet, append mode auto-creates it first with the default ForgeCAD notebook structure, then adds the new cell.
84
+
85
+ Or pipe a larger cell in through stdin:
86
+
87
+ ```bash
88
+ cat /tmp/cell.js | forgecad notebook examples/demo.forge-notebook.json
89
+ ```
90
+
91
+ Re-run the last preview cell, or a specific cell id:
92
+
93
+ ```bash
94
+ forgecad notebook examples/demo.forge-notebook.json
95
+ forgecad notebook run examples/demo.forge-notebook.json <cell-id>
96
+ ```
97
+
98
+ View the notebook in the terminal without dumping raw JSON:
99
+
100
+ ```bash
101
+ forgecad notebook view examples/demo.forge-notebook.json
102
+ forgecad notebook view examples/demo.forge-notebook.json preview
103
+ forgecad notebook view examples/demo.forge-notebook.json 2
104
+ ```
105
+
106
+ `view` is local-only. It parses the notebook JSON and renders notebook metadata, numbered source lines, and stored outputs for each cell. The optional selector accepts a 1-based cell number, an exact cell id, or `preview`.
107
+
108
+ `run`/`view` expect the notebook file to already exist. Auto-creation only applies to append flows (`--code`, `--file`, stdin, or the explicit `append` subcommand).
109
+
110
+ Export a notebook into a plain `.forge.js` script:
111
+
112
+ ```bash
113
+ forgecad notebook export examples/demo.forge-notebook.json
114
+ forgecad notebook export examples/demo.forge-notebook.json out/demo-from-notebook.forge.js
115
+ ```
116
+
117
+ If you already have a Forge server running, point the CLI at it:
118
+
119
+ ```bash
120
+ forgecad notebook examples/demo.forge-notebook.json --server http://localhost:5173 --code "show(box(40, 20, 10));"
121
+ ```
122
+
123
+ Notebook paths are resolved from the shell working directory before the CLI calls the server, so the server's opened project root does not add an extra path prefix.
124
+
125
+ Notebook cell behavior:
126
+
127
+ - Cells share state top-to-bottom
128
+ - `show(value)` pins the geometry that should stay visible in the viewport
129
+ - A trailing expression is also treated as the cell value
130
+ - Cell outputs are written back into the notebook JSON, similar to Jupyter
131
+
132
+ For the `forgecad` entrypoints below, passing a `.forge-notebook.json` uses that notebook's preview cell. That means you can inspect with `view`, validate with `run`, and render or capture the current preview without exporting first.
133
+
134
+ ### Script Validation
135
+
136
+ ```bash
137
+ forgecad run examples/cup.forge.js
138
+ forgecad run examples/api/notebook-iteration.forge-notebook.json
139
+ forgecad run examples/cup.forge.js --debug-imports
140
+ ```
141
+
142
+ Runs a `.forge.js`, `.sketch.js`, or notebook preview cell in the real runtime and prints object stats, diagnostics, and execution time.
143
+
144
+ `--debug-imports` adds an import trace (source file, target file, overrides, return type, success/error phase), useful when debugging `importPart()`/`importSketch()` behavior.
145
+
146
+ ### SVG Export (no browser needed)
147
+
148
+ ```bash
149
+ forgecad export svg examples/frame.sketch.js [output.svg]
150
+ ```
151
+
152
+ Runs a `.sketch.js` script in Node.js using the real forge engine and outputs SVG. No browser, no Puppeteer — pure Node.
153
+
154
+ **How it works:** Initializes the Manifold WASM kernel, runs the script through `runScript()`, extracts the Sketch result, converts polygons to SVG paths.
155
+
156
+ ### STEP / BREP Export (exact subset, Python + CadQuery)
157
+
158
+ ```bash
159
+ forgecad export step examples/api/brep-exportable.forge.js
160
+ forgecad export brep examples/api/brep-exportable.forge.js
161
+
162
+ # Optional overrides:
163
+ forgecad export step examples/api/brep-exportable.forge.js --output out/demo.step
164
+ forgecad export step examples/api/brep-exportable.forge.js --python 3.11
165
+ forgecad export step examples/api/brep-exportable.forge.js --uv /custom/path/to/uv
166
+ forgecad export step examples/chess-set.forge.js --allow-faceted
167
+ ```
168
+
169
+ This exporter is `uv`-first. `cli/forge-brep-export.py` carries inline dependency metadata, so `uv run` provisions CadQuery automatically for the exporter environment.
170
+
171
+ By default this exporter is exact-subset only. It does **not** silently convert arbitrary triangle meshes back into fake BREP. Instead, Forge lowers compile-covered geometry into the `cadquery-occt` compiler target and exports that exact subset through CadQuery/OpenCascade.
172
+
173
+ If you pass `--allow-faceted`, unsupported closed mesh solids are exported as explicit faceted OCCT solids. This keeps hull-heavy designs exportable to STEP/BREP, but that fallback is tessellation-driven rather than exact replay.
174
+
175
+ The maintained feature matrix lives in [`docs/permanent/API/output/brep-export.md`](API/output/brep-export.md).
176
+
177
+ If any returned solid object falls outside the exact subset, the CLI fails with a reason instead of silently exporting degraded geometry. When a scene mixes solids and 2D sketches, the exact solids export and the sketch-only objects are skipped with a warning.
178
+
179
+ With `--allow-faceted`, mesh-solid blockers that still lack an exact replay plan are exported as faceted solids instead of failing. The CLI prints which objects used the fallback.
180
+
181
+ For coverage runs across many examples, use the `uv` matrix scripts:
182
+
183
+ ```bash
184
+ uv run scripts/brep/matrix.py --format step examples
185
+ uv run scripts/brep/matrix.py --format brep examples
186
+ uv run scripts/brep/rerun_failures.py tmp/brep-matrix-step-20260306T120000Z.json
187
+ ```
188
+
189
+ These scripts use the repo-local `.venv-brep/.venv/bin/python` by default, run exports through a bounded parallel worker pool, and write JSON reports under `tmp/`.
190
+
191
+ ### SDF Robot Export (Gazebo package)
192
+
193
+ ```bash
194
+ forgecad export sdf examples/api/sdf-rover-demo.forge.js
195
+
196
+ # Optional output directory:
197
+ forgecad export sdf examples/api/sdf-rover-demo.forge.js --output out/forge_scout
198
+ ```
199
+
200
+ This exporter writes a Gazebo-friendly package workspace:
201
+
202
+ - `models/<model-name>/model.sdf`
203
+ - `models/<model-name>/model.config`
204
+ - `models/<model-name>/meshes/*.stl`
205
+ - `worlds/<world-name>.sdf` when the script requests a demo world
206
+ - `manifest.json` with topic names, link/joint mappings, and exporter warnings
207
+
208
+ The script must call `robotExport({...})` with an `assembly(...)` graph. The exporter uses the declared parts + joints directly; it does **not** try to infer a robot from flattened scene meshes.
209
+
210
+ When `world.generateDemoWorld` and `world.keyboardTeleop.enabled` are on, the exported world includes both:
211
+
212
+ - Gazebo's GUI `KeyPublisher` plugin
213
+ - server-side `TriggeredPublisher` bindings that map arrow keys to the diff-drive `cmd_vel` topic
214
+
215
+ Recommended launch flow:
216
+
217
+ ```bash
218
+ export GZ_SIM_RESOURCE_PATH="$PWD/out/forge_scout/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}"
219
+
220
+ # Terminal 1: server
221
+ gz sim -s -r out/forge_scout/worlds/forge_scout_trial.sdf
222
+
223
+ # Terminal 2: GUI client using the same world layout
224
+ gz sim -g out/forge_scout/worlds/forge_scout_trial.sdf
225
+ ```
226
+
227
+ Notes:
228
+
229
+ - On macOS, use the split `-s` / `-g` flow above. `gz sim <world.sdf>` is not supported there.
230
+ - Click the 3D view so it has keyboard focus, then use `W` / `X` for forward / reverse, `A` / `D` to rotate, `Q` / `E` / `Z` / `C` for diagonals, and `S` or `Space` to stop.
231
+ - For older exports created before the GUI plugin was added, load `Key Publisher` manually from the Gazebo GUI plugins menu.
232
+
233
+ Current behavior:
234
+
235
+ - Per-link geometry is exported as STL mesh assets
236
+ - Collision geometry reuses the same mesh unless `collision: 'none'` is set on a link
237
+ - Link mass comes from `massKg`, else `densityKgM3 * volume`, else a default density
238
+ - Inertia is an approximate box fit based on link bounds
239
+ - Coupled joints are currently rejected
240
+ - Parts without geometry are currently rejected
241
+
242
+ ### PNG Render (requires Chrome)
243
+
244
+ ```bash
245
+ forgecad render examples/cup.forge.js [output.png]
246
+ forgecad render examples/api/notebook-iteration.forge-notebook.json [output.png]
247
+ forgecad render examples/cup.forge.js out/scene.png --scene '{"camera":{"projectionMode":"perspective","position":[200,-160,120],"target":[0,0,20],"up":[0,0,1]},"objects":{"obj-2":{"visible":false},"obj-3":{"opacity":0.35}}}'
248
+ ```
249
+
250
+ Renders 3D shapes to PNG images from multiple camera angles. Uses Puppeteer to launch headless Chrome with WebGL for Three.js rendering.
251
+
252
+ When the input is a notebook, `forgecad render` renders the notebook's preview cell.
253
+
254
+ **How it works:**
255
+ 1. `cli/forge-render.mjs` — Node launcher script. Auto-starts Vite dev server if not running, launches Puppeteer.
256
+ 2. `cli/render.html` + `cli/render.ts` — Loaded in the browser by Puppeteer. Imports from `src/forge/headless.ts`, runs the script, builds a Three.js scene, renders from multiple angles.
257
+ 3. Screenshots are captured as base64 PNG and saved to disk.
258
+
259
+ **Environment variables:**
260
+
261
+ | Variable | Default | Description |
262
+ |----------|---------|-------------|
263
+ | `FORGE_ANGLES` | `front,side,top,iso` | Camera angles to render |
264
+ | `FORGE_SIZE` | `1024` | Image size in pixels |
265
+ | `FORGE_PORT` | `5173` | Vite dev server port |
266
+ | `CHROME_PATH` | Auto-detected | Chrome/Chromium executable path |
267
+
268
+ **CLI options:**
269
+ - `--angles <front,side,top,iso>` — standard angles to render
270
+ - `--size <px>` — output size override
271
+ - `--port <n>` — Vite port override
272
+ - `--camera <spec>` — exact camera pose, e.g. `proj=perspective;pos=120,80,120;target=0,0,0;up=0,0,1`
273
+ - `--scene <json>` — full scene state copied from the viewport, including camera plus object visibility/opacity/color overrides
274
+ - `--background <color>` — background override
275
+ - `--chrome-path <path>` — Chrome executable path override
276
+
277
+ **Camera angles:** `front` (−Y), `back` (+Y), `side` (+X), `top` (+Z), `iso` (diagonal)
278
+
279
+ ### Animated Capture (GIF or MP4, requires Chrome)
280
+
281
+ ```bash
282
+ forgecad capture gif examples/cup.forge.js [output.gif]
283
+ forgecad capture mp4 examples/cup.forge.js [output.mp4]
284
+ forgecad capture gif examples/api/notebook-assembly-debug.forge-notebook.json --list
285
+ forgecad capture mp4 examples/api/runtime-joints-view.forge.js out/step.mp4 --capture animation --animation Step
286
+ forgecad capture gif examples/3d-printer.forge.js out/section.gif --cut-plane "Front Section"
287
+ ```
288
+
289
+ Creates high-quality animated captures from the real Forge viewport renderer:
290
+ - Orbit captures with optional wireframe pass
291
+ - Fixed-camera animation captures for `jointsView()` clips
292
+ - Named cut-plane captures
293
+ - Exact camera replay via `--camera`
294
+ - Full viewport scene replay via `--scene`
295
+
296
+ When the input is a notebook, `forgecad capture gif` / `forgecad capture mp4` capture the notebook's preview cell.
297
+
298
+ **How it works:**
299
+ 1. Auto-starts (or reuses) the Vite dev server.
300
+ 2. Loads `cli/render.html` in headless Chrome.
301
+ 3. Runs the script once, then captures frames from the same scene while applying the selected animation, cut planes, and camera pose.
302
+ 4. Encodes with `ffmpeg` when available:
303
+ - GIF: palettegen/paletteuse for much better colors
304
+ - MP4: H.264 via `libx264`
305
+ 5. Falls back to the pure-JS GIF encoder only when `ffmpeg` is unavailable.
306
+
307
+ **Options:**
308
+ - `--format <gif|mp4>` — output format
309
+ - `--capture <orbit|animation>` — moving orbit camera or fixed animation camera
310
+ - `--animation <name>` — select one `jointsView()` clip
311
+ - `--animation-loops <n>` — repeat the chosen clip
312
+ - `--cut-plane <name>` — enable a named cut plane (repeatable)
313
+ - `--camera <spec>` — exact camera pose, e.g. `proj=perspective;pos=120,80,120;target=0,0,0;up=0,0,1`
314
+ - `--scene <json>` — full scene state copied from the viewport, including camera plus object visibility/opacity/color overrides
315
+ - `--render-mode <solid|wireframe>` — primary render mode
316
+ - `--include-wireframe-pass` / `--no-wireframe-pass` — control the extra wireframe pass
317
+ - `--size <px>` — output frame resolution (default `960`)
318
+ - `--pixel-ratio <n>` — render supersampling factor (default `2`)
319
+ - `--fps <n>` — capture frame rate (default `24`)
320
+ - `--frames-per-turn <n>` — frames per full orbit pass (default `72`)
321
+ - `--hold-frames <n>` — freeze frames before each pass (default `6`)
322
+ - `--pitch <deg>` — orbit elevation override
323
+ - `--background <color>` — background color (default `#252526`)
324
+ - `--quality <default|live|high>` — Forge geometry quality preset for export (default `high`)
325
+ - `--encoder <auto|ffmpeg|js>` — GIF encoder strategy
326
+ - `--crf <n>` — MP4 quality for `libx264` (default `18`)
327
+ - `--list` — print the script's available animation clips and cut planes
328
+ - `--port <n>` — Vite port (default `5173`)
329
+ - `--chrome-path <path>` — Chrome executable path override
330
+ - `--ffmpeg-path <path>` — ffmpeg executable path override
331
+
332
+ **Environment variables:**
333
+ - `FORGE_CAPTURE_SIZE`
334
+ - `FORGE_CAPTURE_PIXEL_RATIO`
335
+ - `FORGE_CAPTURE_FPS`
336
+ - `FORGE_CAPTURE_FRAMES_PER_TURN`
337
+ - `FORGE_CAPTURE_HOLD_FRAMES`
338
+ - `FORGE_CAPTURE_PITCH_DEG`
339
+ - `FORGE_CAPTURE_BACKGROUND`
340
+ - `FORGE_CAPTURE_QUALITY`
341
+ - `FORGE_CAPTURE_ANIMATION_LOOPS`
342
+ - `FORGE_CAPTURE_CRF`
343
+ - `FFMPEG_PATH`
344
+ - Legacy `FORGE_GIF_*` vars are still honored as fallbacks
345
+ - `FORGE_PORT`
346
+ - `CHROME_PATH`
347
+
348
+ **UI scene handoff:**
349
+ - The View Panel exposes a `Camera` section.
350
+ - Use `Copy CLI --scene` to grab the current viewport framing plus per-object scene overrides and paste it directly into `render`, `capture gif`, or `capture mp4`.
351
+
352
+ ### PDF Report (2D drawing pack)
353
+
354
+ ```bash
355
+ forgecad export report examples/cup.forge.js [output.pdf]
356
+ forgecad export report examples/cup.forge.js [output.pdf] --dim-angle-tol 18
357
+ ```
358
+
359
+ Generates a searchable-text PDF report with multiple projected drawing views:
360
+ - Bill of Materials page (auto-summed from script `bom()` entries)
361
+ - Combined model page (front/right/top/isometric)
362
+ - Disassembled component pages (same view set per unique component geometry; repeated identical items collapse into one page)
363
+ - Auto-generated detail continuation pages for elongated/high-detail views (separate pages, not overlayed)
364
+ - `dim()` annotations included per view only when their axis aligns with that view's projection plane axes
365
+
366
+ BOM aggregation rules:
367
+ - Each `bom(quantity, description, { unit })` call contributes one raw entry
368
+ - Report export groups by `key` (if provided) else by normalized `description + unit`
369
+ - Quantities are summed per group and rendered as line items in the BOM table
370
+
371
+ Component dimension ownership for disassembled pages:
372
+ - Preferred: explicit binding via `dim(..., { component: \"Part Name\" })`
373
+ - Imported-part ownership: `dim(..., { currentComponent: true })` to pin to the owning returned component instance (no bbox heuristic)
374
+ - Other-component ownership: `dim(..., { component: \"Tabletop\" })`
375
+ - If multiple owners are bound (e.g. `currentComponent: true` plus another component), it is treated as shared and stays on the overview page
376
+ - Fallback: automatic ownership only when both dimension endpoints are unambiguously inside exactly one returned component bounding box
377
+ - Ambiguous dimensions are intentionally skipped for disassembled pages
378
+
379
+ Optional report flag:
380
+ - `--dim-angle-tol <degrees>`: include dimensions whose projected direction is within this many degrees of the nearest view axis (default: `12`)
381
+
382
+ ### STL Export (from browser)
383
+
384
+ STL export is available in the browser UI via the Export panel. Binary STL format.
385
+
386
+ ### Parameter Validation
387
+
388
+ ```bash
389
+ forgecad check params examples/shoe-rack-doors.forge.js [--samples 10]
390
+ ```
391
+
392
+ Samples each parameter across its range and checks for runtime errors, degenerate geometry (volume ≈ 0), and new collisions between parts. Skips intra-group collisions when assembly groups are used.
393
+
394
+ **Options:**
395
+ - `--samples N` — Number of sample points per parameter (default: 8)
396
+
397
+ **Output example:**
398
+ ```
399
+ ✓ Baseline: 6 objects, 12 params
400
+ ✓ Checked 91 parameter samples (8 per param)
401
+
402
+ ⚠ Found 8 issues across 4 parameters:
403
+
404
+ Parameter "Bottom Left Door":
405
+ 💥 New collision at values: -120.0, -102.9
406
+ Bottom Left Door ∩ Frame (shared vol: 2561.9mm³)
407
+ ```
408
+
409
+ ### Transform/Assembly Invariant Check
410
+
411
+ ```bash
412
+ forgecad check transforms
413
+ ```
414
+
415
+ Runs fast math-level invariants to catch transform order and frame composition regressions before they leak into examples.
416
+
417
+ ### Compiler Snapshot Check
418
+
419
+ ```bash
420
+ forgecad check compiler
421
+ forgecad check compiler --case segmented-runtime-hints
422
+ forgecad check compiler --update
423
+ ```
424
+
425
+ Runs curated compiler regression cases and compares them against committed snapshots.
426
+ This is a unit-style invariant check, not just a debugger convenience.
427
+ The ordinary multi-feature part corpus lives in [`examples/compiler-corpus/README.md`](../../examples/compiler-corpus/README.md).
428
+
429
+ Each snapshot records:
430
+ - Forge compile plans
431
+ - CadQuery/OCCT lowerings
432
+ - export routing decisions
433
+ - quantized runtime Manifold mesh summaries
434
+ - quantized compiler-lowered Manifold mesh summaries
435
+
436
+ This check also fails if:
437
+ - a plan-covered shape or sketch no longer matches its compiler-lowered runtime output
438
+ - export manifests drift away from the per-object compiler routing decisions
439
+ - exact/faceted support claims stop matching the lowered artifacts and diagnostics
440
+
441
+ ### Query Propagation Snapshot Check
442
+
443
+ ```bash
444
+ forgecad check query-propagation
445
+ forgecad check query-propagation --case hull-runtime-boundary
446
+ forgecad check query-propagation --update
447
+ ```
448
+
449
+ Runs focused topology-rewrite query-propagation snapshots without dumping the
450
+ entire compiler scene. This keeps supported, ambiguous, and intentionally
451
+ unsupported rewrite semantics reviewable as the propagation layer evolves.
452
+
453
+ Each snapshot records:
454
+ - the propagated shape objects that actually carry topology-rewrite metadata
455
+ - exact versus faceted routing outcomes for those objects
456
+ - deterministic rewrite-operation ordering
457
+ - preserved and created query summaries
458
+ - explicit ambiguity/unsupported diagnostic codes
459
+
460
+ This check also fails if:
461
+ - a defended propagation case loses the expected preserved or created query shape
462
+ - a known unsupported rewrite stops reporting its explicit diagnostic boundary
463
+ - a multi-feature corpus part stops surfacing the expected rewrite ordering
464
+
465
+ ### Example Architecture Gate
466
+
467
+ ```bash
468
+ forgecad check examples
469
+ forgecad check examples --family api-parts --family compiler-corpus
470
+ forgecad check examples --example examples/api/brep-exportable.forge.js
471
+ ```
472
+
473
+ Runs the checked example manifest for the entire `examples/` tree.
474
+
475
+ The manifest currently lives in `cli/example-manifest/` and covers every:
476
+
477
+ - `.forge.js`
478
+ - `.sketch.js`
479
+ - `.forge-notebook.json`
480
+
481
+ The command always verifies manifest coverage first, so it fails if:
482
+
483
+ - a new example file was added without classification
484
+ - a checked manifest entry points at a missing file
485
+ - an example's assigned validation path fails
486
+ - a `part` example's declared route expectation no longer matches the compiler report
487
+
488
+ Current example classes:
489
+
490
+ - `part`: runtime execution plus optional exact/faceted route assertions on the selected primary shapes
491
+ - `assembly`: runtime solve + scene emission, not exact-route parity
492
+ - `runtime-scene`: viewport/report/runtime examples that still need to execute successfully
493
+ - `sketch`: sketch payload validation via the sketch export path
494
+ - `notebook`: preview-cell validation for `.forge-notebook.json`
495
+ - `experimental`: temporary fenced examples that still have to run
496
+
497
+ The gate dispatches by declared validation path, not just by class label:
498
+
499
+ - `part-runtime`: execute and then enforce any declared exact/faceted route contract
500
+ - `assembly-runtime`: execute and validate solved-scene/assembly-owned runtime behavior
501
+ - `runtime-scene`: execute as a viewport/report/runtime scene without treating it as part-route evidence
502
+ - `sketch-svg`: render returned sketch payloads through the sketch SVG path
503
+ - `notebook-preview`: materialize and execute the notebook preview cell
504
+ - `experimental-runtime`: execute only, while the example stays outside the active architecture claim
505
+
506
+ For non-part entries, the manifest can also pin specific runtime surfaces that
507
+ must remain available to repo checks, such as BOM entries, cut planes,
508
+ `jointsView()` controls, grouped scene structure, or collected
509
+ `robotExport(...)` data.
510
+
511
+ Current part route states:
512
+
513
+ - `exact`: selected primary shapes must stay on the exact compiler route
514
+ - `faceted`: exact must stay blocked and allow-faceted must succeed with diagnostics
515
+ - `holdout`: runtime-checked, but intentionally outside the exact-route claim because the example still mixes route outcomes or depends on a documented unsupported capability; this is a temporary recovery state and should normally trend back to zero
516
+
517
+ Successful runs also print the current temporary fence list, including each
518
+ remaining `holdout` or `experimental` entry's blocker and follow-up task, so
519
+ the command output can be used directly in a phase-entry review.
520
+
521
+ Use `--family` when a task owns only one manifest lane, and `--example` when you
522
+ want to debug a single checked artifact.
523
+
524
+ ### Invariant Test Suite
525
+
526
+ ```bash
527
+ forgecad check suite
528
+ npm test
529
+ npm run test:examples
530
+ npm run test:compiler
531
+ npm run test:compiler:update
532
+ npm run test:query-propagation
533
+ npm run test:query-propagation:update
534
+ ```
535
+
536
+ ForgeCAD's current unit-test surface is assertion-based CLI checks, not a separate Vitest/Jest harness.
537
+
538
+ The important entrypoints are:
539
+ - `npm test` runs the repo invariant suite (`transforms`, `dimensions`, `placement`, `js-modules`, `brep`, `compiler`, `query-propagation`, `examples`, `api`)
540
+ - `npm run test:examples` runs the example architecture gate across the checked `examples/` manifest
541
+ - `npm run test:compiler` runs just the compiler snapshot/invariant suite
542
+ - `npm run test:compiler:update` refreshes committed compiler snapshots after an intentional change
543
+ - `npm run test:query-propagation` runs the focused topology-rewrite query-propagation snapshots
544
+ - `npm run test:query-propagation:update` refreshes those query-propagation snapshots after an intentional change
545
+ - `forgecad check suite` is the CLI equivalent of the invariant suite runner
546
+
547
+ ### Dimension Propagation Invariant Check
548
+
549
+ ```bash
550
+ forgecad check dimensions
551
+ ```
552
+
553
+ Runs shape-level invariants for dimension metadata propagation across:
554
+ - transform APIs (`translate`, `rotate`, `transform`, `scale`, `mirror`, `rotateAround`)
555
+ - copy/style APIs (`clone`, `color`, `setColor`, `smooth/refine/simplify`)
556
+ - boolean APIs (`add/subtract/intersect`, plus `union/difference/intersection/hull3d`)
557
+ - import runtime path (`importPart(...).color(...).translate(...)`)
558
+
559
+ ### Dimension Debugger
560
+
561
+ ```bash
562
+ forgecad debug dimensions /path/to/file.forge.js [--all]
563
+ forgecad debug dimensions /path/to/file.forge.js [--all] [--dim-angle-tol 12]
564
+ ```
565
+
566
+ Prints:
567
+ - total object count
568
+ - total dimension count
569
+ - per-view visibility counts (`front/right/top/iso`) using report angle tolerance
570
+ - report ownership routing (`combined` vs `component:<name>`) per dimension
571
+ - per-object approximate dimension ownership (both endpoints inside object bbox)
572
+ - a dimension coordinate list (first 20 by default, `--all` for full dump)
573
+
574
+ ### Compiler Debugger
575
+
576
+ ```bash
577
+ forgecad debug compiler /path/to/file.forge.js
578
+ forgecad debug compiler /path/to/file.forge.js --compact
579
+ ```
580
+
581
+ Prints JSON for the current script's compiler state, including:
582
+ - per-object compile plans
583
+ - CadQuery/OCCT lowering diagnostics and lowered plans
584
+ - faceted fallback eligibility
585
+ - runtime Manifold summaries
586
+ - compiler-lowered Manifold summaries
587
+
588
+ ### Local Branch Cleanup
589
+
590
+ ```bash
591
+ uv run cli/forge-prune-local-branches.py
592
+ uv run cli/forge-prune-local-branches.py --dry-run
593
+ uv run cli/forge-prune-local-branches.py --base mainline
594
+ ```
595
+
596
+ This is a `uv`-backed Python utility for repository housekeeping. It finds local branches with no matching remote branch that are already merged into the selected base ref, shows them in a Rich terminal UI, then prompts one by one before deleting anything.
597
+
598
+ Behavior:
599
+ - Deletes with `git branch -d`, not force-delete
600
+ - Removes linked worktrees first when the branch is checked out in a secondary worktree
601
+ - Requires an explicit `force` choice if one of those linked worktrees is dirty
602
+ - Refuses to touch the current worktree, the primary worktree, or prunable/missing worktree entries
603
+ - `--path` lets you point at any location inside the target repository
604
+
605
+ ## Adding New CLI Commands
606
+
607
+ 1. Create or extend a module under `cli/`
608
+ 2. Import from `../src/forge/headless`
609
+ 3. Call `await init()` to load the WASM kernel
610
+ 4. Use `runScript(code, fileName, allFiles)` to execute user scripts
611
+ 5. Register the new subcommand in `cli/forgecad.ts`
612
+
613
+ ### Minimal Example
614
+
615
+ ```typescript
616
+ #!/usr/bin/env node
617
+ import { readFileSync } from 'fs';
618
+ import { init, runScript } from '../src/forge/headless';
619
+
620
+ const code = readFileSync(process.argv[2], 'utf-8');
621
+
622
+ await init();
623
+ const result = runScript(code, 'main.forge.js', {});
624
+
625
+ if (result.error) {
626
+ console.error(result.error);
627
+ process.exit(1);
628
+ }
629
+
630
+ for (const obj of result.objects) {
631
+ if (obj.shape) {
632
+ console.log(`${obj.name}: volume=${obj.shape.volume().toFixed(1)}mm³`);
633
+ }
634
+ if (obj.sketch) {
635
+ console.log(`${obj.name}: area=${obj.sketch.area().toFixed(1)}mm²`);
636
+ }
637
+ }
638
+ ```
639
+
640
+ ### Cross-file imports
641
+
642
+ When running scripts that use `importSketch()` / `importSvgSketch()` / `importPart()` or plain JS module imports, pass all project files (or at least all files reachable by imports), keyed by project-relative path. This supports root-relative and relative imports, utility `.js` modules, and `.svg` assets (`./assets/logo.svg`):
643
+
644
+ ```typescript
645
+ import { readdirSync, readFileSync } from 'fs';
646
+
647
+ const allFiles: Record<string, string> = {};
648
+ for (const f of readdirSync(scriptDir)) {
649
+ if (f.endsWith('.forge.js') || f.endsWith('.sketch.js') || f.endsWith('.js') || f.endsWith('.svg')) {
650
+ allFiles[f] = readFileSync(join(scriptDir, f), 'utf-8');
651
+ }
652
+ }
653
+
654
+ const result = runScript(code, 'main.forge.js', allFiles);
655
+ ```
656
+
657
+ For utility modules that want explicit ForgeCAD imports instead of globals, use the virtual runtime module:
658
+
659
+ ```javascript
660
+ import { box, union } from "forgecad";
661
+ ```
662
+
663
+ Keep using `importPart()` / `importSketch()` for model/sketch files when you want ForgeCAD-specific behavior like param override scopes or SVG parsing.
664
+
665
+ ## Dependencies
666
+
667
+ | Package | Purpose | Context |
668
+ |---------|---------|---------|
669
+ | `forgecad` | Installable CLI binary (`forgecad ...`) | Runtime package |
670
+ | `puppeteer-core` | Headless Chrome for PNG/GIF/MP4 rendering | Runtime dependency |
671
+ | `manifold-3d` | Geometry kernel (WASM) | Works in both Node and browser |
672
+ | `three` | 3D rendering (used by render.ts) | Loaded in browser context by Puppeteer |