# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview **WoodShop** - Voice-driven conversational 3D woodworking & furniture modeler. Speak (or type) commands like *"place a 6 foot 2x4, sand it, attach a 2 foot 2x4 at 90 degrees 10 inches from the end"* and watch the model build in a live 3D viewport — Holodeck-style. ## Architecture **Design principle:** reuse existing CmdForge tools for everything that isn't woodshop-specific; don't reinvent voice/AI plumbing. ``` woodshop-talk (driver.py) ── the conversational loop │ dictate ............... speech→text (CmdForge tool, reused) │ pa-load-tools ......... wood-* → Claude schemas (reused) │ claude -p ............. interpret utterance → JSON tool calls (reused provider) │ pa-execute-tool ....... dispatch each wood-* tool (reused) │ read-aloud ........... speak confirmation (reused) ▼ scene.json ← single source of truth (parts, joints, selection, undo stack) ▲ │ writes │ reads/mutates ▼ wood-* CmdForge tools woodshop-view (viewer.py) (place/join/sand/delete/undo) watches scene.json → live pyvista 3D thin wrappers over `woodshop` CLI ``` Only woodshop-specific code lives in this repo: the scene model (`scene.py`), nominal→actual lumber table (`lumber.py`), length parsing (`units.py`), the `woodshop` CLI (`cli.py`), build123d geometry + STL/STEP export (`geometry.py`), the pyvista viewport (`viewer.py`), and the driver (`driver.py`). The driver uses Claude (not `pa-tool-loop`, which hard-wires a small local model) for reliable structured tool-calling. ### Entry points | Command | Purpose | |---------|---------| | `woodshop` (no args) / `woodshop-gui` | **The unified desktop studio** (viewport + parts panel + command bar) | | `woodshop ` | CLI ops: place, join, stand, lay, rotate, move, trim, copy, rename, sand, delete, select, undo, redo, clear, status, cutlist, export, render, save, open, projects | | `woodshop-view` | Standalone live 3D viewport (watches `scene.json`; labels, grid, isometric) | | `woodshop-talk` | Standalone conversational driver (`--voice` for mic, `--once "..."` for one command) | The studio (`src/woodshop/gui/`) is a thin PySide6 shell over the same Scene + operations + interpreter: - `controller.py` — one in-memory `Scene`; buttons/menus call typed methods, voice/typed commands go through `driver.interpret` and are applied via `execute_call`, which **reuses the CLI command functions** (no behavioral drift). Every mutation saves to disk and emits `changed`. - `viewport.py` — embedded `pyvistaqt.QtInteractor`; click a board to select. - `panels.py` — parts list (ExtendedSelection: Ctrl/Shift multi-select) + selected-part inspector (editable length/yaw/tilt) + quick-action buttons. `command_bar.py` — text + push-to-talk + transcript, with slow work (LLM/dictate/TTS) on a `QThreadPool` (`workers.py`). - `numpad.py` — a numberpad control panel (2/4/6/8 move, 1/3/7/9 rotate, +/− raise/lower, 0/. front/iso, 5 fit) that also responds to the **physical numpad keys** (MainWindow.keyPressEvent forwards them when not typing). - **Multi-selection**: `controller.selected` is a list driven by 3D Ctrl+click (`viewport.picked` carries an additive flag) and list multi-select. Group ops (`move_selected`/`rotate_selected`/stand/lay/sand/delete) apply to all selected in one undo step via `scene.batch()`. Voice "move these" works because the selected ids are fed into the interpreter prompt. Scene file location: `$WOODSHOP_SCENE` or `~/.local/share/woodshop/scene.json`. Named projects: `~/.local/share/woodshop/projects/.json`. Parts have full 3D orientation (`yaw_deg`/`tilt_deg`/`roll_deg`) so legs and uprights stand vertically. Parts can be referred to by id (`p1`) or by a name set with `rename`. The cut list (`cutlist.py`) reports board-feet and an 8'-stick shopping estimate. ### CmdForge tools (the documented command vocabulary) `wood-place`, `wood-join`, `wood-sand`, `wood-delete`, `wood-undo` live in `~/.cmdforge//` and wrap the `woodshop` CLI. Regenerate them with `/tmp/gen_wood_tools.py` (kept in the repo plan) if their schemas change. The arg descriptions ARE the LLM's documentation, so keep them clear. ### Setup ```bash python3 -m venv .venv && source .venv/bin/activate pip install -e ".[viewer,dev]" # viewer extra pulls build123d + pyvista pytest # 25 tests ``` ### Known limitations / next steps 1. **Joins are flush butt joints**: B's end sits flush against A's surface, and B is aligned to A's reference corner (top faces level + one side flush) rather than centered. The flush corner is fixed (A's +width/+thick side; no per-join choice of which corner / centered). 2. **Joinery features** (`Feature` on each `Part`) are parametric ops applied in `geometry.part_solid`: `tenon` fuses a protruding tongue; `mortise`/`hole`/ `slot`/`dado`/`rabbet` cut a box/cylinder into a face; `chamfer` bevels the edges around a face via build123d `chamfer()` (handled specially — `_apply_chamfer`, with a try/except fallback for over-sized bevels). The viewport tessellates featured boards (plain boards stay fast pyvista boxes). Edit paths: CLI `feature/feature-edit/feature-delete/features`; voice `wood-feature`/`wood-feature-delete`; **GUI Joinery tab** (`feature_panel.py`) — add-with-default, then dragging a field shows a **live red preview ghost** (`viewer.feature_preview_mesh` — a cheap box/cylinder, no re-tessellation) of the pending change over the committed feature; **Apply** commits it (`controller.set_preview`/`apply_preview`, `preview_changed` signal → `viewport.set_preview`). Per-kind hints + field tooltips explain the parameters. `controller.active_feature` is the one being edited. A **Fit to mate…** button (`controller.fit_feature`) resizes a mortise to a chosen tenon (or vice versa) — pocket = tongue + clearance (1/32"), pocket slightly deeper; a dialog lists the complementary features (`features_of_kind`). The dialog's **Make connection** checkbox calls `controller.make_connection` → `scene.connect(anchor, moving)`, which moves/orients the moving feature's board so the tenon seats into the mortise (faces meet, axes aligned). Connect builds the target world rotation from the feature frames and decomposes it to the board's yaw/tilt/roll via `matrix_to_ypr` (inverse of `Part.local_frame`'s Rz·Ry(-tilt)·Rx(roll)); `Part.feature_world_frame` gives each feature's world point/normal/u/v. Features also have `rotation_deg` (spin about the face normal) to line up cross-sections. CLI `connect`; voice `wood-connect`. Not yet: countersinks, click-a-face-to-place. 2. **Latency** ~7–13s per utterance (one `claude -p` call). 3. Voice path (`--voice`) reuses `dictate`; the driver loop is hardened against failures but the mic path isn't exercised in the unit tests. 4. Auto-placement of parts in a multi-step "build a table" request depends on the LLM choosing good offsets; geometry is correct but corners may need nudging. ## ⚠️ CRITICAL: Updating Todos, Milestones, and Goals **DO NOT edit `todos.md`, `milestones.md`, or `goals.md` files directly.** These files are managed by Development Hub which has file watchers and sync logic. Direct edits will be overwritten or cause conflicts. **Use the `devhub-tasks` CLI instead:** ```bash # Status overview devhub-tasks status woodshop # Add todos devhub-tasks todo add woodshop "Task description" --priority high --milestone M1 # Complete todos (by text match or ID number) devhub-tasks todo complete woodshop "Task description" devhub-tasks todo complete woodshop 3 # List todos devhub-tasks todo list woodshop # Add milestones devhub-tasks milestone add woodshop M2 --name "Milestone Name" --target "March 2026" # Complete milestones (also completes linked todos) devhub-tasks milestone complete woodshop M1 # Goals devhub-tasks goal add woodshop "Goal description" --priority high devhub-tasks goal complete woodshop "Goal description" ``` Use `--json` flag for machine-readable output. Run `devhub-tasks --help` for full documentation. **Files you CAN edit directly:** `overview.md`, `architecture.md`, `README.md`, and any other docs. ## Development Commands ```bash # Install for development pip install -e ".[dev]" # Run tests pytest # Run a single test pytest tests/test_file.py::test_name ``` ## Architecture *TODO: Describe the project architecture* ### Key Modules *TODO: List key modules and their purposes* ### Key Paths - **Source code**: `src/woodshop/` - **Tests**: `tests/` - **Documentation**: `docs/` (symlink to project-docs) ## Documentation Documentation lives in `docs/` (symlink to centralized docs system). **Before updating docs, read `docs/updating-documentation.md`** for full details on visibility rules and procedures. Quick reference: - Edit files in `docs/` folder - Use `public: true` frontmatter for public-facing docs - Use `` / `` to hide sections - Deploy: `~/PycharmProjects/project-docs/scripts/build-public-docs.sh woodshop --deploy` Do NOT create documentation files directly in this repository.