diff --git a/internal/setup/assets/hermes/nudge.sh b/internal/setup/assets/hermes/nudge.sh index a14adf12..44f168c9 100644 --- a/internal/setup/assets/hermes/nudge.sh +++ b/internal/setup/assets/hermes/nudge.sh @@ -14,7 +14,7 @@ from pathlib import Path import sys try: - payload = json.loads(Path(sys.argv[1]).read_text() or "{}") + payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8") or "{}") except Exception: payload = {} @@ -33,7 +33,8 @@ if "mnemon" not in response.lower() and "durable memory" not in response.lower() state_dir.mkdir(parents=True, exist_ok=True) (state_dir / "pending-nudge.txt").write_text( "[mnemon] Consider whether the previous exchange warrants durable memory. " - "Use mnemon remember only for stable decisions, preferences, facts, or insights.\n" + "Use mnemon remember only for stable decisions, preferences, facts, or insights.\n", + encoding="utf-8", ) except Exception: pass diff --git a/internal/setup/assets/hermes/remind.sh b/internal/setup/assets/hermes/remind.sh index 0ddac32b..71224d32 100644 --- a/internal/setup/assets/hermes/remind.sh +++ b/internal/setup/assets/hermes/remind.sh @@ -16,7 +16,7 @@ from pathlib import Path payload_path = Path(sys.argv[1]) try: - payload = json.loads(payload_path.read_text() or "{}") + payload = json.loads(payload_path.read_text(encoding="utf-8") or "{}") except Exception: payload = {} @@ -60,7 +60,7 @@ def read_state_file(name): hermes_home = Path(os.environ.get("HERMES_HOME") or Path.home() / ".hermes") path = hermes_home / "mnemon" / name try: - text = path.read_text().strip() + text = path.read_text(encoding="utf-8").strip() path.unlink(missing_ok=True) return text except Exception: @@ -75,6 +75,8 @@ def recall(query): ["mnemon", "recall", query, "--limit", "5"], capture_output=True, text=True, + encoding="utf-8", + errors="replace", timeout=8, check=False, ) diff --git a/internal/setup/hermes_test.go b/internal/setup/hermes_test.go index 943e2470..852b364a 100644 --- a/internal/setup/hermes_test.go +++ b/internal/setup/hermes_test.go @@ -41,6 +41,43 @@ func TestHermesWriteSkillAndHooks(t *testing.T) { } } +func TestHermesPythonHooksUseExplicitUTF8(t *testing.T) { + tests := []struct { + name string + hook string + want []string + }{ + { + name: "remind", + hook: string(assets.HermesRemindHook), + want: []string{ + `payload_path.read_text(encoding="utf-8")`, + `path.read_text(encoding="utf-8").strip()`, + `encoding="utf-8",`, + `errors="replace",`, + }, + }, + { + name: "nudge", + hook: string(assets.HermesNudgeHook), + want: []string{ + `Path(sys.argv[1]).read_text(encoding="utf-8")`, + `encoding="utf-8",`, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, want := range tt.want { + if !strings.Contains(tt.hook, want) { + t.Fatalf("Hermes %s hook is missing %q", tt.name, want) + } + } + }) + } +} + func TestHermesRegisterHooksPreservesUnrelatedConfig(t *testing.T) { dir := t.TempDir() configPath := filepath.Join(dir, "config.yaml")