The lathe tool.
BeagleLathe registers one MCP tool, lathe, with four actions: search, edit, edit_glob, read. Claude Code picks them up automatically once /mcp shows the server. Seven legacy tool names (sh, run_tests, git_status, git_diff, changed_files, lint_and_typecheck, extract_todos, commit_message) are still dispatched by name as a back-compat layer but are no longer advertised in /mcp. All are documented below.
Read & search
| Tool | Replaces | What it does |
|---|---|---|
search |
glob → grep → read-to-confirm (~3 calls → 1) | Fuses glob filtering, regex (ripgrep) pattern matching, and ranked snippet reading into one call. Returns matches grouped by file with surrounding context. |
read |
cat + head/tail on large files (~2 calls → 1) | AST-aware reader. Twelve languages get full structural truncation: Python (.py), TypeScript (.ts), TSX (.tsx), JavaScript (.js/.mjs/.cjs/.jsx), Rust (.rs), Go (.go), Java (.java), C# (.cs), Ruby (.rb), PHP (.php), C (.c/.h), and C++ (.cc/.cpp/.cxx/.hpp/.hh/.hxx). Keeps signatures, types, imports, and module-level declarations; stubs function bodies. Four modes: truncated (default — bodies stubbed), full (verbatim), structure (declarations + exports only, drops module-level statements), skeleton (regex-picked declaration lines — cheap fallback for unsupported languages or giant files). Pass symbol="ClassName.method" with mode=truncated to keep one symbol’s body in full while stubbing the rest. Hard cap: 200,000 chars. |
sh |
Bash for read-only inspection | Runs a single read-only command: ls, cat, head, tail, wc, find, pwd, which, file, stat, du, df, tree, echo, or git (log/status/diff/show/blame/branch/remote/config/rev-parse/ls-files/describe). No pipes, redirects, or subshells. |
Edit
| Tool | Replaces | What it does |
|---|---|---|
edit |
read → edit → verify-read (~3 calls → 1) | Apply an array of edits across one or more files in one atomic call. Matching cascade: exact → normalized (whitespace / smart-quote fold) → fuzzy (rapidfuzz ≥ 90). All-or-nothing: if any edit fails the dry run, nothing is written. |
edit_glob |
Bash sed -i across many files |
Atomic regex substitution across a glob. Two-phase commit: stage all .tmp files, re-verify hashes, rename atomically. All-or-nothing on failure. The differentiator vs shell-sed is the guarantee. |
Tests & quality
| Tool | Replaces | What it does |
|---|---|---|
run_tests |
Bash(pytest) → grep → read → grep → read (~5 calls → 1) | Auto-detects pytest / jest / vitest / cargo test / go test. Returns parsed summary{total, passed, failed, skipped, errors} plus per-failure {name, file, line, message, source_excerpt, traceback_tail}. |
lint_and_typecheck |
Per-language linter via Bash, then grep output | Dispatches by extension: .py → ruff + mypy; .ts/.tsx → tsc + eslint; .js/.jsx → eslint; .go → go vet + golangci-lint; .rs → cargo check + clippy. Missing runners land in skipped_runners rather than failing the call. |
extract_todos |
grep + per-line git blame |
Scan for TODO / FIXME / XXX / HACK markers, return them with marker, file:line, comment text, surrounding context, and (in git repos) author + commit sha + age in days from blame. Sortable by age or file. |
Git
| Tool | Replaces | What it does |
|---|---|---|
git_status |
git status + manual parsing |
Returns {branch, upstream, ahead, behind, staged, unstaged, untracked, renamed, conflicts}. Detached HEAD adds {detached: true, head_sha}. |
git_diff |
git diff + hunk parsing |
Structured unified diff with per-file additions/deletions and a list of hunks (old_start, old_lines, new_start, new_lines, header, content). Handles added / modified / deleted / renamed / copied / type-changed / binary. |
changed_files |
git diff --numstat + status parsing |
Files changed on the current branch vs a base branch (auto-detected main/master/develop) with per-file {additions, deletions, status}. Optionally folds in uncommitted working-tree changes. |
commit_message |
Hand-writing a conventional commit | Generate a conventional-commit draft from staged / working-tree / last-commit changes. Heuristic only, no LLM call: paths drive type (test, docs, ci, build, feat, fix, refactor), longest common directory drives scope. Intended as a starting draft. |
Measured savings.
Numbers from scripts/benchmark.py: n=5 head-to-head across nine fixtures. Each fixture runs twice in a fresh tmp workdir — once with stock Claude Code (empty MCP config), once with BeagleLathe registered. Same model on both sides, same prompt, same setup. The harness parses stream-json telemetry and runs each fixture’s verify.py to confirm the task actually completed.
Per-task averages across all nine fixtures (Opus 1M tier; re-priced figures for Sonnet 4.6 and Opus 4.7 standard tiers ship in BENCHMARK-RESULTS.md):
| Metric | Stock | Lathe | Delta |
|---|---|---|---|
| Tool calls / task | 7.4 | 4.0 | −46% |
| Turns / task | 8.4 | 5.0 | −40% |
| Output tokens / task | 1,243 | 720 | −42% |
| Cache-creation tokens / task | ~20,400 | ~11,800 | −42% |
| Wall time / task | 25.1 s | 17.9 s | −29% |
| Cost / task (Opus 1M tier) | $0.1960 | $0.1587 | −19.0% |
| Pass rate | 45/45 on both sides | — | |
The bigger lever isn’t the dollar number — it’s the −42% cache-creation tokens, which is what most rate limits actually meter against.
The throughput math
A Claude Code 5-hour window is a fixed token budget. If each unit of useful work costs 19% fewer tokens, you fit 1 / (1 − 0.19) = 1.235× more tasks in the same window — ~24% more shipped code. Applied to the published 5h limits for Opus-heavy use:
Extra tasks per 5-hour window
| Plan | Stock | Lathe | Extra |
|---|---|---|---|
| Pro | ~40 | ~49 | +9 |
| Max 5× | ~200 | ~247 | +47 |
| Max 20× | ~800 | ~988 | +188 |
Caveat: the 19% is a per-task mean across nine paired fixtures. The win is bigger on multi-file edits (rename hits −43%, multi-file-rename hits −73%) and smaller on single-line edits where the schema-load tax still dominates. A rename- or grep-heavy 5h window runs above 24%; an all-one-shot-Q&A window runs closer to 10–15%.
Fixtures cover real refactoring patterns: find-usages, multi-file-rename, constants-in-module, hidden-bug-in-body, large-file-search, re-read-after-edit, rename, run-tests-fix-bug, add-method. The benchmark harness and fixture set are open source. See the repo for scripts/benchmark.py, bench-fixtures/, and BENCHMARK-RESULTS.md.
How the local savings counter works
The benchmark above is the apples-to-apples measurement. Your local /lathe-savings counter is a separate, lighter-weight signal that runs all the time on your own machine. It scores four tools against the stock Claude Code sequences they replace:
search— counted as 3 calls saved (glob → grep → read-to-confirm)edit— counted as 3 calls saved (read → edit → verify-read)read— counted as 2 calls saved (cat + head/tail on large files)sh— counted as 1.5 calls saved (averaged across the read-only shell patterns it replaces)
The structured git, test, lint, todos, and commit-message tools are recorded to ~/.beaglelathe/state.db but don’t currently roll up into the running total — their per-call savings are harder to pin to a fixed multiplier, so the counter stays conservative. Tokens saved are estimated at 500 tokens per replaced call. Dollar cost is priced at the Sonnet input rate of $3/M tokens (not the higher output rate), so the displayed savings are a floor, not a ceiling. Nothing leaves your machine. /lathe-savings reads the SQLite file directly.
Install.
Requires Python 3.10+ and the Claude Code CLI.
One-liner (recommended)
Picks the best Python installer available (uv > pipx > pip), installs beaglelathe, registers the plugin with Claude Code, and walks you through magic-link sign-in.
curl -LsSf https://beaglelathe.dev/install.sh | sh
Pass --no-login to register the plugin without signing in:
curl -LsSf https://beaglelathe.dev/install.sh | sh -s -- --no-login
The script is short and auditable. Read it at install.sh before running.
Manual install
Pick whichever Python tool you prefer. Each option is one shell line followed by beaglelathe login:
With uv (docs, recommended for Linux)
uv tool install beaglelathe
beaglelathe login
uv installs beaglelathe into an isolated venv (sidesteps PEP 668 on Debian / Ubuntu) and puts the console script in ~/.local/bin. If that’s not on your PATH yet, run uv tool update-shell once.
With pipx (docs)
pipx install beaglelathe
beaglelathe login
Same isolation story as uv. Run pipx ensurepath once if ~/.local/bin isn’t on PATH yet.
With pip directly
pip install beaglelathe
beaglelathe login
Fine on macOS and Windows. On Debian / Ubuntu, modern Python distros block system-wide pip installs (PEP 668). Use uv or pipx instead.
beaglelathe login registers the plugin with Claude Code (MCP server, slash commands, agents, hooks) and then walks you through magic-link sign-in. The install step is idempotent. Re-running is a no-op once the plugin is registered. Open Claude Code in your project and run /mcp. You should see lathe listed as one active tool (it exposes four actions: search, edit, edit_glob, read).
Install without signing in
BeagleLathe tools work without an account. To register the plugin without going through the magic-link flow, replace beaglelathe login with:
beaglelathe install
You can sign in later with beaglelathe login or /lathe-login; without an account, savings are tracked locally and there’s no quota enforced.
Per-project install
If you’d rather wire BeagleLathe into a single project’s .mcp.json instead of installing the plugin globally:
uv tool install beaglelathe # or: pipx install / pip install
beaglelathe install --mcp-json
This writes a .mcp.json in the current directory (or the path you pass) that points at the Python interpreter where beaglelathe is installed. No plugin install, no slash commands. Just the lathe MCP tool (with four actions, plus the back-compat names), scoped to that project.
Alternative: install from the GitHub marketplace
The plugin is also published to the BeagleLathe Claude Code marketplace. You still need the Python package installed (the MCP server runs the beaglelathe console script):
uv tool install beaglelathe # or: pipx install / pip install
claude plugin marketplace add BeagleLathe/lathe
claude plugin install lathe@beaglelathe
Or inside a Claude Code session:
/plugin marketplace add BeagleLathe/lathe
/plugin install lathe@beaglelathe
Slash commands.
Inside Claude Code. All commands also have a corresponding CLI form (beaglelathe <subcommand>).
| Command | What it does |
|---|---|
/lathe |
List every BeagleLathe slash command with a one-line description. Useful as a quick reminder of what’s available. |
/lathe-login |
Sign in via magic link. Opens browser, polls for confirmation, writes credentials. Example: /lathe-login → click the link in the printed URL → “Signed in. Run /lathe-status to see your plan.” |
/lathe-logout |
Clear stored credentials from ~/.beaglelathe/credentials.json. The MCP tools keep working anonymously after logout. |
/lathe-status |
Show plan, calls used this month, budget remaining, reset date. Example output: plan: Free tier / calls used: 42 / remaining: 158 / resets: 2026-06-01 |
/lathe-savings |
Show local savings counter: calls saved, tokens saved, estimated cost saved vs stock Claude Code. Computed entirely from ~/.beaglelathe/state.db; never synced. |
/lathe-upgrade |
Open Stripe checkout to upgrade from Free to Pro ($9/mo, unlimited calls). |
/lathe-update |
Update the BeagleLathe package to the latest version. The command detects which Python installer you used and upgrades through it: uv tool upgrade beaglelathe if you installed with uv, pipx upgrade beaglelathe if you installed with pipx, or pip install --upgrade beaglelathe otherwise. Whichever you used, the slash command picks the right one. Restart Claude Code (or run /mcp restart) afterward. |
/lathe-help |
Print usage tips, the tool list, slash-command index, and troubleshooting cheat sheet inside Claude Code. |
Configuration.
Files
| Path | Purpose |
|---|---|
~/.beaglelathe/credentials.json |
Auth token written by beaglelathe login. Cleared by beaglelathe logout. Read-only across all CLI / MCP entry points. |
~/.beaglelathe/state.db |
Local SQLite for the savings counter and per-tool call totals. Never synced; /lathe-savings reads from this file. |
./.mcp.json (per project) |
The MCP server registration written by the plugin installer. Tells Claude Code how to launch the server. |
Free tier
200 tool calls per month, per account. Resets at midnight UTC on the 1st of each calendar month. A “call” is one invocation of the lathe MCP tool, regardless of action. Without signing in, tools work anonymously and no quota is enforced. Only the local savings counter advances.
CLI reference
beaglelathe login Sign in via magic link
beaglelathe logout Clear stored credentials
beaglelathe whoami Print signed-in user
beaglelathe status Show plan and remaining calls
beaglelathe savings Show local savings summary
beaglelathe upgrade Open Stripe checkout (Free → Pro)
beaglelathe serve Start the MCP server (what Claude Code runs)
beaglelathe statusline Print the one-line statusline string
All commands accept --help.
Statusline integration.
BeagleLathe ships a Claude Code statusLine component that prints your plan, calls remaining, and locally-tracked savings on every prompt. To enable, add to ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "beaglelathe statusline"
}
}
Output looks like:
Lathe · Free · 158 left · saved 42
Reads only from ~/.beaglelathe/credentials.json and ~/.beaglelathe/state.db; never makes a network call.
Troubleshooting.
Tools not showing in /mcp
Run claude plugin list to confirm the plugin is installed. If the server isn’t starting, try /mcp restart or restart Claude Code.
“not logged in” / “session expired”
Run beaglelathe login (or /lathe-login) to get a fresh token. Tokens have a 24-hour TTL.
Quota exceeded
Run /lathe-upgrade or beaglelathe upgrade to open Stripe checkout. Pro is unlimited. Free-tier counters reset on the 1st of each month.
edit reports “old_string not found”
The string you provided doesn’t match what’s in the file. Read the current file content first with read, then retry with the exact text. Note that edit already tolerates whitespace and smart-quote drift. If it still can’t match, the underlying content really has diverged.
edit reports “old_string not unique”
Add more surrounding lines to make the match unique, or set replace_all: true if you want all occurrences replaced.
First tool call is slow
The MCP server starts on first use; ~3 seconds is normal. Subsequent calls in the same session are fast.