diff --git a/apps/desktop/src/lib/desktop-slash-commands.test.ts b/apps/desktop/src/lib/desktop-slash-commands.test.ts index a541a0def01..e4874f4dfaf 100644 --- a/apps/desktop/src/lib/desktop-slash-commands.test.ts +++ b/apps/desktop/src/lib/desktop-slash-commands.test.ts @@ -15,6 +15,7 @@ describe('desktop slash command curation', () => { expect(isDesktopSlashSuggestion('/branch')).toBe(true) expect(isDesktopSlashSuggestion('/skin')).toBe(true) expect(isDesktopSlashSuggestion('/usage')).toBe(true) + expect(isDesktopSlashSuggestion('/version')).toBe(true) expect(isDesktopSlashSuggestion('/yolo')).toBe(true) expect(isDesktopSlashCommand('/yolo')).toBe(true) }) diff --git a/apps/desktop/src/lib/desktop-slash-commands.ts b/apps/desktop/src/lib/desktop-slash-commands.ts index 424eba758c2..84b2af8393e 100644 --- a/apps/desktop/src/lib/desktop-slash-commands.ts +++ b/apps/desktop/src/lib/desktop-slash-commands.ts @@ -43,6 +43,7 @@ const DESKTOP_COMMAND_META = [ ['/title', 'Rename the current session'], ['/undo', 'Remove the last user/assistant exchange'], ['/usage', 'Show token usage for this session'], + ['/version', 'Show Hermes Agent version'], ['/yolo', 'Toggle YOLO — auto-approve dangerous commands'] ] as const diff --git a/cli.py b/cli.py index 42accb398c7..1c4899c65e7 100644 --- a/cli.py +++ b/cli.py @@ -9015,6 +9015,10 @@ class HermesCLI: elif canonical == "update": if self._handle_update_command(): return False + elif canonical == "version": + from hermes_cli.main import _print_version_info + + _print_version_info(check_updates=True) elif canonical == "paste": self._handle_paste_command() elif canonical == "image": diff --git a/gateway/run.py b/gateway/run.py index 40721eada6b..2ed53531e2f 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -7932,6 +7932,8 @@ class GatewayRunner: return await self._handle_profile_command(event) if _cmd_def_inner.name == "update": return await self._handle_update_command(event) + if _cmd_def_inner.name == "version": + return await self._handle_version_command(event) # Catch-all: any other recognized slash command reached the # running-agent guard. Reject gracefully rather than falling @@ -8288,6 +8290,9 @@ class GatewayRunner: if canonical == "update": return await self._handle_update_command(event) + if canonical == "version": + return await self._handle_version_command(event) + if canonical == "debug": return await self._handle_debug_command(event) @@ -10913,6 +10918,12 @@ class GatewayRunner: return event.platform_update_id <= recorded_uid + async def _handle_version_command(self, event: MessageEvent) -> str: + """Handle /version — show the running Hermes Agent version.""" + from hermes_cli import __release_date__, __version__ + + return f"Hermes Agent v{__version__} ({__release_date__})" + async def _handle_help_command(self, event: MessageEvent) -> str: """Handle /help command - list available commands.""" from hermes_cli.commands import gateway_help_lines diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index f022ef32b07..f970a1053db 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -216,6 +216,7 @@ COMMAND_REGISTRY: list[CommandDef] = [ CommandDef("image", "Attach a local image file for your next prompt", "Info", cli_only=True, args_hint=""), CommandDef("update", "Update Hermes Agent to the latest version", "Info"), + CommandDef("version", "Show Hermes Agent version", "Info", aliases=("v",)), CommandDef("debug", "Upload debug report (system info + logs) and get shareable links", "Info"), # Exit @@ -349,6 +350,7 @@ ACTIVE_SESSION_BYPASS_COMMANDS: frozenset[str] = frozenset( "steer", "stop", "update", + "version", } ) diff --git a/tests/cli/test_version_command.py b/tests/cli/test_version_command.py new file mode 100644 index 00000000000..c3facc85180 --- /dev/null +++ b/tests/cli/test_version_command.py @@ -0,0 +1,28 @@ +"""Tests for the /version slash command.""" + +from unittest.mock import patch + +from cli import HermesCLI +from hermes_cli.commands import GATEWAY_KNOWN_COMMANDS, resolve_command + + +def test_version_command_is_registered(): + cmd = resolve_command("version") + assert cmd is not None + assert cmd.name == "version" + assert cmd.category == "Info" + assert resolve_command("v") is cmd + + +def test_version_is_gateway_known(): + assert "version" in GATEWAY_KNOWN_COMMANDS + assert "v" in GATEWAY_KNOWN_COMMANDS + + +def test_process_command_version_prints_version_info(): + cli_obj = HermesCLI.__new__(HermesCLI) + + with patch("hermes_cli.main._print_version_info") as mock_print: + assert cli_obj.process_command("/version") is True + + mock_print.assert_called_once_with(check_updates=True) diff --git a/tests/gateway/test_version_command.py b/tests/gateway/test_version_command.py new file mode 100644 index 00000000000..420c95d97c5 --- /dev/null +++ b/tests/gateway/test_version_command.py @@ -0,0 +1,12 @@ +"""Tests for gateway /version command.""" + +import asyncio + +from hermes_cli import __release_date__, __version__ + + +def test_gateway_version_command_returns_release_line(): + from gateway.run import GatewayRunner + + result = asyncio.run(GatewayRunner._handle_version_command(None, None)) # type: ignore[arg-type] + assert result == f"Hermes Agent v{__version__} ({__release_date__})"