127 lines
4.7 KiB
Python
127 lines
4.7 KiB
Python
"""Generate the wood-* CmdForge tools: the documented woodworking command
|
|
vocabulary. Each is a thin wrapper over the `woodshop` CLI so the logic lives in
|
|
one place; pa-load-tools turns these into Claude function schemas."""
|
|
import os
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
CMDFORGE_PY = "/home/rob/.local/share/pipx/venvs/cmdforge/bin/python"
|
|
CMDFORGE_DIR = Path.home() / ".cmdforge"
|
|
BIN_DIR = Path.home() / ".local" / "bin"
|
|
|
|
WS = 'ws = os.path.expanduser("~/PycharmProjects/woodshop/.venv/bin/woodshop")'
|
|
|
|
PLACE = f'''import subprocess, os
|
|
{WS}
|
|
r = subprocess.run([ws, "place", stock, length], capture_output=True, text=True)
|
|
out = (r.stdout + r.stderr).strip()
|
|
'''
|
|
|
|
JOIN = f'''import subprocess, os
|
|
{WS}
|
|
cmd = [ws, "join", part_b]
|
|
if to: cmd += ["--to", to]
|
|
if angle: cmd += ["--angle", str(angle)]
|
|
if offset: cmd += ["--offset", offset]
|
|
if anchor: cmd += ["--anchor", anchor]
|
|
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
out = (r.stdout + r.stderr).strip()
|
|
'''
|
|
|
|
SAND = f'''import subprocess, os
|
|
{WS}
|
|
cmd = [ws, "sand"] + ([part] if part else [])
|
|
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
out = (r.stdout + r.stderr).strip()
|
|
'''
|
|
|
|
DELETE = f'''import subprocess, os
|
|
{WS}
|
|
cmd = [ws, "delete"] + ([part] if part else [])
|
|
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
out = (r.stdout + r.stderr).strip()
|
|
'''
|
|
|
|
UNDO = f'''import subprocess, os
|
|
{WS}
|
|
r = subprocess.run([ws, "undo"], capture_output=True, text=True)
|
|
out = (r.stdout + r.stderr).strip()
|
|
'''
|
|
|
|
TOOLS = {
|
|
"wood-place": {
|
|
"description": "Place a new board of dimensional lumber into the scene. Use for any 'place', 'add', 'put', 'grab', 'cut me a' board command.",
|
|
"arguments": [
|
|
{"flag": "--stock", "variable": "stock",
|
|
"description": "Nominal lumber size, e.g. 2x4, 2x6, 1x4, 4x4"},
|
|
{"flag": "--length", "variable": "length",
|
|
"description": "Length with units, e.g. '6 ft', '72 in', '3 ft 6 in'"},
|
|
],
|
|
"code": PLACE,
|
|
},
|
|
"wood-join": {
|
|
"description": "Attach/join one board to another at an angle, optionally offset along the target board. Use for 'attach', 'join', 'connect', 'fasten', 'screw to'.",
|
|
"arguments": [
|
|
{"flag": "--part-b", "variable": "part_b",
|
|
"description": "Id of the board being attached, e.g. p2"},
|
|
{"flag": "--to", "variable": "to", "default": "",
|
|
"description": "Id of the board to attach to, e.g. p1. Omit to use the most recently touched board."},
|
|
{"flag": "--angle", "variable": "angle", "default": "90",
|
|
"description": "Angle in degrees between the two boards (default 90)"},
|
|
{"flag": "--offset", "variable": "offset", "default": "",
|
|
"description": "Distance from the anchor end, e.g. '10 in'. Omit to attach at the very end."},
|
|
{"flag": "--anchor", "variable": "anchor", "default": "end_b",
|
|
"description": "Measure offset from 'end_a' (start) or 'end_b' (far end)"},
|
|
],
|
|
"code": JOIN,
|
|
},
|
|
"wood-sand": {
|
|
"description": "Sand a board smooth. Use for 'sand', 'smooth', 'finish'.",
|
|
"arguments": [
|
|
{"flag": "--part", "variable": "part", "default": "",
|
|
"description": "Id of the board to sand, e.g. p1. Omit to sand the most recently touched board ('it')."},
|
|
],
|
|
"code": SAND,
|
|
},
|
|
"wood-delete": {
|
|
"description": "Remove a board from the scene. Use for 'delete', 'remove', 'get rid of', 'scrap'.",
|
|
"arguments": [
|
|
{"flag": "--part", "variable": "part", "default": "",
|
|
"description": "Id of the board to delete, e.g. p2. Omit for the most recently touched board."},
|
|
],
|
|
"code": DELETE,
|
|
},
|
|
"wood-undo": {
|
|
"description": "Undo the last operation. Use for 'undo', 'never mind', 'take that back', 'go back'.",
|
|
"arguments": [],
|
|
"code": UNDO,
|
|
},
|
|
}
|
|
|
|
WRAPPER = '''#!/bin/bash
|
|
# CmdForge wrapper for '{name}'
|
|
# Auto-generated - do not edit
|
|
exec "{py}" -m cmdforge.runner "{name}" "$@"
|
|
'''
|
|
|
|
for name, spec in TOOLS.items():
|
|
tool_dir = CMDFORGE_DIR / name
|
|
tool_dir.mkdir(parents=True, exist_ok=True)
|
|
config = {
|
|
"name": name,
|
|
"description": spec["description"],
|
|
"category": "Other",
|
|
"version": "0.1.0",
|
|
"arguments": spec["arguments"],
|
|
"steps": [{"type": "code", "code": spec["code"], "output_var": "out"}],
|
|
"output": "{out}",
|
|
}
|
|
(tool_dir / "config.yaml").write_text(yaml.safe_dump(config, sort_keys=False))
|
|
|
|
wrapper = BIN_DIR / name
|
|
wrapper.write_text(WRAPPER.format(name=name, py=CMDFORGE_PY))
|
|
wrapper.chmod(wrapper.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
print(f"created {name}: {tool_dir/'config.yaml'} + {wrapper}")
|