Dxf logo

Dxf

CommunityPopular
earthtojake
dxf

Generate, regenerate, and validate 2D DXF drawings from Python build123d sources. Use for DXF files, `.py` drawing scripts, @dxf models, 2D profiles, outlines, templates, gaskets, panels, flat patterns, laser/plasma/waterjet cut layouts, and 2D drawing exports of CAD geometry.

Overview

Publisherearthtojake
Repositorytext-to-cad
Skill namedxf
Stars
16K
Forks
1.7K
Bundled files
3
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by earthtojake on GitHub. Read the source before you install it.

Installation

Install the Dxf AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/earthtojake/text-to-cad.git /tmp/text-to-cad
mkdir -p .claude/skills
cp -r /tmp/text-to-cad/skills/dxf .claude/skills/dxf
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Dxf in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Dxf on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Dxf is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

DXF generation and validation

Provenance: maintained in 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:

bash
python -m pip install -r requirements.txt

Drawings are build123d geometry, so a drawing build loads the CAD kernel like a STEP build does (~2.5s cold; the warm daemon absorbs it on re-runs). Only cadgen dxf snapshot additionally needs Node 20 or newer on PATH — it meshes the flat pattern on demand through a bundled Node one-shot; a missing node is reported at render time.

Purpose

Create or modify 2D DXF drawings from natural-language requirements or from CAD geometry, generate validated drawing artifacts, and return checked outputs. A DXF drawing's source of truth is a Python file named <name>.py defining one parameterless @dxf model function.

A drawing is a model. It has the same wrapper, record, freshness gate and build job a @step part has; its one output is the .dxf file; it has no geometry tree (nothing links to a drawing). Every run writes the sibling <name>.dxf (or the out= the decorator names); an unchanged source is a no-op; a drawing that calls a part model — bracket() inside its body — is stale whenever that part's GEOMETRY changes and current when it does not; cadgen store why <drawing>.py explains the verdict; --force rebuilds it anyway. The CAD Viewer and dxf snapshot read the .dxf file itself, so the file you hand a cutting service and the file the viewer renders are one and the same.

The contract

A @dxf function takes no parameters and returns build123d 2D geometry. The engine writes the DXF. You never construct a document, name a file, or place an entity — the same division of labor @step has.

python
from cadgen import build123d as bd
from cadgen import dxf


HOLE_D = 4.5


@dxf
def gasket():
    with bd.BuildSketch() as cut:
        bd.Rectangle(60, 40)
        bd.Circle(HOLE_D / 2, mode=bd.Mode.SUBTRACT)
    return cut.sketch          # bare shape -> the CUT layer


if __name__ == "__main__":
    gasket()
  • Bare shape → one CUT layer. That is the whole contract for most drawings.
  • {layer: shape} → named layers, when the drawing genuinely has more than one CAM operation (CUT / ENGRAVE / SCORE). A Compound whose children are all labelled means the same thing.
  • No parameters. Dimensions are module constants (HOLE_D = 4.5) or constants imported from the part the drawing derives from; a different drawing is a different file.
  • Text is bd.Text(...) engraved OUTLINES on a marking layer, never a DXF TEXT entity: cut and marking toolchains consume geometry, and font rendering inside CAM is unreliable.
  • Geometry must lie in the XY plane. A face taken from a solid sits at that solid's height; relocate it (flatten.flatten_face(face), or bd.Location((0, 0, -z)) * face). The engine REFUSES off-plane geometry rather than silently writing its XY shadow.
  • Output bytes are a function of the geometry. Layers are sorted by name and entities by geometric content, so an unchanged drawing rebuilds to an identical file, cold or warm, on any machine.

The three DXF workflows

Copy the full template for the applicable workflow from references/generator-templates.md when creating a new drawing.

  1. Drafted from scratch (gaskets, panels, templates, cut layouts with no 3D model behind them): a <name>.py that builds sketches and returns them.

  2. Flat pattern of a generated STEP part: a drawing script beside the model it derives from, with its OWN stem (one model per file — bracket_drawing.py beside bracket.py). Import the model and call it, exactly as an assembly composes a child: importing never builds, and inside the drawing's build the call returns the part's geometry (building the part first if it is stale).

    python
    from cadgen import dxf, flatten
    from bracket import bracket        # a child: tracked by its RESULT
    
    KERF = 0.15
    
    
    @dxf
    def bracket_drawing():
        return flatten.flat_pattern(bracket(), coordinate=3.0, kerf=KERF)
    
    
    if __name__ == "__main__":
        bracket_drawing()

    The drawing's record pins the part's tree, so a part edit that changes its geometry makes the drawing stale, and one that does not (a comment, a refactor, a colour) leaves it current. Constants imported from the part (from bracket import THICKNESS) are tracked by value the same way.

  3. Flat pattern of an imported STEP (a .step/.stp with no Python source): read it with cadgen.read_step, not build123d.import_step. It records the file's content hash as a build INPUT, so replacing the vendor STEP makes the drawing stale on its own, with no --force; read it through build123d and the drawing stays "current" against a file that changed underneath it.

    python
    from pathlib import Path
    
    from cadgen import dxf, flatten, read_step
    
    _HERE = Path(__file__).resolve().parent
    
    KERF = 0.15
    
    
    @dxf
    def panel_flat():
        panel = read_step(_HERE / "imported" / "vendor_panel.step")   # recorded input
        return flatten.flat_pattern(panel, coordinate=3.0, kerf=KERF)
    
    
    if __name__ == "__main__":
        panel_flat()

    Never read a STEP this project generates. Reading the .step a @step model writes is not a loop, it is a drawing whose input changes on every run of the model: the freshness gate can never say "current", every build is a full rebuild, and the flat pattern depends on what the last run left on disk. Keep source STEPs in an imported/ directory beside the drawing, committed like any other input — input path and output path being different files is the whole rule. For a STEP this project DOES generate, use workflow 2 instead: import the model script and call it, which is tracked by result and never touches an artifact.

