From 86e64c1d3bc0324452202cf8e26703dbef7839b3 Mon Sep 17 00:00:00 2001 From: MrBob Date: Sun, 3 May 2026 16:49:37 -0300 Subject: [PATCH] fix(gateway): hide required-arg commands from Telegram menu --- hermes_cli/commands.py | 15 +++++++++++++-- scripts/release.py | 1 + tests/hermes_cli/test_commands.py | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index 07e7273bf7..681f228ae9 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -399,6 +399,11 @@ def _is_gateway_available(cmd: CommandDef, config_overrides: set[str] | None = N return False +def _requires_argument(args_hint: str) -> bool: + """Return True when selecting a command without text would be incomplete.""" + return args_hint.strip().startswith("<") + + def gateway_help_lines() -> list[str]: """Generate gateway help text lines from the registry.""" overrides = _resolve_config_gates() @@ -455,7 +460,9 @@ def telegram_bot_commands() -> list[tuple[str, str]]: Telegram command names cannot contain hyphens, so they are replaced with underscores. Aliases are skipped -- Telegram shows one menu entry per - canonical command. + canonical command. Commands that require arguments are skipped because + selecting a Telegram BotCommand sends only ``/command`` and would execute + an incomplete command. Plugin-registered slash commands are included so plugins get native autocomplete in Telegram without touching core code. @@ -465,10 +472,14 @@ def telegram_bot_commands() -> list[tuple[str, str]]: for cmd in COMMAND_REGISTRY: if not _is_gateway_available(cmd, overrides): continue + if _requires_argument(cmd.args_hint): + continue tg_name = _sanitize_telegram_name(cmd.name) if tg_name: result.append((tg_name, cmd.description)) - for name, description, _args_hint in _iter_plugin_command_entries(): + for name, description, args_hint in _iter_plugin_command_entries(): + if _requires_argument(args_hint): + continue tg_name = _sanitize_telegram_name(name) if tg_name: result.append((tg_name, description)) diff --git a/scripts/release.py b/scripts/release.py index 8f3e094ca0..b8fec8e95f 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -623,6 +623,7 @@ AUTHOR_MAP = { "cine.dreamer.one@gmail.com": "LeonSGP43", "zyprothh@gmail.com": "Zyproth", "amitgaur@gmail.com": "amitgaur", + "albuquerque.abner@gmail.com": "mrbob-git", "leozeli@qq.com": "leozeli", "linlehao@cuhk.edu.cn": "LehaoLin", "liutong@isacas.ac.cn": "I3eg1nner", diff --git a/tests/hermes_cli/test_commands.py b/tests/hermes_cli/test_commands.py index d505c8a1a7..620611ad42 100644 --- a/tests/hermes_cli/test_commands.py +++ b/tests/hermes_cli/test_commands.py @@ -236,6 +236,13 @@ class TestTelegramBotCommands: tg_name = cmd.name.replace("-", "_") assert tg_name not in names + def test_excludes_commands_with_required_args(self): + names = {name for name, _ in telegram_bot_commands()} + assert "background" not in names + assert "queue" not in names + assert "steer" not in names + assert "background" in GATEWAY_KNOWN_COMMANDS + class TestSlackSubcommandMap: def test_returns_dict(self): @@ -1661,6 +1668,19 @@ class TestPluginCommandEnumeration: names = {name for name, _desc in telegram_bot_commands()} assert "metricas" in names + def test_plugin_command_with_required_args_excluded_from_telegram_menu(self, monkeypatch): + """Telegram BotCommand selections cannot supply required arguments.""" + self._patch_plugin_commands(monkeypatch, { + "background-job": { + "handler": lambda _a: "ok", + "description": "Run a background job", + "args_hint": "", + "plugin": "jobs-plugin", + } + }) + names = {name for name, _desc in telegram_bot_commands()} + assert "background_job" not in names + def test_plugin_command_appears_in_slack_subcommand_map(self, monkeypatch): """/hermes metricas must route through the Slack subcommand map.""" self._patch_plugin_commands(monkeypatch, {