{"repo":"NousResearch/hermes-agent","public_id":"16115","display_label":"Doctor command gh auth check without GITHUB_TOKEN","summary":"These issues and PRs address the doctor command's handling of GitHub authentication when GITHUB_TOKEN is not set, adding a fallback check of gh CLI auth status. One entry also mentions skipping a minimax China models check.","subsystem":null,"counts":{"members":5,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":3},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":19868},"resolution":{"state":"unresolved","by":null},"freshness":{"state":"verified","last_verified_at":"2026-05-04T19:34:32+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.4},"fix_edges":[{"issue_number":16115,"pr_number":19868,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false}],"members":[{"kind":"issue","number":16115,"title":"doctor warns missing GITHUB_TOKEN when gh CLI is authenticated","url":null,"state":"closed"},{"kind":"pr","number":16121,"title":"fix(doctor): check gh auth status when GITHUB_TOKEN is absent","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#16127","number":16127}],"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":16130,"title":"fix(doctor): check gh auth status when GITHUB_TOKEN absent; skip minimax China models check","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#12800","number":12800},{"kind":"pr","value":"#12768","number":12768},{"kind":"pr","value":"#13757","number":13757},{"kind":"pr","value":"#16115","number":16115}],"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":16132,"title":"fix(doctor): check gh auth status when GITHUB_TOKEN absent","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#19868","number":19868}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":19868,"title":"fix(doctor): check gh auth status when GITHUB_TOKEN absent","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/doctor.py","tests/hermes_cli/test_doctor.py"],"fix_hunks":[{"file":"hermes_cli/doctor.py","hunks":["@@ -1264,9 +1264,23 @@ def run_doctor(args):\n         check_warn(\"Skills Hub directory not initialized\", \"(run: hermes skills list)\")\n \n     from hermes_cli.config import get_env_value\n+\n+    def _gh_authenticated() -> bool:\n+        \"\"\"Check if gh CLI is authenticated via token file or device flow.\"\"\"\n+        try:\n+            result = subprocess.run(\n+                [\"gh\", \"auth\", \"status\", \"--json\", \"authenticated\"],\n+                capture_output=True, timeout=10,\n+            )\n+            return result.returncode == 0\n+        except (FileNotFoundError, subprocess.TimeoutExpired):\n+            return False\n+\n     github_token = [redacted:secret-assignment]\"GITHUB_TOKEN\") or get_env_value(\"GH_TOKEN\")\n     if github_token:\n         [redacted:secret-assignment]\"GitHub token configured (authenticated API access)\")\n+    elif _gh_authenticated():\n+        check_ok(\"GitHub authenticated via gh CLI\", \"(full API access — no GITHUB_TOKEN needed)\")\n     else:\n         check_warn(\"No GITHUB_TOKEN\", f\"(60 req/hr rate limit — set in {_DHH}/.env for better rates)\")\n "]},{"file":"tests/hermes_cli/test_doctor.py","hunks":["@@ -663,3 +663,79 @@ def fake_get(url, headers=None, timeout=None):\n     )\n     assert not any(url == \"https://opencode.ai/zen/go/v1/models\" for url, _, _ in calls)\n     assert not any(\"opencode\" in url.lower() and \"models\" in url.lower() for url, _, _ in calls)\n+\n+\n+class TestGitHubTokenCheck:\n+    \"\"\"Tests for GitHub token / gh auth detection in doctor.\"\"\"\n+\n+    def test_no_token_and_not_gh_authenticated_shows_warn(self, monkeypatch, tmp_path):\n+        home = tmp_path / \".hermes\"\n+        home.mkdir(parents=True, exist_ok=True)\n+        monkeypatch.setenv(\"HERMES_HOME\", str(home))\n+        monkeypatch.setenv(\"PATH\", \"/nonexistent\")  # gh not found\n+\n+        from hermes_cli.doctor import run_doctor, _DHH\n+        import io, contextlib\n+\n+        buf = io.StringIO()\n+        with contextlib.redirect_stdout(buf):\n+            run_doctor(Namespace(fix=False))\n+        out = buf.getvalue()\n+\n+        assert \"No GITHUB_TOKEN\" in out\n+        assert \"60 req/hr\" in out\n+\n+    def test_token_env_present_shows_ok(self, monkeypatch, tmp_path):\n+        home = tmp_path / \".hermes\"\n+        home.mkdir(parents=True, exist_ok=True)\n+        monkeypatch.setenv(\"HERMES_HOME\", str(home))\n+        monkeypatch.setenv(\"GITHUB_TOKEN\", \"ghp_test123\")\n+        monkeypatch.setenv(\"PATH\", \"/nonexistent\")  # gh not found\n+\n+        from hermes_cli.doctor import run_doctor\n+        import io, contextlib\n+\n+        buf = io.StringIO()\n+        with contextlib.redirect_stdout(buf):\n+            run_doctor(Namespace(fix=False))\n+        out = buf.getvalue()\n+\n+        assert \"GitHub token configured\" in out\n+\n+    def test_gh_authenticated_without_env_token_shows_ok(self, monkeypatch, tmp_path):\n+        home = tmp_path / \".hermes\"\n+        home.mkdir(parents=True, exist_ok=True)\n+        monkeypatch.setenv(\"HERMES_HOME\", str(home))\n+        # No GITHUB_TOKEN or GH_TOKEN\n+        monkeypatch.delenv(\"GITHUB_TOKEN\", raising=False)\n+        monkeypatch.delenv(\"GH_TOKEN\", raising=False)\n+\n+        # Mock gh to return success\n+        import shutil\n+        real_which = shutil.which\n+        def mock_which(cmd):\n+            return \"/usr/local/bin/gh\" if cmd == \"gh\" else real_which(cmd)\n+        monkeypatch.setattr(shutil, \"which\", mock_which)\n+\n+        call_log = []\n+        def mock_run(cmd, **kwargs):\n+            call_log.append(cmd)\n+            if cmd[:2] == [\"gh\", \"auth\"]:\n+                result = types.SimpleNamespace(returncode=0, stdout=\"\", stderr=\"\")\n+            else:\n+                result = types.SimpleNamespace(returncode=1, stdout=\"\", stderr=\"\")\n+            return result\n+\n+        import subprocess\n+        monkeypatch.setattr(subprocess, \"run\", mock_run)\n+\n+        from hermes_cli.doctor import run_doctor\n+        import io, contextlib\n+\n+        buf = io.StringIO()\n+        with contextlib.redirect_stdout(buf):\n+            run_doctor(Namespace(fix=False))\n+        out = buf.getvalue()\n+\n+        assert \"gh auth\" in str(call_log) or any(c[0] == \"gh\" for c in call_log), f\"gh not called: {call_log}\"\n+        assert \"GitHub authenticated via gh CLI\" in out or \"token configured\" in out\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}