Part of the shll toolkit — see all projects there.
A small CLI that wraps git worktree with opinionated defaults: worktrees
are created as siblings of the main repo (<repo>.worktrees/<name>/), names
are memorable random adjective-noun pairs, and a shell wrapper makes
cd-into-worktree from a menu actually work. Designed for the parallel-edit
workflow where each branch (or each AI session) gets its own checkout.
curl -fsSL https://shll.ai/install | sh -s -- wtInstalls wt (plus the shll meta-CLI) via Homebrew, handling tap trust automatically. To install the entire shll toolkit instead:
curl -fsSL https://shll.ai/install | sh- Sibling layout, not clutter — worktrees go in
<repo>.worktrees/<name>/, never inside the main repo. - Memorable names — random adjective-noun pairs (
lively-otter,bold-fox) instead offeature-1,feature-2. - Real
cdfrom a menu — the shell wrapper letswt openactually change your shell's directory (something a plain binary can't do). - Per-worktree init — each new worktree runs an init script (default
fab sync, override viaWORKTREE_INIT_SCRIPT) so it's ready to use immediately.
Manual (requires Go and just):
git clone https://github.com/sahil87/wt
cd wt
just local-install # builds bin/wt and copies to ~/.local/bin/wtFor the "Open here" menu option to actually cd your current shell, add the
wrapper to your shell profile:
eval "$(wt shell-init zsh)" # in ~/.zshrc
eval "$(wt shell-init bash)" # in ~/.bashrc💡 Have other shll tools?
shll shell-installhandles all of their shell integrations and autocompletions at once.
For the full install guide — every path, the shell wrapper, and cross-tool setup — see the install guide.
A typical first session:
$ wt create
Created: ../wt.worktrees/lively-otter
Branch: lively-otter (from main)
$ wt list
Worktrees for: wt
Location: /Users/you/code/wt.worktrees
Name Branch Path
* (main) main wt/
lively-otter lively-otter wt.worktrees/lively-otter/
$ wt open lively-otter # menu → "Open here" cd's your shell
$ wt delete lively-otter # removes worktree (and optionally the branch)
| Command | Summary |
|---|---|
wt create [branch] (alias wt new) |
Create a worktree on a new branch (random name, or the named positional). Key flags: --checkout <branch> (existing branch), --base <ref>, --reuse, --name/-n, --non-interactive. |
wt list (alias wt ls) |
List all worktrees with name, branch, and path. Add --status for dirty/unpushed indicators; --path and --json for scripting. |
wt open [name|path] |
Open a worktree — or any directory — in a detected app (editor, terminal, file manager). No arg opens the current context; --app/-a skips the app menu. |
wt go [name] |
Pick a worktree (menu or by name) and cd there via the shell wrapper. --open launches the selection instead (menu with --open prompt). |
wt delete [names...] (alias wt rm) |
Delete one or more worktrees with optional branch cleanup. |
wt init |
Run the worktree init script (default fab sync, override via WORKTREE_INIT_SCRIPT). |
wt shell-init <shell> |
Print a shell wrapper function (zsh or bash) for eval in your shell profile. |
wt skill |
Print the agent usage bundle — a static, one-page markdown briefing for an agent operating wt (offline, embedded, versioned with the binary). |
Run wt <command> --help for inline flag details. For every flag and exit code, see the auto-generated command reference; for the --base start-point rules and the wt open launcher matrix, see the workflows guide.
--base <ref> controls the start-point when wt creates a new branch (maps to git worktree add -b <branch> <path> <start-point>). Behavior depends on whether the branch already exists:
| Scenario | --base |
Behavior |
|---|---|---|
| New branch (doesn't exist locally or remotely) | provided | Branch created from --base ref |
| New branch | omitted | Branch created from HEAD (default) |
| Positional names an existing branch (local or remote) | either | Error (exit 2): the positional only creates new branches — use --checkout <branch> |
With --checkout <branch> |
provided | Error (exit 2): --base sets a new branch's start-point; --checkout targets an existing branch |
| Exploratory (no branch arg) | provided | Exploratory branch created from --base ref |
| Exploratory | omitted | Branch created from current HEAD (default) |
With --reuse (worktree exists) |
provided | --reuse takes precedence; --base has no effect |
| Invalid ref | provided | Error exit; no worktree or branch created |
To put a worktree on an existing branch (local or remote), opt in explicitly
with --checkout <branch> — e.g. wt create --checkout sockets-v2. A remote-only
branch is fetched first; a branch that exists nowhere is an error pointing you
back at the positional/--base form.
The ref is validated via git rev-parse --verify before worktree creation, so invalid refs produce a clear error rather than a partial failure.
The two commands split along two axes — selection (which directory) and
action (navigate vs. launch). Each menu lives in exactly one verb: wt go
owns the "which worktree?" menu, wt open owns the "which app?" menu. Compose
them with wt go --open:
| Invocation | Worktree menu? | App menu? | Result |
|---|---|---|---|
wt go |
yes | no | cd to selection |
wt go frosty-fox |
no | no | cd directly |
wt go --open prompt |
yes | yes | launch selection in chosen app |
wt go --open code |
yes | no | launch selection in VS Code |
wt open |
no | yes | launch current dir (worktree root / repo root / cwd) |
wt open <name|path> |
no | yes | launch that dir |
wt open --app code |
no | no | launch current dir in VS Code |
wt open is the canonical directory launcher in the toolkit (hop
delegates to it too): no arg opens the current context, a name resolves against
this repo's worktrees (requires a git repo), and a path opens literally — git
context doesn't matter. The app menu lists the apps wt detected on your machine
(editors, terminals, file managers) plus an "Open here" option that cds
your current shell into the target — that one needs the shell wrapper (see
Gotchas).
Picking a worktree and launching it, with two worktrees on disk:
$ wt go --open prompt
Select worktree to open:
1) main (main)
2) lively-otter (feature/spinner) (default)
3) bold-fox (fix/race-condition)
0) Cancel
Choice [2]: 2
Open in:
1) Open here
2) VSCode (default)
3) Cursor
4) Ghostty
5) Terminal.app
6) Finder
7) Copy path
0) Cancel
Choice [2]:
Pick 1 to cd your shell into the worktree, 2–6 to launch it in a
detected app, or 7 to copy the absolute path to your clipboard.
wt opencan'tcdwithout the shell wrapper. A child process can't change its parent shell's directory — that's a Unix constraint, not a wt bug.eval "$(wt shell-init zsh)"(orbash) installs a shell function that wraps the binary so the "Open here" menu option actually works.- The
wt createpositional never checks out an existing branch. Naming a branch that already exists (locally or on the remote) is an error (exit 2) — checkout of an existing branch is an explicit opt-in via--checkout <branch>.--reusetakes precedence over--base. - Worktrees survive
cdinto deleted directories. If you delete a worktree from outside (rm -rf), rungit worktree prunein the main repo to clean up git's bookkeeping.