andymai/brepjs/implement
Claude Code and agent skill by andymai/brepjs
- community
Checking that the SKILL.md file is still in the repository.
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.
Install
npx vskill@latest install andymai/brepjs/implementSKILL.md
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>(orasync () => {…}toawait 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
--checkwhile
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 failed expected assertion 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 failed expected assertions: 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/measureAreareturnResult: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 pairwise `fuse()` reduce over real overlaps and confirm getSolids(part).length === 1. (references/booleans.md.) But a `Compound` result is NOT a failure to chase: even genuinely overlapping operands often fuse to shapeType:Compound (ok:true, correct geometry) rather than a single Solid — and that's fine, because shapeType is report-only/non-authorable and bounds/volume/validity still pass. Don't burn attempts trying to force a Solid; only do so (and then only worry) when a downstream `fillet`/`shell`/`offset` needs a ValidSolid (next rule). The report's notes flags a multi-body Compound and its solid count — if a part you meant as ONE piece comes back as N bodies, the weld failed (overlap + fuseAll unsafe); a count matching a deliberate assembly is fine.
- `fillet`/`chamfer`/`shell`/`offset` need a `ValidSolid`. Primitives already are one and
booleans preserve it, so a primitive-rooted chain feeds them directly. A shape from a 2D-sketch .extrude()/.revolve() (or loft/sweep) is typed Shape3D; passing it to these ops is TS2345. Lift in two steps: if (!isSolid(x)) throw …; const solid = unwrap(validSolid(x));. Or build the prism from a primitive when you know you'll fillet/shell it. (references/modifiers.md.)
- A loose-`Compound` boolean can't be lifted to `ValidSolid` — fix the boolean, don't lift. When
fuse/fuseAll only touch (don't overlap) they return a Compound (ok:true), and isSolid returns false on it (shapeType()==='compound', not 'solid' — src/core/shapeTypes.ts:248). There is no lift from a multi-body Compound to a ValidSolid: validSolid() needs one Solid, so if (!isSolid(x)) throw … just throws, and feeding the Compound onward KERNEL_FAILEDs. Make the operands actually overlap and fuseAll(shapes, { unsafe: true }) so the weld yields a single solid first, then fillet/shell. (references/booleans.md.)
- Select edges/faces; don't fillet/chamfer everything.
fillet(solid, radius)with no edge
list rounds EVERY edge and frequently FILLET_FAILEDs. Pass edgeFinder().inDirection('Z').findAll(solid). inDirection matches BOTH ± orientations; discriminate by position with .when(f => getBounds(f).zMax > t) (getBounds is its own import). Finders take a direction ('X'/'Y'/'Z'/Vec3), never a plane: a top face is faceFinder().inDirection('Z'); `parallelTo('XY')` fails `--check` — use 'Z'. (references/modifiers.md.)
- `chamfer` is kernel-fragile.
CHAMFER_FAILEDis common even with a correct edge list. Prefer
fillet, model the bevel additively (cut with an angled tool), or drop it. Re-running the same chamfer rarely helps.
- A _through_ hole/slot/mortise needs a tool proud of BOTH faces. Size the cutting
cylinder/box LONGER than the body and place it so it pokes out each end (e.g. height = wall + 2, positioned past both faces) — a tool flush with or short of a face leaves a blind pocket, not a through feature, and --check can't catch it (the part is still valid). The brief word "through" (or "bore", "passage") is the cue. Confirm it actually passes through with the section/xray view, not just the exterior.
- `revolve` angle is RADIANS (
Math.PI * 2= full turn). Build a revolve profile with
polygon(points3D), not draw().close().sketchOnPlane('XZ').face() (fails --check). (references/sketching-2d.md.)
- `box(width, depth, height)` — depth is Y, height is Z, mm. `at` sets the geometric CENTER:
bare box(w,d,h) is corner-at-origin, { centered: true } centers on origin, { at:[x,y,z] } centers there. cylinder/cone at is the base center; sphere at is its center.
- No half-sphere primitive: clip a full
sphereto a half-space withintersect(aboxover
the half you want). Fusing a whole sphere bulges past a cap face. See the dome-cap example.
- Pattern angles are DEGREES (
circularPattern/rectangularPatternfullAngle), _unlike_
revolve (radians). Check the unit per op.
- Text needs no font file under `brep`:
sketchText/drawTextfall back to a bundled Inter
Bold. Call await loadFont(bytes, family) only for a specific typeface, passing the file's bytes as an ArrayBuffer: a string argument is fetched as a URL, so a filesystem path fails in Node.
- Parts may be `async`:
export default async () => {…}is awaited —await loadFont(...)or
await importSTEP(...). --check type-checks Node built-ins, so a part may import { readFile } from 'node:fs/promises'.
- Author in ESM (the tool's default) so the ker
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingpackages/brepjs-cad/skills/implement/SKILL.md
Popularity
- Repository stars
- 83
- Repository forks
- 8
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 11 Oct 2026, 27 days ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/andymai/brepjs/implement)