Skip to content

About

An agent-native CAD SDK for language models to create, inspect, and reconstruct complex, editable 3D models.

Topics

Resources

Stars

144 stars

Watchers

1 watching

Forks

Repository files navigation

SimpleCADAPI repository cover

SimpleCADAPI

中文说明

Start here. Paste this line to your coding agent (Claude Code, Codex, OpenCode, …):

Read physpace.dev/sca/landing.md and landing me on this sdk.

It installs the SDK, installs the skill for itself, opens a real example in the code-first editor, and shows you how to work with it. SimpleCADAPI is a parametric CAD SDK built for agents: a model is a notebook of code, every dimension is a named parameter, selection is by what geometry is, and every step reports back why it worked or failed.

What Can It Do

Four capability pillars. Every case below is a real, reproducible session: the replay demos are single-file offline HTML pages that faithfully replay the full recorded conversation (user turns, agent thinking, every tool call and patch, role switches) next to the exported 3D model — open them directly in a browser.

1 · Single-Part Modeling

Parametric parts authored as readable Feature Tree Convention feature blocks — named parameters with units, verifier-first staged modeling, deterministic face tags that survive re-parameterization, and synchronized .scadpkg / STEP AP242 / STL / editable FreeCAD exports.

Case First-turn requirement (excerpt) Delivered model · BRep turntable
Robotic-arm U-link + motor mount
15 user turns · 601 tool calls
▶ Full session replay
"Build a parametric robotic-arm link that adapts to length changes and mounts motors generically: sweep a circular profile down D → right L → up D into a U; cut motor sockets with spheres; the mounting face must carry a deterministic name indexable by the query language; split the bottom cylinder into a half cylinder; then fillet everything." u_link turntable
Parametric flange plate
2 user turns · 66 tool calls
▶ Full session replay
"Parametric flange: OD 100, thickness 10, hub ⌀55 top +30, bore ⌀30, 6×⌀11 bolt holes on PCD 78, hub-root R3 / rim R2 fillets; every dimension a named parameter; edge-selection cards printed before each fillet; parameter-feasibility guards; one feature per block."

GIF shows the final 8-hole @PCD 84.5 state: turn 2 re-parameterized 6→8 holes; PCD 88 was rejected live by the guards and 85 excluded for fillet tangency.
flange turntable
Ribbed L-bracket (FEM main model)
2 user turns · 49 tool calls
▶ Full session replay
"Formalize the existing legacy L-bracket script through the single-part workflow: rebuild with FTC feature blocks and named parameters; geometric equivalence to the legacy model (volume delta < 0.1%); the interface.* FEM boundary tags must survive untouched — the downstream Gmsh/CalculiX pipeline selects faces by them." bracket turntable

Turntables are rendered with BRep shading and wide edges; one full turn = 48 deterministic azimuth steps.

2 · Assembly Modeling

Nested durable assemblies with explicit kinematics: gear meshes, revolute joints, and bearing interfaces are solved constraints, not eyeballed positions. Standard parts — involute gears, ball bearings (as subassemblies with individual balls), roller-chain sprockets, metric fasteners with real thread profiles — come from scad.std.* and compose into mechanisms that export to STEP, editable FreeCAD projects, and MJCF for physics engines.

BLDC joint actuator assembled
Integrated BLDC joint actuator — assembled, studio render
29 components · 51 constraints, all solved · 20:1 two-stage planetary
BLDC joint actuator exploded turntable
Same model, exploding turntable — four modules (ESC · motor ·
two-stage reducer · output shaft) separate along the axis while
concentric parts peel into radius bands; camera circles on an inclined orbit

Reproduce it: the model is the integrated_bldc_joint_actuator.py assembly notebook, composed with scad.use from part and sub-assembly notebooks. uv run python examples/integrated_bldc_joint_actuator/export_all.py emits the package plus STEP / editable FCStd / MJCF; render_showcase.py renders the views above.

3 · Code-first Editor

sca edit opens a browser editor over a directory of notebooks, built for working with an agent. The notebook's cells are on the left and the live model is in the middle; a slider or toggle appears for every parameter. Click a solid, face, edge or vertex and the editor writes a scad.ql selection cell into the notebook file (autosaved), so your coding agent reads what you picked from disk and "chamfer these two edges" needs no ids. Copy a cell's reference to tell the agent which cell to change. A log dock shows which cells are being re-evaluated. The editor and sca run share one runtime and one per-cell cache.

