# NousResearch/hermes-agent · Doctor command gh auth check without GITHUB_TOKEN

## Open now

- Nothing open on GitHub.

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.

## TL;DR

- 5 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #19868. Verify it addresses your case.
- **Recommended:** read PR #19868 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.40`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #16115: doctor warns missing GITHUB_TOKEN when gh CLI is authenticated

## Prior & competing PRs

- **#16121: fix(doctor): check gh auth status when GITHUB_TOKEN is absent**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#16127`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#16130: fix(doctor): check gh auth status when GITHUB_TOKEN absent; skip minimax China models check**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#12800`, `#12768`, `#13757`, `#16115`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#19868: fix(doctor): check gh auth status when GITHUB_TOKEN absent**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/doctor.py`, `tests/hermes_cli/test_doctor.py`
  - fix hunks:
  - `hermes_cli/doctor.py`

    ```diff
    @@ -1264,9 +1264,23 @@ def run_doctor(args):
             check_warn("Skills Hub directory not initialized", "(run: hermes skills list)")
     
         from hermes_cli.config import get_env_value
    +
    +    def _gh_authenticated() -> bool:
    +        """Check if gh CLI is authenticated via token file or device flow."""
    +        try:
    +            result = subprocess.run(
    +                ["gh", "auth", "status", "--json", "authenticated"],
    +                capture_output=True, timeout=10,
    +            )
    +            return result.returncode == 0
    +        except (FileNotFoundError, subprocess.TimeoutExpired):
    +            return False
    +
         github_token = [redacted:secret-assignment]"GITHUB_TOKEN") or get_env_value("GH_TOKEN")
         if github_token:
             [redacted:secret-assignment]"GitHub token configured (authenticated API access)")
    +    elif _gh_authenticated():
    +        check_ok("GitHub authenticated via gh CLI", "(full API access — no GITHUB_TOKEN needed)")
         else:
             check_warn("No GITHUB_TOKEN", f"(60 req/hr rate limit — set in {_DHH}/.env for better rates)")
     
    ```
  - `tests/hermes_cli/test_doctor.py`

    ```diff
    @@ -663,3 +663,79 @@ def fake_get(url, headers=None, timeout=None):
         )
         assert not any(url == "https://opencode.ai/zen/go/v1/models" for url, _, _ in calls)
         assert not any("opencode" in url.lower() and "models" in url.lower() for url, _, _ in calls)
    +
    +
    +class TestGitHubTokenCheck:
    +    """Tests for GitHub token / gh auth detection in doctor."""
    +
    +    def test_no_token_and_not_gh_authenticated_shows_warn(self, monkeypatch, tmp_path):
    +        home = tmp_path / ".hermes"
    +        home.mkdir(parents=True, exist_ok=True)
    +        monkeypatch.setenv("HERMES_HOME", str(home))
    +        monkeypatch.setenv("PATH", "/nonexistent")  # gh not found
    +
    +        from hermes_cli.doctor import run_doctor, _DHH
    +        import io, contextlib
    +
    +        buf = io.StringIO()
    +        with contextlib.redirect_stdout(buf):
    +            run_doctor(Namespace(fix=False))
    +        out = buf.getvalue()
    +
    +        assert "No GITHUB_TOKEN" in out
    +        assert "60 req/hr" in out
    +
    +    def test_token_env_present_shows_ok(self, monkeypatch, tmp_path):
    +        home = tmp_path / ".hermes"
    +        home.mkdir(parents=True, exist_ok=True)
    +        monkeypatch.setenv("HERMES_HOME", str(home))
    +        monkeypatch.setenv("GITHUB_TOKEN", "ghp_test123")
    +        monkeypatch.setenv("PATH", "/nonexistent")  # gh not found
    +
    +        from hermes_cli.doctor import run_doctor
    +        import io, contextlib
    +
    +        buf = io.StringIO()
    +        with contextlib.redirect_stdout(buf):
    +            run_doctor(Namespace(fix=False))
    +        out = buf.getvalue()
    +
    +        assert "GitHub token configured" in out
    +
    +    def test_gh_authenticated_without_env_token_shows_ok(self, monkeypatch, tmp_path):
    +        home = tmp_path / ".hermes"
    +        home.mkdir(parents=True, exist_ok=True)
    +        monkeypatch.setenv("HERMES_HOME", str(home))
    +        # No GITHUB_TOKEN or GH_TOKEN
    +        monkeypatch.delenv("GITHUB_TOKEN", raising=False)
    +        monkeypatch.delenv("GH_TOKEN", raising=False)
    +
    +        # Mock gh to return success
    +        import shutil
    +        real_which = shutil.which
    +        def mock_which(cmd):
    +            return "/usr/local/bin/gh" if cmd == "gh" else real_which(cmd)
    +        monkeypatch.setattr(shutil, "which", mock_which)
    +
    +        call_log = []
    +        def mock_run(cmd, **kwargs):
    +            call_log.append(cmd)
    +            if cmd[:2] == ["gh", "auth"]:
    +                result = types.SimpleNamespace(returncode=0, stdout="", stderr="")
    +            else:
    +                result = types.SimpleNamespace(returncode=1, stdout="", stderr="")
    +            return result
    +
    +        import subprocess
    +        monkeypatch.setattr(subprocess, "run", mock_run)
    +
    +        from hermes_cli.doctor import run_doctor
    +        import io, contextlib
    +
    +        buf = io.StringIO()
    +        with contextlib.redirect_stdout(buf):
    +            run_doctor(Namespace(fix=False))
    +        out = buf.getvalue()
    +
    +        assert "gh auth" in str(call_log) or any(c[0] == "gh" for c in call_log), f"gh not called: {call_log}"
    +        assert "GitHub authenticated via gh CLI" in out or "token configured" in out
    ```
- **#16132: fix(doctor): check gh auth status when GITHUB_TOKEN absent**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#19868`

## Fix status

- Merged to main in PR #19868 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-04`.
