worktrunk 0.1.5

A Git worktree manager for trunk-based development
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
# Worktrunk

<!-- User badges -->

[![Crates.io](https://img.shields.io/crates/v/worktrunk?style=for-the-badge&logo=rust)](https://crates.io/crates/worktrunk)
[![License: MIT](https://img.shields.io/badge/LICENSE-MIT-blue?style=for-the-badge)](https://opensource.org/licenses/MIT)

<!-- Dev badges (uncomment when repo is public and has traction) -->
<!-- [![GitHub CI Status](https://img.shields.io/github/actions/workflow/status/max-sixty/worktrunk/ci.yml?event=push&branch=main&logo=github&style=for-the-badge)](https://github.com/max-sixty/worktrunk/actions?query=branch%3Amain+workflow%3Aci) -->
<!-- [![Downloads](https://img.shields.io/crates/d/worktrunk?style=for-the-badge&logo=rust)](https://crates.io/crates/worktrunk) -->
<!-- [![Stars](https://img.shields.io/github/stars/max-sixty/worktrunk?style=for-the-badge&logo=github)](https://github.com/max-sixty/worktrunk/stargazers) -->

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

<!-- Output from: tests/snapshots/integration__integration_tests__merge__readme_example_simple_switch.snap -->

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

**Work, make changes, then merge back:**

<!-- Output from: tests/snapshots/integration__integration_tests__merge__readme_example_simple.snap -->

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

<!-- Output from: tests/snapshots/integration__integration_tests__merge__readme_example_complex.snap -->

```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:**

<!-- Output from: tests/snapshots/integration__integration_tests__merge__readme_example_hooks_post_create.snap -->

```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:**

<!-- Output from: tests/snapshots/integration__integration_tests__merge__readme_example_hooks_pre_merge.snap -->

```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>

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

</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:** `= β‰‘βˆ… β†»β‹ˆ β—‡βŠ βš  ↑↓ ⇑⇣ ?!+»✘`

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

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

<!-- Output from: tests/snapshots/integration__integration_tests__list__with_user_status.snap -->

```
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:**

<!-- Output from: tests/snapshots/integration__integration_tests__list__with_user_status.snap -->

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