cf60ebbdfd264c3fc067b9e3230df8752c50d2ec
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a55a52c72f |
feat(cli): complete the Ctrl+S prompt stash — keybinding, state machine, tests
Finishes the input stash started in the preceding commit from PR #4771. That PR shipped only the panel renderer: its `@kb.add('c-s')` handler and stash-state initialization were lost in a rebase, so the panel predicate read undefined `_stash_panel_open` / `_stash_list` and the feature was unreachable. This adds the missing half and the tests the PR never had. Resolves the review feedback on #4771: - Rebuilt the stash on current main's keybinding setup. The `c-s` key was unbound repo-wide, so there is no conflict. - Extracted the state machine into `hermes_cli/prompt_stash.py` as pure functions (no prompt_toolkit import) so it is directly unit testable — the PR was cli.py-only with zero tests. - Dropped the PR's unrelated changes: delegation `supervisor_model` / `execution_model` config aliases, and stale reverts of the banner builder, worktree pruning, logging setup, and MCP toolset validation that its 14k-commit-old base dragged along. - Fixed the 📌 double-width measurement for real. Three commits in the PR ("subtract 1 from len()", "use bare len()", "subtract 1 again") were chasing this by tweaking `len()`; all horizontal math now goes through `_status_bar_display_width` (prompt_toolkit `get_cwidth`), which also keeps CJK previews inside the border. Narrow terminals fall back to compact header/footer labels instead of overflowing — caught by a parametrized width test, not by eyeballing. Gesture (the contributor's design, kept): - Composer has content → push onto the stash, clear the input. - Composer empty, one stashed → pop it straight back. - Composer empty, 2+ stashed → open the browse panel (↑↓ / Enter / D / Esc). - Panel open → Ctrl+S closes it. Pushing onto a stack rather than a single slot is what makes repeated Ctrl+S safe: a second stash never silently overwrites the first, and with 2+ parked the panel asks rather than guessing which to restore. A `📌 N` status-bar badge and a composer placeholder advertise the parked draft so it cannot be silently forgotten. Deliberate departures from the PR: - No auto-restore after the agent responds, and no `display.stash_auto_restore` config key. The PR itself had already defaulted this to false as "avoids surprising the user"; a keystroke the user pressed should not cause text to reappear on its own, so the dead default is dropped rather than carried as config surface. - Nothing is persisted to disk. Drafts routinely contain pasted credentials and NDA material, so the stash is session-scoped and in-memory only. Any future persistence must route through `get_hermes_home()`. - Suppressed while a modal prompt owns the composer (sudo / secret / approval / clarify / slash-confirm / model picker) so Ctrl+S can never stash a password. - Restoring images extends `_attached_images` instead of replacing it, so an attachment added since the stash was taken is not silently dropped. - `buf.reset()` on stash (not `text = ""`) clears completion state, selection, and undo stack with the text. Tests: 95 new tests across two files — 66 on the state machine (empty buffer is a no-op, exact round-trips including newlines/tabs/CJK/fences, no-clobber ordering, cap eviction, indicator states, panel cursor clamping and deletion, the full resolve_ctrl_s decision table) and 29 on the cli.py wiring (per-instance stash, keybinding registration guard, layout slot, panel bounded at 8 widths, status-bar indicator lifecycle). The keybinding-registration test asserts the `c-s` handler exists in source specifically so the rebase loss that broke #4771 cannot recur. Verified: 153 passed, 0 failed across the two new files plus tests/cli/test_cli_init.py and tests/cli/test_cli_extension_hooks.py. ruff check clean; check-windows-footguns clean. Docs: Ctrl+S added to the CLI keybindings table. Co-authored-by: CK iRonin.IT <cyprian@ironin.pl> |