One model per file is the recommendation, and a drawing gets its own script: a file MAY declare several models — two @dxf drawings, or a @dxf beside a @step — and each is its own record, output and job (a sole model writes <file>.dxf; models sharing a file write <function>.dxf), but they share the file's closure, so editing one rebuilds them all. A drawing composes models, never the reverse: calling a @dxf function from a @step body is just its 2D geometry and links nothing. The viewer catalog is artifacts-only: scripts never list; the .dxf the run writes is the entry the viewer renders.

Use this skill when

Use this skill when the user asks for DXF files, 2D drawings, profiles, outlines, templates, gaskets, panels, flat patterns, or cut layouts for laser, plasma, waterjet, or CNC routing.

Use $cad for the 3D part or assembly a DXF derives from. Use $sendcutsend for SendCutSend-specific upload preflight.

Defaults

Use these defaults unless the user specifies otherwise:

  • Units: millimeters. The engine sets them; a drawing never declares units.
  • Geometry lives at 1:1 scale in the XY plane.
  • Cut profiles close. Open contours belong on bend/engrave/reference layers — generation validation enforces this (see Validation).
  • For CAD-backed parts, derive contours from the real topology with cadgen.flatten rather than redrawing them: planar_faces selects, flatten_face lays a face into XY exactly, union_faces fuses, and flat_pattern does all of it in one call. Hand-drawn parametric outlines only when there is no reliable 3D topology.
  • Kerf / tool-radius compensation is flatten.offset_profile(shape, amount) or flat_pattern(..., kerf=...); never hand-offset coordinates.
  • Curves stay curves. The union and the offset are exact OCC operations, so a filleted corner exports as an ARC and a hole as a CIRCLE, kerf included. A profile that comes out as hundreds of short LINEs means something fell back to the sampled path — investigate rather than accept it.
  • Layers carry intent: keep cut geometry and bend/fold lines on separate layers, and include "bend" in bend-layer names so downstream tools classify them as bends rather than cuts.
  • DXF layers are drawing structure, not STEP part/assembly structure.

Tool

bash
python <drawing>.py [flags]                    # its __main__ calls the @dxf model, which writes the .dxf
cadgen dxf snapshot <drawing.dxf> <file.png>   # render it
cadgen store why <drawing>.py                  # why the drawing is stale or current

Running the script (its __main__ call) is the only door. There is no cadgen dxf build: a .dxf has no derived state a command must materialize — the file IS the product, the CAD Viewer parses it directly, and dxf snapshot meshes it on demand. The drawing's gate makes a rebuild cheap: an unchanged source whose .dxf still verifies and whose part children are unchanged is a no-op, and --force rebuilds anyway. The bytes are a function of the drawing's GEOMETRY, so a cold run and a warm daemon worker write the same file. Builds never wait on or cancel one another; a drawing that calls parts builds them in parallel like any parent.

An imported .dxf needs nothing at all — hand it straight to snapshot or the Viewer.

Use the active project Python interpreter; treat python as an interpreter placeholder, and use --help for the full interface. Target paths resolve from the command's current working directory; run from the workspace that owns the artifacts with cwd-relative target paths. Keep a drawing script in the same directory as the geometry it derives from, named <name>.py.

Flags (a model script runs itself; there is no generation CLI):

  • --force — regenerate even when the recorded output is current.
  • --verbose, --json.

A run answers on stdout exactly as a STEP model's does — built DXF/plate_drawing.dxf or current DXF/plate_drawing.dxf — with progress on stderr; --json makes the result one JSON line (outcome, document, and tree, which is null for a drawing) and the progress one JSON line per transition.

One script, one drawing: run each script you want built. Do not put output paths in the @dxf function's return value; out= on the decorator is the only place a drawing names its destination (relative to the script).

cadgen dxf snapshot renders a drawing's 3D flat pattern to a PNG still:

bash
cadgen dxf snapshot path/to/imported.dxf review.png
cadgen dxf snapshot path/to/drawing.dxf review.png --camera top

