v0.81.0
vectorize-io/hindsightv0.81.0Oct 20, 2026by github-actions[bot]
Upgrading?
Full Release Notes
## Release Notes
### Improved
- **Hooks can prompt**: hooks that run serially in the foreground (`pre-*` hooks, `wt hook <type> --foreground`) inherit wt's stdin, so `gum confirm 'trust this worktree?' && mise trust` works as a hook. Concurrent and background hooks read EOF. (Breaking: hooks no longer receive their context as JSON on stdin; pass values as template arguments, or whole objects with the new `tojson` filter. `wt step for-each` keeps its JSON.) [Docs](https://worktrunk.dev/hook/#interactive-hooks) ([#3129](https://github.com/max-sixty/worktrunk/pull/3129), [#4451](https://github.com/max-sixty/worktrunk/pull/4451), [#4524](https://github.com/max-sixty/worktrunk/pull/4524), thanks @endigma for [#3093](https://github.com/max-sixty/worktrunk/issues/3093))
- **Experimental `wt switch --path`**: places one worktree outside the `worktree-path` template and keeps its branch name, as in `wt switch --create feature/JIRA-1234 --path ../dark-mode`. Commands reach it by branch or by path. [Docs](https://worktrunk.dev/switch/#custom-path) ([#4317](https://github.com/max-sixty/worktrunk/pull/4317), thanks @christowiz for [#1982](https://github.com/max-sixty/worktrunk/issues/1982))
- **`wt merge --branch`**: merges another worktree's branch without leaving the current worktree. Commit, squash, rebase, and hooks run in the source worktree, and cleanup removes it. [Docs](https://worktrunk.dev/merge/) ([#4360](https://github.com/max-sixty/worktrunk/pull/4360), thanks @liuyinchu)
- **OpenCode 2 worktrees go through Worktrunk**: after re-running `wt config plugins opencode install`, worktrees OpenCode creates and removes use `wt switch --create` and `wt remove`, as the Claude Code plugin's do. OpenCode's `worktree.directory` stops applying, and new branches start from the default branch. Like the Claude Code plugin, it opens an existing branch's worktree, which OpenCode's remove then removes. [Docs](https://worktrunk.dev/claude-code/#worktree-isolation-claude-code-and-opencode) ([#4489](https://github.com/max-sixty/worktrunk/pull/4489), thanks @rzane for [#4488](https://github.com/max-sixty/worktrunk/issues/4488))
- **Faster bulk removal**: removal hands files to one background deleter per repository, not one `rm -rf` each, and deletes read-only directories it used to leave in `.git/wt/trash`. `wt step prune` no longer stalls when two worktrees share a directory name, as Codex worktrees do. (Breaking: prune runs pre-remove hooks concurrently; hooks writing shared state must coordinate.) ([#4516](https://github.com/max-sixty/worktrunk/pull/4516), [#4361](https://github.com/max-sixty/worktrunk/pull/4361), [#4337](https://github.com/max-sixty/worktrunk/pull/4337))
- **`wt switch -` returns to the worktree it left**, including a detached worktree or the second checkout of a branch, and `wt remove -` targets the same worktree. Pre-switch hooks see the resolved destination, so `wt switch origin/feature` passes `branch=feature`. (Breaking: from a bare repository's directory, commands given no argument act on the worktree of the branch HEAD names.) ([#4497](https://github.com/max-sixty/worktrunk/pull/4497), [#4496](https://github.com/max-sixty/worktrunk/pull/4496), [#4521](https://github.com/max-sixty/worktrunk/pull/4521))
- **Template errors name the variable**: `{{ brnch }}` reports `undefined variable brnch` and suggests `branch`. (Breaking: filters such as `sanitize` reject an undefined input, so `{{ upstream | sanitize }}` needs a guard or `default` on a branch with no upstream.) ([#4350](https://github.com/max-sixty/worktrunk/pull/4350), [#4440](https://github.com/max-sixty/worktrunk/pull/4440))
- **Templates use MiniJinja 3** (Breaking): objects and lists render Python-style (`{'port': 3000}`) instead of as JSON, so use `| tojson` where JSON is needed; `round` rounds half to even; `title` capitalizes fewer word boundaries (`feat/x-y` becomes `Feat/x-Y`); division by zero errors. ([#4442](https://github.com/max-sixty/worktrunk/pull/4442), [#4524](https://github.com/max-sixty/worktrunk/pull/4524))
- **CI status shows ⚠ for any failed forge call**, where some failures, such as a Gitea 404, used to leave the cell blank. A forge CLI with no login for the host still leaves it blank, and `wt config show --full` names the host it checked. ([#4436](https://github.com/max-sixty/worktrunk/pull/4436), [#4449](https://github.com/max-sixty/worktrunk/pull/4449))
- **Removal explains itself**: `--force` lists the changes it discards, and a refusal over untracked files suggests `git stash -u`. A multi-target `wt remove` continues past a target whose pre-remove hook or removal fails, and exits 1. A branch-only removal names the detached worktree left at the branch's path. ([#4347](https://github.com/max-sixty/worktrunk/pull/4347), [#4357](https://github.com/max-sixty/worktrunk/pull/4357), [#3791](https://github.com/max-sixty/worktrunk/pull/3791), [#4313](https://github.com/max-sixty/worktrunk/pull/4313), thanks @chachi for [#3769](https://github.com/max-sixty/worktrunk/issues/3769))
- **The picker can open without its preview**: `[switch.picker] preview = false` hides it until `Alt-p`. ([#4428](https://github.com/max-sixty/worktrunk/pull/4428), thanks @emacampolo for [#4072](https://github.com/max-sixty/worktrunk/issues/4072))
- **Squash backups print a recovery command**: squashing staged changes prints the `refs/wt-backup/<branch>` ref and a `wt switch --create --base <sha>` command that restores them in a new worktree, even after `wt merge` removed the original. ([#4347](https://github.com/max-sixty/worktrunk/pull/4347))
- **Shell integration**: `wt merge` and `wt remove` suggest restarting the shell when integration is installed but inactive. On Unix, the PowerShell profile comes from the installed `pwsh`, so a custom `XDG_CONFIG_HOME` works. (Breaking: installing for Nushell requires `nu`, and for PowerShell on Unix, `pwsh`.) ([#4343](https://github.com/max-sixty/worktrunk/pull/4343), [#4422](https://github.com/max-sixty/worktrunk/pull/4422), [#4436](https://github.com/max-sixty/worktrunk/pull/4436))
- **System config has one path per platform**: `/Library/Application Support/worktrunk/config.toml` on macOS, `%PROGRAMDATA%\worktrunk\config.toml` on Windows, `/etc/xdg/worktrunk/config.toml` elsewhere, or `WORKTRUNK_SYSTEM_CONFIG_PATH`. (Breaking: `$XDG_CONFIG_DIRS` is ignored and macOS no longer falls back to `/etc/xdg`; a file there stops loading without a warning.) ([#4169](https://github.com/max-sixty/worktrunk/pull/4169))
- **`expected_worktree_path_of_branch()` template function**: returns where `wt switch --create` would put a branch's worktree, even after the worktree at that path has moved to another branch. ([#4502](https://github.com/max-sixty/worktrunk/pull/4502), thanks @BenJurewicz for [#4480](https://github.com/max-sixty/worktrunk/pull/4480))
- **Smaller improvements**: `wt switch --create` reports an invalid branch name in one line instead of Git's error; hook failures name the hook's source (`pre-merge user:lint failed (exit code 7)`); the `wt list` loading footer says how many rows fit; `wt step for-each` labels detached worktrees by path; `install-statusline` keeps `settings.json`'s key order; the Codex plugin shows the Worktrunk icon. ([#4349](https://github.com/max-sixty/worktrunk/pull/4349), [#4352](https://github.com/max-sixty/worktrunk/pull/4352), [#4333](https://github.com/max-sixty/worktrunk/pull/4333), [#4498](https://github.com/max-sixty/worktrunk/pull/4498), [#4420](https://github.com/max-sixty/worktrunk/pull/4420), [#4327](https://github.com/max-sixty/worktrunk/pull/4327))
### Fixed
- **Foreground hooks that change terminal settings**: a hook that touched the terminal, such as turbo on a cache miss, was stopped by `SIGTTOU` and wt hung. Hooks now run in the shell's job group. (Breaking: Nushell integration requires Nushell 0.113+; on Ctrl-C wt dies by the signal instead of exiting 130, so a calling script stops too.) ([#4400](https://github.com/max-sixty/worktrunk/pull/4400), [#3129](https://github.com/max-sixty/worktrunk/pull/3129), thanks @user753 for [#4389](https://github.com/max-sixty/worktrunk/issues/4389) and @willparsons for the turbo report)
- **Formatter pre-commit hooks during a squash**: in `wt merge` and `wt step squash`, a hook's edits missed the squash commit, and the dirty worktree blocked removal. All commit paths now stage, run the hooks, and stage again. (Breaking: `wt step commit` and `wt merge --no-squash` stage first too, so index-reading hooks like `pre-commit run` can block them.) ([#4321](https://github.com/max-sixty/worktrunk/pull/4321), [#4520](https://github.com/max-sixty/worktrunk/pull/4520))
- **`wt step copy-ignored` on Linux**: copied files took the copy's time, so cargo rebuilt much of a copied `target/`; they now keep the source's mtime, as they already did on macOS and Windows. A reflink rejected with `EPERM` now falls back to a copy. ([#4319](https://github.com/max-sixty/worktrunk/pull/4319), [#4518](https://github.com/max-sixty/worktrunk/pull/4518), [#4434](https://github.com/max-sixty/worktrunk/pull/4434), thanks @dmy-gh for [#4248](https://github.com/max-sixty/worktrunk/issues/4248), @paul-hansen for the repro, and @HN05 for [#4433](https://github.com/max-sixty/worktrunk/issues/4433))
- **Directory patterns in `.worktreeinclude` and `[step.copy-ignored] exclude`**: a pattern such as `config/` skipped ignored files that sit beside tracked files in `config/`, so `wt step copy-ignored` left out included files and copied excluded ones. It now covers them, as `.gitignore` does. ([#4406](https://github.com/max-sixty/worktrunk/pull/4406))
- **`wt list` status symbols**: JSON `symbols` strings use the table's order (`⊘_`, was `_⊘`), and the flag for an off-template path or duplicate checkout is a dim `⚐` instead of `⚑`. (Breaking: consumers matching these strings need updating.) ([#4335](https://github.com/max-sixty/worktrunk/pull/4335), [#4334](https://github.com/max-sixty/worktrunk/pull/4334))
- **`wt switch`**: `--create` branches from a remote-tracking default branch such as git-p4's `p4/master` instead of `HEAD`; switching by path, as the picker does, gives pre-switch hooks the destination's variables; the bare-repo warning names the configured path. ([#4482](https://github.com/max-sixty/worktrunk/pull/4482), [#4493](https://github.com/max-sixty/worktrunk/pull/4493), [#4491](https://github.com/max-sixty/worktrunk/pull/4491), thanks @cazador481 for [#4481](https://github.com/max-sixty/worktrunk/issues/4481) and @dlecan for [#4492](https://github.com/max-sixty/worktrunk/issues/4492))
- **Agent plugins**: `claude --worktree NAME` opens the worktree of an existing branch `NAME` rather than failing, so a kept session can be reopened. A clean exit then runs `wt remove` on it, which deletes its ignored files and, if integrated, the branch. The Codex plugin's Windows hooks parse under PowerShell. ([#4487](https://github.com/max-sixty/worktrunk/pull/4487), [#4241](https://github.com/max-sixty/worktrunk/pull/4241), thanks @edziegle for [#4486](https://github.com/max-sixty/worktrunk/issues/4486) and @wstczyw for [#4239](https://github.com/max-sixty/worktrunk/issues/4239))
- **`wt switch` picker**: a per-project `pager` applies to the diff preview; a selected branch-only row no longer draws light text on the light selection bar; an orphan branch's log tab shows its commits. ([#4398](https://github.com/max-sixty/worktrunk/pull/4398), [#4408](https://github.com/max-sixty/worktrunk/pull/4408), [#4470](https://github.com/max-sixty/worktrunk/pull/4470), thanks @willparsons for reporting the contrast bug)
- **`wt step relocate`**: a swap moves the shell with its worktree; relocating the main worktree leaves a shell in a nested worktree where it is; branches whose sanitized names collide no longer relocate partway. ([#4325](https://github.com/max-sixty/worktrunk/pull/4325), [#4256](https://github.com/max-sixty/worktrunk/pull/4256), thanks @Duang777)
- **Aliases**: `--args=X` and `--vars=X` reach `{{ args }}` instead of being dropped, and completion after an alias no longer offers `--dry-run` or `--var`. ([#4391](https://github.com/max-sixty/worktrunk/pull/4391), [#4329](https://github.com/max-sixty/worktrunk/pull/4329))
- **Config errors**: a malformed `approvals.toml` no longer aborts hooks, aliases, or commits run with `--yes`; one invalid `WORKTRUNK_*` override no longer discards the valid ones. ([#4467](https://github.com/max-sixty/worktrunk/pull/4467), [#4348](https://github.com/max-sixty/worktrunk/pull/4348))
- **Windows**: an Azure CLI installed through WinGet (`az.cmd`) is found, and Git Bash is found through the first `git` on `PATH`, including custom installs where it resolves under `Git\mingw64\bin`. (Breaking: without `git` on `PATH`, Git Bash is no longer found at its default install locations.) ([#4402](https://github.com/max-sixty/worktrunk/pull/4402), [#4411](https://github.com/max-sixty/worktrunk/pull/4411), [#4452](https://github.com/max-sixty/worktrunk/pull/4452), thanks @wmmc88 for [#4401](https://github.com/max-sixty/worktrunk/issues/4401) and @LeoWSY-hashblue for [#4410](https://github.com/max-sixty/worktrunk/issues/4410))
- **Forge fixes**: Azure DevOps CI no longer shows a same-named branch's PR from another repository in the project, and `pr:N` suggests a `dev.azure.com` URL for SSH remotes; Gitea fork pushes keep the server's clone URL; `tea` logins in its default macOS and Windows locations are found; `gh` and `glab` protocol settings apply per host. ([#4326](https://github.com/max-sixty/worktrunk/pull/4326), [#4364](https://github.com/max-sixty/worktrunk/pull/4364), [#4495](https://github.com/max-sixty/worktrunk/pull/4495), [#4420](https://github.com/max-sixty/worktrunk/pull/4420))
- **Fixes for unusual setups**: valueless git config keys such as `core.fsmonitor` read as true; untracked files named like `:x` no longer break `wt list`'s diff; worktree paths containing newlines or non-UTF-8 bytes; the statusline's branch label with custom `worktree-path` templates; zsh `compinit` advice names the right file; IPv6 `ssh://` remotes get distinct approvals and `[projects]` settings (Breaking: re-save them). ([#4314](https://github.com/max-sixty/worktrunk/pull/4314), [#4320](https://github.com/max-sixty/worktrunk/pull/4320), [#4453](https://github.com/max-sixty/worktrunk/pull/4453), [#4495](https://github.com/max-sixty/worktrunk/pull/4495), [#4421](https://github.com/max-sixty/worktrunk/pull/4421), [#4526](https://github.com/max-sixty/worktrunk/pull/4526))
### Documentation
- **`wt config shell --help` is the shell setup reference**, covering init, install, uninstall, and diagnostics. ([#4344](https://github.com/max-sixty/worktrunk/pull/4344))
- **Doc corrections**: the `wt config state vars clear` example, `wt list` JSON `null` causes, every supported forge in CI help, `--path` worktrees in `wt list`, the bare-repo navigation example, and Alt as Option on macOS. ([#4363](https://github.com/max-sixty/worktrunk/pull/4363), [#4359](https://github.com/max-sixty/worktrunk/pull/4359), [#4439](https://github.com/max-sixty/worktrunk/pull/4439), [#4328](https://github.com/max-sixty/worktrunk/pull/4328), [#4483](https://github.com/max-sixty/worktrunk/pull/4483), [#4412](https://github.com/max-sixty/worktrunk/pull/4412), thanks @wmcnamee-coreweave for [#4474](https://github.com/max-sixty/worktrunk/issues/4474))
- **Recipes**: the bare repository layout sets `origin`'s fetch refspec; the aichat example passes `--code` to drop a reasoning model's `<think>` block; the OpenCode command uses Claude Haiku 5.5; `wt-tui` joins the custom subcommand examples. ([#4323](https://github.com/max-sixty/worktrunk/pull/4323), [#4332](https://github.com/max-sixty/worktrunk/pull/4332), [#4432](https://github.com/max-sixty/worktrunk/pull/4432), [#4386](https://github.com/max-sixty/worktrunk/pull/4386), thanks @gitNetw0rk for [#4322](https://github.com/max-sixty/worktrunk/issues/4322), @FenjuFu, and @marcusjihansson)
- **The `wt-switch-create` skill reads the new repository's root `CLAUDE.md` and `AGENTS.md`** when it moves a session into another repository's worktree. ([#4414](https://github.com/max-sixty/worktrunk/pull/4414))
### Internal
- **Library API changes** (Breaking library API): `Repository::project_identifier` returns a `ProjectId`, which config accessors take in place of `&str`; `Repository::require_worktree` also returns the branch; `Repository::run_command` takes `AsRef<OsStr>` arguments; `UserConfig::expected_worktree_path` is new. ([#4526](https://github.com/max-sixty/worktrunk/pull/4526), [#4496](https://github.com/max-sixty/worktrunk/pull/4496), [#4453](https://github.com/max-sixty/worktrunk/pull/4453), [#4502](https://github.com/max-sixty/worktrunk/pull/4502))
- **MSRV is Rust 1.98.** ([#4367](https://github.com/max-sixty/worktrunk/pull/4367))
- **A hidden `wt config plugins claude hook` subcommand** handles every hook event the Claude Code plugin uses, toward listing the plugin in Anthropic's plugin directory. ([#4304](https://github.com/max-sixty/worktrunk/pull/4304))
- **Release signing and crates.io OIDC run in the tag-only `release` environment.** ([#4415](https://github.com/max-sixty/worktrunk/pull/4415))
## Install worktrunk 0.81.0
### Install prebuilt binaries via shell script
```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-installer.sh | sh && wt config shell install
```
### Install prebuilt binaries via powershell script
```sh
powershell -ExecutionPolicy Bypass -c "irm https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-installer.ps1 | iex"; git-wt config shell install
```
### Install prebuilt binaries via Homebrew
```sh
brew install worktrunk && wt config shell install
```
## Download worktrunk 0.81.0
| File | Platform | Checksum |
|--------|----------|----------|
| [worktrunk-aarch64-apple-darwin.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-aarch64-apple-darwin.tar.xz) | Apple Silicon macOS | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-aarch64-apple-darwin.tar.xz.sha256) |
| [worktrunk-x86_64-apple-darwin.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-x86_64-apple-darwin.tar.xz) | Intel macOS | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-x86_64-apple-darwin.tar.xz.sha256) |
| [worktrunk-x86_64-pc-windows-msvc.zip](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-x86_64-pc-windows-msvc.zip) | x64 Windows | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-x86_64-pc-windows-msvc.zip.sha256) |
| [worktrunk-aarch64-unknown-linux-musl.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-aarch64-unknown-linux-musl.tar.xz) | ARM64 MUSL Linux | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-aarch64-unknown-linux-musl.tar.xz.sha256) |
| [worktrunk-x86_64-unknown-linux-musl.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-x86_64-unknown-linux-musl.tar.xz) | x64 MUSL Linux | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-x86_64-unknown-linux-musl.tar.xz.sha256) |
### Install via Cargo
```sh
cargo install worktrunk && wt config shell install
```
### Install via Winget (Windows)
```sh
winget install max-sixty.worktrunk && git-wt config shell install
```
### Install via AUR (Arch Linux)
```sh
paru worktrunk-bin && wt config shell install
```
Related Repositories
public-apis/public-apis★ 491,124
A collective list of free APIs
NousResearch/hermes-agent★ 255,893
The agent that grows with you
yt-dlp/yt-dlp★ 199,988
A feature-rich command-line audio/video downloader
microsoft/markitdown★ 191,477
Python tool for converting files and office documents to Markdown.
Significant-Gravitas/AutoGPT★ 187,699
AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.