fix(doctor): report subscription-CLI providers by their opt-in signal - #269
Merged
Conversation
CLAUDE_CODE and GEMINI_CLI had no branch in checkProviderHealth, so they fell through to the generic API key check. Those providers authenticate through their own CLI and never carry a key, which made doctor report them as "Not configured" no matter how they were set up, while generation worked fine from the same config. COPILOT_SDK's skip message carried a related divergence: doctor demanded a configured model, while the runtime activates the provider on a model, a key, or COPILOT_GITHUB_TOKEN. A user who opted in with a key or a token was told that nothing was configured at all. Both cases came from doctor re-deriving availability instead of reading the runtime's predicate. Doctor now consumes hasCopilotSdkAvailable and getConfiguredModels directly, and SUBSCRIPTION_CLI_SERVICES gives the set a single home so a future member inherits the model gate rather than the API key check. Closes #268
The previous commit resolved an unset COPILOT_SDK model to the service default and reported the provider healthy, on the assumption that the service's own fallback would supply it at request time. It does not: the fan-out is one request per configured model (from(getModels(ai)) in ai-request.manager.ts), so an empty model list emits zero requests and the service default is never consulted. countRequests returns 0 for a token-only setup. Doctor was therefore calling a setup healthy that produces no suggestions at all — the same false report as issue #268, pointed the other way. Such a setup is now a warning that names the fix, and the default-model resolution is gone, so doctor no longer asserts a behaviour the runtime does not have. Making the runtime actually default would change generation and belongs in its own change. This also drops a live auth probe from the model-less path, which previously ran only to describe a provider that would send nothing.
tak-bro
force-pushed
the
fix/doctor-subscription-cli-status
branch
from
August 14, 2026 03:35
554f114 to
864aebc
Compare
tak-bro
marked this pull request as ready for review
August 14, 2026 04:12
Owner
Author
|
🎉 This PR is included in version 2.11.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
aicommit2 doctorreportedCLAUDE_CODEas Not configured even when it was set up and generating commit messages (#268). The same divergence existed forGEMINI_CLI, which the reporter left untested, and in mirrored form forCOPILOT_SDK.checkProviderHealthhad no branch for the first two, so they fell through to the generic API key check. Subscription-CLI providers authenticate through their own CLI and never carry a key, so that check could never pass — they were unreachable-healthy regardless of config. Generation worked becauseget-available-ais.tsgates them on a configured model instead.COPILOT_SDKhad a related divergence in its skip message: doctor demanded a configured model, while the runtime activates on a model or a key orCOPILOT_GITHUB_TOKEN. A user who opted in with a key or token was told nothing was configured.That setup does not actually generate, though — the fan-out is one request per configured model (
from(getModels(ai))), so an empty model list emits zero requests and the service's own default-model fallback is never consulted (countRequestsreturns 0). Doctor therefore reports it as a warning naming the fix, rather than healthy. Making the runtime actually default is a generation change and is deliberately not in this PR.The root cause is that doctor re-derived provider availability rather than reading the runtime's predicate. This PR closes both symptoms and removes one of the two duplicate encodings.
Changes
src/commands/doctor.tsNo models configured, model →Model configured (Model: …).COPILOT_SDK's skip now gates on the runtime'shasCopilotSdkAvailableinstead of the model alone, and names all three opt-in signals, since telling the user to configure a model hid the key and token paths.Opted in but no model configured — no requests will be sent, because that is what the fan-out does. This also short-circuits before the live auth probe, which previously ran only to describe a provider that would send nothing.checkCopilotSdkPrereqs'sif (!model)guard became unreachable behind the new gate, so the guard and its now-unused parameter were removed.hasConfiguredModeldropped in favour of the shared helper.src/commands/get-available-ais.ts—getConfiguredModelsextracted,hasConfiguredModelsdefined on top of it, both exported. Doctor consumes them, so the model-parsing logic exists once.src/utils/config.ts—SUBSCRIPTION_CLI_SERVICESgives the set a single home, so a future member inherits the model gate rather than the API key check that caused this bug.tests/specs/doctor.ts— four regression tests plus aproviderLinehelper, so an assertion cannot be satisfied by another provider's line.docs/providers/copilot-sdk.md— the documented healthy signal now matches the default-model behaviour.How to Test
Before:
⏭️ CLAUDE_CODE Not configuredAfter:
✅ CLAUDE_CODE Model configured (Model: sonnet)Three states covered by the suite:
⏭️ COPILOT_SDK Not configured (needs model, key, or COPILOT_GITHUB_TOKEN)COPILOT_GITHUB_TOKENonly⚠️ COPILOT_SDK Opted in but no model configured — no requests will be sentCOPILOT_SDK.model⚠️ COPILOT_SDK Unsupported classic PAT …(reaches the environment check)CLAUDE_CODE.model=sonnet✅ CLAUDE_CODE Model configured (Model: sonnet)pnpm test→ 413 passed, 0 failed (408 on main). Every new test was confirmed to fail before the fix by stashing the source changes and rebuilding.Risks
COPILOT_SDKsetup with a configured model now reachescheckCopilotSdkEnvironment, which it previously exited before. That path runscopilot --versionand a live auth probe, so those users see doctor take longer. Intended — reporting whether the setup actually works is whatdoctoris for, and token-configured setups already paid this cost. Bounded byconfig.timeout.CLAUDE_CODE/GEMINI_CLIreport healthy from config alone, without probing theclaude/geminibinary. A deliberate choice:execSyncinherits a non-login shellPATH, so probing would produce false "CLI not found" for users whose binary comes from a shell rc — the same class of false negative this PR removes. It matches the existing idiom (API key configured,Cookie configured) and the documented contract that the CLI is checked at request time.SUBSCRIPTION_CLI_SERVICEScontainsCOPILOT_SDK, which returns from its own branch earlier, so the shared gate never sees it. TheCOPILOT_GITHUB_TOKENtest pins that ordering: reaching the shared gate would report it as having no models.Alternatives considered
CLAUDE_CODE/GEMINI_CLI. Rejected for thePATHreason above. A real binary-and-auth probe is worth doing, but as its own change with its own handling of non-login shells.SUBSCRIPTION_CLI_SERVICESfor the runtime gate inget-available-ais.ts. Rejected: the runtime groupsOLLAMAwithCLAUDE_CODE/GEMINI_CLIand givesCOPILOT_SDKa wider predicate, so substituting the set there would reintroduce this bug from the other side. The constant's comment says so, to stop a future reader from making that edit.COPILOT_SDKalone. It was raised as out of scope first, since its gate came out of Copilot SDK provider seems to never be taken in consideration #256/Copilot SDK provider seems to still never be taken in consideration - v2 #259. Fixing it here was a deliberate call: it is the same defect class, and leaving one half in place would have left the next reader with a working example of the pattern.COPILOT_SDKis opted in by key or token (sosubscriptionCliConfigParsers.modelyields[gpt-4.1]). That is arguably what Session was not created with authentication info #254 intended, and it would make the model-less setup actually generate. Rejected here because it changes generation behaviour, not a health report, and deserves its own change and its own review.Checklist
pnpm testpasses (413 passed, 0 failed)pnpm lintpasses, zero warningspnpm buildpassespnpm type-checkunchanged — 67 pre-existing errors, none in the touched filesdocs/,README.md)Closes #268