fix: add TTY guard to interactive CLI commands to prevent CPU spin (#3933)
When interactive TUI commands are invoked non-interactively (e.g. via the agent's terminal() tool through a subprocess pipe), curses loops spin at 100% CPU and input() calls hang indefinitely. Defense in depth — two layers: 1. Source-level guard in curses_checklist() (curses_ui.py + checklist.py): Returns cancel_returns immediately when stdin is not a TTY. This catches ALL callers automatically, including future code. 2. Command-level guards with clear error messages: - hermes tools (interactive checklist, not list/disable/enable) - hermes setup (interactive wizard) - hermes model (provider/model picker) - hermes whatsapp (pairing setup) - hermes skills config (skill toggle) - hermes mcp configure (tool selection) - hermes uninstall (confirmation prompt) Non-interactive subcommands (hermes tools list, hermes tools enable, hermes mcp add/remove/list/test, hermes skills search/install/browse) remain unaffected.
This commit is contained in:
@@ -5,6 +5,7 @@ toggleable list of items. Falls back to a numbered text UI when
|
||||
curses is unavailable (Windows without curses, piped stdin, etc.).
|
||||
"""
|
||||
|
||||
import sys
|
||||
from typing import List, Set
|
||||
|
||||
from hermes_cli.colors import Colors, color
|
||||
@@ -26,6 +27,10 @@ def curses_checklist(
|
||||
The indices the user confirmed as checked. On cancel (ESC/q),
|
||||
returns ``pre_selected`` unchanged.
|
||||
"""
|
||||
# Safety: return defaults when stdin is not a terminal.
|
||||
if not sys.stdin.isatty():
|
||||
return set(pre_selected)
|
||||
|
||||
try:
|
||||
import curses
|
||||
selected = set(pre_selected)
|
||||
|
||||
Reference in New Issue
Block a user