fix(cli): fix shortcut config conflict in hermes_cli

This commit is contained in:
Harry Riddle 2026-04-27 01:22:51 +07:00 committed by Teknium
parent a919269eb5
commit 645a2f482d
2 changed files with 93 additions and 1 deletions

93
cli.py
View file

@ -10483,7 +10483,98 @@ class HermesCLI:
else:
self._should_exit = True
event.app.exit()
@kb.add('c-S-c') # Ctrl+Shift+C
def handle_ctrl_shift_c(event):
"""Copy text to clipboard (terminal-native).
This is a no-op at the application level. Terminal emulators
handle the actual copy operation when Ctrl+Shift+C is pressed.
This binding prevents Hermes from intercepting the keystroke
as an interrupt signal.
On macOS the standard copy shortcut is Cmd+C (no Hermes binding
needed). On Linux/Windows Ctrl+Shift+C is the conventional
terminal copy shortcut.
"""
return # No-op — let the terminal perform native copy
@kb.add('c-q') # Ctrl+Q
def handle_ctrl_q(event):
"""Alternative interrupt/exit shortcut (Ctrl+Q).
Behaves like Ctrl+C: cancels active prompts, interrupts the
running agent, or clears the input buffer. Does not support
the double-press 'force exit' feature of Ctrl+C.
"""
# Cancel active voice recording.
_should_cancel_voice = False
_recorder_ref = None
with cli_ref._voice_lock:
if cli_ref._voice_recording and cli_ref._voice_recorder:
_recorder_ref = cli_ref._voice_recorder
cli_ref._voice_recording = False
cli_ref._voice_continuous = False
_should_cancel_voice = True
if _should_cancel_voice:
_cprint(f"\n{_DIM}Recording cancelled.{_RST}")
threading.Thread(
target=_recorder_ref.cancel, daemon=True
).start()
event.app.invalidate()
return
# Cancel sudo prompt
if self._sudo_state:
self._sudo_state["response_queue"].put("")
self._sudo_state = None
event.app.invalidate()
return
# Cancel secret prompt
if self._secret_state:
self._cancel_secret_capture()
event.app.current_buffer.reset()
event.app.invalidate()
return
# Cancel approval prompt (deny)
if self._approval_state:
self._approval_state["response_queue"].put("deny")
self._approval_state = None
event.app.invalidate()
return
# Cancel /model picker
if self._model_picker_state:
self._close_model_picker()
event.app.current_buffer.reset()
event.app.invalidate()
return
# Cancel clarify prompt
if self._clarify_state:
self._clarify_state["response_queue"].put(
"The user cancelled. Use your best judgement to proceed."
)
self._clarify_state = None
self._clarify_freetext = False
event.app.current_buffer.reset()
event.app.invalidate()
return
if self._agent_running and self.agent:
print("\n⚡ Interrupting agent...")
self.agent.interrupt()
else:
if event.app.current_buffer.text or self._attached_images:
event.app.current_buffer.reset()
self._attached_images.clear()
event.app.invalidate()
else:
self._should_exit = True
event.app.exit()
@kb.add('c-d')
def handle_ctrl_d(event):
"""Ctrl+D: delete char under cursor (standard readline behaviour).