diff --git a/packages/types/src/__tests__/claude-code.spec.ts b/packages/types/src/__tests__/claude-code.spec.ts
new file mode 100644
index 0000000000..e07d6fae79
--- /dev/null
+++ b/packages/types/src/__tests__/claude-code.spec.ts
@@ -0,0 +1,55 @@
+import { describe, it, expect } from "vitest"
+
+import { claudeCodeModels, claudeCodeDefaultModelId } from "../providers/claude-code.js"
+import { providerSettingsSchema, providerSettingsSchemaDiscriminated } from "../provider-settings.js"
+
+describe("claudeCodeModels", () => {
+	it("default model ID exists in the models record", () => {
+		expect(claudeCodeModels).toHaveProperty(claudeCodeDefaultModelId)
+	})
+
+	it("all models have inputPrice: 0 and outputPrice: 0", () => {
+		for (const [id, model] of Object.entries(claudeCodeModels)) {
+			expect(model.inputPrice, `${id}: inputPrice must be 0`).toBe(0)
+			expect(model.outputPrice, `${id}: outputPrice must be 0`).toBe(0)
+		}
+	})
+})
+
+describe("claudeCodeSchema (via providerSettingsSchema)", () => {
+	it("accepts an empty object", () => {
+		const result = providerSettingsSchema.safeParse({})
+		expect(result.success).toBe(true)
+	})
+
+	it("accepts claudeCodeCliPath as an optional string", () => {
+		const result = providerSettingsSchema.safeParse({ claudeCodeCliPath: "/usr/local/bin/claude" })
+		expect(result.success).toBe(true)
+		if (result.success) {
+			expect(result.data.claudeCodeCliPath).toBe("/usr/local/bin/claude")
+		}
+	})
+
+	it("rejects unknown keys in strict mode", () => {
+		const result = providerSettingsSchema.strict().safeParse({ claudeCodeApiKey: "should-not-exist" })
+		expect(result.success).toBe(false)
+	})
+})
+
+describe("claudeCodeSchema (via providerSettingsSchemaDiscriminated)", () => {
+	it("accepts apiProvider: 'claude-code' with no other fields", () => {
+		const result = providerSettingsSchemaDiscriminated.safeParse({ apiProvider: "claude-code" })
+		expect(result.success).toBe(true)
+	})
+
+	it("accepts apiProvider: 'claude-code' with claudeCodeCliPath", () => {
+		const result = providerSettingsSchemaDiscriminated.safeParse({
+			apiProvider: "claude-code",
+			claudeCodeCliPath: "/usr/local/bin/claude",
+		})
+		expect(result.success).toBe(true)
+		if (result.success) {
+			expect(result.data.apiProvider).toBe("claude-code")
+		}
+	})
+})
diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts
index 26c4dee7e1..a399f20142 100644
--- a/packages/types/src/provider-settings.ts
+++ b/packages/types/src/provider-settings.ts
@@ -6,6 +6,7 @@ import {
 	anthropicModels,
 	basetenModels,
 	bedrockModels,
+	claudeCodeModels,
 	deepSeekModels,
 	fireworksModels,
 	geminiModels,
@@ -117,6 +118,7 @@ export const providerNames = [
 	"anthropic",
 	"bedrock",
 	"baseten",
+	"claude-code",
 	"deepseek",
 	"fireworks",
 	"gemini",
@@ -301,6 +303,11 @@ const openAiCodexSchema = apiModelIdProviderModelSchema.extend({
 	// No additional settings needed - uses OAuth authentication
 })
 
+const claudeCodeSchema = apiModelIdProviderModelSchema.extend({
+	// No API key - authentication is handled externally via `claude login`.
+	claudeCodeCliPath: z.string().optional(),
+})
+
 const openAiNativeSchema = apiModelIdProviderModelSchema.extend({
 	openAiNativeApiKey: z.string().optional(),
 	openAiNativeBaseUrl: z.string().optional(),
@@ -433,6 +440,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
 	geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })),
 	geminiCliSchema.merge(z.object({ apiProvider: z.literal("gemini-cli") })),
 	openAiCodexSchema.merge(z.object({ apiProvider: z.literal("openai-codex") })),
+	claudeCodeSchema.merge(z.object({ apiProvider: z.literal("claude-code") })),
 	openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
 	mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
 	deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
@@ -469,6 +477,7 @@ export const providerSettingsSchema = z.object({
 	...geminiSchema.shape,
 	...geminiCliSchema.shape,
 	...openAiCodexSchema.shape,
+	...claudeCodeSchema.shape,
 	...openAiNativeSchema.shape,
 	...mistralSchema.shape,
 	...deepSeekSchema.shape,
@@ -545,6 +554,7 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
 	bedrock: "apiModelId",
 	vertex: "apiModelId",
 	"openai-codex": "apiModelId",
+	"claude-code": "apiModelId",
 	"openai-native": "openAiModelId",
 	ollama: "ollamaModelId",
 	lmstudio: "lmStudioModelId",
@@ -668,6 +678,11 @@ export const MODELS_BY_PROVIDER: Record<
 		label: "OpenAI - ChatGPT Plus/Pro",
 		models: Object.keys(openAiCodexModels),
 	},
+	"claude-code": {
+		id: "claude-code",
+		label: "Claude Code (Subscription)",
+		models: Object.keys(claudeCodeModels),
+	},
 	"openai-native": {
 		id: "openai-native",
 		label: "OpenAI",
diff --git a/packages/types/src/providers/claude-code.ts b/packages/types/src/providers/claude-code.ts
new file mode 100644
index 0000000000..9955114723
--- /dev/null
+++ b/packages/types/src/providers/claude-code.ts
@@ -0,0 +1,156 @@
+import type { ModelInfo } from "../model.js"
+
+/**
+ * Claude Code (Subscription) Provider
+ *
+ * This provider shells out to the user's own locally-installed and
+ * already-authenticated `claude` CLI (Pro/Max/Team/Enterprise subscription),
+ * instead of an Anthropic API key. No credentials are read, stored, or
+ * transmitted by this extension.
+ *
+ * Key differences from anthropic:
+ * - Uses the local CLI session instead of API keys
+ * - Subscription-based pricing (no per-token costs)
+ * - Model availability depends on the authenticated subscription
+ *
+ * Pricing below (0 at runtime) mirrors the real per-token list prices from
+ * `anthropic.ts` as of 2026-07-04, for reference only.
+ *
+ * contextWindow/maxTokens are grounded against live `claude --model <id>
+ * --output-format json` output (`modelUsage[id].contextWindow` /
+ * `.maxOutputTokens`) rather than copied from `anthropic.ts`, since the
+ * Agent SDK/CLI path reports different max output caps than the direct API
+ * for the same model name (e.g. 64K here vs. 128K direct for Opus/Sonnet 5/
+ * Fable 5, 32K here vs. 64K direct for Haiku 4.5/Sonnet 4.6).
+ *
+ * `claude-sonnet-4-6[1m]` is a distinct, explicitly-selected model key (not
+ * a flag) that reports contextWindow: 1_000_000 vs. 200_000 for the bare
+ * `claude-sonnet-4-6` id. `claude-opus-4-6[1m]` was tried and did not
+ * resolve to a model (empty modelUsage), so no 1M variant is listed for it.
+ */
+
+export type ClaudeCodeModelId = keyof typeof claudeCodeModels
+
+export const claudeCodeDefaultModelId: ClaudeCodeModelId = "claude-sonnet-5"
+
+/**
+ * Models available through the Claude Code CLI subscription flow.
+ * Costs are 0 as they are covered by the subscription.
+ */
+export const claudeCodeModels = {
+	"claude-fable-5": {
+		maxTokens: 64_000,
+		contextWindow: 1_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $10/$50 per million in/out, $12.50 cache write, $1.00 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		supportsReasoningBinary: true,
+		supportsTemperature: false,
+		description: "Claude Fable 5 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-opus-4-8": {
+		maxTokens: 64_000,
+		contextWindow: 1_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $5/$25 per million in/out, $6.25 cache write, $0.50 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		supportsReasoningBinary: true,
+		supportsTemperature: false,
+		description: "Claude Opus 4.8 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-opus-4-7": {
+		maxTokens: 64_000,
+		contextWindow: 1_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $5/$25 per million in/out, $6.25 cache write, $0.50 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		supportsReasoningBinary: true,
+		supportsTemperature: false,
+		description: "Claude Opus 4.7 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-opus-4-6": {
+		maxTokens: 64_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $5/$25 per million in/out, $6.25 cache write, $0.50 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		description: "Claude Opus 4.6 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-sonnet-5": {
+		maxTokens: 64_000,
+		contextWindow: 1_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $2/$10 per million in/out (introductory, through Aug 31, 2026),
+		// $2.50 cache write, $0.20 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		supportsReasoningBinary: true,
+		supportsTemperature: false,
+		description: "Claude Sonnet 5 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-sonnet-4-6": {
+		maxTokens: 32_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $3/$15 per million in/out, $3.75 cache write, $0.30 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		description: "Claude Sonnet 4.6 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-sonnet-4-6[1m]": {
+		maxTokens: 32_000,
+		contextWindow: 1_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// Same list price as claude-sonnet-4-6; [1m] only changes context window.
+		// Tiered pricing above 200K would apply on the direct Anthropic API path
+		// but subscription usage is $0 either way.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		description: "Claude Sonnet 4.6 (1M context) via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+	"claude-haiku-4-5-20251001": {
+		maxTokens: 32_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		// List price: $1/$5 per million in/out, $1.25 cache write, $0.10 cache read.
+		inputPrice: 0,
+		outputPrice: 0,
+		cacheWritesPrice: 0,
+		cacheReadsPrice: 0,
+		supportsReasoningBudget: true,
+		description: "Claude Haiku 4.5 via Claude Code subscription (Pro/Max/Team/Enterprise).",
+	},
+} as const satisfies Record<string, ModelInfo>
diff --git a/packages/types/src/providers/index.ts b/packages/types/src/providers/index.ts
index f283cb474c..c1fe321420 100644
--- a/packages/types/src/providers/index.ts
+++ b/packages/types/src/providers/index.ts
@@ -1,6 +1,7 @@
 export * from "./anthropic.js"
 export * from "./baseten.js"
 export * from "./bedrock.js"
+export * from "./claude-code.js"
 export * from "./deepseek.js"
 export * from "./fireworks.js"
 export * from "./gemini.js"
@@ -31,6 +32,7 @@ export * from "./zoo-gateway.js"
 import { anthropicDefaultModelId } from "./anthropic.js"
 import { basetenDefaultModelId } from "./baseten.js"
 import { bedrockDefaultModelId } from "./bedrock.js"
+import { claudeCodeDefaultModelId } from "./claude-code.js"
 import { deepSeekDefaultModelId } from "./deepseek.js"
 import { fireworksDefaultModelId } from "./fireworks.js"
 import { geminiDefaultModelId } from "./gemini.js"
@@ -77,6 +79,8 @@ export function getProviderDefaultModelId(
 			return xaiDefaultModelId
 		case "baseten":
 			return basetenDefaultModelId
+		case "claude-code":
+			return claudeCodeDefaultModelId
 		case "bedrock":
 			return bedrockDefaultModelId
 		case "vertex":
diff --git a/src/core/config/__tests__/importExport.spec.ts b/src/core/config/__tests__/importExport.spec.ts
index 9cbb364c92..94e5b9d6c4 100644
--- a/src/core/config/__tests__/importExport.spec.ts
+++ b/src/core/config/__tests__/importExport.spec.ts
@@ -548,7 +548,7 @@ describe("importExport", () => {
 
 		describe("lenient import with invalid providers", () => {
 			it("should sanitize profiles with invalid apiProvider and return warnings", async () => {
-				// Test importing a profile with a removed/invalid provider like "claude-code"
+				// Test importing a profile with a removed/invalid provider like "some-removed-provider"
 				;(vscode.window.showOpenDialog as Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
 
 				const mockFileContent = JSON.stringify({
@@ -561,7 +561,7 @@ describe("importExport", () => {
 								id: "valid-id",
 							},
 							"invalid-profile": {
-								apiProvider: "claude-code", // Invalid/removed provider
+								apiProvider: "some-removed-provider", // Invalid/removed provider
 								apiKey: "some-key",
 								id: "invalid-id",
 							},
@@ -595,7 +595,7 @@ describe("importExport", () => {
 				expect((result as { warnings?: string[] }).warnings).toBeDefined()
 				expect((result as { warnings?: string[] }).warnings!.length).toBeGreaterThan(0)
 				expect((result as { warnings?: string[] }).warnings![0]).toContain("invalid-profile")
-				expect((result as { warnings?: string[] }).warnings![0]).toContain("claude-code")
+				expect((result as { warnings?: string[] }).warnings![0]).toContain("some-removed-provider")
 
 				// The valid profile should be imported
 				expect(mockProviderSettingsManager.import).toHaveBeenCalled()
@@ -853,8 +853,8 @@ describe("importExport", () => {
 								apiKey: "key-2",
 								id: "openai-id",
 							},
-							"old-claude-profile": {
-								apiProvider: "claude-code", // Removed provider
+							"old-removed-profile": {
+								apiProvider: "some-old-removed-provider", // Removed provider
 								apiKey: "key-3",
 								id: "claude-id",
 							},
@@ -891,7 +891,7 @@ describe("importExport", () => {
 				// Should have multiple warnings
 				const warnings = (result as { warnings?: string[] }).warnings!
 				expect(warnings.length).toBe(2) // Two profiles had invalid providers
-				expect(warnings.some((w) => w.includes("old-claude-profile"))).toBe(true)
+				expect(warnings.some((w) => w.includes("old-removed-profile"))).toBe(true)
 				expect(warnings.some((w) => w.includes("another-invalid"))).toBe(true)
 
 				// Valid profiles should be imported correctly
@@ -900,7 +900,7 @@ describe("importExport", () => {
 				expect(importedProfiles.apiConfigs["openai-profile"].apiProvider).toBe("openai")
 
 				// Invalid provider profiles should have apiProvider removed
-				expect(importedProfiles.apiConfigs["old-claude-profile"].apiProvider).toBeUndefined()
+				expect(importedProfiles.apiConfigs["old-removed-profile"].apiProvider).toBeUndefined()
 				expect(importedProfiles.apiConfigs["another-invalid"].apiProvider).toBeUndefined()
 			})
 
diff --git a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts
index 3e314a8941..685f95c574 100644
--- a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts
+++ b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts
@@ -13,6 +13,8 @@ import {
 	openAiModelInfoSaneDefaults,
 	minimaxDefaultModelId,
 	minimaxModels,
+	claudeCodeDefaultModelId,
+	claudeCodeModels,
 	openRouterDefaultModelId,
 	vscodeLlmModels,
 	vscodeLlmDefaultModelId,
@@ -848,6 +850,53 @@ describe("useSelectedModel", () => {
 		})
 	})
 
+	describe("claude-code provider", () => {
+		beforeEach(() => {
+			mockUseRouterModels.mockReturnValue({
+				data: {
+					openrouter: {},
+					requesty: {},
+					litellm: {},
+				},
+				isLoading: false,
+				isError: false,
+			} as any)
+
+			mockUseOpenRouterModelProviders.mockReturnValue({
+				data: {},
+				isLoading: false,
+				isError: false,
+			} as any)
+		})
+
+		it("should return default claude-code model when no custom model is specified", () => {
+			const apiConfiguration: ProviderSettings = {
+				apiProvider: "claude-code",
+			}
+
+			const wrapper = createWrapper()
+			const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
+
+			expect(result.current.provider).toBe("claude-code")
+			expect(result.current.id).toBe(claudeCodeDefaultModelId)
+			expect(result.current.info).toEqual(claudeCodeModels[claudeCodeDefaultModelId])
+		})
+
+		it("should use custom model ID and info when model exists in claudeCodeModels", () => {
+			const apiConfiguration: ProviderSettings = {
+				apiProvider: "claude-code",
+				apiModelId: "claude-opus-4-8",
+			}
+
+			const wrapper = createWrapper()
+			const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
+
+			expect(result.current.provider).toBe("claude-code")
+			expect(result.current.id).toBe("claude-opus-4-8")
+			expect(result.current.info).toEqual(claudeCodeModels["claude-opus-4-8"])
+		})
+	})
+
 	describe("vscode-lm provider", () => {
 		beforeEach(() => {
 			mockUseRouterModels.mockReturnValue({
diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts
index f86619a1d4..8643fcb8fc 100644
--- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts
+++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts
@@ -6,6 +6,7 @@ import {
 	type RouterModels,
 	anthropicModels,
 	bedrockModels,
+	claudeCodeModels,
 	deepSeekModels,
 	moonshotModels,
 	minimaxModels,
@@ -358,6 +359,11 @@ function getSelectedModel({
 			const info = openAiCodexModels[id as keyof typeof openAiCodexModels]
 			return { id, info }
 		}
+		case "claude-code": {
+			const id = apiConfiguration.apiModelId ?? defaultModelId
+			const info = claudeCodeModels[id as keyof typeof claudeCodeModels]
+			return { id, info }
+		}
 		case "vercel-ai-gateway": {
 			const id = getValidatedModelId(
 				apiConfiguration.vercelAiGatewayModelId,
