{"repo":"NousResearch/hermes-agent","public_id":"30195","display_label":"Adding config get/unset/remove subcommands and fixing docs","summary":"These issues and PRs concern adding missing subcommands to 'hermes config' (get, unset, remove) to complete its CRUD surface. Several members also address documentation that references 'hermes config get <key>' as if it were a valid command when it may not exist.","subsystem":"docs","counts":{"members":15,"open_issues":1,"open_issues_distinct":1,"open_prs":1,"closed_issues":2,"merged_prs":1,"attempt_prs":11},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":65540,"merged_at":"2026-07-16T12:44:44+00:00"},"resolution":{"state":"partially_resolved","by":"pr-65540"},"freshness":{"state":"verified","last_verified_at":"2026-07-16T12:44:44+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.12114285714285714},"fix_edges":[{"issue_number":36037,"pr_number":65540,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-07-16T12:44:44+00:00"}],"members":[{"kind":"issue","number":30195,"title":"docs: `hermes config get <key>` referenced in 3 guides is not a valid command","url":null,"state":"closed"},{"kind":"issue","number":36037,"title":"[Feature]: hermes config get <key> and config unset <key> — complete the config CRUD surface","url":null,"state":"closed"},{"kind":"issue","number":59598,"title":"hermes config set: missing 'remove' subcommand","url":null,"state":"open"},{"kind":"pr","number":30293,"title":"docs(config): fix invalid 'hermes config get' references in 3 guides","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#30390","number":30390},{"kind":"pr","value":"#34781","number":34781}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":30349,"title":"docs: replace invalid config get examples","url":null,"state":"closed","close_reason":{"category":"silent_close","implication":"active"},"merged":false},{"kind":"pr","number":30390,"title":"docs: replace invalid 'hermes config get <key>' with 'hermes config s…","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"3f0d44af8ae380996057b620afeae258af830634","number":0},{"kind":"pr","value":"#30195","number":30195}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":30443,"title":"docs: replace invalid 'hermes config get <key>' with 'hermes config show'","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#30293","number":30293},{"kind":"pr","value":"#30349","number":30349},{"kind":"pr","value":"#30195","number":30195}],"category":"stale_abandoned","implication":"active"},"merged":false},{"kind":"pr","number":31491,"title":"docs: replace invalid config get examples","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#30293","number":30293},{"kind":"pr","value":"#30195","number":30195}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":31648,"title":"docs: fix stale CLI references","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"3f0d44af8ae380996057b620afeae258af830634","number":0},{"kind":"commit","value":"d86710528a0245e2638a801f46551dad35230d9b","number":0},{"kind":"pr","value":"#30293","number":30293}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":36067,"title":"feat(config): add get and unset commands","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"53adb3fd9","number":0},{"kind":"pr","value":"#65540","number":65540}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":36628,"title":"docs: replace invalid config get examples","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#30293","number":30293},{"kind":"pr","value":"#30349","number":30349},{"kind":"pr","value":"#30195","number":30195}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":46770,"title":"feat(config): add config get and config unset subcommands","url":null,"state":"closed","close_reason":{"category":"stale_abandoned","implication":"active"},"merged":false},{"kind":"pr","number":59632,"title":"feat(cli): add `hermes config remove <key>` subcommand (#59598)","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":60477,"title":"feat(cli): add 'hermes config remove <key>' command (#59598)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#59632","number":59632},{"kind":"pr","value":"#59598","number":59598}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":65540,"title":"feat(config): add hermes config get and unset commands (#36067 salvage)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["README.md","hermes_cli/config.py","hermes_cli/subcommands/config.py","tests/hermes_cli/test_set_config_value.py","tests/hermes_cli/test_subcommands_batch.py","website/docs/getting-started/installation.md","website/docs/guides/work-with-skills.md","website/docs/user-guide/configuration.md","website/docs/user-guide/configuring-models.md"],"fix_hunks":[{"file":"README.md","hunks":["@@ -109,6 +109,7 @@ hermes              # Interactive CLI — start a conversation\n hermes model        # Choose your LLM provider and model\n hermes tools        # Configure which tools are enabled\n hermes config set   # Set individual config values\n+hermes config get   # Print individual config values\n hermes gateway      # Start the messaging gateway (Telegram, Discord, etc.)\n hermes setup        # Run the full setup wizard (configures everything at once)\n hermes claw migrate # Migrate from OpenClaw (if coming from OpenClaw)"]},{"file":"hermes_cli/config.py","hunks":["@@ -8,7 +8,9 @@\n This module provides:\n - hermes config          - Show current configuration\n - hermes config edit     - Open config in editor\n+- hermes config get      - Print a resolved configuration value\n - hermes config set      - Set a specific value\n+- hermes config unset    - Remove a user configuration value\n - hermes config wizard   - Re-run setup wizard\n \"\"\"\n ","@@ -4647,6 +4649,125 @@ def clear_model_endpoint_credentials(\n     return model_cfg\n \n \n+_MISSING = object()\n+\n+\n+def _get_nested(config, dotted_key: str):\n+    \"\"\"Return a dotted-path value from nested dict/list config data.\"\"\"\n+    current = config\n+    for part in dotted_key.split(\".\"):\n+        if isinstance(current, list):\n+            try:\n+                current = current[int(part)]\n+            except (TypeError, ValueError, IndexError):\n+                return _MISSING\n+        elif isinstance(current, dict):\n+            if part not in current:\n+                return _MISSING\n+            current = current[part]\n+        else:\n+            return _MISSING\n+    return current\n+\n+\n+def _unset_nested(config, dotted_key: str) -> bool:\n+    \"\"\"Remove a dotted-path value from nested dict/list config data.\"\"\"\n+    parts = dotted_key.split(\".\")\n+    if not parts:\n+        return False\n+\n+    parents = []\n+    current = config\n+    for part in parts[:-1]:\n+        parents.append((current, part))\n+        if isinstance(current, list):\n+            try:\n+                current = current[int(part)]\n+            except (TypeError, ValueError, IndexError):\n+                return False\n+        elif isinstance(current, dict):\n+            if part not in current:\n+                return False\n+            current = current[part]\n+        else:\n+            return False\n+\n+    last = parts[-1]\n+    removed = False\n+    if isinstance(current, list):\n+        try:\n+            current.pop(int(last))\n+            removed = True\n+        except (TypeError, ValueError, IndexError):\n+            return False\n+    elif isinstance(current, dict):\n+        if last not in current:\n+            return False\n+        del current[last]\n+        removed = True\n+    else:\n+        return False\n+\n+    # Drop empty dict containers left behind by the deletion while preserving\n+    # user-authored empty lists and non-empty sibling branches.\n+    for parent, part in reversed(parents):\n+        if current != {}:\n+            break\n+        if isinstance(parent, list):\n+            try:\n+                idx = int(part)\n+            except (TypeError, ValueError):\n+                break\n+            if 0 <= idx < len(parent) and parent[idx] == {}:\n+                parent.pop(idx)\n+                current = parent\n+                continue\n+        elif isinstance(parent, dict) and parent.get(part) == {}:\n+            del parent[part]\n+            current = parent\n+            continue\n+        break\n+\n+    return removed\n+\n+\n+def _is_env_config_key(key: str) -> bool:\n+    \"\"\"Return whether `hermes config set` routes this key to .env.\"\"\"\n+    if \".\" in key:\n+        return False\n+    key_upper = key.upper()\n+    api_keys = [\n+        'OPENROUTER_API_KEY', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'VOICE_TOOLS_OPENAI_KEY',\n+        'EXA_API_KEY', 'PARALLEL_API_KEY', 'FIRECRAWL_API_KEY', 'FIRECRAWL_API_URL',\n+        'FIRECRAWL_GATEWAY_URL', 'TOOL_GATEWAY_DOMAIN', 'TOOL_GATEWAY_SCHEME',\n+        'TOOL_GATEWAY_USER_TOKEN', 'TAVILY_API_KEY',\n+        'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'BROWSER_USE_API_KEY',\n+        'FAL_KEY', 'TELEGRAM_BOT_TOKEN', 'DISCORD_BOT_TOKEN',\n+        'TERMINAL_SSH_HOST', 'TERMINAL_SSH_USER', 'TERMINAL_SSH_KEY',\n+        'SUDO_PASSWORD', 'SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN',\n+        'GITHUB_TOKEN', 'HONCHO_API_KEY',\n+    ]\n+    return (\n+        key_upper in api_keys\n+        or key_upper.endswith(('_API_KEY', '_TOKEN'))\n+        or key_upper.startswith('TERMINAL_SSH')\n+    )\n+\n+\n+def _format_config_get_value(value, *, as_json: bool) -> str:\n+    \"\"\"Format a config value for command-line output.\"\"\"\n+    if as_json:\n+        import json\n+        return json.dumps(value, ensure_ascii=False)\n+    if isinstance(value, bool):\n+        return \"true\" if value else \"false\"\n+    if value is None:\n+        return \"null\"\n+    if isinstance(value, (dict, list)):\n+        return yaml.safe_dump(value, sort_keys=False).rstrip()\n+    return str(value)\n+\n+\n def get_missing_config_fields() -> List[Dict[str, Any]]:\n     \"\"\"\n     Check which config fields are missing or outdated (recursive).","@@ -8238,19 +8359,7 @@ def set_config_value(key: str, value: str):\n         )\n         sys.exit(1)\n     # Check if it's an API key (goes to .env)\n-    api_keys = [\n-        'OPENROUTER_API_KEY', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'VOICE_TOOLS_OPENAI_KEY',\n-        'EXA_API_KEY', 'PARALLEL_API_KEY', 'FIRECRAWL_API_KEY', 'FIRECRAWL_API_URL',\n-        'FIRECRAWL_GATEWAY_URL', 'TOOL_GATEWAY_DOMAIN', 'TOOL_GATEWAY_SCHEME',\n-        'TOOL_GATEWAY_USER_TOKEN', 'TAVILY_API_KEY',\n-        'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'BROWSER_USE_API_KEY',\n-        'FAL_KEY', 'TELEGRAM_BOT_TOKEN', 'DISCORD_BOT_TOKEN',\n-        'TERMINAL_SSH_HOST', 'TERMINAL_SSH_USER', 'TERMINAL_SSH_KEY',\n-        'SUDO_PASSWORD', 'SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN',\n-        'GITHUB_TOKEN', 'HONCHO_API_KEY',\n-    ]\n-    \n-    if key.upper() in api_keys or key.upper().endswith(('_API_KEY', '_TOKEN')) or key.upper().startswith('TERMINAL_SSH'):\n+    if _is_env_config_key(key):\n         save_env_value(key.upper(), value)\n         print(f\"✓ Set {key} in {get_env_path()}\")\n         return","@@ -8322,6 +8431,75 @@ def set_config_value(key: str, value: str):\n     print(f\"✓ Set {key} = {_display_value} in {config_path}\")\n \n \n+def get_config_value(key: str, *, as_json: bool = False):\n+    \"\"\"Print a resolved configuration value.\"\"\"\n+    if _is_env_config_key(key):\n+        env_value = get_env_value(key.upper())\n+        value = _MISSING if env_value is None else env_value\n+    else:\n+        value = _get_nested(load_config(), key)\n+\n+    if value is _MISSING:\n+        print(f\"Config key not set: {key}\", file=sys.stderr)\n+        sys.exit(1)\n+\n+    print(_format_config_get_value(value, as_json=as_json))\n+\n+\n+def unset_config_value(key: str):\n+    \"\"\"Remove a user-set configuration or .env value.\"\"\"\n+    if is_managed():\n+        managed_error(\"unset configuration values\")\n+        return\n+    # Managed scope guard: a key pinned by the managed layer cannot be unset by\n+    # the user — the next load would reinstate it anyway (mirrors set_config_value).\n+    from hermes_cli import managed_scope\n+\n+    if managed_scope.is_key_managed(key):\n+        managed_dir = managed_scope.get_managed_dir()\n+        src = (managed_dir / \"config.yaml\") if managed_dir else \"the managed scope\"\n+        print(\n+            f\"Cannot unset '{key}': it is managed by your administrator ({src}) \"\n+            f\"and cannot be changed. Contact your administrator to modify it.\",\n+            file=sys.stderr,\n+        )\n+        sys.exit(1)\n+\n+    if _is_env_config_key(key):\n+        removed = remove_env_value(key.upper())\n+        if not removed:\n+            print(f\"Config key not set: {key}\", file=sys.stderr)\n+            sys.exit(1)\n+        print(f\"✓ Unset {key} from {get_env_path()}\")\n+        return\n+\n+    config_path = get_config_path()\n+    require_readable_config_before_write(config_path)\n+    user_config = {}\n+    if config_path.exists():\n+        try:\n+            with open(config_path, encoding=\"utf-8\") as f:\n+                user_config = fast_safe_load(f) or {}\n+        except Exception:\n+            user_config = {}\n+\n+    removed = _unset_nested(user_config, key)\n+\n+    # Keep .env in sync for keys that terminal_tool reads directly from env vars.\n+    env_var = terminal_config_env_var_for_key(key)\n+    if env_var and key != \"terminal.cwd\":\n+        removed = remove_env_value(env_var) or removed\n+\n+    if not removed:\n+        print(f\"Config key not set: {key}\", file=sys.stderr)\n+        sys.exit(1)\n+\n+    ensure_hermes_home()\n+    from utils import atomic_yaml_write\n+    atomic_yaml_write(config_path, user_config, sort_keys=False)\n+    print(f\"✓ Unset {key} from {config_path}\")\n+\n+\n # =============================================================================\n # Command handler\n # =============================================================================","@@ -8336,6 +8514,18 @@ def config_command(args):\n     elif subcmd == \"edit\":\n         edit_config()\n     \n+    elif subcmd == \"get\":\n+        key = getattr(args, 'key', None)\n+        if not key:\n+            print(\"Usage: hermes config get <key> [--json]\")\n+            print()\n+            print(\"Examples:\")\n+            print(\"  hermes config get model\")\n+            print(\"  hermes config get terminal.backend\")\n+            print(\"  hermes config get skills.config --json\")\n+            sys.exit(1)\n+        get_config_value(key, as_json=getattr(args, 'json', False))\n+\n     elif subcmd == \"set\":\n         key = getattr(args, 'key', None)\n         value = getattr(args, 'value', None)","@@ -8348,6 +8538,18 @@ def config_command(args):\n             print(\"  hermes config set OPENROUTER_API_KEY sk-or-...\")\n             sys.exit(1)\n         set_config_value(key, value)\n+\n+    elif subcmd == \"unset\":\n+        key = getattr(args, 'key', None)\n+        if not key:\n+            print(\"Usage: hermes config unset <key>\")\n+            print()\n+            print(\"Examples:\")\n+            print(\"  hermes config unset model\")\n+            print(\"  hermes config unset terminal.backend\")\n+            print(\"  hermes config unset OPENROUTER_API_KEY\")\n+            sys.exit(1)\n+        unset_config_value(key)\n     \n     elif subcmd == \"path\":\n         print(get_config_path())","@@ -8455,7 +8657,9 @@ def config_command(args):\n         print(\"Available commands:\")\n         print(\"  hermes config           Show current configuration\")\n         print(\"  hermes config edit      Open config in editor\")\n+        print(\"  hermes config get <key>          Print a resolved config value\")\n         print(\"  hermes config set <key> <value>   Set a config value\")\n+        print(\"  hermes config unset <key>        Remove a config value\")\n         print(\"  hermes config check     Check for missing/outdated config\")\n         print(\"  hermes config migrate   Update config with new options\")\n         print(\"  hermes config path      Show config file path\")"]},{"file":"hermes_cli/subcommands/config.py","hunks":["@@ -27,6 +27,13 @@ def build_config_parser(subparsers, *, cmd_config: Callable) -> None:\n     # config edit\n     config_subparsers.add_parser(\"edit\", help=\"Open config file in editor\")\n \n+    # config get\n+    config_get = config_subparsers.add_parser(\n+        \"get\", help=\"Print a resolved configuration value\"\n+    )\n+    config_get.add_argument(\"key\", nargs=\"?\", help=\"Configuration key (e.g., model)\")\n+    config_get.add_argument(\"--json\", action=\"store_true\", help=\"Print value as JSON\")\n+\n     # config set\n     config_set = config_subparsers.add_parser(\"set\", help=\"Set a configuration value\")\n     config_set.add_argument(","@@ -34,6 +41,12 @@ def build_config_parser(subparsers, *, cmd_config: Callable) -> None:\n     )\n     config_set.add_argument(\"value\", nargs=\"?\", help=\"Value to set\")\n \n+    # config unset\n+    config_unset = config_subparsers.add_parser(\n+        \"unset\", help=\"Remove a configuration value\"\n+    )\n+    config_unset.add_argument(\"key\", nargs=\"?\", help=\"Configuration key to remove\")\n+\n     # config path\n     config_subparsers.add_parser(\"path\", help=\"Print config file path\")\n "]},{"file":"tests/hermes_cli/test_set_config_value.py","hunks":["@@ -164,6 +164,122 @@ def test_config_command_accepts_empty_string(self, _isolated_hermes_home):\n         assert \"model\" in config\n \n \n+class TestConfigGetUnset:\n+    \"\"\"config get/unset should mirror config set for scriptable workflows.\"\"\"\n+\n+    def test_config_get_prints_resolved_nested_value(self, _isolated_hermes_home, capsys):\n+        set_config_value(\"terminal.timeout\", \"120\")\n+        capsys.readouterr()\n+\n+        args = argparse.Namespace(config_command=\"get\", key=\"terminal.timeout\", json=False)\n+        config_command(args)\n+\n+        assert capsys.readouterr().out.strip() == \"120\"\n+\n+    def test_config_get_prints_structured_json(self, _isolated_hermes_home, capsys):\n+        set_config_value(\"terminal.backend\", \"docker\")\n+        capsys.readouterr()\n+\n+        args = argparse.Namespace(config_command=\"get\", key=\"terminal\", json=True)\n+        config_command(args)\n+\n+        import json\n+        assert json.loads(capsys.readouterr().out)[\"backend\"] == \"docker\"\n+\n+    def test_config_get_prints_null_for_resolved_null_value(self, capsys):\n+        args = argparse.Namespace(config_command=\"get\", key=\"cron.max_parallel_jobs\", json=False)\n+        config_command(args)\n+\n+        assert capsys.readouterr().out.strip() == \"null\"\n+\n+    def test_config_get_missing_env_key_exits(self, capsys):\n+        args = argparse.Namespace(config_command=\"get\", key=\"OPENROUTER_API_KEY\", json=False)\n+\n+        with pytest.raises(SystemExit) as exc:\n+            config_command(args)\n+\n+        assert exc.value.code == 1\n+        assert \"Config key not set: OPENROUTER_API_KEY\" in capsys.readouterr().err\n+\n+    def test_config_get_dotted_token_yaml_key(self, _isolated_hermes_home, capsys):\n+        (_isolated_hermes_home / \"config.yaml\").write_text(\n+            \"platforms:\\n\"\n+            \"  teams:\\n\"\n+            \"    extra:\\n\"\n+            \"      access_token: [redacted:secret-assignment]\"\n+        )\n+\n+        args = argparse.Namespace(\n+            config_command=\"get\",\n+            key=\"platforms.teams.extra.access_token\",\n+            json=False,\n+        )\n+        config_command(args)\n+\n+        assert capsys.readouterr().out.strip() == \"yaml-token\"\n+\n+    def test_config_get_missing_key_exits(self, capsys):\n+        args = argparse.Namespace(config_command=\"get\", key=\"not.a.real.key\", json=False)\n+\n+        with pytest.raises(SystemExit) as exc:\n+            config_command(args)\n+\n+        assert exc.value.code == 1\n+        assert \"Config key not set: not.a.real.key\" in capsys.readouterr().err\n+\n+    def test_config_unset_removes_yaml_key_and_synced_env(self, _isolated_hermes_home, capsys):\n+        set_config_value(\"terminal.backend\", \"docker\")\n+        assert \"TERMINAL_ENV=docker\" in _read_env(_isolated_hermes_home)\n+        capsys.readouterr()\n+\n+        args = argparse.Namespace(config_command=\"unset\", key=\"terminal.backend\")\n+        config_command(args)\n+\n+        import yaml\n+        reloaded = yaml.safe_load(_read_config(_isolated_hermes_home)) or {}\n+        assert reloaded == {}\n+        assert \"TERMINAL_ENV=\" not in _read_env(_isolated_hermes_home)\n+        assert \"Unset terminal.backend\" in capsys.readouterr().out\n+\n+    def test_config_unset_removes_env_key(self, _isolated_hermes_home, capsys):\n+        set_config_value(\"OPENROUTER_API_KEY\", \"sk-test\")\n+        assert \"OPENROUTER_API_KEY=[redacted:secret-assignment]\" in _read_env(_isolated_hermes_home)\n+        capsys.readouterr()\n+\n+        args = argparse.Namespace(config_command=\"unset\", key=\"OPENROUTER_API_KEY\")\n+        config_command(args)\n+\n+        assert \"OPENROUTER_API_KEY=\" not in _read_env(_isolated_hermes_home)\n+        assert \"Unset OPENROUTER_API_KEY\" in capsys.readouterr().out\n+\n+    def test_config_unset_removes_dotted_token_yaml_key(self, _isolated_hermes_home, capsys):\n+        (_isolated_hermes_home / \"config.yaml\").write_text(\n+            \"platforms:\\n\"\n+            \"  teams:\\n\"\n+            \"    extra:\\n\"\n+            \"      access_token: [redacted:secret-assignment]\"\n+            \"      tenant_id: tenant\\n\"\n+        )\n+\n+        args = argparse.Namespace(config_command=\"unset\", key=\"platforms.teams.extra.access_token\")\n+        config_command(args)\n+\n+        import yaml\n+        reloaded = yaml.safe_load(_read_config(_isolated_hermes_home))\n+        assert \"access_token\" not in reloaded[\"platforms\"][\"teams\"][\"extra\"]\n+        assert reloaded[\"platforms\"][\"teams\"][\"extra\"][\"tenant_id\"] == \"tenant\"\n+        assert \"Unset platforms.teams.extra.access_token\" in capsys.readouterr().out\n+\n+    def test_config_unset_missing_key_exits(self, capsys):\n+        args = argparse.Namespace(config_command=\"unset\", key=\"not.a.real.key\")\n+\n+        with pytest.raises(SystemExit) as exc:\n+            config_command(args)\n+\n+        assert exc.value.code == 1\n+        assert \"Config key not set: not.a.real.key\" in capsys.readouterr().err\n+\n+\n # ---------------------------------------------------------------------------\n # List navigation — regression tests for #17876\n # ---------------------------------------------------------------------------"]},{"file":"tests/hermes_cli/test_subcommands_batch.py","hunks":["@@ -86,6 +86,25 @@ def test_single_handler_builders(name, builder, kw, argv):\n     assert ns.func is handler\n \n \n+def test_config_get_unset_subcommands_parse():\n+    \"\"\"`hermes config get/unset` parse key args (and --json for get).\"\"\"\n+    parser = argparse.ArgumentParser(prog=\"hermes\")\n+    sub = parser.add_subparsers(dest=\"command\")\n+    handler = _h(\"config\")\n+    build_config_parser(sub, cmd_config=handler)\n+\n+    ns = parser.parse_args([\"config\", \"get\", \"terminal.backend\", \"--json\"])\n+    assert ns.func is handler\n+    assert ns.config_command == \"get\"\n+    assert ns.key == \"terminal.backend\"\n+    assert ns.json is True\n+\n+    ns = parser.parse_args([\"config\", \"unset\", \"terminal.backend\"])\n+    assert ns.func is handler\n+    assert ns.config_command == \"unset\"\n+    assert ns.key == \"terminal.backend\"\n+\n+\n def test_dashboard_builder_two_handlers():\n     parser = argparse.ArgumentParser(prog=\"hermes\")\n     sub = parser.add_subparsers(dest=\"command\")"]},{"file":"website/docs/getting-started/installation.md","hunks":["@@ -68,6 +68,7 @@ hermes model          # Choose your LLM provider and model\n hermes tools          # Configure which tools are enabled\n hermes gateway setup  # Set up messaging platforms\n hermes config set     # Set individual config values\n+hermes config get     # Inspect individual config values\n hermes setup          # Or run the full setup wizard to configure everything at once\n ```\n "]},{"file":"website/docs/guides/work-with-skills.md","hunks":["@@ -162,7 +162,7 @@ Manage skill config from the CLI:\n hermes skills config gif-search\n \n # View all skill config\n-hermes config show | grep '^skills\\.config'\n+hermes config get skills.config --json\n ```\n \n ---"]},{"file":"website/docs/user-guide/configuration.md","hunks":["@@ -32,13 +32,17 @@ Run `hermes setup --portal` — one OAuth gets you a model provider and all four\n ```bash\n hermes config              # View current configuration\n hermes config edit         # Open config.yaml in your editor\n+hermes config get KEY      # Print a resolved value\n hermes config set KEY VAL  # Set a specific value\n+hermes config unset KEY    # Remove a user-set value\n hermes config check        # Check for missing options (after updates)\n hermes config migrate      # Interactively add missing options\n \n # Examples:\n+hermes config get model\n hermes config set model anthropic/claude-opus-4\n hermes config set terminal.backend docker\n+hermes config unset terminal.backend\n hermes config set OPENROUTER_API_KEY sk-or-...  # Saves to .env\n ```\n "]},{"file":"website/docs/user-guide/configuring-models.md","hunks":["@@ -232,7 +232,7 @@ hermes model            # Interactive provider + model picker (the canonical way\n \n `hermes model` walks you through picking a provider, authenticating (OAuth flows open a browser; API-key providers prompt for the key), and then choosing a specific model from that provider's curated catalog. The choice is written to `model.provider` and `model.default` in `~/.hermes/config.yaml`.\n \n-To list providers/models without launching the picker, use the dashboard or the REST endpoints below. To inspect what the CLI will actually use right now: `hermes config show | grep '^model\\.'` and `hermes status`.\n+To list providers/models without launching the picker, use the dashboard or the REST endpoints below. To inspect what the CLI will actually use right now: `hermes config get model --json` and `hermes status`.\n \n ### Direct config edit\n \n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}