diff --git a/features/steps/tui_input_modes_steps.py b/features/steps/tui_input_modes_steps.py index 38f8ae327..3477e5cc8 100644 --- a/features/steps/tui_input_modes_steps.py +++ b/features/steps/tui_input_modes_steps.py @@ -143,3 +143,13 @@ def step_run_fallback_tui_app(context: Context) -> None: def step_fallback_tui_fails(context: Context, message: str) -> None: assert context.tui_fallback_error is not None assert message in str(context.tui_fallback_error) + + +@when('I detect mode for "{text}"') +def step_detect_mode(context: Context, text: str) -> None: + context.detected_mode = InputModeRouter.detect_mode(text) + + +@then('the detected mode should be "{mode}"') +def step_detected_mode_equals(context: Context, mode: str) -> None: + assert context.detected_mode.value == mode diff --git a/features/tui_input_modes.feature b/features/tui_input_modes.feature index 954e7cc12..52c60ece9 100644 --- a/features/tui_input_modes.feature +++ b/features/tui_input_modes.feature @@ -37,3 +37,23 @@ Feature: TUI input modes Then TUI textual availability should be boolean When I run fallback TUI app Then fallback TUI app should fail with "Textual dependency missing." + + Scenario: Dollar prefix activates shell mode + When I route TUI input "$echo hello" + Then the TUI mode should be "shell" + And the TUI shell stdout should contain "hello" + + Scenario: Dollar prefix detect_mode returns shell + When I detect mode for "$echo hello" + Then the detected mode should be "shell" + + + Scenario: Dollar prefix with leading whitespace activates shell mode + When I detect mode for " $echo hello" + Then the detected mode should be "shell" + + + Scenario: Dollar prefix blocks dangerous command by default + When I route TUI input "$rm -rf /" + Then the TUI mode should be "shell" + And the TUI shell stderr should contain "blocked dangerous shell command" diff --git a/src/cleveragents/tui/input/modes.py b/src/cleveragents/tui/input/modes.py index 76231444c..7b6728c08 100644 --- a/src/cleveragents/tui/input/modes.py +++ b/src/cleveragents/tui/input/modes.py @@ -51,7 +51,7 @@ class InputModeRouter: stripped = text.lstrip() if stripped.startswith("/"): return InputMode.COMMAND - if stripped.startswith("!"): + if stripped.startswith(("!", "$")): return InputMode.SHELL return InputMode.NORMAL