From 0f415eafda3decc351a0c236915a67cafc07890b Mon Sep 17 00:00:00 2001 From: K7 Date: Sat, 14 Feb 2026 00:04:54 -0800 Subject: [PATCH] fix: resolve posix_spawnp and nested session failures on macOS Two issues prevented Claude from spawning in certain environments: 1. node-pty's spawn-helper binary loses execute permission during npm install, causing posix_spawnp to fail on every PTY spawn. Fixed by adding chmod in postinstall. 2. When Claudeman is launched from within a Claude Code session, the CLAUDECODE env var leaks into child processes, causing Claude CLI to refuse to start ("cannot be launched inside another Claude Code session"). Fixed by unsetting CLAUDECODE in both tmux and direct PTY spawn paths. --- scripts/postinstall.js | 25 +++++++++++++++++++++++-- src/session.ts | 25 ++++++++++++++----------- src/tmux-manager.ts | 1 + 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/scripts/postinstall.js b/scripts/postinstall.js index 49b0f54..8ebc6be 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -6,9 +6,10 @@ */ import { execSync } from 'child_process'; -import { existsSync } from 'fs'; +import { chmodSync, existsSync } from 'fs'; import { homedir, platform } from 'os'; -import { join } from 'path'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; // ============================================================================ // Configuration @@ -96,6 +97,26 @@ function getTmuxInstallInstructions() { console.log(colors.bold('Claudeman postinstall check...')); console.log(''); +// ---------------------------------------------------------------------------- +// 0. Fix node-pty spawn-helper permissions (required on macOS/Linux) +// ---------------------------------------------------------------------------- + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const spawnHelperPaths = [ + join(__dirname, '..', 'node_modules', 'node-pty', 'prebuilds', 'darwin-arm64', 'spawn-helper'), + join(__dirname, '..', 'node_modules', 'node-pty', 'prebuilds', 'darwin-x64', 'spawn-helper'), +]; + +for (const helperPath of spawnHelperPaths) { + if (existsSync(helperPath)) { + try { + chmodSync(helperPath, 0o755); + } catch { + // Non-critical on platforms where this path doesn't apply + } + } +} + let hasWarnings = false; let hasErrors = false; diff --git a/src/session.ts b/src/session.ts index 52bf074..bd14a6c 100644 --- a/src/session.ts +++ b/src/session.ts @@ -868,7 +868,7 @@ export class Session extends EventEmitter { cols: 120, rows: 40, cwd: this.workingDir, - env: { ...process.env, TERM: 'xterm-256color' }, + env: (() => { const e: Record = { ...process.env, TERM: 'xterm-256color' }; delete e.CLAUDECODE; return e; })(), }); // Set claudeSessionId immediately since we passed --session-id to Claude @@ -934,15 +934,18 @@ export class Session extends EventEmitter { cols: 120, rows: 40, cwd: this.workingDir, - env: { - ...process.env, - PATH: getAugmentedPath(), - TERM: 'xterm-256color', - // Inform Claude it's running within Claudeman (helps prevent self-termination) - CLAUDEMAN_SCREEN: '1', - CLAUDEMAN_SESSION_ID: this.id, - CLAUDEMAN_API_URL: process.env.CLAUDEMAN_API_URL || 'http://localhost:3000', - }, + env: (() => { + const env: Record = { + ...process.env, + PATH: getAugmentedPath(), + TERM: 'xterm-256color', + CLAUDEMAN_SCREEN: '1', + CLAUDEMAN_SESSION_ID: this.id, + CLAUDEMAN_API_URL: process.env.CLAUDEMAN_API_URL || 'http://localhost:3000', + }; + delete env.CLAUDECODE; + return env; + })(), }); } catch (spawnErr) { console.error('[Session] Failed to spawn Claude PTY:', spawnErr); @@ -1146,7 +1149,7 @@ export class Session extends EventEmitter { cols: 120, rows: 40, cwd: this.workingDir, - env: { ...process.env, TERM: 'xterm-256color' }, + env: (() => { const e: Record = { ...process.env, TERM: 'xterm-256color' }; delete e.CLAUDECODE; return e; })(), }); } catch (spawnErr) { console.error('[Session] Failed to spawn PTY for shell mux attachment:', spawnErr); diff --git a/src/tmux-manager.ts b/src/tmux-manager.ts index 11df98a..9058000 100644 --- a/src/tmux-manager.ts +++ b/src/tmux-manager.ts @@ -256,6 +256,7 @@ export class TmuxManager extends EventEmitter implements TerminalMultiplexer { const pathExport = claudeDir ? `export PATH="${claudeDir}:$PATH" && ` : ''; const envExports = [ + 'unset CLAUDECODE', 'export CLAUDEMAN_SCREEN=1', `export CLAUDEMAN_SESSION_ID=${sessionId}`, `export CLAUDEMAN_SCREEN_NAME=${muxName}`,