Release Notes
Improved
-
Aliases route
--KEY=VALUEto template variables and forward everything else as{{ args }}:--KEY=VALUE(or--KEY VALUE) bindsKEYwhenever the template references{{ KEY }}—wt deploy --env=stagingsets{{ env }}tostaging. Everything else joins{{ args }}, a space-joined, shell-escaped sequence ready to splice into a command. Withs = "wt switch {{ args }}",wt s some-branchexpands towt switch some-branch. Index with{{ args[0] }}, loop with{% for a in args %}…, count with{{ args | length }}; each element is escaped individually, sowt run 'a b' 'c;d'renders as'a b' 'c;d'— no shell injection. Tokens after--forward unconditionally, bypassing any binding. Hyphens in keys become underscores:--my-var=xbinds{{ my_var }}. Built-in vars can be overridden inside the template —--branch=foosets{{ branch }}for the invocation, but the worktree's actual branch doesn't move. (Breaking:--var KEY=VALUEand--var=KEY=VALUEremoved;wt <alias>no longer errors on unrecognized flags — they forward to{{ args }}.) (#2280, #2287, #2304) -
-y, --yesis a top-level global flag: Lives once onCliinstead of being duplicated across switch, remove, merge, commit, squash, prune, the ten hook subcommands, shell install/uninstall, plugin install/uninstall, and config update.wt -y <anything>,wt <anything> --yes, andwt --yes <anything>all skip approval and confirmation prompts for that invocation. (Breaking: post-alias--yesremoved — use the global formwt -y <alias>.) (#2279, #2290) -
wt config alias showandwt config alias dry-run:showprints the configured template tagged by source (user/project).dry-runpreviews what an invocation would run without executing —wt config alias dry-run s -- target-branchrenders exactly whatwt s target-branchwould produce. Output annotates routing with# bound:and# args:comments so you can see how each token was interpreted. Both warn when the alias name shadows a top-level built-in (e.g.list,switch); the alias is only reachable viawt step <name>.wt <alias> --help/-hprints a hint pointing at these subcommands rather than silently forwarding the flag into{{ args }}; usewt <alias> -- --helpto forward. (Breaking:wt <alias> --dry-runandwt step <alias> --dry-runretired — use the new subcommand.) (#2291, #2304) -
wt config approvalsreplaceswt hook approvals: Approvals cover both project hooks and project aliases, so the old namespace underhookmis-scoped the command.addnow walks both hook and alias commands — a project that only declares aliases can bulk-pre-approve in one shot.wt hook approvalsremains as a hidden alias that emits a deprecation warning and forwards. (#2282) -
Scope-aware template variable validation: A new
ValidationScope(Hook(HookType),SwitchExecute,Alias) drives validation across every template surface.{{ args }}only validates inside aliases;{{ target }}only in switch/start/merge contexts;{{ pr_number }}only in PR-aware switch hooks. A typo like{{ target }}in apre-starthook is caught at validation time instead of failing at runtime with an undefined-var error after the worktree was created. (#2288) -
pr_numberandpr_urltemplate variables for PR/MR worktree hooks: Available inpre-switch,post-switch,pre-start, andpost-startwhen the worktree was created viawt switch pr:N/mr:N. One canonical pair for both GitHub and GitLab — no separatemr_*aliases. Previously the runtime injected these inpre-startonly and the validator rejected them, so the feature was unreachable. (#2300) -
targettemplate variable injected symmetrically on switch/create/start:pre-switchalready injectedtarget(and conditionallytarget_worktree_path);post-switch,pre-start, andpost-startnow do too. A user writing{{ target }}inpost-startno longer hits an undefined-var error at runtime. (#2295) -
Single announce line for combined background hooks: When user and project hooks both fire on post-merge, post-commit, post-start/post-switch, or post-remove, output collapses into one
◎ Running <hook>: user:…, project:… @ <path>line instead of one per source. Extracted a sharedspawn_background_hooksso every site uses the same path. (#2294, #2298) -
wt config state getshows trash and git commands cache: Two categories thatwt config state clearsweeps (.git/wt/trash/staged worktree directories and.git/wt/cache/SHA-keyed caches) were missing fromstate get, so users could clear state without ever knowing those entries existed. (#2292)
Fixed
-
Reject underscore in
varskeys with a clear error:wt config state vars set db_suffix=foopreviously passed validation and then failed with a crypticerror: invalid key: worktrunk.state.main.vars.db_suffixfrom git (git config variable names must match[a-zA-Z][a-zA-Z0-9-]*). Now rejected atvalidate_vars_keywith a message pointing users to use hyphens instead. (#2285, thanks @Mziserman) -
Surface the full anyhow error chain in spawn and ref-update messages:
Failed to spawn pipeline: Failed to spawn detached processpreviously dropped the underlyingio::Error(errno + OS description). Three sites switched from{err}to{err:#}so the full source chain renders — affects pipeline spawn warnings,wt switchLLM-summary preview errors, andgit pushfailure messages. (#2251) -
Template parse errors in aliases now surface before flag routing: A syntax error in any alias step previously caused that step to silently contribute no names to the referenced-var set, which could change how
--KEY=VALUEtokens bound vs. forwarded as positionals; the syntax error only surfaced later at expansion time. Now errors propagate up front so flag routing isn't determined by malformed templates. (#2299)
Documentation
-
Hook template variables grouped by kind: Variables in help text and docs now follow a consistent ordering (worktree, base, target, PR/MR, hook infrastructure) instead of mixed kinds. (#2303)
-
Aliases section rewrite: Replaced the "How arguments are routed" table with a concrete
fly deployexample, restored theuprebase-every-worktree recipe, added asince-mainexample, and reorganized so the simpler "Passing values" section comes before the routing mechanism. (#2304) -
Trim filler in prose and help text: Removed redundant qualifiers and parenthetical hedges across
extending.md,faq.md,tips-patterns.md, and thelist/stephelp text. (#2277, #2289) -
Drop stale
[experimental]from aliases docstring (#2283) and expand the worktrunk skill description with lexical triggers so Claude finds it more reliably (#2301).
Internal
-
Move approval handlers to config module: Reflects the new
wt config approvalshome. (#2286) -
Nix flake reads Rust channel from
rust-toolchain.toml: Single source of truth instead of duplicating the toolchain version. (#2188)
Install worktrunk 0.40.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.40.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.40.0/worktrunk-installer.ps1 | iex"; git-wt config shell installInstall prebuilt binaries via Homebrew
brew install worktrunk && wt config shell installDownload worktrunk 0.40.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