Release Notes
Improved
-
wt switchstatusline links in Claude Code: The Claude Code statusline suppressed OSC 8 hyperlinks, so its CI segment printed colored but inert and its dev-server URL printed in full. Claude Code renders OSC 8, so both segments now link, matchingwt list. (#3550) -
wt merge --no-rebaseaccepts merge-shaped histories:--no-rebasepreviously required a strictly linear rebased history and rejected a branch carrying a merge commit, even when the target could already fast-forward to its tip. It now accepts any history the target can fast-forward to, sowt merge --no-commit --no-rebasepreserves an exact commit graph — merge commits and all. (#3509, thanks @reneleonhardt) -
-vvariable blocks name their template and render consistently: The four-vtemplate-variable listings (foreground/background hooks, aliases,wt step eval) now label each block with the template it belongs to, andevalrenders through the shared formatter — curated help-table order rather than its own alphabetical layout. (#3495, #3536)
Fixed
-
wt mergemeasures the squash/rebase span against the target's upstream: When the primary checkout's local default branch was behind its upstream (e.g. localmainbehindorigin/main) and the branch descended from the newer upstream tip (created with--base origin/main),wt merge— andwt step squash/wt step rebase— measured the commit span against the stale local ref and swept in commits already upstream, folding them into the squash and corrupting the local default branch (duplicating upstream content under new SHAs if later pushed). The span is now measured against the target's upstream (a local-only check, no fetch), and a target that has genuinely diverged from its upstream is refused up front. Fixes #3519. (#3549, thanks @starlightromero for reporting) -
Ctrl-C during a rebase surfaces as an interrupt, not a conflict: A
git rebasekilled by a signal (SIGINT/SIGTERM) mid-operation left the worktree inREBASINGstate, whichwtclassified as a merge conflict — printing conflict-resolution guidance and the wrong exit code. A signal-killed rebase now exits cleanly with the signal's conventional code (130 for SIGINT, 143 for SIGTERM). (#3539) -
wt switchpicker responsiveness: Accepting a row could stall for ~10s on a large repo (indefinitely under sustained background traffic) while the switch queued behind per-row preview diffs; the foreground thread now bypasses the command-concurrency semaphore. Separately, an idle picker with pending background work — a slow CI fetch, or an LLM branch summary ([list] summary = true) — spun 100% of a CPU core; the reader now exits once the last row batch lands. (#3544, #3534) -
Clear error when a new branch name collides with an existing branch namespace: Creating
featwhilefeat/xexists (or the reverse) failed with git's raw ref-lock error;wt switch --createnow explains the namespace conflict. (#3528) -
wt switchpicker no longer shows another row's branch name in an empty diff preview: The branch-diff and upstream-diff caches are keyed by SHA, so branches parked at the same commit (common after merged branches reset to the default branch's tip) shared one entry — and the cached pane had the first row's branch name baked into its "no file changes" headline. The cached value is now branch-agnostic and the headline renders per row. (#3481) -
WorktreeRemoveplugin hook no longer strands a completed session: Claude Code fires the hook on session teardown for the recorded worktree path, which may already be gone (removed bywt mergeorwt remove). In that case the hook exited non-zero, which Claude Code read as a failed removal, leaving the completed session row undeletable; it now exits 0 when the worktree is already gone (#3493, closes #3488). Separately, the hook now anchors at the project directory rather than inheriting the session's working directory (#3489). Thanks @judewang for reporting #3488 and for #3489. -
WorktreeCreateplugin hook surfaceswtfailures: The hook pipedwt switch --create … --format=jsonintojqwithoutset -o pipefail, so a failedwt(e.g. a branch collision after a partial creation) tookjq's exit status — 0 on empty input — and Claude Code saw a successful hook that returned no path. The hook now setspipefail. (#3546, closes #3545, thanks @avdi for reporting) -
Picker no longer crashes on the legacy Windows console: skim 5.3.1 drives keyboard-enhancement handling the legacy Windows console API doesn't support, crashing the picker at startup; skim is held at 5.1.0 until the upstream regression is resolved. (#3538)
-
Non-ASCII and non-UTF-8 content handled throughout: Diagnostics no longer panic slicing a config or log at a non-UTF-8 byte boundary, the shell-integration config scan no longer truncates at a non-UTF-8 line, and
wt step copy-ignoredhandles non-ASCII filenames (git'squotePathescaping). (#3514, #3499, #3487) -
-vvoutput cleanup: The end-of-run block names onlydiagnostic.md, dropping the redundanttrace.jsonl/subprocess.loggutter lines the report body already links; and the startup pointer uses@before the log directory, matching the rest ofwt's path output. (#3521, #3543)
Internal
-
Windows release binaries are submitted to SignPath for code signing: Signed with a test certificate for now, while the project's OSS-program application is under review, and non-blocking so a signing failure can't hold up publishing to crates.io, Homebrew, winget, or AUR. (#3553, #3556)
-
wt listruns its merge analysis in a read-only object database: When the git object store is read-only,wt listandwt list statuslineredirect their object-writing merge/conflict probes into a temporary object database layered over the real one, so the full analysis still runs. Mutating commands keep the persistent store and fail loudly on a read-only one. (#3535) -
wt step relocatepreserves your subdirectory position: Routed through the shared subdir-resolution helper, sorelocatefollows the cwd into the moved worktree likeswitch,remove, andmergealready do. (#3346)
Install worktrunk 0.69.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.69.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.69.0/worktrunk-installer.ps1 | iex"; git-wt config shell installInstall prebuilt binaries via Homebrew
brew install worktrunk && wt config shell installDownload worktrunk 0.69.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