( prompt: string | undefined, options: ChatOptions, )
| 434 | } |
| 435 | |
| 436 | async function runHeadlessMode( |
| 437 | prompt: string | undefined, |
| 438 | options: ChatOptions, |
| 439 | ): Promise<void> { |
| 440 | // Initialize services for headless mode |
| 441 | const { permissionOverrides } = processCommandFlags(options); |
| 442 | |
| 443 | await initializeServices({ |
| 444 | options, |
| 445 | headless: true, |
| 446 | toolPermissionOverrides: permissionOverrides, |
| 447 | }); |
| 448 | |
| 449 | // Get required services from the service container |
| 450 | const modelState = await serviceContainer.get<ModelServiceState>( |
| 451 | SERVICE_NAMES.MODEL, |
| 452 | ); |
| 453 | const { llmApi, model } = modelState; |
| 454 | |
| 455 | if (!model) { |
| 456 | throw new Error("No models were found."); |
| 457 | } |
| 458 | |
| 459 | if (!llmApi) { |
| 460 | throw new Error("No LLM API instance found."); |
| 461 | } |
| 462 | |
| 463 | // Initialize service-driven history (resume if requested) |
| 464 | const chatHistory = await initializeChatHistory(options); |
| 465 | let compactionIndex: number | null = null; |
| 466 | if (options.resume || options.fork) { |
| 467 | services.chatHistory.setHistory(chatHistory); |
| 468 | compactionIndex = findCompactionIndex(chatHistory); |
| 469 | } |
| 470 | |
| 471 | // Handle additional prompts from --prompt flags |
| 472 | const { processAndCombinePrompts } = await import( |
| 473 | "../util/promptProcessor.js" |
| 474 | ); |
| 475 | const agentFileState = await serviceContainer.get<AgentFileServiceState>( |
| 476 | SERVICE_NAMES.AGENT_FILE, |
| 477 | ); |
| 478 | |
| 479 | const initialPrompt = prependPrompt( |
| 480 | agentFileState?.agentFile?.prompt, |
| 481 | prompt, |
| 482 | ); |
| 483 | const initialUserInput = await processAndCombinePrompts( |
| 484 | options.prompt, |
| 485 | initialPrompt, |
| 486 | ); |
| 487 | |
| 488 | // Critical validation: Ensure we have actual prompt text in headless mode |
| 489 | // This prevents the CLI from hanging in TTY-less environments when question() is called |
| 490 | // We check AFTER processing all prompts (including agent files) to ensure we have real content |
| 491 | // EXCEPTION: Allow empty prompts when resuming/forking since they may just want to view history |
| 492 | if (!initialUserInput || !initialUserInput.trim()) { |
| 493 | // If resuming or forking, allow empty prompt - just exit successfully after showing history |
no test coverage detected