From 19ce396a114af1108765b9191e63966a54186cb1 Mon Sep 17 00:00:00 2001 From: Dakota Brown <41762023+cdakotabrown@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:36:54 -0500 Subject: [PATCH 1/3] fix: disable termflow OSC 52 clipboard to prevent cross-session pollution When multiple code-puppy sessions run concurrently, termflow's default behavior of auto-copying code blocks to clipboard via OSC 52 escape sequences caused clipboard cross-pollination between sessions. The fix disables clipboard integration by passing RenderFeatures(clipboard=False) to the TermflowRenderer. --- code_puppy/agents/event_stream_handler.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code_puppy/agents/event_stream_handler.py b/code_puppy/agents/event_stream_handler.py index c21aff552..2ceb14e23 100644 --- a/code_puppy/agents/event_stream_handler.py +++ b/code_puppy/agents/event_stream_handler.py @@ -106,6 +106,11 @@ async def event_stream_handler( from termflow import Parser as TermflowParser from termflow import Renderer as TermflowRenderer + from termflow.render.style import RenderFeatures + + # Disable OSC 52 clipboard integration to prevent cross-session clipboard pollution + # when multiple code-puppy instances are running concurrently + termflow_features = RenderFeatures(clipboard=False) # Use the module-level console (set via set_streaming_console) console = get_streaming_console() @@ -190,7 +195,9 @@ async def _print_response_banner() -> None: # Initialize termflow streaming for this text part termflow_parsers[event.index] = TermflowParser() termflow_renderers[event.index] = TermflowRenderer( - output=console.file, width=console.width + output=console.file, + width=console.width, + features=termflow_features, ) termflow_line_buffers[event.index] = "" # Handle initial content if present From 361ad629f8fcb5fba1afa941c324e63a7386297d Mon Sep 17 00:00:00 2001 From: Dakota Brown Date: Thu, 9 Apr 2026 09:39:06 -0500 Subject: [PATCH 2/3] fix: disable CSI u mode on Ctrl+C to prevent terminal garbage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace reset_unix_terminal() to send escape sequences that disable: • CSI u / kitty keyboard protocol (fixes ^[[27;5;99~) • modifyOtherKeys mode • bracketed paste mode • ANSI formatting - Use stty sane instead of reset (more portable, less disruptive) - Call reset_unix_terminal() on Unix in KeyboardInterrupt handler - Add platform import to cli_runner.py Fixes terminal corruption (O[, I[, ^[[27;5;99~) after long sessions. --- code_puppy/cli_runner.py | 9 ++++++--- code_puppy/terminal_utils.py | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/code_puppy/cli_runner.py b/code_puppy/cli_runner.py index 458f51dec..200d367a5 100644 --- a/code_puppy/cli_runner.py +++ b/code_puppy/cli_runner.py @@ -11,6 +11,7 @@ import argparse import asyncio import os +import platform import sys import time import traceback @@ -561,9 +562,11 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non except KeyboardInterrupt: # Handle Ctrl+C - cancel input and continue - # Windows-specific: Reset terminal state after interrupt to prevent - # the terminal from becoming unresponsive (can't type characters) - reset_windows_terminal_full() + # Reset terminal to fix CSI u mode garbage (^[[27;5;99~) + if platform.system() != "Windows": + reset_unix_terminal() + else: + reset_windows_terminal_full() # Stop wiggum mode on Ctrl+C from code_puppy.command_line.wiggum_state import ( is_wiggum_active, diff --git a/code_puppy/terminal_utils.py b/code_puppy/terminal_utils.py index 6efb37e02..1e636ff85 100644 --- a/code_puppy/terminal_utils.py +++ b/code_puppy/terminal_utils.py @@ -129,16 +129,24 @@ def reset_windows_terminal_full() -> None: def reset_unix_terminal() -> None: """Reset Unix/Linux/macOS terminal to sane state. - Uses the `reset` command to restore terminal sanity. - Silently fails if the command isn't available. + Disables CSI u / kitty keyboard protocol (fixes ^[[27;5;99~ from Ctrl+C) + and bracketed paste mode, then falls back to stty sane. """ if platform.system() == "Windows": return + # Disable enhanced keyboard protocols left enabled by prompt_toolkit try: - subprocess.run(["reset"], check=True, capture_output=True) - except (subprocess.CalledProcessError, FileNotFoundError): - pass # Silently fail if reset command isn't available + sys.stdout.write("\x1b[4;0m\x1b[?2004l\x1b[0m") + sys.stdout.flush() + except Exception: + pass + + # stty sane is more portable than `reset` and less disruptive + try: + subprocess.run(["stty", "sane"], capture_output=True, timeout=2) + except Exception: + pass def reset_terminal() -> None: From 3094709672903355ed546f0839a1e96ec4c94b19 Mon Sep 17 00:00:00 2001 From: Dakota Brown Date: Thu, 9 Apr 2026 09:39:06 -0500 Subject: [PATCH 3/3] fix: disable CSI u mode on Ctrl+C to prevent terminal garbage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace reset_unix_terminal() to send escape sequences that disable: • CSI u / kitty keyboard protocol (fixes ^[[27;5;99~) • modifyOtherKeys mode • bracketed paste mode • ANSI formatting - Use stty sane instead of reset (more portable, less disruptive) - Call reset_unix_terminal() on Unix in KeyboardInterrupt handler - Add platform import to cli_runner.py Fixes terminal corruption (O[, I[, ^[[27;5;99~) after long sessions. --- code_puppy/cli_runner.py | 9 ++++++--- code_puppy/terminal_utils.py | 22 +++++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/code_puppy/cli_runner.py b/code_puppy/cli_runner.py index 458f51dec..200d367a5 100644 --- a/code_puppy/cli_runner.py +++ b/code_puppy/cli_runner.py @@ -11,6 +11,7 @@ import argparse import asyncio import os +import platform import sys import time import traceback @@ -561,9 +562,11 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non except KeyboardInterrupt: # Handle Ctrl+C - cancel input and continue - # Windows-specific: Reset terminal state after interrupt to prevent - # the terminal from becoming unresponsive (can't type characters) - reset_windows_terminal_full() + # Reset terminal to fix CSI u mode garbage (^[[27;5;99~) + if platform.system() != "Windows": + reset_unix_terminal() + else: + reset_windows_terminal_full() # Stop wiggum mode on Ctrl+C from code_puppy.command_line.wiggum_state import ( is_wiggum_active, diff --git a/code_puppy/terminal_utils.py b/code_puppy/terminal_utils.py index 6efb37e02..845b82fed 100644 --- a/code_puppy/terminal_utils.py +++ b/code_puppy/terminal_utils.py @@ -129,16 +129,28 @@ def reset_windows_terminal_full() -> None: def reset_unix_terminal() -> None: """Reset Unix/Linux/macOS terminal to sane state. - Uses the `reset` command to restore terminal sanity. - Silently fails if the command isn't available. + Disables CSI u / kitty keyboard protocol (fixes ^[[27;5;99~ from Ctrl+C) + and bracketed paste mode, then falls back to stty sane (or reset). """ if platform.system() == "Windows": return + # Disable enhanced keyboard protocols left enabled by prompt_toolkit try: - subprocess.run(["reset"], check=True, capture_output=True) - except (subprocess.CalledProcessError, FileNotFoundError): - pass # Silently fail if reset command isn't available + sys.stdout.write("\x1b[4;0m\x1b[?2004l\x1b[0m") + sys.stdout.flush() + except Exception: + pass + + # stty sane is less disruptive than reset (preserves screen) + try: + subprocess.run(["stty", "sane"], capture_output=True, timeout=2) + except Exception: + # Fall back to reset if stty isn't available + try: + subprocess.run(["reset"], capture_output=True, timeout=2) + except Exception: + pass def reset_terminal() -> None: