Skip to main content

Worktrunk: Deep Dive and Advanced Workflows

1. Overview

Beyond the basic create-edit-merge workflow, Worktrunk is a sophisticated tool designed for complex development scenarios: parallel AI agent orchestration, CI/CD integration, advanced hook automation, team-based workflows, and fine-grained repository configuration.

This reference covers the complete Worktrunk ecosystem: the deep mechanics of each command, configuration patterns, hook design for automation, parallel agent coordination, LLM integration for commit messages and branch summaries, advanced merge strategies, and production-grade team patterns.

For a gentler introduction, see the [[git-worktrees-worktrunk-beginner-guide|Worktrunk Beginner's Guide]].


2. Prerequisites

  • Comfort with the Linux/macOS command line (shells, piping, environment variables)
  • Familiarity with git fundamentals (branches, commits, rebasing, merging) — covered in the beginner guide
  • A working Worktrunk installation with shell integration enabled
  • Understanding of your project's dependencies and CI/CD setup
  • Optionally, experience with template languages (Jinja2 syntax) and TOML configuration
  • For LLM features: access to an LLM CLI tool (Claude Code, llm, aichat, etc.)
  • For CI integration: familiarity with GitHub Actions, GitLab CI, or your CI provider

3. Key Concepts

3.1 The Worktrunk Architecture

Worktrunk operates as a layer above git, providing:

  1. Branch and path management — Automatically compute worktree paths from branch names using configurable templates.
  2. Lifecycle hooks — Run arbitrary commands at 11 distinct points: pre-switch, post-switch, pre-start, post-start, pre-commit, post-commit, pre-merge, post-merge, pre-remove, post-remove.
  3. State persistence — Track branch markers, per-branch variables, and user approvals across sessions.
  4. Rich output — Display worktree status with commit counts, file changes, CI results, and LLM-generated summaries.
  5. LLM integration — Pipe diffs through your choice of LLM for commit messages and branch analysis.

3.2 The Merge Pipeline

wt merge executes a well-defined 8-step pipeline:

  1. Commit — Stage and commit any uncommitted changes (skipped with --no-commit)
  2. Squash — Combine all commits since the target branch into one (skipped with --no-squash)
  3. Rebase — Rebase onto the target branch if behind (skipped with --no-rebase)
  4. Pre-merge hooks — Run validation tests, linters, builds (skipped with --no-hooks)
  5. Fast-forward merge — Update the target branch to your squashed commit (replaced with merge commit if --no-ff)
  6. Pre-remove hooks — Run cleanup commands before deleting the worktree
  7. Cleanup — Remove worktree directory and delete the branch (skipped with --no-remove)
  8. Post-remove + post-merge hooks — Run in background after cleanup

Backup refs are created at refs/wt-backup/<branch> before the merge, allowing recovery if needed.

3.3 Configuration Hierarchy

Worktrunk reads configuration in this order (later entries override earlier):

  1. Defaults — Hardcoded in Worktrunk
  2. User config~/.config/worktrunk/config.toml (personal, never committed)
  3. Project config.config/wt.toml (repository-specific, committed to git)
  4. Environment variablesWORKTRUNK_* prefixed (highest priority)

This allows per-repository customization while maintaining user-level defaults.

3.4 Hook Execution Model

Blocking hooks (pre-*):

  • Execute sequentially by default
  • If any hook fails, the operation aborts immediately
  • Used for validation (tests, lint checks)
  • Named commands run in parallel unless explicitly ordered

Background hooks (post-*):

  • Spawn in the background after the operation completes
  • Failures are logged but don't affect the operation
  • Used for cleanup, dev server startup, logging
  • Named commands run concurrently by default

3.5 Template Variables and Filters

All hook commands, path templates, and configuration values support Jinja2-style variable interpolation:

Variables:

{{ branch }} # Current branch name
{{ worktree_path }} # Full path to the worktree
{{ target }} # Target branch for merge
{{ repo }} # Repository directory name
{{ repo_path }} # Full path to repo root
{{ default_branch }} # Default branch (main or master)

Filters:

{{ branch | sanitize }} # feature/auth → feature-auth (shell-safe)
{{ branch | hash_port }} # Deterministic port 10000–19999
{{ branch | sanitize_db }} # Database-safe identifier

4. Step-by-Step Instructions

4.1 Advanced Worktree Path Configuration

By default, Worktrunk creates worktrees as sibling directories (e.g., ../repo.my-feature). Customize this in ~/.config/worktrunk/config.toml:

Default (sibling directories):

worktree-path = "{{ repo_path }}/../{{ repo }}.{{ branch | sanitize }}"

Inside the repo (.worktrees/ subdirectory):

worktree-path = "{{ repo_path }}/.worktrees/{{ branch | sanitize }}"

Centralized worktree folder:

worktree-path = "~/worktrees/{{ repo }}/{{ branch | sanitize }}"

Per-project override:

[projects."github.com/your-org/your-repo"]
worktree-path = ".worktrees/{{ branch | sanitize }}"

After changing the path template, new worktrees are created at the new location. Existing worktrees remain at their original paths.

4.2 Configuring Merge Defaults

Control the default behavior of wt merge in user config:

[merge]
squash = true # Default: squash commits
commit = true # Default: auto-commit uncommitted changes
rebase = true # Default: rebase onto target
remove = true # Default: remove worktree after merge
ff = true # Default: fast-forward merge

With these defaults:

wt merge # Behavior: squash, rebase, merge, remove
wt merge --no-ff # Override: create merge commit instead

4.3 Defining Hooks in Project Config

Create .config/wt.toml in your repository root (commit this file to share with your team):

Simple hook (single command):

pre-start = "npm ci"
post-merge = "npm run build"

Named hooks (multiple commands):

[pre-merge]
test = "npm test"
lint = "npm run lint"
build = "npm run build"

By default, named pre-hooks run sequentially (test, then lint, then build). Named post-hooks run concurrently.

Control ordering of post-hooks:

post-start = [
{ install = "npm ci" },
{ build = "npm run build", server = "npm run dev" }
]

This installs first, then runs build and server in parallel.

Use template variables:

[post-start]
server = "npm run dev -- --port {{ branch | hash_port }}"

[post-remove]
kill = "lsof -ti :{{ branch | hash_port }} -sTCP:LISTEN | xargs kill 2>/dev/null || true"

This assigns each worktree a unique port based on branch name, preventing conflicts.

4.4 Progressive Validation Strategy

Run fast checks on commit, thorough checks on merge:

# Fast checks on every commit
[pre-commit]
lint = "npm run lint"
typecheck = "npm run typecheck"

# Comprehensive checks only before merge
[pre-merge]
test = "npm test"
build = "npm run build"
e2e = "npm run test:e2e"

This accelerates development (quick feedback) while still gating merges with comprehensive validation.

4.5 Setting Up Per-Worktree Dev Servers

Give each worktree a unique development server:

[post-start]
server = "npm run dev -- --port {{ branch | hash_port }}"

[post-remove]
kill = "lsof -ti :{{ branch | hash_port }} -sTCP:LISTEN | xargs kill 2>/dev/null || true"

[list]
# Show each worktree's dev server URL in wt list output
url = "http://localhost:{{ branch | hash_port }}"

Now multiple dev servers run simultaneously without port conflicts. wt list shows each URL.

4.6 Per-Worktree Databases with Docker

Spin up isolated database containers per worktree:

post-start = [
"""wt config state vars set container='{{ repo }}-{{ branch | sanitize }}-postgres' port='{{ ('db-' ~ branch) | hash_port }}'""",
{ db = "docker run -d --rm --name {{ vars.container }} -p {{ vars.port }}:5432 -e POSTGRES_DB={{ branch | sanitize_db }} -e POSTGRES_PASSWORD=dev postgres:16" }
]

[post-remove]
db-stop = "docker stop {{ vars.container }} 2>/dev/null || true"

Each worktree gets its own Postgres container on a unique port, with a database name derived from the branch. On removal, the container stops.

4.7 Copying Build Caches (Warm Starts)

Avoid cold rebuilds by copying build caches from the main worktree:

[post-start]
copy = "wt step copy-ignored"

wt step copy-ignored copies all gitignored files (node_modules/, target/, .venv/, etc.) from the main worktree to the new one, allowing builds to start warm.

4.8 LLM Commit Message Generation

Automatically generate commit messages from diffs during merge:

For Claude Code:

[commit.generation]
command = "CLAUDECODE= MAX_THINKING_TOKENS=0 claude -p --no-session-persistence --model=haiku --tools='' --disable-slash-commands --setting-sources='' --system-prompt=''"

For the llm CLI tool:

[commit.generation]
command = "llm -m claude-haiku-4.5"

For aichat:

[commit.generation]
command = "aichat -m claude:claude-haiku-4.5"

When configured, wt merge and wt step commit pipe diffs to your LLM command to generate meaningful commit messages automatically.

4.9 LLM Summaries in wt list

Enable AI-generated branch summaries:

[list]
summary = true

Then run:

wt list --full

The output includes a "Summary" column with AI-generated descriptions of what each branch has changed, helping you understand progress at a glance.

4.10 Integration with CI/CD Systems

GitHub Actions pre-merge validation:

Add a .github/workflows/wt-merge-check.yml:

name: Worktree Merge Check
on:
workflow_dispatch:
inputs:
branch:
required: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.branch }}
- uses: actions/setup-node@v3
- run: npm ci && npm test && npm run build

