Release Notes
Improved
wt removeresolves every fsmonitor daemon in onelsofcall: The end-of-command sweep forked onelsofpergit fsmonitor--daemonon the machine — withcore.fsmonitorenabled globally that is one daemon per repo ever touched, routinely over a hundred — and the spawn cost compounds under load rather than staying fixed per call. All daemons now resolve in a single call: 108 spawns to 1 on a live machine. (#3581)
Fixed
-
Shell integration no longer deletes user data that merely quotes the init command: Three places decided whether text was worktrunk's by testing a blob for substrings, and two of them deleted on a wrong guess.
wt config shell uninstallremoved an rc line likealias setup='echo "run: wt config shell init fish | source"';wt config shell install's legacy cleanup deleted a user's own~/.config/fish/conf.d/wt.fishoutright when the file happened to mention the init command. Both now require the command name in command position, not merely somewhere in the text. (#3589) -
Forge detection matches host labels, not substrings:
wt switch pr:<n>and friends picked their provider withhost.contains("github")/contains("dev.azure.com"), so a host that only spells the name inside a longer one —github-mirror.example,dev.azure.com.attacker.example— resolved as that provider. Matching is now label-wise, accepting the domain and its subdomains only; a self-hosted deployment this misses can still name its provider explicitly. (#3589) -
A crash mid-write no longer empties the file being written:
wt config shell uninstallended its rc rewrite by truncating in place, so a crash, a full disk, or a lost power cable between the truncate and the write left~/.bashrc,~/.zshrc, or a PowerShell profile empty or half-written, taking every line the user had ever added. Every user-file write now goes through one writer that writes a temp file beside the target and renames it into place. (#3585, #3591) -
wt step pushrefuses to run out of a half-finished operation: Mid-rebase the detached HEAD looks like a linear extension of the target, sowt step push mainreported✓ Pushed to main (1 commit)while moving the target branch onto a half-replayed history and leaving the rebase open; it now runs the same operation gate aswt step rebaseandwt merge. The same change fixes an annotated-tag target always reporting as needing a rebase — the tag object's SHA was compared against amerge-basethat peels it — and makes both push paths refuse a target worktree whose directory is gone, rather than--no-ffmoving the ref over the stale registration. (#3578) -
Conflict markers can't reach a commit, and the refusal names the command you ran:
wt step relocate --commitstaged withgit add -Aand committed straight through an unresolved merge, reaching neither gate added for the other staging commands; every staging path is now gated on an unmerged index.wt mergealready refused, but in a sub-step's name. (#3588, #3587) -
OpenCode activity markers land in the worktree they belong to: The plugin issued its marker commands through the process-global Bun shell without scoping them, so each ran in whatever the process-wide cwd happened to be — under concurrent parallel-agent sessions, a marker write could land in another session's worktree. (#3554, thanks @4i3n6)
Documentation
-
wt step rebaseandwt step pushrender on the docs site: the only two of twelve step operations whose help was terminal-only. Both bodies are rewritten, correcting (among others) the claim that conflicts abort immediately — nothing aborts; the worktree is left mid-rebase with git's markers — and awt step squashnote promising a backup ref unconditionally, when a clean-tree squash writes none. (#3578) -
Troubleshooting no longer suggests disabling
core.fsmonitorglobally: the guidance for a wedged daemon keeps to the targeted fixes — kill the daemon serving that worktree, or let the nextwt listrespawn the live ones. (#3581)
Internal
- The Windows zip ships the signed binary: SignPath names its download after the GitHub artifact, so the signed zip landed beside the unsigned build as
worktrunk-x86_64-pc-windows-msvc.zip.zipwhile the checksum step and the release upload both kept reading the original — so v0.69.1 shipped an unsigned binary under a green run and a completed signing request. (v0.69.0's unsigned binary was the separate upload bug fixed in #3566, where the request failed outright.) The signed file now replaces the built one only after it verifies, and a final step reads the shipped zip and reports any executable without a certificate. Signing remains non-blocking on a self-signed test certificate pending SignPath's OSS review, so Windows will not show a trusted publisher yet. (#3590)
Install worktrunk 0.69.2
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.69.2/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.69.2/worktrunk-installer.ps1 | iex"; git-wt config shell installInstall prebuilt binaries via Homebrew
brew install worktrunk && wt config shell installDownload worktrunk 0.69.2
| 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