Files
max-sixty__worktrunk/templates/fish.fish
Worktrunk Bot 03dbf5f70e fix: use builtin cd in fish shell integration (#3160)
## Problem

`wt switch <branch>` reported a successful switch but then failed to
change directory under fish when the
[`kidonng/zoxide.fish`](https://github.com/kidonng/zoxide.fish) plugin
is active, printing `zoxide: no match found` / `Error: Directory not
found` even though the worktree exists (#3159).

The fish integration changed directory with a bare `cd -- "$target"`.
`zoxide.fish` replaces `cd` with a zoxide query function. With `cd --
"$target"` it receives two arguments (`--` and the path), so its
single-directory fast path (`argc -eq 1`) is skipped and it falls
through to fuzzy-query mode, which finds no match for `--
/path/to/worktree`.

The bash and zsh wrappers were already immune because they use `builtin
cd`; only fish used a bare `cd`.

## Solution

Use `builtin cd -- "$target"` in `templates/fish.fish`, bypassing any
user `cd` override (verified: in fish `cd` is a regular function that
`builtin cd` skips). The `--` is retained for consistency with the
bash/zsh wrappers; the target is an absolute path written by the binary,
so it can never be flag-injected.

## Testing

- Added `test_fish_init_uses_builtin_cd` in `src/shell/mod.rs`, which
fails on the old template (`cd -- "$target"`) and passes after the fix.
- Updated the fish init snapshots
(`src/shell/snapshots/...init_fish.snap`,
`tests/snapshots/...init_fish.snap`).
- Verified fish `3.7.0` behavior directly: `cd` is a function, `builtin
cd` and `builtin cd --` both succeed, and `builtin cd` bypasses a
user-defined `cd` function (the simulated override was not invoked).
- `cargo test --test integration test_docs_are_in_sync` passes.

---
Closes #3159 — automated triage

Co-authored-by: worktrunk-bot <worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com>
2026-06-23 16:50:17 +00:00

68 lines
2.5 KiB
Fish

# worktrunk shell integration for fish
#
# This is the full function definition, output by `{{ cmd }} config shell init fish`.
# It's sourced at runtime by the wrapper in ~/.config/fish/functions/{{ cmd }}.fish.
# Override {{ cmd }} command with split directive passing.
# Creates two temp files: one for cd (raw path) and one for exec (shell).
# WORKTRUNK_BIN can override the binary path (for testing dev builds).
function {{ cmd }}
set -l use_source false
set -l args
for arg in $argv
if test "$arg" = "--source"; set use_source true; else; set -a args $arg; end
end
test -n "$WORKTRUNK_BIN"; or set -l WORKTRUNK_BIN (type -P {{ cmd }} 2>/dev/null)
if test -z "$WORKTRUNK_BIN"
echo "{{ cmd }}: command not found" >&2
return 127
end
set -l cd_file (mktemp)
set -l exec_file (mktemp)
# WORKTRUNK_SHELL tells the binary to escape the exec directive for fish's
# `eval` below — fish treats `\` as an escape inside '...', unlike POSIX.
# --source: use cargo run (builds from source)
if test $use_source = true
env WORKTRUNK_DIRECTIVE_CD_FILE=$cd_file WORKTRUNK_DIRECTIVE_EXEC_FILE=$exec_file \
WORKTRUNK_SHELL=fish \
cargo run --bin {{ cmd }} --quiet -- $args
else
env WORKTRUNK_DIRECTIVE_CD_FILE=$cd_file WORKTRUNK_DIRECTIVE_EXEC_FILE=$exec_file \
WORKTRUNK_SHELL=fish \
$WORKTRUNK_BIN $args
end
set -l exit_code $status
# cd file holds a raw path — read with fish builtin (no cat subprocess,
# safe even if CWD was removed by worktree removal).
if test -s "$cd_file"
set -l target (string trim < "$cd_file")
# `builtin cd` bypasses any user `cd` override (e.g. the zoxide.fish
# plugin replaces `cd` with a query function that mishandles the `--`
# separator). Matches the bash/zsh wrappers. (#3159)
builtin cd -- "$target"
set -l cd_exit $status
if test $exit_code -eq 0
set exit_code $cd_exit
end
end
# exec file holds arbitrary shell (e.g. from --execute)
if test -s "$exec_file"
set -l directive (string collect < "$exec_file")
eval $directive
set -l src_exit $status
if test $exit_code -eq 0
set exit_code $src_exit
end
end
command rm -f "$cd_file" "$exec_file"
return $exit_code
end
# Completions are in ~/.config/fish/completions/{{ cmd }}.fish (installed by `{{ cmd }} config shell install`)