Files
cleveragents-core/features/steps/tui_input_modes_steps.py
T
HAL9000 0050a0ed50 fix: detect dollar prefix as shell mode trigger in InputModeRouter
The spec requires both ! and dollar sign to activate shell mode, but
detect_mode() only recognised ! as the shell mode trigger. This caused
dollar-prefixed inputs like dollar-sign-echo-hello to be routed to NORMAL
mode instead of SHELL mode.

- Add dollar sign to detect_mode() shell prefix check alongside !
- Add BDD tests for dollar prefix shell mode detection
- Add step definitions for detect_mode() direct testing

Closes #10412
2026-04-24 03:01:13 +00:00

156 lines
5.3 KiB
Python

"""Behave steps for TUI input mode router."""
from __future__ import annotations
import os
import shutil
import tempfile
from pathlib import Path
from unittest.mock import patch
from behave import given, then, when
from behave.runner import Context
from cleveragents.tui import app as tui_app_module
from cleveragents.tui.input import reference_parser
from cleveragents.tui.input.modes import InputModeRouter
from cleveragents.tui.input.reference_parser import parse_references, suggestions
from cleveragents.tui.input.shell_exec import looks_dangerous
@when('I parse TUI references for "{line}"')
def step_parse_references(context: Context, line: str) -> None:
context.tui_ref_result = parse_references(line)
@then('the parsed line should include "{token}"')
def step_parsed_contains(context: Context, token: str) -> None:
assert token in context.tui_ref_result.expanded_line
@when('I route TUI input "{line}"')
def step_route_input(context: Context, line: str) -> None:
router = InputModeRouter(command_handler=lambda raw: f"ok:/{raw}")
context.tui_mode_result = router.process(line)
@then('the TUI mode should be "{mode}"')
def step_mode_equals(context: Context, mode: str) -> None:
assert context.tui_mode_result.mode.value == mode
@then('the TUI command result should be "{value}"')
def step_command_result(context: Context, value: str) -> None:
assert context.tui_mode_result.command_result == value
@then('the TUI shell stdout should contain "{value}"')
def step_shell_stdout(context: Context, value: str) -> None:
shell = context.tui_mode_result.shell_result
assert shell is not None
assert value in shell.stdout
@then('the TUI shell stderr should contain "{value}"')
def step_shell_stderr(context: Context, value: str) -> None:
shell = context.tui_mode_result.shell_result
assert shell is not None
assert value in shell.stderr
@when('I check shell danger detection for "{command}"')
def step_check_shell_danger(context: Context, command: str) -> None:
context.shell_danger_result = looks_dangerous(command)
@then('shell danger detection result should be "{value}"')
def step_shell_danger_result(context: Context, value: str) -> None:
expected = value.strip().lower() == "true"
assert context.shell_danger_result is expected
@given("a deterministic TUI reference fixture")
def step_deterministic_tui_reference_fixture(context: Context) -> None:
fixture_dir = Path(tempfile.mkdtemp())
(fixture_dir / "README.md").write_text("# fixture\n", encoding="utf-8")
(fixture_dir / ".git").mkdir(exist_ok=True)
(fixture_dir / ".git" / "secret.txt").write_text("secret\n", encoding="utf-8")
context.tui_reference_fixture_dir = fixture_dir
context.add_cleanup(lambda: shutil.rmtree(str(fixture_dir), ignore_errors=True))
@when("I parse and suggest TUI references using fixture")
def step_parse_and_suggest_with_fixture(context: Context) -> None:
context.tui_walk_count = 0
context.tui_fixture_suggestions = []
reference_parser._catalog_cache["cwd"] = None
reference_parser._catalog_cache["created_at"] = 0.0
reference_parser._catalog_cache["catalog"] = None
real_walk = os.walk
def counting_walk(*args: object, **kwargs: object):
context.tui_walk_count += 1
return real_walk(*args, **kwargs)
with (
patch(
"cleveragents.tui.input.reference_parser.Path.cwd",
return_value=context.tui_reference_fixture_dir,
),
patch(
"cleveragents.tui.input.reference_parser.os.walk", side_effect=counting_walk
),
):
parse_references("inspect @README.md")
context.tui_fixture_suggestions = suggestions("secret", category="resource")
@then('TUI walk count should be "{count}"')
def step_tui_walk_count(context: Context, count: str) -> None:
assert context.tui_walk_count == int(count)
@then('TUI suggestions should not include "{value}"')
def step_tui_suggestions_not_include(context: Context, value: str) -> None:
assert all(
value not in suggestion for suggestion in context.tui_fixture_suggestions
)
@when("I check TUI app textual availability flag")
def step_check_tui_textual_availability(context: Context) -> None:
context.tui_textual_available = tui_app_module.textual_available()
@then("TUI textual availability should be boolean")
def step_tui_textual_is_bool(context: Context) -> None:
assert isinstance(context.tui_textual_available, bool)
@when("I run fallback TUI app")
def step_run_fallback_tui_app(context: Context) -> None:
context.tui_fallback_error = None
fallback = tui_app_module._FallbackCleverAgentsTuiApp(
command_router=object(), persona_state=object()
)
try:
fallback.run()
except RuntimeError as exc:
context.tui_fallback_error = exc
@then('fallback TUI app should fail with "{message}"')
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