Worktrunk
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:
Work, make changes, then merge back:
)
)
|
)
)
&
&
# Shell back in main
See all active worktrees:
Installation
Automation Features
LLM-Powered Commit Messages
During merge operations, worktrunk can invoke a binary, such as llm, to generate commit messages based on the diff and a configurable prompt.
Add to ~/.config/worktrunk/config.toml:
[]
= "llm"
= ["-m", "claude-haiku-4-5-20251001"]
Then wt merge will generate commit messages automatically:
)
Set up LLM integration: run wt config --help to see the setup guide, or wt config init to create an example config file.
Worktrunk uses minijinja templates 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 for complete template examples with all available variables (git_diff, branch, recent_commits, commits, target_branch, repo).
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.
# Install deps when creating a worktree
[]
= "uv sync"
# Start dev server automatically
[]
= "uv run dev"
# Run tests before merging
[]
= "uv run pytest"
= "uv run ruff check"
Example: Creating a worktree with hooks:
)
Example: Merging with pre-merge hooks:
)
)
=============================
==============================
)
) )
|
|
)
)
&
&
| Hook | When It Runs | Execution | Failure Behavior |
|---|---|---|---|
| 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.
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:
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.
Auto-generated commit messages - Simon Willison's llm 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.
Environment setup with hooks - Each worktree is a separate directory. Use post-create-command to ensure consistent environments:
# In .config/wt.toml
[]
= "uv sync && nvm install"
Delegate to task runners - Reference existing Justfile/Makefile commands instead of duplicating logic:
[]
= "just install"
[]
= "just test lint"
All Commands
wt switch [branch]- Switch to existing worktreewt 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, cleanupwt list- Show all worktreeswt config- Manage configurationwt beta- Development and testing utilities (see below)
See wt --help for details.
Experimental commands for advanced workflows. These are subject to change.
wt beta commit- Commit changes with LLM-generated messagewt beta squash [target]- Squash commits with LLM-generated messagewt beta push [target]- Push changes to target branch (auto-stashes non-conflicting edits)wt beta rebase [target]- Rebase current branch onto targetwt beta ask-approvals- Approve commands in project configwt beta run-hook <hook-type>- Run a project hook for testingwt beta select- Interactive worktree selector (Unix only)
Note: Beta commands may have breaking changes between releases.
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: = β‘β
β»β ββ β ββ β‘β£ ?!+Β»β
| Symbol | Meaning | Category | Dimmed? |
|---|---|---|---|
Β· |
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).
Configuration
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):
= "../{{ main_worktree }}.{{ branch }}"
[]
= "llm"
= ["-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 }}.
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:
# Set an emoji status for a branch (works everywhere)
# Clear the status
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.
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 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:
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
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):
Set status from within a worktree:
# From within the worktree
# Clear status
Claude Code hooks for per-worktree:
Copy the hooks from .claude-plugin/hooks/hooks.worktree.json to your ~/.claude/settings.json.
Priority: Worktree-specific config takes precedence over branch-keyed config when both exist.
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:
# Inside the repo (keeps everything contained)
= ".worktrees/{{ branch }}"
# Shared directory with multiple repos
= "../worktrees/{{ main_worktree }}/{{ branch }}"
Shell Integration
Worktrunk can automatically configure your 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.
Releases
Use cargo-release to publish new versions:
# Bump version, update Cargo.lock, commit, tag, and push
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.
FAQ
Does Worktrunk execute arbitrary commands on my machine?
Worktrunk executes commands in three contexts:
- Project hooks (
.config/wt.toml) - Automation for worktree lifecycle - LLM commands (
~/.config/worktrunk/config.toml) - Commit message generation - --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:
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.