From 6575fb0f80dd01a3d53ce47485cc7e65d0acc67f Mon Sep 17 00:00:00 2001 From: GodsBoy Date: Thu, 6 Aug 2026 22:51:31 +0200 Subject: [PATCH] fix(plugins): preserve opaque stdio commands --- hermes_cli/agent_plugins.py | 19 ++++++++++++++++--- tests/hermes_cli/test_agent_plugins.py | 9 ++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/hermes_cli/agent_plugins.py b/hermes_cli/agent_plugins.py index ba6e846e2f..dad1d9e946 100644 --- a/hermes_cli/agent_plugins.py +++ b/hermes_cli/agent_plugins.py @@ -260,8 +260,14 @@ def _expand(value: str, plugin_root: Path, data_root: Path) -> str: return _PLACEHOLDER_RE.sub(lambda match: replacements[match.group(1)], value) -def _resolve_scoped_path(value: str, plugin_root: Path, data_root: Path) -> Path: - expanded = _expand(value, plugin_root, data_root) +def _resolve_scoped_path( + value: str, + plugin_root: Path, + data_root: Path, + *, + expand_placeholders: bool = True, +) -> Path: + expanded = _expand(value, plugin_root, data_root) if expand_placeholders else value if value.startswith("./"): base = plugin_root candidate = base / expanded[2:] @@ -310,7 +316,14 @@ def _translate_stdio( if not isinstance(command, str) or not command or "\x00" in command: raise ValueError("command must be a non-empty executable token") if command.startswith("./"): - command_value = str(_resolve_scoped_path(command, plugin_root, data_root)) + command_value = str( + _resolve_scoped_path( + command, + plugin_root, + data_root, + expand_placeholders=False, + ) + ) elif any(character.isspace() for character in command): raise ValueError("command must contain one executable token") elif "/" in command or "\\" in command or command in {".", ".."}: diff --git a/tests/hermes_cli/test_agent_plugins.py b/tests/hermes_cli/test_agent_plugins.py index 55c8a6a3e5..9dea011c27 100644 --- a/tests/hermes_cli/test_agent_plugins.py +++ b/tests/hermes_cli/test_agent_plugins.py @@ -165,6 +165,10 @@ def test_stdio_command_and_data_cwd_containment(tmp_path: Path) -> None: "command": "./bin/server", "cwd": "${PLUGIN_DATA}/state", }, + "opaque-command": { + "type": "stdio", + "command": "./${PLUGIN_ROOT}", + }, "escape": { "type": "stdio", "command": "./../outside", @@ -178,10 +182,13 @@ def test_stdio_command_and_data_cwd_containment(tmp_path: Path) -> None: }, ) package = load_agent_plugin(root, tmp_path / "data") - assert set(package.mcp_servers) == {"valid"} + assert set(package.mcp_servers) == {"opaque-command", "valid"} assert package.mcp_servers["valid"]["cwd"] == str( (tmp_path / "data" / "state").resolve() ) + assert package.mcp_servers["opaque-command"]["command"] == str( + (root / "${PLUGIN_ROOT}").resolve() + ) def test_malformed_skill_yaml_is_skipped(tmp_path: Path) -> None: