cad

Create, modify, inspect, and validate parametric CAD parts and assemblies authored as cadgen model scripts. Use for natural-language CAD specs, reference images, 2D technical drawings, STEP/STP generation or direct inspection, Python CAD source, source-level joints, selector references, geometry fac

By earthtojake · 10,534 installs

npx skills add earthtojake/text-to-cad --skill cad

Source repository · Upstream listing

CAD generation, inspection, and validation Provenance: maintained in [earthtojake/text to cad](https://github.com/earthtojake/text to cad). Use the installed local skill files as the runtime source of truth; the repository link is only for provenance and release review. Setup This skill's commands are thin entrypoints over the cadgen distribution, which carries the Python build runtime and the JavaScript it executes. Install it once: Rendering additionally needs a browser, which pip cannot supply: Purpose Create or modify parametric CAD models from natural language requirements, build validated STEP/STP (or mesh) outputs, inspect geometry references, and return checked outputs. STEP is the default output of CAD geometry and the one the inspection tools read; STL, 3MF, and native GLB are mesh outputs a model declares beside it — or instead of it, when the part is print only. For assemblies, prefer cadgen.assembly.AssemblyHelper with source level build123d joints, named mating datums, and native labels when the parts have functional assembly relationships. There are two ways into the STEP workflow: build from a build123d model script (the default when designing from scratch or modifying a generated model), or import an existing STEP/STP file directly (when no script exists or the user explicitly targets the STEP file). Both are inspected, snapshotted and exported the same way. Use this skill when Use this skill when the user asks for CAD files, STEP/STP files, build123d source, selector refs such as o1.2.f1 , mechanical parts, assemblies, enclosures, brackets, fixtures, holes, counterbores, countersinks, slots, pockets, bosses, standoffs, ribs, fillets, chamfers, shells, source level joints, mating, or measurements. Also use it when the user supplies reference images or 2D technical drawings of a part to reproduce or take design intent from. Also use it when the user asks for STL, 3MF, or native GLB output from CAD geometry; load supported exports.md for details. For 2D DXF drawings, use the $dxf skill; when a DXF projects from a 3D part, this skill owns the part and $dxf owns the drawing. Do not use this skill for render only concept art, CAM toolpaths, engineering certification, FEA conclusions, architectural BIM, or freehand illustration unless the user also needs CAD geometry. Default assumptions Use these defaults unless the user specifies otherwise. These are first pass modeling defaults, not manufacturability, tolerance, or certification claims: Units: millimeters. Origin: per the part type defaults in references/positioning.md ; center of the main part or assembly when nothing better applies. Base plane: XY. Up/extrusion axis: positive Z. Output geometry: closed, positive volume solids unless the user requests surfaces or construction geometry. STEP structure: one valid solid, a compound of solids, or a labeled assembly compound. Assembly structure: fixed root part, part local frames, named mating datums, AssemblyHelper relationships backed by build123d joints where applicable, explicit generated placements, and verbose native labels. Small plastic enclosure wall: 2.0 3.0 mm when unspecified. Cosmetic fillet: 1.0 3.0 mm when safe for local geometry. M3/M4/M5 normal clearance holes: 3.4/4.5/5.5 mm unless another standard is requested. Ask one focused clarification question only when missing information makes the model impossible, fit critical, safety critical, or compliance bound. Otherwise proceed with explicit assumptions. Tools and paths The command surface (the cadgen console script, installed with the package): Scripts are RUN; commands take DOCUMENTS. python model.py is the one source door — it writes every output the model declares and (only when the model declares kinematics, animation, or mesh exports) its sidecar. Every command above takes a .step / .stl / .dxf FILE, and one handed a .py says so. A door asks one question of a document: does the store have a tree for this file's bytes? If so it reads it; if not it compiles one from the bytes as a job in the pool — generated or imported alike. A door never refuses a document and never runs a script. Whether a document is behind its script is the model's business ( cadgen store why ), not the door's. Use the active project Python interpreter; treat python in examples as an interpreter placeholder. Every operational verb is a cadgen subcommand ( python m cadgen.cli <verb is the PATH independent equivalent). Use cadgen <verb help for the complete current interface; reference docs show recommended workflows, not every flag. Install per requirements.txt ; cadgen doctor <skill dir verifies the installed cadgen matches this skill's pin (docs drift silently on a mismatched install). Target paths resolve from the command's current working directory, not from the skill directory. Run commands from the workspace that owns the artifacts and pass cwd relative target paths so project CAD files never resolve accidentally under the skill directory. CAD references are ... selector tokens local to a target, for example o1.2 or o1.2.f1 . Pass the STEP/CAD file as a separate target argument when using CAD CLIs. A model Generation has NO CLI. A model is a plain Python script: one parameterless decorated function, built by calling it from main : The rules, each enforced by the decorator or the build: The decorator only declares; a call builds. Importing a model module never builds; a file without if name == " main ": <model () never builds either — always end the script that way. python bracket.py writes bracket.step beside the script and the model's result into the store; an unchanged model is a fast no op. force rebuilds this model only. A model takes no parameters and its function is called with no arguments. Parametric geometry lives in a plain factory the model calls with its values ( def bracket(width, thickness): ... ); another configuration is another model in another file, the way two part numbers are two parts. The return is a bare build123d Shape — a solid, a compound, or a labeled assembly compound. Never a dict, never a path. Outputs are exactly what the decorators declare. @step writes the .step ; @stl / @threemf / @glb stacked on it write meshes. STEP is not required : a function with only @stl (or @glb , @threemf ) — no @step — is a full model with the same tree, record, build and no op, whose outputs are the meshes and which writes no .step and no sidecar. Use it for print only parts and render assets. references/supported exports.md . Decorator arguments never change the geometry. They decide where the files land ( out= ), how they are written ( mesh tolerance= , mesh angular tolerance= ) and what the sidecar declares ( kinematics= ). The geometry is the return value and nothing else: a Compound placing children is packaged as occurrences, a single solid as one component, and part / assembly is read off the tree. There is no kind= and no bake point — a posed or differently configured export is authored geometry, or another model. A sidecar only when strictly necessary. <name .step.json is written only when the model declares kinematics= ; a model that declares none has no sidecar, and a rebuild that dropped the declaration deletes the stale file. What a model declares about its outputs lives in its record, not in a file beside the geometry. One model per file, as a rule of thumb. A model's identity is its file plus its function ( plate.py::plate ); a file holding one model is named by its path alone. A file MAY hold several (a small family of variants): each is its own record, output and job (a sole model writes <file .step ; models sharing a file write <function .step ), but they share the file's closure, so editing one rebuilds them all — which is why one per file is the recommendation. Composition is a call. Import a sibling model and call it inside your body ( from arm import arm … arm() ); it returns the child's geometry. references/step generation.md has the whole composition contract. from cadgen import build123d as bd is the canonical import — a lazy, transparent re export of build123d (same names, same behaviour) — so the freshness gate and the warm worker handoff run before any kernel import is paid. Raw import build123d works but costs ~2.5s on every re run. Per run flags ride the script's argv: force , json , verbose , mesh tolerance , mesh angular tolerance . Composition, freshness and builds The essentials; references/step generation.md has the code and the edge cases. Children are models you call. A parent's body imports sibling models and calls them; each call returns that child's geometry (built if stale, loaded from the store if current), and the parent's result LINKS to the child's — stored once, shared by every parent. Place a child with Pos/Rot/Location child or child.moved(loc) ; never child.located(loc) (it deep copies the geometry, so the parent owns a copy instead of linking). Every build is parallel. A child call submits the child's build and returns at once; siblings build on their own workers while the body keeps going; the parent waits when it first reads the geometry — normally the closing bd.Compound(children=[...]) . Nothing to configure, nothing to annotate. Builds never wait on or cancel each other. Two runs of one model both run; the store keeps the result whose sources match the files as they are now, so the disk ends at the newer source. Editing a child while its parent builds leaves the parent finished against the child it pinned. A rebuilt part does not update the assemblies that use it. Dependency is pull: rebuild the parent ( python assembly.py ) to pick up a child's change. A child edit that yields identical geometry leaves parents current. What a rebuild tracks — models by result, constants by value, functions by file. Importing a model function tracks that model by its result; importing a module level literal ( from plate import WIDTH ) tracks the value; importing anything else from a file (a helper function, a bd. object) makes that whole file part of your model's source, so any edit to it rebuilds you. Shared constants may live in a model file or in lib/ . The environment is not an input. Model and lib/ code takes no parameter from os.environ , the working directory, the current time or a random source: the gate tracks source by hash, constants by value and children by result, and cannot see any of those — a value that changes geometry through them leaves a stale result reading as current. A configuration is a factory argument; another configuration is another model. A mirrored part is its own model. STEP cannot express a reflection, so a right hand part is a separate model file calling the same factory with mirror=True (or mirroring the factory's result), not a mirrored child. read step files are inputs, not models. Replacing the file makes the reader stale. To make an imported part first class, wrap it: @step def servo(): return read step(...) . cadgen store why <model .py is the freshness door: it prints the gate's verdict clause by clause (record, closure files, constants, each child's pinned vs current tree, tree objects, declared outputs). Reach for it whenever a model did or did not rebuild when you expected it to. Workers. A warm daemon is on by default: each model gets a persistent worker (a second, an extra , when the model is asked for while already building); spares stand by so a new model never pays the import; idle workers unbind after ten minutes. Running builds are limited to one per core ( CADGEN JOBS overrides); a parent waiting on its children hold