sca edit on the u_link motor mount: a picked face, the selection cell it wrote, its properties
u_link motor mount in sca edit — a picked cylindrical face, the selected_face cell the editor wrote for it, and its properties; sca example u_link copies this workspace out

4 · Simulation Plugins

The same parametric package feeds downstream solvers without manual rework: AP242 STEP into Gmsh for volume meshing and CalculiX for static FEM (boundary faces are selected by preserved interface.* tags, so simulation survives model revisions), and MJCF into MuJoCo for mechanism dynamics — where a virtual-collision pass catches assembly interference and drives the correction before the mechanism runs clean.

Bracket FEM von Mises
L-bracket static FEM — CalculiX von Mises
meshed via Gmsh OpenCASCADE kernel from the AP242 export
Four-bar assembly with links crashing into each other in MuJoCo
Four-bar linkage + MuJoCo — first assembly pass:
the links crash through each other mid-cycle; the assembly is wrong · ▶ full video
Corrected four-bar assembly articulating clean in MuJoCo
After the virtual-collision correction — corrected assembly:
the full crank cycle sweeps clean in the same sim · ▶ full video

Update Notes (2.2.3)

Validate generated definitions, assembly constraints, and manufacturing geometry before production use.

  • The editor takes in edits made to the notebook file. An agent's saved change is applied cell by cell (changed cells update and run again, no reload), and is no longer written over by the editor. If a cell was edited in both places, the file's version is kept and your unsaved text goes to the log. Notebooks added to the workspace appear in the list by themselves.
  • sca cell and sca new. sca cell ls|get|put|insert|rm|run edits the open notebook's cells through the running editor (the page updates live, each write prints the cell's status and errors); sca new NAME creates a notebook the editor then lists.

Full notes for this version: docs/updates/2.2.3.md. Earlier versions: the changelog and docs/updates/.


SimpleCADAPI Research Artifact

This repository is an artifact of

CADIR: A Cross-Backend Editable Intermediate Representation for Agentic CAD Generation


SimpleCADAPI is an OCP-native Python SDK for building CAD models with clear, functional operations and replayable model graphs. It wraps OpenCascade geometry in a compact public API for creating solids, applying features, tagging semantic intent, querying topology, exporting manufacturing files, and translating recorded models into FreeCAD workflows.

Current release: simplecadapi==2.2.3.

What It Provides

  • Models as marimo notebooks: plain .py files that are the only source of truth, one feature per cell, a per-cell cache for incremental headless runs (sca run), and notebook composition (scad.use).
  • A code-first web editor, sca edit: live model, parameter controls, viewport picks written back as selection code, built for working alongside an agent.
  • OCP-native shape types: Vertex, Edge, Wire, Face, and Solid.
  • Functional modeling operations for primitives, profiles, extrude, revolve, loft, sweep, booleans, transforms, patterns, fillets, chamfers, and shells.
  • Canonical .scadpkg product packages generated from the notebooks for exchange and publishing; they are never read back as model input.
  • Replayable operation graphs with explicit GraphSession, export_model_json(...), import_model_json(...), and replay_model_json(...).
  • Expression parameters with var(...), arithmetic expressions, and serialized expression graphs.
  • Physical units with automatic dimension inference, canonical CAD conversion, and manufacturing tolerance-chain validation.
  • QL selectors for geometry grounding, topology queries, and stable feature selections.
  • Semantic tags through apply_tag(shape=..., tag=...) and list_tags(shape=...).
  • STEP/STL export, editable FreeCAD product-package translation, and AP242 product-structure export with material and named metadata properties.
  • Agent-oriented STEP/BREP reconstruction with stable entity IDs, focused local diagnostics, highlighted region renders, and measured acceptance gates.
  • Replayable open and periodic interpolated B-spline Edges/Wires for freeform profiles and Loft sections.

Install

The shortest way is the line at the top of this page: paste it to your coding agent and it does the rest.

By hand:

pip install simplecadapi

The code-first editor (sca edit) needs its extra, pip install "simplecadapi[editor]"; uv tool install "simplecadapi[editor]" or pipx install "simplecadapi[editor]" put sca on your PATH without a virtual environment. sca example u_link copies a bundled example workspace out to open with sca edit.

With uv:

uv add simplecadapi

For local development from this repository:

uv sync --group dev

Agent Skill

After installing with pip above, install the bundled skill for your agent harness:

