diff --git a/packages/cli/src/ui/views/App.tsx b/packages/cli/src/ui/views/App.tsx index 456030c..3b2886c 100644 --- a/packages/cli/src/ui/views/App.tsx +++ b/packages/cli/src/ui/views/App.tsx @@ -48,6 +48,7 @@ import type { } from "@vegamo/deepcode-core"; import { SessionManager } from "@vegamo/deepcode-core"; import { getCompactPromptTokenThreshold } from "@vegamo/deepcode-core"; +import { writeStdout, writeStdoutLine } from "../../utils/stdio-helpers"; type View = "chat" | "session-list" | "undo" | "mcp-status"; @@ -145,8 +146,8 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp onAssistantMessage: (message: SessionMessage) => { setMessages((prev) => [...prev, message]); if (rawModeRef.current === RawMode.Raw) { - process.stdout.write("\n"); - process.stdout.write(renderMessageToStdout(message, rawModeRef.current) + "\n\n"); + writeStdoutLine("\n"); + writeStdoutLine(renderMessageToStdout(message, rawModeRef.current) + "\n\n"); } }, onSessionEntryUpdated: (entry) => { @@ -196,7 +197,7 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp const resetStaticView = useCallback( (loadedMessages: SessionMessage[], options?: { clearScreen?: boolean }): Promise => { if (options?.clearScreen) { - process.stdout.write(ANSI_CLEAR_SCREEN); + writeStdout(ANSI_CLEAR_SCREEN); } setMessages([]); setWelcomeNonce((n) => n + 1); @@ -298,19 +299,19 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp const session = activeSessionId ? sessionManager.getSession(activeSessionId) : null; const resumeHint = buildResumeHintText(activeSessionId ?? undefined); - process.stdout.write("\n"); + writeStdoutLine("\n"); if (showCommand) { - process.stdout.write(chalk.rgb(34, 154, 195)("> /exit ")); - process.stdout.write("\n\n"); + writeStdoutLine(chalk.rgb(34, 154, 195)(" > /exit ")); + writeStdoutLine("\n"); } if (showSummary) { const summary = buildExitSummaryText({ session, sessionId: activeSessionId ?? undefined }); - process.stdout.write(summary); - process.stdout.write("\n\n"); + writeStdoutLine(summary); + writeStdoutLine("\n"); } if (resumeHint) { - process.stdout.write(resumeHint); - process.stdout.write("\n"); + writeStdoutLine(resumeHint); + writeStdoutLine("\n"); } sessionManager.dispose(); @@ -628,7 +629,7 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp setShowWelcome(false); setMessages([]); // Clear screen to remove stale formatted text. - process.stdout.write(ANSI_CLEAR_SCREEN); + writeStdout(ANSI_CLEAR_SCREEN); setTimeout(() => { if (nextMode === RawMode.Raw) { @@ -666,8 +667,8 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp if (mode === RawMode.Raw) { // In raw mode, re-render all messages directly to stdout at the new width. - // Use process.stdout.write instead of writeRef to avoid Ink interference. - process.stdout.write(ANSI_CLEAR_SCREEN); + // Use direct stdout instead of writeRef to avoid Ink interference. + writeStdout(ANSI_CLEAR_SCREEN); const activeSessionId = sessionManager.getActiveSessionId(); const allMessages = activeSessionId ? loadVisibleMessages(sessionManager, activeSessionId) : []; renderRawModeMessages(allMessages, mode); @@ -898,7 +899,7 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp ); }} - {busy || statusLine ? : null} + {(busy || statusLine) && !isExiting ? : null} {errorLine ? ( Error: {errorLine} diff --git a/packages/cli/src/utils/stdio-helpers.ts b/packages/cli/src/utils/stdio-helpers.ts index f0202e9..3f11726 100644 --- a/packages/cli/src/utils/stdio-helpers.ts +++ b/packages/cli/src/utils/stdio-helpers.ts @@ -1,3 +1,11 @@ +/** + * Writes a message to stdout exactly as provided. + * Use for terminal control sequences or output that manages its own spacing. + */ +export const writeStdout = (message: string): void => { + process.stdout.write(message); +}; + /** * Writes a message to stdout with a trailing newline. * Use for normal command output that the user expects to see.