Then in .config/wt.toml:

[pre-merge]
ci = "gh workflow run wt-merge-check.yml -f branch={{ branch }} --ref={{ branch }}"

This ensures pre-merge hooks can integrate with remote CI for comprehensive validation.


5. Practical Examples

Example 1 — Coordinating Parallel AI Agents

Launch 5 AI agents working on different features in parallel:

# Terminal 1: Feature A (Auth)
wt switch -c feature-auth -x claude -- 'Implement OAuth2 login flow'

# Terminal 2: Feature B (API)
wt switch -c feature-api -x claude -- 'Build REST API endpoints'

# Terminal 3: Feature C (UI)
wt switch -c feature-ui -x claude -- 'Redesign dashboard UI'

# Terminal 4: Feature D (Tests)
wt switch -c feature-tests -x claude -- 'Write comprehensive test suite'

# Terminal 5: Feature E (Docs)
wt switch -c feature-docs -x claude -- 'Update API documentation'

Each agent gets an isolated worktree with its own branch. Monitor progress:

wt list --full

Shows all agents' status: commits, uncommitted changes, CI results, and LLM-generated summaries of what each did.

Merge when ready:

wt switch feature-auth
# Review the agent's work...
wt merge

wt switch feature-api
# Review...
wt merge

# ... repeat for each agent

