{"repo":"NousResearch/hermes-agent","public_id":"7697","display_label":"Fix hardcoded ~/.hermes paths in output and text","summary":"These issues and PRs address places where the literal path ~/.hermes is hardcoded in user-facing text and schema defaults, which becomes misleading when HERMES_HOME or profiles point elsewhere. The fixes replace hardcoded references with dynamic resolution.","subsystem":null,"counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":10285,"merged_at":"2026-04-15T11:57:56+00:00"},"resolution":{"state":"unresolved","by":null},"freshness":{"state":"stale","last_verified_at":"2026-04-15T11:57:56+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.6666666666666666},"fix_edges":[{"issue_number":7697,"pr_number":10285,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-04-15T11:57:56+00:00"}],"members":[{"kind":"issue","number":7697,"title":"text_to_speech schema hardcodes ~/.hermes default path (misleading with HERMES_HOME / profiles)","url":null,"state":"closed"},{"kind":"pr","number":7732,"title":"fix(tts): make schema default output path profile-safe","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"722331a57","number":0},{"kind":"pr","value":"#10285","number":10285}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":10285,"title":"fix: replace hardcoded ~/.hermes with display_hermes_home() in agent-facing text","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/skill_commands.py","tools/cronjob_tools.py","tools/skill_manager_tool.py","tools/skills_tool.py","tools/tts_tool.py"],"fix_hunks":[{"file":"agent/skill_commands.py","hunks":["@@ -12,6 +12,8 @@\n from pathlib import Path\n from typing import Any, Dict, Optional\n \n+from hermes_constants import display_hermes_home\n+\n logger = logging.getLogger(__name__)\n \n _skill_commands: Dict[str, Dict[str, Any]] = {}","@@ -108,7 +110,7 @@ def _inject_skill_config(loaded_skill: dict[str, Any], parts: list[str]) -> None\n         if not resolved:\n             return\n \n-        lines = [\"\", \"[Skill config (from ~/.hermes/config.yaml):\"]\n+        lines = [\"\", f\"[Skill config (from {display_hermes_home()}/config.yaml):\"]\n         for key, value in resolved.items():\n             display_val = str(value) if value else \"(not set)\"\n             lines.append(f\"  {key} = {display_val}\")"]},{"file":"tools/cronjob_tools.py","hunks":["@@ -13,6 +13,8 @@\n from pathlib import Path\n from typing import Any, Dict, List, Optional\n \n+from hermes_constants import display_hermes_home\n+\n logger = logging.getLogger(__name__)\n \n # Import from cron module (will be available when properly installed)","@@ -455,7 +457,7 @@ def cronjob(\n             },\n             \"script\": {\n                 \"type\": \"string\",\n-                \"description\": \"Optional path to a Python script that runs before each cron job execution. Its stdout is injected into the prompt as context. Use for data collection and change detection. Relative paths resolve under ~/.hermes/scripts/. On update, pass empty string to clear.\"\n+                \"description\": f\"Optional path to a Python script that runs before each cron job execution. Its stdout is injected into the prompt as context. Use for data collection and change detection. Relative paths resolve under {display_hermes_home()}/scripts/. On update, pass empty string to clear.\"\n             },\n         },\n         \"required\": [\"action\"]"]},{"file":"tools/skill_manager_tool.py","hunks":["@@ -39,7 +39,7 @@\n import shutil\n import tempfile\n from pathlib import Path\n-from hermes_constants import get_hermes_home\n+from hermes_constants import get_hermes_home, display_hermes_home\n from typing import Dict, Any, Optional, Tuple\n \n logger = logging.getLogger(__name__)","@@ -655,7 +655,7 @@ def skill_manage(\n     \"description\": (\n         \"Manage skills (create, update, delete). Skills are your procedural \"\n         \"memory — reusable approaches for recurring task types. \"\n-        \"New skills go to ~/.hermes/skills/; existing skills can be modified wherever they live.\\n\\n\"\n+        f\"New skills go to {display_hermes_home()}/skills/; existing skills can be modified wherever they live.\\n\\n\"\n         \"Actions: create (full SKILL.md + optional category), \"\n         \"patch (old_string/new_string — preferred for fixes), \"\n         \"edit (full SKILL.md rewrite — major overhauls only), \""]},{"file":"tools/skills_tool.py","hunks":["@@ -69,7 +69,7 @@\n import json\n import logging\n \n-from hermes_constants import get_hermes_home\n+from hermes_constants import get_hermes_home, display_hermes_home\n import os\n import re\n from enum import Enum","@@ -408,7 +408,7 @@ def _gateway_setup_hint() -> str:\n \n         return GATEWAY_SECRET_CAPTURE_UNSUPPORTED_MESSAGE\n     except Exception:\n-        return \"Secure secret entry is not available. Load this skill in the local CLI to be prompted, or add the key to ~/.hermes/.env manually.\"\n+        return f\"Secure secret entry is not available. Load this skill in the local CLI to be prompted, or add the key to {display_hermes_home()}/.env manually.\"\n \n \n def _build_setup_note(","@@ -666,7 +666,7 @@ def skills_list(category: str = None, task_id: str = None) -> str:\n                     \"success\": True,\n                     \"skills\": [],\n                     \"categories\": [],\n-                    \"message\": \"No skills found. Skills directory created at ~/.hermes/skills/\",\n+                    \"message\": f\"No skills found. Skills directory created at {display_hermes_home()}/skills/\",\n                 },\n                 ensure_ascii=False,\n             )"]},{"file":"tools/tts_tool.py","hunks":["@@ -40,6 +40,8 @@\n from typing import Callable, Dict, Any, Optional\n from urllib.parse import urljoin\n \n+from hermes_constants import display_hermes_home\n+\n logger = logging.getLogger(__name__)\n from tools.managed_tool_gateway import resolve_managed_tool_gateway\n from tools.tool_backend_helpers import managed_nous_tools_enabled, resolve_openai_audio_api_key","@@ -1050,7 +1052,7 @@ def _check(importer, label):\n             },\n             \"output_path\": {\n                 \"type\": \"string\",\n-                \"description\": \"Optional custom file path to save the audio. Defaults to ~/.hermes/audio_cache/<timestamp>.mp3\"\n+                \"description\": f\"Optional custom file path to save the audio. Defaults to {display_hermes_home()}/audio_cache/<timestamp>.mp3\"\n             }\n         },\n         \"required\": [\"text\"]\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}