feat: Add wt cd command and wt init for native shell integration#41
feat: Add wt cd command and wt init for native shell integration#41beastyrabbit wants to merge 6 commits intojohnlindquist:mainfrom
wt cd command and wt init for native shell integration#41Conversation
Add `wt cd` to navigate into worktree directories via subshell, and `wt init` to output a shell wrapper function that enables native `cd` without spawning a subshell. wt cd: - Resolve worktree by branch name, filesystem path, or interactive picker - Spawn a subshell in the target directory (fallback for users without shell integration) - --print flag outputs the resolved path to stdout for scripting/shell wrappers - Proper signal forwarding and exit code propagation - All user-facing output goes to stderr; stdout reserved for machine-readable output wt init: - Outputs a shell wrapper function for zsh, bash, or fish - Auto-detects shell from $SHELL when no argument given - Wrapper intercepts `wt cd`, uses --print + builtin cd for native directory change - Falls back to subshell-based `wt cd` if --print fails - Detects if already installed in rc file - Shows copyable commands to add to rc file and reload shell Also improves wt open: - Route all errors to stderr consistently (process.stderr.write) - Narrow catch blocks to ENOENT only - Output raw path when editor is set to "none" - Add reject:false to git rev-parse for graceful error handling Powered by human calories and mass GPU cycles.
📝 WalkthroughWalkthroughAdds two CLI subcommands ( Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI
participant TUI
participant Filesystem
participant Git
participant Shell
User->>CLI: wt cd [pathOrBranch]
alt no argument
CLI->>TUI: prompt selection (forwards stdout if provided)
TUI-->>CLI: selected WorktreeInfo or null
alt null
CLI-->>User: write cancellation to stderr
CLI->>CLI: exit 0
end
end
CLI->>Filesystem: stat(path) (capture ENOENT)
alt path exists & is directory
CLI->>Git: findWorktreeByPath(path)
alt found
Git-->>CLI: WorktreeInfo
else .git present
Filesystem-->>CLI: construct minimal WorktreeInfo
end
else path missing or not dir
CLI->>Git: findWorktreeByBranch(branch)
alt found
Git-->>CLI: WorktreeInfo
else not found
CLI-->>User: write error to stderr (suggest interactive)
CLI->>CLI: exit 1
end
end
CLI->>Filesystem: verify target path exists (handle ENOENT)
alt options.print
CLI-->>User: write path to stdout
else spawn subshell
CLI-->>User: "Entering ..." to stderr
CLI->>Shell: spawn subshell in target dir
Shell-->>User: interactive session
Shell-->>CLI: exit code/signal
CLI-->>User: propagate exit code
end
sequenceDiagram
actor User
participant CLI
participant ShellDetect
participant Filesystem
participant UserRC
User->>CLI: wt init [shell]
alt shell arg provided
CLI->>ShellDetect: validate shell name
ShellDetect-->>CLI: supported or unsupported
else autodetect
CLI->>ShellDetect: detect from $SHELL
ShellDetect-->>CLI: shell name or null
end
CLI->>ShellDetect: getShellFunction(shell)
ShellDetect-->>CLI: function text (stdout)
CLI->>UserRC: read RC file (~/.rc)
alt already installed
CLI-->>User: write installed notice to stderr
else not installed
CLI-->>User: write append command and reload instruction to stderr
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@build/commands/open.js`:
- Around line 13-14: The git validation currently calls execa("git",
["rev-parse", "--is-inside-work-tree"], { reject: false }) but ignores the
returned result, so failures are not detected; change this to capture the result
(e.g., const result = await execa(...)) and check result.exitCode (or
result.failed) and, if non-zero/failed, handle it (throw an error or log and
process.exit(1)) so the command aborts when not inside a git repo; alternatively
remove reject: false to let execa throw and catch that exception—update the call
site where execa("git", ["rev-parse", "--is-inside-work-tree"], ...) is invoked.
In `@src/commands/open.ts`:
- Around line 18-19: The git repo validation using execa("git", ["rev-parse",
"--is-inside-work-tree"], { reject: false }) in open.ts currently ignores the
command result; update the validation to inspect the returned result (e.g.,
check result.exitCode === 0 or result.stdout === "true") and handle
non-zero/false by throwing or returning a clear error, or switch to { reject:
true } and catch the thrown error; modify the code around the execa call so that
the function (the surrounding command handler in open.ts) fails fast when not
inside a git work tree.
Powered by human calories and mass GPU cycles.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/commands/open.ts (1)
54-69:⚠️ Potential issue | 🟡 MinorInconsistent ENOENT handling in
.gitcheck.The inner catch block at line 66 catches all errors, unlike other catch blocks in this file that use
isEnoent(). Ifstat(.git)fails with a permission error, the message incorrectly states the path "is not a git worktree."🔧 Proposed fix for consistency
try { await stat(resolve(pathOrBranch, ".git")); targetWorktree = { path: resolve(pathOrBranch), head: '', branch: null, detached: false, locked: false, prunable: false, isMain: false, bare: false, }; - } catch { + } catch (gitErr: unknown) { + if (!isEnoent(gitErr)) throw gitErr; process.stderr.write(chalk.red(`The path "${pathOrBranch}" exists but is not a git worktree.`) + "\n"); process.exit(1); }
🤖 Fix all issues with AI agents
In `@build/commands/open.js`:
- Around line 60-63: The bare catch in the open command swallow errors; update
the catch in the open logic (the try/catch that checks whether pathOrBranch is a
git worktree in open.js and, more importantly, in src/commands/open.ts) to
detect ENOENT using isEnoent(err) and only treat missing-path as the expected
case; rethrow or log other errors. Specifically, change the catch to accept the
error parameter (e.g., catch (err)) and call isEnoent(err) to decide whether to
print the "not a git worktree" / exit or rethrow (or log) the unexpected error,
then rebuild the project so the generated build/commands/open.js matches
src/commands/open.ts.
Powered by human calories and mass GPU cycles.
When `eval "$(wt init zsh)"` runs from .zshrc on every terminal open, the "Already installed" message and setup hints were printed to stderr each time. Now `initHandler` returns silently after emitting the shell function when called with an explicit shell arg. Guidance output is reserved for the interactive `wt init` (auto-detect) path. Powered by human calories and mass GPU cycles.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/commands/init.ts`:
- Around line 24-35: Update the embedded FISH_FUNCTION so the resolved directory
is passed safely: change the builtin cd invocation to use an argument separator
and quoted variable (replace builtin cd $dir with builtin cd -- "$dir") and also
ensure the fallback call uses -- before positional args (replace command wt cd
$argv[2..-1] with command wt cd -- $argv[2..-1]) so directories that start with
dashes or contain spaces are handled correctly.
fix for fish Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/commands/init.ts`:
- Around line 6-22: The bash/zsh wrapper BASH_ZSH_FUNCTION should protect
against dash-prefixed directory names by terminating option parsing when calling
cd; update the branch that currently does builtin cd "$dir" inside the wt()
function to use builtin cd -- "$dir" so leading "-" is treated as a literal
directory name (note that a directory literally named "-" still requires a user
to refer to it as ./-). Ensure the change is only applied to the builtin cd
invocation in the wt() function.
also zsh fix Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Summary
Adds two new commands that work together to let users navigate into worktree directories with a native
cd— no subshell required.wt cd [pathOrBranch]Navigate into a worktree directory. Resolves by branch name, filesystem path, or interactive picker.
--print: outputs the resolved path to stdout and exits — used by the shell wrapper belowErrors go to stderr, stdout is reserved for machine-readable output. Propagates shell exit codes and signals correctly.
wt init [shell]Outputs a shell wrapper function (zsh, bash, or fish) that intercepts
wt cdand performs a nativebuiltin cdusing--print.$SHELLwhen no argument given--printfails, falls back to the normal subshellwt cdOther improvements
wt opennow routes all errors toprocess.stderr.writeconsistentlyreject: falsetogit rev-parsecalls for graceful error handlingwt openoutputs raw path when editor isnoneFiles changed
src/commands/cd.tssrc/commands/init.tssrc/index.tssrc/utils/tui.tssrc/commands/open.tstest/cd.test.tstest/init.test.tsREADME.mdSomewhere a GPU is overheating so I don't have to think.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.