# wt
`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.
## Getting Started
### 1. Install
#### Homebrew (recommended)
```bash
brew install getkono/tap/wt
```
This pulls a prebuilt binary from the [getkono/homebrew-tap](https://github.com/getkono/homebrew-tap)
tap (macOS arm64/x86_64 and Linux arm64/x86_64).
#### Cargo (crates.io)
The crate is published as [`kono-wt`](https://crates.io/crates/kono-wt) (the bare
`wt` name was already taken); the installed binary is still `wt`.
```bash
cargo install kono-wt # installs `wt` to ~/.cargo/bin
```
#### From source
You need the [Rust toolchain](https://rustup.rs) (rustup), `git` ≥ 2.20 on your
`PATH`, and — only for PR commands — the [`gh` CLI](https://cli.github.com).
```bash
cargo install --git https://github.com/getkono/wt # latest from master
# or, from a checkout:
cargo install --path . # installs `wt` to ~/.cargo/bin
```
Make sure `~/.cargo/bin` is on your `PATH`. Then enable shell integration
(below) — that single step also gives you the best tab completion.
### 2. Enable shell integration (required for navigation)
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 `cd`s you in. Source it from your shell rc once:
```bash
# ~/.zshrc or ~/.bashrc
eval "$(wt shell-init zsh)" # use `bash` for bash
# fish (~/.config/fish/config.fish)
wt shell-init fish | source
# PowerShell ($PROFILE)
**Without 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.)
### 3. Authenticate `gh` (only for PR commands)
```bash
gh auth login
```
Everything 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.
### 4. Open it
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:
```bash
wt new feature/login # create the branch + worktree and switch into it
wt switch # fuzzy-pick a worktree to jump to
```
Run `wt --help` (or `wt <command> --help`) for the complete command surface.
## Key features to know
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 `Enter` to 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, `Enter` to 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 with
`wt 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 `.env`
don't follow a new worktree. List glob patterns under `copy` to bring them along
on `wt 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_remove`
runs before removal. Hooks receive `WT_WORKTREE_PATH`, `WT_BRANCH`,
`WT_REPO_ROOT`, and friends in their environment.
- **Configuration lives in two places.** A per-repo `.wt.toml` at the repo root and
a global user config, managed with `wt config get|set|list|edit` (`--global` for
the user config); precedence is flags > repo > global. `wt init` is an optional
convenience that scaffolds a starter `.wt.toml` and, 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]`: `preset` selects the base (`one-dark` (default) or `solarized`),
and the named slots (`accent`, `green`, `red`, `yellow`, `orange`, `cyan`,
`magenta`, `gray`, `selection_bg`, `chip_fg`) override it. Colors are `#rrggbb`
hex, 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.
```toml
[ui.theme]
preset = "solarized"
accent = "#ff8800"
```
- **Removal protects your work.** `wt remove` and `wt prune` refuse to drop a
worktree with uncommitted or unpushed changes unless you pass `--force`.
- **Drop the worktree you're in.** `wt drop` removes the worktree containing the
current directory (from any depth), keeps the branch, and `cd`s you back to the
main worktree. It refuses the primary worktree and honors the same `--force`
guard.
- **Bulk-clean stale branches.** `wt prune --merged` removes worktrees whose branch
is merged into the default branch, and `wt prune --gone` removes 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 `--gone`
branch 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.
## Development
### Prerequisites
- [Rust (rustup)](https://rustup.rs) — toolchain (pinned via `rust-toolchain.toml`)
- [mise](https://mise.jdx.dev) — tool manager + task runner
- [hk](https://hk.jdx.dev) — git hooks manager
Run `mise install` once to fetch the pinned dev tools (`hk`, `pkl`,
`cargo-llvm-cov`, `cargo-mutants`).
| `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.
### Tech Stack
- **Runtime:** Rust (edition 2024)
- **Formatter:** rustfmt
- **Linter:** Clippy
- **Task runner:** mise
- **Git hooks:** hk
- **Key Dependencies:** tokio, eyre + color-eyre, tracing + tracing-subscriber, thiserror
### Architecture
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.
### Git Hooks
This project uses [hk](https://hk.jdx.dev), 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.
### CI/CD
GitHub Actions runs format checks, linting, tests, and coverage on pushes to
`main` and pull requests.
### Code Coverage
This project uses [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov)
for LLVM-based code coverage. CI enforces a minimum of 80% line coverage and
uploads the report as a CI artifact.
```bash
mise run coverage
```
## License
MIT — see [LICENSE](LICENSE) for details.