fix: read source files with encoding=utf-8-sig

Read text files with the encoding utf-8-sig so a BOM at the start of a
file does not cause a Unicode decode error (Windows editors add BOMs).

Reconstructed from ethie/pm commits 48a32b135b + 013219e814 onto the
current upstream/main base: only the utf-8 -> utf-8-sig transforms were
carried (370 exact line pairs across 205 files); pm-rename hunks that
rode in the original commit were left to the pm-store commit, and
utf8sig hunks entangled with content changes ride their owning commit.

Rebuilt on ethie/pm-clean off ac6c8028e0 (upstream/main).
This commit is contained in:
ethernet committed 2026-08-29 21:23:00 -04:00
1 parent ac6c8028e0
commit 6e294f543d
184 files changed
+804 -385

No files matched your search

+1 -1
View File
@@ -76,7 +76,7 @@ def _read_text_if_exists(path: str) -> str | None:
return None
if not p.is_file():
raise OSError(f"Cannot edit non-file path: {path}")
return p.read_text(encoding="utf-8", errors="replace")
return p.read_text(encoding="utf-8-sig", errors="replace")
def _proposal_for_write_file(arguments: dict[str, Any]) -> EditProposal:
+1 -1
View File
@@ -773,7 +773,7 @@ def _read_small(path: Path) -> str:
try:
if not path.is_file() or path.stat().st_size > _MAX_FACT_FILE_BYTES:
return ""
return path.read_text(encoding="utf-8", errors="replace")
return path.read_text(encoding="utf-8-sig", errors="replace")
except OSError:
return ""
+2 -2
View File
@@ -387,7 +387,7 @@ def _expand_file_reference(
# so it can read/convert/view the file itself.
return None, _binary_reference_block(ref, path)
text = path.read_text(encoding="utf-8")
text = path.read_text(encoding="utf-8-sig")
if ref.line_start is not None:
lines = text.splitlines()
start_idx = max(ref.line_start - 1, 0)
@@ -697,7 +697,7 @@ def _file_metadata(path: Path) -> str:
if _is_binary_file(path):
return f"{path.stat().st_size} bytes"
try:
line_count = path.read_text(encoding="utf-8").count("\n") + 1
line_count = path.read_text(encoding="utf-8-sig").count("\n") + 1
except Exception:
return f"{path.stat().st_size} bytes"
return f"{line_count} lines"
+6 -2
View File
@@ -111,7 +111,11 @@ def _acp_supported(command: str, args: list[str]) -> bool | None:
try:
probe = subprocess.run(
[command, "--help"],
capture_output=True, text=True, timeout=5,
# Explicit codec because text=True alone decodes with the
# locale default and crashes on non-ASCII help text under
# GBK/CP932 locales.
capture_output=True, text=True, encoding="utf-8",
errors="replace", timeout=5,
)
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
return None
@@ -622,7 +626,7 @@ class CopilotACPClient:
if block_error:
raise PermissionError(block_error)
try:
content = path.read_text(encoding="utf-8")
content = path.read_text(encoding="utf-8-sig")
except FileNotFoundError:
content = ""
line = params.get("line")
+1 -1
View File
@@ -725,7 +725,7 @@ def dev_fixture_credits_state() -> Optional[CreditsState]:
name = raw
if os.path.sep in raw or "/" in raw: # looks like a path → read the name from the file
try:
with open(raw, "r", encoding="utf-8") as fh:
with open(raw, "r", encoding="utf-8-sig") as fh:
name = fh.read().strip()
except OSError:
return None
+1 -1
View File
@@ -103,7 +103,7 @@ def load_state() -> Dict[str, Any]:
if not path.exists():
return _default_state()
try:
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
if isinstance(data, dict):
base = _default_state()
base.update({k: v for k, v in data.items() if k in base or k.startswith("_")})
+2 -2
View File
@@ -343,7 +343,7 @@ def _read_manifest(snap_dir: Path) -> Dict[str, Any]:
if not mf.exists():
return {}
try:
return json.loads(mf.read_text(encoding="utf-8"))
return json.loads(mf.read_text(encoding="utf-8-sig"))
except (OSError, json.JSONDecodeError):
return {}
@@ -434,7 +434,7 @@ def _restore_cron_skill_links(snapshot_dir: Path) -> Dict[str, Any]:
return report
try:
backup_text = backup_file.read_text(encoding="utf-8")
backup_text = backup_file.read_text(encoding="utf-8-sig")
backup_parsed = json.loads(backup_text)
except (OSError, json.JSONDecodeError) as e:
report["error"] = f"failed to load backed-up jobs: {e}"
+1 -1
View File
@@ -801,7 +801,7 @@ def _resolved_path(path: str) -> Path:
def _snapshot_text(path: Path) -> str | None:
"""Return UTF-8 file content, or None for missing/unreadable files."""
try:
return path.read_text(encoding="utf-8")
return path.read_text(encoding="utf-8-sig")
except (FileNotFoundError, IsADirectoryError, UnicodeDecodeError, OSError):
return None
+1 -1
View File
@@ -113,7 +113,7 @@ def get_state() -> Optional[dict]:
reason = None
engaged_at = None
try:
raw = json.loads(path.read_text(encoding="utf-8"))
raw = json.loads(path.read_text(encoding="utf-8-sig"))
if isinstance(raw, dict):
reason = raw.get("reason") or None
engaged_at = raw.get("engaged_at") or None
+1 -1
View File
@@ -162,7 +162,7 @@ def _load_catalog(lang: str) -> dict[str, str]:
try:
import yaml # PyYAML is already a hermes dependency
with path.open("r", encoding="utf-8") as f:
with path.open("r", encoding="utf-8-sig") as f:
raw = yaml.safe_load(f) or {}
except Exception as exc:
logger.warning("Failed to load i18n catalog %s: %s", path, exc)
+3 -3
View File
@@ -89,7 +89,7 @@ def _load_usage() -> dict[str, dict[str, Any]]:
except Exception:
path = get_hermes_home() / "skills" / ".usage.json"
try:
return json.loads(path.read_text(encoding="utf-8"))
return json.loads(path.read_text(encoding="utf-8-sig"))
except Exception:
return {}
@@ -130,7 +130,7 @@ def build_skill_nodes(skill_roots: list[tuple[str, Path]]) -> dict[str, SkillNod
if any(p in {".archive", ".hub", "node_modules", ".git"} for p in skill_md.parts):
continue
try:
fm = _frontmatter(skill_md.read_text(encoding="utf-8")[:4000])
fm = _frontmatter(skill_md.read_text(encoding="utf-8-sig")[:4000])
except OSError:
continue
name = str(fm.get("name") or skill_md.parent.name).strip()
@@ -201,7 +201,7 @@ def _memory_cards() -> list[dict[str, Any]]:
for fname, source in (("MEMORY.md", "memory"), ("USER.md", "profile")):
path = base / fname
try:
text = path.read_text(encoding="utf-8").strip()
text = path.read_text(encoding="utf-8-sig").strip()
file_ts = _to_int_ts(path.stat().st_mtime)
except OSError:
continue
+1 -1
View File
@@ -114,7 +114,7 @@ def _node_detail(node_id: str) -> dict[str, Any]:
"kind": "skill",
"id": node_id,
"label": node_id,
"content": skill_md.read_text(encoding="utf-8"),
"content": skill_md.read_text(encoding="utf-8-sig"),
}
+1 -1
View File
@@ -764,7 +764,7 @@ class LSPClient:
abs_path = os.path.abspath(path)
try:
text = Path(abs_path).read_text(encoding="utf-8", errors="replace")
text = Path(abs_path).read_text(encoding="utf-8-sig", errors="replace")
except OSError as e:
raise LSPProtocolError(f"cannot read {abs_path}: {e}") from e
+2 -2
View File
@@ -248,7 +248,7 @@ def _load_etag() -> str:
try:
etag_path = _get_etag_path()
if etag_path.exists():
return etag_path.read_text(encoding="utf-8").strip()
return etag_path.read_text(encoding="utf-8-sig").strip()
except Exception as e:
logger.debug("Failed to load models.dev ETag: %s", e)
return ""
@@ -314,7 +314,7 @@ def _load_disk_cache() -> Dict[str, Any]:
try:
cache_path = _get_cache_path()
if cache_path.exists():
with open(cache_path, encoding="utf-8") as f:
with open(cache_path, encoding="utf-8-sig") as f:
data = json.load(f)
if not _validate_registry(data):
logger.warning(
+1 -1
View File
@@ -144,7 +144,7 @@ def nous_rate_limit_remaining() -> Optional[float]:
"""
path = _state_path()
try:
with open(path, encoding="utf-8") as f:
with open(path, encoding="utf-8-sig") as f:
state = json.load(f)
reset_at = state.get("reset_at", 0)
remaining = reset_at - time.time()
+1 -1
View File
@@ -230,7 +230,7 @@ def mark_seen(config_path: Path, flag: str) -> bool:
try:
cfg: dict = {}
if config_path.exists():
with open(config_path, encoding="utf-8") as f:
with open(config_path, encoding="utf-8-sig") as f:
cfg = yaml.safe_load(f) or {}
if not isinstance(cfg.get("onboarding"), dict):
cfg["onboarding"] = {}
+2 -2
View File
@@ -65,7 +65,7 @@ def _read_pet_json(directory: Path) -> dict:
if not pet_json.is_file():
return {}
try:
return json.loads(pet_json.read_text(encoding="utf-8"))
return json.loads(pet_json.read_text(encoding="utf-8-sig"))
except (OSError, ValueError) as exc:
logger.debug("unreadable pet.json in %s: %s", directory, exc)
return {}
@@ -438,7 +438,7 @@ def rename_pet(slug: str, display_name: str) -> str | None:
if not pet_json.is_file():
return None
try:
meta = json.loads(pet_json.read_text(encoding="utf-8"))
meta = json.loads(pet_json.read_text(encoding="utf-8-sig"))
except (OSError, ValueError):
meta = {}
if not isinstance(meta, dict):
+11 -11
View File
@@ -1573,7 +1573,7 @@ def _load_skills_snapshot(skills_dir: Path) -> Optional[dict]:
if not snapshot_path.exists():
return None
try:
snapshot = json.loads(snapshot_path.read_text(encoding="utf-8"))
snapshot = json.loads(snapshot_path.read_text(encoding="utf-8-sig"))
except Exception:
return None
if not isinstance(snapshot, dict):
@@ -1651,7 +1651,7 @@ def _build_snapshot_entry(
prov_path = (
skills_dir / ORG_MIRROR_DIR_NAME / org_id / ORG_PROVENANCE_FILE
)
prov = _json.loads(prov_path.read_text(encoding="utf-8"))
prov = _json.loads(prov_path.read_text(encoding="utf-8-sig"))
device = str(prov.get("author_device") or "")
entry["org_author"] = device or str(prov.get("author_user_id") or "")
except Exception:
@@ -1670,7 +1670,7 @@ def _parse_skill_file(skill_file: Path) -> tuple[bool, dict, str]:
(True, {}, "") to err on the side of showing the skill.
"""
try:
raw = skill_file.read_text(encoding="utf-8")
raw = skill_file.read_text(encoding="utf-8-sig")
frontmatter, _ = parse_frontmatter(raw)
if not skill_matches_platform(frontmatter):
@@ -1961,7 +1961,7 @@ def _build_skills_system_prompt_inner(
# Read category-level DESCRIPTION.md files
for desc_file in iter_skill_index_files(skills_dir, "DESCRIPTION.md"):
try:
content = desc_file.read_text(encoding="utf-8")
content = desc_file.read_text(encoding="utf-8-sig")
fm, _ = parse_frontmatter(content)
cat_desc = fm.get("description")
if not cat_desc:
@@ -2019,7 +2019,7 @@ def _build_skills_system_prompt_inner(
# External category descriptions
for desc_file in iter_skill_index_files(ext_dir, "DESCRIPTION.md"):
try:
content = desc_file.read_text(encoding="utf-8")
content = desc_file.read_text(encoding="utf-8-sig")
fm, _ = parse_frontmatter(content)
cat_desc = fm.get("description")
if not cat_desc:
@@ -2193,7 +2193,7 @@ def load_soul_md(
if not soul_path.exists():
return None
try:
content = soul_path.read_text(encoding="utf-8").strip()
content = soul_path.read_text(encoding="utf-8-sig").strip()
if not content:
return None
content = _scan_context_content(content, "SOUL.md")
@@ -2213,7 +2213,7 @@ def _load_hermes_md(cwd_path: Path, context_length: Optional[int] = None) -> str
if not hermes_md_path:
return ""
try:
content = hermes_md_path.read_text(encoding="utf-8").strip()
content = hermes_md_path.read_text(encoding="utf-8-sig").strip()
if not content:
return ""
content = _strip_yaml_frontmatter(content)
@@ -2283,7 +2283,7 @@ def _load_agents_md(cwd_path: Path, context_length: Optional[int] = None) -> str
if not candidate.exists():
continue
try:
content = candidate.read_text(encoding="utf-8").strip()
content = candidate.read_text(encoding="utf-8-sig").strip()
except Exception as e:
logger.debug("Could not read %s: %s", candidate, e)
continue
@@ -2324,7 +2324,7 @@ def _load_claude_md(cwd_path: Path, context_length: Optional[int] = None) -> str
candidate = cwd_path / name
if candidate.exists():
try:
content = candidate.read_text(encoding="utf-8").strip()
content = candidate.read_text(encoding="utf-8-sig").strip()
if content:
content = _scan_context_content(content, name)
result = f"## {name}\n\n{content}"
@@ -2343,7 +2343,7 @@ def _load_cursorrules(cwd_path: Path, context_length: Optional[int] = None) -> s
cursorrules_file = cwd_path / ".cursorrules"
if cursorrules_file.exists():
try:
content = cursorrules_file.read_text(encoding="utf-8").strip()
content = cursorrules_file.read_text(encoding="utf-8-sig").strip()
if content:
content = _scan_context_content(content, ".cursorrules")
cursorrules_content += f"## .cursorrules\n\n{content}\n\n"
@@ -2355,7 +2355,7 @@ def _load_cursorrules(cwd_path: Path, context_length: Optional[int] = None) -> s
mdc_files = sorted(cursor_rules_dir.glob("*.mdc"))
for mdc_file in mdc_files:
try:
content = mdc_file.read_text(encoding="utf-8").strip()
content = mdc_file.read_text(encoding="utf-8-sig").strip()
if content:
content = _scan_context_content(content, f".cursor/rules/{mdc_file.name}")
cursorrules_content += f"## .cursor/rules/{mdc_file.name}\n\n{content}\n\n"
+8 -8
View File
@@ -632,7 +632,7 @@ def _verify_checksums_signature(tmp: Path, checksum_path: Path) -> bool:
def _expected_sha256(checksum_file: Path, asset_name: str) -> str:
"""Parse the standard ``sha256sum`` output: ``<hex> <filename>``."""
text = checksum_file.read_text(encoding="utf-8", errors="replace")
text = checksum_file.read_text(encoding="utf-8-sig", errors="replace")
for line in text.splitlines():
parts = line.strip().split()
if len(parts) >= 2 and parts[-1] == asset_name:
@@ -854,7 +854,7 @@ def ensure_management_token(*, force: bool = False) -> str:
p = _management_token_path()
if not force and p.exists():
try:
existing = p.read_text(encoding="utf-8").strip()
existing = p.read_text(encoding="utf-8-sig").strip()
if existing:
return existing
except OSError:
@@ -879,7 +879,7 @@ def ensure_management_token(*, force: bool = False) -> str:
def _read_management_token() -> Optional[str]:
p = _proxy_state_dir_ro() / "management.token"
try:
token = p.read_text(encoding="utf-8").strip()
token = p.read_text(encoding="utf-8-sig").strip()
except OSError:
return None
return token or None
@@ -898,7 +898,7 @@ def _read_management_listen_from_config(
except ImportError:
return None
try:
data = yaml.safe_load(cfg.read_text(encoding="utf-8"))
data = yaml.safe_load(cfg.read_text(encoding="utf-8-sig"))
except (OSError, yaml.YAMLError):
return None
listen = ((data or {}).get("management") or {}).get("listen") or ""
@@ -1427,7 +1427,7 @@ def load_mappings() -> List[TokenMapping]:
if not f.exists():
return []
try:
payload = json.loads(f.read_text(encoding="utf-8"))
payload = json.loads(f.read_text(encoding="utf-8-sig"))
except (OSError, json.JSONDecodeError) as exc:
logger.warning("Failed to read iron-proxy mappings.json: %s", exc)
return []
@@ -1579,7 +1579,7 @@ def _read_pid() -> Optional[int]:
if not pf.exists():
return None
try:
pid = int(pf.read_text(encoding="utf-8").strip())
pid = int(pf.read_text(encoding="utf-8-sig").strip())
except (OSError, ValueError):
return None
return pid if pid > 0 else None
@@ -1601,7 +1601,7 @@ def _pid_proc_starttime(pid: int) -> Optional[str]:
the cmdline + nonce check.
"""
try:
text = Path(f"/proc/{pid}/stat").read_text(encoding="utf-8")
text = Path(f"/proc/{pid}/stat").read_text(encoding="utf-8-sig")
except OSError:
return None
# /proc/<pid>/stat: pid (comm-with-parens) state ppid ... fields[21]=starttime
@@ -2403,7 +2403,7 @@ def _read_http_listen_from_config() -> Optional[Tuple[str, int]]:
except ImportError:
return None
try:
data = yaml.safe_load(cfg.read_text(encoding="utf-8"))
data = yaml.safe_load(cfg.read_text(encoding="utf-8-sig"))
except (OSError, yaml.YAMLError):
return None
proxy_block = (data or {}).get("proxy") or {}
+1 -1
View File
@@ -137,7 +137,7 @@ class DiskCache(Generic[K]):
return None
path = self.path(home_path)
try:
with open(path, "r", encoding="utf-8") as f:
with open(path, "r", encoding="utf-8-sig") as f:
payload = json.load(f)
except (OSError, json.JSONDecodeError):
return None
+2 -2
View File
@@ -289,7 +289,7 @@ def _expected_sha256(checksum_file: Path, asset_name: str) -> str:
Format is the standard ``sha256sum`` output: ``<hex> <filename>``,
one per line.
"""
text = checksum_file.read_text(encoding="utf-8", errors="replace")
text = checksum_file.read_text(encoding="utf-8-sig", errors="replace")
for line in text.splitlines():
parts = line.strip().split()
if len(parts) >= 2 and parts[-1] == asset_name:
@@ -466,7 +466,7 @@ def _read_encrypted_disk_cache(
try:
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
payload = json.loads(path.read_text(encoding="utf-8"))
payload = json.loads(path.read_text(encoding="utf-8-sig"))
if not isinstance(payload, dict):
return None
serialized_key = _cache_key_str(cache_key)
+1 -1
View File
@@ -853,7 +853,7 @@ def allowlist_path() -> Path:
def load_allowlist() -> Dict[str, Any]:
"""Return the parsed allowlist, or an empty skeleton if absent."""
try:
raw = json.loads(allowlist_path().read_text(encoding="utf-8"))
raw = json.loads(allowlist_path().read_text(encoding="utf-8-sig"))
except (FileNotFoundError, json.JSONDecodeError, OSError):
return {"approvals": []}
if not isinstance(raw, dict):
+1 -1
View File
@@ -120,7 +120,7 @@ def _load_bundle_file(path: Path) -> Optional[Dict[str, Any]]:
shouldn't take down slash command discovery.
"""
try:
raw = path.read_text(encoding="utf-8")
raw = path.read_text(encoding="utf-8-sig")
except OSError as exc:
logger.warning("Could not read bundle %s: %s", path, exc)
return None
+1 -1
View File
@@ -467,7 +467,7 @@ def scan_skill_commands() -> Dict[str, Dict[str, Any]]:
if any(part in {'.git', '.github', '.hub', '.archive'} for part in skill_md.parts):
continue
try:
content = skill_md.read_text(encoding='utf-8')
content = skill_md.read_text(encoding='utf-8-sig')
frontmatter, body = _parse_frontmatter(content)
# Skip skills incompatible with the current OS platform
if not skill_matches_platform(frontmatter):
+2 -2
View File
@@ -105,7 +105,7 @@ class SubdirectoryHintTracker:
try:
if not candidate.is_file():
continue
content = candidate.read_text(encoding="utf-8").strip()
content = candidate.read_text(encoding="utf-8-sig").strip()
except (OSError, UnicodeDecodeError):
continue
if content:
@@ -285,7 +285,7 @@ class SubdirectoryHintTracker:
except OSError:
continue
try:
content = hint_path.read_text(encoding="utf-8").strip()
content = hint_path.read_text(encoding="utf-8-sig").strip()
if not content:
continue
# Skip content we've already injected. The same AGENTS.md is
+1 -1
View File
@@ -32,7 +32,7 @@ def load_manifest(root: Path) -> Recipe | None:
"""
path = manifest_path(root)
try:
raw = path.read_text(encoding="utf-8")
raw = path.read_text(encoding="utf-8-sig")
except OSError:
return None
try:
+1 -1
View File
@@ -121,7 +121,7 @@ class Recipe:
def _read_text(root: Path, name: str) -> str | None:
try:
return (root / name).read_text(encoding="utf-8")
return (root / name).read_text(encoding="utf-8-sig")
except OSError:
return None
+5 -5
View File
@@ -310,7 +310,7 @@ def _jobs_lock():
try:
try:
ensure_dirs()
lock_fd = open(_jobs_lock_file(), "a+", encoding="utf-8")
lock_fd = open(_jobs_lock_file(), "a+", encoding="utf-8-sig")
lock_fd.seek(0)
if fcntl is not None:
# Bounded acquisition (#60703): a plain blocking
@@ -1287,7 +1287,7 @@ def record_ticker_heartbeat(success: bool = False) -> None:
def _epoch_file_age(path: Path) -> Optional[float]:
try:
raw = path.read_text(encoding="utf-8").strip()
raw = path.read_text(encoding="utf-8-sig").strip()
return max(0.0, time.time() - float(raw))
except Exception:
return None
@@ -1323,7 +1323,7 @@ def record_catch_up_occurrence() -> None:
path = _current_cron_store().cron_dir / "catch_up_occurrences"
try:
try:
value = int(path.read_text(encoding="utf-8").strip())
value = int(path.read_text(encoding="utf-8-sig").strip())
except (OSError, ValueError):
value = 0
_atomic_write_counter(path, max(0, value) + 1)
@@ -1370,7 +1370,7 @@ def get_catch_up_occurrence_count() -> int:
"""Return the profile-local stale-schedule catch-up count."""
path = _current_cron_store().cron_dir / "catch_up_occurrences"
try:
return max(0, int(path.read_text(encoding="utf-8").strip()))
return max(0, int(path.read_text(encoding="utf-8-sig").strip()))
except (OSError, ValueError):
return 0
@@ -1388,7 +1388,7 @@ def get_ticker_last_error() -> Optional[str]:
"""Return the most recent recorded tick error message, or None."""
store = _current_cron_store()
try:
raw = (store.cron_dir / "ticker_last_error").read_text(encoding="utf-8")
raw = (store.cron_dir / "ticker_last_error").read_text(encoding="utf-8-sig")
except Exception:
return None
lines = raw.splitlines()
+1 -1
View File
@@ -93,7 +93,7 @@ def _read_last_output(job_id: str) -> str:
try:
path = _snapshot_path(job_id)
if path.exists():
return path.read_text(encoding="utf-8")
return path.read_text(encoding="utf-8-sig")
except Exception as exc:
logger.warning("Monitor: failed to read last output for %r: %s", job_id, exc)
return ""
+3 -3
View File
@@ -4040,7 +4040,7 @@ def _get_session_db_timeout() -> float:
def _read_windows_pyvenv_cfg(venv_dir: Path) -> dict[str, str]:
cfg_path = venv_dir / "pyvenv.cfg"
try:
lines = cfg_path.read_text(encoding="utf-8").splitlines()
lines = cfg_path.read_text(encoding="utf-8-sig").splitlines()
except OSError:
return {}
@@ -4665,7 +4665,7 @@ def _build_job_prompt(
)
if not output_files:
continue # silent skip — no output yet
latest_output = output_files[0].read_text(encoding="utf-8").strip()
latest_output = output_files[0].read_text(encoding="utf-8-sig").strip()
# Truncate to 8K characters to avoid prompt bloat
_MAX_CONTEXT_CHARS = 8000
if len(latest_output) > _MAX_CONTEXT_CHARS:
@@ -6009,7 +6009,7 @@ def run_job(
pfpath = _get_hermes_home() / pfpath
if pfpath.exists():
try:
with open(pfpath, "r", encoding="utf-8") as _pf:
with open(pfpath, "r", encoding="utf-8-sig") as _pf:
prefill_messages = json.load(_pf)
if not isinstance(prefill_messages, list):
prefill_messages = None
+1 -1
View File
@@ -48,7 +48,7 @@ def _eprint(*args: Any) -> None:
def _load_items(input_file: Optional[str]) -> List[Dict[str, Any]]:
raw = ""
if input_file:
with open(input_file, encoding="utf-8") as f:
with open(input_file, encoding="utf-8-sig") as f:
raw = f.read()
else:
raw = sys.stdin.read()
+1 -1
View File
@@ -79,7 +79,7 @@ def _load_raw() -> Dict[str, Any]:
if not SUGGESTIONS_FILE.exists():
return {"suggestions": []}
try:
with open(SUGGESTIONS_FILE, "r", encoding="utf-8") as f:
with open(SUGGESTIONS_FILE, "r", encoding="utf-8-sig") as f:
data = json.load(f)
except (json.JSONDecodeError, OSError) as e:
logger.warning("suggestions.json unreadable (%s); starting empty", e)
+1 -1
View File
@@ -124,7 +124,7 @@ def _cgroup_limit_bytes() -> Optional[int]:
)
for candidate in candidates:
try:
raw = Path(candidate).read_text(encoding="utf-8").strip()
raw = Path(candidate).read_text(encoding="utf-8-sig").strip()
except OSError:
continue
if not raw or raw == "max":
+2 -2
View File
@@ -24,7 +24,7 @@ from pathlib import Path
def _own_cgroup_path() -> str | None:
"""Return the cgroup v2 path for the calling process, or None."""
try:
text = Path("/proc/self/cgroup").read_text(encoding="utf-8")
text = Path("/proc/self/cgroup").read_text(encoding="utf-8-sig")
except OSError:
return None
match = re.search(r"^0::(.+)$", text, re.MULTILINE)
@@ -36,7 +36,7 @@ def _own_cgroup_path() -> str | None:
def _read_cgroup_pids(cgroup_path: str) -> list[int]:
procs_file = Path(f"/sys/fs/cgroup{cgroup_path}/cgroup.procs")
try:
raw = procs_file.read_text(encoding="utf-8")
raw = procs_file.read_text(encoding="utf-8-sig")
except OSError:
return []
pids: list[int] = []
+3 -3
View File
@@ -40,7 +40,7 @@ def _load_channel_aliases() -> Dict[str, Dict[str, str]]:
if not CHANNEL_ALIASES_PATH.exists():
return {}
try:
with open(CHANNEL_ALIASES_PATH, encoding="utf-8") as f:
with open(CHANNEL_ALIASES_PATH, encoding="utf-8-sig") as f:
data = json.load(f)
return data if isinstance(data, dict) else {}
except Exception:
@@ -491,7 +491,7 @@ def _build_from_sessions_json(platform_name: str) -> List[Dict[str, str]]:
entries = []
try:
with open(sessions_path, encoding="utf-8") as f:
with open(sessions_path, encoding="utf-8-sig") as f:
data = json.load(f)
seen_ids = set()
@@ -530,7 +530,7 @@ def load_directory() -> Dict[str, Any]:
_apply_channel_aliases(base["platforms"])
return base
try:
with open(DIRECTORY_PATH, encoding="utf-8") as f:
with open(DIRECTORY_PATH, encoding="utf-8-sig") as f:
data = json.load(f)
# Re-apply aliases on read so friendly names take effect immediately,
# even between timed rebuilds and for brand-new alias entries.
+2 -2
View File
@@ -1388,7 +1388,7 @@ def load_gateway_config() -> GatewayConfig:
gateway_json_path = _home / "gateway.json"
if gateway_json_path.exists():
try:
with open(gateway_json_path, "r", encoding="utf-8") as f:
with open(gateway_json_path, "r", encoding="utf-8-sig") as f:
gw_data = json.load(f) or {}
logger.info(
"Loaded legacy %s — consider moving settings to config.yaml",
@@ -1402,7 +1402,7 @@ def load_gateway_config() -> GatewayConfig:
import yaml
config_yaml_path = _home / "config.yaml"
if config_yaml_path.exists():
with open(config_yaml_path, encoding="utf-8") as f:
with open(config_yaml_path, encoding="utf-8-sig") as f:
yaml_cfg = yaml.safe_load(f) or {}
# Managed scope: overlay administrator-pinned values so the gateway
+1 -1
View File
@@ -139,7 +139,7 @@ def resolve_client_socket_path(home: Path) -> Optional[Path]:
pointer = _pointer_path(home)
try:
if pointer.is_file():
target = pointer.read_text(encoding="utf-8").strip()
target = pointer.read_text(encoding="utf-8-sig").strip()
if target:
candidate = Path(target)
if candidate.exists():
+1 -1
View File
@@ -67,7 +67,7 @@ class DeadTargetRegistry:
def _load(self) -> None:
try:
if self._path.exists():
raw = json.loads(self._path.read_text(encoding="utf-8"))
raw = json.loads(self._path.read_text(encoding="utf-8-sig"))
if isinstance(raw, dict):
# Only keep well-shaped entries.
self._dead = {
+3 -3
View File
@@ -131,7 +131,7 @@ def current_instantiation_epoch() -> str:
try:
boot_id = (
Path("/proc/sys/kernel/random/boot_id")
.read_text(encoding="utf-8")
.read_text(encoding="utf-8-sig")
.strip()
)
except OSError:
@@ -144,7 +144,7 @@ def current_instantiation_epoch() -> str:
# index into the whitespace-delimited tail. starttime is field 22
# (1-indexed); after the comm the tail starts at field 3, so it is the
# tail's index 19.
stat = Path("/proc/1/stat").read_text(encoding="utf-8")
stat = Path("/proc/1/stat").read_text(encoding="utf-8-sig")
tail = stat.rsplit(")", 1)[1].split()
pid1_start = tail[19]
except (OSError, IndexError):
@@ -357,7 +357,7 @@ def read_drain_request(*, home: Optional[Path] = None) -> Optional[dict[str, Any
"""
path = drain_request_path(home)
try:
raw = path.read_text(encoding="utf-8")
raw = path.read_text(encoding="utf-8-sig")
except FileNotFoundError:
return None
except OSError as e:
+1 -1
View File
@@ -106,7 +106,7 @@ class HookRegistry:
continue
try:
manifest = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
manifest = yaml.safe_load(manifest_path.read_text(encoding="utf-8-sig"))
if not manifest or not isinstance(manifest, dict):
print(f"[hooks] Skipping {hook_dir.name}: invalid HOOK.yaml", flush=True)
continue
+1 -1
View File
@@ -146,7 +146,7 @@ def _acquire_singleton_lock(lock_path) -> "tuple[Optional[object], str]":
return None, "unavailable"
try:
Path(lock_path).parent.mkdir(parents=True, exist_ok=True)
handle = open(str(lock_path), "a+", encoding="utf-8")
handle = open(str(lock_path), "a+", encoding="utf-8-sig")
except OSError:
return None, "unavailable"
if not _try_acquire_file_lock(handle):
+3 -3
View File
@@ -82,7 +82,7 @@ def sample_memory() -> Dict[str, Any]:
"""
sample: Dict[str, Any] = {}
try:
with open("/proc/self/status", encoding="utf-8") as fh:
with open("/proc/self/status", encoding="utf-8-sig") as fh:
for line in fh:
if line.startswith("VmRSS:"):
sample["rss_kib"] = int(line.split()[1])
@@ -92,7 +92,7 @@ def sample_memory() -> Dict[str, Any]:
try:
meminfo: Dict[str, int] = {}
wanted = {"MemTotal", "MemAvailable", "SwapTotal", "SwapFree"}
with open("/proc/meminfo", encoding="utf-8") as fh:
with open("/proc/meminfo", encoding="utf-8-sig") as fh:
for line in fh:
key = line.split(":", 1)[0]
if key in wanted:
@@ -112,7 +112,7 @@ def sample_memory() -> Dict[str, Any]:
def _read_json(path: Path) -> Optional[Dict[str, Any]]:
try:
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, ValueError):
return None
return data if isinstance(data, dict) else None
+1 -1
View File
@@ -155,7 +155,7 @@ def _find_session_id(
return None
try:
with open(_SESSIONS_INDEX, encoding="utf-8") as f:
with open(_SESSIONS_INDEX, encoding="utf-8-sig") as f:
data = json.load(f)
except Exception:
return None
+2 -2
View File
@@ -360,7 +360,7 @@ def _sync_allowlist_remove(platform: str, user_id: str) -> None:
def _load_json_file(path: Path) -> dict:
if path.exists():
try:
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
return data if isinstance(data, dict) else {}
except (json.JSONDecodeError, OSError):
return {}
@@ -497,7 +497,7 @@ class PairingStore:
def _load_json(self, path: Path) -> dict:
if path.exists():
try:
return json.loads(path.read_text(encoding="utf-8"))
return json.loads(path.read_text(encoding="utf-8-sig"))
except PermissionError as e:
# Surface this loudly: a 0600 file owned by a different user
# (classic Docker symptom: `docker exec` runs as root and writes
+1 -1
View File
@@ -250,7 +250,7 @@ class ThreadParticipationTracker:
path = self._state_path()
if path.exists():
try:
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
if isinstance(data, list):
return [str(thread_id) for thread_id in data]
except Exception:
+1 -1
View File
@@ -518,7 +518,7 @@ class WebhookAdapter(BasePlatformAdapter):
mtime = subs_path.stat().st_mtime
if mtime <= self._dynamic_routes_mtime:
return # No change
data = json.loads(subs_path.read_text(encoding="utf-8"))
data = json.loads(subs_path.read_text(encoding="utf-8-sig"))
if not isinstance(data, dict):
return
# Merge: static routes take precedence over dynamic ones.
+1 -1
View File
@@ -76,7 +76,7 @@ def _load_filter_file_values(path_value: Any) -> list[Any]:
if path is None:
return []
try:
raw = path.read_text(encoding="utf-8")
raw = path.read_text(encoding="utf-8-sig")
except OSError as exc:
logger.warning("[webhook] filter in_file read failed for %s: %s", path, exc)
return []
+3 -3
View File
@@ -290,7 +290,7 @@ def load_weixin_account(hermes_home: str, account_id: str) -> Optional[Dict[str,
if not path.exists():
return None
try:
return json.loads(path.read_text(encoding="utf-8"))
return json.loads(path.read_text(encoding="utf-8-sig"))
except Exception:
return None
@@ -313,7 +313,7 @@ class ContextTokenStore:
if not path.exists():
return
try:
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
except Exception as exc:
logger.warning("weixin: failed to restore context tokens for %s: %s", _safe_id(account_id), exc)
return
@@ -1024,7 +1024,7 @@ def _load_sync_buf(hermes_home: str, account_id: str) -> str:
if not path.exists():
return ""
try:
return json.loads(path.read_text(encoding="utf-8")).get("get_updates_buf", "")
return json.loads(path.read_text(encoding="utf-8-sig")).get("get_updates_buf", "")
except Exception:
return ""
+1 -1
View File
@@ -50,7 +50,7 @@ def _probe_config(home: Path) -> dict[str, Any]:
if not path.exists():
return _check("ok", "using defaults")
try:
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
raw = yaml.safe_load(path.read_text(encoding="utf-8-sig"))
if raw is not None and not isinstance(raw, dict):
return _check("degraded", "top level is not a mapping")
return _check("ok")
+1 -1
View File
@@ -71,7 +71,7 @@ def _state_path():
def _load_boots() -> List[float]:
try:
raw = _state_path().read_text(encoding="utf-8")
raw = _state_path().read_text(encoding="utf-8-sig")
data = json.loads(raw)
boots = data.get("boots", [])
return [float(t) for t in boots if isinstance(t, (int, float))]
+2 -2
View File
@@ -44,7 +44,7 @@ def record(chat_id, message_id, text: Optional[str]) -> None:
try:
os.makedirs(os.path.dirname(path), exist_ok=True)
try:
with open(path, "r", encoding="utf-8") as fh:
with open(path, "r", encoding="utf-8-sig") as fh:
data = json.load(fh)
if not isinstance(data, dict):
data = {}
@@ -73,7 +73,7 @@ def lookup(chat_id, message_id) -> Optional[str]:
if message_id is None or chat_id is None:
return None
try:
with open(_store_path(), "r", encoding="utf-8") as fh:
with open(_store_path(), "r", encoding="utf-8-sig") as fh:
data = json.load(fh)
entry = data.get(_key(chat_id, message_id))
if isinstance(entry, dict):
+1 -1
View File
@@ -1497,7 +1497,7 @@ class SessionStore:
sessions_file = self.sessions_dir / "sessions.json"
if sessions_file.exists():
try:
with open(sessions_file, "r", encoding="utf-8") as f:
with open(sessions_file, "r", encoding="utf-8-sig") as f:
data = json.load(f)
imported = 0
for key, entry_data in data.items():
+2 -2
View File
@@ -211,7 +211,7 @@ def drain_transcript_spool(session_id: str, replay) -> tuple[int, int]:
entries = []
for path in candidates:
try:
payload = json.loads(path.read_text(encoding="utf-8"))
payload = json.loads(path.read_text(encoding="utf-8-sig"))
except Exception:
continue
if payload.get("reason") != TRANSCRIPT_CAP_DROP_REASON:
@@ -327,7 +327,7 @@ def recover_pending_to_db(
recovered = 0
for path in flush_files:
try:
payload = json.loads(path.read_text(encoding="utf-8"))
payload = json.loads(path.read_text(encoding="utf-8-sig"))
# Agent-history snapshots use a different schema (reason +
# messages list) and are meant for manual operator recovery,
# not automatic DB insertion. Skip them silently.
+4 -4
View File
@@ -53,7 +53,7 @@ def _signal_name(sig: Any) -> str:
def _read_proc_field(pid: int, key: str) -> Optional[str]:
"""Read a single field from /proc/<pid>/status. Linux only; None elsewhere."""
try:
with open(f"/proc/{pid}/status", encoding="utf-8") as fh:
with open(f"/proc/{pid}/status", encoding="utf-8-sig") as fh:
for line in fh:
if line.startswith(key + ":"):
return line.split(":", 1)[1].strip()
@@ -178,7 +178,7 @@ def snapshot_shutdown_context(received_signal: Any = None) -> Dict[str, Any]:
takeover_path = Path(hermes_home_str) / ".gateway-takeover.json"
if takeover_path.exists():
try:
raw = takeover_path.read_text(encoding="utf-8")
raw = takeover_path.read_text(encoding="utf-8-sig")
ctx["takeover_marker"] = raw[:300]
ctx["takeover_marker_for_self"] = (
f'"target_pid": {pid}' in raw
@@ -189,7 +189,7 @@ def snapshot_shutdown_context(received_signal: Any = None) -> Dict[str, Any]:
planned_stop_path = Path(hermes_home_str) / ".gateway-planned-stop.json"
if planned_stop_path.exists():
try:
raw = planned_stop_path.read_text(encoding="utf-8")
raw = planned_stop_path.read_text(encoding="utf-8-sig")
ctx["planned_stop_marker"] = raw[:300]
except OSError:
pass
@@ -353,7 +353,7 @@ def check_systemd_timing_alignment(
unit_name: Optional[str] = None
try:
# /proc/self/cgroup gives us "0::/user.slice/.../hermes-gateway.service"
with open("/proc/self/cgroup", encoding="utf-8") as fh:
with open("/proc/self/cgroup", encoding="utf-8-sig") as fh:
for line in fh:
# systemd cgroup line ends with the unit name
if ".service" in line:
+1 -1
View File
@@ -77,7 +77,7 @@ def _merge_phrase_mapping(catalog: dict[str, list[str]], section: Mapping[str, A
def _merge_phrase_file(catalog: dict[str, list[str]], path: Path, *, inherited_mode: str | None = None) -> None:
try:
loaded = yaml.safe_load(path.read_text(encoding="utf-8"))
loaded = yaml.safe_load(path.read_text(encoding="utf-8-sig"))
except Exception:
return
if isinstance(loaded, Mapping):
+1 -1
View File
@@ -30,7 +30,7 @@ def _load_cache() -> dict:
"""Load the sticker cache from disk."""
if CACHE_PATH.exists():
try:
return json.loads(CACHE_PATH.read_text(encoding="utf-8"))
return json.loads(CACHE_PATH.read_text(encoding="utf-8-sig"))
except (json.JSONDecodeError, OSError):
return {}
return {}
+1 -1
View File
@@ -159,7 +159,7 @@ def expand_whatsapp_aliases(identifier: str) -> Set[str]:
continue
try:
mapped = normalize_whatsapp_identifier(
json.loads(mapping_path.read_text(encoding="utf-8"))
json.loads(mapping_path.read_text(encoding="utf-8-sig"))
)
except (OSError, json.JSONDecodeError) as exc:
logger.debug("whatsapp_identity: failed to read %s: %s", mapping_path, exc)
+2 -2
View File
@@ -201,7 +201,7 @@ def _pid_is_running(pid: int) -> bool:
def _marker_owner_is_live(marker: Path) -> bool:
"""True when a legacy update marker names a process still running."""
try:
body = marker.read_text(encoding="utf-8", errors="replace")
body = marker.read_text(encoding="utf-8-sig", errors="replace")
except OSError:
return False
for line in body.splitlines():
@@ -605,7 +605,7 @@ def _complete_pending_core_install(root: Path, core_marker: Path) -> bool:
# increment the counter reflects THIS attempt.
attempts = 0
try:
raw = core_marker.read_text(encoding="utf-8", errors="replace").strip()
raw = core_marker.read_text(encoding="utf-8-sig", errors="replace").strip()
if raw:
import json as _json
+1 -1
View File
@@ -201,7 +201,7 @@ class _FileLock:
def _read_entries(path: Path, *, strict: bool = False) -> list[dict[str, Any]]:
try:
with open(path, "r", encoding="utf-8") as fh:
with open(path, "r", encoding="utf-8-sig") as fh:
data = json.load(fh)
except FileNotFoundError:
return []
+1 -1
View File
@@ -92,7 +92,7 @@ def normalize_text(text: str) -> str:
def read_text(path: Path) -> str:
return path.read_text(encoding="utf-8", errors="replace")
return path.read_text(encoding="utf-8-sig", errors="replace")
class ConfigReadError(RuntimeError):
+2 -2
View File
@@ -86,7 +86,7 @@ def _inside(path: Path, root: Path) -> bool:
def _read_json_object(path: Path, *, label: str) -> dict:
try:
value = json.loads(path.read_text(encoding="utf-8"))
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise AgentPluginError(f"{label} is not valid readable JSON: {exc}") from exc
if not isinstance(value, dict):
@@ -219,7 +219,7 @@ def _discover_skills(
)
continue
try:
content = skill_md.read_text(encoding="utf-8")
content = skill_md.read_text(encoding="utf-8-sig")
content = content.lstrip("\ufeff")
if not content.startswith("---"):
raise ValueError("missing YAML frontmatter")
+1 -1
View File
@@ -2794,7 +2794,7 @@ def _read_qwen_cli_tokens() -> Dict[str, Any]:
code="qwen_auth_missing",
)
try:
data = json.loads(auth_path.read_text(encoding="utf-8"))
data = json.loads(auth_path.read_text(encoding="utf-8-sig"))
except Exception as exc:
raise AuthError(
f"Failed to read Qwen CLI credentials from {auth_path}: {exc}",
+1 -1
View File
@@ -528,7 +528,7 @@ def _last_used_profile(src: str) -> str:
import json
try:
with open(os.path.join(src, "Local State"), encoding="utf-8", errors="replace") as fh:
with open(os.path.join(src, "Local State"), encoding="utf-8-sig", errors="replace") as fh:
state = json.load(fh)
last = ((state.get("profile") or {}).get("last_used")) or "Default"
except (OSError, ValueError, AttributeError):
+2 -2
View File
@@ -204,7 +204,7 @@ def _read_default_model(codex_home: Path) -> Optional[str]:
except Exception:
return None
try:
payload = tomllib.loads(config_path.read_text(encoding="utf-8"))
payload = tomllib.loads(config_path.read_text(encoding="utf-8-sig"))
except Exception:
return None
model = payload.get("model") if isinstance(payload, dict) else None
@@ -218,7 +218,7 @@ def _read_cache_models(codex_home: Path) -> List[str]:
if not cache_path.exists():
return []
try:
raw = json.loads(cache_path.read_text(encoding="utf-8"))
raw = json.loads(cache_path.read_text(encoding="utf-8-sig"))
except Exception:
return []
+1 -1
View File
@@ -708,7 +708,7 @@ def migrate(
# append the new one.
if target.exists():
try:
existing = target.read_text(encoding="utf-8")
existing = target.read_text(encoding="utf-8-sig")
except Exception as exc:
report.errors.append(f"could not read {target}: {exc}")
return report
+1 -1
View File
@@ -331,7 +331,7 @@ def _migrate_to_21(results: Dict[str, Any], quiet: bool) -> None:
if not manifest_file.exists():
continue
try:
with open(manifest_file, encoding="utf-8") as _mf:
with open(manifest_file, encoding="utf-8-sig") as _mf:
manifest = fast_safe_load(_mf) or {}
except Exception:
manifest = {}
+1 -1
View File
@@ -395,7 +395,7 @@ def _read_desired_state(profile_dir: Path) -> str | None:
if not state_file.exists():
return None
try:
data = json.loads(state_file.read_text(encoding="utf-8"))
data = json.loads(state_file.read_text(encoding="utf-8-sig"))
desired_state = data.get("desired_state")
if desired_state is not None:
return desired_state
+1 -1
View File
@@ -408,7 +408,7 @@ def _read_jwt_store(path: Path) -> Optional[dict]:
"Persisted Copilot JWT store exceeds %d bytes; ignoring", _JWT_DISK_MAX_BYTES
)
return None
loaded = json.loads(path.read_text(encoding="utf-8"))
loaded = json.loads(path.read_text(encoding="utf-8-sig"))
return loaded if isinstance(loaded, dict) else None
except Exception as exc:
logger.debug("Failed to read persisted Copilot JWT store: %s", exc)
+1 -1
View File
@@ -132,7 +132,7 @@ def _scrub_config_yaml_mirrors(old_value: str, new_value: str | None) -> List[st
if not config_path.exists():
return []
try:
with open(config_path, encoding="utf-8") as f:
with open(config_path, encoding="utf-8-sig") as f:
user_config = fast_safe_load(f) or {}
except Exception:
return []
+1 -1
View File
@@ -91,7 +91,7 @@ def _load_pending() -> list[dict]:
if not path.exists():
return []
try:
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
if isinstance(data, list):
# Filter to well-formed entries only
return [
+2 -2
View File
@@ -93,7 +93,7 @@ def _env_keys_defined_in_dotenv(path: Path) -> set[str]:
"""
keys: set[str] = set()
try:
text = path.read_text(encoding="utf-8", errors="replace")
text = path.read_text(encoding="utf-8-sig", errors="replace")
except Exception:
try:
text = path.read_text(encoding="latin-1", errors="replace")
@@ -786,7 +786,7 @@ def _load_secrets_config(home_path: Path) -> dict:
except ImportError:
return {}
try:
with open(config_path, "r", encoding="utf-8") as f:
with open(config_path, "r", encoding="utf-8-sig") as f:
data = fast_safe_load(f) or {}
except Exception: # noqa: BLE001
return {}
+1 -1
View File
@@ -78,7 +78,7 @@ class ForeignSession:
def _read_json_lines(path: Path):
"""Yield parsed JSON objects, silently skipping unparseable lines."""
try:
with open(path, "r", encoding="utf-8", errors="replace") as f:
with open(path, "r", encoding="utf-8-sig", errors="replace") as f:
for line in f:
line = line.strip()
if not line:
+2 -2
View File
@@ -1347,7 +1347,7 @@ def _print_deep_probes() -> None:
pid_value: int | None = None
if pid_exists:
try:
data = json.loads(pid_path.read_text(encoding="utf-8"))
data = json.loads(pid_path.read_text(encoding="utf-8-sig"))
pid_value = int(data.get("pid")) if data.get("pid") is not None else None
print(f" [1] {_mark(True):4s} PID file present: {pid_path} (pid={pid_value})")
except Exception as exc:
@@ -1395,7 +1395,7 @@ def _print_deep_probes() -> None:
# [5] runtime status file
if state_path.exists():
try:
state_data = json.loads(state_path.read_text(encoding="utf-8"))
state_data = json.loads(state_path.read_text(encoding="utf-8-sig"))
gateway_state = state_data.get("gateway_state")
updated_at = state_data.get("updated_at")
age_str = ""
+1 -1
View File
@@ -258,7 +258,7 @@ def _cmd_test(args) -> None:
if getattr(args, "payload_file", None):
try:
custom = json.loads(Path(args.payload_file).read_text(encoding="utf-8"))
custom = json.loads(Path(args.payload_file).read_text(encoding="utf-8-sig"))
if isinstance(custom, dict):
payload.update(custom)
else:
+1 -1
View File
@@ -89,7 +89,7 @@ def read_image_provenance(
return _invalid(path, "marker_not_regular_file")
try:
payload = json.loads(path.read_text(encoding="utf-8"))
payload = json.loads(path.read_text(encoding="utf-8-sig"))
except Exception as exc:
# The file may disappear between lstat/read; it was nevertheless
# observed present, so the decision remains fail-closed.
+1 -1
View File
@@ -143,7 +143,7 @@ def build_init_prompt_for_cwd(cwd: str | None = None, extra: str = "") -> str:
agents_path = os.path.join(resolved, "AGENTS.md")
try:
if os.path.isfile(agents_path):
with open(agents_path, encoding="utf-8", errors="replace") as fh:
with open(agents_path, encoding="utf-8-sig", errors="replace") as fh:
existing = fh.read()
except OSError:
existing = None
+1 -1
View File
@@ -356,7 +356,7 @@ def _open_in_editor(initial: str, *, suffix: str) -> Optional[str]:
path = fh.name
try:
subprocess.call([*editor.split(), path])
with open(path, encoding="utf-8") as fh:
with open(path, encoding="utf-8-sig") as fh:
return fh.read()
except OSError as exc:
print(f" editor failed: {exc}")
+4 -4
View File
@@ -642,7 +642,7 @@ def get_current_board() -> str:
try:
f = current_board_path()
if f.exists():
val = f.read_text(encoding="utf-8").strip()
val = f.read_text(encoding="utf-8-sig").strip()
if val:
try:
normed = _normalize_board_slug(val)
@@ -856,7 +856,7 @@ def read_board_metadata(board: Optional[str] = None) -> dict:
try:
p = board_metadata_path(slug)
if p.exists():
raw = json.loads(p.read_text(encoding="utf-8"))
raw = json.loads(p.read_text(encoding="utf-8-sig"))
if isinstance(raw, dict):
# Never let the metadata file claim a different slug than
# its directory — trust the filesystem.
@@ -8229,7 +8229,7 @@ def _pid_alive(pid: Optional[int]) -> bool:
# where we have a cheap, deterministic process-state probe.
if sys.platform == "linux":
try:
with open(f"/proc/{int(pid)}/status", "r", encoding="utf-8") as f:
with open(f"/proc/{int(pid)}/status", "r", encoding="utf-8-sig") as f:
for line in f:
if line.startswith("State:"):
# "State:\tZ (zombie)" → dead
@@ -11943,7 +11943,7 @@ def read_worker_log(
return None
try:
if tail_bytes is None:
return path.read_text(encoding="utf-8", errors="replace")
return path.read_text(encoding="utf-8-sig", errors="replace")
size = path.stat().st_size
with open(path, "rb") as f:
if size > tail_bytes:
+1 -1
View File
@@ -196,7 +196,7 @@ def install_desktop_entry(project_root: Path) -> Optional[Path]:
entry_path.parent.mkdir(parents=True, exist_ok=True)
# When nothing changed, skip the rewrite. Then a launch does not
# churn the menu caches.
if entry_path.is_file() and entry_path.read_text(encoding="utf-8") == contents:
if entry_path.is_file() and entry_path.read_text(encoding="utf-8-sig") == contents:
return entry_path
entry_path.write_text(contents, encoding="utf-8")
# Some launchers (and older Plasma) offer the entry only when it
+3 -3
View File
@@ -295,7 +295,7 @@ def _read_last_n_lines(path: Path, n: int) -> list:
# For files up to 1MB, just read the whole thing — simple and correct.
if size <= 1_048_576:
with open(path, "r", encoding="utf-8", errors="replace") as f:
with open(path, "r", encoding="utf-8-sig", errors="replace") as f:
all_lines = f.readlines()
return all_lines[-n:]
@@ -333,7 +333,7 @@ def _read_last_n_lines(path: Path, n: int) -> list:
except Exception:
# Fallback: read entire file
with open(path, "r", encoding="utf-8", errors="replace") as f:
with open(path, "r", encoding="utf-8-sig", errors="replace") as f:
all_lines = f.readlines()
return all_lines[-n:]
@@ -347,7 +347,7 @@ def _follow_log(
component_prefixes: Optional[Sequence[str]] = None,
) -> None:
"""Poll a log file for new content and print matching lines."""
with open(path, "r", encoding="utf-8", errors="replace") as f:
with open(path, "r", encoding="utf-8-sig", errors="replace") as f:
# Seek to end
f.seek(0, 2)
while True:
+1 -1
View File
@@ -97,7 +97,7 @@ def _cached_read(path: Path, cache: Dict[str, tuple], parse):
if hit is not None and hit[:2] == key:
return copy.deepcopy(hit[2])
try:
with open(path, encoding="utf-8") as f:
with open(path, encoding="utf-8-sig") as f:
parsed = parse(f)
except Exception as exc: # noqa: BLE001 — fail-open, but LOUD
logger.warning(
+1 -1
View File
@@ -190,7 +190,7 @@ def _parse_env_spec(raw: Any) -> EnvVarSpec:
def _parse_manifest(path: Path) -> CatalogEntry:
"""Read and validate a manifest.yaml. Raise CatalogError on any problem."""
try:
with open(path, "r", encoding="utf-8") as f:
with open(path, "r", encoding="utf-8-sig") as f:
data = yaml.safe_load(f) or {}
except Exception as exc:
raise CatalogError(f"failed to read {path}: {exc}") from exc
+1 -1
View File
@@ -98,7 +98,7 @@ def _read_proc_status() -> str | None:
if sys.platform != "linux":
return None
try:
return Path("/proc/self/status").read_text(encoding="utf-8")
return Path("/proc/self/status").read_text(encoding="utf-8-sig")
except OSError:
return None
+2 -2
View File
@@ -208,7 +208,7 @@ def _read_disk_cache() -> tuple[dict[str, Any] | None, float]:
except (OSError, FileNotFoundError):
return (None, 0.0)
try:
with open(path, encoding="utf-8") as fh:
with open(path, encoding="utf-8-sig") as fh:
data = json.load(fh)
except (OSError, json.JSONDecodeError):
return (None, 0.0)
@@ -451,7 +451,7 @@ def seed_cache_from_checkout(project_root: "Path | str") -> bool:
"""
src = Path(project_root) / "website" / "static" / "api" / "model-catalog.json"
try:
with open(src, encoding="utf-8") as fh:
with open(src, encoding="utf-8-sig") as fh:
data = json.load(fh)
except (OSError, json.JSONDecodeError) as exc:
logger.debug("model catalog seed from checkout skipped (%s): %s", src, exc)
+4 -4
View File
@@ -1009,7 +1009,7 @@ def _read_nous_recommended_disk(base: str) -> dict[str, Any] | None:
``{"<base>": {"data": {...}, "ts": <epoch_seconds>}}``.
"""
try:
with open(_nous_recommended_disk_path(), encoding="utf-8") as fh:
with open(_nous_recommended_disk_path(), encoding="utf-8-sig") as fh:
blob = json.load(fh)
except (OSError, json.JSONDecodeError):
return None
@@ -1033,7 +1033,7 @@ def _write_nous_recommended_disk(base: str, data: dict[str, Any]) -> None:
path = _nous_recommended_disk_path()
try:
try:
with open(path, encoding="utf-8") as fh:
with open(path, encoding="utf-8-sig") as fh:
blob = json.load(fh)
if not isinstance(blob, dict):
blob = {}
@@ -4386,7 +4386,7 @@ def _load_provider_models_cache() -> dict:
path = _provider_models_cache_path()
if not path.exists():
return {}
with open(path, encoding="utf-8") as f:
with open(path, encoding="utf-8-sig") as f:
data = json.load(f)
return data if isinstance(data, dict) else {}
except Exception:
@@ -6222,7 +6222,7 @@ def _load_ollama_cloud_cache(*, ignore_ttl: bool = False) -> Optional[dict]:
cache_path = _ollama_cloud_cache_path()
if not cache_path.exists():
return None
with open(cache_path, encoding="utf-8") as f:
with open(cache_path, encoding="utf-8-sig") as f:
data = json.load(f)
if not isinstance(data, dict):
return None
+2 -2
View File
@@ -151,7 +151,7 @@ def _parse_entries(raw: Any) -> List[PluginIndexEntry]:
def _load_seed_entries() -> List[PluginIndexEntry]:
try:
return _parse_entries(json.loads(SEED_INDEX_PATH.read_text(encoding="utf-8")))
return _parse_entries(json.loads(SEED_INDEX_PATH.read_text(encoding="utf-8-sig")))
except (OSError, ValueError) as exc: # pragma: no cover - bundled file
logger.warning("plugin index: bundled seed unreadable: %s", exc)
return []
@@ -167,7 +167,7 @@ def _read_cache(*, max_age: Optional[float]) -> Optional[List[PluginIndexEntry]]
age = time.time() - cache.stat().st_mtime
if age > max_age:
return None
return _parse_entries(json.loads(cache.read_text(encoding="utf-8")))
return _parse_entries(json.loads(cache.read_text(encoding="utf-8-sig")))
except (OSError, ValueError) as exc:
logger.debug("plugin index: cache read failed: %s", exc)
return None
+1 -1
View File
@@ -287,7 +287,7 @@ def load_pack(path_or_url: str) -> PluginPack:
raise PackError(f"Pack file not found: {path}")
if path.stat().st_size > _MAX_PACK_BYTES:
raise PackError("Pack file exceeds the 1 MiB size limit.")
return parse_pack(path.read_text(encoding="utf-8"), source=str(path))
return parse_pack(path.read_text(encoding="utf-8-sig"), source=str(path))
# ---------------------------------------------------------------------------
+5 -4
View File
@@ -716,6 +716,7 @@ _KNOWN_MANIFEST_FIELDS: Set[str] = {
"name", "version", "description", "author", "requires_env",
"provides_tools", "provides_hooks", "kind", "hooks", "label",
"optional_env", "platforms", "external_dependencies", "pip_dependencies",
"extra",
"provides_browser_providers", "provides_web_providers",
# v2 (#64165)
"manifest_version", "api_version", "requires_plugins",
@@ -1018,7 +1019,7 @@ def _read_source_from_origin(origin: Optional[str], limit: int = 8192) -> str:
if not origin.endswith(".py"):
return ""
try:
return Path(origin).read_text(encoding="utf-8", errors="replace")[:limit]
return Path(origin).read_text(encoding="utf-8-sig", errors="replace")[:limit]
except Exception:
return ""
@@ -1415,7 +1416,7 @@ class PluginState:
def _read_unlocked(self) -> dict[str, Any]:
try:
with open(self.path, encoding="utf-8") as handle:
with open(self.path, encoding="utf-8-sig") as handle:
data = json.load(handle)
except FileNotFoundError:
return {}
@@ -4775,7 +4776,7 @@ class PluginManager:
if yaml is None:
logger.warning("PyYAML not installed – cannot load %s", manifest_file)
return None
data = fast_safe_load(manifest_file.read_text(encoding="utf-8")) or {}
data = fast_safe_load(manifest_file.read_text(encoding="utf-8-sig")) or {}
name = data.get("name", plugin_dir.name)
key = f"{prefix}/{plugin_dir.name}" if prefix else name
@@ -6372,7 +6373,7 @@ def _read_plugin_keys_cache() -> Optional[dict]:
try:
import json as _json
blob = _json.loads(
_plugin_toolset_keys_cache_path().read_text(encoding="utf-8")
_plugin_toolset_keys_cache_path().read_text(encoding="utf-8-sig")
)
if isinstance(blob, dict):
return blob
+1 -1
View File
@@ -189,7 +189,7 @@ def _read_ledger(path: Path) -> Optional[list[dict]]:
be silently treated as an empty roster.
"""
try:
text = path.read_text(encoding="utf-8")
text = path.read_text(encoding="utf-8-sig")
except FileNotFoundError:
return []
except OSError:
+1 -1
View File
@@ -103,7 +103,7 @@ def _skill_md_paths_by_name() -> Dict[str, Path]:
frontmatter_name = skill_file.parent.name
try:
frontmatter, _ = parse_frontmatter(
skill_file.read_text(encoding="utf-8")
skill_file.read_text(encoding="utf-8-sig")
)
frontmatter_name = str(frontmatter.get("name") or frontmatter_name)
except Exception:
+1 -1
View File
@@ -340,7 +340,7 @@ def _read_banner_cache() -> dict[str, float]:
return {}
out: dict[str, float] = {}
try:
for line in p.read_text(encoding="utf-8").splitlines():
for line in p.read_text(encoding="utf-8-sig").splitlines():
line = line.strip()
if not line:
continue
+2 -2
View File
@@ -184,7 +184,7 @@ def _discover_plugins(hermes_home: Path) -> list[Component]:
path = plugin_dir / req_file
if path.is_file():
try:
pins = _parse_requirements(path.read_text(encoding="utf-8", errors="replace"))
pins = _parse_requirements(path.read_text(encoding="utf-8-sig", errors="replace"))
except OSError:
continue
for name, version in pins:
@@ -192,7 +192,7 @@ def _discover_plugins(hermes_home: Path) -> list[Component]:
pyproject = plugin_dir / "pyproject.toml"
if pyproject.is_file():
try:
pins = _parse_pyproject_pins(pyproject.read_text(encoding="utf-8", errors="replace"))
pins = _parse_pyproject_pins(pyproject.read_text(encoding="utf-8-sig", errors="replace"))
except OSError:
continue
for name, version in pins:
+3 -3
View File
@@ -75,7 +75,7 @@ def _iter_sshd_config_lines() -> list[str]:
pass
for p in paths:
try:
for raw in p.read_text(encoding="utf-8", errors="replace").splitlines():
for raw in p.read_text(encoding="utf-8-sig", errors="replace").splitlines():
stripped = raw.strip()
if stripped and not stripped.startswith("#"):
lines.append(stripped)
@@ -119,7 +119,7 @@ def _in_container() -> bool:
if os.environ.get("HERMES_DESKTOP_CHILD_PID"):
return False # desktop child, not a server container
try:
cgroup = Path("/proc/1/cgroup").read_text(encoding="utf-8", errors="replace")
cgroup = Path("/proc/1/cgroup").read_text(encoding="utf-8-sig", errors="replace")
if any(tok in cgroup for tok in ("docker", "containerd", "kubepods", "libpod")):
return True
except Exception:
@@ -140,7 +140,7 @@ def _path_is_mounted(path: Path) -> bool:
except Exception:
target = path
try:
mounts = Path("/proc/mounts").read_text(encoding="utf-8", errors="replace").splitlines()
mounts = Path("/proc/mounts").read_text(encoding="utf-8-sig", errors="replace").splitlines()
except Exception:
return True # can't tell — fail safe (no warning)
best = None
+1 -1
View File
@@ -58,7 +58,7 @@ def _read_message_body(
if file_path == "-":
return sys.stdin.read()
try:
return Path(file_path).read_text(encoding="utf-8")
return Path(file_path).read_text(encoding="utf-8-sig")
except UnicodeDecodeError:
print(
f"hermes send: {file_path} is not a text file. --file reads the "
+1 -1
View File
@@ -201,7 +201,7 @@ def verify_export_file(path: Path | str, session: dict[str, Any]) -> tuple[bool,
p = Path(path)
if not p.exists():
return False, "file missing"
text = p.read_text(encoding="utf-8")
text = p.read_text(encoding="utf-8-sig")
match = _SHA_LINE_RE.search(text)
if not match:
return False, "sha256 marker missing"
+2 -2
View File
@@ -1570,7 +1570,7 @@ def do_publish(skill_path: str, target: str = "github", repo: str = "",
# Validate the skill
import yaml
skill_md = (path / "SKILL.md").read_text(encoding="utf-8")
skill_md = (path / "SKILL.md").read_text(encoding="utf-8-sig")
skill_md = skill_md.lstrip("\ufeff") # tolerate UTF-8 BOM (Windows editors)
fm = {}
if skill_md.startswith("---"):
@@ -1771,7 +1771,7 @@ def do_snapshot_import(input_path: str, force: bool = False,
return
try:
snapshot = json.loads(inp.read_text(encoding="utf-8"))
snapshot = json.loads(inp.read_text(encoding="utf-8-sig"))
except json.JSONDecodeError:
c.print(f"[bold red]Error:[/] Invalid JSON in {inp}\n")
return
+1 -1
View File
@@ -49,7 +49,7 @@ def _skin_set(key: str, value: str, skin: str | None) -> int:
path = _skins_dir() / f"{name}.yaml"
if path.exists():
data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
data = yaml.safe_load(path.read_text(encoding="utf-8-sig")) or {}
target = name
else:
# Built-in (or missing): fork into an editable copy that keeps its full
+1 -1
View File
@@ -794,7 +794,7 @@ def _load_skin_from_yaml(path: Path) -> Optional[Dict[str, Any]]:
"""Load a skin definition from a YAML file."""
try:
import yaml
with open(path, "r", encoding="utf-8") as f:
with open(path, "r", encoding="utf-8-sig") as f:
data = yaml.safe_load(f)
if isinstance(data, dict) and "name" in data:
return data
+1 -1
View File
@@ -200,7 +200,7 @@ def slack_manifest_command(args) -> int:
source_arg = str(long_description_file)
try:
source = Path(source_arg).expanduser()
with source.open("r", encoding="utf-8", newline="") as handle:
with source.open("r", encoding="utf-8-sig", newline="") as handle:
long_description = handle.read()
except (OSError, UnicodeError, RuntimeError) as exc:
print(
+1 -1
View File
@@ -136,7 +136,7 @@ def read_breadcrumb() -> Optional[dict]:
if not terminal_id:
return None
path = _breadcrumbs_dir() / terminal_id
data = json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8-sig"))
if not isinstance(data, dict) or not str(data.get("session_id") or "").strip():
return None
ts = data.get("ts")
Loaded 100 of 184 files, more files were not shown because too many files have changed in this diff. Show more