@@ -22,6 +22,7 @@ vi.mock("node:child_process", async () => {
2222
2323import {
2424 clearExecutablePathCache ,
25+ extractWindowsCmdShimScript ,
2526 getRefreshedWindowsPath ,
2627 invalidateExecutablePathCache ,
2728 resolveExecutablePath ,
@@ -186,6 +187,42 @@ describe.skipIf(process.platform !== "win32")("Windows executable path fallback"
186187 expect ( resolveExecutablePath ( "command-code" ) ) . toBe ( cmdPath ) ;
187188 } ) ;
188189
190+ it ( "keeps the .cmd path for npm shims with an extensionless bin script (e.g. grok.cmd)" , ( ) => {
191+ // Regression: @xai -official/grok's npm bin entry has no .js extension
192+ // (`"%_prog%" "%dp0%\node_modules\@xai-official\grok\bin\grok" %*`), so
193+ // the .js-only shim guard missed it and the exe substitution matched the
194+ // shim's `IF EXIST "%dp0%\node.exe"` line — resolving `grok` to node.exe.
195+ // Detection then read node's version and the ACP probe spawned
196+ // `node.exe agent stdio`, breaking version, models, and account info.
197+ const root = mkdtempSync ( join ( tmpdir ( ) , "poracode-grok-shim-" ) ) ;
198+ tempDirs . push ( root ) ;
199+ const cmdPath = join ( root , "grok.cmd" ) ;
200+ const nodeExePath = join ( root , "node.exe" ) ;
201+ writeFileSync ( nodeExePath , "" ) ;
202+ writeFileSync (
203+ cmdPath ,
204+ [
205+ "@ECHO off" ,
206+ "SETLOCAL" ,
207+ 'IF EXIST "%dp0%\\node.exe" (' ,
208+ ' SET "_prog=%dp0%\\node.exe"' ,
209+ ") ELSE (" ,
210+ ' SET "_prog=node"' ,
211+ ")" ,
212+ 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\node_modules\\@xai-official\\grok\\bin\\grok" %*' ,
213+ "" ,
214+ ] . join ( "\r\n" ) ,
215+ ) ;
216+ spawnSyncMock . mockReturnValueOnce ( {
217+ error : undefined ,
218+ status : 0 ,
219+ stdout : [ join ( root , "grok" ) , cmdPath ] . join ( "\r\n" ) ,
220+ stderr : "" ,
221+ } ) ;
222+
223+ expect ( resolveExecutablePath ( "grok" ) ) . toBe ( cmdPath ) ;
224+ } ) ;
225+
189226 it ( "applies the same fallback to async resolution" , async ( ) => {
190227 execFileAsyncMock . mockRejectedValueOnce ( new Error ( "not found" ) ) . mockResolvedValueOnce ( {
191228 stdout : "C:\\Users\\demo\\scoop\\shims\\opencode.exe\r\n" ,
@@ -275,3 +312,20 @@ describe.skipIf(process.platform !== "win32")("Windows executable path fallback"
275312 expect ( getRefreshedWindowsPath ( ) ) . toContain ( "C:\\Users\\demo\\.local\\bin" ) ;
276313 } ) ;
277314} ) ;
315+
316+ describe ( "extractWindowsCmdShimScript" , ( ) => {
317+ it ( "extracts .js/.mjs script entries" , ( ) => {
318+ const body = '"%dp0%\\node.exe" "%dp0%\\node_modules\\command-code\\dist\\index.mjs" %*' ;
319+ expect ( extractWindowsCmdShimScript ( body ) ) . toBe ( "node_modules\\command-code\\dist\\index.mjs" ) ;
320+ } ) ;
321+
322+ it ( "extracts extensionless %_prog% bin scripts" , ( ) => {
323+ const body = '"%_prog%" "%dp0%\\node_modules\\@xai-official\\grok\\bin\\grok" %*' ;
324+ expect ( extractWindowsCmdShimScript ( body ) ) . toBe ( "node_modules\\@xai-official\\grok\\bin\\grok" ) ;
325+ } ) ;
326+
327+ it ( "returns undefined for exe-wrapping shims" , ( ) => {
328+ const body = '"%dp0%\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe" %*' ;
329+ expect ( extractWindowsCmdShimScript ( body ) ) . toBeUndefined ( ) ;
330+ } ) ;
331+ } ) ;
0 commit comments