Feature: TUI App Coverage Scenarios exercising previously uncovered code paths in cleveragents.tui.app module, including the Textual-based application class behind a mocked Textual import gate. Background: Given the TUI app module is imported with mocked Textual # --- Module-level import gate (lines 31-38) --- Scenario: Mocked Textual import makes _TEXTUAL_AVAILABLE True Then the reloaded app module should have _TEXTUAL_AVAILABLE True Scenario: Mocked Textual import assigns _TextualApp from textual.app Then the reloaded app module should expose _TextualCleverAgentsTuiApp # --- textual_available function (line 43-45) --- Scenario: textual_available returns True when Textual is mocked Then textual_available should return True on the reloaded module # --- SessionView dataclass (lines 48-54) --- Scenario: SessionView can be created with session_id and transcript When I create a SessionView with session_id "sess-1" and transcript entries Then the SessionView session_id should be "sess-1" And the SessionView transcript should contain 2 entries # --- _CommandRouter protocol (lines 56-61) --- Scenario: A class implementing _CommandRouter protocol can be used When I create a _CommandRouter implementation Then calling handle on the implementation should return a response # --- _TextualCleverAgentsTuiApp.__init__ (lines 91-100) --- Scenario: The Textual TUI app can be instantiated with mocked Textual Given a mock command router and persona state When I instantiate the Textual TUI app Then the app should have a _session with session_id "default" And the app should store the command router And the app should store the persona state # --- CSS_PATH and BINDINGS class variables (lines 84-89) --- Scenario: The Textual TUI app has correct class variables Given a mock command router and persona state When I instantiate the Textual TUI app Then the app class should have CSS_PATH set to "cleveragents.tcss" And the app class should have 6 key bindings # --- compose method (lines 102-112) --- Scenario: compose yields the expected widget tree Given a mock command router and persona state When I instantiate the Textual TUI app And I call compose on the app Then compose should yield at least 5 widgets # --- on_mount method (lines 114-121) --- Scenario: on_mount initialises persona bar and overlays Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app Then the persona bar should have content set And the help panel should be hidden on mount And the reference picker should have suggestions initialised And the slash overlay should be hidden on mount # --- action_help method (lines 123-125) --- Scenario: action_help opens a main-screen help panel Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I call action_help on the app Then the help panel text should contain "Help: Main Screen" And the help panel text should contain "ctrl+q" Scenario: action_help reflects slash command context Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I set the prompt text to "/persona list" And I call action_help on the app Then the help panel text should contain "Help: Slash Commands" Scenario: action_help reflects shell mode context Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I set the prompt text to "!ls" And I call action_help on the app Then the help panel text should contain "Help: Shell Mode" Scenario: action_help toggles the visible help panel off Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I call action_help on the app And I call action_help on the app Then the help panel should be hidden on mount # --- action_cycle_preset method (lines 127-129) --- Scenario: action_cycle_preset cycles the preset and refreshes the bar Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I call action_cycle_preset on the app Then the persona bar content should be refreshed # --- _refresh_persona_bar method (lines 131-142) --- Scenario: _refresh_persona_bar builds scope text from active persona Given a mock command router and persona state When I instantiate the Textual TUI app And I call _refresh_persona_bar on the app Then the persona bar should display the persona name and scope # --- on_input_submitted with empty text (lines 144-150) --- Scenario: on_input_submitted with empty text returns early Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit empty text to the app Then the conversation widget should not be updated by input # --- on_input_submitted with command mode (lines 152-167) --- Scenario: on_input_submitted routes slash commands Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "/help" to the app Then the conversation widget should contain "handled:help" # --- on_input_submitted with shell mode (lines 168-177) --- Scenario: on_input_submitted routes shell commands Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "!echo tui_shell_test" to the app Then the conversation widget should contain "tui_shell_test" Scenario: on_input_submitted surfaces shell safety warnings Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "!rm -rf /tmp" to the app with shell execution mocked Then the shell warning indicator should be visible And the prompt should be marked as dangerous Scenario: shell warning indicator is cleared after safe command Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "!rm -rf /tmp" to the app with shell execution mocked And I submit "!echo cleared" to the app with shell execution mocked Then the shell warning indicator should not be visible And the prompt should not be marked as dangerous Scenario: shell danger warnings can be disabled via settings Given shell danger warnings are disabled in settings And a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "!rm -rf /tmp" to the app with shell execution mocked Then the shell warning indicator should not be visible And the prompt should not be marked as dangerous Scenario: ShellSafetyService owns dangerous command confirmation Given a mock command router and persona state When I instantiate the Textual TUI app And I ask the app to confirm shell command "rm -rf /tmp" Then the shell confirmation result should be allowed Scenario: shell confirmation allows safe commands without ShellSafetyService Given shell danger warnings are disabled in settings And a mock command router and persona state When I instantiate the Textual TUI app And I ask the app to confirm shell command "echo safe" Then the shell confirmation result should be allowed Scenario: shell confirmation uses the dangerous shell flag without ShellSafetyService Given shell danger warnings are disabled in settings And a mock command router and persona state When I instantiate the Textual TUI app And I ask the app to confirm shell command "rm -rf /tmp" Then the shell confirmation result should be blocked Scenario: shell warning callback allows execution when warnings are disabled Given shell danger warnings are disabled in settings And a mock command router and persona state When I instantiate the Textual TUI app And I ask the app to handle a shell warning Then the shell warning callback result should be allowed # --- on_input_submitted with shell returning None (lines 170-171) --- Scenario: on_input_submitted handles None shell result Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit shell text that produces a None shell result Then the conversation widget should contain "(no shell output)" # --- on_input_submitted normal mode with @ reference (lines 179-185) --- Scenario: on_input_submitted handles normal text with @ references Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "check @README" to the app Then the conversation widget should contain "check" And the reference picker should have been updated # --- on_input_submitted normal mode without @ (line 179, 185) --- Scenario: on_input_submitted handles normal text without references Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "plain message" to the app Then the conversation widget should contain "plain message" # --- CleverAgentsTuiApp alias (line 189) --- Scenario: CleverAgentsTuiApp resolves to the Textual app when available Then CleverAgentsTuiApp should be _TextualCleverAgentsTuiApp on the reloaded module # --- on_input_submitted shell with stdout output (lines 173-176) --- Scenario: on_input_submitted displays shell stdout and command Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "!echo coverage_run" to the app Then the conversation widget should contain "$ echo coverage_run" # --- on_input_submitted shell with empty output (line 174) --- Scenario: on_input_submitted shows empty output for silent shell commands Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I submit "!true" to the app Then the conversation widget should contain "(empty output)" # --- THEME class variable (issue #4742) --- @tdd_issue @tdd_issue_4742 Scenario: The Textual TUI app has THEME set to dracula by default Given a mock command router and persona state When I instantiate the Textual TUI app Then the app class should have THEME set to "dracula" # --- action_escape cascade (issue #6450) --- Scenario: action_escape cascades overlays toward the prompt Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I set the prompt text to "draft @README.md" And I show the actor selection overlay And I show the help panel overlay for context "Main Screen" And I show the slash overlay for query "help" And I show the reference picker overlay for query "README.md" And I call action_escape on the app Then the actor selection overlay should be hidden And the help panel should remain visible When I call action_escape on the app Then the help panel should be hidden on mount And the slash overlay should remain visible When I call action_escape on the app Then the slash overlay should be hidden And the reference picker overlay should remain visible When I call action_escape on the app Then the reference picker overlay should be hidden And the prompt text should be "draft" # --- on_input_changed handler (live overlay updates) --- Scenario: on_input_changed with slash prefix filters slash commands live Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I trigger on_input_changed with text "/ses" Then the slash overlay should show only commands matching "ses" And the reference picker should be reset to empty Scenario: on_input_changed with at-sign updates reference picker live Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I trigger on_input_changed with text "@proj" Then the reference picker should have been updated And the slash overlay should be reset to all commands Scenario: on_input_changed with plain text resets both overlays Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I trigger on_input_changed with text "hello world" Then the slash overlay should be reset to all commands And the reference picker should be reset to empty Scenario: on_input_changed with empty at-sign resets reference picker Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I trigger on_input_changed with text "send @" Then the reference picker should be reset to empty Scenario: on_input_changed with empty text resets both overlays Given a mock command router and persona state When I instantiate the Textual TUI app And I call on_mount on the app And I trigger on_input_changed with empty text Then the slash overlay should be reset to all commands And the reference picker should be reset to empty