source dump of claude code
at main 128 lines 4.5 kB view raw
1import { env } from '../../../utils/env.js' 2import { execFileNoThrow } from '../../../utils/execFileNoThrow.js' 3import { TMUX_COMMAND } from '../constants.js' 4 5/** 6 * Captured at module load time to detect if the user started Claude from within tmux. 7 * Shell.ts may override TMUX env var later, so we capture the original value. 8 */ 9// eslint-disable-next-line custom-rules/no-process-env-top-level 10const ORIGINAL_USER_TMUX = process.env.TMUX 11 12/** 13 * Captured at module load time to get the leader's tmux pane ID. 14 * TMUX_PANE is set by tmux to the pane ID (e.g., %0, %1) when a process runs inside tmux. 15 * We capture this at startup so we always know the leader's original pane, even if 16 * the user switches to a different pane later. 17 */ 18// eslint-disable-next-line custom-rules/no-process-env-top-level 19const ORIGINAL_TMUX_PANE = process.env.TMUX_PANE 20 21/** Cached result for isInsideTmux */ 22let isInsideTmuxCached: boolean | null = null 23 24/** Cached result for isInITerm2 */ 25let isInITerm2Cached: boolean | null = null 26 27/** 28 * Checks if we're currently running inside a tmux session (synchronous version). 29 * Uses the original TMUX value captured at module load, not process.env.TMUX, 30 * because Shell.ts overrides TMUX when Claude's socket is initialized. 31 * 32 * IMPORTANT: We ONLY check the TMUX env var. We do NOT run `tmux display-message` 33 * as a fallback because that command will succeed if ANY tmux server is running 34 * on the system, not just if THIS process is inside tmux. 35 */ 36export function isInsideTmuxSync(): boolean { 37 return !!ORIGINAL_USER_TMUX 38} 39 40/** 41 * Checks if we're currently running inside a tmux session. 42 * Uses the original TMUX value captured at module load, not process.env.TMUX, 43 * because Shell.ts overrides TMUX when Claude's socket is initialized. 44 * Caches the result since this won't change during the process lifetime. 45 * 46 * IMPORTANT: We ONLY check the TMUX env var. We do NOT run `tmux display-message` 47 * as a fallback because that command will succeed if ANY tmux server is running 48 * on the system, not just if THIS process is inside tmux. 49 */ 50export async function isInsideTmux(): Promise<boolean> { 51 if (isInsideTmuxCached !== null) { 52 return isInsideTmuxCached 53 } 54 55 // Check the original TMUX env var (captured at module load) 56 // This tells us if the user started Claude from within their tmux session 57 // If TMUX is not set, we are NOT inside tmux - period. 58 isInsideTmuxCached = !!ORIGINAL_USER_TMUX 59 return isInsideTmuxCached 60} 61 62/** 63 * Gets the leader's tmux pane ID captured at module load. 64 * Returns null if not running inside tmux. 65 */ 66export function getLeaderPaneId(): string | null { 67 return ORIGINAL_TMUX_PANE || null 68} 69 70/** 71 * Checks if tmux is available on the system (installed and in PATH). 72 */ 73export async function isTmuxAvailable(): Promise<boolean> { 74 const result = await execFileNoThrow(TMUX_COMMAND, ['-V']) 75 return result.code === 0 76} 77 78/** 79 * Checks if we're currently running inside iTerm2. 80 * Uses multiple detection methods: 81 * 1. TERM_PROGRAM env var set to "iTerm.app" 82 * 2. ITERM_SESSION_ID env var is present 83 * 3. env.terminal detection from utils/env.ts 84 * 85 * Caches the result since this won't change during the process lifetime. 86 * 87 * Note: iTerm2 backend uses AppleScript (osascript) which is built into macOS, 88 * so no external CLI tool installation is required. 89 */ 90export function isInITerm2(): boolean { 91 if (isInITerm2Cached !== null) { 92 return isInITerm2Cached 93 } 94 95 // Check multiple indicators for iTerm2 96 const termProgram = process.env.TERM_PROGRAM 97 const hasItermSessionId = !!process.env.ITERM_SESSION_ID 98 const terminalIsITerm = env.terminal === 'iTerm.app' 99 100 isInITerm2Cached = 101 termProgram === 'iTerm.app' || hasItermSessionId || terminalIsITerm 102 103 return isInITerm2Cached 104} 105 106/** 107 * The it2 CLI command name. 108 */ 109export const IT2_COMMAND = 'it2' 110 111/** 112 * Checks if the it2 CLI tool is available AND can reach the iTerm2 Python API. 113 * Uses 'session list' (not '--version') because --version succeeds even when 114 * the Python API is disabled in iTerm2 preferences — which would cause 115 * 'session split' to fail later with no fallback. 116 */ 117export async function isIt2CliAvailable(): Promise<boolean> { 118 const result = await execFileNoThrow(IT2_COMMAND, ['session', 'list']) 119 return result.code === 0 120} 121 122/** 123 * Resets all cached detection results. Used for testing. 124 */ 125export function resetDetectionCache(): void { 126 isInsideTmuxCached = null 127 isInITerm2Cached = null 128}