Replaces the from_center boolean with miter_offset_in / bevel_offset_in: how far to move the cut's hinge IN from the edge. 0 = full edge-to-edge cut; half the board size = the centre (corner notch); two centred cuts (+angle/−angle) make a point (picket); intermediate/over values give asymmetric & partial cuts — much more range than the old edge/centre toggle. - Feature.miter_offset_in/bevel_offset_in (replaces from_center); geometry pivot = edge + inward·offset (per width/thickness); serialization additive. - Joinery panel: Miter offset / Bevel offset spin fields (miter only), checkbox removed. CLI feature --miter-offset/--bevel-offset; wood-feature tool args (regenerated); controller passthrough; apply_preview carries the offsets. - tests updated: offset=half-width notches a corner, two make a point, offset roundtrips. 247 pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| scripts | ||
| src/woodshop | ||
| tests | ||
| .gitignore | ||
| CLAUDE.md | ||
| MATERIALS_INVENTORY_PLAN.md | ||
| README.md | ||
| SHOP_PACKET_PLAN.md | ||
| pyproject.toml | ||
README.md
WoodShop
Voice-driven conversational 3D woodworking & furniture modeler.
Talk to it like the Star Trek holodeck and watch furniture build itself:
"Place a 6 foot 2x4, sand it, then attach a 2 foot 2x4 at 90 degrees, 10 inches from the end."
"Build a coffee table: a four foot by two foot frame from 2x4s, with four legs 18 inches tall standing at the corners."
You can also attach reference(s) (📎, drag-drop, paste, or a URL) and say "build something like this": one or several photos (front/side/detail), a PDF plan, a 3D model (STL/STEP/OBJ — rendered to an image, with its bounding box measured), or a web-page guide (its text is pulled). WoodShop builds a simplified, buildable interpretation in dimensional lumber. Then click 🔄 Match photo and it renders the build from several angles, compares them to your reference, and self-corrects — repeat until it looks right. (Still an interpretation, not a measured replica.)
Each board is real dimensional lumber (a 2x4 is modeled at its true 1.5″ × 3.5″), so the result is buildable — export to STEP (CAD/CNC) or STL (3D print), and get a cut list with board-feet and a shopping estimate.
How it works
WoodShop reuses the existing CmdForge tool ecosystem for everything that isn't woodworking-specific, so no wheels are reinvented:
woodshop-talk ── the conversational loop
│ dictate ............. speech → text (CmdForge tool)
│ pa-load-tools ....... wood-* → Claude schemas (CmdForge tool)
│ claude -p ........... interpret → tool calls (provider)
│ pa-execute-tool ..... dispatch each wood-* (CmdForge tool)
│ read-aloud .......... speak confirmation (CmdForge tool)
▼
scene.json ← single source of truth (parts, joints, selection, undo)
▲ │ writes
│ reads/mutates ▼
wood-* CmdForge tools woodshop-view
(place/join/stand/move/...) live pyvista 3D, watches scene.json
The wood-* tools are thin wrappers over the woodshop CLI, so the modeling
logic lives in one place and the tools double as the LLM's documented command
vocabulary.
Installation
python -m venv .venv && source .venv/bin/activate
pip install -e ".[gui,dev]" # 'gui' pulls build123d + pyvista + PySide6 + pyvistaqt
python scripts/gen_wood_tools.py # register the wood-* CmdForge tools
Usage
The studio (recommended)
woodshop # launches the unified desktop app
One window with the 3D viewport (click a board to select it; Ctrl+click to select several), a parts panel (list + selected-part inspector + quick-action buttons), a numberpad control panel (move/rotate the selection by clicking or with your keyboard's numpad — 2/4/6/8 move, 1/3/7/9 rotate, +/− raise/lower, 0/. front/iso, 5 fit), and a command bar where you type or push-to-talk (🎤). Mouse, keyboard, and voice all drive the same scene and the same visible selection — so "move these 4 inches", the numpad 8 key, and the move button are interchangeable, and act on every selected board at once (one undo). Menus cover New/Open/Save projects, Export STL/STEP, Save Image, Undo/Redo, camera views, and Build templates.
Standalone tools (headless / scripting)
woodshop-view & # just the live 3D window (watches the scene)
woodshop-talk # just the voice/text loop; --voice to speak
woodshop-talk --once "build a workbench top from five 2x6 boards 6 feet long"
Or drive it directly from the CLI:
woodshop place 2x4 "6 ft" # place a board
woodshop stand # stand it up (a leg)
woodshop join p2 --to p1 --angle 90 --offset "10 in"
woodshop rename "front-left leg"
woodshop cutlist # bill of materials
woodshop export table.step # STEP / STL export
woodshop save "coffee table" # named projects
woodshop open "coffee table"
Run woodshop --help for the full command list (place, join, stand, lay,
rotate, move, trim, copy, rename, sand, delete, undo, clear, status, cutlist,
export, save, open, projects).
The active scene lives at $WOODSHOP_SCENE or
~/.local/share/woodshop/scene.json; named projects in
~/.local/share/woodshop/projects/.
Development
pytest # 200+ tests
Key modules:
| Module | Role |
|---|---|
scene.py |
Part/Joint/Connection/Feature/Scene model, ops, undo, persistence |
lumber.py |
nominal → actual dimensions, material colors/defaults |
colors.py |
color name → hex + lightness blends for the viewer |
units.py |
parse "6 ft" / "3 ft 6 in" / "-2 ft" → inches |
cli.py |
the woodshop command |
geometry.py |
build123d solids (incl. joinery booleans) + STL/STEP export |
cutlist.py |
quick cut list / board-feet / shopping summary |
cutplan.py |
the deterministic keystone: kerf-aware nesting, rough/final, batch, offcut reuse |
prices.py |
editable price book (Kent NB) + material-aware cost estimate |
estimate.py |
project quote: consumables + labor + suggested selling price |
inventory.py |
shop-wide event-sourced stock / offcut / build ledger |
instructions.py / jigs.py |
deterministic build steps & jig suggestions |
viewer.py |
live pyvista 3D viewport (woodshop-view) |
driver.py |
conversational loop (woodshop-talk) |
gui/ |
the unified PySide6 studio (woodshop-gui), incl. the Cut List & BOM window |
scripts/gen_wood_tools.py |
(re)generate the wood-* CmdForge tools |
Known limitations
- Joinery is parametric (tenon/mortise/dado/rabbet/hole/slot/chamfer as build123d boolean ops, with connections/assemblies); what's not modeled is joinery-fit compensation for material lost to sanding, and lap/pocket-hole presets. Boards still attach as flush butt joints unless you add features.
- Render is flat colors per material/finish — no image textures (wood grain) yet.
- Command interpretation latency is ~7–13s per utterance (one
claude -pcall).
License
MIT