diff --git a/hermes_cli/curses_ui.py b/hermes_cli/curses_ui.py index b05295f1e6..01d759d387 100644 --- a/hermes_cli/curses_ui.py +++ b/hermes_cli/curses_ui.py @@ -156,6 +156,8 @@ def curses_checklist( flush_stdin() return result_holder[0] if result_holder[0] is not None else cancel_returns + except KeyboardInterrupt: + return cancel_returns except Exception: return _numbered_fallback(title, items, selected, cancel_returns, status_fn) @@ -278,6 +280,8 @@ def curses_radiolist( flush_stdin() return result_holder[0] if result_holder[0] is not None else cancel_returns + except KeyboardInterrupt: + return cancel_returns except Exception: return _radio_numbered_fallback(title, items, selected, cancel_returns) @@ -401,6 +405,8 @@ def curses_single_select( return None return result_holder[0] + except KeyboardInterrupt: + return None except Exception: all_items = list(items) + [cancel_label] cancel_idx = len(items) diff --git a/tests/hermes_cli/test_plugins_cmd.py b/tests/hermes_cli/test_plugins_cmd.py index 72b9bdde2c..11231350e1 100644 --- a/tests/hermes_cli/test_plugins_cmd.py +++ b/tests/hermes_cli/test_plugins_cmd.py @@ -508,7 +508,7 @@ class TestPromptPluginEnvVars: class TestCursesRadiolist: - """Test the curses_radiolist function (non-TTY fallback path).""" + """Test the curses_radiolist function.""" def test_non_tty_returns_default(self): from hermes_cli.curses_ui import curses_radiolist @@ -524,6 +524,14 @@ class TestCursesRadiolist: result = curses_radiolist("Pick", ["x", "y"], selected=0, cancel_returns=1) assert result == 1 + def test_keyboard_interrupt_returns_cancel_value(self): + from hermes_cli.curses_ui import curses_radiolist + + with patch("sys.stdin") as mock_stdin, patch("curses.wrapper", side_effect=KeyboardInterrupt): + mock_stdin.isatty.return_value = True + result = curses_radiolist("Pick", ["x", "y"], selected=0, cancel_returns=-1) + assert result == -1 + # ── Provider discovery helpers ───────────────────────────────────────────