(args: string[])
| 277 | * falls back to DetachedEngine on Windows or when tmux is absent. |
| 278 | */ |
| 279 | export async function handleBgStart(args: string[]): Promise<void> { |
| 280 | const engine = await selectEngine() |
| 281 | |
| 282 | // Strip --bg/--background from args (for backward-compat shortcut) |
| 283 | const filteredArgs = args.filter(a => a !== '--bg' && a !== '--background') |
| 284 | |
| 285 | // Engines without interactive TTY input (e.g. detached) require -p/--print |
| 286 | // or piped input. Tmux provides a virtual terminal so it works without -p. |
| 287 | if ( |
| 288 | !engine.supportsInteractiveInput && |
| 289 | !filteredArgs.some(a => a === '-p' || a === '--print' || a === '--pipe') |
| 290 | ) { |
| 291 | console.error( |
| 292 | 'Error: Background sessions with detached engine require -p/--print flag.\n' + |
| 293 | 'The detached engine has no terminal for interactive input.\n\n' + |
| 294 | 'Usage:\n' + |
| 295 | ' claude daemon bg -p "your prompt here"\n' + |
| 296 | ' echo "prompt" | claude daemon bg --pipe', |
| 297 | ) |
| 298 | if (process.platform !== 'win32') { |
| 299 | console.error( |
| 300 | '\nAlternatively, install tmux for interactive background sessions:\n' + |
| 301 | ` ${process.platform === 'darwin' ? 'brew install tmux' : 'sudo apt install tmux'}`, |
| 302 | ) |
| 303 | } |
| 304 | process.exitCode = 1 |
| 305 | return |
| 306 | } |
| 307 | |
| 308 | const sessionName = `claude-bg-${randomUUID().slice(0, 8)}` |
| 309 | const logPath = join( |
| 310 | getClaudeConfigHomeDir(), |
| 311 | 'sessions', |
| 312 | 'logs', |
| 313 | `${sessionName}.log`, |
| 314 | ) |
| 315 | |
| 316 | try { |
| 317 | const result = await engine.start({ |
| 318 | sessionName, |
| 319 | args: filteredArgs, |
| 320 | env: { ...process.env }, |
| 321 | logPath, |
| 322 | cwd: process.cwd(), |
| 323 | }) |
| 324 | |
| 325 | console.log(`Background session started: ${result.sessionName}`) |
| 326 | console.log(` Engine: ${result.engineUsed}`) |
| 327 | console.log(` Log: ${result.logPath}`) |
| 328 | console.log() |
| 329 | console.log( |
| 330 | `Use \`claude daemon attach ${result.sessionName}\` to reconnect.`, |
| 331 | ) |
| 332 | console.log(`Use \`claude daemon status\` to check status.`) |
| 333 | console.log(`Use \`claude daemon kill ${result.sessionName}\` to stop.`) |
| 334 | } catch (e) { |
| 335 | console.error(e instanceof Error ? e.message : String(e)) |
| 336 | process.exitCode = 1 |
nothing calls this directly
no test coverage detected