sca skill targets
# Default location (~/.agents/skills), or select ZCode's explicitly:
sca skill install --target zcode --skills-dir ~/.zcode/skills

targets lists the available harness targets. install compiles the bundled source and writes it to <skills-dir>/simplecadapi; without --skills-dir, it uses the existing addon configuration/environment resolution, falling back to ~/.agents/skills; sca init is not required. The wheel includes the uncompiled docs/skill/ source tree and skillproj.toml as package resources — compilation happens on install, no repository checkout needed. With uv, prefix these commands with uv run.

install fails if the destination already exists. Add --force to replace it only if it is a directory whose SKILL.md declares the matching skill name (simplecadapi); unrelated directories cannot be overwritten.

Quick Start

A model is a marimo notebook: a plain .py file in your repository, which stays the only source of truth. A [tool.simplecadapi] table in its PEP 723 header names the product; each cell holds one feature block and records its geometry automatically — no session or decorator boilerplate.

# bracket.py
# /// script
# requires-python = ">=3.10"
# dependencies = ["simplecadapi"]
#
# [tool.simplecadapi]
# id = "bracket"
# revision = "1.0.0"
# ///
import marimo

app = marimo.App()

with app.setup:
    import simplecadapi as scad


@app.cell
def _():
    # ---- params: plate ----
    width = scad.var("width", 60.0, unit="mm")
    return (width,)


@app.cell
def _():
    # ---- params: bore ----
    hole_radius = scad.var("hole_radius", 5.0, unit="mm")
    return (hole_radius,)


@app.cell
def _(width):
    # ---- feature: base-plate (build) ----
    base = scad.make_box_rsolid(
        width=width, height=36.0, depth=8.0, bottom_face_center=(0.0, 0.0, 0.0)
    )
    return (base,)


@app.cell
def _(base, hole_radius):
    # ---- feature: bore-and-slot (subtract) ----
    _bore = scad.make_cylinder_rsolid(
        radius=hole_radius, height=14.0, bottom_face_center=(0.0, 0.0, -3.0)
    )
    _slot = scad.make_box_rsolid(
        width=18.0, height=8.0, depth=14.0, bottom_face_center=(14.0, 0.0, -3.0)
    )
    drilled = scad.cut_rsolid(base, _bore, _slot)
    return (drilled,)


@app.cell
def _(drilled):
    # ---- feature: boss (add) ----
    _boss = scad.make_cylinder_rsolid(
        radius=8.0, height=7.0, bottom_face_center=(-18.0, 0.0, 8.0)
    )
    bossed = scad.union_rsolid(drilled, _boss)
    return (bossed,)


@app.cell
def _(bossed):
    # ---- feature: role-tag (annotate) ----
    tagged = scad.apply_tag(shape=bossed, tag="role.demo.bracket")
    return (tagged,)


@app.cell
def _(tagged):
    bracket = scad.Part(part_id="bracket", body=tagged)
    return (bracket,)


if __name__ == "__main__":
    app.run()

The product is the top-level value whose id equals the notebook id. Edit the notebook interactively, or run it headlessly with only simplecadapi installed:

marimo edit bracket.py                                    # reactive editor
sca run bracket.py                                        # run, print a JSON report
sca run bracket.py --set width=80 --out out/bracket.scadpkg
sca export out/bracket.scadpkg --output-dir out/exports   # AP242 STEP, STL, OBJ
sca edit .                                                # web editor over this directory of notebooks

sca edit (pip install "simplecadapi[editor]") is the code-first editor described above: the notebook's cells on the left, the live model in the middle, a slider or toggle for every parameter (# ---- params ---- cells; a # ui: comment on the line picks the control), and your viewport picks written back as selected_* selection cells. See docs/skill/references/docs/guides/notebook-runtime.md and docs/skill/references/domains/editor-collaboration.md (how an agent works with what you picked and which cell you mean).

Notebook Runtime

The same runtime runs a notebook in the marimo editor, under sca run, and from Python with simplecadapi.runtime.run_notebook(). Headless runs cache every cell, so a rerun only executes the cells whose code, inputs, local modules, or child notebooks changed. --set overrides a top-level variable (the cell that defines it is skipped, so group parameters into cells by what changes together); the report lists each cell as ran, cached, or skipped.

Export and verification scripts are plain Python that load the product with run_notebook and write packages from it:

from pathlib import Path

