Skip to content

fix: use cross-platform wrapper for wt CLI in Claude Code integration - #1754

Merged
max-sixty merged 17 commits into
max-sixty:mainfrom
lucaspimentel:main
May 6, 2026
Merged

max-sixty merged 17 commits into
max-sixty:mainfrom
lucaspimentel:main

Conversation

@lucaspimentel

@lucaspimentel lucaspimentel commented Mar 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Currently, Worktrunk's Claude Code hook keeps opening Windows Terminal windows because it calls wt, which is an alias ~/AppData/Local/Microsoft/WindowsApps/wt.exe by default.

Windows users have three workarounds:

  • install Worktrunk as git-wt.exe
  • install Worktrunk as wt.exe and disable the Windows Terminal alias to avoid conflicts
  • install Worktrunk as wt.exe and re-order the paths in PATH so Worktrunk is found first

Changes

  • Add a wt.sh wrapper script that tries to find the correct Worktrunk binary.
  • Update hooks.json to use wt.sh.
  • Bump the plugin version.

Manual tests

Tested all scenarios (I hope)

  • Windows (pwsh), Git Bash on Windows, WSL (Windows Subsystem for Linux)
  • with Worktrunk installed as git-wt.exe or wt.exe
  • with Windows Terminal wt alias enabled or disabled

On Windows the binary is git-wt.exe, not wt. Add a wt.sh wrapper
script that detects the platform and calls the correct binary, then
update hooks.json to use it via ${CLAUDE_PLUGIN_ROOT}.

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
@lucaspimentel lucaspimentel changed the title fix: use cross-platform wrapper for wt CLI in plugin hooks fix: use cross-platform wrapper for wt CLI in Claude Code integration Mar 26, 2026
@max-sixty

Copy link
Copy Markdown
Owner

thanks!

can we have it fall back to wt if git-wt doesn't exist? some people deactivate wt in windows

(unless there's some non-convoluted way to detect that wt is Windows Terminal — that would be ideal — @worktrunk-bot research any ideas!)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9be3764104

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude-plugin/hooks/wt.sh Outdated
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

A few options for detecting Windows Terminal's wt:

  1. Path check — Windows Terminal lives at */WindowsApps/wt.exe. On MSYS/Cygwin, command -v wt or which wt would resolve to that path. If it contains WindowsApps, it's Windows Terminal.

  2. Try git-wt first, fall back to wt — simplest approach that covers both cases (Windows with git-wt installed, and Windows where someone disabled the WT alias):

case "$(uname -o 2>/dev/null)" in
    Msys|Cygwin)
        if command -v git-wt.exe >/dev/null 2>&1; then
            WT=git-wt.exe
        else
            WT=wt
        fi
        ;;
    *) WT=wt ;;
esac
  1. Hybrid — try git-wt first, fall back to wt only if it doesn't resolve to WindowsApps:
case "$(uname -o 2>/dev/null)" in
    Msys|Cygwin)
        if command -v git-wt.exe >/dev/null 2>&1; then
            WT=git-wt.exe
        elif command -v wt >/dev/null 2>&1 && ! command -v wt 2>/dev/null | grep -qi WindowsApps; then
            WT=wt
        else
            echo "worktrunk not found — install with: cargo install worktrunk --features git-wt" >&2
            exit 1
        fi
        ;;
    *) WT=wt ;;
esac

Option 2 seems like the right balance — simple, handles the "deactivated wt" case, and the worst that happens if someone only has Windows Terminal's wt is a confusing error (new terminal window opens) which is the same behavior they'd get without the wrapper. Option 3 adds detection but more complexity for an edge case.

@lucaspimentel what do you think — want to go with option 2 (fallback) or 3 (with WindowsApps detection)?

@lucaspimentel

Copy link
Copy Markdown
Contributor Author

@worktrunk-bot can we combine both checks? "if (is windows) AND (git-wt.exe exists), then use git-wt.exe, else use wt"

On Windows, try git-wt.exe first and fall back to wt if it doesn't
exist. This handles users who have deactivated the wt alias.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Done — pushed a commit that does exactly that: on Windows, tries git-wt.exe first via command -v, falls back to wt if it's not found. Non-Windows platforms still use wt as before.

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
Check that `wt` doesn't resolve to the WindowsApps directory before
using it as a fallback, since that path is the Windows Terminal alias.

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WindowsApps detection on line 11 works, but the ${WT:=wt} default on line 16 undoes it: when git-wt.exe is absent and wt is Windows Terminal, neither branch sets WT, so the default still invokes Windows Terminal — the exact bug this PR fixes.

Comment thread .claude-plugin/hooks/wt.sh Outdated
Co-authored-by: worktrunk-bot <w@worktrunk.dev>

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows Terminal detection logic looks good now. One bug: the final invocation line is duplicated, so every hook command will execute twice.

Comment thread .claude-plugin/hooks/wt.sh Outdated
@max-sixty

Copy link
Copy Markdown
Owner

@lucaspimentel would you mind running this for a day or so to confirm it behaves well? It's difficult to test this stuff...

@lucaspimentel

lucaspimentel commented Mar 26, 2026 •

Copy link
Copy Markdown
Contributor Author

@max-sixty definitely. I opened the PR too quickly (edit: the fix wasn't as simple as I originally thought) 😅 I'll convert it to a draft while I keep testing. worktrunk-bot and I keep finding edge cases.

@lucaspimentel
lucaspimentel marked this pull request as draft March 26, 2026 20:16
Remove OS-specific branching — git-wt.exe check is harmless on
non-Windows. Add missing PATH validation and distinct error for
Windows Terminal alias vs not-found cases.

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
@max-sixty

Copy link
Copy Markdown
Owner

thank you v much @lucaspimentel !

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
The UserPromptSubmit hook runs on every prompt, including outside git
repos. Guard against missing git and non-repo directories to prevent
"fatal: not a git repository" stderr noise in Claude Code.

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
Comment thread .claude-plugin/hooks/wt.sh Outdated
lucaspimentel and others added 2 commits March 27, 2026 15:45
When set, use WORKTRUNK_BIN exclusively to find the worktrunk binary
instead of searching PATH. Fail with a clear error if not found.

🤖 Co-Authored-By: Claude Code <noreply@anthropic.com>
@max-sixty

Copy link
Copy Markdown
Owner

@lucaspimentel to the extent you've been using this and it's been successful, we could consider merging...

@lucaspimentel
lucaspimentel marked this pull request as ready for review April 21, 2026 22:27
@max-sixty

Copy link
Copy Markdown
Owner

@worktrunk-bot resopve conflicts

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Resolved and pushed (d3da440).

Conflict was in .claude-plugin/plugin.json — this branch added "version": "1.0.1", but main dropped the static version field entirely in #2402 (so Claude Code uses the commit SHA). Took main's side and removed the line.

Heads up — main also added WorktreeCreate and WorktreeRemove hooks in hooks.json that invoke bare wt, which auto-merged cleanly but undoes the wrapper coverage this PR introduces for the other three hooks. If you'd like them wrapped too, let me know and I can push that change.

@max-sixty

Copy link
Copy Markdown
Owner

@lucaspimentel give the go ahead if you've been using it and we should merge! ty!

@lucaspimentel

Copy link
Copy Markdown
Contributor Author

@worktrunk-bot

Heads up — main also added WorktreeCreate and WorktreeRemove hooks in hooks.json that invoke bare wt, which auto-merged cleanly but undoes the wrapper coverage this PR introduces for the other three hooks. If you'd like them wrapped too, let me know and I can push that change.

Fix this.

These hooks were added to main after this branch and merged in cleanly,
but invoked bare wt — undoing the cross-platform wrapper coverage this
PR introduces for the other three hooks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Done — pushed de3cd47b wrapping the WorktreeCreate and WorktreeRemove commands with wt.sh. All five hooks now go through the wrapper.

@lucaspimentel

Copy link
Copy Markdown
Contributor Author

give the go ahead if you've been using it and we should merge

@max-sixty good to go, thanks!

@max-sixty
max-sixty merged commit 5969838 into max-sixty:main May 6, 2026
26 checks passed
@max-sixty max-sixty mentioned this pull request May 10, 2026
3 tasks done
max-sixty added a commit that referenced this pull request May 10, 2026
Bump worktrunk to **0.49.0** — minor bump (semver-checks reports a
breaking change in `GitError::UncommittedChanges`, which gained a
`dirty_files: Vec<String>` field).

## Highlights

**Improved**
- New `codename(n)` template filter — deterministic friendly worktree
names from a ~1.26M-combo pool (`{{ branch | codename(2) }}` →
`malleable-opah`).
[#2641](#2641), thanks
@endigma
- Picker preview disk cache for Log / BranchDiff / UpstreamDiff, with
background refresh for stale Log decorations.
[#2628](#2628),
[#2646](#2646)
- Picker shares one `default_branch_sha` lookup across BranchDiff items
instead of forking N rev-parse subprocesses.
[#2658](#2658)
- "X has uncommitted changes" errors now list the dirty files inline.
[#2653](#2653)

**Fixed**
- `wt switch` integrates with `cd` aliases like zoxide (uses `builtin
cd`). [#2644](#2644), thanks
@xkumiyu for reporting
- Empty hook tables no longer panic during `wt switch --create`.
[#2635](#2635), thanks @topit
for reporting
- `Repository::current_worktree()` and five sibling helpers no longer
silently leak the process CWD when called via `Repository::at(p)`.
[#2652](#2652)
- `WorkingTree::is_linked` tolerates non-git CWDs (e.g. Nix sandbox).
[#2625](#2625), thanks
@DArtagan for reporting
- Picker no longer overlays `collect` warnings on the active TUI; clears
its frame on exit in inline mode.
[#2627](#2627),
[#2626](#2626)
- Failed `git log` no longer poisons the picker's preview disk cache.
[#2651](#2651)
- `wt step prune` says "removing branch" for branch-only candidates.
[#2619](#2619)
- Claude Code Windows integration uses a `wt.sh` wrapper to avoid
colliding with Windows Terminal's `wt.exe`.
[#1754](#1754), thanks
@lucaspimentel

**Documentation**
- cmux workspace integration recipe.
[#1907](#1907), thanks
@alvistar

See
[CHANGELOG.md](https://github.com/max-sixty/worktrunk/blob/release/CHANGELOG.md)
for the full list including internal CI / test-isolation changes.

## Test plan

- [x] `cargo run -- hook pre-merge --yes` (tests + lints clean)
- [x] `cargo semver-checks check-release -p worktrunk` (one breaking
change confirmed → minor bump)
- [x] CHANGELOG entries verified by subagent against actual diffs

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants