fix: ESC cancels secret/sudo prompts, clearer skip messaging (#9902)

- 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
This commit is contained in:
Teknium 2026-04-14 16:11:37 -07:00 committed by GitHub
parent 55ce76b372
commit 1e5e1e822b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 6 deletions

24
cli.py
View file

@ -8631,6 +8631,24 @@ class HermesCLI:
self._should_exit = True self._should_exit = True
event.app.exit() 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') @kb.add('c-z')
def handle_ctrl_z(event): def handle_ctrl_z(event):
"""Handle Ctrl+Z - suspend process to background (Unix only).""" """Handle Ctrl+Z - suspend process to background (Unix only)."""
@ -8928,9 +8946,9 @@ class HermesCLI:
if cli_ref._voice_processing: if cli_ref._voice_processing:
return "transcribing..." return "transcribing..."
if cli_ref._sudo_state: 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: 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: if cli_ref._approval_state:
return "" return ""
if cli_ref._clarify_freetext: 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')}" prompt = state.get("prompt") or f"Enter value for {state.get('var_name', 'secret')}"
metadata = state.get("metadata") or {} metadata = state.get("metadata") or {}
help_text = metadata.get("help") 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] content_lines = [prompt, body]
if help_text: if help_text:
content_lines.insert(1, str(help_text)) content_lines.insert(1, str(help_text))

View file

@ -75,12 +75,12 @@ def prompt_for_secret(cli, var_name: str, prompt: str, metadata=None) -> dict:
if not hasattr(cli, "_secret_deadline"): if not hasattr(cli, "_secret_deadline"):
cli._secret_deadline = 0 cli._secret_deadline = 0
try: 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): except (EOFError, KeyboardInterrupt):
value = "" value = ""
if not value: if not value:
cprint(f"\n{_DIM} ⏭ Secret entry cancelled{_RST}") cprint(f"\n{_DIM} ⏭ Secret entry skipped{_RST}")
return { return {
"success": True, "success": True,
"reason": "cancelled", "reason": "cancelled",
@ -133,7 +133,7 @@ def prompt_for_secret(cli, var_name: str, prompt: str, metadata=None) -> dict:
cli._app.invalidate() cli._app.invalidate()
if not value: if not value:
cprint(f"\n{_DIM} ⏭ Secret entry cancelled{_RST}") cprint(f"\n{_DIM} ⏭ Secret entry skipped{_RST}")
return { return {
"success": True, "success": True,
"reason": "cancelled", "reason": "cancelled",