From c1ec394160377155177e5dd38cf15731809c6b9d Mon Sep 17 00:00:00 2001 From: "CK iRonin.IT" Date: Thu, 30 Jul 2026 04:20:41 -0500 Subject: [PATCH] feat(cli): double ESC discards the draft, even mid-stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ctrl+C interrupts a running turn and only clears the composer when idle, so there was no way to discard a half-typed prompt while the agent was streaming. Claude Code and Gemini CLI both bind that to double-Esc. Appends the draft to history before clearing, so Up recalls it — the same undo affordance Claude Code gives, which is what makes this safe on a key people hit by reflex. Excluded when a modal prompt is up, since those bind ESC eagerly and cancel should still win. Co-authored-by: Brooklyn Nicholson --- cli.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cli.py b/cli.py index c1d5ada556..8cf25b13f5 100644 --- a/cli.py +++ b/cli.py @@ -15592,6 +15592,32 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): event.app.invalidate() return + @kb.add('escape', 'escape', filter=~_modal_prompt_active) + def handle_double_escape(event): + """Double ESC: discard the current draft and any attached images. + + Matches Claude Code / Gemini CLI, where double-Esc is the + clear-the-composer gesture. It works while the agent is + streaming, which is the gap Ctrl+C leaves: Ctrl+C interrupts a + running turn and only clears the draft when idle, so mid-stream + there was no way to discard a half-typed prompt. + + The draft is appended to history first, so Up recalls it — the + same undo affordance Claude Code provides, and the reason this + is safe to bind to a key pressed by reflex. + + Single ESC is the prefix for Alt sequences (escape+enter, + escape+g, escape+v), so prompt_toolkit's escape-timeout keeps + those distinct from the double press. Modal prompts bind ESC + eagerly and are excluded here so cancel still wins. + """ + buf = event.app.current_buffer + if not (buf.text or cli_ref._attached_images): + return + buf.reset(append_to_history=bool(buf.text)) + cli_ref._attached_images.clear() + event.app.invalidate() + @kb.add('c-z') def handle_ctrl_z(event): """Handle Ctrl+Z - suspend process to background (Unix only)."""