From c8ecb56f27b034187ce8dd24156497997d247c76 Mon Sep 17 00:00:00 2001 From: briandevans <252620095+briandevans@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:44:28 -0700 Subject: [PATCH] fix(cli): reject invalid argv values from -p/--profile before resolving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_apply_profile_override()` scans `sys.argv` for `-p / --profile` at module import time. When `hermes_cli.main` is imported inside pytest with `-p no:xdist` on the command line, it picks up `'no:xdist'` as a profile name candidate, then passes it to `resolve_profile_env()` which raises `ValueError` (invalid format), and the function calls `sys.exit(1)` — aborting test collection with an INTERNALERROR before any test runs. The same conflict affects any tool or wrapper that uses `-p` for its own flag and then imports `hermes_cli.main`. Fix: add a format guard immediately after step 1 (explicit flag scan). If `consume == 2` (the value came from `-p `, not `--profile=value`) and the candidate doesn't match the canonical profile-name pattern `[a-z0-9][a-z0-9_-]{0,63}` (mirrored from `hermes_cli.profiles._PROFILE_ID_RE`), discard it and continue as if no `-p` flag was found. The `active_profile` file-based fallback (step 2) only reads a file written by hermes itself, so it always produces valid names and needs no guard. Regression guard: with the guard reverted, importing `hermes_cli.main` with `sys.argv = ['pytest', '-p', 'no:xdist', ...]` raises `SystemExit(1)`. With the guard in place, the import succeeds and `sys.argv` is left intact for pytest. Legitimate `-p coder` still flows through to `resolve_profile_env()` unchanged. Rebased onto current `origin/main` (`e5dad4ac5`) — the prior branch base (`4fade39c9`) was 824 commits behind and the PR was DIRTY / CONFLICTING. The 1.5 HERMES_HOME-set early-return block has since landed between the original insertion point and step 2; the new guard is positioned correctly before the early return so a bogus `-p` value no longer prevents the early return from kicking in. Co-Authored-By: Claude Opus 4.7 (1M context) --- hermes_cli/main.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 6c2544e905..89cc2e40d9 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -114,6 +114,16 @@ def _apply_profile_override() -> None: consume = 1 break + # 1b. Reject values that can't be valid profile names (e.g. pytest's + # "-p no:xdist" would be misread as profile "no:xdist" otherwise). + # Mirrors hermes_cli.profiles._PROFILE_ID_RE so we never call + # resolve_profile_env() with a value it must reject + sys.exit on. + if profile_name is not None and consume == 2: + import re as _re + if not _re.match(r"^[a-z0-9][a-z0-9_-]{0,63}$", profile_name): + profile_name = None + consume = 0 + # 1.5 If HERMES_HOME is already set and no explicit flag was given, trust it. # This lets child processes (relaunch, subprocess) inherit the parent's # profile choice without having to pass --profile again.