worktrunk 0.1.5

A Git worktree manager for trunk-based development
Documentation

Worktrunk

Crates.io License: 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:

$ wt switch --create fix-auth
βœ… Created new worktree for fix-auth from main at ../repo.fix-auth/

Work, make changes, then merge back:

$ 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:

$ 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

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, to generate commit messages based on the diff and a configurable prompt.

Add to ~/.config/worktrunk/config.toml:

[commit-generation]
command = "llm"
args = ["-m", "claude-haiku-4-5-20251001"]

Then wt merge will generate commit messages automatically:

$ 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.

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
[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:

$ 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:

$ 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/
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:

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.

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
[post-create-command]
"setup" = "uv sync && nvm install"

Delegate to task runners - Reference existing Justfile/Makefile commands instead of duplicating logic:

[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.

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.

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

wt config list    # Show all config files and locations
wt config init    # Create global config with examples
wt config --help  # Show LLM setup guide

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):

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 }}.

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)
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.

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:

$ 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

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):

git config extensions.worktreeConfig true

Set status from within a worktree:

# 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 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)
worktree-path = ".worktrees/{{ branch }}"

# Shared directory with multiple repos
worktree-path = "../worktrees/{{ main_worktree }}/{{ branch }}"

Shell Integration

Worktrunk can automatically configure your shell:

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.

Releases

Use cargo-release to publish new versions:

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.

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:

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.