It takes the .dxf document only — a model script is refused by name (run python <drawing>.py, then snapshot the drawing it wrote). The command meshes the flat pattern on demand through the bundled Node one-shot and renders it through the shared snapshot CLI (cadgen.snapshot_cli) and the same headless browser runtime every rendering skill uses. A normal snapshot uses deterministic light CAD lighting and hides grid and axis guides.

OUT — the second positional — is written exactly as given, with a relative path resolved against the current working directory. The target is deleted before the render starts and the finished image is written atomically, so: reuse one name while iterating (every read is provably the render you just ran), name the iterations when you genuinely need to compare two. Invalid request combinations fail before touching OUT; after a request is accepted, OUT is cleared first so a later failure leaves a missing file instead of a stale image. A directory (tmp/ as OUT) is the don't-care case and gets a generated timestamped name inside it, printed on the saved snapshot: line.

Grammar: cadgen dxf snapshot TARGET [OUT] [flags]. Flags: --mode view|list, --camera, --render, --display, --size-profile, --width/--height, --job, --view-labels, --debug, --json. --render opts into the photographic scene and accepts light, dark, compact Render JSON, or a file path. Set the photographic camera inside Render JSON. Top-level --camera and --display control normal drawing snapshots and cannot be combined with Render. A drawing has no selectors, kinematics, section mode, exploded assembly structure, or CAD-edge topology, and those combinations are absent or rejected clearly.

No CLI inspects an existing .dxf. For entity/layer checks read it with ezdxf directly (it arrives with build123d), and validate_dxf_file for the drawing checks; review geometry visually with $cad-viewer.

Workflow

  1. Convert the request into a short brief: outline dimensions, holes and slots, layers, units, output path, and validation targets.
  2. Pick the workflow: drafted from scratch, flat pattern of a generated model (create and validate the 3D geometry with $cad first), or flat pattern of an imported STEP.
  3. Write or edit the <name>.py source with meaningful dimensions as named constants, reusing the model's geometry helpers instead of duplicating formulas.
  4. Run each drawing script directly (python <drawing>.py); do not sweep directories.
bash
python path/to/source.py
python path/to/source.py --force
  1. Validate the generated DXF deterministically, then hand off and report.

Viewer integration

The CAD Viewer catalogs .dxf files only (artifacts, never scripts) and is a static visualization tool: it renders the .dxf that exists on disk (parsing and meshing it itself — 2D line work for dimensioned drawings, a fold-able 3D flat pattern for cut layouts) and never runs a script. A drawing with no .dxf yet simply does not appear until its script has been run; regenerating after edits is likewise the script's job. There is no in-viewer export. An imported .dxf renders directly with no artifact management.

Validation

Validation happens IN generation, not after: every @dxf build runs the drawing checks on the document the engine just serialized, before anything is written, and a build with error findings fails. The checks: cut-layer profiles must close (polylines, circles, or chained line/arc loops), zero-length/degenerate entities are rejected, exact duplicate geometry (double-cut risk) is rejected, explicitly unitless documents are rejected, and an empty modelspace is rejected. Open geometry is allowed only on bend/engrave/reference-intent layers (matched by name).

The same checks run post-hoc on any existing .dxf file — including one that never came from a generator — through cadgen.drawing_checks:

python
from cadgen.drawing_checks import validate_dxf_file

for finding in validate_dxf_file("path/to/file.dxf"):
    print(finding.render())

Beyond the built-in checks, verify requested dimensions with targeted ezdxf reads (entity counts by layer, drawing extents, every dimension the user specified) against the generated sibling .dxf (or the out= path when one is declared), and review geometry visually in the CAD Viewer:

python
import ezdxf

doc = ezdxf.readfile("path/to/source.dxf")
msp = doc.modelspace()
cut = msp.query('*[layer=="CUT"]')
holes = msp.query('CIRCLE[layer=="CUT"]')

Report only checks that actually ran.

Handoff

After creating or modifying DXF drawings, you must ALWAYS hand the explicit .dxf file path(s) to $cad-viewer when that skill is installed and include its live viewer link(s) in the final response. If $cad-viewer is unavailable or startup fails, report that and rely on ezdxf checks instead of silently omitting the handoff.

Final responses should include generated files, returned viewer links, validation actually run, and assumptions.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Dxf AI skill do?

Generate, regenerate, and validate 2D DXF drawings from Python build123d sources. Use for DXF files, `.py` drawing scripts, @dxf models, 2D profiles, outlines, templates, gaskets, panels, flat patterns, laser/plasma/waterjet cut layouts, and 2D drawing exports of CAD geometry.

Why use Dxf on TypingMind?

Because you install it once and use it with any model. Dxf is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Dxf in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/earthtojake/text-to-cad/tree/main/skills/dxf. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Dxf?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Dxf?

As many as you like. As long as a model supports skills, you can use Dxf with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Dxf AI skill free?

Yes. It is published on GitHub by earthtojake under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