andymai/brepjs/implement

Claude Code and agent skill by andymai/brepjs

Trust tier 2
  • community
LLM review pending
Source check pending

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/implement

SKILL.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> (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.

  1. 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).

  1. 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.

  1. 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/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 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_FAILED is 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 sphere to a half-space with intersect (a box over

the half you want). Fusing a whole sphere bulges past a cap face. See the dome-cap example.

  • Pattern angles are DEGREES (circularPattern/rectangularPattern fullAngle), _unlike_

revolve (radians). Check the unit per op.

  • Text needs no font file under `brep`: sketchText/drawText fall 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

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
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/andymai/brepjs/implement/badge)](https://verified-skill.com/skills/andymai/brepjs/implement)

Something wrong with this skill or its page? Tell us.