Feature: REPL module full coverage Comprehensive unit-level tests that exercise every function and branch inside ``src/cleveragents/cli/commands/repl.py`` to bring its coverage from 0% to near-100%. # ----------------------------------------------------------------- # Constants # ----------------------------------------------------------------- Scenario: Module constants are defined correctly Given replcov the repl module is imported Then the constant _MULTILINE_CONTINUATION equals backslash And the constant _LAST_COMMAND_TOKEN equals "!!" And the _REPL_COMMANDS list is non-empty And the _BUILTIN_COMMANDS list contains help exit and quit # ----------------------------------------------------------------- # _get_prompt_context # ----------------------------------------------------------------- Scenario: _get_prompt_context returns bare prompt when no env vars set Given neither CLEVERAGENTS_PROJECT nor CLEVERAGENTS_PLAN is set When I call _get_prompt_context Then the prompt result is "agents> " Scenario: _get_prompt_context returns project-only prompt Given CLEVERAGENTS_PROJECT is set to "proj1" and CLEVERAGENTS_PLAN is unset When I call _get_prompt_context Then the prompt result is "agents (proj1)> " Scenario: _get_prompt_context returns project+plan prompt Given CLEVERAGENTS_PROJECT is set to "proj1" and CLEVERAGENTS_PLAN is set to "plan1" When I call _get_prompt_context Then the prompt result is "agents (proj1/plan1)> " # ----------------------------------------------------------------- # _setup_history # ----------------------------------------------------------------- Scenario: _setup_history loads existing history file Given a temporary history file that exists When I call _setup_history with that path Then readline.read_history_file is called with the path And readline.set_history_length is called with 10000 Scenario: _setup_history skips read when file does not exist Given a temporary history path that does not exist When I call _setup_history with that path Then readline.read_history_file is not called And readline.set_history_length is called with 10000 # ----------------------------------------------------------------- # _save_history # ----------------------------------------------------------------- Scenario: _save_history writes the history file Given a temporary history path for saving When I call _save_history with that path Then readline.write_history_file is called with the path Scenario: _save_history suppresses OSError Given readline.write_history_file will raise OSError When I call _save_history with a dummy path Then no exception is raised # ----------------------------------------------------------------- # _setup_completer # ----------------------------------------------------------------- Scenario: _setup_completer installs readline completer When I call _setup_completer Then readline.set_completer is called with a callable And readline.set_completer_delims is called And readline.parse_and_bind is called with tab complete Scenario: The installed completer returns matching tokens Given the completer is installed via _setup_completer When I query the completer with text "ver" and state 0 Then the completer returns "version" Scenario: The installed completer returns None for out-of-range state Given the completer is installed via _setup_completer When I query the completer with text "ver" and state 99 Then the completer returns None # ----------------------------------------------------------------- # _read_multiline # ----------------------------------------------------------------- Scenario: _read_multiline reads continuation lines ending normally Given builtins.input will return "continued line" When I call _read_multiline with initial line ending in backslash Then the multiline result is "first continued line" Scenario: _read_multiline handles chained continuations Given builtins.input will return chained continuations When I call _read_multiline with initial line ending in backslash Then the multiline result is "first second third" Scenario: _read_multiline handles EOFError in continuation Given builtins.input will raise EOFError on continuation When I call _read_multiline with initial line ending in backslash Then the multiline result is "only" Scenario: _read_multiline handles KeyboardInterrupt in continuation Given builtins.input will raise KeyboardInterrupt on continuation When I call _read_multiline with initial line ending in backslash Then the multiline result is "only" # ----------------------------------------------------------------- # _print_repl_help # ----------------------------------------------------------------- Scenario: _print_repl_help runs without error When I call _print_repl_help Then no exception is raised from _print_repl_help # ----------------------------------------------------------------- # dispatch_command # ----------------------------------------------------------------- Scenario: dispatch_command returns 0 on success Given the root Typer app is mocked to succeed When I call dispatch_command with args "version" Then the dispatch return code is 0 Scenario: dispatch_command returns code from SystemExit Given the root Typer app raises SystemExit with code 42 When I call dispatch_command with args "bad" Then the dispatch return code is 42 Scenario: dispatch_command returns 0 for SystemExit with None code Given the root Typer app raises SystemExit with None code When I call dispatch_command with args "something" Then the dispatch return code is 0 Scenario: dispatch_command returns 130 on KeyboardInterrupt Given the root Typer app raises KeyboardInterrupt When I call dispatch_command with args "slow" Then the dispatch return code is 130 Scenario: dispatch_command returns 1 and prints error on generic Exception Given the root Typer app raises RuntimeError boom When I call dispatch_command with args "fail" Then the dispatch return code is 1 And the dispatch error output contains "boom" # ----------------------------------------------------------------- # run_repl — exit commands # ----------------------------------------------------------------- Scenario: run_repl exits on :exit command Given the REPL input sequence is | line | | :exit | When I run the REPL loop Then the REPL loop exit code is 0 Scenario: run_repl exits on :quit command Given the REPL input sequence is | line | | :quit | When I run the REPL loop Then the REPL loop exit code is 0 # ----------------------------------------------------------------- # run_repl — help # ----------------------------------------------------------------- Scenario: run_repl handles :help and continues Given the REPL input sequence is | line | | :help | | :exit | When I run the REPL loop Then the REPL loop output contains "Built-in commands" # ----------------------------------------------------------------- # run_repl — !! repeat # ----------------------------------------------------------------- Scenario: run_repl repeats last command with !! Given the REPL input sequence is | line | | version | | !! | | :exit | And dispatch_command is mocked for run_repl When I run the REPL loop Then dispatch_command was called at least 2 times with "version" Scenario: run_repl warns when !! has no previous command Given the REPL input sequence is | line | | !! | | :exit | When I run the REPL loop Then the REPL loop output contains "No previous command" # ----------------------------------------------------------------- # run_repl — empty and whitespace input # ----------------------------------------------------------------- Scenario: run_repl skips empty lines Given the REPL input sequence is | line | | | | :exit | When I run the REPL loop Then the REPL loop exit code is 0 # ----------------------------------------------------------------- # run_repl — multiline # ----------------------------------------------------------------- Scenario: run_repl handles multiline input ending with backslash Given the REPL input sequence for multiline is "version \" then "" then ":exit" And dispatch_command is mocked for run_repl When I run the REPL loop Then the REPL loop exit code is 0 # ----------------------------------------------------------------- # run_repl — KeyboardInterrupt / EOFError # ----------------------------------------------------------------- Scenario: run_repl handles KeyboardInterrupt and continues Given the REPL input sequence for interrupt then exit When I run the REPL loop Then the REPL loop exit code is 0 Scenario: run_repl handles EOFError for clean exit Given the REPL input sequence is empty When I run the REPL loop Then the REPL loop exit code is 0 # ----------------------------------------------------------------- # run_repl — shlex parse error # ----------------------------------------------------------------- Scenario: run_repl handles shlex parse error gracefully Given the REPL input sequence has an unclosed quote then exit When I run the REPL loop Then the REPL loop output contains "Parse error" # ----------------------------------------------------------------- # run_repl — history on/off # ----------------------------------------------------------------- Scenario: run_repl with history enabled calls setup and save Given the REPL input sequence is | line | | :exit | When I run the REPL loop with history enabled Then _setup_history was called And _save_history was called Scenario: run_repl with no_history skips setup and save Given the REPL input sequence is | line | | :exit | When I run the REPL loop with no_history Then _setup_history was not called And _save_history was not called # ----------------------------------------------------------------- # run_repl — dispatch a real-ish command # ----------------------------------------------------------------- Scenario: run_repl dispatches tokenised command Given the REPL input sequence is | line | | info | | :exit | And dispatch_command is mocked for run_repl When I run the REPL loop Then dispatch_command was called with args "info" # ----------------------------------------------------------------- # repl_callback # ----------------------------------------------------------------- Scenario: repl_callback starts REPL when stdout is a tty Given sys.stdout.isatty returns True And run_repl is mocked to return 0 When I call repl_callback Then run_repl was invoked And repl_callback raises SystemExit with code 0 Scenario: repl_callback starts REPL when CLEVERAGENTS_FORCE_REPL is set Given sys.stdout.isatty returns False And CLEVERAGENTS_FORCE_REPL is set in the environment And run_repl is mocked to return 0 When I call repl_callback Then run_repl was invoked Scenario: repl_callback does nothing when not a tty and no force env Given sys.stdout.isatty returns False And CLEVERAGENTS_FORCE_REPL is not set When I call repl_callback Then run_repl was not invoked # ----------------------------------------------------------------- # Completer edge case: empty text matches all tokens # ----------------------------------------------------------------- Scenario: The installed completer returns first token for empty text Given the completer is installed via _setup_completer When I query the completer with empty text and state 0 Then the completer returns a non-None value