Example 2 — Multi-Step Feature Development with Manual Control

Use wt step commands when you want granular control:

# Create and work
wt switch -c feature-complex

# Make changes incrementally
vim src/part1.rs
git add -A
wt step commit

# More changes
vim src/part2.rs
git add -A
wt step commit

# Review before squashing
wt step diff

# Squash everything into one commit
wt step squash main

# Review the squashed state
wt step diff

# Rebase to ensure clean merge
wt step rebase main

# Run tests manually
npm test

# If tests pass, push to main
wt step push main

# Cleanup
wt remove

This gives you checkpoint commits during development, but a clean single-commit history on main.

Example 3 — Advanced Merge Strategy with Pre-Merge Hooks

# .config/wt.toml
[pre-merge]
# Run tests
test = "npm test"
# Run lint with auto-fix
lint = "npm run lint -- --fix"
# Build
build = "npm run build"
# Security audit
audit = "npm audit"

[post-merge]
# Update dependencies post-merge
update = "npm outdated"
# Push to origin
push = "git push origin main"

Now wt merge is a comprehensive pipeline: tests, linting, building, auditing, and finally pushing to remote — all gated by the merge command.

Example 4 — Merging with Preserved Commit History

For teams that prefer clear commit history over squashed commits:

# .config/wt.toml
[merge]
squash = false # Preserve individual commits
ff = false # Create merge commits (semi-linear history)

Now wt merge preserves each commit and creates a merge commit, giving you a clear record of when features landed.

Alternatively, use a one-off flag:

wt merge --no-squash --no-ff main

Example 5 — Team-Based Workflow with Approval Gating

# Project lead reviews and approves hooks once
wt hook approvals add

# Team members can then run the hooks without re-approval
# (hooks are checked against the approved SHA)

# Remove approvals if hooks change
wt hook approvals clear

This allows shared .config/wt.toml hooks to be run safely across a team without re-prompting every developer.

Example 6 — Branch Markers for Agent Tracking

Mark branches by agent or status:

# Mark as AI-managed
wt config state marker set 🤖

# Mark as ready for review
wt config state marker set

# Mark as blocked
wt config state marker set 🚧

Markers show in wt list, giving you a visual status board at a glance.


6. Hands-On Exercises

Exercise 1 — Custom Worktree Path Templates

  1. Edit ~/.config/worktrunk/config.toml and set:
    worktree-path = ".worktrees/{{ branch | sanitize }}"
  2. Create a new worktree: wt switch -c exercise-path-test
  3. Verify the path is inside .worktrees/: pwd
  4. Restore the default path template and verify new worktrees go back to sibling directories

Exercise 2 — Multi-Command Hooks

  1. Create .config/wt.toml in your repo:
    [pre-start]
    npm = "npm ci"

    [post-start]
    lint = "npm run lint"
    test = "npm test"
  2. Create a worktree: wt switch -c exercise-hooks
  3. Confirm hooks ran (check output and verify dependencies are installed)
  4. Remove the hook config and verify no hooks run on the next worktree

Exercise 3 — Per-Worktree Dev Servers

  1. Add to .config/wt.toml:
    [post-start]
    server = "echo 'Starting server on port {{ branch | hash_port }}' && sleep 10"

    [post-remove]
    cleanup = "echo 'Cleaning up port {{ branch | hash_port }}'"
  2. Create two worktrees and observe the unique port assignments in logs
  3. Verify different branches get different ports (deterministic based on branch name)

Exercise 4 — Progressive Validation

  1. Create .config/wt.toml with fast pre-commit checks and comprehensive pre-merge checks
  2. Create a worktree with some failing tests in pre-commit
  3. Notice the commit aborts due to test failure
  4. Fix the tests and retry
  5. Now merge with wt merge and observe all pre-merge hooks run

Exercise 5 — Manual Step Commands

  1. Create a worktree: wt switch -c exercise-steps
  2. Make and commit a change: echo "test" > f1.txt && git add -A && wt step commit
  3. Make another: echo "test2" > f2.txt && git add -A && wt step commit
  4. Review the diff: wt step diff
  5. Squash: wt step squash main
  6. Rebase: wt step rebase main
  7. Push: wt step push main
  8. Cleanup: wt remove
  9. Verify on main the change is squashed: git log --oneline -n 2

Exercise 6 — LLM Commit Message Generation

  1. Configure LLM in ~/.config/worktrunk/config.toml with a valid command
  2. Create a worktree and make changes
  3. Run wt merge and observe the LLM-generated commit message in the merge output
  4. Verify the message on main: git log -1

7. Troubleshooting

Hook fails and blocks merge

Cause: A pre-merge hook (test, lint, build) failed.

Diagnosis:

wt merge -v # Verbose mode shows which hook failed
wt hook pre-merge --yes # Run the hook directly to see the error

Fix: Resolve the underlying issue and retry:

# Fix the code
vim src/broken.rs
npm test
wt merge

Or skip the hook if it's non-critical:

wt merge --no-hooks

Hook approval required repeatedly

Cause: .config/wt.toml has changed; Worktrunk requires re-approval.

Fix: Re-approve the hooks:

wt hook approvals add

Or clear all approvals if they become too restrictive:

wt hook approvals clear

Port conflict on dev server hook

Cause: Multiple worktrees trying to use the same port.

Fix: Use the hash_port filter to assign unique ports:

[post-start]
server = "npm run dev -- --port {{ branch | hash_port }}"

Merge conflicts after rebase

Cause: Your branch conflicts with changes in the target.

Fix:

  1. Resolve conflicts manually:
# See conflicts
git status

# Edit conflicted files
vim src/conflicted.rs

# Mark as resolved
git add -A
git commit
  1. Re-run merge:
wt merge

If the merge still fails, complete it manually:

wt step rebase main
# Resolve any remaining conflicts
wt step push main
wt remove

Worktree gets stuck in "merging" state

Cause: wt merge was interrupted (killed, network failure, etc.).

Fix:

# Check status
wt list

# If merge is incomplete, manually abort and clean up
git merge --abort
wt remove --no-delete-branch # Keep branch, remove worktree

LLM command fails silently

Cause: The LLM command path is wrong or the tool isn't installed.

Diagnosis:

wt merge -vv # Debug mode shows command execution
which claude # Verify the tool is in PATH

Fix: Update the command in ~/.config/worktrunk/config.toml:

[commit.generation]
command = "/usr/local/bin/claude -p ..." # Use absolute path

Environment variables not expanding in hooks

Cause: Template variables are Jinja2, not shell variables.

Fix: Use Worktrunk template syntax:

# Wrong:
command = "echo $BRANCH"

# Correct:
command = "echo {{ branch }}"

Backup refs pile up

Cause: Many merges create many backup refs at refs/wt-backup/*.

Fix: Prune unreferenced backups periodically:

git gc --aggressive
git reflog expire --all --expire=now
git reflog prune

8. References


9. Summary

Key takeaways:

  • Advanced configuration enables per-project and per-user customization of paths, merge strategies, and hook behavior through .config/wt.toml and ~/.config/worktrunk/config.toml.
  • Hooks are the backbone of Worktrunk automation. Use pre-hooks for validation, post-hooks for cleanup and setup. Template variables and filters allow dynamic commands based on branch and environment.
  • The merge pipeline is a well-defined 8-step process: commit → squash → rebase → pre-merge hooks → merge → cleanup → post-remove hooks. Each step can be skipped with command-line flags.
  • Parallel agents can work independently on different features, each in its own worktree with its own dev server, database, and build cache. Coordinate with wt list --full and LLM-generated summaries.
  • LLM integration generates commit messages automatically from diffs and enables branch summaries in wt list, accelerating code review and merging.
  • Progressive validation runs fast checks on commit, thorough checks on merge, balancing developer experience with code quality.
  • Team patterns like approval gating, branch markers, and shared configuration files enable Worktrunk to scale across teams.

Advanced workflows to explore:

  • Running parallel AI agents on different features, coordinating merges through status reviews
  • Integrating with remote CI/CD systems to gate merges on distributed test results
  • Per-worktree development environments (servers, databases, caches) for zero-interference parallel development
  • Advanced merge strategies (merge commits, preserved history, squash-and-merge) tailored to team preferences
  • Custom hooks for deployment, documentation generation, dependency updates, and post-merge automation

Next steps:

  • Configure hooks for your project's specific validation needs
  • Experiment with LLM commit message generation for faster merging
  • Set up per-worktree dev servers and databases for isolation
  • Integrate with your CI/CD system to gate merges on remote test results
  • Launch multiple AI agents on different features and coordinate their merges

  • [[git-worktrees-worktrunk-beginner-guide|Git Worktrees with Worktrunk (Beginner)]]
  • [[claude-code-vscode-go-beginner-guide|Claude Code]]
  • [[git-advanced-workflows|Advanced Git Workflows]]
  • [[ci-cd-automation|CI/CD Automation]]

Related Tutorials

  • [[sesh-beginner-guide|Sesh Beginner Guide]] — Combine sesh tmux sessions with Worktrunk worktrees

  • [[sesh-deep-dive|Sesh Deep Dive]] — Advanced sesh patterns for worktree-based workflows

  • [[dotfiles-beginner-guide|Dotfiles Beginner Guide]] — Version control your Worktrunk and terminal configurations

  • [[dotfiles-deep-dive|Dotfiles Deep Dive]] — Cross-platform dotfile management with encryption and templates

  • [[gh-cli-beginner-guide|GitHub CLI Beginner Guide]] — manage PRs from your terminal alongside worktrunk

  • [[gh-cli-deep-dive|GitHub CLI Deep Dive]] — advanced GitHub API scripting with worktrunk workflows