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 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: