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).
|
||||
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`
|
||||
// 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 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.
|
||||
const pty: IPty = spawn(file, args, {
|
||||
|
||||
Reference in New Issue
Block a user