From d66007053b3fb2e7b25f039bfdfc3b9936d5a7f2 Mon Sep 17 00:00:00 2001 From: timkjr Date: Tue, 4 Aug 2026 16:37:23 -0500 Subject: [PATCH] fix(shell): launch shell tabs as an interactive login shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shell-mode sessions resolve to an absolute shell path (issue #208's fix) but launch it bare, with no -i/-l flags. Without those, the spawned shell runs as a non-interactive child of the non-interactive `bash -c` that launches the pane, so it never sources ~/.zshrc or ~/.bashrc — silently dropping aliases, PATH additions, and tool init (zoxide, nvm, etc.). Co-Authored-By: Claude Sonnet 5 --- src/tmux-manager.ts | 6 +++++- test/shell-session-launch.test.ts | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/tmux-manager.ts b/src/tmux-manager.ts index acfecb3a..2be5daa9 100644 --- a/src/tmux-manager.ts +++ b/src/tmux-manager.ts @@ -780,7 +780,11 @@ export function buildSpawnCommand(options: { // SERVER process's env — empty in containers and system systemd units, leaving // the pane command ending in a dangling `&&` ("syntax error: unexpected end of // file", pane dead on arrival). Resolve it in Node and quote the result. - return shellescape(resolveLocalShell()); + // -i -l: interactive login shell, so the resolved shell sources the user's rc + // files (~/.zshrc, ~/.bashrc, etc.) instead of running as a bare non-interactive + // child of the non-interactive `bash -c` that launches the pane — without this, + // aliases, PATH additions, and tool init (zoxide, nvm, etc.) are silently dropped. + return `${shellescape(resolveLocalShell())} -i -l`; } /** diff --git a/test/shell-session-launch.test.ts b/test/shell-session-launch.test.ts index e3a1ba42..078ad48a 100644 --- a/test/shell-session-launch.test.ts +++ b/test/shell-session-launch.test.ts @@ -88,6 +88,14 @@ describe('shell-mode spawn command (issue #208)', () => { expect(cmd.trim()).not.toBe(''); }); + it('launches an interactive login shell, so rc files (~/.zshrc, ~/.bashrc, etc.) are sourced', () => { + // Without -i -l, the resolved shell runs as a bare non-interactive child of + // the non-interactive `bash -c` that launches the pane, silently dropping + // aliases, PATH additions, and tool init (zoxide, nvm, etc.). + const cmd = buildSpawnCommand({ mode: 'shell', sessionId: 'abc123de-0000-0000-0000-000000000000' }); + expect(cmd.trim().endsWith('-i -l')).toBe(true); + }); + it('produces a launch command that parses after the outer sh -c expansion layer', () => { const cmd = buildSpawnCommand({ mode: 'shell', sessionId: 'abc123de-0000-0000-0000-000000000000' }); // Mirrors tmux-manager: `… bash -c ${JSON.stringify(launchCmd)}` handed to `sh -c`.