Verify native hermes is actually the right agent before trusting it

hermes_cli_args() previously trusted any 'hermes' found on native PATH
without checking what it actually was. Windows machines can have an
unrelated tool also named 'hermes' (softwarepub/HERMES, an academic
software-publication tool with harvest/process/curate/deposit
subcommands - confirmed to be what was actually on this machine's
PATH), which would silently get used instead of the real NousResearch
agent installed in WSL, producing the misleading 'Hermes needs setup'
message.

Now check that a native 'hermes' actually exposes the agent's 'chat'
subcommand before using it directly, falling back to the WSL bridge
otherwise.
This commit is contained in:
Claude 2026-07-08 23:03:44 +00:00
parent d36785c7ee
commit 2d131bfb8a
1 changed files with 18 additions and 5 deletions

View File

@ -142,16 +142,29 @@ def append_audit(entry: dict):
# ─── Agent Discovery (instant filesystem checks) ────────────────────
def _cli_has_subcommand(base_args: list, subcommand: str) -> bool:
try:
r = subprocess.run([*base_args, "--help"], capture_output=True, text=True, timeout=10)
return subcommand in ((r.stdout or "") + (r.stderr or ""))
except Exception:
return False
def hermes_cli_args(*args: str) -> list:
"""Build the command to invoke Hermes, bridging through WSL if it's only installed there.
"""Build the command to invoke Hermes, bridging through WSL if the real agent only lives there.
The dashboard commonly runs as a native Windows process while Hermes (whose official
installer is Bash-only) lives inside WSL - a plain PATH lookup on Windows will never find it.
installer is Bash-only) lives inside WSL - a plain PATH lookup on Windows will never find it
there. Windows machines can also have an unrelated tool also named 'hermes' on PATH (e.g. the
academic softwarepub/HERMES metadata-publishing project, which coincidentally shares the name),
so don't just trust that a native 'hermes' is the right one - confirm it exposes the
NousResearch agent's `chat` subcommand before using it, falling back to WSL otherwise.
"""
if shutil.which("hermes") is not None or shutil.which("wsl") is None:
if shutil.which("hermes") is not None and _cli_has_subcommand(["hermes"], "chat"):
return ["hermes", *args]
quoted = " ".join(shlex.quote(a) for a in args)
return ["wsl", "-e", "bash", "-lc", f"hermes {quoted}"]
if shutil.which("wsl") is not None:
quoted = " ".join(shlex.quote(a) for a in args)
return ["wsl", "-e", "bash", "-lc", f"hermes {quoted}"]
return ["hermes", *args]
def hermes_available() -> bool:
try: