From 3f2a0aaa920b24095ff8b4755b520e27db9a5d31 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 26 Feb 2026 01:26:47 +0300 Subject: [PATCH] refactor(cmd): route non-interactive mode through app.RunOnce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the legacy runNonInteractiveMode path (which used runAgenticLoop and the old CLI spinner/streaming display) with runNonInteractiveModeApp, which delegates directly to appInstance.RunOnce(ctx, prompt, os.Stdout). RunOnce never creates a tea.Program, so no intermediate spinner or tool-call output is produced — satisfying both the normal and --quiet non-interactive cases with the same codepath. When --no-exit is set, runNonInteractiveModeApp hands off to the interactive BubbleTea TUI via runInteractiveModeBubbleTea after the single step completes. --- cmd/root.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index f18adb49..5434f5cf 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -768,7 +768,7 @@ func runNormalMode(ctx context.Context) error { // Check if running in non-interactive mode if promptFlag != "" { - return runNonInteractiveMode(ctx, mcpAgent, cli, promptFlag, modelName, messages, quietFlag, noExitFlag, mcpConfig, sessionManager, hookExecutor) + return runNonInteractiveModeApp(ctx, appInstance, promptFlag, quietFlag, noExitFlag, cli, modelName) } // Quiet mode is not allowed in interactive mode @@ -1393,6 +1393,29 @@ func runInteractiveMode(ctx context.Context, mcpAgent *agent.Agent, cli *ui.CLI, return runAgenticLoop(ctx, mcpAgent, cli, messages, config, hookExecutor) } +// runNonInteractiveModeApp executes a single prompt via the app layer and exits, +// or transitions to the interactive BubbleTea TUI when --no-exit is set. +// +// RunOnce does not create a tea.Program, so there is no spinner or tool-call +// display; only the final response text is written to os.Stdout. This +// satisfies both the normal and --quiet non-interactive cases (quiet simply +// means "no intermediate output", which RunOnce already guarantees). +// +// When --no-exit is set, after RunOnce completes the interactive BubbleTea TUI +// is started so the user can continue the conversation. +func runNonInteractiveModeApp(ctx context.Context, appInstance *app.App, prompt string, _, noExit bool, cli *ui.CLI, modelName string) error { + if err := appInstance.RunOnce(ctx, prompt, os.Stdout); err != nil { + return err + } + + // If --no-exit was requested, hand off to the interactive TUI. + if noExit { + return runInteractiveModeBubbleTea(ctx, appInstance, cli, modelName) + } + + return nil +} + // runInteractiveModeBubbleTea starts the new unified Bubble Tea interactive TUI. // // It: