(command: string)
| 27 | * First checks macOS-specific paths for known apps, then falls back to `which`. |
| 28 | */ |
| 29 | export async function isCommandAvailable(command: string): Promise<boolean> { |
| 30 | // Check known paths for macOS apps |
| 31 | if (process.platform === "darwin" && command in MACOS_APP_PATHS) { |
| 32 | for (const appPath of MACOS_APP_PATHS[command]) { |
| 33 | try { |
| 34 | const stats = await fs.stat(appPath); |
| 35 | // Check if it's a file and any executable bit is set |
| 36 | if (stats.isFile() && (stats.mode & 0o111) !== 0) { |
| 37 | return true; |
| 38 | } |
| 39 | } catch { |
| 40 | // Try next path |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // Fall back to which (inherits enriched PATH from process.env) |
| 46 | try { |
| 47 | const result = spawnSync("which", [command], { encoding: "utf8" }); |
| 48 | return result.status === 0; |
| 49 | } catch { |
| 50 | return false; |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * Find the first available command from a list of candidates. |
no test coverage detected