diff --git a/hermes_cli/container_boot.py b/hermes_cli/container_boot.py index 4bec8534008..ed345ecdd9e 100644 --- a/hermes_cli/container_boot.py +++ b/hermes_cli/container_boot.py @@ -462,7 +462,7 @@ def _register_service(scandir: Path, profile: str, *, start: bool) -> None: run.chmod(0o755) finish = tmp_dir / "finish" - finish.write_text(S6ServiceManager._render_finish_script()) + finish.write_text(S6ServiceManager._render_finish_script(), encoding="utf-8") finish.chmod(0o755) # Persistent log rotation (OQ8-C). diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 46625b0633d..101617de9ad 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -4806,7 +4806,7 @@ def _gateway_prompt(prompt_text: str, default: str = "", timeout: float = 300.0) "id": str(_uuid.uuid4()), } tmp = prompt_path.with_suffix(".tmp") - tmp.write_text(_json.dumps(payload)) + tmp.write_text(_json.dumps(payload), encoding="utf-8") tmp.replace(prompt_path) # Poll for response @@ -11905,7 +11905,7 @@ def _cmd_update_impl(args, gateway_mode: bool): if gateway_mode: _exit_code_path = get_hermes_home() / ".update_exit_code" try: - _exit_code_path.write_text("0") + _exit_code_path.write_text("0", encoding="utf-8") except OSError: pass diff --git a/hermes_cli/profiles.py b/hermes_cli/profiles.py index df5c5984d19..0ab0562deae 100644 --- a/hermes_cli/profiles.py +++ b/hermes_cli/profiles.py @@ -458,7 +458,7 @@ def create_wrapper_script(name: str, target: Optional[str] = None) -> Optional[P if is_windows: wrapper_path = wrapper_dir / f"{canon}.bat" try: - wrapper_path.write_text(f"@echo off\r\nhermes -p {profile} %*\r\n") + wrapper_path.write_text(f"@echo off\r\nhermes -p {profile} %*\r\n", encoding="utf-8") return wrapper_path except OSError as e: print(f"⚠ Could not create wrapper at {wrapper_path}: {e}") @@ -467,7 +467,7 @@ def create_wrapper_script(name: str, target: Optional[str] = None) -> Optional[P wrapper_path = wrapper_dir / canon try: hermes_exe = shutil.which("hermes") or "hermes" - wrapper_path.write_text(f'#!/bin/sh\nexec {shlex.quote(hermes_exe)} -p {profile} "$@"\n') + wrapper_path.write_text(f'#!/bin/sh\nexec {shlex.quote(hermes_exe)} -p {profile} "$@"\n', encoding="utf-8") wrapper_path.chmod(wrapper_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) return wrapper_path except OSError as e: @@ -1825,7 +1825,7 @@ def set_active_profile(name: str) -> None: else: # Atomic write tmp = path.with_suffix(".tmp") - tmp.write_text(canon + "\n") + tmp.write_text(canon + "\n", encoding="utf-8") tmp.replace(path) diff --git a/hermes_cli/service_manager.py b/hermes_cli/service_manager.py index f213fe2e409..471c6a412cb 100644 --- a/hermes_cli/service_manager.py +++ b/hermes_cli/service_manager.py @@ -995,7 +995,7 @@ class S6ServiceManager: run_path.chmod(0o755) finish_path = tmp_dir / "finish" - finish_path.write_text(self._render_finish_script()) + finish_path.write_text(self._render_finish_script(), encoding="utf-8") finish_path.chmod(0o755) # Persistent log rotation (OQ8-C). diff --git a/hermes_cli/uninstall.py b/hermes_cli/uninstall.py index c66136cc156..c02fb8cca16 100644 --- a/hermes_cli/uninstall.py +++ b/hermes_cli/uninstall.py @@ -87,7 +87,7 @@ def remove_path_from_shell_configs(): new_content = new_content.replace('\n\n\n', '\n\n') if new_content != original_content: - config_path.write_text(new_content) + config_path.write_text(new_content, encoding="utf-8") removed_from.append(config_path) except Exception as e: diff --git a/tools/skills_hub.py b/tools/skills_hub.py index 70e72fc5ad6..6a96e41e327 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -1154,7 +1154,7 @@ class GitHubSource(SkillSource): index_cache_dir.mkdir(parents=True, exist_ok=True) cache_file = index_cache_dir / f"{key}.json" try: - cache_file.write_text(json.dumps(data, ensure_ascii=False)) + cache_file.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8") except OSError as e: logger.debug("Could not write cache: %s", e) @@ -3370,12 +3370,12 @@ def _write_index_cache(key: str, data: Any) -> None: ignore_file = _hub_dir() / ".ignore" if not ignore_file.exists(): try: - ignore_file.write_text("# Exclude hub internals from search tools\n*\n") + ignore_file.write_text("# Exclude hub internals from search tools\n*\n", encoding="utf-8") except OSError: pass cache_file = index_cache_dir / f"{key}.json" try: - cache_file.write_text(json.dumps(data, ensure_ascii=False, default=str)) + cache_file.write_text(json.dumps(data, ensure_ascii=False, default=str), encoding="utf-8") except OSError as e: logger.debug("Could not write cache: %s", e) @@ -3415,7 +3415,7 @@ class HubLockFile: def save(self, data: dict) -> None: self.path.parent.mkdir(parents=True, exist_ok=True) - self.path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n") + self.path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") def record_install( self, @@ -3490,7 +3490,7 @@ class TapsManager: def save(self, taps: List[dict]) -> None: self.path.parent.mkdir(parents=True, exist_ok=True) - self.path.write_text(json.dumps({"taps": taps}, indent=2) + "\n") + self.path.write_text(json.dumps({"taps": taps}, indent=2) + "\n", encoding="utf-8") def add(self, repo: str, path: str = "skills/") -> bool: """Add a tap. Returns False if already exists.""" @@ -3549,11 +3549,11 @@ def ensure_hub_dirs() -> None: _quarantine_dir().mkdir(exist_ok=True) _index_cache_dir().mkdir(exist_ok=True) if not lock_file.exists(): - lock_file.write_text('{"version": 1, "installed": {}}\n') + lock_file.write_text('{"version": 1, "installed": {}}\n', encoding="utf-8") if not audit_log.exists(): audit_log.touch() if not taps_file.exists(): - taps_file.write_text('{"taps": []}\n') + taps_file.write_text('{"taps": []}\n', encoding="utf-8") def quarantine_bundle(bundle: SkillBundle) -> Path: @@ -3857,7 +3857,7 @@ def _load_hermes_index() -> Optional[dict]: # Cache locally try: hermes_index_cache_file.parent.mkdir(parents=True, exist_ok=True) - hermes_index_cache_file.write_text(json.dumps(data)) + hermes_index_cache_file.write_text(json.dumps(data), encoding="utf-8") except OSError: pass diff --git a/tools/web_tools.py b/tools/web_tools.py index 7754c386a56..131c0030365 100644 --- a/tools/web_tools.py +++ b/tools/web_tools.py @@ -422,7 +422,7 @@ def _web_requires_env() -> list[str]: DEFAULT_EXTRACT_CHAR_LIMIT = 15000 # Hard ceiling on the full-text file written to cache/web. The truncate-store -# path otherwise calls path.write_text(content) with no upper bound, so a +# path otherwise calls path.write_text(content, encoding="utf-8") with no upper bound, so a # multi-MB page (some backends return very large markdown) writes unbounded # bytes to disk on every extract. Cap the stored copy; the model only ever # sees char_limit anyway, and a 2MB page is already far more than any single diff --git a/tools/xai_http.py b/tools/xai_http.py index 8d80ae1bbd4..af820a0baf2 100644 --- a/tools/xai_http.py +++ b/tools/xai_http.py @@ -234,7 +234,7 @@ def maybe_mark_xai_storage_notice_seen(section_name: str) -> Optional[str]: marker = marker_dir / f"{section_name}_xai_storage_notice_seen" if marker.exists(): return None - marker.write_text(datetime.datetime.now(datetime.UTC).isoformat() + "\n") + marker.write_text(datetime.datetime.now(datetime.UTC).isoformat() + "\n", encoding="utf-8") return notice except Exception: return notice