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.
This commit is contained in:
AlexFucuson9 2026-07-18 16:46:35 +07:00 committed by Teknium
parent 40828997a5
commit de9d480413
3 changed files with 6 additions and 6 deletions

View file

@ -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

View file

@ -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", {})

View file

@ -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