@@ -9,6 +9,10 @@ import { setTimeout as sleep } from "node:timers/promises"
99const SIGKILL_TIMEOUT_MS = 200
1010
1111export namespace Shell {
12+ const BLACKLIST = new Set ( [ "fish" , "nu" ] )
13+ const LOGIN = new Set ( [ "bash" , "dash" , "fish" , "ksh" , "sh" , "zsh" ] )
14+ const POSIX = new Set ( [ "bash" , "dash" , "ksh" , "sh" , "zsh" ] )
15+
1216 export async function killTree ( proc : ChildProcess , opts ?: { exited ?: ( ) => boolean } ) : Promise < void > {
1317 const pid = proc . pid
1418 if ( ! pid || opts ?. exited ?.( ) ) return
@@ -39,18 +43,46 @@ export namespace Shell {
3943 }
4044 }
4145 }
42- const BLACKLIST = new Set ( [ "fish" , "nu" ] )
46+
47+ function full ( file : string ) {
48+ if ( process . platform !== "win32" ) return file
49+ const shell = Filesystem . windowsPath ( file )
50+ if ( path . win32 . dirname ( shell ) !== "." ) {
51+ if ( shell . startsWith ( "/" ) && name ( shell ) === "bash" ) return gitbash ( ) || shell
52+ return shell
53+ }
54+ return Bun . which ( shell ) || shell
55+ }
56+
57+ function pick ( ) {
58+ const pwsh = Bun . which ( "pwsh" )
59+ if ( pwsh ) return pwsh
60+ const powershell = Bun . which ( "powershell" )
61+ if ( powershell ) return powershell
62+ }
63+
64+ function select ( file : string | undefined , opts ?: { acceptable ?: boolean } ) {
65+ if ( file && ( ! opts ?. acceptable || ! BLACKLIST . has ( name ( file ) ) ) ) return full ( file )
66+ if ( process . platform === "win32" ) {
67+ const shell = pick ( )
68+ if ( shell ) return shell
69+ }
70+ return fallback ( )
71+ }
72+
73+ export function gitbash ( ) {
74+ if ( process . platform !== "win32" ) return
75+ if ( Flag . OPENCODE_GIT_BASH_PATH ) return Flag . OPENCODE_GIT_BASH_PATH
76+ const git = which ( "git" )
77+ if ( ! git ) return
78+ const file = path . join ( git , ".." , ".." , "bin" , "bash.exe" )
79+ if ( Filesystem . stat ( file ) ?. size ) return file
80+ }
4381
4482 function fallback ( ) {
4583 if ( process . platform === "win32" ) {
46- if ( Flag . OPENCODE_GIT_BASH_PATH ) return Flag . OPENCODE_GIT_BASH_PATH
47- const git = which ( "git" )
48- if ( git ) {
49- // git.exe is typically at: C:\Program Files\Git\cmd\git.exe
50- // bash.exe is at: C:\Program Files\Git\bin\bash.exe
51- const bash = path . join ( git , ".." , ".." , "bin" , "bash.exe" )
52- if ( Filesystem . stat ( bash ) ?. size ) return bash
53- }
84+ const file = gitbash ( )
85+ if ( file ) return file
5486 return process . env . COMSPEC || "cmd.exe"
5587 }
5688 if ( process . platform === "darwin" ) return "/bin/zsh"
@@ -59,15 +91,20 @@ export namespace Shell {
5991 return "/bin/sh"
6092 }
6193
62- export const preferred = lazy ( ( ) => {
63- const s = process . env . SHELL
64- if ( s ) return s
65- return fallback ( )
66- } )
94+ export function name ( file : string ) {
95+ if ( process . platform === "win32" ) return path . win32 . parse ( Filesystem . windowsPath ( file ) ) . name . toLowerCase ( )
96+ return path . basename ( file ) . toLowerCase ( )
97+ }
6798
68- export const acceptable = lazy ( ( ) => {
69- const s = process . env . SHELL
70- if ( s && ! BLACKLIST . has ( process . platform === "win32" ? path . win32 . basename ( s ) : path . basename ( s ) ) ) return s
71- return fallback ( )
72- } )
99+ export function login ( file : string ) {
100+ return LOGIN . has ( name ( file ) )
101+ }
102+
103+ export function posix ( file : string ) {
104+ return POSIX . has ( name ( file ) )
105+ }
106+
107+ export const preferred = lazy ( ( ) => select ( process . env . SHELL ) )
108+
109+ export const acceptable = lazy ( ( ) => select ( process . env . SHELL , { acceptable : true } ) )
73110}
0 commit comments