From 6e2be3356db2078b0aee184b49a450c0248b4b02 Mon Sep 17 00:00:00 2001 From: Evey <42-evey@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:52:21 -0700 Subject: [PATCH] fix(display): suppress spinner animation in non-TTY environments In Docker/systemd/piped environments, the KawaiiSpinner animation generates ~500 log lines per tool call. Now checks isatty() and falls back to clean [tool]/[done] log lines in non-TTY contexts. Interactive CLI behavior unchanged. Based on work by 42-evey in PR #2203. --- agent/display.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/agent/display.py b/agent/display.py index 28878f6f..9d579698 100644 --- a/agent/display.py +++ b/agent/display.py @@ -254,6 +254,15 @@ class KawaiiSpinner: pass def _animate(self): + # When stdout is not a real terminal (e.g. Docker, systemd, pipe), + # skip the animation entirely — it creates massive log bloat. + # Just log the start once and let stop() log the completion. + if not hasattr(self._out, 'isatty') or not self._out.isatty(): + self._write(f" [tool] {self.message}", flush=True) + while self.running: + time.sleep(0.5) + return + # Cache skin wings at start (avoid per-frame imports) skin = _get_skin() wings = skin.get_spinner_wings() if skin else [] @@ -319,12 +328,19 @@ class KawaiiSpinner: self.running = False if self.thread: self.thread.join(timeout=0.5) - # Clear the spinner line with spaces instead of \033[K to avoid - # garbled escape codes when prompt_toolkit's patch_stdout is active. - blanks = ' ' * max(self.last_line_len + 5, 40) - self._write(f"\r{blanks}\r", end='', flush=True) + + is_tty = hasattr(self._out, 'isatty') and self._out.isatty() + if is_tty: + # Clear the spinner line with spaces instead of \033[K to avoid + # garbled escape codes when prompt_toolkit's patch_stdout is active. + blanks = ' ' * max(self.last_line_len + 5, 40) + self._write(f"\r{blanks}\r", end='', flush=True) if final_message: - self._write(f" {final_message}", flush=True) + elapsed = f" ({time.time() - self.start_time:.1f}s)" if self.start_time else "" + if is_tty: + self._write(f" {final_message}", flush=True) + else: + self._write(f" [done] {final_message}{elapsed}", flush=True) def __enter__(self): self.start()