diff --git a/src/extension.ts b/src/extension.ts index 6ab5a3dcbb6..e6ac5f137bf 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -46,6 +46,7 @@ import { registerGhostProvider } from "./services/ghost" // kilocode_change import { registerMainThreadForwardingLogger } from "./utils/fowardingLogger" // kilocode_change import { getKiloCodeWrapperProperties } from "./core/kilocode/wrapper" // kilocode_change import { registerAutocompleteProvider } from "./services/autocomplete" // kilocode_change +import { checkAnthropicApiKeyConflict } from "./utils/anthropicApiKeyWarning" /** * Built using https://github.com/microsoft/vscode-webview-ui-toolkit @@ -280,6 +281,13 @@ export async function activate(context: vscode.ExtensionContext) { ) } + // Check for env var conflicts that might confuse users + try { + checkAnthropicApiKeyConflict() + } catch (error) { + outputChannel.appendLine(`Failed to check API key conflicts: ${error}`) + } + registerCommands({ context, outputChannel, provider }) /** diff --git a/src/utils/__tests__/anthropicApiKeyWarning.spec.ts b/src/utils/__tests__/anthropicApiKeyWarning.spec.ts new file mode 100644 index 00000000000..59c9bf50823 --- /dev/null +++ b/src/utils/__tests__/anthropicApiKeyWarning.spec.ts @@ -0,0 +1,100 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest" +import * as vscode from "vscode" + +import { checkAnthropicApiKeyConflict } from "../anthropicApiKeyWarning" + +// Mock VS Code API +vi.mock("vscode", () => ({ + workspace: { + getConfiguration: vi.fn(), + }, + window: { + showWarningMessage: vi.fn(), + }, + env: { + openExternal: vi.fn(), + }, + Uri: { + parse: vi.fn(), + }, +})) + +describe("anthropicApiKeyWarning", () => { + const mockGetConfiguration = vi.mocked(vscode.workspace.getConfiguration) + const mockShowWarningMessage = vi.mocked(vscode.window.showWarningMessage) + + beforeEach(() => { + vi.clearAllMocks() + }) + + afterEach(() => { + // Clean up environment variable + delete process.env.ANTHROPIC_API_KEY + }) + + describe("checkAnthropicApiKeyConflict", () => { + it("should not show warning when ANTHROPIC_API_KEY is not set", () => { + // Ensure environment variable is not set + delete process.env.ANTHROPIC_API_KEY + + checkAnthropicApiKeyConflict() + + expect(mockGetConfiguration).not.toHaveBeenCalled() + expect(mockShowWarningMessage).not.toHaveBeenCalled() + }) + + it("should not show warning when ANTHROPIC_API_KEY is set but provider is not claude-code", () => { + // Set environment variable + process.env.ANTHROPIC_API_KEY = "test-key" + + // Mock configuration to return different provider + const mockConfig = { + get: vi.fn().mockReturnValue("anthropic"), + } + mockGetConfiguration.mockReturnValue(mockConfig as any) + + checkAnthropicApiKeyConflict() + + expect(mockGetConfiguration).toHaveBeenCalledWith("kilo-code") + expect(mockConfig.get).toHaveBeenCalledWith("apiProvider") + expect(mockShowWarningMessage).not.toHaveBeenCalled() + }) + + it("should show warning when ANTHROPIC_API_KEY is set and provider is claude-code", () => { + process.env.ANTHROPIC_API_KEY = "test-key" + + const mockConfig = { + get: vi.fn().mockReturnValue("claude-code"), + } + mockGetConfiguration.mockReturnValue(mockConfig as any) + mockShowWarningMessage.mockResolvedValue(undefined) + + checkAnthropicApiKeyConflict() + + expect(mockGetConfiguration).toHaveBeenCalledWith("kilo-code") + expect(mockConfig.get).toHaveBeenCalledWith("apiProvider") + expect(mockShowWarningMessage).toHaveBeenCalledWith( + "An ANTHROPIC_API_KEY environment variable was detected. This may conflict with your subscription login and cause errors. Please unset it to ensure your Claude Max/Pro plan is used.", + "More Info", + "Got it", + ) + }) + + it("should handle undefined apiProvider gracefully", () => { + // Set environment variable + process.env.ANTHROPIC_API_KEY = "test-key" + + // Mock configuration to return undefined provider + const mockConfig = { + get: vi.fn().mockReturnValue(undefined), + } + mockGetConfiguration.mockReturnValue(mockConfig as any) + + checkAnthropicApiKeyConflict() + + expect(mockGetConfiguration).toHaveBeenCalledWith("kilo-code") + expect(mockConfig.get).toHaveBeenCalledWith("apiProvider") + expect(mockShowWarningMessage).not.toHaveBeenCalled() + }) + }) +}) diff --git a/src/utils/anthropicApiKeyWarning.ts b/src/utils/anthropicApiKeyWarning.ts new file mode 100644 index 00000000000..1c77a13986b --- /dev/null +++ b/src/utils/anthropicApiKeyWarning.ts @@ -0,0 +1,32 @@ +import * as vscode from "vscode" + +/** + * Check for potential ANTHROPIC_API_KEY conflicts with Claude Code provider. + * Shows a warning if the environment variable is set while using Claude Code provider. + * Fixes issue #2026 - users were getting confused when their env var conflicted with subscription. + */ +export function checkAnthropicApiKeyConflict(): void { + const anthropicKey = process.env.ANTHROPIC_API_KEY + if (!anthropicKey) { + return + } + + const config = vscode.workspace.getConfiguration("kilo-code") + const provider = config.get("apiProvider") + + if (provider === "claude-code") { + showAnthropicApiKeyWarning() + } +} + +function showAnthropicApiKeyWarning(): void { + const msg = + "An ANTHROPIC_API_KEY environment variable was detected. This may conflict with your subscription login and cause errors. Please unset it to ensure your Claude Max/Pro plan is used." + + vscode.window.showWarningMessage(msg, "More Info", "Got it").then((choice) => { + if (choice === "More Info") { + vscode.env.openExternal(vscode.Uri.parse("https://github.com/Kilo-Org/kilocode/issues/2026")) + } + // User dismissed or clicked "Got it" - nothing else to do + }) +}