import simplecadapi as scad
from simplecadapi.runtime import run_notebook

out = Path("out")
run = run_notebook("bracket.py", overrides={"width": 80.0})
print("volume", round(run.product.body.get_volume(), 3))
print("tags", scad.list_tags(shape=run.product.body))

package = out / "bracket.scadpkg"
scad.capture(run.definition, package)
scad.exporter.export_product_package_to_step(package, out / "bracket.step")
scad.exporter.export_product_package_to_stl(package, out / "bracket.stl")
scad.exporter.export_product_package_to_obj(package, out / "bracket.obj")

The .scadpkg embeds the definition closure, evaluated scene, feature graphs, source snapshots, topology, and render/selection assets. It is generated for exchange and publishing, never read back as model input.

Composing notebooks

An assembly notebook brings in other notebooks with scad.use. A relative path is relative to the calling notebook, and keyword arguments override the child's top-level variables:

@app.cell
def _():
    bracket = scad.use("bracket.py", width=80.0)
    return (bracket,)


@app.cell
def _(bracket):
    rig = scad.make_assembly_rassembly(assembly_id="rig", name="Rig")
    rig = scad.add_component_rassembly(
        assembly=rig, item=bracket, component_id="bracket",
        placement=scad.identity_placement_rplacement(),
    )
    return (rig,)

One notebook can describe a part family — parts that differ only in their parameters, such as the links of a linkage. Its product takes part_id=scad.notebook_id(), and each use names its member: scad.use("link_bar.py", id="crank", center_distance=40.0) (or sca run link_bar.py --id crank). Each member is one definition with its own content hash and cell cache.

@scad.part / @scad.assemble remain for reusable library builders in plain modules next to the notebooks; cells call them and take .value. See the notebook runtime and product build workflow for the project layout, the cell cache, and downstream targets.

Migrating from @scad.part scripts

The script-anchored part cache of 2.1.2 (the cache= builder option, the .simplecad cache directories, and the incremental assembly report) is removed; the cell is now the unit of caching.

  • Move each part into a notebook: its parameters into params cells, each FTC block into a cell of its own (each cell binds a new name), and the final scad.Part(...) / assembly into the last cell.
  • Replace main.py entry points that build and export as a side effect with export scripts that call run_notebook and scad.capture.
  • Replace direct builder calls between products with scad.use(...); keep @scad.part builders only for library parts called from cells.
  • Add __marimo__/ (generated runtime state) to .gitignore.

Command-line product export

Export the standard delivery set (AP242 STEP, binary STL, and OBJ) from a validated product package with no wrapper script:

uv run sca export out/bracket.scadpkg --output-dir out/exports

Request additional targets explicitly. FCStd requires FreeCADCmd (or an explicit --freecad-cmd path); --check validates the package, output paths, and selected target prerequisites without writing files.

uv run sca export out/bracket.scadpkg \
  --format fcstd --format mjcf --output-dir out/exports --check
uv run sca export out/bracket.scadpkg \
  --format fcstd --freecad-cmd /path/to/FreeCADCmd --output-dir out/exports

STL and OBJ share one direct OpenCASCADE tessellation of the evaluated BREP. Both outputs contain the same oriented triangles and require no optional remeshing dependency. Control curved-surface accuracy with linear_deflection and angular_deflection_degrees.

Optional CalculiX FEM workflow

The AP242/Gmsh example also includes an optional CalculiX FEM workflow. Install the Python-side FEM dependencies with uv sync --extra fem, and install the external CalculiX solver separately (on macOS: brew install costerwi/homebrew-calculix/calculix-ccx). The example uses consistent mm, N, and MPa units:

uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py \
  --ccx "$(brew --prefix calculix-ccx)/bin/ccx_2.23"
uv run --extra fem python examples/ap242_gmsh_volume_mesh/visualize_calculix.py
uv run --extra fem python examples/ap242_gmsh_volume_mesh/study_mesh_convergence.py \
  --ccx "$(brew --prefix calculix-ccx)/bin/ccx_2.23" \
  --linear-solver "ITERATIVE CHOLESKY" --solver-timeout 2400

The analysis writes CalculiX .inp, .dat, .frd, solver-log, summary JSON, ParaView .vtu, and displaced von-Mises PNG artifacts. The preview shows the load physical group's yellow boundary and red -Z force arrows without covering the stress heatmap. The convergence study supports --resume; failed solver levels are reported separately and never enter the numerical sequence.

The checked-in -1000 N study evaluates eleven mesh sizes from h=3.0 mm to h=0.25 mm. A platform requires three consecutive refinement pairs below 5% maximum-displacement change and 10% peak integration-point von-Mises change. The verification level N is h=0.25 mm (0.0331843 mm, 98.6392 MPa), so the recommended production level N-1 is h=0.27 mm. Fine levels use iterative Cholesky after a same-mesh comparison at h=0.375 mm matched SPOOLES within 0.005%; this avoids the direct solver's in-memory capacity limit.

Replayable Operation Graphs

Use an explicit GraphSession when a geometry flow outside a notebook should be inspectable, serializable, or replayable.

import simplecadapi as scad
from simplecadapi import GraphSession, export_model_json, replay_model_json
from simplecadapi import ql as Q

with GraphSession(graph_id="chamfered_block") as session:
    body = scad.make_box_rsolid(
        width=40.0, height=24.0, depth=10.0,
        bottom_face_center=(0.0, 0.0, 0.0),
    )
    cutter = scad.make_cylinder_rsolid(
        radius=4.0, height=16.0, bottom_face_center=(0.0, 0.0, -3.0)
    )
    drilled = scad.cut_rsolid(body, cutter)

    bottom_circle = (
        Q.edges()
        .where(Q.curve_type(kind="circle"))
        .order_by(Q.center_axis(axis="z"))
        .take(1)
        .exactly(1)
    )
    final = scad.chamfer_rsolid(solid=drilled, edges=bottom_circle, distance=0.6)
    session.capture_result(value=final)
    model_json = export_model_json(session=session)
    recorded_nodes = session.graph.node_count

rebuilt = replay_model_json(json_str=model_json)
print("recorded_nodes", recorded_nodes)
print("replayed_outputs", len(rebuilt))

An explicit GraphSession is for inspection, serialization, and replay outside a notebook; notebook cells already record into sessions of their own, so do not open one there. It stays in memory until an export API is called. A durable CAD/viewer deliverable is a notebook product captured to .scadpkg (see Notebook Runtime).

STEP/BREP Inspection

Install the optional rendering dependency when synchronized STEP views, highlighted regions, or slice overlays are needed:

pip install "simplecadapi[inspect]"

Inspection lives under simplecadapi.inspect.brep. These APIs are diagnostic tools, not modeling operations: they do not enter the graph and are rejected inside GraphSession. Export or obtain the geometry first, then inspect it outside the modeling script.

Choose calls from the evidence required by the case instead of following a fixed reverse-engineering pipeline. Start with bounded global and local facts; add sections, component renders, boundary distance, material difference, or strict topology comparison only when those facts answer the current question.

from simplecadapi.inspect import brep

summary = brep.inspect_step_rsummary(
    path="target.step",
    include_parameter_groups=True,
)
face = brep.inspect_step_entity_rdescriptor(
    path="target.step",
    entity_id="face:0",
)

print("faces", summary["face_count"])
print("carrier", face["geometry"]["type"])

Use the Reconstruction Agent test specification for controlled runs and the STEP reconstruction workflow for the inspection primitives, modeling loop, replay checks, and acceptance gates.

Physical Units And Tolerances

Declare nominal and manufacturing-tolerance units at the variable boundary. SimpleCAD evaluates lengths in millimeters and angles in degrees while preserving the declaration units in model JSON:

import simplecadapi as scad

width = scad.var(
    "width",
    1.0,
    unit="in",
    tolerance=0.1,
    tolerance_unit="mm",
)
height = scad.var("height", 40.0, unit="mm", tolerance=0.2)
diagonal = scad.sqrt(width**2 + height**2)

analysis = scad.analyze_tolerance(diagonal)
check = scad.check_tolerance(diagonal, 0.3, tolerance_unit="mm")

print(analysis.dimension.name, analysis.unit.symbol)
print(analysis.nominal, analysis.lower_bound, analysis.upper_bound)
print("passes", check.passed)

Addition and subtraction require matching dimensions. Multiplication, division, integer powers, and square root derive dimensions. Trigonometric functions require angle or dimensionless inputs as appropriate. Legacy variables without unit remain supported, but cannot be mixed with unit-declared variables in one expression.

