fix(cli): preserve startup banner on terminal resize#25227
Conversation
Recover from SIGWINCH without clearing the physical screen or scrollback buffer. The startup banner and tool summary are printed before prompt_toolkit owns the live chrome, so they live in normal terminal scrollback. Calling erase_screen() + \x1b[3J] on every resize removed that UI permanently — _replay_output_history cannot reconstruct it because the banner was never added to _OUTPUT_HISTORY. Instead, just reset prompt_toolkit's renderer cache and invalidate so the next incremental redraw starts from a clean slate, then let the original on_resize handler recalculate layout for the new terminal size. This matches the behaviour of bash/zsh/fish on SIGWINCH. Fixes #22999
🚨 CRITICAL Supply Chain Risk DetectedThis PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging. 🚨 CRITICAL: Install-hook file added or modifiedThese files can execute code during package installation or interpreter startup. Files: Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting. |
🔎 Lint report:
|
|
Supersedes #23048 (by @vominh1919, cherry-picked here with authorship preserved) and duplicate #23294. All three fix #22999 — same renderer.reset()+invalidate() approach. |
Salvage of #23048 onto current main (219 commits behind at submission). Cherry-picked @vominh1919's commit; authorship preserved.
Summary
Classic-CLI resize no longer wipes terminal scrollback. The startup banner, tool summary, and earlier output stay reachable via scroll after any
SIGWINCH.Root cause
Commit 76074d9 ("fix(cli): recover classic CLI output after resize") had
_recover_after_resize()call_clear_prompt_toolkit_screen(app, rebuild_scrollback=True), which emitsESC[3J— the ANSI sequence that erases terminal scrollback. The banner is printed before prompt_toolkit owns the chrome, so it lives in regular scrollback and is never in_OUTPUT_HISTORY; clearing scrollback destroys it for good.Fix
_recover_after_resize()now only callsrenderer.reset(leave_alternate_screen=False)+app.invalidate()and defers tooriginal_on_resize(). No screen clear, no scrollback wipe, no replay of a buffer that never held the banner.Test plan
tests/cli/test_cli_force_redraw.py— 9/9 pass, including newtest_resize_preserves_scrollback_and_resets_rendererassertingerase_screen/write_raw/cursor_gotoare NOT called on resize.Credit
Reported by @Dc-j0e in Discord. Fixed by @vominh1919 in #23048.