From 1e5e1e822bc7bbf2a9bdefe12384745dc8730c23 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:11:37 -0700 Subject: [PATCH] fix: ESC cancels secret/sudo prompts, clearer skip messaging (#9902) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ESC key binding (eager) for secret_state and sudo_state modal prompts — fires immediately, same behavior as Ctrl+C cancel - Update placeholder text: 'Enter to submit · ESC to skip' (was 'Enter to skip' which was confusing — Enter on empty looked like submitting nothing rather than intentionally skipping) - Update widget body text: 'ESC or Ctrl+C to skip' - Change feedback message from 'Secret entry cancelled' to 'Secret entry skipped' — more accurate for the action taken - getpass fallback prompt also updated for non-TUI mode --- cli.py | 24 +++++++++++++++++++++--- hermes_cli/callbacks.py | 6 +++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cli.py b/cli.py index 970c98b06..ebc8b7637 100644 --- a/cli.py +++ b/cli.py @@ -8631,6 +8631,24 @@ class HermesCLI: self._should_exit = True event.app.exit() + _modal_prompt_active = Condition( + lambda: bool(self._secret_state or self._sudo_state) + ) + + @kb.add('escape', filter=_modal_prompt_active, eager=True) + def handle_escape_modal(event): + """ESC cancels active secret/sudo prompts.""" + if self._secret_state: + self._cancel_secret_capture() + event.app.current_buffer.reset() + event.app.invalidate() + return + if self._sudo_state: + self._sudo_state["response_queue"].put("") + self._sudo_state = None + event.app.invalidate() + return + @kb.add('c-z') def handle_ctrl_z(event): """Handle Ctrl+Z - suspend process to background (Unix only).""" @@ -8928,9 +8946,9 @@ class HermesCLI: if cli_ref._voice_processing: return "transcribing..." if cli_ref._sudo_state: - return "type password (hidden), Enter to skip" + return "type password (hidden), Enter to submit · ESC to skip" if cli_ref._secret_state: - return "type secret (hidden), Enter to skip" + return "type secret (hidden), Enter to submit · ESC to skip" if cli_ref._approval_state: return "" if cli_ref._clarify_freetext: @@ -9173,7 +9191,7 @@ class HermesCLI: prompt = state.get("prompt") or f"Enter value for {state.get('var_name', 'secret')}" metadata = state.get("metadata") or {} help_text = metadata.get("help") - body = 'Enter secret below (hidden), or press Enter to skip' + body = 'Enter secret below (hidden), ESC or Ctrl+C to skip' content_lines = [prompt, body] if help_text: content_lines.insert(1, str(help_text)) diff --git a/hermes_cli/callbacks.py b/hermes_cli/callbacks.py index 724e6e4c8..fa40eced5 100644 --- a/hermes_cli/callbacks.py +++ b/hermes_cli/callbacks.py @@ -75,12 +75,12 @@ def prompt_for_secret(cli, var_name: str, prompt: str, metadata=None) -> dict: if not hasattr(cli, "_secret_deadline"): cli._secret_deadline = 0 try: - value = getpass.getpass(f"{prompt} (hidden, Enter to skip): ") + value = getpass.getpass(f"{prompt} (hidden, ESC or empty Enter to skip): ") except (EOFError, KeyboardInterrupt): value = "" if not value: - cprint(f"\n{_DIM} ⏭ Secret entry cancelled{_RST}") + cprint(f"\n{_DIM} ⏭ Secret entry skipped{_RST}") return { "success": True, "reason": "cancelled", @@ -133,7 +133,7 @@ def prompt_for_secret(cli, var_name: str, prompt: str, metadata=None) -> dict: cli._app.invalidate() if not value: - cprint(f"\n{_DIM} ⏭ Secret entry cancelled{_RST}") + cprint(f"\n{_DIM} ⏭ Secret entry skipped{_RST}") return { "success": True, "reason": "cancelled",