Skip to main content

git_worktree_manager/operations/launchers/
mod.rs

1/// Terminal launcher implementations.
2pub mod detached;
3pub mod foreground;
4pub mod iterm;
5pub mod tmux;
6pub mod wezterm;
7pub mod zellij;
8
9/// Wrap a command line so that, after it exits, the pane/window/tab keeps a
10/// fresh interactive login shell instead of closing.
11///
12/// The tmux and zellij launchers (session, window/tab, panes) run the command
13/// *as* the pane's program (`bash -lc <cmd>`), so when the AI tool exits the
14/// pane/tab/session closes with it — you lose the worktree context and can't
15/// run follow-up commands. WezTerm and iTerm don't have this problem because
16/// they type the command into an already-running shell; tmux's own session
17/// launcher sidesteps it by `send-keys`-ing into a pre-spawned shell.
18///
19/// Appending `; exec "${SHELL:-bash}" -l` makes the affected launchers behave
20/// the same way: the command runs, then — regardless of its exit code (`;`,
21/// not `&&`) — control drops to a fresh login shell in the same cwd. A
22/// non-zero exit leaves the user at a prompt with the context intact rather
23/// than closing the pane.
24///
25/// `${SHELL:-bash}` honors the user's login shell when the env var is present
26/// (the common case when `gw` is run from an interactive session) and falls
27/// back to `bash` otherwise. The result is still meant to be passed to
28/// `bash -lc`, so `<cmd>` keeps whatever quoting it already carries.
29pub fn keep_shell_after(command: &str) -> String {
30    format!("{command}; exec \"${{SHELL:-bash}}\" -l")
31}
32
33#[cfg(test)]
34mod tests {
35    use super::keep_shell_after;
36
37    #[test]
38    fn appends_exec_login_shell() {
39        assert_eq!(
40            keep_shell_after("/usr/local/bin/gw _spawn-ai /tmp/x.json"),
41            "/usr/local/bin/gw _spawn-ai /tmp/x.json; exec \"${SHELL:-bash}\" -l"
42        );
43    }
44
45    #[test]
46    fn preserves_existing_quoting() {
47        // spawn_spec emits quoted segments when the path has spaces; we must
48        // not disturb them — the whole thing is still handed to `bash -lc`.
49        let cmd = r#""/My App/gw" _spawn-ai "/tmp/x y.json""#;
50        assert_eq!(
51            keep_shell_after(cmd),
52            r#""/My App/gw" _spawn-ai "/tmp/x y.json"; exec "${SHELL:-bash}" -l"#
53        );
54    }
55}