feat: consolidate 'hermes version' into 'hermes --version', remove the subcommand

'hermes --version' (and -V) now prints the full version report — banner
version line with upstream SHA, install directory, authoritative install
method, Python and OpenAI SDK versions, and update status — making the
separate 'hermes version' subcommand redundant. The subcommand is removed.

- _startup_fast.print_fast_version_info() is now THE canonical version
  printer: static lines print instantly from stdlib probes, then the
  banner label, install-method resolver, and update check lazy-import
  after the first line is on screen (each degrades gracefully).
- main.py _print_version_info() delegates to it (used by /version in the
  CLI chat surface and the --version flag path); the old duplicate
  implementation is deleted.
- hermes_cli/subcommands/version.py removed; parser wiring, subcommand
  sets, console-engine extraction entry, and tests updated. Hermes
  Console keeps a 'version' command wired to the shared printer.
- Termux fast paths now include update status too (previously
  check_updates=False).
- Docs/i18n, CONTRIBUTING, SECURITY, and nix checks updated to
  'hermes --version'.
This commit is contained in:
Teknium
2026-08-20 02:46:40 -07:00
parent d425658d27
commit e69b8e561d
21 changed files with 109 additions and 123 deletions
+1 -1
View File
@@ -582,7 +582,7 @@ test(tools): añadir tests unitarios para file_operations
## Reportar Issues ## Reportar Issues
- Usa [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues) - Usa [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
- Incluye: SO, versión de Python, versión de Hermes (`hermes version`), traza de error completa - Incluye: SO, versión de Python, versión de Hermes (`hermes --version`), traza de error completa
- Incluye pasos para reproducir - Incluye pasos para reproducir
- Verifica los issues existentes antes de crear duplicados - Verifica los issues existentes antes de crear duplicados
- Para vulnerabilidades de seguridad, por favor reporta de forma privada - Para vulnerabilidades de seguridad, por favor reporta de forma privada
+1 -1
View File
@@ -973,7 +973,7 @@ test(tools): add unit tests for file_operations
## Reporting Issues ## Reporting Issues
- Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues) - Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
- Include: OS, Python version, Hermes version (`hermes version`), full error traceback - Include: OS, Python version, Hermes version (`hermes --version`), full error traceback
- Include steps to reproduce - Include steps to reproduce
- Check existing issues before creating duplicates - Check existing issues before creating duplicates
- For security vulnerabilities, please report privately - For security vulnerabilities, please report privately
+1 -1
View File
@@ -16,7 +16,7 @@ Un informe útil incluye:
- Una descripción concisa y evaluación de severidad. - Una descripción concisa y evaluación de severidad.
- El componente afectado, identificado por ruta de archivo y rango de líneas - El componente afectado, identificado por ruta de archivo y rango de líneas
(ej. `path/to/file.py:120-145`). (ej. `path/to/file.py:120-145`).
- Detalles del entorno (`hermes version`, SHA del commit, SO, versión de Python). - Detalles del entorno (`hermes --version`, SHA del commit, SO, versión de Python).
- Una reproducción contra `main` o el último release. - Una reproducción contra `main` o el último release.
- Una declaración de qué límite de confianza del §2 se cruza. - Una declaración de qué límite de confianza del §2 se cruza.
+1 -1
View File
@@ -16,7 +16,7 @@ A useful report includes:
- A concise description and severity assessment. - A concise description and severity assessment.
- The affected component, identified by file path and line range - The affected component, identified by file path and line range
(e.g. `path/to/file.py:120-145`). (e.g. `path/to/file.py:120-145`).
- Environment details (`hermes version`, commit SHA, OS, Python - Environment details (`hermes --version`, commit SHA, OS, Python
version). version).
- A reproduction against `main` or the latest release. - A reproduction against `main` or the latest release.
- A statement of which trust boundary in §2 is crossed. - A statement of which trust boundary in §2 is crossed.
+64 -11
View File
@@ -73,7 +73,7 @@ def is_termux_env() -> bool:
def is_termux_fast_version_argv(argv: list[str]) -> bool: def is_termux_fast_version_argv(argv: list[str]) -> bool:
return argv in (["--version"], ["-V"], ["version"]) return argv in (["--version"], ["-V"])
def is_global_fast_version_argv(argv: list[str]) -> bool: def is_global_fast_version_argv(argv: list[str]) -> bool:
@@ -180,12 +180,43 @@ def read_install_method() -> str | None:
return None return None
def print_fast_version_info() -> None: def print_fast_version_info(*, check_updates: bool = True) -> None:
from hermes_cli import __release_date__, __version__ """THE canonical ``hermes --version`` output (also used by /version).
The static lines print instantly from stdlib-only probes; everything
heavier (upstream SHA in the version line, authoritative install-method
detection, the update-status check) is lazy-imported AFTER the first
line is already on screen, so perceived latency stays instant while the
output carries the full information that used to require the (removed)
``hermes version`` subcommand. Every lazy block degrades gracefully —
a broken/heavy import can never take the basic version output down.
"""
# Line 1: registry-owned banner label (includes "· upstream <sha>" for
# git installs). banner.py keeps rich/prompt_toolkit lazy, so this
# import is light; fall back to the plain label if anything fails.
try:
from hermes_cli.banner import format_banner_version_label
print(format_banner_version_label())
except Exception:
from hermes_cli import __release_date__, __version__
print(f"Hermes Agent v{__version__} ({__release_date__})")
print(f"Hermes Agent v{__version__} ({__release_date__})")
print(f"Install directory: {project_root_str()}") print(f"Install directory: {project_root_str()}")
install_method = read_install_method()
# Install method: authoritative resolver first (code-scoped stamp →
# managed → nix → git → pip; also self-heals poisoned shared-home
# 'docker' stamps). Fall back to the cheap stdlib stamp probe only if
# the resolver import/run fails.
try:
from pathlib import Path
from hermes_cli.config import detect_install_method
install_method = detect_install_method(Path(project_root_str()))
except Exception:
install_method = read_install_method()
if install_method: if install_method:
print(f"Install method: {install_method}") print(f"Install method: {install_method}")
@@ -193,17 +224,39 @@ def print_fast_version_info() -> None:
openai_version = read_openai_version() openai_version = read_openai_version()
print(f"OpenAI SDK: {openai_version}" if openai_version else "OpenAI SDK: Not installed") print(f"OpenAI SDK: {openai_version}" if openai_version else "OpenAI SDK: Not installed")
print("Run 'hermes version' for update status.")
if not check_updates:
return
# Update status (synchronous — acceptable since the user asked for
# version info). Bounded by check_for_updates' own subprocess/network
# timeouts and its 6-hour cache; any failure prints nothing.
try:
from hermes_cli.banner import UPDATE_AVAILABLE_NO_COUNT, check_for_updates
from hermes_cli.config import recommended_update_command
behind = check_for_updates()
if behind == UPDATE_AVAILABLE_NO_COUNT:
print(f"Update available — run '{recommended_update_command()}'")
elif behind and behind > 0:
commits_word = "commit" if behind == 1 else "commits"
print(
f"Update available: {behind} {commits_word} behind — "
f"run '{recommended_update_command()}'"
)
elif behind == 0:
print("Up to date")
except Exception:
pass
def try_fast_version(argv: list[str] | None = None) -> bool: def try_fast_version(argv: list[str] | None = None) -> bool:
"""Handle ``hermes --version`` before the heavy import wall. """Handle ``hermes --version`` before the heavy import wall.
Termux keeps its historical contract (also accepts the ``version`` Only ``--version``/``-V`` (the ``version`` subcommand was removed —
subcommand + the HERMES_TERMUX_DISABLE_FAST_CLI escape hatch). Everywhere ``--version`` now carries the full output incl. update status), and
else: only ``--version``/``-V`` (the ``version`` subcommand stays on the never when container mode may need to route the command into the
slow path for full output incl. update check), and never when container container. Termux keeps the HERMES_TERMUX_DISABLE_FAST_CLI escape hatch.
mode may need to route the command into the container.
""" """
if argv is None: if argv is None:
argv = sys.argv[1:] argv = sys.argv[1:]
+8 -7
View File
@@ -567,6 +567,7 @@ class HermesConsoleEngine:
def _register_defaults(self) -> None: def _register_defaults(self) -> None:
self.register(("status",), "status", "Show Hermes component status.", _status) self.register(("status",), "status", "Show Hermes component status.", _status)
self.register(("version",), "version", "Show Hermes version information.", _version)
self.register(("doctor",), "doctor", "Run diagnostics without auto-fix.", _doctor) self.register(("doctor",), "doctor", "Run diagnostics without auto-fix.", _doctor)
self.register(("logs",), "logs [name] [-n N]", "Show recent Hermes logs.", _logs) self.register(("logs",), "logs [name] [-n N]", "Show recent Hermes logs.", _logs)
self.register(("sessions", "list"), "sessions list [--limit N]", "List recent sessions.", _sessions_list) self.register(("sessions", "list"), "sessions list [--limit N]", "List recent sessions.", _sessions_list)
@@ -613,13 +614,6 @@ class HermesConsoleEngine:
"""Register non-admin CLI commands that are safe for Hermes Console.""" """Register non-admin CLI commands that are safe for Hermes Console."""
extracted = { extracted = {
"version": (
"hermes_cli.subcommands.version",
"build_version_parser",
"cmd_version",
[()],
set(),
),
"dump": ( "dump": (
"hermes_cli.subcommands.dump", "hermes_cli.subcommands.dump",
"build_dump_parser", "build_dump_parser",
@@ -1280,6 +1274,13 @@ def _apply_confirmed_defaults(args: argparse.Namespace) -> None:
setattr(args, "yes", True) setattr(args, "yes", True)
def _version(_engine: HermesConsoleEngine, args: list[str]) -> str:
_expect_no_args(args, "version")
from hermes_cli._startup_fast import print_fast_version_info
return _capture_output(lambda: print_fast_version_info(check_updates=True))
def _status(_engine: HermesConsoleEngine, args: list[str]) -> str: def _status(_engine: HermesConsoleEngine, args: list[str]) -> str:
_expect_no_args(args, "status") _expect_no_args(args, "status")
from types import SimpleNamespace from types import SimpleNamespace
+12 -60
View File
@@ -34,7 +34,7 @@ Usage:
hermes honcho identity # Show AI peer identity representation hermes honcho identity # Show AI peer identity representation
hermes honcho identity <file> # Seed AI peer identity from a file (SOUL.md etc.) hermes honcho identity <file> # Seed AI peer identity from a file (SOUL.md etc.)
hermes honcho migrate # Step-by-step migration guide: OpenClaw native → Hermes + Honcho hermes honcho migrate # Step-by-step migration guide: OpenClaw native → Hermes + Honcho
hermes version Show version hermes --version Show version and update status
hermes update Update to latest version hermes update Update to latest version
hermes uninstall Uninstall Hermes Agent hermes uninstall Uninstall Hermes Agent
hermes acp Run as an ACP server for editor integration hermes acp Run as an ACP server for editor integration
@@ -468,7 +468,6 @@ from hermes_cli.subcommands.import_agent import build_import_agent_parser
from hermes_cli.subcommands.config import build_config_parser from hermes_cli.subcommands.config import build_config_parser
from hermes_cli.subcommands.skin import build_skin_parser from hermes_cli.subcommands.skin import build_skin_parser
from hermes_cli.subcommands.console import build_console_parser from hermes_cli.subcommands.console import build_console_parser
from hermes_cli.subcommands.version import build_version_parser
from hermes_cli.subcommands.update import build_update_parser from hermes_cli.subcommands.update import build_update_parser
from hermes_cli.subcommands.uninstall import build_uninstall_parser from hermes_cli.subcommands.uninstall import build_uninstall_parser
from hermes_cli.subcommands.dashboard import build_dashboard_parser from hermes_cli.subcommands.dashboard import build_dashboard_parser
@@ -5700,58 +5699,14 @@ def cmd_import(args):
def _print_version_info(*, check_updates: bool = True) -> None: def _print_version_info(*, check_updates: bool = True) -> None:
from hermes_cli.config import detect_install_method # Single source of truth for version output — shared with the
from hermes_cli.slash_exec import CommandContext, execute_command # `hermes --version` pre-import fast path (the `version` subcommand
# was consolidated into `--version`).
# Core version line is registry-owned (shared with the gateway /version); _startup_fast.print_fast_version_info(check_updates=check_updates)
# the install/python/SDK detail below is CLI-only decoration.
print(execute_command("version", CommandContext(surface="cli")).text)
print(f"Install directory: {PROJECT_ROOT}")
print(f"Install method: {detect_install_method(PROJECT_ROOT)}")
# Show Python version
print(f"Python: {sys.version.split()[0]}")
# Check for key dependencies. Use importlib.metadata rather than
# ``import openai`` — the SDK drags in ~800ms of pydantic-backed type
# modules just to expose ``__version__``. Metadata lookup is ~2ms.
try:
from importlib.metadata import version as _pkg_version, PackageNotFoundError
try:
print(f"OpenAI SDK: {_pkg_version('openai')}")
except PackageNotFoundError:
print("OpenAI SDK: Not installed")
except ImportError:
print("OpenAI SDK: Not installed")
if not check_updates:
return
# Show update status (synchronous — acceptable since user asked for version info)
try:
from hermes_cli.banner import UPDATE_AVAILABLE_NO_COUNT, check_for_updates
from hermes_cli.config import recommended_update_command
behind = check_for_updates()
if behind == UPDATE_AVAILABLE_NO_COUNT:
print(
f"Update available — run '{recommended_update_command()}'"
)
elif behind and behind > 0:
commits_word = "commit" if behind == 1 else "commits"
print(
f"Update available: {behind} {commits_word} behind — "
f"run '{recommended_update_command()}'"
)
elif behind == 0:
print("Up to date")
except Exception:
pass
def cmd_version(args): def cmd_version(args):
"""Show version.""" """Show version (--version/-V flag)."""
_print_version_info(check_updates=True) _print_version_info(check_updates=True)
@@ -10198,7 +10153,6 @@ def _coalesce_session_name_args(argv: list) -> list:
"mcp", "mcp",
"sessions", "sessions",
"insights", "insights",
"version",
"update", "update",
"uninstall", "uninstall",
"profile", "profile",
@@ -11644,7 +11598,7 @@ _BUILTIN_SUBCOMMANDS = frozenset(
"resume", "resume",
"send", "sessions", "setup", "send", "sessions", "setup",
"skin", "skills", "slack", "status", "sync", "tools", "uninstall", "update", "skin", "skills", "slack", "status", "sync", "tools", "uninstall", "update",
"version", "webhook", "whatsapp", "whatsapp-cloud", "worktree", "chat", "secrets", "security", "webhook", "whatsapp", "whatsapp-cloud", "worktree", "chat", "secrets", "security",
"verify", "verify",
# Help-ish invocations — plugin commands not being listed in # Help-ish invocations — plugin commands not being listed in
# top-level --help is an acceptable trade-off for skipping an # top-level --help is an acceptable trade-off for skipping an
@@ -12004,7 +11958,7 @@ def _try_termux_fast_cli_launch() -> bool:
return False return False
if _is_termux_fast_version_argv(argv): if _is_termux_fast_version_argv(argv):
_print_version_info(check_updates=False) _print_version_info(check_updates=True)
return True return True
first = _first_positional_argv() first = _first_positional_argv()
@@ -12022,7 +11976,7 @@ def _try_termux_fast_cli_launch() -> bool:
args = parser.parse_args(_coalesce_session_name_args(argv)) args = parser.parse_args(_coalesce_session_name_args(argv))
if getattr(args, "version", False): if getattr(args, "version", False):
_print_version_info(check_updates=False) _print_version_info(check_updates=True)
return True return True
if getattr(args, "oneshot", None): if getattr(args, "oneshot", None):
@@ -12926,7 +12880,7 @@ def main():
# own argparse tree. No hardcoded plugin commands in main.py. # own argparse tree. No hardcoded plugin commands in main.py.
# #
# Skipped when the invocation is already targeting a known built-in # Skipped when the invocation is already targeting a known built-in
# subcommand — ``hermes --help``, ``hermes version``, ``hermes logs``, # subcommand — ``hermes --help``, ``hermes logs``,
# etc. This avoids eagerly importing every bundled plugin module # etc. This avoids eagerly importing every bundled plugin module
# (google.cloud.pubsub_v1, aiohttp, grpc, PIL …) which costs # (google.cloud.pubsub_v1, aiohttp, grpc, PIL …) which costs
# 500-650ms on typical installs. # 500-650ms on typical installs.
@@ -13801,10 +13755,8 @@ def main():
# ========================================================================= # =========================================================================
build_claw_parser(subparsers, cmd_claw=cmd_claw) build_claw_parser(subparsers, cmd_claw=cmd_claw)
# ========================================================================= # NOTE: the `hermes version` subcommand was removed — `hermes --version`
# version command (parser built in hermes_cli/subcommands/version.py) # / `-V` now carries the full output including update status.
# =========================================================================
build_version_parser(subparsers, cmd_version=cmd_version)
# ========================================================================= # =========================================================================
# update command (parser built in hermes_cli/subcommands/update.py) # update command (parser built in hermes_cli/subcommands/update.py)
-18
View File
@@ -1,18 +0,0 @@
"""``hermes version`` subcommand parser.
Extracted verbatim from ``hermes_cli/main.py:main()`` (god-file Phase 2).
Handler injected to avoid importing ``main``.
"""
from __future__ import annotations
from typing import Callable
def build_version_parser(subparsers, *, cmd_version: Callable) -> None:
"""Attach the ``version`` subcommand to ``subparsers``."""
# =========================================================================
# version command
# =========================================================================
version_parser = subparsers.add_parser("version", help="Show version information")
version_parser.set_defaults(func=cmd_version)
+1 -1
View File
@@ -571,7 +571,7 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2)
echo "PASS: All binaries present" echo "PASS: All binaries present"
echo "=== Checking version ===" echo "=== Checking version ==="
${hermes-agent}/bin/hermes version 2>&1 | grep -qi "hermes" || (echo "FAIL: version check"; exit 1) ${hermes-agent}/bin/hermes --version 2>&1 | grep -qi "hermes" || (echo "FAIL: version check"; exit 1)
echo "PASS: Version check" echo "PASS: Version check"
echo "=== All checks passed ===" echo "=== All checks passed ==="
@@ -2,7 +2,7 @@
``hermes_cli.main`` skips eager plugin discovery at argparse-setup time ``hermes_cli.main`` skips eager plugin discovery at argparse-setup time
when the invocation is clearly targeting a known built-in subcommand. when the invocation is clearly targeting a known built-in subcommand.
This saves 500-650ms on ``hermes --help``, ``hermes version``, This saves 500-650ms on ``hermes --help``, ``hermes --version``,
``hermes logs``, etc., by not importing ``google.cloud.pubsub_v1``, ``hermes logs``, etc., by not importing ``google.cloud.pubsub_v1``,
``aiohttp``, ``grpc``, and friends. ``aiohttp``, ``grpc``, and friends.
@@ -35,7 +35,6 @@ from hermes_cli.subcommands.slack import build_slack_parser
from hermes_cli.subcommands.status import build_status_parser from hermes_cli.subcommands.status import build_status_parser
from hermes_cli.subcommands.uninstall import build_uninstall_parser from hermes_cli.subcommands.uninstall import build_uninstall_parser
from hermes_cli.subcommands.update import build_update_parser from hermes_cli.subcommands.update import build_update_parser
from hermes_cli.subcommands.version import build_version_parser
from hermes_cli.subcommands.webhook import build_webhook_parser from hermes_cli.subcommands.webhook import build_webhook_parser
from hermes_cli.subcommands.whatsapp import build_whatsapp_parser from hermes_cli.subcommands.whatsapp import build_whatsapp_parser
@@ -67,7 +66,6 @@ SINGLE_HANDLER_CASES = [
("backup", build_backup_parser, "cmd_backup", ["backup"]), ("backup", build_backup_parser, "cmd_backup", ["backup"]),
("import", build_import_cmd_parser, "cmd_import", ["import", "/tmp/x.zip"]), ("import", build_import_cmd_parser, "cmd_import", ["import", "/tmp/x.zip"]),
("config", build_config_parser, "cmd_config", ["config"]), ("config", build_config_parser, "cmd_config", ["config"]),
("version", build_version_parser, "cmd_version", ["version"]),
("update", build_update_parser, "cmd_update", ["update"]), ("update", build_update_parser, "cmd_update", ["update"]),
("uninstall", build_uninstall_parser, "cmd_uninstall", ["uninstall"]), ("uninstall", build_uninstall_parser, "cmd_uninstall", ["uninstall"]),
("gui", build_gui_parser, "cmd_gui", ["gui"]), ("gui", build_gui_parser, "cmd_gui", ["gui"]),
+1 -1
View File
@@ -287,7 +287,7 @@ When you ask Hermes to review a PR in a repository that has `.agents/checks/`, t
## Reporting Issues ## Reporting Issues
- Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues) - Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
- Include: OS, Python version, Hermes version (`hermes version`), full error traceback - Include: OS, Python version, Hermes version (`hermes --version`), full error traceback
- Include steps to reproduce - Include steps to reproduce
- Check existing issues before creating duplicates - Check existing issues before creating duplicates
- For security vulnerabilities, please report privately - For security vulnerabilities, please report privately
+5 -5
View File
@@ -147,7 +147,7 @@ Setting `addToSystemPackages = true` does two things: puts the `hermes` CLI on y
:::info :::info
When `container.enable = true` and `addToSystemPackages = true`, **every** `hermes` command on the host automatically routes into the managed container. This means your interactive CLI session runs inside the same environment as the gateway service — with access to all container-installed packages and tools. When `container.enable = true` and `addToSystemPackages = true`, **every** `hermes` command on the host automatically routes into the managed container. This means your interactive CLI session runs inside the same environment as the gateway service — with access to all container-installed packages and tools.
- The routing is transparent: `hermes chat`, `hermes sessions list`, `hermes version`, etc. all exec into the container under the hood - The routing is transparent: `hermes chat`, `hermes sessions list`, `hermes --version`, etc. all exec into the container under the hood
- All CLI flags are forwarded as-is - All CLI flags are forwarded as-is
- If the container isn't running, the CLI retries briefly (5s with a spinner for interactive use, 10s silently for scripts) then fails with a clear error — no silent fallback - If the container isn't running, the CLI retries briefly (5s with a spinner for interactive use, 10s silently for scripts) then fails with a clear error — no silent fallback
- For developers working on the hermes codebase, set `HERMES_DEV=1` to bypass container routing and run the local checkout directly - For developers working on the hermes codebase, set `HERMES_DEV=1` to bypass container routing and run the local checkout directly
@@ -191,7 +191,7 @@ systemctl status hermes-agent
journalctl -u hermes-agent -f journalctl -u hermes-agent -f
# If addToSystemPackages is true, test the CLI # If addToSystemPackages is true, test the CLI
hermes version hermes --version
hermes config # shows the generated config hermes config # shows the generated config
``` ```
@@ -681,7 +681,7 @@ journalctl --user -u hermes-agent -f
launchctl list | grep hermes launchctl list | grep hermes
tail -f ~/Library/Logs/hermes-agent.log tail -f ~/Library/Logs/hermes-agent.log
hermes version hermes --version
hermes config # shows the configuration that Nix wrote hermes config # shows the configuration that Nix wrote
``` ```
@@ -937,7 +937,7 @@ nix build .#checks.x86_64-linux.config-roundtrip # merge script preserves use
| Check | What it tests | | Check | What it tests |
|---|---| |---|---|
| `package-contents` | `hermes` and `hermes-agent` binaries exist and `hermes version` runs | | `package-contents` | `hermes` and `hermes-agent` binaries exist and `hermes --version` runs |
| `entry-points-sync` | Every `[project.scripts]` entry in `pyproject.toml` has a wrapped binary in the Nix package | | `entry-points-sync` | Every `[project.scripts]` entry in `pyproject.toml` has a wrapped binary in the Nix package |
| `cli-commands` | `hermes --help` exposes `gateway` and `config` subcommands | | `cli-commands` | `hermes --help` exposes `gateway` and `config` subcommands |
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` prints the NixOS error | | `managed-guard` | `HERMES_MANAGED=true hermes config set ...` prints the NixOS error |
@@ -1180,7 +1180,7 @@ nix-store --query --roots $(docker exec hermes-agent readlink /data/current-pack
| `Cannot save configuration: managed by NixOS` | CLI guards active | Edit `configuration.nix` and `nixos-rebuild switch` | | `Cannot save configuration: managed by NixOS` | CLI guards active | Edit `configuration.nix` and `nixos-rebuild switch` |
| `No adapter available for discord` (or telegram/slack) | Messaging deps missing from the sealed Nix venv | Install `#messaging` variant: `nix profile install ...#messaging`. For NixOS module: `extraDependencyGroups = [ "messaging" ]`. Check `journalctl -u hermes-agent` for `FeatureUnavailable` or `requirements not met` for the underlying error. | | `No adapter available for discord` (or telegram/slack) | Messaging deps missing from the sealed Nix venv | Install `#messaging` variant: `nix profile install ...#messaging`. For NixOS module: `extraDependencyGroups = [ "messaging" ]`. Check `journalctl -u hermes-agent` for `FeatureUnavailable` or `requirements not met` for the underlying error. |
| Container recreated unexpectedly | `extraVolumes`, `extraOptions`, or `image` changed | Expected — writable layer resets. Reinstall packages or use a custom image | | Container recreated unexpectedly | `extraVolumes`, `extraOptions`, or `image` changed | Expected — writable layer resets. Reinstall packages or use a custom image |
| `hermes version` shows old version | Container not restarted | `systemctl restart hermes-agent` | | `hermes --version` shows old version | Container not restarted | `systemctl restart hermes-agent` |
| Permission denied on `/var/lib/hermes` | State dir is `0750 hermes:hermes` | Use `docker exec` or `sudo -u hermes` | | Permission denied on `/var/lib/hermes` | State dir is `0750 hermes:hermes` | Use `docker exec` or `sudo -u hermes` |
| `nix-collect-garbage` removed hermes | GC root missing | Restart the service (preStart recreates the GC root) | | `nix-collect-garbage` removed hermes | GC root missing | Restart the service (preStart recreates the GC root) |
| `no container with name or ID "hermes-agent"` (Podman) | Podman rootful container not visible to regular user | Add passwordless sudo for podman (see [Container Mode](#container-mode) section) | | `no container with name or ID "hermes-agent"` (Podman) | Podman rootful container not visible to regular user | Add passwordless sudo for podman (see [Container Mode](#container-mode) section) |
+1 -1
View File
@@ -157,7 +157,7 @@ ln -sf "$PWD/venv/bin/hermes" "$PREFIX/bin/hermes"
### 6. Verify the install ### 6. Verify the install
```bash ```bash
hermes version hermes --version
hermes doctor hermes doctor
``` ```
+1 -1
View File
@@ -170,7 +170,7 @@ You no longer need to wrap `hermes update` in `screen` or `tmux` to survive a te
### Checking your current version ### Checking your current version
```bash ```bash
hermes version hermes --version
``` ```
Compare against the latest release at the [GitHub releases page](https://github.com/NousResearch/hermes-agent/releases). Compare against the latest release at the [GitHub releases page](https://github.com/NousResearch/hermes-agent/releases).
+2 -2
View File
@@ -97,7 +97,7 @@ hermes [global-options] <command> [subcommand/options]
| `hermes desktop` (alias `gui`) | Build and launch the native Electron desktop app. | | `hermes desktop` (alias `gui`) | Build and launch the native Electron desktop app. |
| `hermes profile` | Manage profiles — multiple isolated Hermes instances. | | `hermes profile` | Manage profiles — multiple isolated Hermes instances. |
| `hermes completion` | Print shell completion scripts (bash/zsh/fish). | | `hermes completion` | Print shell completion scripts (bash/zsh/fish). |
| `hermes version` | Show version information. | | `hermes --version` | Show version information. |
| `hermes update` | Pull latest code and reinstall dependencies. `--check` previews without installing; `--backup` takes a pre-pull `HERMES_HOME` snapshot. | | `hermes update` | Pull latest code and reinstall dependencies. `--check` previews without installing; `--backup` takes a pre-pull `HERMES_HOME` snapshot. |
| `hermes uninstall` | Remove Hermes from the system. | | `hermes uninstall` | Remove Hermes from the system. |
@@ -1758,7 +1758,7 @@ Additional behavior:
| Command | Description | | Command | Description |
|---------|-------------| |---------|-------------|
| `hermes version` | Print version information. | | `hermes --version` | Print version information. |
| `hermes update` | Pull latest changes and reinstall dependencies. | | `hermes update` | Pull latest changes and reinstall dependencies. |
| `hermes uninstall [--full] [--gui] [--dry-run] [--yes]` | Remove Hermes, optionally deleting all config/data. `--gui` removes only the desktop Chat GUI, leaving the agent intact; `--full` also deletes config/data; `--dry-run` prints what would be removed without changing anything; `--yes` skips prompts. | | `hermes uninstall [--full] [--gui] [--dry-run] [--yes]` | Remove Hermes, optionally deleting all config/data. `--gui` removes only the desktop Chat GUI, leaving the agent intact; `--full` also deletes config/data; `--dry-run` prints what would be removed without changing anything; `--yes` skips prompts. |
@@ -237,7 +237,7 @@ fix(security): prevent shell injection in sudo password piping
## 报告问题 ## 报告问题
- 使用 [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues) - 使用 [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
- 请包含:操作系统、Python 版本、Hermes 版本(`hermes version`)、完整错误堆栈 - 请包含:操作系统、Python 版本、Hermes 版本(`hermes --version`)、完整错误堆栈
- 包含复现步骤 - 包含复现步骤
- 创建前请检查是否已有重复 issue - 创建前请检查是否已有重复 issue
- 安全漏洞请私下报告 - 安全漏洞请私下报告
@@ -127,7 +127,7 @@ services.hermes-agent.environmentFiles = [ "/var/lib/hermes/env" ];
:::info :::info
当 `container.enable = true` 且 `addToSystemPackages = true` 时,主机上的**所有** `hermes` 命令都会自动路由到托管容器中执行。这意味着你的交互式 CLI 会话在与 gateway 服务相同的环境中运行——可以访问所有容器内安装的包和工具。 当 `container.enable = true` 且 `addToSystemPackages = true` 时,主机上的**所有** `hermes` 命令都会自动路由到托管容器中执行。这意味着你的交互式 CLI 会话在与 gateway 服务相同的环境中运行——可以访问所有容器内安装的包和工具。
- 路由是透明的:`hermes chat`、`hermes sessions list`、`hermes version` 等命令都会在底层 exec 进容器 - 路由是透明的:`hermes chat`、`hermes sessions list`、`hermes --version` 等命令都会在底层 exec 进容器
- 所有 CLI 参数原样转发 - 所有 CLI 参数原样转发
- 如果容器未运行,CLI 会短暂重试(交互式使用时显示 5 秒 spinner,脚本中静默等待 10 秒),然后以明确的错误退出——不会静默回退 - 如果容器未运行,CLI 会短暂重试(交互式使用时显示 5 秒 spinner,脚本中静默等待 10 秒),然后以明确的错误退出——不会静默回退
- 对于在 hermes 代码库上工作的开发者,设置 `HERMES_DEV=1` 可绕过容器路由,直接运行本地检出版本 - 对于在 hermes 代码库上工作的开发者,设置 `HERMES_DEV=1` 可绕过容器路由,直接运行本地检出版本
@@ -171,7 +171,7 @@ systemctl status hermes-agent
journalctl -u hermes-agent -f journalctl -u hermes-agent -f
# 如果 addToSystemPackages 为 true,测试 CLI # 如果 addToSystemPackages 为 true,测试 CLI
hermes version hermes --version
hermes config # 显示生成的配置 hermes config # 显示生成的配置
``` ```
@@ -763,7 +763,7 @@ nix build .#checks.x86_64-linux.config-roundtrip # 合并脚本保留用户
| 检查 | 测试内容 | | 检查 | 测试内容 |
|---|---| |---|---|
| `package-contents` | `hermes` 和 `hermes-agent` 二进制文件存在且 `hermes version` 可运行 | | `package-contents` | `hermes` 和 `hermes-agent` 二进制文件存在且 `hermes --version` 可运行 |
| `entry-points-sync` | `pyproject.toml` 中 `[project.scripts]` 的每个条目在 Nix 包中都有对应的封装二进制文件 | | `entry-points-sync` | `pyproject.toml` 中 `[project.scripts]` 的每个条目在 Nix 包中都有对应的封装二进制文件 |
| `cli-commands` | `hermes --help` 暴露 `gateway` 和 `config` 子命令 | | `cli-commands` | `hermes --help` 暴露 `gateway` 和 `config` 子命令 |
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` 打印 NixOS 错误 | | `managed-guard` | `HERMES_MANAGED=true hermes config set ...` 打印 NixOS 错误 |
@@ -967,7 +967,7 @@ nix-store --query --roots $(docker exec hermes-agent readlink /data/current-pack
|---|---|---| |---|---|---|
| `Cannot save configuration: managed by NixOS` | CLI 守卫已激活 | 编辑 `configuration.nix` 并执行 `nixos-rebuild switch` | | `Cannot save configuration: managed by NixOS` | CLI 守卫已激活 | 编辑 `configuration.nix` 并执行 `nixos-rebuild switch` |
| 容器意外重建 | `extraVolumes`、`extraOptions` 或 `image` 发生变更 | 预期行为——可写层重置。重新安装包或使用自定义镜像 | | 容器意外重建 | `extraVolumes`、`extraOptions` 或 `image` 发生变更 | 预期行为——可写层重置。重新安装包或使用自定义镜像 |
| `hermes version` 显示旧版本 | 容器未重启 | `systemctl restart hermes-agent` | | `hermes --version` 显示旧版本 | 容器未重启 | `systemctl restart hermes-agent` |
| `/var/lib/hermes` 权限拒绝 | 状态目录为 `0750 hermes:hermes` | 使用 `docker exec` 或 `sudo -u hermes` | | `/var/lib/hermes` 权限拒绝 | 状态目录为 `0750 hermes:hermes` | 使用 `docker exec` 或 `sudo -u hermes` |
| `nix-collect-garbage` 删除了 hermes | GC root 缺失 | 重启服务(preStart 会重新创建 GC root) | | `nix-collect-garbage` 删除了 hermes | GC root 缺失 | 重启服务(preStart 会重新创建 GC root) |
| `no container with name or ID "hermes-agent"`(Podman) | Podman rootful 容器对普通用户不可见 | 为 podman 添加免密 sudo(参见[容器模式](#container-mode)章节) | | `no container with name or ID "hermes-agent"`(Podman) | Podman rootful 容器对普通用户不可见 | 为 podman 添加免密 sudo(参见[容器模式](#container-mode)章节) |
@@ -118,7 +118,7 @@ ln -sf "$PWD/venv/bin/hermes" "$PREFIX/bin/hermes"
### 6. 验证安装 ### 6. 验证安装
```bash ```bash
hermes version hermes --version
hermes doctor hermes doctor
``` ```
@@ -120,7 +120,7 @@ tail -f ~/.hermes/logs/update.log
### 查看当前版本 ### 查看当前版本
```bash ```bash
hermes version hermes --version
``` ```
与 [GitHub releases 页面](https://github.com/NousResearch/hermes-agent/releases) 上的最新版本进行比较。 与 [GitHub releases 页面](https://github.com/NousResearch/hermes-agent/releases) 上的最新版本进行比较。
@@ -78,7 +78,7 @@ hermes [global-options] <command> [subcommand/options]
| `hermes dashboard` | 启动用于管理配置、API 密钥和会话的 Web 控制台。 | | `hermes dashboard` | 启动用于管理配置、API 密钥和会话的 Web 控制台。 |
| `hermes profile` | 管理 profile——多个隔离的 Hermes 实例。 | | `hermes profile` | 管理 profile——多个隔离的 Hermes 实例。 |
| `hermes completion` | 打印 shell 补全脚本(bash/zsh/fish)。 | | `hermes completion` | 打印 shell 补全脚本(bash/zsh/fish)。 |
| `hermes version` | 显示版本信息。 | | `hermes --version` | 显示版本信息。 |
| `hermes update` | 拉取最新代码并重新安装依赖。`--check` 预览而不安装;`--backup` 在拉取前对 `HERMES_HOME` 进行快照。 | | `hermes update` | 拉取最新代码并重新安装依赖。`--check` 预览而不安装;`--backup` 在拉取前对 `HERMES_HOME` 进行快照。 |
| `hermes uninstall` | 从系统中删除 Hermes。 | | `hermes uninstall` | 从系统中删除 Hermes。 |
@@ -1246,7 +1246,7 @@ hermes update [--check] [--backup] [--restart-gateway]
| 命令 | 说明 | | 命令 | 说明 |
|---------|-------------| |---------|-------------|
| `hermes version` | 打印版本信息。 | | `hermes --version` | 打印版本信息。 |
| `hermes update` | 拉取最新变更并重新安装依赖。 | | `hermes update` | 拉取最新变更并重新安装依赖。 |
| `hermes uninstall [--full] [--yes]` | 删除 Hermes,可选择删除所有 config/数据。 | | `hermes uninstall [--full] [--yes]` | 删除 Hermes,可选择删除所有 config/数据。 |