* feat(gemini-vertex): wire env-flag provider routing, selection and model defaults Builds on the native client PR. CLAUDE_CODE_USE_GEMINI_VERTEX=1 now selects the provider end-to-end: - integrations: gemini-vertex vendor descriptor, route/runtime metadata, Gemini model catalog additions, regenerated artifacts - client.ts: route to the native Vertex client on the env flag; the Anthropic-on-Vertex branch is only reached for real Claude-on-Vertex - providerConfig.ts: resolveProviderRequest Vertex model/project/location resolution; getAdditionalModelOptionsCacheScope returns null instead of 'firstParty' for Gemini Vertex sessions (bootstrap stays third-party) - model defaults / deprecation / context windows recognise the category - startup selection: hasProviderSelectionFlags + hasCompleteProviderSelection recognise the flag and its concrete project/model/location vars, so an explicit Vertex env wins over a saved profile like every other provider - drift sweep: auth (is3P, is1PApiCustomer), log, apiPreconnect, agentRouting, spawnUtils teammate forwarding, statusNotice, StartupScreen, onboard-github, providerFlag (--provider gemini-vertex, --model mapping), managedEnvConstants (managed; project ids deliberately NOT in SAFE_ENV_VARS, same policy as ANTHROPIC_VERTEX_PROJECT_ID), providerStartupOverrides, GCP credential prefetch, SDK provider type - durable guard: providerSelectionFlags.ts is the single source of truth for CLAUDE_CODE_USE_* flags; enumerating call sites derive from it and a parity test fails when a new provider flag appears unregistered Profile/UI integration follows in the next PR. Typecheck clean, test:provider 703/703 plus 353 targeted tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): sanitize smuggled thought-signature tool ids on foreign wires The Gemini Vertex client smuggles Gemini thought signatures into tool_use ids (toolu_vertex_x~~sig~~<base64>, up to ~1.8k chars) so they survive session persistence and can be replayed to Vertex, which requires them. Any other provider consuming that history then echoes the giant id and the backend rejects it - e.g. Codex: 400 "Invalid 'input[2].id': string too long" after a /provider switch or --resume from a Vertex session. Fix at every wire boundary, never in the stored history (Vertex keeps reading its signatures): - utils/toolUseIds.ts: shared dependency-free sanitizeToolUseIdForWire - cut at the first character outside [A-Za-z0-9_-] (no legitimate id contains one), cap with a deterministic FNV-1a hash tail so distinct overlong ids stay distinct - codexShim.normalizeToolUseId: sanitize before building fc_/call_ ids (Responses API limit 64) - openaiShim chat/completions: sanitize tool_calls[].id, tool_call_id and the correlation sets (OpenAI limit 40) - normalizeMessagesForAPI: same sanitation for the Anthropic wire, gated on getAPIProvider() !== 'gemini-vertex' Verified E2E on a real Vertex session containing 1.7k-char signed ids: resumed under the Codex profile -> completes (was the 400); resumed under the Vertex profile -> signatures preserved, completes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review on #1614 - onboard-github: clear CLAUDE_CODE_USE_GEMINI_VERTEX in the fast-path (applyGithubOnboardingProcessEnv + buildGithubOnboardingSettingsEnv), so GitHub activation resets Vertex routing on both onboarding paths, not just the device-flow finalize path. Covered by an assertion in the existing test. - messages.normalizeMessages: guard against falsy/non-object slots before the switch reads message.type, matching the default arm's intent. - toolUseIds.sanitizeToolUseIdForWire: keep at least one prefix char if a caller passes maxLength < tail length. - providerFlag.test: add the Gemini Vertex env vars to the cleanup lists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(gemini-vertex): add profile compatibility so the preset routes natively Addresses jatmn's P1 review on #1614: gemini-vertex is selectable in ProviderManager (it is in ORDERED_PROVIDER_PRESETS), but without a profile compatibility mode a saved profile fell through to the OpenAI-compatible path and was routed through the shim to aiplatform.googleapis.com instead of the native client. Brings the profile env-shaping into this PR (the /provider UI forms and launcher stay in the follow-up UI PR): - providerProfile.ts: 'gemini-vertex' compatibility/profile types, buildGeminiVertexProfileEnv, managed + secret env keys. - providerProfiles.ts: resolveProfileCompatibility mode, profile env application, startup-profile bootstrap, env/profile alignment. - client.ts: route a saved gemini-vertex profile to the native client (gated by shouldRouteToGeminiVertexFromProfile, yields to an explicit selection of another provider). - providerValidation.ts: Vertex project/credential validation messages. So a saved or preset-selected Gemini Vertex profile now sets CLAUDE_CODE_USE_GEMINI_VERTEX + the Vertex project/location/model env and reaches the native client. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn review on #1614 (P1 test, P2 isolation, P3 startup) - ProviderManager.test.tsx: add 'Google Vertex AI Gemini' to PRESET_ORDER at its generated position (between Fireworks and Gemini) so navigation selects the intended preset and the ProviderManager suite is green again. (P1) - messages.toolUseIds.test.ts: clear the full provider-selection registry in before/afterEach so getAPIProvider() (which checks FOUNDRY before route resolution) reflects only the test's own flags — proven by passing under CLAUDE_CODE_USE_FOUNDRY=1. (P2) - StartupScreen detectProvider: when no Vertex project resolves, show a /projects/<set GEMINI_VERTEX_PROJECT>/locations/... project-required state instead of a project-less endpoint the native client never calls. (P3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): make tool-id tests suite-order independent - providerSelectionFlags parity: scan only runtime source (skip *.test.*), so a flag a test fixture sets but the code never reads (e.g. a placeholder in discoveryService.test.ts from #1620) doesn't trip the gate. - messages.toolUseIds: mock getAPIProvider directly instead of relying on global env/route state, which other suite tests mutate via mock.module — makes the wire assertions hermetic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review on the routing PR - providerProfile.ts: honor an explicit shell GEMINI_VERTEX_AUTH_MODE over the persisted/default value (same precedence as model/project). - providerProfiles.ts: validate the active Vertex project in the profile alignment check so a project drift forces re-apply instead of routing to the wrong project (only when the profile pins a project; ADC-resolved stays free). - messages.ts: harden isNotEmptyMessage against malformed user/assistant slots ({ type } / { message: {} }) that previously crashed on message.message.content; add a regression test. - providerConfig.local.test.ts: clear a possibly-leaked CLAUDE_CODE_USE_OPENAI so the Gemini Vertex cache-scope assertion can't pass/fail for the wrong reason. The Critical "typecheck must pass" item is a false positive: both `bun run typecheck` and `bun run typecheck:type-tests` pass cleanly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn re-review on #1614 (P2 content guard, P3 /provider summary) - messages.ts: isNotEmptyMessage now narrows content to string | array before the indexed reads, so a non-string/non-array content (object/number) no longer crashes on content[0].type. Added the object/number regression cases. - provider.tsx: buildCurrentProviderSummary now has a Gemini Vertex branch, so a CLAUDE_CODE_USE_GEMINI_VERTEX session reports the Vertex provider/model and the /projects/<project>/locations/<location> endpoint (project-required state when unset) instead of falling through to Anthropic. Added summary tests for both the resolved and project-required cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): guard normalizeMessages payloads and forward all Vertex project aliases Addresses jatmn's third re-review on #1614: - messages.ts: normalizeMessages now drops malformed assistant/user slots (missing message, or non-array/non-string content) before the branches dereference message.message.content — so progress-derived messages routed through normalizeMessages (queryHelpers) can't crash the pipeline. Added a regression test for the missing/non-array content paths. - spawnUtils.ts: TEAMMATE_ENV_VARS now forwards every project alias Gemini Vertex accepts (adds GCLOUD_PROJECT and GOOGLE_PROJECT_ID), so an env-only teammate launched with any of them keeps the project hint instead of failing Vertex validation/routing. Added a spawn-env coverage test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): drop content arrays with malformed elements in normalizeMessages Addresses CodeRabbit follow-up: the array guard rejected bad content containers but arrays whose elements are null/primitive/typeless or a text block without text still reached block.type / isNotEmptyMessage and could crash the same malformed-history path. Drop the whole slot when any element is malformed, with regression cases. Also assert the normalized output shape in the well-formed cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): use toMatchObject for normalized-shape assertion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): use saved profile for project/model and validate credentials Addresses jatmn's re-review on #1614: - client.ts: when routing purely from the saved profile (no CLAUDE_CODE_USE_GEMINI_VERTEX flag), fall back to the profile's own project (stored in baseUrl) and model instead of only reading process.env — so a saved gemini-vertex profile no longer throws "project required" or routes an ADC session to the ambient/default project and gemini-2.5-flash. Env still wins when explicitly set. - providerValidation.ts: the Gemini Vertex branch now also resolves the selected credential path (mirroring getAnthropicClient's auth-mode mapping) and fails when none is available, so an access-token profile without GEMINI_ACCESS_TOKEN (or a missing ADC) is caught at setup/startup instead of on the first request. Added a credential-missing regression test and made the project-alias test inject a deterministic credential. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): wire the provider into doctor, /provider GitHub switch, and profile scripts Addresses jatmn's re-review on #1614 (provider-list drift in peripheral tools): - scripts/system-check.ts: doctor now dispatches Gemini Vertex — env checks (project/model/location/credential), reachability + generation readiness show a Vertex-specific skip instead of reporting the Anthropic login flow, currentBaseUrl and serializeSafeEnvSummary cover it. Test-only export + tests for the dispatch and safe-env summary. - ProviderManager.tsx: GitHub activation now clears every selection flag from the central PROVIDER_SELECTION_FLAGS list (including CLAUDE_CODE_USE_GEMINI_VERTEX) in both the settings patch and process.env, so switching to GitHub can't keep routing on a previously selected Vertex profile. - scripts/provider-launch.ts + provider-bootstrap.ts: accept gemini-vertex in the provider allowlists, build a real gemini-vertex profile (instead of a broken openai one), and fail loudly when project/credentials are missing. Verified end-to-end: `doctor:runtime` reports the Vertex provider; the launch script errors clearly without creds; bootstrap saves a gemini-vertex profile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review on PR-2 (8 findings) - client.ts: re-resolve the Vertex credential per request via getAccessToken so long-lived clients pick up refreshed ADC tokens instead of a stale one captured at construction (Critical); coerce geminiVertexModel to a plain string so the model fallback satisfies tsc and never passes undefined. - toolUseIds.ts: coerce non-string ids before .replace so loosely typed history blocks (number/object/undefined) cannot throw on the wire path. - system-check.ts / provider-launch.ts / provider-bootstrap.ts: resolve the Gemini Vertex project through the shared getGeminiVertexProjectId resolver instead of three drifting OR chains, matching the runtime validator/client contract and adding whitespace normalization. - providerSelectionFlags.test.ts: keep the parity scan non-tautological by excluding the registry and its own test. - bugfixes.test.ts: add a sectionBetween() helper that asserts both anchors resolve before slicing the drift-guard regions (a missing end anchor used to silently widen the slice and pass); fix the StartupScreen detectProvider end anchor it exposed. - deprecation.test.ts: lock the gemini-vertex bypass so Anthropic model retirement dates never leak into the Vertex deprecation banner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(gemini-vertex): make deprecation bypass test hermetic The env-based variant failed deterministically in the full CI suite: getModelDeprecationWarning() resolves the provider through getAPIProvider(), which reads global env/route state that other suites mutate, so setting CLAUDE_CODE_USE_GEMINI_VERTEX on process.env did not reliably select the gemini-vertex route. Mock getAPIProvider directly (same pattern as messages.toolUseIds.test.ts) so the assertions are order-independent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(provider-flags): exclude all test files from the consumed-flag scan CodeRabbit follow-up: the "registered flags are actually consumed" check only skipped the registry and its own test, so a flag referenced solely by another test fixture would still look consumed. Skip every *.test.* file so the assertion proves real runtime consumption. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn review — project/ADC/preset issues - providerProfiles.ts: never persist the Vertex API endpoint URL as the project id. The /provider preset seeds the shared baseUrl field with https://aiplatform.googleapis.com; a URL-shaped value would build invalid /v1/projects/https://.../locations/... requests. Add geminiVertexProjectFromProfile() to drop URL-shaped values and reuse it in both the profile-env builder and the alignment check. - providerUiMetadata.ts: base requiresApiKey on authMode === 'api-key' instead of requiresAuth, so the ADC-only Gemini Vertex preset is no longer pushed through the generic API-key step (ADC profiles have no key to enter). - provider-launch.ts: only require an upfront credential in access-token mode; ADC mode (explicit or default) relies on ambient Google ADC that only the runtime resolver can discover, matching doctor and the native client. - Tests: lock the endpoint-not-project guard (both URL and real-project cases) and the ADC-preset API-key gating; harden providerProfiles env cleanup with the Vertex project/credential keys. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit follow-up — credentials, URL guards, tests - providerProfiles.ts / providerProfile.ts: carry the active credential forward when applying a saved Gemini Vertex profile. buildCompatibilityProcessEnv clears every managed key (incl. GEMINI_ACCESS_TOKEN / GOOGLE_APPLICATION_CREDENTIALS), so a profile switch could relaunch unauthed; re-add the access token (access-token mode) or ADC file (adc mode). - providerProfiles.ts (startup-profile path) and provider-bootstrap.ts: extend the endpoint-URL guard to the remaining sites that fed baseUrl into the Vertex project — drop URL-shaped values via geminiVertexProjectFromProfile() and reject http(s) --base-url in the bootstrap script. - Tests: add applyModelFlagFromArgs GEMINI_VERTEX_MODEL coverage; lock access-token / ADC credential preservation on profile apply; isolate every provider-selection flag in the cache-scope test via PROVIDER_SELECTION_FLAGS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address jatmn round-3 — saved-profile-only path - messages.ts: sanitize Vertex tool_use ids by the effective provider (env flag OR saved active profile) via new isGeminiVertexEffectiveProvider(), not the env-only getAPIProvider(). On the saved-profile-only route the signed thought-signature ids were stripped before geminiVertexClient could decode them, re-triggering the 400 the smuggling avoids. - client.ts: run the saved-profile project through geminiVertexProjectFromProfile (shared "URL is not a project" sanitizer) instead of using activeProfile.baseUrl raw, so a preset profile can't build /v1/projects/https://.../. - providerProfiles.ts: carry the active credential into the persisted startup profile (buildStartupProfileFromActiveProfile), mirroring the live switch path, so a saved Vertex profile relaunches authenticated. - vendors/gemini-vertex.ts: credentialEnvVars now lists only real credentials (GEMINI_ACCESS_TOKEN / GOOGLE_APPLICATION_CREDENTIALS), not routing/config vars, so generic consumers don't surface the enablement flag/project id as an apiKey; defaultBaseUrl is a project placeholder so the preset routes through the full setup form to collect the project id. - Tests: saved-profile effective-provider preservation; startup-profile credential persistence (access-token + ADC); credentialEnvVars/apiKey gating; preset baseUrl placeholder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): doctor validates the Vertex credential before launch The Vertex generation-readiness check unconditionally passed ("relying on ambient ADC"), so a fresh env with a project but no resolvable ADC passed the doctor that provider-launch runs as its preflight, then `node bin/openclaude` failed startup validation. Reuse the runtime resolveGeminiCredential() (mapping GEMINI_VERTEX_AUTH_MODE -> GEMINI_AUTH_MODE and clearing API keys, exactly like the native client) and fail when the credential cannot be resolved, so the preflight matches the provider that will actually run. Also clarify the GEMINI_VERTEX_AUTH line that ambient ADC is validated by the readiness check. Tests: readiness passes when a credential resolves and fails when none does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review (in-scope findings) - provider-bootstrap.ts: the saved-profile summary now prints GEMINI_VERTEX_MODEL for gemini-vertex profiles instead of falling through to a default. - provider-launch.ts: the usage string lists gemini-vertex and no longer repeats mistral, matching the accepted profile arguments. - StartupScreen.ts: detectProvider resolves Vertex model/location/project through the shared getGeminiVertexModel/Location/ProjectId helpers (same contract as runtime/provider summary) instead of manual env reads. - StartupScreen.test.ts: lock the alias fallback (GOOGLE_CLOUD_PROJECT resolves the endpoint project when GEMINI_VERTEX_PROJECT is unset). - client.test.ts: snapshot/clear/restore all three project aliases (GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, GOOGLE_PROJECT_ID) so the Vertex tests can't leak host-environment project ids. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): thread saved-profile-only routing through provider identity The saved-profile-only Vertex route (active gemini-vertex profile, no CLAUDE_CODE_USE_GEMINI_VERTEX flag) was only honoured by the API client, so other env-flag-only "is this Vertex?" checks disagreed with it: - routeMetadata.ts: resolveActiveRouteIdFromEnv() now returns 'gemini-vertex' when activeProfileProvider is gemini-vertex and no provider env flag is set, so /model loads the Vertex catalog/discovery for the same session. - providerConfig.ts: getAdditionalModelOptionsCacheScope() scopes a saved gemini-vertex profile as third-party (null) instead of 'firstParty', so bootstrap/model-option traffic is not fetched through the Anthropic path. - provider.tsx: buildCurrentProviderSummary() reports the effective provider so a saved Vertex profile no longer shows "Anthropic"/api.anthropic.com. Tests cover each: saved-profile route resolution (and precedence when a flag is set), the cache-scope fallback, and the provider summary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): resolve saved Vertex profile before env-only inference CodeRabbit follow-up: resolveActiveRouteIdFromEnv() placed the saved-profile gemini-vertex check after resolveEnvOnlyProviderRouteId(), so an ambient credential-only env (e.g. a stray XAI_API_KEY) won over the active profile and disagreed with getAnthropicClient (which routes saved gemini-vertex profiles unless a conflicting provider flag is set). Move the check above env-only inference, guarded on CLAUDE_CODE_USE_OPENAI to mirror the conflicting-flag rule. Add regressions: profile wins over ambient credential env, and yields to an explicit OpenAI flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): accept ADC-derived project; GitHub token over blank key - providerValidation.ts: resolve the Vertex credential before failing on a missing project, and accept credential.projectId (ADC-derived) as the project — mirroring getAnthropicClient, which falls back to the ADC project. A valid ambient-ADC setup with no project env var no longer fails startup validation. - provider-launch.ts: defer the project check to runtime in ADC mode (ADC can supply the project); access-token mode still requires an explicit project. - openaiShim.ts: GitHub hydration uses || (not ??) so a blank exported OPENAI_API_KEY no longer wins over GITHUB_TOKEN/GH_TOKEN and leaves GitHub requests unauthenticated. Pre-existing main behaviour, fixed at maintainer request; matches the Gemini/Mistral branches. - Tests: ADC-derived project acceptance + access-token project-required; GitHub-token fallback when OPENAI_API_KEY is blank. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): whitespace-safe Vertex config + ADC-only bootstrap CodeRabbit follow-up (all on this PR's Vertex code): - provider-bootstrap.ts: only require an explicit project for access-token profiles; ADC can supply credential.projectId at runtime, so ADC-only Vertex profiles can now be created. Pass the resolved authMode through. - providerProfile.ts: sanitize each shell/persisted candidate before applying precedence (model/project/location and the access-token re-add), so a whitespace-only shell value (e.g. GEMINI_VERTEX_PROJECT=' ') can't drop the real persisted config; normalize the auth-mode check too. - providerValidation.ts: trim GEMINI_ACCESS_TOKEN before inferring the default auth mode so a blank token doesn't force access-token over ADC. - Tests: assert the concrete 'openai' route in the precedence case; isolate the GCLOUD_PROJECT/GOOGLE_PROJECT_ID aliases in the env-diagnostics fallback tests; lock the whitespace-over-persisted precedence in buildStartupEnvFromProfile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): saved profile wins over ambient env in profile-only routing getAnthropicClient's saved-profile-only Vertex branch resolved the project/model from process.env before falling back to the profile, contradicting its own comment. A saved gemini-vertex profile targeting one project/model could route to an ambient GOOGLE_CLOUD_PROJECT / stale GEMINI_VERTEX_MODEL when no CLAUDE_CODE_USE_GEMINI_VERTEX flag is set. profileProject/profileModel are only populated when routedFromProfileOnly, so leading the precedence with them makes the selected profile win in profile-only mode while env still wins for an explicit Vertex selection. Regression locks the precedence (profile project + model win over ambient env). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): align saved-profile route resolution with defined flag rule resolveActiveRouteIdFromEnv's saved-profile gemini-vertex branch only blocked on a *truthy* CLAUDE_CODE_USE_OPENAI, but the client's shouldRouteToGeminiVertexFromProfile treats any *defined* conflicting provider flag as explicit intent. With a saved gemini-vertex profile and CLAUDE_CODE_USE_OPENAI=0, route metadata reported gemini-vertex while the client refused the saved-profile Vertex route, so /model loaded the Vertex catalog for a session whose requests would not use Vertex. Mirror the client exactly via PROVIDER_SELECTION_FLAGS (any defined flag other than the Vertex one blocks the route). Regression covers the falsey OpenAI flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): single source of truth for Vertex auth mode + edge cases CodeRabbit follow-up — the recurring "infer auth mode from token" logic was duplicated inline (and inconsistently) across the client/doctor/profile paths, so a token-only or whitespace-only config was classified differently per site. - geminiAuth.ts: add resolveGeminiVertexAuthMode() — explicit GEMINI_VERTEX_AUTH_MODE (case/space-normalized) wins, else a present (sanitized) token implies access-token, else adc. Wire it into providerProfiles.ts (apply + startup builders) and system-check.ts (env check, generation-readiness, safe summary), fixing the token-only case that previously forced adc and dropped the bearer. - system-check.ts: don't hard-fail a missing project in ADC mode (ADC supplies credential.projectId; only access-token mode requires an explicit project); normalize the credential-set boolean with trim instead of ??. - statusNoticeLocalModel.ts: handle the gemini-vertex route id explicitly so a saved-profile Vertex route never resolves to an OPENAI base URL. - Tests: resolveGeminiVertexAuthMode unit cases; token-only auth-mode regressions for apply + startup persistence; deterministic access-token validator stub; exact provider-summary assertions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(gemini-vertex): document the Gemini-on-Vertex provider and setup The PR adds a first-class native Gemini Vertex route but the user-facing docs didn't mention it (repo guidance requires doc updates for provider/setup changes). - README.md: add a "Google Vertex AI (Gemini)" row to the supported-provider table (enable flag, ADC vs access-token auth, project aliases, model/region), and clarify the existing Gemini row is Google AI Studio (API key). - docs/advanced-setup.md: add a "Gemini on Vertex AI" section with ADC and access-token setup, distinct from Claude-on-Vertex, and cross-link it from the Claude-on-Vertex section (which previously only pointed to the API-key Gemini provider for Gemini models). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gemini-vertex): add CLAUDE_CODE_USE_GEMINI_VERTEX to isUsing3PServices() The exported isUsing3PServices() helper was missing the Gemini Vertex flag, causing /login //logout to remain visible and auth status to report as first-party when CLAUDE_CODE_USE_GEMINI_VERTEX=1. The local is3P check inside isAnthropicAuthEnabled() was already correct; this aligns the exported helper to match. Adds a dedicated drift guard test targeting isUsing3PServices() directly, since the existing test only covered the separate local is3P block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review findings - providerProfile.ts: use firstSanitizedProviderValue for Google alias vars (GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, GOOGLE_PROJECT_ID, GOOGLE_APPLICATION_CREDENTIALS) so a whitespace-only shell value doesn't shadow a real persisted value - providerProfiles.ts: wrap project alias env lookups with trimOrUndefined() to prevent whitespace-only values blocking the fallback chain - providerProfiles.ts: add GOOGLE_APPLICATION_CREDENTIALS and GEMINI_VERTEX_AUTH_MODE=adc to isEnvOnlyProviderProfile so ADC-only configurations are correctly detected - providerValidation.ts: use resolveGeminiVertexAuthMode() instead of duplicating the auth-mode inference inline - provider.tsx: simplify Gemini Vertex branch condition to effectiveRouteId check only (resolveActiveRouteIdFromEnv already covers the env flag) - system-check.ts: add project validation after credential resolution so the doctor catches missing project in ADC mode (mirrors getProviderValidationError) - provider-bootstrap.ts: use resolveGeminiVertexAuthMode() to infer access-token mode when GEMINI_ACCESS_TOKEN is set without explicit GEMINI_VERTEX_AUTH_MODE - docs/advanced-setup.md: clarify that ADC project derivation only applies in ADC mode; access-token mode requires an explicit project env var Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gemini-vertex): use resolveGeminiVertexAuthMode in provider-launch Replace duplicated inline auth-mode inference with the shared helper, consistent with provider-bootstrap and system-check (CodeRabbit #3417084093). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gemini-vertex): persist Vertex creds, sanitize project fallback, harden env test Round-3 CodeRabbit findings on #1614: - provider-bootstrap.ts: carry the active credential into the saved profile. PROFILE_ENV_KEYS clears GEMINI_ACCESS_TOKEN / GOOGLE_APPLICATION_CREDENTIALS on relaunch, so an access-token profile must persist the bearer (and fail fast if absent) and an ADC profile must persist the credentials file — otherwise the saved profile relaunches unauthed (thread 3460713116). - providerProfiles.ts: sanitize each Vertex project candidate with trimOrUndefined() + ?? at the startup-profile site so a whitespace-only GEMINI_VERTEX_PROJECT no longer shadows a real GOOGLE_CLOUD_PROJECT / GCLOUD_PROJECT / GOOGLE_PROJECT_ID (thread 3460713146; the compatibility builder site was already fixed in 0501694). - security-hardening.test.ts: assert env classification via runtime isProviderManagedEnvVar() / SAFE_ENV_VARS.has() instead of slicing source text, so the policy test tracks behavior, not formatting (thread 3460713129). Validation: tsc --noEmit, type-tests, security-hardening.test.ts (13/13), knip — all green. providerProfiles getAPIProvider() timeouts are pre-existing (identical on the unmodified tree). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(provider-flags): derive flag-agnostic checks from the registry Address jatmn review 4558518184 on PR #1614: - Export hasAnyTruthyProviderSelectionFlag / hasConflictingProviderFlag from providerSelectionFlags.ts so the flag-agnostic call sites derive from the single source of truth instead of hand-enumerating CLAUDE_CODE_USE_* flags. Adopted by hasExplicitProviderSelection, hasNoExplicitNonOpenAICompatibleProvider, isUsing3PServices, and hasConflictingProviderFlagsForProfile (via an exhaustive activeProviderFlagForCompatibilityMode switch that preserves the github-enterprise -> CLAUDE_CODE_USE_GITHUB mapping and is compile-checked). Per-provider checks (hasConcreteProviderSelection, hasCompleteProviderSelection) stay as-is since they are not flag-agnostic. - Give the two source-scanning parity tests an explicit 30s timeout so they no longer flake under parallel test load. - Add unit tests for both helpers covering every registered flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): align access-token startup precedence and use isEnvTruthy Address jatmn review 4565330236 on PR #1614: - [P2] providerProfiles.ts: hasCompleteProviderSelection now treats GEMINI_ACCESS_TOKEN (and GEMINI_VERTEX_AUTH_MODE='access-token') as a concrete Gemini Vertex selection, matching the sibling hasConcreteProviderSelection. Without this, CLAUDE_CODE_USE_GEMINI_VERTEX=1 + GEMINI_ACCESS_TOKEN (no project) was not "complete", so a saved profile could override the user's explicit access-token env selection at startup — unlike the ADC path. Regressions added for both the token and the auth-mode signals. - [P3] providerFlag.ts (applyModelFlagFromArgs) and StartupScreen.ts (detectProvider): recognize the Vertex flag via isEnvTruthy() instead of an exact '1'/'true' match, so 'yes'/'on'/whitespace-normalized values get the same model routing and startup banner as the rest of the codebase. Regressions added for both sites. Validation: tsc --noEmit clean; providerFlag, StartupScreen, providerProfiles, client, routeMetadata, compatibility, providerSelectionFlags suites green. Branch rebased on latest main (integrations artifacts regenerated). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gemini-vertex): lock isUsing3PServices drift guard to the flag registry After the round-4 refactor (jatmn review 4558518184) isUsing3PServices() derives from hasAnyTruthyProviderSelectionFlag() instead of hand-enumerating CLAUDE_CODE_USE_* flags, so the source-text drift guard that scanned the function body for the literal CLAUDE_CODE_USE_GEMINI_VERTEX no longer matched and failed under test:full. Update the guard to lock the real post-refactor guarantee: (1) the function routes through the registry helper, and (2) the Vertex flag is registered in PROVIDER_SELECTION_FLAGS (the single source of truth). This tracks the behavior the refactor introduced instead of regressing to the literal-scan drift it eliminated. Validation: bugfixes.test.ts Gemini Vertex guards 5/5 green; tsc --noEmit clean. Remaining local test:full failures are environmental only (FR-locale Intl thousands separator and the CLI-bundle build artifact) and pass on CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): wire flag into env-file allowlist, test scrubs and truthiness Addresses maintainer review on PR #1614: - envFile.ts: add CLAUDE_CODE_USE_GEMINI_VERTEX plus GEMINI_VERTEX_* / GOOGLE_APPLICATION_CREDENTIALS / GCLOUD_PROJECT / GOOGLE_PROJECT_ID to ALLOWED_ENV_FILE_KEYS so --provider-env-file .env no longer silently rejects the documented Vertex setup. Adds a PROVIDER_SELECTION_FLAGS-derived parity test plus a full Vertex .env load test. - Add CLAUDE_CODE_USE_GEMINI_VERTEX to the provider env scrub/restore lists in betas, conversationRecovery, model, withRetry and promptCacheBreakDetection tests to prevent cross-test flag leakage. - providerFlag.applyModelFlagFromArgs and StartupScreen.detectProvider: use isEnvTruthy for all provider flags instead of literal '1'/'true' checks, removing the Vertex-only inconsistency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): address CodeRabbit review — drift, malformed-history and credential hardening Verified each CodeRabbit finding against current code; applied the valid ones, skipped two with documented reasons. Provider-flag drift / registry adoption: - auth.ts: isAnthropicAuthEnabled's is3P gate now derives from hasAnyTruthyProviderSelectionFlag() instead of a hand-maintained 8-flag list. - routeMetadata.ts: reuse the shared hasConflictingProviderFlag() helper instead of an inline PROVIDER_SELECTION_FLAGS scan. - withRetry/promptCacheBreakDetection tests: derive env scrub lists from PROVIDER_SELECTION_FLAGS so new flags are covered automatically. - bugfixes drift guard updated to assert the registry derivation rather than the now-removed literal in auth.ts. Credential / managed-env correctness: - managedEnvConstants.ts: treat GOOGLE_APPLICATION_CREDENTIALS as provider-managed so host-managed sessions can't have ADC creds swapped via settings. - providerStartupOverrides.ts: clear the Google ADC/project aliases (GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, GOOGLE_PROJECT_ID) so a cleared Vertex override can't stay partially active. - agentRouting.ts: clear the Vertex-specific selectors before forcing an OpenAI-compatible override, mirroring the existing GEMINI_MODEL/BASE_URL clears. - providerProfile.ts: treat GEMINI_VERTEX_AUTH_MODE as a concrete Vertex signal in hasConcreteProviderSelection (mirrors hasCompleteProviderSelection). - client.ts: reuse resolveGeminiVertexAuthMode() instead of an inline auth-mode computation (trims whitespace tokens, validates the mode). - system-check.ts: use getGeminiVertexLocation() so doctor output matches runtime. - openaiShim.ts: pick the first non-blank credential so a whitespace-only OPENAI_API_KEY no longer suppresses a real GITHUB_TOKEN/GH_TOKEN (+regression). Malformed-history hardening: - messages.ts: validate the first content block before reading .type/.text; sanitize non-string tool_result.tool_use_id to mirror the assistant id path. - normalizeMessages malformed test: cover falsy slots and unknown message types. - providerConfig.envDiagnostics test: clear CLAUDE_CODE_USE_GITHUB in Vertex tests. Skipped (documented): - log.ts saved-profile gate: importing isGeminiVertexEffectiveProvider would create a log -> providerProfiles -> config -> log cycle (config.ts imports log.ts). - providerProfiles.ts hasCompleteProviderSelection access-token gate: CodeRabbit asked to require a project, which contradicts the maintainer's explicit P2 request to treat a bare access-token Vertex selection as complete. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): require project for token-only, gate logs, validate region Addresses jatmn's round-7 review (6 findings): - P1 hasCompleteProviderSelection: an access-token / token-only Vertex env (GEMINI_ACCESS_TOKEN or GEMINI_VERTEX_AUTH_MODE=access-token) with no project is unusable — the native client throws "project is required" since only ADC can derive a project. Such a stale shell no longer counts as complete, so it stops suppressing the saved active profile. Reverses the round-5 tests that encoded the opposite (now corrected). - P1 logError gate: use isGeminiVertexEffectiveProvider() so a saved-profile-only Vertex session (no env flag) gets the same no-error-reporting treatment. - P2 buildCurrentProviderSummary: merge the saved Vertex profile env so the /provider summary shows the saved project/model/location in profile-only mode. - P2 detectProvider: honor profile-only Vertex routing via the effective provider/profile logic (mirrors getAnthropicClient) instead of the raw flag. - P2 global-only models: reject gemini-3.5-flash on a regional GEMINI_VERTEX_ LOCATION via isGlobalOnlyVertexModel() rather than building an unusable URL. - P3 isNotEmptyMessage: reject typeless first content blocks ({ foo: 'bar' }) before the non-text branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): tighten ADC completeness, neutral project placeholder, hermetic tests Addresses CodeRabbit review on6236de2(5 findings): - hasCompleteProviderSelection: use the *effective* auth mode (resolveGeminiVertexAuthMode) so a GEMINI_ACCESS_TOKEN alongside a GOOGLE_APPLICATION_CREDENTIALS file (which resolves to access-token and still needs a project) is no longer treated as complete. The credentials file only counts when the effective mode stays ADC. +2 regressions (token+creds, ADC). - StartupScreen / buildCurrentProviderSummary: show a neutral "<ADC-resolved project>" placeholder when an explicit ADC credential is present (the client derives credential.projectId at runtime) instead of the imperative "<set GEMINI_VERTEX_PROJECT>"; keep the actionable hint for access-token/ambient setups. +startup regression. - client.test.ts: add an ADC-only routing regression asserting the generated Vertex URL uses the credential-discovered project (no env/profile project). - Test isolation: snapshot a structuredClone of the global config before mutating it (StartupScreen), and save/restore DISABLE_ERROR_REPORTING + essential-traffic env so the logError gate control case is hermetic in CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(gemini-vertex): centralize ADC-resolves-project check, fix placeholder drift Addresses CodeRabbit re-review on365cc2e(2 findings, same class as the prior raw-vs-effective auth-mode issue): The ADC project placeholder in StartupScreen.detectProvider and buildCurrentProviderSummary checked GOOGLE_APPLICATION_CREDENTIALS directly, so a GEMINI_ACCESS_TOKEN alongside a credentials file (effective mode: access-token, which still needs an explicit project) wrongly showed "<ADC-resolved project>". - Extract geminiVertexAdcWillResolveProject(env) in utils/geminiAuth.ts as the single source of truth: explicit auth_mode=adc, or effective mode resolves to ADC AND a credentials file is present (a bare/ambient flag is not ADC- resolvable). Reused by hasCompleteProviderSelection (replacing the inline copy) and both placeholders, so the gate and the display cannot drift apart. - StartupScreen regression: GEMINI_ACCESS_TOKEN + GOOGLE_APPLICATION_CREDENTIALS with no project keeps the actionable "<set GEMINI_VERTEX_PROJECT>" hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): honor explicit model override and make profile tests order-independent Addresses jatmn's round-7c review (3 findings): - P1 smoke-and-tests isolation: the full suite failed with provider-detection assertions returning "Gemini Vertex" for unrelated providers because providerProfiles.test.ts installs mock.module('./config.js') and bun's mock.restore() does NOT revert mock.module(), so the dead config mock leaked into every later test file in the process. Add an afterAll that re-installs the real config module. StartupScreen.test.ts and log.errorReportingGate.test.ts stop mutating the shared global config and instead spyOn getActiveProviderProfile / isGeminiVertexEffectiveProvider, so their saved-profile-only assertions no longer depend on a possibly-leaked config mock and become order-independent across OSes. - P2 client.ts model precedence: in saved-profile-only Vertex routing an explicit model override (runtime/--model) now wins over the profile's saved model, matching the startup display and the other provider paths; the saved model still wins over ambient GEMINI_VERTEX_MODEL. Env-flag routing keeps an explicit GEMINI_VERTEX_MODEL ahead of a generic model arg. - P2 runtimeMetadata.test.ts: add the no-env-flag case where activeProfileProvider: 'gemini-vertex' alone selects the Vertex catalog limits, protecting the saved-profile-only path this PR relies on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(test): stop ProviderManager providerProfiles mock from leaking across files Root cause of the failing smoke-and-tests full-suite run (jatmn round-7c P1): ProviderManager.test.tsx installs a partial mock.module('../utils/ providerProfiles.js') via mockProviderProfilesModule() and only calls mock.restore() in afterEach. bun's mock.restore() does NOT revert mock.module(), so the partial stub stayed installed for every later test file in the process. The stub omits exports the gemini-vertex routing now depends on (notably isGeminiVertexEffectiveProvider) and returns getActiveProviderProfile() = null, so once ProviderManager ran before the provider-detection suites the leaked stub turned StartupScreen.detectProvider, the logError reporting gate and client.ts Vertex routing order-dependent — the exact "provider detection returns Gemini Vertex / wrong provider" failures jatmn saw in CI (and that pass in focused runs). main stayed green only because its detectProvider didn't yet read those exports. - Capture the real providerProfiles module before any mock runs and spread it into the stub, so exports the tests don't explicitly override keep their real implementations during ProviderManager's own tests. - Add an afterAll that re-installs the real module, mirroring the restore pattern in providerProfiles.test.ts / messages.toolUseIds.test.ts / user.test.ts. Verified: ProviderManager + StartupScreen + log gate + client run sequentially (the CI failure order) now pass 108/0; ProviderManager alone stays 24/0; tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gemini-vertex): pin getActiveProviderProfile in StartupScreen detectProvider specs The full-suite smoke-and-tests run failed because StartupScreen.detectProvider specs depended on shared global-config state to decide Gemini Vertex routing. A prior test file can leave a mock.module('./config.js') installed (bun's mock.restore() does not revert mock.module()), silently turning the saveGlobalConfig()-based setup into a no-op and making detection order-dependent. Spy getActiveProviderProfile() in beforeEach to return "no active profile" by default so a leaked gemini-vertex profile can't make these assertions return "Gemini Vertex"; the profile-only spec overrides it for its single case. afterEach restores the spy. detectProvider's real routing still runs. Verified: ProviderManager + StartupScreen + log gate + client (the CI failure order) pass 108/0; all detectProvider-bearing files + polluter pass 145/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(gemini-vertex): fix provider-profile mock leak with spyOn messages.toolUseIds.test.ts mocked isGeminiVertexEffectiveProvider via mock.module(), then "restored" by re-spreading the `import * as` namespace. Because that namespace is a live binding, after the mock it already reflects the mocked exports, so the restore re-installs the mock instead of reverting it. Bun's mock.module() is also not undone by mock.restore(), so the mocked isGeminiVertexEffectiveProvider (pinned true) leaked into every later test file in the full suite. When that leak reaches StartupScreen.test.ts, detectProvider() reads the leaked isGeminiVertexEffectiveProvider() === true and labels every provider as "Gemini Vertex", which is the source of the smoke-and-tests failures (30 detectProvider specs + the client/log-gate Vertex specs) seen only in CI's Linux file-discovery order. Switch to spyOn(...).mockReturnValue() restored via mockRestore() in afterEach — the same cross-module-binding-safe pattern StartupScreen.test.ts uses for getActiveProviderProfile. spyOn is properly restorable and never calls mock.module, so the file can no longer leak the provider-profile module. Verified: the leak reproduces deterministically on bun 1.3.9 and 1.3.13; the spyOn version restores cleanly on both. * test: stop mock.module restore leaks via static real-module snapshots Two more test files reinstalled their real modules through an `import * as` namespace, which is a live binding: once mock.module() has run, the namespace already reflects the mocked exports, so "restoring" by returning that namespace re-installs the stub. bun's mock.restore() does not revert mock.module() either, so the stub leaks into every later file in the full suite (only manifesting in CI's Linux file-discovery order). - ProviderManager.test.tsx: the leaked partial providerProfiles stub left getActiveProviderProfile() returning null, so client routing could not see a saved Gemini Vertex profile and fell back to the generic OPENAI_MODEL — failing "Gemini Vertex provider uses GEMINI_VERTEX_MODEL instead of generic OPENAI_MODEL". - mcpValidation.test.ts: the leaked log.js stub ({ logError: () => {} }) made logError a no-op, so the logError error-reporting gate recorded nothing and the control case ("records errors when not routing to a cloud provider") failed. Capture a frozen { ...realModule } snapshot at import time, before any mock.module() mutates the live binding, and restore from that snapshot. Verified with isolated repros that the live-binding restore leaks on bun 1.3.9/1.3.13 and the snapshot restore reverts cleanly; both files' own suites still pass. * test(model): reinstall real providerProfiles in afterAll to stop mock leak model.test.tsx mock.module()'d providerProfiles.js with a partial stub (getActiveProviderProfile -> undefined) and only called mock.restore() in afterEach. Since bun's mock.restore() does not revert mock.module(), the stub leaked into later files in the full suite; client.test.ts then could not resolve the saved Gemini Vertex profile and fell back to the generic OPENAI_MODEL, failing "Gemini Vertex provider uses GEMINI_VERTEX_MODEL instead of generic OPENAI_MODEL" (the last remaining smoke-and-tests failure after the earlier ProviderManager/mcpValidation fixes). Capture a frozen { ...realProviderProfilesModule } snapshot at import time and reinstall it via mock.module() in a new afterAll, matching the pattern now used in ProviderManager.test.tsx. model.test.tsx's own 37 tests still pass. * test(client): isolate GEMINI_VERTEX_AUTH_MODE across Gemini Vertex specs The "ADC-only Vertex routing" test set process.env.GEMINI_VERTEX_AUTH_MODE='adc' but the suite's beforeEach/afterEach never cleared or restored it (only GEMINI_AUTH_MODE was managed). The very next test, "Gemini Vertex provider uses GEMINI_VERTEX_MODEL instead of generic OPENAI_MODEL", then inherited GEMINI_VERTEX_AUTH_MODE='adc'. resolveGeminiVertexAuthMode() treats an explicit 'adc' as authoritative over a present GEMINI_ACCESS_TOKEN, so resolveGeminiCredential took the ADC path, found no ADC credentials, and threw "Gemini Vertex authentication requires GEMINI_ACCESS_TOKEN or Google ADC credentials" — the last remaining smoke-and-tests failure on CI (on Windows the same leak instead hangs the ADC credential lookup until the 5s test timeout). Capture, clear (beforeEach) and restore (afterEach) GEMINI_VERTEX_AUTH_MODE the same way GEMINI_AUTH_MODE is already handled, so each spec starts from a clean auth mode. Verified: the model spec now passes even when the ADC spec runs immediately before it. * test(client): assert explicit model overrides saved Vertex profile model Covers the saved-profile-only Gemini Vertex precedence contract at the client boundary: with CLAUDE_CODE_USE_GEMINI_VERTEX unset and an active gemini-vertex profile, an explicit model passed to getAnthropicClient() must drive the native Vertex request instead of the profile's saved model (addresses CodeRabbit's requested regression). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(status): render Gemini Vertex provider route in /status getAPIProvider() can return 'gemini-vertex' (env flag or saved-profile routing), but /status had no API_PROVIDER_LABELS entry or render branch, so it showed an 'API provider' row with an undefined value and no endpoint/model. Add the label and a Vertex branch mirroring the /provider summary: project/location endpoint, model, and credential (access token vs Google ADC). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): stop saved Vertex profile hijacking an explicit OpenAI flag resolveActiveRouteIdFromEnv's saved-profile guard already rejects a gemini-vertex profile when a conflicting provider flag is defined, but the two generic profile-route fallbacks below it re-resolved the profile provider without re-checking that conflict. With CLAUDE_CODE_USE_OPENAI set, resolveProfileRoute('gemini-vertex') returned 'gemini-vertex', so an explicit OpenAI flag (truthy) yielded 'gemini-vertex' instead of 'openai', and a defined-but-falsey flag (=0) yielded 'gemini-vertex' instead of falling back to 'anthropic'. gemini-vertex is a native (non-OpenAI-compatible) route reached only via its explicit flag or the guarded saved-profile branch, so exclude it from both generic fallbacks. Mirrors shouldRouteToGeminiVertexFromProfile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gemini-vertex): preserve routing and privacy on current main * fix(gemini-vertex): address review routing and credential findings * fix(gemini-vertex): preserve routing and request contracts * fix(gemini-vertex): restore saved configuration and routing precedence Restore matching active startup settings before marking plural profiles applied, reuse saved Vertex bearers, and honor complete custom Anthropic selection. Align native model and source-launch precedence, normalize doctor location, and restore diagnostics test environment state with regression coverage. * fix(gemini-vertex): preserve credential identity and routing boundaries * fix: isolate Vertex credentials when switching to keyless profiles * fix(gemini-vertex): reject global-only models on regional locations before the first request Startup validation and doctor used a narrower Vertex readiness check than the native client, so a global-only model with a regional location looked ready and then failed on the first call. Share one location rule across the client, validator, and doctor. * fix(gemini-vertex): complete model fallbacks and redact shared diagnostics * test(gemini-vertex): isolate default-model regression environment * test(provider): remove duplicate environment snapshot import * fix(gemini-vertex): preserve profile identity and native payloads Keep valid project IDs and ADC locators during native profile shaping, honor prospective user-profile ADC files, and reject implicit workspace filename adoption before credential validation. Preserve image/PDF bytes at native wire conversion and resolve installed diagnostic reports from native fields. * fix(gemini-vertex): validate native URL components and profile provenance Reject endpoint and resource-path injection at native request and validation boundaries while preserving project numbers and domain-scoped identifiers. Keep the loaded profile's source through startup so concurrent user-profile writes cannot admit a workspace ADC filename. * fix(gemini-vertex): align validation and saved profile switching * fix(gemini-vertex): restore original env-only routing scope * fix(gemini-vertex): preserve ambient ADC selection and tool identities --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: jatmn <the@jat.mn> Co-authored-by: jatmn <12479882+jatmn@users.noreply.github.com>
OpenClaude is an open-source coding-agent CLI for cloud and local model providers.
Use OpenAI-compatible APIs, Gemini, GitHub Models, Codex OAuth, Codex, Ollama, Atomic Chat, and other supported backends while keeping one terminal-first workflow: prompts, tools, agents, MCP, slash commands, and streaming output.
OpenClaude is also mirrored to Twigpine: gitlawb.com/node/repos/z6MkqDnb/openclaude
Quick Start | Setup Guides | Providers | Development | VS Code Extension | Partners | Community
Partners
|
|
|
|
|
| Twigpine | Bankr.bot | Atomic Chat | Xiaomi MiMo | Atlas Cloud |
|
|
|
|
|
|
| AI/ML API | Novita AI | ApiSmart | Concentrate | Exa |
|
|
||||
| GMI Cloud |
Why OpenClaude
- One CLI across cloud APIs and local model backends — no per-provider tooling
- Guided provider setup and saved profiles with
/provider - Coding-agent workflows in one place: bash, file tools, grep, glob, agents, tasks, MCP, and web tools
- A bundled VS Code extension for launch integration and theme support
- A pixel-art hero companion who fires an arrow every time you press Enter (really — see Meet your buddy)
Quick Start
Install
OpenClaude requires Node.js >=24.22.1 for npm installs and runtime (Node 26 recommended/default). Bun is
only needed for source builds and local development.
npm install -g @twigpine/openclaude@latest
Upgrading from @gitlawb/openclaude? The package moved to the @twigpine
scope and the old one is no longer updated. Both packages provide the
openclaude command, so npm refuses to install one over the other — remove
the old one first:
npm uninstall -g @gitlawb/openclaude
npm install -g @twigpine/openclaude@latest
Your settings and sessions (~/.openclaude and ~/.openclaude.json) are not
touched. Don't use --force to install over the old package instead: a later
uninstall of the old one would then remove the openclaude command. If that
happens, uninstall @twigpine/openclaude and install it again.
If you're on Arch Linux, you can install OpenClaude from the community-maintained AUR package:
paru -S openclaude
If the install later reports ripgrep not found, install ripgrep system-wide and confirm rg --version works in the same terminal before starting OpenClaude.
Verify / troubleshoot installed version:
openclaude --version
npm view @twigpine/openclaude dist-tags
npm install -g @twigpine/openclaude@latest
Start
openclaude
Inside OpenClaude:
- run
/providerfor guided provider setup and saved profiles - run
/onboard-githubfor GitHub Models onboarding - run
/searchto choose the web search backend (Exa by default) or add a search API key
Note: OpenClaude does not automatically load project
.envfiles. We recommend using the/providercommand for setup, which saves provider profiles and credentials in.openclaude-profile.json. If you prefer environment variables, export them explicitly or runopenclaude --provider-env-file .envfor provider/setup variables. Export runtime/debug knobs from your shell or launcher.
Resume or fork a conversation
Resume an existing conversation by session ID, or continue the most recent conversation in the current directory:
openclaude --resume <session-id>
openclaude --continue
Add --fork-session to branch the conversation history into a new session ID
instead of reusing the original transcript:
openclaude --resume <session-id> --fork-session
openclaude --continue --fork-session
Forking is conversation branching only. It does not create filesystem isolation, copy your working tree, or create a git worktree branch.
Manage saved conversations
Run /sessions inside OpenClaude to list this project's saved conversations.
Press Enter on one to Resume, Delete (asks for confirmation, defaulting
to Cancel), or go Back to the list. As a shortcut, press d on a row and
then d again to delete it; any other key in between cancels. Deleting removes
the transcript and its sidecar files from disk and cannot be undone. The
session you are currently in is not listed and cannot be deleted.
Background sessions
Run long non-interactive prompts detached from the current terminal:
openclaude --bg "fix failing tests"
openclaude --bg --name auth-refactor "refactor auth middleware"
openclaude ps
openclaude logs auth-refactor
openclaude logs auth-refactor -f
openclaude kill auth-refactor
Background sessions are local child processes. OpenClaude does not start a daemon
or network service, and permission/provider/model/settings flags are passed to
the child process the same way they are for a foreground --print run. Session
metadata and logs are stored under the resolved OpenClaude config directory,
usually ~/.openclaude/bg-sessions/; OPENCLAUDE_CONFIG_DIR can point
OpenClaude somewhere else. CLAUDE_CONFIG_DIR is ignored for OpenClaude
background-session storage. Session names can be reused after older sessions
reach a terminal state; use the session ID to inspect older logs with the same
name. A naturally finished session is recorded as exited when its process
returns zero and failed when it returns nonzero or handles a termination
signal. stale remains the conservative result when the process disappears
without an observed outcome; an explicit successful openclaude kill is
recorded as killed, and killed takes precedence over a natural exited or
failed outcome for the same process. Terminal outcomes are stored separately
under bg-sessions/terminal/; deleting that directory makes finished sessions
fall back to liveness-derived status. OpenClaude does not infer POSIX signal
names on Windows.
Unobservable force termination, host crashes, and power loss remain stale on
every platform.
openclaude attach <id-or-name> currently reports the matching session and
points to openclaude logs <id> -f; full terminal reattach is not implemented
for local background sessions yet.
OpenClaude config cutover
OpenClaude stores its own config under ~/.openclaude and ~/.openclaude.json
by default. It does not read ~/.claude, project .claude/ directories, or
CLAUDE_CONFIG_DIR; new users can start with an empty OpenClaude config and do
not need Claude Code installed.
If you previously used OpenClaude with .claude paths, migrate intentionally:
copy only the settings, commands, agents, skills, scheduled tasks, or other files
you personally created for OpenClaude into the matching .openclaude location.
Do not blanket-copy .claude, and do not copy Claude Code credentials or auth
files. For provider authentication, prefer running OpenClaude's provider setup
again or exporting provider-specific environment variables.
Fastest OpenAI setup
macOS / Linux:
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=sk-your-key-here
export OPENAI_MODEL=gpt-4o
openclaude
Windows PowerShell:
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_API_KEY="sk-your-key-here"
$env:OPENAI_MODEL="gpt-4o"
openclaude
Fastest local Ollama setup
macOS / Linux:
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:11434/v1
export OPENAI_MODEL=qwen2.5-coder:7b
openclaude
Windows PowerShell:
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_BASE_URL="http://localhost:11434/v1"
$env:OPENAI_MODEL="qwen2.5-coder:7b"
openclaude
For Ollama, OpenClaude uses Ollama's native chat API and requests a 32768-token
context window on each chat request so same-session history is not silently
truncated by Ollama's OpenAI-compatible shim. Set OPENCLAUDE_OLLAMA_NUM_CTX
or OLLAMA_CONTEXT_LENGTH if you need a different request-level context size.
See Advanced Setup for
verification with ollama ps.
Setup Guides
Beginner-friendly guides:
Advanced and source-build guides:
- Advanced Setup
- Smart Auto-Routing
- Agent Routing and Step Limits
- Headless gRPC Server
- Repo Map (codebase intelligence)
- Skills
- Android Install
Supported Providers
| Provider | Setup Path | Notes |
|---|---|---|
| OpenAI-compatible | /provider or env vars |
Works with OpenAI, OpenRouter, DeepSeek, Groq, Mistral, LM Studio, and other compatible /v1 servers |
| Z.AI GLM Coding Plan | /provider or OpenAI-compatible env vars |
Uses OPENAI_API_KEY at https://api.z.ai/api/coding/paas/v4, defaults to glm-5.2, and offers the vision-capable glm-5.3-flash option |
| AI/ML API | /provider or AIMLAPI_API_KEY (setup guide) |
Uses https://api.aimlapi.com/v1, auto-detects the OpenAI-compatible route from AIMLAPI_API_KEY, sends OpenClaude attribution headers, and discovers chat-capable models from the public /models catalog |
| Merge Gateway | /provider or MERGE_GATEWAY_API_KEY (setup guide) |
Multi-provider model router at https://api-gateway.merge.dev/v1/openai; supports Chat Completions, Responses, authenticated model discovery, and routing policies |
| Concentrate | /provider or CONCENTRATE_API_KEY |
Unified OpenAI-compatible gateway at https://api.concentrate.ai/v1; defaults to deepseek-v4-flash and auto-discovers the chat model catalog |
| LLMTR | /provider or OpenAI-compatible env vars |
Multi-model gateway at https://llmtr.com/v1; /provider and --provider llmtr default to deepseek/deepseek-v4-flash, while raw env setup must set OPENAI_BASE_URL=https://llmtr.com/v1 and OPENAI_MODEL; accepts LLMTR_API_KEY or OPENAI_API_KEY after the route is selected and discovers tool-capable Chat Completions models from the public catalog |
| Command Code | /provider or OpenAI-compatible env vars |
Hybrid OpenAI-compatible gateway at https://api.commandcode.ai/provider/v1; /provider and --provider commandcode default to deepseek/deepseek-v4-flash, while raw env setup must set OPENAI_BASE_URL=https://api.commandcode.ai/provider/v1 and OPENAI_MODEL; requires CMD_API_KEY, COMMANDCODE_API_KEY, or the official COMMAND_CODE_API_KEY after the route is selected and discovers Chat Completions models from the public catalog. Claude models are unsupported by this Chat Completions integration |
| ApiSmart | /provider or APISMART_API_KEY |
Uses https://gw.apismart.ai/v1, defaults to DEEPSEEK_V4_FLASH, and supports optional APISMART_MODEL plus authenticated model discovery |
| API Route | /provider or API_ROUTE_API_KEY |
Uses https://global.api-route.com/v1, defaults to claude-sonnet-4-6, and supports optional API_ROUTE_MODEL plus authenticated model discovery |
| GMI Cloud | /provider, --provider gmi-cloud, or env vars |
Uses GMI_API_KEY at https://api.gmi-serving.com/v1 and defaults to Qwen/Qwen3.8-Max; --provider gmi-cloud also reads an optional GMI_MODEL, and the model list comes from authenticated discovery |
| Hicap | /provider or OpenAI-compatible env vars |
Uses api-key auth, discovers models from unauthenticated /models, and supports Responses mode for gpt- models |
| Fireworks AI | /provider or env vars |
First-class provider with 276 curated models (DeepSeek, Qwen, Llama, Gemma, and more); uses FIREWORKS_API_KEY |
| LongCat | /provider or env vars |
Meituan LongCat OpenAI-compatible API at https://api.longcat.chat/openai/v1; uses LONGCAT_API_KEY and defaults to LongCat-2.0 |
| ClinePass | /provider or env vars |
AI model gateway with usage limits (5hr, weekly, monthly); uses CLINE_API_KEY at https://api.cline.bot/api/v1 |
| Gemini | /provider or env vars |
Supports API key only |
| Gemini on Vertex AI | --provider gemini-vertex or env vars |
Native Gemini requests with Google ADC or an access token; setup |
| GitHub Models | /onboard-github |
Interactive onboarding with saved credentials |
| Codex OAuth | /provider |
Opens ChatGPT sign-in in your browser and stores Codex credentials securely |
| Codex | /provider |
Uses existing Codex CLI auth, OpenClaude secure storage, or env credentials |
| Twigpine Opengateway | Startup default, /provider, or env vars |
Smart gateway at https://opengateway.gitlawb.com/v1; requires an API key from https://gitlawb.com/opengateway/keys and routes Xiaomi MiMo and other partner models by OPENAI_MODEL |
| OpenCode Zen | /provider or env vars |
Pay-as-you-go AI gateway; uses OPENCODE_API_KEY via https://opencode.ai/zen/v1; shared key with OpenCode Go |
| OpenCode Go | /provider or env vars |
$10/mo subscription for open models (13 models); uses OPENCODE_API_KEY via https://opencode.ai/zen/go/v1; shared key with OpenCode Zen |
| Xiaomi MiMo | /provider or env vars |
OpenAI-compatible API at https://mimo.mi.com; uses MIMO_API_KEY and defaults to mimo-v2.5-pro |
| NEAR AI | /provider or env vars |
Unified gateway (Claude, GPT, Gemini + TEE open models); uses NEARAI_API_KEY at https://cloud-api.near.ai/v1 |
| Cloudflare Workers AI | /provider or env vars |
OpenAI-compatible API at https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1; uses CLOUDFLARE_API_TOKEN. Replace <ACCOUNT_ID> with your Cloudflare account id. |
| Ollama | /provider or env vars |
Local inference with no API key |
| Atomic Chat | /provider, env vars, or bun run dev:atomic-chat |
Local Model Provider; auto-detects loaded models |
| Bedrock / Vertex / Foundry | env vars | Anthropic-family cloud routes; Vertex is for Claude on Vertex AI, not arbitrary Model Garden models |
What Works
- Tool-driven coding workflows: Bash, file read/write/edit, grep, glob, agents, tasks, MCP, and slash commands
- Streaming responses: Real-time token output and tool progress
- Tool calling: Multi-step tool loops with model calls, tool execution, and follow-up responses
- Images: URL and base64 image inputs for providers that support vision. Paste from the clipboard with Ctrl+V (Alt+V on Windows). 1080p/4K screenshots still attach. If processing fails and the image is over the 5MB payload limit or the 8000px API edge, the prompt shows that error instead of "No image found". For Anthropic requests containing more than 20 images (including retained conversation history and tool results), local images must fit within 2000×2000 pixels. On Amazon Bedrock and Google Cloud Vertex AI, document blocks also count toward that 20-block threshold. OpenClaude attempts to resize them before sending; if processing cannot produce a compliant image, it reports an actionable error rather than silently dropping attachments. Resize the affected images or start a new conversation with fewer images (or documents on those partners).
- Provider profiles: Guided setup plus saved user-level provider profile support
- Local and remote model backends: Cloud APIs, local servers, and Apple Silicon local inference
- Codebase intelligence (repo map): Structural map of the repository ranked by PageRank importance, auto-injected into context when the
REPO_MAPflag is enabled or theREPO_MAPenvironment variable is set. Inspect with/repomap(2048-token default). See docs/repo-map.md for details. - A companion with signature moves: A truecolor pixel-art hero who lives beside your prompt and reacts when you work. See below.
Meet Your Buddy
Run /buddy to hatch a companion — a truecolor pixel-art hero who stands
beside your prompt, idles, blinks, and fires their signature move every time
you submit a message:
/buddy hatch (first run) or pet your companion
/buddy set robinhood the green archer — arrow shot on every Enter
/buddy set kaio gold-haired warrior — charges a full-width energy wave
/buddy set strawhat stretchy punch that snaps back
/buddy set merlin twinkling sparkle stream
/buddy set kage spinning shuriken
/buddy set ember dragon fire with a real heat gradient
/buddy set corsair cannonball with smoke trail
/buddy name Robin rename your companion
/buddy set random back to your rolled hero
Companions respect prefersReducedMotion, degrade gracefully to line art in
low-color terminals, and can be silenced with /buddy mute. Requires a
terminal at least 100 columns wide for the full sprite.
Provider Notes
OpenClaude supports multiple providers, but behavior is not identical across all of them.
- Anthropic-specific features may not exist on other providers
- Tool quality depends heavily on the selected model
- Smaller local models can struggle with long multi-step tool flows
- Some providers impose lower output caps than the CLI defaults, and OpenClaude adapts where possible
- AI/ML API uses the OpenAI-compatible route, defaults to
gpt-4o, and only surfaces chat-capable models from its public catalog - Twigpine Opengateway is the fresh-install startup default and requires an API key from https://gitlawb.com/opengateway/keys. It uses one OpenAI-compatible base URL; switch between
mimo-*andgoogle/gemini-3.1-flash-lite-previewwith/model, and do not pin the base URL to/v1/xiaomi-mimo. - Z.AI GLM Coding Plan uses
https://api.z.ai/api/coding/paas/v4withglm-5.2by default. On that direct route, GLM-5.3-Flash is selectable asglm-5.3-flashand accepts image input. Its OpenClaude effort choices arelow,high, andxhigh, withxhighrequesting Z.AImax; GLM-5.3 and the existing GLM-5.2 query controls remain supported. These capabilities are not claimed for gateways that happen to carry the same model name. - Xiaomi MiMo uses
api-keyheader auth on the direct OpenAI-compatible route and currently does not support/usagereporting in OpenClaude - GitHub Copilot serializes sub-agent execution by default to reduce Premium Request consumption — see Agent Routing and Step Limits for tuning
For best results, use models with strong tool/function calling support.
Agents
Route different agents to different models (cost optimization, splitting work
by model strength), cap sub-agent tool steps with maxSteps, and tune GitHub
Copilot sub-agent behavior. Configured via settings, agent frontmatter, and
environment variables:
- per-agent provider/model overrides via
agentModels+agentRoutingin~/.openclaude/settings.json - model-only routes that reuse your current provider's credentials
- built-in agents (
ExploreandPlan[feature-gated],verification[feature-gated: requiresVERIFICATION_AGENT+tengu_hive_evidence],code-reviewer[requires diff inline]) routable by type name
See Agent Routing and Step Limits for the full guide.
Web Search and Fetch
Exa is the default web search backend. With no setup, WebSearch on non-Anthropic models uses Exa's keyless free tier, which has per-second and daily limits. Run /search to pick a backend, add an API key, or test search:
/search # pick a backend
/search key exa # add an Exa API key (hidden input) for higher limits
/search remove-key # remove a saved Exa key
/search status # show which backend handles searches
/search test # run a test search
/search anysearch # select AnySearch (works without a key)
/search key anysearch # optionally add an AnySearch API key in the hidden input
Other backends in the auto chain that you configure with a key (Tavily, Brave, Firecrawl, …) run before the Exa free tier, and DuckDuckGo is the last-resort fallback. When the active provider is Ollama, OpenClaude uses the signed-in local Ollama Web Search endpoint ahead of the free tier. Set OLLAMA_API_KEY to enable the hosted Ollama endpoint as a fallback. Set EXA_FREE_TIER=0 if searches should never go to Exa without a key. See Web Search Providers for every backend and option.
AnySearch is available as an explicit backend with /search anysearch or WEB_SEARCH_PROVIDER=anysearch. ANYSEARCH_API_KEY is optional; anonymous access has lower service limits. Set ANYSEARCH_MAX_RESULTS to an integer from 1 to 10 (default 10) to control the requested result count. AnySearch does not change the auto search order.
Note: DuckDuckGo fallback works by scraping search results and may be rate-limited, blocked, or subject to DuckDuckGo's Terms of Service.
For Anthropic-native backends and Codex responses, OpenClaude keeps the native provider web search behavior unless you pick a specific backend with /search (for example /search exa).
To select Ollama search explicitly, use WEB_SEARCH_PROVIDER=ollama. A local Ollama route uses its configured OPENAI_BASE_URL or OLLAMA_BASE_URL; hosted search sends OLLAMA_API_KEY only to https://ollama.com/api/web_search.
WebFetch works, but its basic HTTP plus HTML-to-markdown path can still fail on JavaScript-rendered sites or sites that block plain HTTP requests.
Set a Firecrawl API key if you want Firecrawl-powered search/fetch behavior:
export FIRECRAWL_API_KEY=your-key-here
With Firecrawl enabled:
WebSearchcan use Firecrawl's search API while the Exa free tier remains the default free path for non-Claude modelsWebFetchuses Firecrawl's scrape endpoint instead of raw HTTP, handling JS-rendered pages correctly
Free tier at firecrawl.dev includes 500 credits. The key is optional.
Headless gRPC Server
OpenClaude can run as a headless gRPC service with bidirectional streaming —
integrate its agentic capabilities into other applications, CI/CD pipelines,
or custom UIs. Start it with npm run dev:grpc; a test CLI client ships with
the repo. See Headless gRPC Server for configuration
and client generation from src/proto/openclaude.proto.
Development
Use Node.js >=24.22.1 (Node 26 recommended/default) and Bun 1.4.4 for source builds.
bun install
bun run build
node dist/cli.mjs
Day-to-day commands:
bun run dev— build and launch from sourcebun test— full unit suite (Bun's built-in runner)bun test path/to/file.test.ts— focused runs for the areas you touchbun run test:coverage— coverage tocoverage/lcov.infoplus a visual report atcoverage/index.html(bun run test:coverage:uirebuilds just the UI)bun run smoke— smoke checksbun run doctor:runtime,bun run verify:privacy; for PR intent scanning, use the fresh-upstream, explicit-ref workflow in the local pre-push validation contract
Focused suites: bun run test:provider, bun run test:provider-recommendation.
To benchmark the launcher module compile cache, build the CLI and run:
bun run build
bun run benchmark:startup
The benchmark and built OpenClaude launcher require Node >=24.22.1;
Node 26 is recommended/default.
The benchmark defaults to 30 separate-process warm runs and 10 isolated
empty-cache runs. It reports the median, IQR, MAD, first cache-populating run,
first warm-up, Node/OS/CPU details, bundle size, and commit. Direct bundle
timings are included only as a secondary diagnostic; the full launcher result
is the decision signal. Use
bun run benchmark:startup -- --warm-runs 40 --cold-runs 10 to request a
larger sample set. The benchmark records results without enforcing a timing
threshold in CI.
OpenClaude leaves Node's standard compile-cache controls authoritative. Set
NODE_DISABLE_COMPILE_CACHE=1 to disable the optimization, including for V8
coverage runs that require uncached compilation.
Before opening or updating a PR, run the authoritative local pre-push validation contract. The commands below are useful for narrow iteration, but they do not replace that required preflight:
bun run buildbun run smokebun run test:coveragewhen your change affects shared runtime or provider logic- focused
bun test ...runs for the files and flows you changed
Repository Structure
src/- core CLI/runtimescripts/- build, verification, and maintenance scriptsdocs/- setup, contributor, and project documentationvscode-extension/openclaude-vscode/- VS Code extension.github/- repo automation, templates, and CI configurationbin/- CLI launcher entrypoints
VS Code Extension
The repo includes a VS Code extension in vscode-extension/openclaude-vscode for OpenClaude launch integration, provider-aware Control Center, in-editor chat, theme support, and optional Microsoft Foundry / Azure OpenAI configuration (endpoint, API version, deployment, API key via Secret Storage) injected into launched terminals. See that folder's README.
Security
If you believe you found a security issue, see SECURITY.md.
Community
- Use GitHub Discussions for Q&A, ideas, and community conversation
- Use GitHub Issues for confirmed bugs and actionable feature work
- Join the Discord to chat with the community in real time
- Follow @twigpine on X for updates and announcements
Contributing
Contributions are welcome. For larger changes, open an issue first so the scope is clear before implementation. See Development for the build, test, and pre-PR validation commands.
Disclaimer
OpenClaude is an independent community project and is not affiliated with, endorsed by, or sponsored by Anthropic.
OpenClaude originated from the Claude Code codebase and has since been substantially modified to support multiple providers and open use. "Claude" and "Claude Code" are trademarks of Anthropic PBC. See LICENSE for details.
License
MIT for OpenClaude contributors' modifications; the derived Claude Code remains Anthropic's. See more.
