# How to Add New Export Formats to the CAD Skill in text-to-cad: A Complete 8-Step Workflow

> Extend the text-to-cad CAD skill by adding new export formats. Follow this 8-step workflow to implement exporters and integrate them seamlessly into your project.

- Repository: [earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad)
- Tags: how-to-guide
- Published: 2026-08-01

---

**Adding a new export format to the CAD skill requires implementing an exporter function in the cadpy package, extending the `StepImportOptions` data class, exposing the format through the CLI in [`scripts/step/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/scripts/step/cli.py), and wiring the exporter into the generation orchestration in [`generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/generation.py).**

The earthtojake/text-to-cad repository generates primary STEP files alongside optional side-car mesh formats like STL, 3MF, and GLB. When extending the system to support additional formats such as OBJ or PLY, developers must follow a predictable four-layer pipeline that ensures consistent CLI exposure, generation orchestration, and documentation. Mastering this workflow for adding new export formats to the CAD skill enables clean integration with the existing side-car processing loop.

## Core Implementation

### Step 1: Create the Exporter Function

Create a new module under `skills/cad/scripts/packages/cadpy/src/cadpy/` (for example, [`obj.py`](https://github.com/earthtojake/text-to-cad/blob/main/obj.py)). The function signature must mirror the existing exporters like [`stl.py`](https://github.com/earthtojake/text-to-cad/blob/main/stl.py):

```python
from pathlib import Path
from cadpy.types import LoadedStepScene

def export_part_obj_from_scene(
    step_path: Path,
    scene: LoadedStepScene,
    *,
    target_path: Path | None = None,
) -> Path:
    """Export the supplied `scene` as an OBJ file.

    * `step_path` – the original STEP file (used for error messages).
    * `scene` – the fully-loaded step scene produced by `load_step_scene`.
    * `target_path` – optional explicit output location; if omitted a path is
      derived from the STEP file name.
    """
    # Use a geometry library (e.g., trimesh, pywavefront) to write OBJ

    return target_path

```

The **STL exporter** at [`skills/cad/scripts/packages/cadpy/src/cadpy/stl.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/scripts/packages/cadpy/src/cadpy/stl.py) serves as the reference implementation for error handling and path derivation logic.

### Step 2: Extend the Configuration Data Class

The `StepImportOptions` dataclass in [`skills/cad/scripts/packages/cadpy/src/cadpy/catalog.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/scripts/packages/cadpy/src/cadpy/catalog.py) carries side-car flags from the CLI to the generation engine. Add a new field for your format:

```python
from dataclasses import dataclass
from typing import Optional

@dataclass
class StepImportOptions:
    stl: Optional[str] = None
    three_mf: Optional[str] = None
    glb: Optional[str] = None
    obj: Optional[str] = None          # ← NEW FIELD

    mesh_tolerance: Optional[float] = None
    mesh_angular_tolerance: Optional[float] = None

```

This dataclass acts as the **configuration contract** between the CLI layer and the generation pipeline.

## Interface Integration

### Step 3: Add CLI Arguments

Edit [`skills/cad/scripts/step/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/scripts/step/cli.py) to expose the new option. Existing STL, 3MF, and GLB flags are defined around lines 48-62:

```python
parser.add_argument(
    "--obj",
    metavar="OUTPUT",
    help="Export an OBJ side-car to this relative .obj path.",
)

```

Update the `_step_import_options_from_args` function to populate the field:

```python
return StepImportOptions(
    stl=args.stl,
    three_mf=args.three_mf,
    glb=args.glb,
    obj=args.obj,          # ← NEW

    mesh_tolerance=args.mesh_tolerance,
    mesh_angular_tolerance=args.mesh_angular_tolerance,
)

```

### Step 4: Wire into the Generation Pipeline

Inside [`skills/cad/scripts/packages/cadpy/src/cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/scripts/packages/cadpy/src/cadpy/generation.py), the `generate_step_targets` function builds a list of `_ArtifactJob` instances. Locate the side-car handling block around lines 1700-1800 and add your exporter:

```python
if spec.obj_path:
    jobs.append(_ArtifactJob(
        name="OBJ",
        func=lambda s=spec, sc=scene: export_part_obj_from_scene(
            s.step_path, sc, target_path=Path(s.obj_path)
        ),
    ))

```

The **lambda closure** captures the spec and scene variables to ensure the exporter receives the correct paths when executed.

## Documentation and Quality Assurance

### Step 5: Update Export References

Document the new format in [`skills/cad/references/supported-exports.md`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/references/supported-exports.md):

```markdown

## OBJ

Export a Wavefront OBJ file. Useful for downstream tools that require explicit vertex/face listings.

```

The CLI flag added in Step 3 automatically appears in the generated `--help` output.

### Step 6: Add Automated Tests

Create a test case under `tests/python/skills/cad/` following the pattern of existing format tests:

1. Run `scripts/step` with `--obj` on a minimal sample model.
2. Assert the file is created with the expected `.obj` suffix.
3. Validate the output contains proper OBJ syntax (vertex and face definitions).

Name the file [`test_step_obj.py`](https://github.com/earthtojake/text-to-cad/blob/main/test_step_obj.py) to maintain consistency with [`test_step_stl.py`](https://github.com/earthtojake/text-to-cad/blob/main/test_step_stl.py).

## Validation and Deployment

### Step 7: Verify End-to-End

Run the complete generation workflow locally:

```bash
python scripts/step path/to/sample_model.py --obj meshes/sample.obj --verbose

```

Confirm that:
- The primary STEP file is produced.
- The OBJ side-car appears next to the STEP file.
- `scripts/inspect` operates on the STEP file without interference from the side-car.

### Step 8: Continuous Integration Validation

Execute the CI test suite via [`scripts/test/test.sh`](https://github.com/earthtojake/text-to-cad/blob/main/scripts/test/test.sh) to ensure the new Python tests pass and no regressions occur in existing STL, 3MF, or GLB export paths. The test matrix automatically validates all configured export formats.

## Summary

- **Implement** the exporter function in `skills/cad/scripts/packages/cadpy/src/cadpy/` using the signature pattern from [`stl.py`](https://github.com/earthtojake/text-to-cad/blob/main/stl.py).
- **Extend** `StepImportOptions` in [`cadpy/catalog.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/catalog.py) to carry the new export path.
- **Expose** the format via CLI flags in [`skills/cad/scripts/step/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/scripts/step/cli.py).
- **Wire** the exporter into `generate_step_targets` in [`cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/generation.py) using the `_ArtifactJob` pattern.
- **Document** the format in [`skills/cad/references/supported-exports.md`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/references/supported-exports.md).
- **Test** locally with sample models before submitting to CI via [`scripts/test/test.sh`](https://github.com/earthtojake/text-to-cad/blob/main/scripts/test/test.sh).

## Frequently Asked Questions

### What is the minimum code required to add a basic export format?

You must modify four files: create an exporter module in `cadpy/`, add a field to `StepImportOptions` in [`catalog.py`](https://github.com/earthtojake/text-to-cad/blob/main/catalog.py), add a flag in [`step/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/step/cli.py), and append an `_ArtifactJob` in [`generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/generation.py). No changes are required to the core STEP generation logic, as side-car exports run after the primary CAD file creation.

### How does the `_ArtifactJob` pattern handle multiple simultaneous exports?

The `generate_step_targets` function appends each requested format to a `jobs` list as separate `_ArtifactJob` instances. These execute sequentially after the primary STEP generation, allowing users to request STL, 3MF, GLB, and your new format in a single command without interference between exporters.

### Where should mesh tolerance settings be configured for new formats?

Mesh tolerances are stored in `StepImportOptions` alongside format-specific paths. Your exporter function receives the `scene` object already loaded, but should respect `mesh_tolerance` and `mesh_angular_tolerance` if performing additional tessellation. Access these values via the spec object passed to your lambda in the `_ArtifactJob` definition.

### How do I ensure backward compatibility when adding CLI flags?

Use `Optional[str]` type hints with `None` defaults in both the CLI argument parser and the `StepImportOptions` dataclass. This ensures existing scripts that do not specify the new flag continue to function without raising `AttributeError` or `KeyError` exceptions during option parsing.