Conversation
Session browse uses a hand-rolled curses loop for live filtering, but some terminals deliver arrows as raw CSI bytes. Reusing the existing curses menu decoder preserves prompt text behavior while making raw ESC [ A/B navigate like translated curses key constants. Constraint: Terminal arrow keys may arrive as raw CSI/SS3 sequences even under curses. Rejected: Switch the browser to the shared menu driver | it would be a wider rewrite of the live filter/table UI. Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep printable search handling before nav decoding so j/k/space continue to filter rather than becoming menu shortcuts. Tested: scripts/run_tests.sh tests/hermes_cli/test_session_browse.py tests/hermes_cli/test_curses_arrow_keys.py Tested: python -m ruff check hermes_cli/main.py tests/hermes_cli/test_session_browse.py Tested: python scripts/check-windows-footguns.py hermes_cli/main.py tests/hermes_cli/test_session_browse.py Tested: git diff --check -- hermes_cli/main.py tests/hermes_cli/test_session_browse.py Not-tested: Full repository test suite.
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: LGTM
Fixes raw CSI arrow key navigation in the sessions browse curses picker. Uses _decode_menu_key to translate raw escape sequences (ESC [ A/B) into NAV_UP/NAV_DOWN actions, making navigation work in terminals that don't emit curses.KEY_UP/KEY_DOWN constants.
Looks Good
- Correct ordering: check backspace and printable chars before key decode
- Tests use FakeStdscr that simulates real terminal behavior (timeout-based getch)
- Raw CSI sequence tests confirm fix works
Reviewed by Hermes Agent
|
Thanks for the focused regression fix. The reported session-browser bug is present on current main: Problems
Suggested changes
Automated hermes-sweeper review. |
The plugins composite picker had the same hand-rolled curses input gap as the session browser: terminals that surface arrows as raw CSI bytes would hit the ESC exit path before navigation could happen. Route its key dispatch through the shared curses decoder while preserving page/home/end handling and existing toggle/select behavior. Constraint: Keep the composite plugin picker shape unchanged for this focused PR follow-up. Rejected: Convert the picker to the generic curses menu driver | broader UI rewrite than the review comment requires. Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep PageUp/PageDown/Home/End checks on the raw key because the shared decoder only normalizes common menu actions. Tested: scripts/run_tests.sh tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_session_browse.py tests/hermes_cli/test_curses_arrow_keys.py Tested: python -m ruff check hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_session_browse.py hermes_cli/main.py Tested: python scripts/check-windows-footguns.py hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py hermes_cli/main.py tests/hermes_cli/test_session_browse.py Tested: git diff --check -- hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py hermes_cli/main.py tests/hermes_cli/test_session_browse.py Not-tested: Full repository test suite.
|
Addressed the sweeper follow-up for the sibling Changes added in
Validation rerun:
|
Resolve the plugins composite menu test conflict by keeping upstream canonical-key coverage and the raw CSI arrow regression coverage. Constraint: Preserve already-reviewed PR commits while clearing GitHub's merge-conflict state Confidence: high Scope-risk: narrow Tested: scripts/run_tests.sh tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_session_browse.py tests/hermes_cli/test_curses_arrow_keys.py Tested: python -m ruff check hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_session_browse.py hermes_cli/main.py Tested: python scripts/check-windows-footguns.py hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py hermes_cli/main.py tests/hermes_cli/test_session_browse.py Tested: git diff --check -- hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py hermes_cli/main.py tests/hermes_cli/test_session_browse.py Not-tested: Full repository test suite
What does this PR do?
Fixes raw CSI arrow-key handling in hand-rolled Hermes curses pickers. In affected terminals, Up/Down can arrive as raw escape sequences (
ESC [ A/ESC [ B) instead of translatedcurses.KEY_UP/curses.KEY_DOWNconstants. The old loops treated the leadingESCbyte as cancel/exit before reading the CSI continuation.This PR now routes both affected pickers through the existing shared curses menu decoder:
hermes sessions browsehermes pluginscomposite toggle pickerThe change preserves existing text-filter behavior in the session browser and existing plugin picker behavior for page/home/end, toggle, select, and cancel.
Related Issue
N/A. Reproduced locally with
scripts/keystroke_diagnostic.py: Up emitted\x1b[A, Down emitted\x1b[B.Type of Change
Changes Made
hermes_cli/main.py: route non-printable keys in_session_browse_pickerthrough the existing curses escape-sequence decoder, while keeping printable filter input,q, Backspace, Enter, and lone Esc semantics intact.tests/hermes_cli/test_session_browse.py: add regression coverage for rawESC [ BandESC [ Aarrow sequences, and make the fake curses input model support short escape continuation timeouts.hermes_cli/plugins_cmd.py: route the composite plugins picker through the same shared decoder so raw CSI arrows navigate instead of exiting, while preserving PageUp/PageDown/Home/End handling on raw curses keys.tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py: add regression coverage proving rawESC [ Btoggles the second plugin and rawESC [ Awraps to the last plugin instead of exiting.How to Test
scripts/run_tests.sh tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_session_browse.py tests/hermes_cli/test_curses_arrow_keys.py$HOME/.hermes/hermes-agent/venv/bin/python -m ruff check hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_session_browse.py hermes_cli/main.py$HOME/.hermes/hermes-agent/venv/bin/python scripts/check-windows-footguns.py hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py hermes_cli/main.py tests/hermes_cli/test_session_browse.pygit diff --check -- hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py hermes_cli/main.py tests/hermes_cli/test_session_browse.pyChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests pass (not run; targeted tests above passed)Documentation & Housekeeping
docs/, docstrings) — N/Acli-config.yaml.exampleif I added/changed config keys — N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/Acheck-windows-footguns.pyScreenshots / Logs
Relevant verification output: