( terminal: TerminalInfo, claudePath: string, claudeArgs: string[], cwd?: string, )
| 253 | } |
| 254 | |
| 255 | async function launchMacosTerminal( |
| 256 | terminal: TerminalInfo, |
| 257 | claudePath: string, |
| 258 | claudeArgs: string[], |
| 259 | cwd?: string, |
| 260 | ): Promise<boolean> { |
| 261 | switch (terminal.command) { |
| 262 | // --- SHELL-STRING PATHS (AppleScript has no argv interface) --- |
| 263 | // User input is shell-quoted via shellQuote(). These two are the only |
| 264 | // macOS paths where shellQuote() correctness is load-bearing. |
| 265 | |
| 266 | case 'iTerm': { |
| 267 | const shCmd = buildShellCommand(claudePath, claudeArgs, cwd) |
| 268 | // If iTerm isn't running, `tell application` launches it and iTerm's |
| 269 | // default startup behavior opens a window — so `create window` would |
| 270 | // make a second one. Check `running` first: if already running (even |
| 271 | // with zero windows), create a window; if not, `activate` lets iTerm's |
| 272 | // startup create the first window. |
| 273 | const script = `tell application "iTerm" |
| 274 | if running then |
| 275 | create window with default profile |
| 276 | else |
| 277 | activate |
| 278 | end if |
| 279 | tell current session of current window |
| 280 | write text ${appleScriptQuote(shCmd)} |
| 281 | end tell |
| 282 | end tell` |
| 283 | const { code } = await execFileNoThrow('osascript', ['-e', script], { |
| 284 | useCwd: false, |
| 285 | }) |
| 286 | if (code === 0) return true |
| 287 | break |
| 288 | } |
| 289 | |
| 290 | case 'Terminal': { |
| 291 | const shCmd = buildShellCommand(claudePath, claudeArgs, cwd) |
| 292 | const script = `tell application "Terminal" |
| 293 | do script ${appleScriptQuote(shCmd)} |
| 294 | activate |
| 295 | end tell` |
| 296 | const { code } = await execFileNoThrow('osascript', ['-e', script], { |
| 297 | useCwd: false, |
| 298 | }) |
| 299 | return code === 0 |
| 300 | } |
| 301 | |
| 302 | // --- PURE ARGV PATHS (no shell, no shellQuote) --- |
| 303 | // open -na <App> --args <argv> → app receives argv verbatim → |
| 304 | // terminal's native --working-directory + -e exec the command directly. |
| 305 | |
| 306 | case 'Ghostty': { |
| 307 | const args = [ |
| 308 | '-na', |
| 309 | terminal.command, |
| 310 | '--args', |
| 311 | '--window-save-state=never', |
| 312 | ] |
no test coverage detected