Repository navigation
v0.2.0
Thirteen commits since v0.1.33, including three new configuration features, a security fix that changes how repo-supplied hooks are treated, and cross-platform fixes for Git Bash, PowerShell and fish.
⚠️ Action required
Hooks from a repository's committed .wt.toml now require approval before they run (#129). Previously they were merged into the effective hook config and executed via sh -c with no prompt and no opt-in, so cloning an untrusted repo and running wt create was arbitrary code execution as the invoking user.
Approval is pinned to (repository, sha256 of .wt.toml), so any edit — a pull that adds a post_create, a branch with a different .wt.toml — asks again, and an identical file in another repository is not covered. Approvals live in ~/.config/wt/trust.toml, never in the repository. Declining skips the hooks with a warning; it never aborts the operation. With no terminal to ask on (scripts, CI, --format json) the answer is skip, unless WT_HOOKS_APPROVE_ALL=1.
New hooks_policy setting (config file or WT_HOOKS_POLICY) chooses how much is gated: prompt-untrusted (default), prompt-all, trusted-only, off. New commands: wt trust, wt trust --list, wt untrust.
Hooks from your own ~/.config/wt/config.toml are unaffected under the default policy.
Features
wt clone(#127) —wt clone <owner/repo|url> [dest]acquires the main repository sowt create/wt checkouthave something to hang worktrees off. Placement is driven by two new top-level settings,repo_rootandrepo_pattern; the trailing branch segment resolves from the remote's default branch (git ls-remote --symref), so the clone lands in a valid main-worktree slot.owner/reporesolves throughghorglab, each asked for its owngit_protocol— account selection stays with those tools and wt never mutates their auth state. Based on #122 by @brosu.- Configuration from git config (#128) — per-repo settings can now live outside the working tree, in
.git/config, which is never committed:Supported keys are the scalars:git config --local wt.strategy sibling-repo git config --global wt.root ~/projects/worktreeswt.root,wt.strategy,wt.pattern,wt.separator. Precedence, highest first: env vars > local git config >.wt.toml> config file > global git config > defaults. Local git config outranks.wt.tomlbecause it is personal local state rather than project policy arriving via a pull request. Hooks are deliberately out of scope for now. This also corrects the documented precedence, which previously omitted.wt.tomlentirely. - Per-directory environment variables via
[[context]]rules (#145) — includeIf-style rules bind variables to a directory tree instead of requiring a per-command export or direnv:Matching is a path prefix (not a glob), evaluated after resolving symlinks, case-insensitively on macOS and Windows. Every matching rule applies, later rules winning per variable. Repo-scoped commands match the repository's main checkout so all linked worktrees resolve to the same rule;[[context]] when_path = "~/dev/repos/work" env = { WT_CATEGORY = "work" }
wt clonematches the working directory. Rules are rendering-only — merged underneathos.Environ(), never exported, so a real export still wins and hooks see an unchanged environment. Read from the user config file only, so a committed.wt.tomlcannot redirect where worktrees land. - Default values for env vars in patterns (#139) —
{.env.WT_CATEGORY:-personal}falls back topersonalwhen the variable is unset. Without the:-syntax unset variables still error, preserving typo detection. Defaults respect the configured separator, so withseparator = "-",{.env.X:-a/b}yieldsa-b.wt infonow advertises the syntax.
Fixes
- Interactive prompts are invisible under Git Bash (#124, #126) — prompts were live and accepted keystrokes, but rendered to stdout, which the shell wrapper captures to find the navigation marker. Git Bash ships no
script(1), so the whole menu landed in a temp file and only appeared when the trailingcatreplayed it. Prompts now render to stderr, which no integration redirects. Covers all six prompts: checkout, cd, remove and pr/mr selectors, and the cleanup confirmation. On Windows the raw VT stream is kept on stderr and console mode is enabled viaSetConsoleModefor plain conhost. Reproduced and confirmed fixed on a real windows-latest runner driving Git Bash through ConPTY. - fish integration fails where
script(1)is absent (#131, #133) — the fish wrapper calledscriptunconditionally, so on minimal containers or any distro without util-linux everywtcall died withUnknown command: scriptand exit 127. Now mirrors the bash/zsh fallback.$statusis captured inside each branch so the replayingcatcan no longer mask a failing command into an auto-cd. - PowerShell profile line silently does nothing (#130, #135) — the documented
Invoke-Expression (& wt shellenv powershell)fails on Windows PowerShell 5.1:shellenvemits many lines, so the subexpression is anObject[]and-Command([string]) rejects it. Thewtfunction was never defined, so auto-cd, completion and interactive menus were silently absent whilewt.exeon PATH kept plain commands working. Now piped throughOut-String, with the line hoisted into a shared constant used bywt initandwt shellenvhelp, and a test pinning docs and the e2e harness to the documented form. - e2e runner tested the wrong binary (#141, #142) — it scanned for a pre-existing
./bin/wtand otherwise fell back to whateverwtwas on PATH, never building anything. Sincebin/is gitignored, a stale build survives a rebase, and the PATH fallback picks up a system-wide install — producing failures that are not real and, worse, passes that hide a regression. It now builds from the module root into a temp directory;-wtremains the explicit escape hatch. Adds unit tests for the orchestrator, which had none.
Docs
- direnv recipe for per-directory categories (#143) — complements the
[[context]]rules above; the:-defaults from #139 make it degrade gracefully where no.envrcexists. - The examples page now notes that repo-supplied hooks need approval.
- Dropped the paragraph claiming interactive menus are unavailable under Git Bash — that described the bug, not the design.
Chores
Full commit list: v0.1.33...v0.2.0