@@ -75,48 +75,66 @@ function getWindowsAncestors(startPid, maxDepth = 4) {
7575 const safePid = parseInt ( startPid , 10 ) ;
7676 if ( ! Number . isFinite ( safePid ) || safePid <= 0 ) return names ;
7777
78+ // Strategy 1: wmic (available on older Windows builds)
7879 try {
7980 const result = execFileSync (
8081 'wmic' ,
8182 [ 'process' , 'get' , 'Name,ParentProcessId,ProcessId' , '/format:csv' ] ,
8283 { stdio : [ 'pipe' , 'pipe' , 'ignore' ] , encoding : 'utf8' , timeout : 5000 , windowsHide : true } ,
8384 ) ;
8485
85- // Parse CSV output — first non-empty line is the header
8686 const lines = result . split ( / \r ? \n / ) . filter ( ( l ) => l . trim ( ) ) ;
87- if ( lines . length === 0 ) return names ;
88-
89- const header = lines [ 0 ] . split ( ',' ) . map ( ( h ) => h . trim ( ) ) ;
90- const nameIdx = header . indexOf ( 'Name' ) ;
91- const pidIdx = header . indexOf ( 'ProcessId' ) ;
92- const ppidIdx = header . indexOf ( 'ParentProcessId' ) ;
93- if ( nameIdx === - 1 || pidIdx === - 1 || ppidIdx === - 1 ) return names ;
94-
95- const processes = new Map ( ) ;
96- for ( let i = 1 ; i < lines . length ; i ++ ) {
97- const cols = lines [ i ] . split ( ',' ) ;
98- if ( cols . length <= Math . max ( nameIdx , pidIdx , ppidIdx ) ) continue ;
99- const pid = parseInt ( cols [ pidIdx ] , 10 ) ;
100- if ( Number . isFinite ( pid ) ) {
101- processes . set ( pid , {
102- name : cols [ nameIdx ] . trim ( ) . toLowerCase ( ) ,
103- ppid : parseInt ( cols [ ppidIdx ] , 10 ) ,
104- } ) ;
87+ if ( lines . length > 0 ) {
88+ const header = lines [ 0 ] . split ( ',' ) . map ( ( h ) => h . trim ( ) ) ;
89+ const nameIdx = header . indexOf ( 'Name' ) ;
90+ const pidIdx = header . indexOf ( 'ProcessId' ) ;
91+ const ppidIdx = header . indexOf ( 'ParentProcessId' ) ;
92+ if ( nameIdx !== - 1 && pidIdx !== - 1 && ppidIdx !== - 1 ) {
93+ const processes = new Map ( ) ;
94+ for ( let i = 1 ; i < lines . length ; i ++ ) {
95+ const cols = lines [ i ] . split ( ',' ) ;
96+ if ( cols . length <= Math . max ( nameIdx , pidIdx , ppidIdx ) ) continue ;
97+ const pid = parseInt ( cols [ pidIdx ] , 10 ) ;
98+ if ( Number . isFinite ( pid ) ) {
99+ processes . set ( pid , {
100+ name : cols [ nameIdx ] . trim ( ) . toLowerCase ( ) ,
101+ ppid : parseInt ( cols [ ppidIdx ] , 10 ) ,
102+ } ) ;
103+ }
104+ }
105+
106+ let currentPid = safePid ;
107+ for ( let i = 0 ; i < maxDepth ; i ++ ) {
108+ const proc = processes . get ( currentPid ) ;
109+ if ( ! proc ) break ;
110+ log . debug ( `Process tree: ${ proc . name } ` ) ;
111+ names . push ( proc . name ) ;
112+ if ( ! Number . isFinite ( proc . ppid ) || proc . ppid === 0 || proc . ppid === currentPid ) break ;
113+ currentPid = proc . ppid ;
114+ }
115+ if ( names . length > 0 ) return names ;
105116 }
106117 }
118+ } catch ( err ) {
119+ log . debug ( `wmic not available: ${ err . message } ` ) ;
120+ }
107121
108- // Walk up the tree in memory — no more subprocess calls
109- let currentPid = safePid ;
110- for ( let i = 0 ; i < maxDepth ; i ++ ) {
111- const proc = processes . get ( currentPid ) ;
112- if ( ! proc ) break ;
113- log . debug ( `Process tree: ${ proc . name } ` ) ;
114- names . push ( proc . name ) ;
115- if ( ! Number . isFinite ( proc . ppid ) || proc . ppid === 0 || proc . ppid === currentPid ) break ;
116- currentPid = proc . ppid ;
122+ // Strategy 2: tasklist for direct parent name (always available on Windows)
123+ try {
124+ const result = execFileSync ( 'tasklist' , [ '/FI' , `PID eq ${ safePid } ` , '/FO' , 'CSV' , '/NH' ] , {
125+ stdio : [ 'pipe' , 'pipe' , 'ignore' ] ,
126+ encoding : 'utf8' ,
127+ timeout : 5000 ,
128+ windowsHide : true ,
129+ } ) ;
130+ const match = result . match ( / " ( [ ^ " ] + ) " / ) ;
131+ if ( match ) {
132+ const name = match [ 1 ] . toLowerCase ( ) ;
133+ log . debug ( `Process tree (tasklist): ${ name } ` ) ;
134+ names . push ( name ) ;
117135 }
118136 } catch ( err ) {
119- log . debug ( `Could not query process tree : ${ err . message } ` ) ;
137+ log . debug ( `tasklist failed : ${ err . message } ` ) ;
120138 }
121139
122140 return names ;
@@ -188,6 +206,19 @@ function getDefaultShell() {
188206 log . debug ( `Using detected shell: cmd.exe` ) ;
189207 return 'cmd.exe' ;
190208 }
209+
210+ // Heuristic: PSModulePath env var is set by PowerShell and inherited by children.
211+ // When the tree walk only found intermediaries (node.exe), this detects the real shell.
212+ const psModulePath = ( process . env . PSModulePath || '' ) . toLowerCase ( ) ;
213+ if ( psModulePath . includes ( '\\powershell\\' ) ) {
214+ log . debug ( 'Detected pwsh.exe via PSModulePath' ) ;
215+ return 'pwsh.exe' ;
216+ }
217+ if ( psModulePath . includes ( '\\windowspowershell\\' ) ) {
218+ log . debug ( 'Detected powershell.exe via PSModulePath' ) ;
219+ return 'powershell.exe' ;
220+ }
221+
191222 const fallback = process . env . COMSPEC || 'cmd.exe' ;
192223 log . debug ( `Falling back to: ${ fallback } ` ) ;
193224 return fallback ;
0 commit comments