Skip to content

CLI: Enter key inserts newline instead of submitting — c-j binding breaks prompt_toolkit safety net #9299

@romanornr

Description

@romanornr

Bug Description

Pressing Enter in the CLI input sometimes inserts a newline instead of submitting the message. The cursor moves down but nothing is sent. The only way to recover is Ctrl+D (which exits the app) or Ctrl+C.

This was observed on Ghostty (Arch Linux, TERM=xterm-ghostty) but affects any terminal that sends \n (0x0a) instead of \r (0x0d) for the Enter key.

Root Cause

The custom c-j key binding in cli.py (line ~8424) overrides prompt_toolkit's built-in safety net for terminals that send \n for Enter.

How prompt_toolkit handles this natively

In prompt_toolkit/key_binding/bindings/basic.py (lines 195-202):

@handle("c-j")
def _newline2(event):
    """
    By default, handle \n as if it were a \r (enter).
    (It appears that some terminals send \n instead of \r when pressing
    enter. - at least the Linux subsystem for Windows.)
    """
    event.key_processor.feed(KeyPress(Keys.ControlM, "\r"), first=True)

This built-in handler re-feeds \n as \r so both key codes trigger the Enter action. It exists specifically because terminal behavior varies.

What Hermes does

@kb.add('c-j')
def handle_ctrl_enter(event):
    """Ctrl+Enter (c-j) inserts a newline."""
    event.current_buffer.insert_text('\n')

This binding shadows the built-in safety net. Now when any terminal sends \n for Enter, it inserts a newline instead of submitting.

Key mappings at the terminal level

Byte received prompt_toolkit Key Built-in behavior Hermes override
\r (0x0d) Keys.ControlM / Keys.Enter Submit or newline (multiline) ✅ Submit (works)
\n (0x0a) Keys.ControlJ Re-feed as \r → Submit ❌ Insert newline (broken)

Affected Terminals

  • Ghostty — known to send \n in certain states (see ghostty-org/ghostty#3320, #11563)
  • WSL terminals — prompt_toolkit's own comment documents this
  • Any terminal where stty icrnl gets disrupted or the terminal sends line feed for Enter

Reproduction

  1. Use a terminal that sends \n for Enter (Ghostty, or simulate with stty -icrnl before launching)
  2. Launch hermes
  3. Type any text and press Enter
  4. Expected: message submits
  5. Actual: cursor moves down, newline inserted in input area

Suggested Fix

Make the c-j handler behave like the enter handler (submit), and use only escape enter (Alt+Enter) for newline insertion. The c-j binding was intended for "Ctrl+Enter" but most terminals send c-j for Ctrl+J, not Ctrl+Enter — and some send it for plain Enter too.

# Option A: Remove the c-j binding entirely — let prompt_toolkit's 
# built-in remap c-j -> c-m handle it
# (Just delete the @kb.add('c-j') handler)

# Option B: Make c-j also submit (same as enter)
@kb.add('c-j')
def handle_ctrl_j(event):
    """Ctrl+J / \\n — treat as Enter (submit). Some terminals send \\n for Enter."""
    handle_enter(event)

Alt+Enter (escape enter) already works for multiline input, so removing the c-j newline binding loses nothing.

Environment

  • Hermes Agent: latest (git)
  • Terminal: Ghostty (xterm-ghostty)
  • OS: Arch Linux
  • prompt_toolkit: 3.0.52
  • Python: 3.14

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundcomp/cliCLI entry point, hermes_cli/, setup wizardtype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions