# Worktrunk
[](https://crates.io/crates/worktrunk)
[](https://opensource.org/licenses/MIT)
Worktrunk is a CLI tool which makes working with git worktrees much much easier.
It's designed for those running many concurrent AI coding agents.
Git worktrees let multiple agents work on a single repo without colliding; each agent
gets a separate directory. But creating worktrees, tracking paths, and cleaning
up afterward is manual. Worktrunk automates that lifecycle.
## Quick Start
**Create a worktree:**
```bash
$ wt switch --create fix-auth
β
Created new worktree for fix-auth from main at ../repo.fix-auth/
```
**Work, make changes, then merge back:**
```bash
$ wt merge
π Merging 1 commit to main @ a1b2c3d (no commit/squash/rebase needed)
* a1b2c3d (HEAD -> fix-auth) Implement JWT validation
auth.rs | 13 +++++++++++++
1 file changed, 13 insertions(+)
β
Merged to main (1 commit, 1 file, +13)
π Removing worktree & branch...
β
Removed worktree & branch for fix-auth, returned to primary at ../repo/
# Shell back in main
```
**See all active worktrees:**
<!-- Output from: tests/snapshots/integration__integration_tests__list__readme_example_simple_list.snap -->
```bash
$ wt list
Branch Status HEADΒ± mainβ Path
main ./myapp/
feature-x β! +5 -2 β3 ./myapp.feature-x/
bugfix-y β +0 -0 β1 ./myapp.bugfix-y/
```
## Installation
```bash
cargo install worktrunk
wt config shell # Sets up shell integration
```
## Automation Features
### LLM-Powered Commit Messages
During merge operations, worktrunk can invoke a binary, such as [llm](https://llm.datasette.io/), to generate commit messages based on the diff and a configurable prompt.
Add to `~/.config/worktrunk/config.toml`:
```toml
[commit-generation]
command = "llm"
args = ["-m", "claude-haiku-4-5-20251001"]
```
Then `wt merge` will generate commit messages automatically:
```bash
$ wt merge
π Generating squash commit message...
feat(auth): Implement JWT authentication system
Add comprehensive JWT token handling including validation, refresh logic,
and authentication tests. This establishes the foundation for secure
API authentication.
- Implement token refresh mechanism with expiry handling
- Add JWT encoding/decoding with signature verification
- Create test suite covering all authentication flows
β
Squashed @ a1b2c3d
```
Set up LLM integration: run `wt config --help` to see the setup guide, or `wt config init` to create an example config file.
<details>
<summary><b>Advanced: Custom Prompt Templates</b></summary>
Worktrunk uses [minijinja templates](https://docs.rs/minijinja/latest/minijinja/syntax/index.html) for commit message prompts. Customize the prompts by setting `template` (inline) or `template-file` (external file) in the `[commit-generation]` section. Use `squash-template` / `squash-template-file` for squash commits.
See [`config.example.toml`](config.example.toml) for complete template examples with all available variables (`git_diff`, `branch`, `recent_commits`, `commits`, `target_branch`, `repo`).
</details>
### Project Hooks
Automate common tasks by creating `.config/wt.toml` in your repository root. Run tests before merging, install dependencies when creating worktrees, start dev servers automatically.
```toml
# Install deps when creating a worktree
[post-create-command]
"install" = "uv sync"
# Start dev server automatically
[post-start-command]
"dev" = "uv run dev"
# Run tests before merging
[pre-merge-command]
"test" = "uv run pytest"
"lint" = "uv run ruff check"
```
**Example: Creating a worktree with hooks:**
```bash
$ wt switch --create feature-x
π Creating worktree for feature-x...
β
Created worktree, changed directory to ../repo.feature-x/
π Running post-create install:
uv sync
Resolved 24 packages in 145ms
Installed 24 packages in 1.2s
π Running post-start dev (background):
uv run dev
Starting dev server on http://localhost:3000...
```
**Example: Merging with pre-merge hooks:**
```bash
$ wt merge
π Squashing 3 commits into 1 (2 files, +45)...
π Generating squash commit message...
feat(api): Add user authentication endpoints
Implement login and token refresh endpoints with JWT validation.
Includes comprehensive test coverage and input validation.
β
Squashed @ a1b2c3d
π Running pre-merge test:
uv run pytest
============================= test session starts ==============================
collected 18 items
tests/test_auth.py::test_login_success PASSED [ 11%]
tests/test_auth.py::test_login_invalid_password PASSED [ 22%]
tests/test_auth.py::test_token_refresh PASSED [ 33%]
tests/test_auth.py::test_token_validation PASSED [ 44%]
============================== 18 passed in 0.8s ===============================
π Running pre-merge lint:
uv run ruff check
All checks passed!
π Merging 1 commit to main @ a1b2c3d (no rebase needed)
* a1b2c3d (HEAD -> feature-auth) feat(api): Add user authentication endpoints
api/auth.py | 32 ++++++++++++++++++++++++++++++++
tests/test_auth.py | 13 +++++++++++++
2 files changed, 45 insertions(+)
β
Merged to main (1 commit, 2 files, +45)
π Removing worktree & branch...
β
Removed worktree & branch for feature-auth, returned to primary at ../repo/
```
<details>
<summary>All available hooks</summary>
| **post-create-command** | After `git worktree add` completes | Sequential, blocking | Logs warning, continues with remaining commands |
| **post-start-command** | After post-create completes | Parallel, non-blocking (background processes) | Logs warning, doesn't affect switch result |
| **pre-commit-command** | Before committing changes during `wt merge` (both squash and no-squash modes) | Sequential, blocking, fail-fast | Terminates merge immediately |
| **pre-merge-command** | After rebase completes during `wt merge` (validates rebased state before push) | Sequential, blocking, fail-fast | Terminates merge immediately |
| **post-merge-command** | After successful merge and push to target branch, before cleanup | Sequential, blocking | Logs warning, continues with remaining commands |
**Template variables:** `{{ repo }}`, `{{ branch }}`, `{{ worktree }}`, `{{ repo_root }}`, `{{ target }}`
**Skipping hooks:** `wt switch --no-verify` or `wt merge --no-verify`
**Security:** Commands require approval on first run. Use `--force` to bypass.
</details>
## Design Philosophy
Worktrunk is opinionated! It's not designed to be all things to all people. The choices optimize for agent workflows:
- Lots of short-lived worktrees
- CLI-based Agents
- Local inner dev loops
- Navigated using the shell
- Commits are squashed, linear histories
- Maximum automation
Standard `git worktree` commands continue working fine βΒ adopting Worktrunk for a portion of a workflow doesn't require adopting it for everything.
## Tips
**Create an alias for your favorite agent** - Shell aliases streamline common workflows. For example, to create a worktree and immediately start Claude:
```bash
alias wsl='wt switch --create --execute=claude'
```
Now `wsl new-feature` creates a branch, sets up the worktree, runs initialization hooks, and launches Claude in that directory.
**Automatic branch status in Claude Code** - The Claude Code integration shows which branches have active AI sessions. When Claude starts working, the branch shows `π€` in `wt list`. When waiting for input, it shows `π¬`. Setup instructions: [Custom Worktree Status](#custom-worktree-status).
**Auto-generated commit messages** - Simon Willison's [llm](https://llm.datasette.io/) tool integrates seamlessly with worktrunk's commit generation. Install it, configure the command, and `wt merge` will automatically generate contextual commit messages. Setup guide: [LLM-Powered Commit Messages](#llm-powered-commit-messages).
**Environment setup with hooks** - Each worktree is a separate directory. Use `post-create-command` to ensure consistent environments:
```toml
# In .config/wt.toml
[post-create-command]
"setup" = "uv sync && nvm install"
```
**Delegate to task runners** - Reference existing Justfile/Makefile commands instead of duplicating logic:
```toml
[post-create-command]
"setup" = "just install"
[pre-merge-command]
"validate" = "just test lint"
```
## All Commands
- `wt switch [branch]` - Switch to existing worktree
- `wt switch --create [branch]` - Create and switch (supports `--base=@` to branch from current HEAD)
- `wt remove [branch]` - Remove worktree (use `@` for current)
- `wt merge [target]` - Merge, push, cleanup
- `wt list` - Show all worktrees
- `wt config` - Manage configuration
- `wt beta` - Development and testing utilities (see below)
See `wt --help` for details.
<details>
<summary>Beta commands (<code>wt beta</code>)</summary>
Experimental commands for advanced workflows. These are subject to change.
- `wt beta commit` - Commit changes with LLM-generated message
- `wt beta squash [target]` - Squash commits with LLM-generated message
- `wt beta push [target]` - Push changes to target branch (auto-stashes non-conflicting edits)
- `wt beta rebase [target]` - Rebase current branch onto target
- `wt beta ask-approvals` - Approve commands in project config
- `wt beta run-hook <hook-type>` - Run a project hook for testing
- `wt beta select` - Interactive worktree selector (Unix only)
**Note:** Beta commands may have breaking changes between releases.
</details>
<details>
<summary>Status column symbols in <code>wt list</code></summary>
The Status column shows git repository state using compact symbols. Symbol order indicates priority: conflicts (blocking) β worktree state β git operations β branch divergence β working tree changes.
**Symbol order:** `= β‘β
β»β ββ β ββ β‘β£ ?!+Β»β`
| `Β·` | Branch without worktree | N/A | No |
| `=` | Conflicts with main | Blocking | No |
| `β‘` | Working tree matches main (identical to main branch, regardless of commit history) | Worktree state | Yes |
| `β
` | No commits (no commits ahead AND no uncommitted changes) | Worktree state | Yes |
| `β»` | Rebase in progress | Git operation | No |
| `β` | Merge in progress | Git operation | No |
| `β` | Bare worktree (no working directory) | Worktree attribute | No |
| `β ` | Locked worktree | Worktree attribute | No |
| `β ` | Prunable worktree | Worktree attribute | No |
| `β` | Commits ahead of main | Branch divergence | No |
| `β` | Commits behind main | Branch divergence | No |
| `β‘` | Commits ahead of remote | Remote divergence | No |
| `β£` | Commits behind remote | Remote divergence | No |
| `?` | Untracked files | Working tree | No |
| `!` | Modified files (unstaged) | Working tree | No |
| `+` | Staged files | Working tree | No |
| `Β»` | Renamed files | Working tree | No |
| `β` | Deleted files | Working tree | No |
Symbols combine to show complete state (e.g., `β‘β!` means matches main, behind main, and has unstaged changes).
**Dimming logic:** **Dimmed rows** indicate worktrees with no marginal information beyond main (no unique work). Lines dim when they have either `β‘` (matches main) OR `β
` (no commits). Both conditions use OR logic: either is sufficient to dim. This focuses attention on worktrees containing work.
**Branch-only entries:** Branches without worktrees show `Β·` in the Status column, indicating git status is not applicable (no working directory to check).
</details>
## Configuration
```bash
wt config list # Show all config files and locations
wt config init # Create global config with examples
wt config --help # Show LLM setup guide
```
<details>
<summary>Configuration details</summary>
**Global config** (`~/.config/worktrunk/config.toml`):
- `worktree-path` - Path template for new worktrees
- `[commit-generation]` - LLM command and prompt templates
**Project config** (`.config/wt.toml` in repository root):
- `[post-create-command]` - Commands after worktree creation
- `[post-start-command]` - Background commands after creation
- `[pre-commit-command]` - Validation before committing
- `[pre-merge-command]` - Validation before merge
- `[post-merge-command]` - Cleanup after merge
---
**Example global config** (`~/.config/worktrunk/config.toml`):
```toml
worktree-path = "../{{ main_worktree }}.{{ branch }}"
[commit-generation]
command = "llm"
args = ["-m", "claude-haiku-4-5-20251001"]
```
**Example project config** (`.config/wt.toml`): See Project Hooks section above.
**Path template defaults:** `../repo.branch/` (siblings to main repo). Available variables: `{{ main_worktree }}`, `{{ branch }}`, `{{ repo }}`.
</details>
## Advanced Features
### Custom Worktree Status
Add emoji status markers to worktrees that appear in `wt list`. Perfect for tracking work-in-progress states, CI status, or team coordination.
**Set status manually:**
```bash
# Set an emoji status for a branch (works everywhere)
git config worktrunk.status.feature-x "π§"
# Clear the status
git config --unset worktrunk.status.feature-x
```
**Status appears in the Status column:**
```
Branch Status HEADΒ± mainβ Path Remoteβ
Commit Age Message
main ./test-repo b834638e 10 months ago Initial commit
clean-no-status β‘ ./clean-no-status b834638e 10 months ago Initial commit
clean-with-status β‘ π¬ ./clean-with-status b834638e 10 months ago Initial commit
dirty-no-status ! +1 -1 ./dirty-no-status b834638e 10 months ago Initial commit
dirty-with-status β‘?π€ ./dirty-with-status b834638e 10 months ago Initial commit
```
The custom emoji appears directly after the git status symbols.
<details>
<summary><b>Automation with Claude Code Hooks</b></summary>
Claude Code can automatically set/clear emoji status when coding sessions start and end. This shows which branches have active AI sessions.
**Easy setup:** The Worktrunk repository includes a `.claude-plugin` directory with pre-configured hooks. If you're working in this repository, the hooks are automatically available.
**Manual setup for other repositories:** Copy the hooks from [`.claude-plugin/hooks/hooks.json`](.claude-plugin/hooks/hooks.json) to your `~/.claude/settings.json`.
Now when you use Claude:
- Sets status to `π€` for the current branch when you submit a prompt (working)
- Changes to `π¬` when Claude needs your input (waiting for permission or idle)
- Clears the status completely when the session ends
**Status from other terminal:**
```bash
$ wt list
Branch Status HEADΒ± mainβ Path
main ./myapp/
dirty-with-status β‘?π€ ./myapp.dirty-with-status/
```
**How it works:**
- Status is stored as `worktrunk.status.<branch>` in `.git/config`
- Each branch can have its own status emoji
- The hooks automatically detect the current branch and set/clear its status
- Status is shared across all worktrees on the same branch (by design)
- Works with any git repository, no special configuration needed
<details>
<summary><b>Alternative: Per-Worktree Status (Advanced)</b></summary>
For true per-worktree isolation (different status for multiple worktrees on the same branch), use worktree-specific config:
**One-time setup (enables per-worktree config for the repo):**
```bash
git config extensions.worktreeConfig true
```
**Set status from within a worktree:**
```bash
# From within the worktree
git config --worktree worktrunk.status "π§"
# Clear status
git config --worktree --unset worktrunk.status
```
**Claude Code hooks for per-worktree:**
Copy the hooks from [`.claude-plugin/hooks/hooks.worktree.json`](.claude-plugin/hooks/hooks.worktree.json) to your `~/.claude/settings.json`.
**Priority:** Worktree-specific config takes precedence over branch-keyed config when both exist.
</details>
</details>
### Worktree Paths
By default, worktrees live as siblings to the main repo:
```
myapp/ # primary worktree
myapp.feature-x/ # secondary worktree
myapp.bugfix-y/ # secondary worktree
```
Customize the pattern in `~/.config/worktrunk/config.toml`:
```toml
# Inside the repo (keeps everything contained)
worktree-path = ".worktrees/{{ branch }}"
# Shared directory with multiple repos
worktree-path = "../worktrees/{{ main_worktree }}/{{ branch }}"
```
### Shell Integration
Worktrunk can automatically configure your shell:
```bash
wt config shell
```
This adds shell integration to your config files (supports Bash, Zsh, Fish, Nushell, PowerShell, Elvish, Xonsh, Oil). The integration enables `wt switch` to change directories and `wt remove` to return to the previous location.
For manual setup instructions, see `wt config shell --help`.
## Status
Worktrunk is in active development. The core features are stable and ready for use. While the project is pre-1.0, the CLI interface and major features are unlikely to change significantly.
<details>
<summary><b>Developing</b></summary>
### Releases
Use [cargo-release](https://github.com/crate-ci/cargo-release) to publish new versions:
```bash
cargo install cargo-release
# Bump version, update Cargo.lock, commit, tag, and push
cargo release patch --execute # 0.1.0 -> 0.1.1
cargo release minor --execute # 0.1.0 -> 0.2.0
cargo release major --execute # 0.1.0 -> 1.0.0
```
This updates Cargo.toml and Cargo.lock, creates a commit and tag, then pushes to GitHub. The tag push triggers GitHub Actions to build binaries, create the release, and publish to crates.io.
Run without `--execute` to preview changes first.
</details>
## FAQ
### Does Worktrunk execute arbitrary commands on my machine?
Worktrunk executes commands in three contexts:
1. **Project hooks** (`.config/wt.toml`) - Automation for worktree lifecycle
2. **LLM commands** (`~/.config/worktrunk/config.toml`) - Commit message generation
3. **--execute flag** - Commands you provide explicitly
Commands from project hooks and LLM configuration require approval on first run. Approved commands are saved to `~/.config/worktrunk/approved.toml`. If a command changes, worktrunk requires new approval.
**Example approval prompt:**
```
π‘ Permission required: post-create install
uv sync
π‘ Allow and remember? [y/N]
```
Use `--force` to bypass prompts (useful for CI/automation).
### Installation fails with C compilation errors
If you encounter errors related to tree-sitter or C compilation (like "error: 'for' loop initial declarations are only allowed in C99 mode" or "undefined reference to le16toh"), install without syntax highlighting:
```bash
cargo install worktrunk --no-default-features
```
This disables bash syntax highlighting in command output but keeps all core functionality. The syntax highlighting feature requires C99 compiler support and can fail on older systems or minimal Docker images.