| name | implement |
| description | Use when authoring or editing a brepjs `.brep.ts` part — writing the geometry with the functional API (box, cylinder, fuse, cut, fillet, sketch→extrude…), declaring an `expected` block, and following the hard rules (import every function, unwrap Results, select edges, coordinate semantics). Also covers buildings/BIM/IFC via the declarative family layer (references/families-bim.md). This is the authoring step; pair it with brepjs:verify to check the result. |
| version | 0.1.0 |
Author a brepjs part
You write a .brep.ts part; the brep CLI runs it on a geometry kernel and reports what it
measured. Judge the part by the report (see brepjs:verify), not by how the code reads. This skill
is self-contained — everything needed to author correctly is here or in references/.
The CLI ships in the brepjs-cad package as brep. Installed: brep verify part.brep.ts ….
Otherwise: npx -y -p brepjs-cad brep verify ….
Authoring contract
export default () => <shape> (or async () => {…} to await loadFont/importSTEP).
- Short functional API (
box, cylinder, fuse, cut, fillet, …), named consts at the top.
- Scaffold with
brep init <name>. Edit source, never generated artifacts (STEP/STL/GLB derive
from the .brep.ts).
Realize the designed object — not just a valid one
--check passing means buildable, not correct. The bar is the part the brief names, recognisable
as that designed object — and the way that fails is simplification: a valid, generic version that
drops the one feature that makes it itself.
- Decompose the brief into named features, then mark the ONE defining feature — the geometry without
which it's a generic blob.
GT2 pulley → the belt-tooth profile (a smooth groove is not a GT2 pulley);
fluted knob → full-height flutes around the whole perimeter (scattered scallops are not flutes);
twisted impeller/swept fan → a cambered airfoil section extruded radially with a pitch twist
(references/airfoils.md), not flat blades or twistAngle on a paddle; scroll chuck →
the spiral face groove; involute gear → the tooth flank (references/gears.md).
- Build that feature to spec, not an eyeballed approximation. Each of the above passes
--check while
missing its headline feature — a blob that verifies. If the feature needs real math (gear/thread/involute/
scroll), use the reference recipe; don't substitute a smooth or sparse stand-in.
- Before finishing, re-read the brief noun by noun and confirm each named feature is actually in the
geometry — including count (e.g. four mount holes, N teeth), not just present-ish.
Choose the operation (reliability tiers)
Prefer ops that succeed first-try; lean on the report and small steps for advanced ops. Full table:
references/operation-tiers.md. In short: primitives, booleans, compound, sketch→extrude,
fillet, shell/offset, transforms are reliable; sweeps/lofts/revolves/fuseAll/text are
advanced; chamfer is the fragile exception (prefer fillet).
Declare intent — the expected block
Add export const expected = { … } from the brief; the CLI asserts it, catching valid-but-wrong
sizing. The only authorable keys are volume, area, bounds, tolerancePct (each optional;
tolerancePct sets the match window) — TOP_LEVEL_KEYS in src/verify/expected.ts:45. Bounds
shape is exactly { xMin, xMax, yMin, yMax, zMin, zMax } (any subset) — not { min, max } or
{ x, y, z } (a wrong shape reports EXPECTED_UNKNOWN_KEY). shapeType is report-only, not
authorable: the report tells you whether the part measured as a solid/compound/etc., but
putting shapeType (or any other field) in expected also reports EXPECTED_UNKNOWN_KEY — assert
the body count or shape via volume/bounds, never a shapeType key.
Prefer bounds over a hand-computed volume (a wrong number fails a correct part). Predict
only extents you place directly — a footprint, where each body sits, the flat face of a body you
placed there — these read off your datums and catch a dropped/misplaced body. An extent governed by a
rotation, a part's orientation, a proud sub-feature, a half-space clip, or the outer top/bottom of a
deep multi-body stack is not a datum: bound it generously or measure-first (run once, copy the
report's measured value). That last one is the #1 EXPECTED_ASSERTION_FAILED on assemblies — a
stack's extreme z is usually crowned by a rounded/proud feature (a carrier hub, a ball cap) and sums
every body's placement error, so measure it; don't hand-add the stack. A flat lid-on-base height
you place is fine; the moment a curved or proud sub-feature defines the extreme, it's governed. This
was the #1 first-try failure across the corpus (rotated handles, articulated yokes, flange discs,
clipped balls): when an operand is rotated, a disc/sphere crowns an axis, or a cut clips an
extreme, measure that one axis — don't predict it.
A chamfer/fillet only REMOVES material — it never grows the bounding box. A beveled or rounded
outer corner keeps the original face plane as its bound, so the extent stays at the un-chamfered face:
predict xMin = 0 for a corner chamfered at x = 0, never xMin = -chamfer.
An extent is a datum only if you place that face directly. A derived extent is not — and these
are the other half of the EXPECTED_ASSERTION_FAILEDs: a body translated beside another (its far
edge is offset ± its own half-extent, not the offset), a face/foot widened to overlap a neighbour
for fusing (its outer edge is the widened size, not the nominal feature length), or a cylinder/cone
given a non-default axis (its far end is base + axis·length, e.g. base x=-15, axis -X,
length 14 → xMin=-29). Compute these from the SAME const that places the geometry, or measure-first.
Hard rules
- Import every function you call. No globals — every op is a named export from
'brepjs'. A
used-but-unimported symbol is TS2304: Cannot find name and fails --check before geometry runs
(the #1 first-attempt failure). Re-scan the body before finishing.
- Transforms are free functions, shape-first —
translate(shape, [x,y,z]), rotate(shape, deg, { axis }),
mirror, scale (angles in degrees), the same shape-first form as booleans. They are NOT methods:
shape.translate(...) is TS2339: Property 'translate' does not exist. Placing assembly parts needs
these even when the brief doesn't shout "transform". (references/transforms.md.)
- Unwrap Results. Booleans and
measureVolume/measureArea return Result: unwrap(cut(...))
and check the Err branch before chaining. TS2322: Result<X> is not assignable to X (on an
assignment/return) — or TS2345 when you feed an un-unwrapped Result straight into another
op's argument (e.g. fuse(a, cut(b, c))) — means an op (cut/fuse/fillet/chamfer/shell/…)
was used without unwrap(). Unwrap at every step, including the final return — a Result
default export (export default cut(...)) slips past verify (it auto-unwraps a Result default
export, so --check is green) but renders nothing in a viewer/mesh path. Always unwrap() the
returned shape.
fuse welds only where solids overlap. Bodies merely touching on a coplanar face/ring may
return a loose Compound (ok:true, not one watertight solid). Overlap the operands +
fuseAll(shapes, { unsafe: true }) to weld; use compound for a distinct-bodies assembly. For
MANY operands (a grille, a stud grid, a space frame), fuseAll unsafe routinely leaves a loose
N-solid compound even with overlap — fold with a over real overlaps and
confirm .
(.) even genuinely
overlapping operands often fuse to (, correct geometry) rather than a
single — and that's fine, because is report-only/non-authorable and
bounds/volume/validity still pass. Don't burn attempts trying to force a ; only do so (and
then only worry) when a needs a (next rule).
The report's flags a — if a part you meant as
ONE piece comes back as N bodies, the weld failed (overlap + unsafe); a count matching a
deliberate assembly is fine.
Reference index (load only what the task needs)
references/getting-started.md · primitives.md · sketching-2d.md · booleans.md ·
modifiers.md · transforms.md · measurement-validation.md · assemblies-motion.md ·
operation-tiers.md. Maker recipes: fdm-conventions.md · mechanical-joints.md ·
gridfinity.md · gears.md · threads.md · airfoils.md (fans/props/impellers/vanes).
Buildings/BIM/IFC: families-bim.md (declarative components → viewport meshes + IFC export).
Backstop: any symbol not covered →
reference/llms-full.txt (every export with signatures), bundled in the package.
Examples index (read the closest before authoring)
Each is a complete examples/<name>.brep.ts + <name>.expected.json baseline.
- Primitives + booleans:
mounting-bracket · flanged-coupler · transform-bracket · dome-cap.
- 2D sketch → solid:
extruded-bracket · revolved-pulley · swept-gasket.
- Modifiers:
rounded-block (fillet) · chamfered-block (API shape only; chamfer is fragile) ·
hollow-enclosure (shelled).
- Mechanical:
spur-gear (polygon→extrude, BOSL2-faithful) · threaded-rod (loft sections).
- Gridfinity:
gridfinity-baseplate · gridfinity-bin · gridfinity-divider.