refactor(cli): decompose HermesCLI.run() (3433 -> 384 LOC) into _tui_* methods

70 nested closures (every prompt_toolkit key handler and handler factory, the
clarify/sudo/secret/approval/model-picker/command-palette/stash display
renderers, hint/placeholder/spinner callables, process_loop, spinner_loop,
_signal_handler, wake-startup) become HermesCLI._tui_* methods; run() only
binds them (kb.add(...)(self._tui_x)). The run() locals they closed over are
published on self at their original binding sites (_tui_multiline_shortcuts,
the four paste list-cells; process_loop reads self._app). Then four zero-
back-ref prologue regions become _tui_print_startup, _tui_init_run_state,
_tui_build_key_bindings -> kb and _tui_build_layout(kb) -> (layout, style).

All moved bodies verified AST-identical modulo cli_ref->self and the free-var
rewrites; unresolved-free-name check on every new method is empty. Source-
inspection tests repointed: getsource(run) -> _tui_process_loop, AST lookup of
handle_enter -> _tui_handle_enter.
This commit is contained in:
Teknium
2026-09-02 12:00:16 -07:00
parent 0227cf7d9c
commit 57b2dfdf0e
4 changed files with 2306 additions and 2248 deletions
+2302 -2245
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -133,7 +133,7 @@ def test_handle_enter_never_gates_on_command_running():
target = None
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == "handle_enter":
if isinstance(node, ast.FunctionDef) and node.name == "_tui_handle_enter":
target = node
break
assert target is not None, "handle_enter closure not found in cli.py"
+1 -1
View File
@@ -32,7 +32,7 @@ def _load_handle_enter_node() -> ast.FunctionDef:
target = None
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == "handle_enter":
if isinstance(node, ast.FunctionDef) and node.name == "_tui_handle_enter":
target = node
break
assert target is not None, "handle_enter closure not found in cli.py"
@@ -93,7 +93,8 @@ class TestFinallyBlockWiring:
"""
def test_recovery_is_invoked_behind_interrupt_guard(self):
src = inspect.getsource(HermesCLI.run)
# process_loop is the REPL worker thread body (HermesCLI._tui_process_loop).
src = inspect.getsource(HermesCLI._tui_process_loop)
# The recovery call must be gated on _last_turn_interrupted so it only
# fires after an actual interrupt, not on every normal turn.
guard = re.search(