11import { spawn } from 'child_process' ;
2+ import path from 'path' ;
23import { TerminalSession , CommandExecutionResult , ActiveSession , TimingInfo , OutputEvent } from './types.js' ;
34import { DEFAULT_COMMAND_TIMEOUT } from './config.js' ;
45import { configManager } from './config-manager.js' ;
@@ -13,6 +14,76 @@ interface CompletedSession {
1314 endTime : Date ;
1415}
1516
17+ /**
18+ * Configuration for spawning a shell with appropriate flags
19+ */
20+ interface ShellSpawnConfig {
21+ executable : string ;
22+ args : string [ ] ;
23+ useShellOption : string | boolean ;
24+ }
25+
26+ /**
27+ * Get the appropriate spawn configuration for a given shell
28+ * This handles login shell flags for different shell types
29+ */
30+ function getShellSpawnArgs ( shellPath : string , command : string ) : ShellSpawnConfig {
31+ const shellName = path . basename ( shellPath ) . toLowerCase ( ) ;
32+
33+ // Unix shells with login flag support
34+ if ( shellName . includes ( 'bash' ) || shellName . includes ( 'zsh' ) ) {
35+ return {
36+ executable : shellPath ,
37+ args : [ '-l' , '-c' , command ] ,
38+ useShellOption : false
39+ } ;
40+ }
41+
42+ // PowerShell Core (cross-platform, supports -Login)
43+ if ( shellName === 'pwsh' || shellName === 'pwsh.exe' ) {
44+ return {
45+ executable : shellPath ,
46+ args : [ '-Login' , '-Command' , command ] ,
47+ useShellOption : false
48+ } ;
49+ }
50+
51+ // Windows PowerShell 5.1 (no login flag support)
52+ if ( shellName === 'powershell' || shellName === 'powershell.exe' ) {
53+ return {
54+ executable : shellPath ,
55+ args : [ '-Command' , command ] ,
56+ useShellOption : false
57+ } ;
58+ }
59+
60+ // CMD
61+ if ( shellName === 'cmd' || shellName === 'cmd.exe' ) {
62+ return {
63+ executable : shellPath ,
64+ args : [ '/c' , command ] ,
65+ useShellOption : false
66+ } ;
67+ }
68+
69+ // Fish shell (uses -l for login, -c for command)
70+ if ( shellName . includes ( 'fish' ) ) {
71+ return {
72+ executable : shellPath ,
73+ args : [ '-l' , '-c' , command ] ,
74+ useShellOption : false
75+ } ;
76+ }
77+
78+ // Unknown/other shells - use shell option for safety
79+ // This provides a fallback for shells we don't explicitly handle
80+ return {
81+ executable : command ,
82+ args : [ ] ,
83+ useShellOption : shellPath
84+ } ;
85+ }
86+
1687export class TerminalManager {
1788 private sessions : Map < number , TerminalSession > = new Map ( ) ;
1889 private completedSessions : Map < number , CompletedSession > = new Map ( ) ;
@@ -66,29 +137,42 @@ export class TerminalManager {
66137 console . log ( `Enhanced SSH command: ${ enhancedCommand } ` ) ;
67138 }
68139
69- // Wrap command to run in login shell if using bash/zsh to get full PATH
70- let finalCommand = enhancedCommand ;
71- let finalShell : string | boolean = shellToUse ;
140+ // Get the appropriate spawn configuration for the shell
141+ let spawnConfig : ShellSpawnConfig ;
142+ let spawnOptions : any ;
72143
73- if ( typeof shellToUse === 'string' && shellToUse !== 'powershell.exe' ) {
74- // For bash/zsh, wrap the command to run as login shell
75- // This ensures PATH is loaded from profile files (like .zprofile, .bash_profile)
76- if ( shellToUse . includes ( 'bash' ) || shellToUse . includes ( 'zsh' ) ) {
77- finalCommand = `${ shellToUse } -l -c ${ JSON . stringify ( enhancedCommand ) } ` ;
78- finalShell = true ; // Use system shell to execute the wrapped command
144+ if ( typeof shellToUse === 'string' ) {
145+ // Use shell-specific configuration with login flags where appropriate
146+ spawnConfig = getShellSpawnArgs ( shellToUse , enhancedCommand ) ;
147+ spawnOptions = {
148+ env : {
149+ ...process . env ,
150+ TERM : 'xterm-256color' // Better terminal compatibility
151+ }
152+ } ;
153+
154+ // Add shell option if needed (for unknown shells)
155+ if ( spawnConfig . useShellOption ) {
156+ spawnOptions . shell = spawnConfig . useShellOption ;
79157 }
158+ } else {
159+ // Boolean or undefined shell - use default shell option behavior
160+ spawnConfig = {
161+ executable : enhancedCommand ,
162+ args : [ ] ,
163+ useShellOption : shellToUse
164+ } ;
165+ spawnOptions = {
166+ shell : shellToUse ,
167+ env : {
168+ ...process . env ,
169+ TERM : 'xterm-256color'
170+ }
171+ } ;
80172 }
81173
82- const spawnOptions : any = {
83- shell : finalShell ,
84- env : {
85- ...process . env ,
86- TERM : 'xterm-256color' // Better terminal compatibility
87- }
88- } ;
89-
90- // Spawn the process with an empty array of arguments and our options
91- const childProcess = spawn ( finalCommand , [ ] , spawnOptions ) ;
174+ // Spawn the process with appropriate arguments
175+ const childProcess = spawn ( spawnConfig . executable , spawnConfig . args , spawnOptions ) ;
92176 let output = '' ;
93177
94178 // Ensure childProcess.pid is defined before proceeding
0 commit comments