"""The relayed-command dispatcher: what runs, what asks first, what's refused.""" import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from bolt_console import tools as tools_mod from bolt_console.tools import Tools class FakeUI: def __init__(self, answers=()): self.answers = list(answers) self.calls = [] self.results = [] self.questions = [] def tool_call(self, name, argument): self.calls.append((name, argument)) def tool_result(self, text, error=False): self.results.append((text, error)) def diff(self, old, new): self.calls.append(("diff", old, new)) def confirm(self, question): self.questions.append(question) return self.answers.pop(0) if self.answers else "no" class FakeShell: def __init__(self, output="[exit 0]\nhello"): self.output = output self.ran = [] def __call__(self, command): self.ran.append(command) return self.output def filectl(**payload) -> str: return "filectl " + json.dumps(payload) def test_shell_command_asks_first_and_runs_on_yes(): ui, shell = FakeUI(["yes"]), FakeShell() output = Tools(ui, run_shell=shell).handle("echo hello") assert shell.ran == ["echo hello"] assert output == "[exit 0]\nhello" assert ui.calls == [("Shell", "echo hello")] assert ui.results == [("hello", False)] def test_declined_command_never_runs_and_tells_the_model_why(): ui, shell = FakeUI(["no"]), FakeShell() output = Tools(ui, run_shell=shell).handle("rm -rf build") assert shell.ran == [] assert "declined" in output and "did not run" in output def test_always_switches_the_session_to_auto(): ui, shell = FakeUI(["always"]), FakeShell() tools = Tools(ui, run_shell=shell) tools.handle("echo one") tools.handle("echo two") assert tools.mode == "auto" assert len(ui.questions) == 1 assert shell.ran == ["echo one", "echo two"] def test_auto_mode_never_asks(): ui, shell = FakeUI(), FakeShell() Tools(ui, mode="auto", run_shell=shell).handle("echo hi") assert ui.questions == [] assert shell.ran == ["echo hi"] def test_failed_command_is_shown_as_an_error_with_its_status(): ui = FakeUI(["yes"]) Tools(ui, run_shell=FakeShell("[exit 2]\nno such file")).handle("cat nope") assert ui.results == [("[exit 2]\nno such file", True)] def test_reads_and_lists_never_ask(tmp_path): (tmp_path / "a.txt").write_text("one\ntwo\n", encoding="utf-8") ui = FakeUI() tools = Tools(ui, run_shell=FakeShell()) read = tools.handle(filectl(op="read", path=str(tmp_path / "a.txt"))) listed = tools.handle(filectl(op="list", path=str(tmp_path))) assert ui.questions == [] assert "one" in read and "two" in read assert "a.txt" in listed assert ui.calls[0] == ("Read", str(tmp_path / "a.txt")) assert ui.results[0] == ("Read 2 lines", False) def test_write_asks_and_declining_leaves_the_disk_alone(tmp_path): target = tmp_path / "new.txt" ui = FakeUI(["no"]) output = Tools(ui).handle(filectl(op="write", path=str(target), content="x")) assert not target.exists() assert "declined" in output def test_edit_shows_the_diff_then_applies_on_yes(tmp_path): target = tmp_path / "f.py" target.write_text("x = 1\n", encoding="utf-8") ui = FakeUI(["yes"]) output = Tools(ui).handle(filectl(op="edit", path=str(target), old="x = 1", new="x = 2")) assert target.read_text(encoding="utf-8") == "x = 2\n" assert ("diff", "x = 1", "x = 2") in ui.calls assert "edited" in output def test_filectl_errors_go_back_to_the_model_not_up_the_stack(tmp_path): ui = FakeUI() output = Tools(ui).handle(filectl(op="read", path=str(tmp_path / "missing.txt"))) assert output.startswith("[filectl] no such file") assert ui.results[-1][1] is True def test_pet_commands_are_explained_rather_than_run(): ui, shell = FakeUI(), FakeShell() output = Tools(ui, mode="auto", run_shell=shell).handle("petctl emote wave") assert shell.ran == [] assert output == tools_mod.PET_UNAVAILABLE.format(name="petctl") def test_dialogue_scenes_are_explained_rather_than_run(): shell = FakeShell() output = Tools(FakeUI(), mode="auto", run_shell=shell).handle('dialoguectl {"lines": []}') assert shell.ran == [] assert "isn't available" in output def test_a_stray_command_label_is_stripped_and_reported(): """Seen live: the server relayed `command: powershell -Command "..."` and cmd.exe answered "'command:' is not recognized".""" ui, shell = FakeUI(["yes"]), FakeShell() output = Tools(ui, run_shell=shell).handle('command: powershell -Command "Get-PnpDevice"') assert shell.ran == ['powershell -Command "Get-PnpDevice"'] assert ui.calls[0] == ("Shell", 'powershell -Command "Get-PnpDevice"') assert 'removed a leading "command:" label' in output def test_wrapping_backticks_are_stripped(): shell = FakeShell() Tools(FakeUI(), mode="auto", run_shell=shell).handle("`git status`") assert shell.ran == ["git status"] def test_the_label_is_stripped_before_filectl_is_recognised(tmp_path): (tmp_path / "a.txt").write_text("hi", encoding="utf-8") output = Tools(FakeUI()).handle( "command: filectl " + json.dumps({"op": "read", "path": str(tmp_path / "a.txt")})) assert "hi" in output and "command:" in output # read ran; note explains the fix def test_a_clean_command_gets_no_note(): output = Tools(FakeUI(), mode="auto", run_shell=FakeShell()).handle("echo hi") assert "note" not in output