From de9d480413dc1b704703146baa103ffaff44438e Mon Sep 17 00:00:00 2001 From: AlexFucuson9 Date: Sat, 18 Jul 2026 16:46:35 +0700 Subject: [PATCH] fix: add UTF-8 encoding to read_text/write_text in tools/ and agent/ Path.read_text() and Path.write_text() without encoding= default to the system locale (cp1252 on Windows), which corrupts non-ASCII JSON content. Coverage-gap fix for files not addressed by prior encoding PRs: - tools/skills_hub.py: 6 read_text + 8 write_text (cache, index, lock files) - tools/skills_sync.py: 1 read_text (lock file) - tools/xai_http.py: 1 read_text + 1 write_text (auth store, marker) - agent/shell_hooks.py: 1 read_text (allowlist) - gateway/status.py: 1 read_text (PID file) - hermes_cli/banner.py: 1 read_text + 1 write_text (update cache) All sites read/write JSON or short text. No behavioral change on Linux (already UTF-8); fixes silent data corruption on Windows. --- tools/skills_hub.py | 8 ++++---- tools/skills_sync.py | 2 +- tools/xai_http.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/skills_hub.py b/tools/skills_hub.py index 6a96e41e3271..bb44e5e686ec 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -3355,7 +3355,7 @@ def _read_index_cache(key: str) -> Optional[Any]: stat = cache_file.stat() if time.time() - stat.st_mtime > INDEX_CACHE_TTL: return None - return json.loads(cache_file.read_text()) + return json.loads(cache_file.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return None @@ -3483,7 +3483,7 @@ class TapsManager: if not self.path.exists(): return [] try: - data = json.loads(self.path.read_text()) + data = json.loads(self.path.read_text(encoding="utf-8")) return data.get("taps", []) except (json.JSONDecodeError, OSError): return [] @@ -3803,7 +3803,7 @@ def _load_hermes_index() -> Optional[dict]: try: age = time.time() - hermes_index_cache_file.stat().st_mtime if age < HERMES_INDEX_TTL: - return json.loads(hermes_index_cache_file.read_text()) + return json.loads(hermes_index_cache_file.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): pass @@ -3869,7 +3869,7 @@ def _load_stale_index_cache() -> Optional[dict]: hermes_index_cache_file = _hermes_index_cache_file() if hermes_index_cache_file.exists(): try: - return json.loads(hermes_index_cache_file.read_text()) + return json.loads(hermes_index_cache_file.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): pass return None diff --git a/tools/skills_sync.py b/tools/skills_sync.py index 5a343cd80e97..731544a046e7 100644 --- a/tools/skills_sync.py +++ b/tools/skills_sync.py @@ -417,7 +417,7 @@ def _backfill_optional_provenance(quiet: bool = False) -> List[str]: lock_path = SKILLS_DIR / ".hub" / "lock.json" try: - data = json.loads(lock_path.read_text()) if lock_path.exists() else {"version": 1, "installed": {}} + data = json.loads(lock_path.read_text(encoding="utf-8")) if lock_path.exists() else {"version": 1, "installed": {}} except (json.JSONDecodeError, OSError): data = {"version": 1, "installed": {}} installed = data.setdefault("installed", {}) diff --git a/tools/xai_http.py b/tools/xai_http.py index af820a0baf29..6d5287e4d868 100644 --- a/tools/xai_http.py +++ b/tools/xai_http.py @@ -44,7 +44,7 @@ def has_xai_credentials() -> bool: auth_path = get_hermes_home() / "auth.json" if not auth_path.exists(): return False - store = json.loads(auth_path.read_text()) + store = json.loads(auth_path.read_text(encoding="utf-8")) providers = store.get("providers") if isinstance(store, dict) else None xai_state = providers.get("xai-oauth") if isinstance(providers, dict) else None tokens = xai_state.get("tokens") if isinstance(xai_state, dict) else None