Generate, validate, and render JSCAD v2 CAD scripts from natural language prompts
94
94%
Does it follow best practices?
Impact
97%
1.64xAverage score across 3 eval scenarios
Passed
No known issues
Generate valid JSCAD v2 main() scripts from natural language, render them via
the @magistr/jscad-cad swamp model, and validate the resulting STL with
@magistr/jscad-stl-validator. Auto-retry on failures up to 3 times.
Every generated script MUST follow these rules. Violations cause runtime errors.
const main = (params = {}) => { ... }; — always this exact formimport, require(), or from. All APIs are injectedconsole.log, no Deno.*, no fetchprimitives.cuboid(), booleans.subtract() etc. — never bare namescuboid({ size: [x,y,z] }) — never size: 10center: [x,y,z] — never center: true{ radius: 5, height: 20 } — never r, h, or lengthrotate([rx, ry, rz], shape) — values in radians, not degreessubtract() for holes/cavities, the cutter
must be taller than the full outer body it cuts through. Use height: outerH + 2
(outer body height + 2mm), not bore depth + 2mm. This prevents z-fighting at both ends.primitives — cuboid, cylinder, sphere, torus, cone, polygon, circle, square
transforms — translate, rotate, scale, mirror, align, center
booleans — subtract, union, intersect
extrusions — extrudeLinear, extrudeRotate, extrudeFromSlices
hulls — hull, hullChain
measurements — measureBoundingBox, measureVolume, measureSurfaceArea
colors — colorize, colorNameToRgbSee full verified API: references/jscad-v2-api.md
Every position and dimension must be a named constant. No inline arithmetic
in center: or translate().
// ✅ CORRECT — all positions derived and named
const wall = 3;
const innerW = 100;
const innerD = 60;
const innerH = 40;
const outerW = innerW + 2 * wall;
const outerD = innerD + 2 * wall;
const outerH = innerH + wall; // open top
const boxCenterX = 0;
const boxCenterY = 0;
const boxCenterZ = outerH / 2;
const innerCenterZ = wall + innerH / 2;
const outer = cuboid({ size: [outerW, outerD, outerH], center: [boxCenterX, boxCenterY, boxCenterZ] });
const inner = cuboid({ size: [innerW, innerD, innerH + 2], center: [boxCenterX, boxCenterY, innerCenterZ] });
// ❌ WRONG — inline arithmetic in center
const outer = cuboid({ size: [106, 66, 43], center: [0, 0, 43/2] });
const inner = cuboid({ size: [100, 60, 40], center: [0, 0, 3 + 40/2] });See also: references/geometry-positioning.md
Before writing geometry, decompose the object into features. See: references/feature-based-modeling.md and references/reverse-engineering.md
const BODY_R = TOTAL_H * 0.22 — never magic numbers[radius, z] profile, use segments: 64hulls.hull() or hulls.hullChain() of
sphere()/cylinder() primitives for handles. Do NOT use torus() — it creates
a closed ring that needs complex clipping. Hull of spheres gives direct control
over the handle curve and attachment points.User prompt → Generate script → Render STL → Validate → Copy to project
↑ | |
└── retry ←──────┘── retry ←────┘ (max 3 attempts)Write a JSCAD main() function following the rules above.
Always write script to a YAML file first. Never pass inline via --input script=...
# Write script to YAML input file using the Write tool
# Then render:
swamp model method run box-test run \
--input-file /tmp/jscad-inputs.yaml \
--json > /tmp/render-result.json 2>&1The YAML file format:
script: |
const main = (params = {}) => {
// ... script here ...
};
outputFormat: stlswamp model method run stl-check validate \
--input cadModelName=box-test \
--json > /tmp/validate-result.json 2>&1valid: true → proceedvalid: false → read issues[], retry with error contextswamp data get box-test output --json # get contentPath
cp "$(pwd)/$CONTENT_PATH" "$(pwd)/<filename>.stl"Show: generated script, triangle count, bounding box, file path.
When a reference STL exists, analyze before generating:
# Symmetry analysis
swamp model method run <slicer> analyzeSymmetry \
--input filePath=/path/to/reference.stl --json
# Profile extraction
swamp model method run <slicer> extractDirectionalProfile \
--input filePath=/path/to/reference.stl \
--input sliceAxis=Z --input measureAxis=X --json
# Feature detection
swamp model method run <slicer> detectFeatures \
--input filePath=/path/to/reference.stl \
--input sliceAxis=Z --json
# Compare after generation
swamp model method run <slicer> enhancedCompareModels \
--input refPath=/path/to/reference.stl \
--input cadModelName=box-test --jsonswamp model search --json # verify box-test and stl-check existIf missing:
swamp model create @magistr/jscad-cad box-test --json
swamp model create @magistr/jscad-stl-validator stl-check --json