Modeling Mental Model

  • Start from design intent: reference axes, critical profiles, and the features that produce the final solid.
  • Build from lower-dimensional geometry to higher-dimensional geometry: profile wires/faces first, then solid features such as extrude, revolve, loft, and sweep.
  • Keep operations functional. Create new values with public functions such as make_rectangle_rface(...), extrude_rsolid(...), cut_rsolid(...), and fillet_rsolid(...).
  • Use tags for semantic intent and selection anchors, for example role.mounting.surface, anchor.datum.primary, or group.fasteners.
  • Store numeric and geometric facts in metadata or graph payloads, not in tags.
  • Use QL to ground selections by geometry facts rather than relying on topology iteration order.
  • When an indexed topology pick is intentional, pass the index to the plural child-geometry getter, such as get_edges(index), get_faces(index), get_wires(index), or get_vertices(index), so replayable graph workflows preserve the pick as a geo select node.

Use model JSON only for graph replay and inspection. External CAD translation and file export consume the validated .scadpkg closure:

script = scad.translator.freecad_translator.translate_product_package_to_freecad_script(
    package_path
)
scad.translator.freecad_translator.translate_product_package_to_fcstd(
    package_path, "bracket.FCStd"
)

Part/Assembly models are written as editable FreeCAD assembly structure: parts are App::Part, assemblies are Assembly::AssemblyObject, and components are links. The exporter namespace owns neutral STEP and STL file output.

Examples

Every example is a self-contained folder: part and assembly notebooks, the plain modules they import, export and verification scripts, and fresh artifacts under examples/<name>/out/. The set covers part modeling, assemblies, reverse engineering, and FEM — see the category index in examples/README.md.

# part (quickstart FTC notebook, external verification script included)
marimo edit examples/flange_plate/flange_plate.py
uv run python examples/flange_plate/verify.py

# assembly (two-stage planetary reducer: part-family notebooks, MJCF export)
sca run examples/compact_two_stage_planetary_reducer/compact_two_stage_planetary_reducer.py
uv run python examples/compact_two_stage_planetary_reducer/export_mjcf.py

# FEM (AP242 STEP -> Gmsh volume mesh -> Calculix statics)
uv run python examples/ap242_gmsh_volume_mesh/export_step.py
uv run --extra gmsh python examples/ap242_gmsh_volume_mesh/export_fem_mesh.py
uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py

To try the editor on a real model, sca example u_link copies a motor mount (two part notebooks and the assembly that fixes them) out of the package, and sca edit u_link_motor_mount opens it; from a checkout, sca edit examples/u_link_motor_mount. Reconstructing a STEP file (see examples/bowl_connector/) is driven by the agent through the inspection API and the STEP reconstruction workflow.

Documentation

Releasing the Agent Skill

The skill source tree lives under docs/skill/ and stays harness-neutral. tools/skillbuild.py wraps the same compiler the sca skill CLI uses, for maintainer builds in this repository: it compiles per harness target (targets and default output directories are configured in skillproj.toml; the outputs under skills/simplecadapi-*/ are regenerable build artifacts and are not committed). Harness-specific text, when it is ever needed, is marked with <!-- skill:if ... --> conditional blocks that compile per target. The wheel itself never contains precompiled targets — end users install from the bundled source with sca skill install.

From a clean checkout, update the project version and documentation, then build and validate the release artifacts:

uv sync --group dev
uv run python tools/auto_docs_gen.py --quiet
uv run python tools/skillbuild.py --target omp
uv run pytest test/test_skill_build.py
tar -C skills -czf skills/simplecadapi.tar.gz simplecadapi-omp

The commands regenerate the API references inside the skill tree, recompile the omp target, and create skills/simplecadapi.tar.gz. Review the source tree and the compiled output before release:

git diff -- docs/skill
tar -tzf skills/simplecadapi.tar.gz | head

Commit only the docs/skill/ source tree; the release workflow builds the harness outputs and uploads the archive to the GitHub release. Prefer sca skill for installation from a pip-installed package.

Development

uv sync --group dev
uv run python -m pytest test tests
python3 -m compileall src/simplecadapi

License

Apache-2.0, see LICENSE.

Community

The group chat currently has too many members for direct QR-code joining. Scan the QR code below to add Teacher Du Peng on WeChat, then ask him for an invitation to the CADDesigner technical community:

Teacher Du Peng's personal WeChat QR code

Star History

Star History Chart

About

An agent-native CAD SDK for language models to create, inspect, and reconstruct complex, editable 3D models.

Topics

Resources

Stars

144 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages