detect-terminal
Version:
Detect the terminal program currently being used, with support for iTerm, Terminal.app, Hyper, iTerm2, ConEmu, Cmde,r Alacritty, Xterm, Terminator, Termux, Kitty, and others. Detection is based on environment variables and process-level indicators to iden
1 lines • 10 kB
Source Map (JSON)
{"version":3,"sources":["../index.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport path from 'node:path';\n\n/**\n * Attempt to detect the terminal using environment variables.\n */\n\nexport const detectFromEnv = (): string | null => {\n const platform = process.platform;\n\n // Android/Termux special case\n if (platform === 'android' && process.env.TERMUX_VERSION) {\n return 'termux';\n }\n\n // Check TERM_PROGRAM (reliable for many modern terminals)\n let termProgram = process.env.TERM_PROGRAM?.trim()?.toLowerCase();\n\n // macOS: extract app name from full path\n if (platform === 'darwin' && termProgram) {\n termProgram = path.parse(termProgram).name;\n }\n\n if (termProgram) {\n switch (termProgram) {\n case 'apple_terminal': return 'terminal';\n case 'eterm': return 'eterm';\n case 'gnome-terminal': return 'gnome_terminal';\n case 'gnome-terminal-server': return 'gnome_terminal';\n case 'hyper': return 'hyper';\n case 'iterm.app': return 'iterm';\n case 'iterm': return 'iterm';\n case 'iterm2': return 'iterm';\n case 'kitty': return 'kitty';\n case 'konsole': return 'konsole';\n case 'mate-terminal': return 'mate_terminal';\n case 'powershell': return 'powershell';\n case 'putty': return 'putty';\n case 'qterminal': return 'qterminal';\n case 'rxvt': return 'rxvt';\n case 'terminal.app': return 'terminal_app';\n case 'terminal': return 'terminal';\n case 'terminator': return 'terminator';\n case 'termux': return 'termux';\n case 'vscode': return 'vscode';\n case 'warp': return 'warp';\n case 'wezterm': return 'wezterm';\n case 'xfce4-terminal': return 'xfce4_terminal';\n case 'alacritty': return 'alacritty';\n default: break;\n }\n // Unrecognized: sanitize to a string identifier\n return termProgram.replace(/[^a-z0-9]+/g, '_');\n }\n\n // Check for VSCode environment (alternative detection)\n if (\n typeof process.env.VSCODE_PID !== 'undefined' ||\n (typeof process.env.TERM_PROGRAM_VERSION !== 'undefined' && /vscode/i.test(process.env.TERM_PROGRAM_VERSION))\n ) {\n return 'vscode';\n }\n\n // Fallback: $TERM variable (common in UNIX)\n const term = process.env.TERM?.trim()?.toLowerCase();\n if (term && term !== 'unknown') {\n // Special handling for xterm (often misidentified)\n if (term === 'xterm' || term === 'xterm-256color') {\n // Check VTE_VERSION for GNOME Terminal detection\n if (process.env.VTE_VERSION) {\n const vteVersion = parseInt(process.env.VTE_VERSION, 10);\n if (vteVersion >= 3803) {\n return 'gnome_terminal';\n }\n }\n\n // Check for Konsole via environment variables\n for (const [key] of Object.entries(process.env)) {\n if (/konsole/i.test(key)) {\n return 'konsole';\n }\n }\n\n // macOS terminals often advertise as xterm\n if (platform === 'darwin') {\n return 'terminal';\n }\n\n return 'xterm';\n }\n\n // Handle other terminal types\n if (/screen/.test(term)) return 'screen';\n if (/tmux/.test(term)) return 'tmux';\n if (/rxvt/.test(term)) return 'rxvt';\n if (/vt100/.test(term)) return 'vt100';\n if (/linux/.test(term)) return 'linux_console';\n if (/alacritty/.test(term)) return 'alacritty';\n if (/dopamine/.test(term)) return 'dopamine';\n if (/kitty/.test(term)) return 'kitty';\n if (/ghostty/.test(term)) return 'ghostty';\n\n // Fallback: sanitize TERM value\n return term.replace(/[^a-z0-9]+/g, '_');\n }\n\n // Fallback to COLORTERM\n const colorTerm = process.env.COLORTERM?.trim()?.toLowerCase();\n if (colorTerm === 'truecolor' || colorTerm === '24bit') {\n return 'truecolor_terminal';\n }\n\n if (colorTerm) {\n return colorTerm.replace(/[^a-z0-9]+/g, '_');\n }\n\n return null;\n};\n\n/**\n * Attempt to detect the terminal using the $TERM variable via shell,\n * fallback: only if absolutely needed.\n */\n\nexport const detectFromShell = (): string | null => {\n // Only for environments where process.env.TERM is missing/unusable.\n try {\n const isWindows = process.platform === 'win32';\n if (isWindows) {\n // Check for Windows Terminal first\n if (process.env.WT_SESSION) return 'windows_terminal';\n\n const shell = process.env.COMSPEC?.toLowerCase() || '';\n if (/powershell/i.test(shell)) return 'powershell';\n if (/pwsh/i.test(shell)) return 'powershell';\n if (/cmd\\.exe/i.test(shell)) return 'cmd';\n if (/wt\\.exe/i.test(shell)) return 'windows_terminal';\n if (/conhost\\.exe/i.test(shell)) return 'conhost';\n return 'windows_cmd';\n }\n\n // For Unix: check $TERM from shell as a last resort\n const terminal = execSync('echo $TERM', {\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore']\n }).trim().toLowerCase();\n\n return terminal ? terminal.replace(/[^a-z0-9]+/g, '_') : null;\n } catch {\n return null;\n }\n};\n\n/**\n * Attempt to detect the shell/terminal from the process title.\n */\n\nexport const detectFromProcessTitle = (): string | null => {\n const processTitle = process.title?.toLowerCase() ?? '';\n\n // Terminal applications\n if (/^alacritty/.test(processTitle)) return 'alacritty';\n if (/^kitty/.test(processTitle)) return 'kitty';\n if (/^wezterm/.test(processTitle)) return 'wezterm';\n if (/^hyper/.test(processTitle)) return 'hyper';\n\n // Shell names\n if (/bash/.test(processTitle)) return 'bash';\n if (/zsh/.test(processTitle)) return 'zsh';\n if (/ksh/.test(processTitle)) return 'ksh';\n if (/fish/.test(processTitle)) return 'fish';\n if (/csh/.test(processTitle)) return 'csh';\n if (/tcsh/.test(processTitle)) return 'tcsh';\n if (/pwsh/.test(processTitle)) return 'powershell';\n if (/powershell/.test(processTitle)) return 'powershell';\n if (/cmd/.test(processTitle)) return 'cmd';\n if (/sh$/.test(processTitle)) return 'sh';\n\n // \"node\" is not a terminal, but could happen\n if (/^node/.test(processTitle)) return 'node';\n\n return null;\n};\n\n/**\n * Heuristic to detect terminal as reliably as possible, with wide support.\n */\n\nexport const detectTerminal = (): string => {\n let terminal = detectFromEnv();\n if (!terminal) {\n terminal = detectFromShell();\n }\n if (!terminal) {\n terminal = detectFromProcessTitle();\n }\n return terminal || 'unknown';\n};\n\nexport default detectTerminal;\n"],"mappings":"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","names":[]}