# Lorax

> Prune finished git worktrees in the current repo, or report what is safe to prune and what is being held. Speaks for the trees - removes a worktree only when its work exists somewhere else. Use when the user says "/lorax", "lorax", "prune worktrees", "clean up worktrees", "what worktrees can I delete", "worktree status", or asks to turn the truffula animation on or off.

- Skill: `seanx10m/lorax` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add seanx10m/lorax`
- Raw SKILL.md: https://api.skillmd.com/api/skills/seanx10m/lorax/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: seanx10m (https://skillmd.com/u/seanx10m)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/seanx10m/lorax

---


# Lorax

Prunes finished git worktrees under `.claude/worktrees/`, and refuses to touch work
that exists nowhere else.

Everything it needs ships in `scripts/` next to this file. Below, `$LORAX` means
that directory.

As a member of the **lorax plugin** the SessionStart hook is already wired for you.
Standalone, run `sh install.sh` (next to this file) once to wire it, so every
session opens with a pass over the repo it starts in. That pass is
self-throttled to one PR check per hour per repo; `--report` here ignores the throttle.
`sh install.sh --uninstall` removes it. Both are idempotent and touch only
lorax's own entry in `~/.claude/settings.json`.

## The one criterion

Remove a worktree only when its work is **recoverable without it**. Not age, not
cleanliness - recoverability. It removes only when the PR is **MERGED**, there are
no *tracked* edits, and the last commit is over a day old. Capped at 10 per run.
**It never deletes a branch.**

Ancestry lies here: squash-merged branch commits never become ancestors of the
default branch, so `git branch --merged` misses almost everything. Merged-PR state
from `gh` is the only truth, which is why it needs the network.

## Verbs

Always run from inside the target repo, or aim it with `--repo <dir>`.

| The user wants | Run |
|---|---|
| See what would go, change nothing | `sh $LORAX/lorax.sh --report` |
| Actually prune | `sh $LORAX/lorax.sh` |
| Another checkout | `sh $LORAX/lorax.sh --repo /path/to/repo --report` |
| Animation on / off / status | `sh $LORAX/lorax.sh --anim on` \| `off` \| (no arg) |

**Default to `--report` first.** Show the user what it holds and why, and only run
the real prune if they confirm or clearly asked to prune outright.

## Reading the output

One line. `Would prune`/`Pruned` names what is going, then every hold category with
its reason. The interesting half is what it **refuses** to cut:

- `HELD ... no merged PR` - may hold commits on no other disk. **Push before removing.**
- `Kept ... merged-but-dirty` - tracked edits. Look before deleting.
- `Kept ... open PR` / `closed unmerged` - your call, never automatic.
- `Not registered as worktrees` - phantom directories git does not know about.
  Never auto-deleted; inspect by hand.

Relay these to the user rather than only the prune count - a held branch with
unpushed commits is the one thing that actually needs a human.

## Scope and safety

- Skips any repo with no `.claude/worktrees/` directory.
- **Defers to a repo-local `.claude/hooks/lorax.sh`** if one exists, so a repo with
  its own tuned copy is never pruned twice.
- Exits silently with no `gh`, no `origin` remote, or no network - no PR truth means
  nothing safe to decide, so it holds everything.
- Never runs from inside a worktree.

## Animation

An opt-in truffula animation plays in a small native panel (macOS) when a run
actually removes something. Gated on `~/.claude/lorax-anim-on`; off by default.
A `--report` never animates, because it cut nothing.

To demo it without waiting for a real prune, feed `--anim-prep` tab-separated
`<1|0>\t<name>\t<reason>` lines (1 = doomed) and open the result:

```sh
printf '1\tsome-branch\tPR #123 merged - work is in main\n0\tother\tPR still open\n' \
  | sh $LORAX/lorax.sh --anim-prep $LORAX/lorax-anim.html /tmp/lorax-demo.html
sh $LORAX/lorax-open.sh /tmp/lorax-demo.html 220 130
```

