feat(cli): configurable default interface (cli vs tui)

Add `display.interface` config key so users can make the modern TUI the
default for bare `hermes` / `hermes chat` without exporting HERMES_TUI=1 in
every shell. Default stays "cli" to preserve current behavior.

Add a `--cli` flag (mirrors `--tui`) so an explicit invocation can force the
classic prompt_toolkit REPL even when `display.interface: tui` is configured.

Precedence (highest first): `--cli` > `--tui`/`HERMES_TUI=1` > config
`display.interface` > classic REPL. Two resolvers enforce it:

  * `_resolve_use_tui(args)` — the args-aware resolver used by `cmd_chat`
    and the Termux fast-TUI path (uses full load_config()).
  * `_wants_tui_early(argv)` — a dependency-free early resolver used by
    mouse-residue suppression and the Termux fast paths, which run before
    argparse / hermes_cli.config are importable (minimal cached YAML read).

Both `--cli` and `--tui` are registered via `_inherited_flag`, so they are
carried across self-relaunch automatically.

- config: add display.interface ("cli" default), bump _config_version 25->26.
  The generic missing-field migration + load_config() deep-merge seed the key
  for existing configs; no bespoke migration block needed.
- docs: document --cli flag and display.interface in cli-commands.md and
  the TUI user guide.
- tests: new test_default_interface_resolution.py covering resolver
  precedence at every layer, early resolver edge cases (missing/garbage
  config), parser flags, and relaunch inheritance.
This commit is contained in:
Brooklyn Nicholson 2026-06-02 20:49:44 -05:00
parent 54343bcade
commit d6b0c23f87
6 changed files with 312 additions and 9 deletions

View file

@ -41,6 +41,8 @@ _EPILOGUE = """
Examples:
hermes Start interactive chat
hermes chat -q "Hello" Single query mode
hermes --tui Launch the modern TUI (or set display.interface: tui)
hermes --cli Force the classic REPL (overrides display.interface: tui)
hermes -c Resume the most recent session
hermes -c "my project" Resume a session by name (latest in lineage)
hermes --resume <session_id> Resume a specific session by ID
@ -218,6 +220,13 @@ def build_top_level_parser():
default=False,
help="Launch the modern TUI instead of the classic REPL",
)
_inherited_flag(
parser,
"--cli",
action="store_true",
default=False,
help="Force the classic prompt_toolkit REPL (overrides display.interface=tui)",
)
_inherited_flag(
parser,
"--dev",
@ -369,6 +378,13 @@ def build_top_level_parser():
default=False,
help="Launch the modern TUI instead of the classic REPL",
)
_inherited_flag(
chat_parser,
"--cli",
action="store_true",
default=False,
help="Force the classic prompt_toolkit REPL (overrides display.interface=tui)",
)
_inherited_flag(
chat_parser,
"--dev",