diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index e90fae3c08..6c7b58c8f2 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -53,6 +53,33 @@ def _has_provider_env_config(content: str) -> bool: return any(key in content for key in _PROVIDER_ENV_HINTS) +def _honcho_is_configured_for_doctor() -> bool: + """Return True when Honcho is configured, even if this process has no active session.""" + try: + from honcho_integration.client import HonchoClientConfig + + cfg = HonchoClientConfig.from_global_config() + return bool(cfg.enabled and cfg.api_key) + except Exception: + return False + + +def _apply_doctor_tool_availability_overrides(available: list[str], unavailable: list[dict]) -> tuple[list[str], list[dict]]: + """Adjust runtime-gated tool availability for doctor diagnostics.""" + if not _honcho_is_configured_for_doctor(): + return available, unavailable + + updated_available = list(available) + updated_unavailable = [] + for item in unavailable: + if item.get("name") == "honcho": + if "honcho" not in updated_available: + updated_available.append("honcho") + continue + updated_unavailable.append(item) + return updated_available, updated_unavailable + + def check_ok(text: str, detail: str = ""): print(f" {color('✓', Colors.GREEN)} {text}" + (f" {color(detail, Colors.DIM)}" if detail else "")) @@ -582,6 +609,7 @@ def run_doctor(args): from model_tools import check_tool_availability, TOOLSET_REQUIREMENTS available, unavailable = check_tool_availability() + available, unavailable = _apply_doctor_tool_availability_overrides(available, unavailable) for tid in available: info = TOOLSET_REQUIREMENTS.get(tid, {}) diff --git a/tests/hermes_cli/test_doctor.py b/tests/hermes_cli/test_doctor.py index 6594de4fad..5c038e3f58 100644 --- a/tests/hermes_cli/test_doctor.py +++ b/tests/hermes_cli/test_doctor.py @@ -1,5 +1,8 @@ """Tests for hermes doctor helpers.""" +from types import SimpleNamespace + +import hermes_cli.doctor as doctor from hermes_cli.doctor import _has_provider_env_config @@ -15,3 +18,50 @@ class TestProviderEnvDetection: def test_returns_false_when_no_provider_settings(self): content = "TERMINAL_ENV=local\n" assert not _has_provider_env_config(content) + + +class TestDoctorToolAvailabilityOverrides: + def test_marks_honcho_available_when_configured(self, monkeypatch): + monkeypatch.setattr(doctor, "_honcho_is_configured_for_doctor", lambda: True) + + available, unavailable = doctor._apply_doctor_tool_availability_overrides( + [], + [{"name": "honcho", "env_vars": [], "tools": ["query_user_context"]}], + ) + + assert available == ["honcho"] + assert unavailable == [] + + def test_leaves_honcho_unavailable_when_not_configured(self, monkeypatch): + monkeypatch.setattr(doctor, "_honcho_is_configured_for_doctor", lambda: False) + + honcho_entry = {"name": "honcho", "env_vars": [], "tools": ["query_user_context"]} + available, unavailable = doctor._apply_doctor_tool_availability_overrides( + [], + [honcho_entry], + ) + + assert available == [] + assert unavailable == [honcho_entry] + + +class TestHonchoDoctorConfigDetection: + def test_reports_configured_when_enabled_with_api_key(self, monkeypatch): + fake_config = SimpleNamespace(enabled=True, api_key="honcho-test-key") + + monkeypatch.setattr( + "honcho_integration.client.HonchoClientConfig.from_global_config", + lambda: fake_config, + ) + + assert doctor._honcho_is_configured_for_doctor() + + def test_reports_not_configured_without_api_key(self, monkeypatch): + fake_config = SimpleNamespace(enabled=True, api_key=None) + + monkeypatch.setattr( + "honcho_integration.client.HonchoClientConfig.from_global_config", + lambda: fake_config, + ) + + assert not doctor._honcho_is_configured_for_doctor()