wt is a single-binary CLI + TUI for managing Git worktrees and their GitHub
pull requests: create a branch and worktree in one step, jump between them, check
out PRs into isolated directories, and clean up when work merges. Git is the
source of truth — worktrees you create or remove with plain git show up
automatically.
brew install getkono/tap/wtThis pulls a prebuilt binary from the getkono/homebrew-tap tap (macOS arm64/x86_64 and Linux arm64/x86_64).
The crate is published as kono-wt (the bare
wt name was already taken); the installed binary is still wt.
cargo install kono-wt # installs `wt` to ~/.cargo/binYou need the Rust toolchain (rustup), git ≥ 2.20 on your
PATH, and — only for PR commands — the gh CLI.
cargo install --git https://github.com/getkono/wt # latest from master
# or, from a checkout:
cargo install --path . # installs `wt` to ~/.cargo/binMake sure ~/.cargo/bin is on your PATH. Then enable shell integration
(below) — that single step also gives you the best tab completion.
A program can't change its parent shell's working directory, so on its own wt
can only print where to go. The shell wrapper closes that gap: it captures the
path and cds you in. Source it from your shell rc once:
# ~/.zshrc or ~/.bashrc
eval "$(wt shell-init zsh)" # use `bash` for bash
# fish (~/.config/fish/config.fish)
wt shell-init fish | source
# PowerShell ($PROFILE)
wt shell-init powershell | Out-String | Invoke-ExpressionWithout it, switch, new, pr, and the TUI just print a path instead of
moving you. Supported shells: bash, zsh, fish, powershell, elvish. On anything
else, wt switch --print-path lets you build your own cd alias.
This is also the recommended way to get tab completion. The shell-init snippet
installs dynamic completions that suggest live values — real worktree names,
branches, and PR numbers (via wt __complete) — not just the static command and
flag list. Because you need to source it for navigation anyway, it's the single
step that sets up everything; there's no separate completions install. (A static,
values-unaware script is still available via wt completions <shell> if you want
to manage it yourself.)
gh auth loginEverything except wt pr works fully offline. If gh is missing or
unauthenticated, only the PR commands fail (with an actionable message); the rest
keep working.
Run wt with no arguments in any repository to launch the TUI dashboard, then
press ? for the full keymap — creating, switching, removing, checking out PRs,
sorting, and filtering are all discoverable from there. For example:
wt new feature/login # create the branch + worktree and switch into it
wt switch # fuzzy-pick a worktree to jump toRun wt --help (or wt <command> --help) for the complete command surface.
These are the things worth knowing up front; the rest is discoverable from
--help and the TUI.
-
See every branch, not just worktrees. The TUI lists your worktrees first, then — dimmed beneath them — any local branch that has no worktree, each with how far it is ahead/behind its base. Select one and press
Enterto create a worktree for it and switch in (it asks first). A branch left behind after you remove its worktree stays visible here instead of vanishing. -
Pick options on pop-up fields. TUI fields with known choices offer an inline dropdown instead of blind typing. The new-worktree branch/base fields suggest existing local and remote branches to fork from or check out — type to filter,
↑/↓to pick,Enterto accept, or just type a brand-new name. The PR compose form's model and effort fields list their choices the same way. -
Where worktrees are created. New worktrees follow a configurable path template. The default keeps them beside the repo, out of it, and prefixes each worktree directory with the repo name so it's obvious which repo you're in:
{repo_parent}/{repo}.worktrees/{repo}-{branch_slug}. Change it withwt config set path_template …. Common alternatives: a subdir inside the repo,{repo_root}/.worktrees/{branch_slug}(add it to.gitignore), or a central store,{home}/worktrees/{repo}/{branch_slug}. Worktrees you made by hand anywhere are still listed and managed. -
Auto-copy ignored files into new worktrees. Git-ignored files like
.envdon't follow a new worktree. List glob patterns undercopyto bring them along onwt new, e.g.copy = [".env", ".env.local"]. -
Run a command after creating a worktree.
hooks.post_create(e.g.npm install,direnv allow) runs inside the new worktree;hooks.pre_removeruns before removal. Hooks receiveWT_WORKTREE_PATH,WT_BRANCH,WT_REPO_ROOT, and friends in their environment. -
Hand the new worktree straight to a tool.
--start <command>runs a command inside the worktree once it is fully set up — after the copy step, thepost_createhook, and submodule init — and leaves your shell there afterwards:wt new feat/login -y --start "claude --permission-mode plan" wt pr 42 --start "claude" # check a PR out and review it
It works on
wt new,wt checkout, andwt pr. The command gets a real terminal, so interactive tools work, andwtexits with the command's status. It sees the sameWT_*variables hooks do ($WT_BRANCH,$WT_PR_NUMBER, …) —wtdoes not interpolate{branch}-style placeholders, which would collide with shell braces. Thecdafterwards needs the shell integration from step 2; re-runwt shell-init <shell>if you set it up before--startexisted. -
Say yes to everything.
-y/--yesis a global flag that answers every confirmation prompt, sowtcan run unattended. It is not--force: the safety guards onremove,drop, andprune— dirty worktrees, unpushed commits, unmerged branches — still hold, and still need--forceto override. -
Configuration lives in two places. A per-repo
.wt.tomlat the repo root and a global user config, managed withwt config get|set|list|edit(--globalfor the user config); precedence is flags > repo > global.wt initis an optional convenience that scaffolds a starter.wt.tomland, for a subdir store, offers to add it to.gitignore. -
Theme the TUI. Pick a built-in palette and tweak individual colors under
[ui.theme]:presetselects the base (one-dark(default) orsolarized), and the named slots (accent,green,red,yellow,orange,cyan,magenta,gray,selection_bg,chip_fg) override it. Colors are#rrggbbhex, a named color (e.g.cyan,light-blue), or a 0–255 ANSI index. Like every setting, themes merge across layers (a global base palette, per-repo accents), e.g.[ui.theme] preset = "solarized" accent = "#ff8800"
-
Removal protects your work.
wt removeandwt prunerefuse to drop a worktree with uncommitted or unpushed changes unless you pass--force. -
Drop the worktree you're in.
wt dropremoves the worktree containing the current directory (from any depth), keeps the branch, andcds you back to the main worktree. It refuses the primary worktree and honors the same--forceguard. -
Bulk-clean stale branches.
wt prune --mergedremoves worktrees whose branch is merged into the default branch, andwt prune --goneremoves worktrees whose upstream was deleted (plus any missing worktrees). Both also delete matching local branches that no longer have a worktree — so a repo left with a pile of merged feature branches gets cleaned up too. Preview with--dry-run. A--gonebranch that isn't also merged may hold unmerged commits, so it is skipped unless you pass--force. The current and default branches are never touched.
- Rust (rustup) — toolchain (pinned via
rust-toolchain.toml) - mise — tool manager + task runner
- hk — git hooks manager
Run mise install once to fetch the pinned dev tools (hk, pkl,
cargo-llvm-cov, cargo-mutants).
| Command | Description |
|---|---|
mise tasks |
List available tasks |
cargo run |
Run the application |
mise run install |
Build and install wt to ~/.cargo/bin |
mise run test |
Run tests |
mise run format |
Format code |
mise run lint |
Lint with Clippy (warnings as errors) |
mise run lint-fix |
Lint and auto-fix |
mise run coverage |
Run tests with coverage (min 80%) |
After cloning, run mise install to fetch the dev tools, then hk install
once to activate the git hooks.
- Runtime: Rust (edition 2024)
- Formatter: rustfmt
- Linter: Clippy
- Task runner: mise
- Git hooks: hk
- Key Dependencies: tokio, eyre + color-eyre, tracing + tracing-subscriber, thiserror
The logic lives in the library crate (src/lib.rs) so it is unit-testable and
measured by coverage. The binary (src/main.rs) is a thin entry point that
wires up error reporting and tracing, then delegates to the library; it is
excluded from coverage.
This project uses hk, configured in hk.pkl.
Pre-commit hooks auto-fix formatting and linting on staged Rust files.
Pre-push hooks run format checks, Clippy, tests, and the coverage gate.
GitHub Actions runs format checks, linting, tests, and coverage on pushes to
main and pull requests.
This project uses cargo-llvm-cov
for LLVM-based code coverage. CI enforces a minimum of 80% line coverage and
uploads the report as a CI artifact.
mise run coverageMIT — see LICENSE for details.