fix: prevent session summary from being cleared on exit
The session stats summary was being displayed momentarily then cleared. This was caused by drainStdin() re-enabling raw mode AFTER we had already exited the alternate screen and written the summary. Changes: - Remove drainStdin() from normal exit flow - it was interfering with terminal - Drain pending DECRQM responses BEFORE exiting alternate screen, while still in raw mode - Add newline after summary to ensure shell prompt appears correctly - Keep drainStdin() available for signal handlers but don't use it in normal exit
This commit is contained in:
@@ -18,7 +18,6 @@ import { getThinkingMessage } from "@constants/status-messages";
|
||||
import {
|
||||
enterFullscreen,
|
||||
registerExitHandlers,
|
||||
drainStdin,
|
||||
} from "@utils/core/terminal";
|
||||
import { createCallbacks } from "@commands/chat-tui";
|
||||
import { agentLoader } from "@services/agent-loader";
|
||||
@@ -32,9 +31,8 @@ interface ExecuteContext {
|
||||
const createHandleExit = (): (() => void) => (): void => {
|
||||
cleanupPermissionHandler();
|
||||
// Note: Session stats are displayed by the TUI exit handler in app.tsx
|
||||
// Drain stdin to consume pending terminal responses (e.g. DECRQM 997;1n)
|
||||
// before exiting, so they don't echo as garbage text in the shell
|
||||
drainStdin().then(() => process.exit(0));
|
||||
// The TUI handles terminal cleanup and draining stdin before exit
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
const createHandleModelSelect =
|
||||
|
||||
@@ -579,21 +579,47 @@ export function tui(options: TuiRenderOptions): Promise<TuiOutput> {
|
||||
|
||||
const handleExit = (output: TuiOutput): void => {
|
||||
try {
|
||||
writeSync(1, TERMINAL_RESET);
|
||||
|
||||
const state = appStore.getState();
|
||||
const summary = generateSessionSummary({
|
||||
sessionId: output.sessionId ?? "unknown",
|
||||
sessionStats: state.sessionStats,
|
||||
modifiedFiles: state.modifiedFiles,
|
||||
modelName: state.model,
|
||||
providerName: state.provider,
|
||||
});
|
||||
writeSync(1, summary);
|
||||
// First, drain any pending terminal responses (e.g. DECRQM 997;1n)
|
||||
// while still in raw mode, before exiting alternate screen
|
||||
const drainTimeout = 50; // 50ms is enough for terminal responses
|
||||
|
||||
const finishExit = (): void => {
|
||||
writeSync(1, TERMINAL_RESET);
|
||||
|
||||
const state = appStore.getState();
|
||||
const summary = generateSessionSummary({
|
||||
sessionId: output.sessionId ?? "unknown",
|
||||
sessionStats: state.sessionStats,
|
||||
modifiedFiles: state.modifiedFiles,
|
||||
modelName: state.model,
|
||||
providerName: state.provider,
|
||||
});
|
||||
writeSync(1, summary + "\n");
|
||||
resolve(output);
|
||||
};
|
||||
|
||||
// If stdin is TTY and in raw mode, try to drain pending data
|
||||
if (process.stdin.isTTY && process.stdin.isRaw) {
|
||||
const sink = (): void => {};
|
||||
process.stdin.on("data", sink);
|
||||
|
||||
const cleanup = (): void => {
|
||||
process.stdin.removeListener("data", sink);
|
||||
// Read and discard any buffered data
|
||||
while (process.stdin.read() !== null) {
|
||||
// drain
|
||||
}
|
||||
finishExit();
|
||||
};
|
||||
|
||||
setTimeout(cleanup, drainTimeout);
|
||||
} else {
|
||||
finishExit();
|
||||
}
|
||||
} catch {
|
||||
// Ignore - stdout may already be closed
|
||||
resolve(output);
|
||||
}
|
||||
resolve(output);
|
||||
};
|
||||
|
||||
render(() => <App {...options} onExit={handleExit} />, {
|
||||
|
||||
Reference in New Issue
Block a user