wt is an ergonomic CLI for working with git worktrees. It creates, switches between, and tears down worktrees with zero config for the basics — plus optional per-project automation for setup commands, file symlinks, and background services.
Built with Bun.
- Conventions over configuration —
wt my-featurejust works; no config required. - Smart branch detection — checks out a local or remote branch, or creates a new one.
cdinto your worktree — the shell integration drops you straight into the new worktree oncreate/switch.- Automatic setup — run install/migration commands in each new worktree (
setup-worktree). - Teardown hooks — run cleanup commands before a worktree is removed (
teardown-worktree). - File symlinking — share
node_modules,.env, data dirs, etc. from the main worktree (opt-in). - Service orchestration — start/stop background services per worktree, ensuring only the active worktree's services run (
wt run). - Interactive cleanup — multi-select stale worktrees to remove (
wt clean). - Scriptable —
--jsonoutput pluswt path/wt existsmake it easy to build other tooling on top.
- Bun
- Git 2.5+ (worktree support)
- zsh (for the shell integration and completion installer)
git clone https://github.com/DXTimer/wt.git
cd wt
bun install
bun wt install # configures PATH, shell wrapper, and completion
source ~/.zshrcwt install is idempotent — safe on a fresh or already-configured machine. It:
- runs
bun install - adds
wtto yourPATH - installs the shell wrapper so
wt create/wt switchchange your current directory into the worktree - enables zsh tab completion
It manages a single marked block in ~/.zshrc (honoring $ZDOTDIR), so re-running never duplicates setup.
Useful flags:
bun wt install --print # print the shell block without writing anything
bun wt install --skip-install # configure the shell but skip `bun install`Prefer to wire it up by hand? See Shell integration below.
wt feature/login # create + cd into a worktree for feature/login
wt ls # list all worktrees
wt sw - # switch back to the previous worktree
wt rm feature/login # remove it| Command | Description |
|---|---|
wt <branch> [path] |
Create a worktree (default command). Detects a local/remote branch or creates a new one. |
wt switch | sw [name|-] |
Switch to a worktree (interactive, by name, or - for the previous one). |
wt list | ls |
List all worktrees with branch, commit, and path. |
wt remove | rm | delete <name> |
Remove a worktree by branch, path, or directory name. Runs teardown-worktree first. |
wt clean | cleanup |
Interactive multi-select removal of worktrees. |
wt setup [name] |
Re-run setup (symlinks + commands) on an existing worktree. |
wt run <worktree> [services...] |
Start the worktree's services/modifiers, stopping conflicting ones running elsewhere. |
wt logs [service|all] |
Stream service logs. |
wt path --branch <name> |
Print the filesystem path of a branch's worktree. |
wt exists --branch <name> |
Exit 0 if a worktree exists for the branch. |
wt install | init |
Set up / refresh the zsh integration (idempotent). |
Common flags: --force and --delete-branch (remove); --skip-links, --skip-setup, --verbose/-v (create/setup); and the global --json and --project <path>.
wt feature/authentication # existing remote branch
wt feature/new-feature # new branch
wt hotfix/critical-bug ~/wt/urgent # custom pathBy default, worktrees are created at ../worktrees/<branch-name> relative to your git root, with the branch name sanitized (feature/login → feature-login). For a repo at /Users/you/projects/myapp, wt feature/login creates /Users/you/projects/worktrees/feature-login.
wt rm feature/authentication # by branch name
wt rm feature-authentication # by directory name
wt rm --force old-branch # even with uncommitted changes
wt rm --delete-branch stale # also delete the branch
wt clean # interactive multi-selectCreate .wt/worktrees.json in your project root (or any ancestor directory up to ~). All keys are optional — see .wt/worktrees.json.example for a complete example.
{
"setup-worktree": [
"bun install",
"cp $WT_MAIN/.env .env"
],
"teardown-worktree": [
"docker compose down || true"
],
"symlinks": {
"patterns": ["node_modules", ".env*", "*.sqlite", ".data/**"],
"exclude": [".env.test", ".env.example"]
},
"services": {
"backend": {
"start": "bun run dev:backend > .backend.log 2>&1 &",
"check": "pgrep -f 'bun run dev:backend'",
"stop": "pkill -f 'bun run dev:backend'",
"logs": "tail -f .backend.log"
}
},
"modifiers": {
"seed": "bun run db:seed"
}
}| Key | Purpose |
|---|---|
setup-worktree |
Commands run, in order, in each new worktree after creation. |
teardown-worktree |
Best-effort commands run in a worktree before it is removed. |
symlinks |
Glob patterns (and optional exclude) of files to symlink from the main worktree. Opt-in. |
services |
Named start/stop/check/logs commands managed by wt run. wt run ensures a service runs only for the active worktree. |
modifiers |
Named one-off commands invoked via wt run <worktree> <modifier>. |
A command ending in || true (or || :) is treated as soft: a failure logs a warning instead of aborting setup.
Setup, teardown, and modifier commands receive:
| Variable | Value |
|---|---|
$WT_MAIN |
Path to the main worktree (original checkout). |
$WT_PATH |
Path to the worktree the command is running in. |
$WT_GIT_ROOT |
Git root directory. |
$WT_BRANCH |
Branch name (available in teardown and wt run). |
Legacy: if no
.wt/worktrees.jsonis found,wtfalls back to.cursor/worktrees.jsonfor backward compatibility.
wt install does this for you. To wire it up by hand, add the following to ~/.zshrc (replace the paths):
export PATH="/path/to/wt/bin:$PATH"
# cd into the worktree after create/switch
wt() {
command wt "$@"
if [[ -f /tmp/wt_switch_target ]]; then
cd "$(cat /tmp/wt_switch_target)"
rm /tmp/wt_switch_target
fi
}
# Completion (zsh)
fpath=(/path/to/wt/completion $fpath)
autoload -Uz compinit && compinitwt is built to be composed with other tools:
# Get the path of a worktree (prints just the path)
dir=$(wt path --branch feature/login)
# Or as JSON: { "path": "...", "branch": "...", "exists": true }
dir=$(wt path --branch feature/login --json | jq -r .path)
# Guard on existence (exits non-zero if missing)
wt exists --branch feature/login && echo "already provisioned"--json makes commands emit machine-readable output on stdout while informational logs go to stderr — which is what programmatic consumers (including Claude Code worktree hooks) rely on. --project <path> runs against a repo other than the current directory.
docs/ai_config_prompt.md is a ready-made prompt for having an AI assistant generate a .wt/worktrees.json tailored to your project.
MIT © Ivan Bakalov
Built and maintained by Ivan Bakalov.