Release Notes
Improved
-
Hooks can prompt: hooks that run serially in the foreground (
pre-*hooks,wt hook <type> --foreground) inherit wt's stdin, sogum confirm 'trust this worktree?' && mise trustworks 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 newtojsonfilter.wt step for-eachkeeps its JSON.) Docs (#3129, #4451, #4524, thanks @endigma for #3093) -
Experimental
wt switch --path: places one worktree outside theworktree-pathtemplate and keeps its branch name, as inwt switch --create feature/JIRA-1234 --path ../dark-mode. Commands reach it by branch or by path. Docs (#4317, thanks @christowiz for #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 (#4360, thanks @liuyinchu) -
OpenCode 2 worktrees go through Worktrunk: after re-running
wt config plugins opencode install, worktrees OpenCode creates and removes usewt switch --createandwt remove, as the Claude Code plugin's do. OpenCode'sworktree.directorystops 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 (#4489, thanks @rzane for #4488) -
Faster bulk removal: removal hands files to one background deleter per repository, not one
rm -rfeach, and deletes read-only directories it used to leave in.git/wt/trash.wt step pruneno 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, #4361, #4337) -
wt switch -returns to the worktree it left, including a detached worktree or the second checkout of a branch, andwt remove -targets the same worktree. Pre-switch hooks see the resolved destination, sowt switch origin/featurepassesbranch=feature. (Breaking: from a bare repository's directory, commands given no argument act on the worktree of the branch HEAD names.) (#4497, #4496, #4521) -
Template errors name the variable:
{{ brnch }}reportsundefined variable brnchand suggestsbranch. (Breaking: filters such assanitizereject an undefined input, so{{ upstream | sanitize }}needs a guard ordefaulton a branch with no upstream.) (#4350, #4440) -
Templates use MiniJinja 3 (Breaking): objects and lists render Python-style (
{'port': 3000}) instead of as JSON, so use| tojsonwhere JSON is needed;roundrounds half to even;titlecapitalizes fewer word boundaries (feat/x-ybecomesFeat/x-Y); division by zero errors. (#4442, #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 --fullnames the host it checked. (#4436, #4449) -
Removal explains itself:
--forcelists the changes it discards, and a refusal over untracked files suggestsgit stash -u. A multi-targetwt removecontinues 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, #4357, #3791, #4313, thanks @chachi for #3769) -
The picker can open without its preview:
[switch.picker] preview = falsehides it untilAlt-p. (#4428, thanks @emacampolo for #4072) -
Squash backups print a recovery command: squashing staged changes prints the
refs/wt-backup/<branch>ref and awt switch --create --base <sha>command that restores them in a new worktree, even afterwt mergeremoved the original. (#4347) -
Shell integration:
wt mergeandwt removesuggest restarting the shell when integration is installed but inactive. On Unix, the PowerShell profile comes from the installedpwsh, so a customXDG_CONFIG_HOMEworks. (Breaking: installing for Nushell requiresnu, and for PowerShell on Unix,pwsh.) (#4343, #4422, #4436) -
System config has one path per platform:
/Library/Application Support/worktrunk/config.tomlon macOS,%PROGRAMDATA%\worktrunk\config.tomlon Windows,/etc/xdg/worktrunk/config.tomlelsewhere, orWORKTRUNK_SYSTEM_CONFIG_PATH. (Breaking:$XDG_CONFIG_DIRSis ignored and macOS no longer falls back to/etc/xdg; a file there stops loading without a warning.) (#4169) -
expected_worktree_path_of_branch()template function: returns wherewt switch --createwould put a branch's worktree, even after the worktree at that path has moved to another branch. (#4502, thanks @BenJurewicz for #4480) -
Smaller improvements:
wt switch --createreports 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)); thewt listloading footer says how many rows fit;wt step for-eachlabels detached worktrees by path;install-statuslinekeepssettings.json's key order; the Codex plugin shows the Worktrunk icon. (#4349, #4352, #4333, #4498, #4420, #4327)
Fixed
-
Foreground hooks that change terminal settings: a hook that touched the terminal, such as turbo on a cache miss, was stopped by
SIGTTOUand 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, #3129, thanks @user753 for #4389 and @willparsons for the turbo report) -
Formatter pre-commit hooks during a squash: in
wt mergeandwt 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 commitandwt merge --no-squashstage first too, so index-reading hooks likepre-commit runcan block them.) (#4321, #4520) -
wt step copy-ignoredon Linux: copied files took the copy's time, so cargo rebuilt much of a copiedtarget/; they now keep the source's mtime, as they already did on macOS and Windows. A reflink rejected withEPERMnow falls back to a copy. (#4319, #4518, #4434, thanks @dmy-gh for #4248, @paul-hansen for the repro, and @HN05 for #4433) -
Directory patterns in
.worktreeincludeand[step.copy-ignored] exclude: a pattern such asconfig/skipped ignored files that sit beside tracked files inconfig/, sowt step copy-ignoredleft out included files and copied excluded ones. It now covers them, as.gitignoredoes. (#4406) -
wt liststatus symbols: JSONsymbolsstrings 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, #4334) -
wt switch:--createbranches from a remote-tracking default branch such as git-p4'sp4/masterinstead ofHEAD; switching by path, as the picker does, gives pre-switch hooks the destination's variables; the bare-repo warning names the configured path. (#4482, #4493, #4491, thanks @cazador481 for #4481 and @dlecan for #4492) -
Agent plugins:
claude --worktree NAMEopens the worktree of an existing branchNAMErather than failing, so a kept session can be reopened. A clean exit then runswt removeon it, which deletes its ignored files and, if integrated, the branch. The Codex plugin's Windows hooks parse under PowerShell. (#4487, #4241, thanks @edziegle for #4486 and @wstczyw for #4239) -
wt switchpicker: a per-projectpagerapplies 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, #4408, #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, #4256, thanks @Duang777) -
Aliases:
--args=Xand--vars=Xreach{{ args }}instead of being dropped, and completion after an alias no longer offers--dry-runor--var. (#4391, #4329) -
Config errors: a malformed
approvals.tomlno longer aborts hooks, aliases, or commits run with--yes; one invalidWORKTRUNK_*override no longer discards the valid ones. (#4467, #4348) -
Windows: an Azure CLI installed through WinGet (
az.cmd) is found, and Git Bash is found through the firstgitonPATH, including custom installs where it resolves underGit\mingw64\bin. (Breaking: withoutgitonPATH, Git Bash is no longer found at its default install locations.) (#4402, #4411, #4452, thanks @wmmc88 for #4401 and @LeoWSY-hashblue for #4410) -
Forge fixes: Azure DevOps CI no longer shows a same-named branch's PR from another repository in the project, and
pr:Nsuggests adev.azure.comURL for SSH remotes; Gitea fork pushes keep the server's clone URL;tealogins in its default macOS and Windows locations are found;ghandglabprotocol settings apply per host. (#4326, #4364, #4495, #4420) -
Fixes for unusual setups: valueless git config keys such as
core.fsmonitorread as true; untracked files named like:xno longer breakwt list's diff; worktree paths containing newlines or non-UTF-8 bytes; the statusline's branch label with customworktree-pathtemplates; zshcompinitadvice names the right file; IPv6ssh://remotes get distinct approvals and[projects]settings (Breaking: re-save them). (#4314, #4320, #4453, #4495, #4421, #4526)
Documentation
-
wt config shell --helpis the shell setup reference, covering init, install, uninstall, and diagnostics. (#4344) -
Doc corrections: the
wt config state vars clearexample,wt listJSONnullcauses, every supported forge in CI help,--pathworktrees inwt list, the bare-repo navigation example, and Alt as Option on macOS. (#4363, #4359, #4439, #4328, #4483, #4412, thanks @wmcnamee-coreweave for #4474) -
Recipes: the bare repository layout sets
origin's fetch refspec; the aichat example passes--codeto drop a reasoning model's<think>block; the OpenCode command uses Claude Haiku 5.5;wt-tuijoins the custom subcommand examples. (#4323, #4332, #4432, #4386, thanks @gitNetw0rk for #4322, @FenjuFu, and @marcusjihansson) -
The
wt-switch-createskill reads the new repository's rootCLAUDE.mdandAGENTS.mdwhen it moves a session into another repository's worktree. (#4414)
Internal
-
Library API changes (Breaking library API):
Repository::project_identifierreturns aProjectId, which config accessors take in place of&str;Repository::require_worktreealso returns the branch;Repository::run_commandtakesAsRef<OsStr>arguments;UserConfig::expected_worktree_pathis new. (#4526, #4496, #4453, #4502) -
MSRV is Rust 1.98. (#4367)
-
A hidden
wt config plugins claude hooksubcommand handles every hook event the Claude Code plugin uses, toward listing the plugin in Anthropic's plugin directory. (#4304) -
Release signing and crates.io OIDC run in the tag-only
releaseenvironment. (#4415)
Install worktrunk 0.81.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-installer.sh | sh && wt config shell installInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/max-sixty/worktrunk/releases/download/v0.81.0/worktrunk-installer.ps1 | iex"; git-wt config shell installInstall prebuilt binaries via Homebrew
brew install worktrunk && wt config shell installDownload worktrunk 0.81.0
| File | Platform | Checksum |
|---|---|---|
| worktrunk-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| worktrunk-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| worktrunk-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| worktrunk-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| worktrunk-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Install via Cargo
cargo install worktrunk && wt config shell installInstall via Winget (Windows)
winget install max-sixty.worktrunk && git-wt config shell installInstall via AUR (Arch Linux)
paru worktrunk-bin && wt config shell install