diff --git a/hermes_cli/personality.py b/hermes_cli/personality.py index 10ee2ac6f2..8682ae4200 100644 --- a/hermes_cli/personality.py +++ b/hermes_cli/personality.py @@ -1,23 +1,19 @@ """Single owner for personality overlays. -The v34 config migration resets the selection once; this module ensures the split cannot happen -again. - -* ``display.personality`` holds the selected NAME (empty = no overlay). * ``agent.system_prompt`` is -the user-owned manual overlay. Personality code never writes it. * ``agent.personalities`` holds -user-defined/overridden personalities; they overlay the built-ins by name. +* ``display.personality`` holds the selected NAME (empty = no overlay). +* ``agent.system_prompt`` is the user-owned manual overlay; personality code never writes it. +* ``agent.personalities`` holds user-defined/overridden personalities, overlaying built-ins by name. """ from __future__ import annotations +import os from typing import Any, Dict, Optional, Tuple #: Names that mean "no personality overlay". NEUTRAL_PERSONALITY_NAMES = frozenset({"", "none", "default", "neutral"}) -#: Built-in personalities, available on every surface (CLI, gateway, TUI, -#: desktop) without any config. User entries in ``agent.personalities`` -#: overlay these by name. +#: Built-in personalities, available on every surface without any config. BUILTIN_PERSONALITIES: Dict[str, str] = { "helpful": "You are a helpful, friendly AI assistant.", "concise": "You are a concise assistant. Keep responses brief and to the point.", @@ -97,9 +93,7 @@ def available_personalities(cfg: Optional[Dict[str, Any]] = None) -> Dict[str, A return merged -def resolve_personality( - value: Any, cfg: Optional[Dict[str, Any]] = None -) -> Tuple[str, str]: +def resolve_personality(value: Any, cfg: Optional[Dict[str, Any]] = None) -> Tuple[str, str]: """Resolve a requested personality to ``(canonical_name, prompt_text)``.""" name = normalize_personality_name(value) if not name: @@ -116,18 +110,12 @@ def resolve_personality( def active_personality_name(cfg: Optional[Dict[str, Any]]) -> str: """The currently selected personality name ('' when none is active).""" name = normalize_personality_name(_get(cfg, "display", "personality", default="")) - if name and name in available_personalities(cfg): - return name - return "" + return name if name and name in available_personalities(cfg) else "" def resolve_ephemeral_system_prompt(cfg: Optional[Dict[str, Any]]) -> str: - """Resolve the session overlay from config. - - ``display.personality`` wins when it names a known personality; otherwise the user-owned - ``agent.system_prompt`` applies. Callers should still prefer ``HERMES_EPHEMERAL_SYSTEM_PROMPT`` - when that env var is set. - """ + """Session overlay: ``display.personality`` when it names a known personality, else the + user-owned ``agent.system_prompt``. Callers still prefer ``HERMES_EPHEMERAL_SYSTEM_PROMPT``.""" name = active_personality_name(cfg) if name: return render_personality_prompt(available_personalities(cfg)[name]) @@ -135,12 +123,9 @@ def resolve_ephemeral_system_prompt(cfg: Optional[Dict[str, Any]]) -> str: def persist_personality(value: Any) -> bool: - """Persist the personality selection — the ONLY sanctioned write path. - - Writes the canonical name (or '') to ``display.personality`` in the active HERMES_HOME - config.yaml atomically, preserving comments and ordering. Never touches ``agent.system_prompt``. - Returns True on success. - """ + """Persist the selection — the ONLY sanctioned write path. Writes the canonical name (or '') + to ``display.personality`` atomically (comments/ordering preserved); never touches + ``agent.system_prompt``. Returns True on success.""" name = normalize_personality_name(value) try: from hermes_constants import get_hermes_home @@ -150,8 +135,6 @@ def persist_personality(value: Any) -> bool: config_path.parent.mkdir(parents=True, exist_ok=True) atomic_roundtrip_yaml_update(config_path, "display.personality", name) try: - import os - os.chmod(config_path, 0o600) except (OSError, NotImplementedError): pass diff --git a/hermes_cli/platforms.py b/hermes_cli/platforms.py index 11d0cf8699..222cfdeeaa 100644 --- a/hermes_cli/platforms.py +++ b/hermes_cli/platforms.py @@ -42,11 +42,10 @@ def _plugin_label(entry) -> str: def platform_label(key: str, default: str = "") -> str: - """Return the display label for a platform key, or *default*.""" + """Return the display label for a platform key (builtin, then plugin registry), or *default*.""" info = PLATFORMS.get(key) if info is not None: return info.label - # Check plugin registry try: from gateway.platform_registry import platform_registry entry = platform_registry.get(key) @@ -58,11 +57,7 @@ def platform_label(key: str, default: str = "") -> str: def get_all_platforms() -> "OrderedDict[str, PlatformInfo]": - """Return PLATFORMS merged with any plugin-registered platforms. - - Plugin platforms are appended after builtins. This is the function that tools_config and - skills_config should use for platform menus. - """ + """PLATFORMS plus plugin-registered platforms (appended after builtins) — use for menus.""" merged = OrderedDict(PLATFORMS) try: from gateway.platform_registry import platform_registry