diff --git a/packages/coding-agent/src/modes/index.ts b/packages/coding-agent/src/modes/index.ts index e1dea209b..039196489 100644 --- a/packages/coding-agent/src/modes/index.ts +++ b/packages/coding-agent/src/modes/index.ts @@ -1,6 +1,3 @@ -import { emergencyTerminalRestore } from "@oh-my-pi/pi-tui"; -import { postmortem } from "@oh-my-pi/pi-utils"; - /** * Interactive mode and embeddable RPC client exports for the coding agent. * @@ -11,7 +8,3 @@ import { postmortem } from "@oh-my-pi/pi-utils"; export * from "./interactive-mode"; export * from "./rpc/rpc-client"; export * from "./rpc/rpc-types"; - -postmortem.register("terminal-restore", () => { - emergencyTerminalRestore(); -}); diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index 1a6af1e17..a2c4c4001 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Registered emergency terminal restore with the postmortem fatal path whenever a real terminal starts, so fatal exits restore raw mode/alternate screen on the normal CLI graph (previously the registration was a side effect of a barrel module the CLI never imported). + ## [16.3.9] - 2026-07-06 ### Fixed diff --git a/packages/tui/src/terminal.ts b/packages/tui/src/terminal.ts index f70b454a5..ed9210af9 100644 --- a/packages/tui/src/terminal.ts +++ b/packages/tui/src/terminal.ts @@ -1,6 +1,6 @@ import { dlopen, FFIType, ptr } from "bun:ffi"; import * as fs from "node:fs"; -import { $env, isBunTestRuntime, isTerminalHeadless, logger } from "@oh-my-pi/pi-utils"; +import { $env, isBunTestRuntime, isTerminalHeadless, logger, postmortem } from "@oh-my-pi/pi-utils"; import { setKittyProtocolActive } from "./keys"; import { StdinBuffer } from "./stdin-buffer"; import { @@ -161,6 +161,15 @@ let terminalEverStarted = false; // jumps to the viewport home, dropping the parent shell prompt on top of the // dead frame after exit. let altScreenActive = false; +let terminalRestoreRegistered = false; + +function registerPostmortemTerminalRestore(): void { + if (terminalRestoreRegistered) return; + terminalRestoreRegistered = true; + postmortem.register("terminal-restore", () => { + emergencyTerminalRestore(); + }); +} /** Record alternate-screen state (called by the TUI on `?1049h`/`?1049l` writes). */ export function setAltScreenActive(active: boolean): void { @@ -523,6 +532,7 @@ export class ProcessTerminal implements Terminal { // escapes never reach the developer's terminal during `bun test`. this.#headless = isTerminalHeadless(); if (this.#headless) return; + registerPostmortemTerminalRestore(); // Register for emergency cleanup activeTerminal = this;