fix(session): spawn PTY as login shell so hooks find nvm node
Spawn the shell with -l (POSIX only) so it loads the full profile chain and rebuilds PATH. Without it, a shell spawned by a GUI-launched app or a long-lived tmux keepalive server inherits a minimal PATH, so nvm-managed node (only on the login PATH) is missing and Claude Code hooks fail with 'node: command not found'. PowerShell (Windows) has no -l; skipped there. Applies to both the tmux new-session command and the direct-spawn path.
This commit is contained in:
@@ -81,10 +81,23 @@ export function createSession(
|
|||||||
// event belongs to ($WEBTERM_SESSION) and where to POST ($WEBTERM_HOOK_URL, H2).
|
// event belongs to ($WEBTERM_SESSION) and where to POST ($WEBTERM_HOOK_URL, H2).
|
||||||
const tName = cfg.useTmux ? tmuxName(id) : null;
|
const tName = cfg.useTmux ? tmuxName(id) : null;
|
||||||
|
|
||||||
|
// Spawn the shell as a LOGIN shell (POSIX only) so it loads the full profile
|
||||||
|
// chain (/etc/zprofile, ~/.zprofile, ~/.zshrc, …) and rebuilds PATH — exactly
|
||||||
|
// like Terminal.app / iTerm. Without `-l`, a shell spawned by a GUI-launched
|
||||||
|
// app (Finder / login-item) or a long-lived tmux keepalive server inherits a
|
||||||
|
// minimal PATH, so nvm-managed `node` (only on the login PATH) is missing and
|
||||||
|
// Claude Code hooks fail with "node: command not found". PowerShell (Windows)
|
||||||
|
// has no `-l`; skip it there (Windows also forces tmux off — server-config.ts).
|
||||||
|
const loginArgs = process.platform === 'win32' ? [] : ['-l'];
|
||||||
|
|
||||||
// H1: under tmux, the node-pty process is a tmux CLIENT; `new-session -A`
|
// H1: under tmux, the node-pty process is a tmux CLIENT; `new-session -A`
|
||||||
// attaches to web_<id> if it exists (restart survival) or creates it.
|
// attaches to web_<id> if it exists (restart survival) or creates it. The
|
||||||
|
// shell-command (shellPath + login flag) is the tmux session's root process.
|
||||||
const file = tName !== null ? 'tmux' : cfg.shellPath;
|
const file = tName !== null ? 'tmux' : cfg.shellPath;
|
||||||
const args = tName !== null ? ['new-session', '-A', '-s', tName, cfg.shellPath] : [];
|
const args =
|
||||||
|
tName !== null
|
||||||
|
? ['new-session', '-A', '-s', tName, cfg.shellPath, ...loginArgs]
|
||||||
|
: loginArgs;
|
||||||
|
|
||||||
// M4: let a spawn failure (e.g. missing shell / tmux) propagate synchronously.
|
// M4: let a spawn failure (e.g. missing shell / tmux) propagate synchronously.
|
||||||
const pty: IPty = spawn(file, args, {
|
const pty: IPty = spawn(file, args, {
|
||||||
|
|||||||
Reference in New Issue
Block a user