diff --git a/agent/lsp/install.py b/agent/lsp/install.py index da8f1fd778820..c4951854dcade 100644 --- a/agent/lsp/install.py +++ b/agent/lsp/install.py @@ -124,10 +124,9 @@ def _is_windows() -> bool: def hermes_lsp_bin_dir() -> Path: """Return the Hermes-owned bin staging dir for LSP servers.""" - home = os.environ.get("HERMES_HOME") - if home is None: - home = os.path.join(os.path.expanduser("~"), ".hermes") - p = Path(home) / "lsp" / "bin" + from hermes_constants import get_hermes_home + + p = get_hermes_home() / "lsp" / "bin" p.mkdir(parents=True, exist_ok=True) return p diff --git a/agent/lsp/servers.py b/agent/lsp/servers.py index 4056ba4dbab6f..fc2a0b2616930 100644 --- a/agent/lsp/servers.py +++ b/agent/lsp/servers.py @@ -710,9 +710,9 @@ def _find_pses_bundle(ctx: ServerContext) -> Optional[str]: env_path = os.environ.get("PSES_BUNDLE_PATH") if env_path: candidates.append(env_path) - home = os.environ.get("HERMES_HOME") or os.path.join( - os.path.expanduser("~"), ".hermes" - ) + from hermes_constants import get_hermes_home + + home = str(get_hermes_home()) candidates.append(os.path.join(home, "lsp", "PowerShellEditorServices")) for cand in candidates: @@ -796,9 +796,9 @@ def _spawn_powershell_es(root: str, ctx: ServerContext) -> Optional[SpawnSpec]: def hermes_lsp_session_dir() -> str: """Return (and create) the dir for PSES session/log scratch files.""" - home = os.environ.get("HERMES_HOME") or os.path.join( - os.path.expanduser("~"), ".hermes" - ) + from hermes_constants import get_hermes_home + + home = str(get_hermes_home()) d = os.path.join(home, "lsp", "pses") os.makedirs(d, exist_ok=True) return d diff --git a/agent/secret_sources/_cache.py b/agent/secret_sources/_cache.py index 03bd4eb70958e..64e1e5e13ca06 100644 --- a/agent/secret_sources/_cache.py +++ b/agent/secret_sources/_cache.py @@ -82,7 +82,9 @@ def resolve_cache_home(home_path: Optional[Path] = None) -> Path: (and tests that don't thread a home through) working. """ if home_path is None: - home_path = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes")) + from hermes_constants import get_hermes_home + + home_path = get_hermes_home() return home_path diff --git a/gateway/run.py b/gateway/run.py index bb94a569148c2..71f1ab0a8eb06 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8046,7 +8046,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew except (RuntimeError, ValueError, OSError): try: _fh_log_dir = getattr(self.config, "log_dir", None) or os.path.join( - os.environ.get("HERMES_HOME", str(Path.home() / ".hermes")), + str(get_hermes_home()), "logs", ) os.makedirs(_fh_log_dir, exist_ok=True) @@ -8065,7 +8065,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew if _sigusr2 is not None and hasattr(faulthandler, "register"): try: _log_dir = getattr(self.config, "log_dir", None) or os.path.join( - os.environ.get("HERMES_HOME", str(Path.home() / ".hermes")), + str(get_hermes_home()), "logs", ) _faulthandler_path = os.path.join(_log_dir, "gateway_faulthandler.log") diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 27d85818f9196..44c6376c0aa7c 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -6221,7 +6221,9 @@ def warn_deprecated_cwd_env_vars(config: Optional[Dict[str, Any]] = None) -> Non f"this is deprecated." ) if lines: - hint_path = os.environ.get("HERMES_HOME", "~/.hermes") + from hermes_constants import display_hermes_home + + hint_path = display_hermes_home() lines.insert(0, "\033[33m⚠ Deprecated .env settings detected:\033[0m") lines.append( " \033[2mMove to config.yaml instead: " diff --git a/hermes_cli/dashboard_auth/audit.py b/hermes_cli/dashboard_auth/audit.py index 4c05b42adacb7..937fa95a75ba9 100644 --- a/hermes_cli/dashboard_auth/audit.py +++ b/hermes_cli/dashboard_auth/audit.py @@ -57,14 +57,15 @@ class AuditEvent(enum.Enum): def _resolve_log_path() -> Path: - """``$HERMES_HOME/logs/dashboard-auth.log`` with the standard fallback. + """``$HERMES_HOME/logs/dashboard-auth.log``. - Mirrors ``hermes_constants.get_hermes_home`` semantics: env var wins, - else ``~/.hermes``. A local copy avoids an import cycle with the - middleware which lives below ``hermes_cli``. + Uses ``hermes_constants.get_hermes_home()`` (a leaf module — no import + cycle) so profile overrides and the native-Windows ``%LOCALAPPDATA%`` + fallback are honored. """ - home = os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes") - return Path(home) / "logs" / "dashboard-auth.log" + from hermes_constants import get_hermes_home + + return get_hermes_home() / "logs" / "dashboard-auth.log" def audit_log(event: AuditEvent, **fields: Any) -> None: diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 411e99d5c8a94..ca7e87d0ce148 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -1818,7 +1818,9 @@ def _ensure_tui_node() -> None: if not helper.is_file(): return - hermes_home = os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes") + from hermes_constants import get_hermes_home + + hermes_home = str(get_hermes_home()) try: # Helper writes logs to stderr; we ask bash to print `command -v node` # on stdout once ensure_node succeeds. Subshell PATH edits don't leak diff --git a/hermes_cli/security_audit_startup.py b/hermes_cli/security_audit_startup.py index 5d29b79f90ab6..ca4db54c47ec8 100644 --- a/hermes_cli/security_audit_startup.py +++ b/hermes_cli/security_audit_startup.py @@ -168,9 +168,12 @@ def _path_is_mounted(path: Path) -> bool: def _container_no_volume_mount(hermes_home: Optional[Path]) -> Optional[str]: if not _in_container(): return None - home = hermes_home or Path( - os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")) - ) + if hermes_home is not None: + home = hermes_home + else: + from hermes_constants import get_hermes_home + + home = get_hermes_home() try: if _path_is_mounted(home): return None diff --git a/hermes_cli/slack_cli.py b/hermes_cli/slack_cli.py index 51e4059be5d77..584a991a7528a 100644 --- a/hermes_cli/slack_cli.py +++ b/hermes_cli/slack_cli.py @@ -256,12 +256,9 @@ def slack_manifest_command(args) -> int: if write_target is not None: if isinstance(write_target, bool) and write_target: # --write with no value → default location - try: - from hermes_constants import get_hermes_home + from hermes_constants import get_hermes_home - target = Path(get_hermes_home()) / "slack-manifest.json" - except Exception: - target = Path(os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes")) / "slack-manifest.json" + target = Path(get_hermes_home()) / "slack-manifest.json" else: target = Path(write_target).expanduser() target.parent.mkdir(parents=True, exist_ok=True) diff --git a/mcp_serve.py b/mcp_serve.py index 558239153e6c6..bb8e950309a2c 100644 --- a/mcp_serve.py +++ b/mcp_serve.py @@ -62,11 +62,9 @@ except ImportError: def _get_sessions_dir() -> Path: """Return the sessions directory using HERMES_HOME.""" - try: - from hermes_constants import get_hermes_home - return get_hermes_home() / "sessions" - except ImportError: - return Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes")) / "sessions" + from hermes_constants import get_hermes_home + + return get_hermes_home() / "sessions" def _get_session_db(): @@ -194,13 +192,9 @@ def _load_sessions_index_from_json() -> dict: def _load_channel_directory() -> dict: """Load the cached channel directory for available targets.""" - try: - from hermes_constants import get_hermes_home - directory_file = get_hermes_home() / "channel_directory.json" - except ImportError: - directory_file = Path( - os.environ.get("HERMES_HOME", Path.home() / ".hermes") - ) / "channel_directory.json" + from hermes_constants import get_hermes_home + + directory_file = get_hermes_home() / "channel_directory.json" if not directory_file.exists(): return {} @@ -450,11 +444,9 @@ class EventBridge: eliminating the old dual-file (sessions.json + state.db) race that could drop brand-new conversations (#8925). """ - try: - from hermes_constants import get_hermes_home - db_file = get_hermes_home() / "state.db" - except ImportError: - db_file = Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes")) / "state.db" + from hermes_constants import get_hermes_home + + db_file = get_hermes_home() / "state.db" try: db_mtime = db_file.stat().st_mtime if db_file.exists() else 0.0 diff --git a/tools/hook_output_spill.py b/tools/hook_output_spill.py index 8b9f2aec38a1e..58275d6db9718 100644 --- a/tools/hook_output_spill.py +++ b/tools/hook_output_spill.py @@ -117,13 +117,9 @@ def _resolve_spill_dir(directory_override: Optional[str], session_id: Optional[s if directory_override: base = Path(os.path.expanduser(directory_override)) else: - try: - from hermes_constants import get_hermes_home - base = Path(get_hermes_home()) / "hook_outputs" - except Exception: - # Last-resort fallback: HERMES_HOME env var, then ~/.hermes - home = os.environ.get("HERMES_HOME") or os.path.expanduser("~/.hermes") - base = Path(home) / "hook_outputs" + from hermes_constants import get_hermes_home + + base = Path(get_hermes_home()) / "hook_outputs" # Group by session so spills are contained per conversation. session_segment = session_id or "no-session" diff --git a/tools/mcp_oauth.py b/tools/mcp_oauth.py index df2c16b2d651d..7e53375d160a6 100644 --- a/tools/mcp_oauth.py +++ b/tools/mcp_oauth.py @@ -137,11 +137,9 @@ def _get_token_dir(hermes_home: str | Path | None = None) -> Path: Uses HERMES_HOME so each profile gets its own OAuth tokens. Layout: ``HERMES_HOME/mcp-tokens/`` """ - try: - from hermes_constants import get_hermes_home - base = Path(hermes_home) if hermes_home is not None else Path(get_hermes_home()) - except ImportError: - base = Path(os.environ.get("HERMES_HOME", str(Path.home() / ".hermes"))) + from hermes_constants import get_hermes_home + + base = Path(hermes_home) if hermes_home is not None else Path(get_hermes_home()) return base / "mcp-tokens"