From 6593ac9b5bd4802990a4964759d6b912e061bb12 Mon Sep 17 00:00:00 2001 From: Ziheng Zhang <142805986+MuXinCG2004@users.noreply.github.com> Date: Thu, 18 Jun 2026 21:07:43 +0800 Subject: [PATCH] fix(cli): submit slash command on Enter when name prefixes another (#293) (#300) Typing `/model` and pressing Enter did nothing in the TUI; the picker only opened via `/model --save` or `/model `. The completion popup matched both `/model` and `/model-fallback` by prefix, so the exact-match-hide guard (which required a single match) never fired. With the popup still visible, the TUI's Enter handler completed the text instead of submitting the command, so it never executed. Treat the typed prefix as an exact match whenever it equals any matched command name, not only when it is the sole match. This hides the popup on a complete command name so Enter submits it, even when a longer command shares the prefix. --- EvoScientist/commands/_completion_engine.py | 12 ++++++++++-- tests/test_cli_completion.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/EvoScientist/commands/_completion_engine.py b/EvoScientist/commands/_completion_engine.py index 2b52b22..ac094e2 100644 --- a/EvoScientist/commands/_completion_engine.py +++ b/EvoScientist/commands/_completion_engine.py @@ -58,12 +58,20 @@ def compute_completions(text: str, cursor_pos: int) -> CompletionResult: commands = cmd_manager.list_commands() matches = [(c, d) for c, d in commands if c.startswith(prefix)] + # Whether the typed prefix is itself a complete command name. Checked + # via membership rather than ``len(matches) == 1`` so a command whose + # name is a strict prefix of another (e.g. ``/model`` vs + # ``/model-fallback``) still counts as an exact match — otherwise the + # popup never hides on the shorter command and Enter completes instead + # of submitting it. + exact = any(c == prefix for c, _ in matches) + # Exact match with no trailing space → hide - if len(matches) == 1 and matches[0][0] == prefix and not has_trailing_space: + if exact and not has_trailing_space: return CompletionResult(CompletionKind.EMPTY, []) # Exact match + trailing space + has subcommands → show subcommands - if len(matches) == 1 and matches[0][0] == prefix and has_trailing_space: + if exact and has_trailing_space: if not cmd_manager.get_subcommands(prefix): return CompletionResult(CompletionKind.EMPTY, []) sub_items = cmd_manager.list_subcommands(cmd_name) diff --git a/tests/test_cli_completion.py b/tests/test_cli_completion.py index a9224d1..606ae22 100644 --- a/tests/test_cli_completion.py +++ b/tests/test_cli_completion.py @@ -29,6 +29,20 @@ class TestSlashCommandCompleter: completions = list(completer.get_completions(_doc("/help"), None)) assert completions == [] + def test_exact_command_hides_even_when_prefix_of_another(self): + """Regression for #293: ``/model`` must hide on exact match so Enter + submits it, even though ``/model-fallback`` shares the prefix. Before + the fix the popup stayed visible (two prefix matches) and the TUI's + Enter handler completed the text instead of executing the command. + """ + completer = SlashCommandCompleter() + # Sanity: both commands share the prefix, so a partial prefix lists both. + partial = {c.text for c in completer.get_completions(_doc("/mode"), None)} + assert {"/model", "/model-fallback"} <= partial + # Exact ``/model`` with no trailing space → hide. + completions = list(completer.get_completions(_doc("/model"), None)) + assert completions == [] + def test_non_slash_returns_empty(self): completer = SlashCommandCompleter() completions = list(completer.get_completions(_doc("hello"), None))