Worktree namespaces
This is an opinionated naming convention that I build into the tool. It is
entirely optional — you can use slice wt (or its alias slice branch) to
create a worktree with any path you like, and the rest of the tool works the
same. But I like this convention enough to build shortcuts for it, and here
is my pitch for why you might like it too.
The problem
I found that in a bare+worktrees layout I accumulate worktrees quickly — a feature branch, a throwaway experiment, a colleague's PR I'm reviewing. After a week they all look the same: a flat list of directories with no indication of which ones I can safely blow away and which ones have uncommitted work I care about.
The convention
I use three prefixes — local/, tmp/, and pr/ — to signal my intent for
each worktree:
tmp/— stuff I don't intend to keep. Quick experiments, ideas I launched a coding agent to run down, things I tried and forgot about. Not necessarily safe to delete right now — an agent might still be running — but this is work I haven't invested care into and don't plan to maintain.local/— work I plan to ship. These are my in-progress feature branches. I should clean them up after they merge, but deleting one prematurely means losing real work, soslice rmprompts for confirmation.pr/— someone else's code. I fetched a pull request to review it locally. There are no local commits here, just a detached snapshot of the remote branch. Safe to delete in bulk once I'm done reviewing.
The distinction between tmp/ and local/ is the one I care about most. It
lets me bucket "here are throwaway ideas I launched a coding agent to run down
for me" separately from "here are things I've put thought and care and
attention into." That difference matters to me when I'm cleaning up, and it
matters when I'm demoing to colleagues — they can immediately tell whether
I'm showing a throwaway prototype or something I plan to ship.
Shared branches are naturally top-level
Branches that everyone on the team uses — main, dev — sit at the top level
as siblings of these directories. Most teams have a small fixed number of
shared branches, two or three, so they don't add noise. My project directory
looks like this:
~/src/foo/
+clone+/
+state+/
main/ shared branch
dev/ shared branch
local/ my in-progress work (may have several inside)
tmp/ throwaway experiments (may have many inside)
pr/ reviews (come and go)
The top-level entry count stays bounded no matter how many worktrees I have open, because my worktrees grow inside subdirectories rather than alongside the shared branches.
Why this is visible, not hidden
I like my software to be easily inspectable. Worktrees are plain directories,
not tucked into a dotfile or a database. ls is my UI for seeing what
workstreams exist, and du -sh */ tells me where disk is going. There is no
metadata to query; the filesystem is the source of truth.
Why "local" and not my name
I could use my name as the prefix — steve/feature-one — and everything
would work fine via slice wt. I chose local because it means the same
thing on every machine and every project without leaking a personal name into
branch listings. If you prefer a name-based convention, slice wt is right
there.
Why it matters
I find the convention pays off in three situations:
-
Coming back after a week. I open the project directory and the layout tells me what I was doing. The
tmp/stuff I can probably ignore. Thelocal/stuff is my active work. Thepr/entries are reviews I haven't finished. -
Cleanup. The naming makes cleanup mechanical.
tmp/is almost certainly safe to delete.pr/has no local commits and can be re-fetched.local/is the only category that requires judgment. This is what makesslice gcpossible — it can confidently targettmp/worktrees without asking me about each one. -
Demos and pairing. When I'm sharing my screen, a colleague can immediately see the difference between a throwaway prototype and something I've put thought into. It manages expectations without me having to explain.
The same clarity carries over into git itself — git branch shows
local/feature-auth, tmp/try-sqlx, pr/4521 — so the signal follows me
into any tool that lists branches, not just the filesystem.
Creating worktrees
slice local, slice tmp, and slice pr are shortcuts that create worktrees
following this convention. The directory path and the git branch name are always
the same.
slice local
Create a worktree for your own work.
slice local <name> [-b <ref>] [--from <state>] [hook] [args...]
$ slice local feature-auth # branch local/feature-auth from HEAD
$ slice local feature-auth -b main # branch from main
$ slice local feature-auth -b v2.1.0 # branch from a tag
slice tmp
Create a throwaway worktree. Same mechanics as slice local, but signals that this branch is disposable.
slice tmp <name> [-b <ref>] [--from <state>] [hook] [args...]
$ slice tmp try-sqlx # quick experiment
$ slice tmp fix-login --from current claude -p "fix the redirect"
Throwaway worktrees are cleaned up with slice rm tmp/<name> or in bulk with slice gc.
slice pr checkout
Fetch a pull request and create a worktree for reviewing it.
slice pr checkout <number> [--from <state>] [hook] [args...]
$ slice pr checkout 4521
$ slice pr checkout 4521 --from template-clean review
Internally this runs:
git fetch origin pull/<number>/head:pr/<number>
git worktree add pr/<number> pr/<number>
The --from flag
When creating a worktree, --from specifies which state to bind it to:
--from template-clean # clone the template-clean state directory
--from current # clone whatever state the current worktree is using
--from dev-signed-in # clone a specific named state
When --from is used, a new state directory is created as a clone of the source, the worktree is bound to the new state, and (if a hook is provided) everything is started.
When --from is omitted, the worktree is created but no state is attached. You'll need to run slice use <state> before slice up.
Hooks on creation
If a hook name is provided after the flags, it's executed after the worktree and state are set up. Everything after the hook name is passed through as arguments.
slice tmp fix-login --from current claude -p "fix the redirect bug"
# ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
# hook args ($@ inside the hook)
The hook must be defined in hooks.new-worktree in your config. See Hooks.
Branch naming
The worktree path is the branch name. This gives you a clean branch namespace for free:
$ git branch
dev
local/feature-auth
local/refactor-db
* main
pr/4521
tmp/fix-login
tmp/try-sqlx
At a glance you know whose branches are whose, what's disposable, and what's under review. Top-level names (main, dev) track shared remote branches. Namespaced names are local work.
Removing worktrees
slice rm <worktree> [--yes|-y]
This stops any running processes, removes the git worktree, deletes the branch, and removes any throwaway state that was created with --from during worktree creation.
$ slice rm tmp/fix-login # clean up throwaway work
$ slice rm pr/4521 # done reviewing
$ slice rm local/feature-auth # prompts for confirmation
Before removing any worktree, slice rm checks whether the working tree is
clean and the branch tip exists on at least one remote (git branch -r --contains). If both hold, removal proceeds silently. Otherwise it shows the
reasons and prompts for confirmation. Pass --yes (-y) to skip. The remote
check looks at all configured remotes, not just the tracking remote.
Garbage collection
slice gc
Removes all tmp/ worktrees that aren't currently running. This is the "my laptop is getting cluttered" escape valve after firing off several agents.

