فهرست منبع

feat: add Claude Code provider for local CLI integration (#4864)

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Daniel <[email protected]>
Co-authored-by: Daniel Riccio <[email protected]>
Co-authored-by: Matt Rubens <[email protected]>
Hannes Rudolph 6 ماه پیش
والد
کامیت
ff9b6b3ef6
49فایلهای تغییر یافته به همراه714 افزوده شده و 52 حذف شده
  1. 7 0
      packages/types/src/provider-settings.ts
  2. 13 0
      packages/types/src/providers/claude-code.ts
  3. 1 0
      packages/types/src/providers/index.ts
  4. 3 0
      src/api/index.ts
  5. 171 0
      src/api/providers/claude-code.ts
  6. 1 0
      src/api/providers/index.ts
  7. 7 1
      src/api/transform/stream.ts
  8. 14 2
      src/i18n/locales/ca/common.json
  9. 14 2
      src/i18n/locales/de/common.json
  10. 8 1
      src/i18n/locales/en/common.json
  11. 14 2
      src/i18n/locales/es/common.json
  12. 14 2
      src/i18n/locales/fr/common.json
  13. 14 2
      src/i18n/locales/hi/common.json
  14. 19 1
      src/i18n/locales/id/common.json
  15. 14 2
      src/i18n/locales/it/common.json
  16. 14 2
      src/i18n/locales/ja/common.json
  17. 14 2
      src/i18n/locales/ko/common.json
  18. 19 1
      src/i18n/locales/nl/common.json
  19. 14 2
      src/i18n/locales/pl/common.json
  20. 14 2
      src/i18n/locales/pt-BR/common.json
  21. 14 2
      src/i18n/locales/ru/common.json
  22. 14 2
      src/i18n/locales/tr/common.json
  23. 14 2
      src/i18n/locales/vi/common.json
  24. 14 2
      src/i18n/locales/zh-CN/common.json
  25. 14 2
      src/i18n/locales/zh-TW/common.json
  26. 44 0
      src/integrations/claude-code/run.ts
  27. 52 0
      src/integrations/claude-code/types.ts
  28. 7 0
      webview-ui/src/components/settings/ApiOptions.tsx
  29. 3 0
      webview-ui/src/components/settings/constants.ts
  30. 40 0
      webview-ui/src/components/settings/providers/ClaudeCode.tsx
  31. 1 0
      webview-ui/src/components/settings/providers/index.ts
  32. 6 1
      webview-ui/src/i18n/locales/ca/settings.json
  33. 6 1
      webview-ui/src/i18n/locales/de/settings.json
  34. 6 1
      webview-ui/src/i18n/locales/en/settings.json
  35. 6 1
      webview-ui/src/i18n/locales/es/settings.json
  36. 6 1
      webview-ui/src/i18n/locales/fr/settings.json
  37. 6 1
      webview-ui/src/i18n/locales/hi/settings.json
  38. 6 1
      webview-ui/src/i18n/locales/id/settings.json
  39. 6 1
      webview-ui/src/i18n/locales/it/settings.json
  40. 6 1
      webview-ui/src/i18n/locales/ja/settings.json
  41. 6 1
      webview-ui/src/i18n/locales/ko/settings.json
  42. 6 1
      webview-ui/src/i18n/locales/nl/settings.json
  43. 6 1
      webview-ui/src/i18n/locales/pl/settings.json
  44. 6 1
      webview-ui/src/i18n/locales/pt-BR/settings.json
  45. 6 1
      webview-ui/src/i18n/locales/ru/settings.json
  46. 6 1
      webview-ui/src/i18n/locales/tr/settings.json
  47. 6 1
      webview-ui/src/i18n/locales/vi/settings.json
  48. 6 1
      webview-ui/src/i18n/locales/zh-CN/settings.json
  49. 6 1
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 7 - 0
packages/types/src/provider-settings.ts

@@ -9,6 +9,7 @@ import { codebaseIndexProviderSchema } from "./codebase-index.js"
 
 export const providerNames = [
 	"anthropic",
+	"claude-code",
 	"glama",
 	"openrouter",
 	"bedrock",
@@ -76,6 +77,10 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({
 	anthropicUseAuthToken: z.boolean().optional(),
 })
 
+const claudeCodeSchema = apiModelIdProviderModelSchema.extend({
+	claudeCodePath: z.string().optional(),
+})
+
 const glamaSchema = baseProviderSettingsSchema.extend({
 	glamaModelId: z.string().optional(),
 	glamaApiKey: z.string().optional(),
@@ -208,6 +213,7 @@ const defaultSchema = z.object({
 
 export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [
 	anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })),
+	claudeCodeSchema.merge(z.object({ apiProvider: z.literal("claude-code") })),
 	glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })),
 	openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })),
 	bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })),
@@ -234,6 +240,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
 export const providerSettingsSchema = z.object({
 	apiProvider: providerNamesSchema.optional(),
 	...anthropicSchema.shape,
+	...claudeCodeSchema.shape,
 	...glamaSchema.shape,
 	...openRouterSchema.shape,
 	...bedrockSchema.shape,

+ 13 - 0
packages/types/src/providers/claude-code.ts

@@ -0,0 +1,13 @@
+import type { ModelInfo } from "../model.js"
+import { anthropicModels } from "./anthropic.js"
+
+// Claude Code
+export type ClaudeCodeModelId = keyof typeof claudeCodeModels
+export const claudeCodeDefaultModelId: ClaudeCodeModelId = "claude-sonnet-4-20250514"
+export const claudeCodeModels = {
+	"claude-sonnet-4-20250514": anthropicModels["claude-sonnet-4-20250514"],
+	"claude-opus-4-20250514": anthropicModels["claude-opus-4-20250514"],
+	"claude-3-7-sonnet-20250219": anthropicModels["claude-3-7-sonnet-20250219"],
+	"claude-3-5-sonnet-20241022": anthropicModels["claude-3-5-sonnet-20241022"],
+	"claude-3-5-haiku-20241022": anthropicModels["claude-3-5-haiku-20241022"],
+} as const satisfies Record<string, ModelInfo>

+ 1 - 0
packages/types/src/providers/index.ts

@@ -1,6 +1,7 @@
 export * from "./anthropic.js"
 export * from "./bedrock.js"
 export * from "./chutes.js"
+export * from "./claude-code.js"
 export * from "./deepseek.js"
 export * from "./gemini.js"
 export * from "./glama.js"

+ 3 - 0
src/api/index.ts

@@ -27,6 +27,7 @@ import {
 	GroqHandler,
 	ChutesHandler,
 	LiteLLMHandler,
+	ClaudeCodeHandler,
 } from "./providers"
 
 export interface SingleCompletionHandler {
@@ -64,6 +65,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
 	switch (apiProvider) {
 		case "anthropic":
 			return new AnthropicHandler(options)
+		case "claude-code":
+			return new ClaudeCodeHandler(options)
 		case "glama":
 			return new GlamaHandler(options)
 		case "openrouter":

+ 171 - 0
src/api/providers/claude-code.ts

@@ -0,0 +1,171 @@
+import type { Anthropic } from "@anthropic-ai/sdk"
+import { claudeCodeDefaultModelId, type ClaudeCodeModelId, claudeCodeModels } from "@roo-code/types"
+import { type ApiHandler } from ".."
+import { ApiStreamUsageChunk, type ApiStream } from "../transform/stream"
+import { runClaudeCode } from "../../integrations/claude-code/run"
+import { ClaudeCodeMessage } from "../../integrations/claude-code/types"
+import { BaseProvider } from "./base-provider"
+import { t } from "../../i18n"
+import { ApiHandlerOptions } from "../../shared/api"
+
+export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
+	private options: ApiHandlerOptions
+
+	constructor(options: ApiHandlerOptions) {
+		super()
+		this.options = options
+	}
+
+	override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
+		const claudeProcess = runClaudeCode({
+			systemPrompt,
+			messages,
+			path: this.options.claudeCodePath,
+			modelId: this.getModel().id,
+		})
+
+		const dataQueue: string[] = []
+		let processError = null
+		let errorOutput = ""
+		let exitCode: number | null = null
+
+		claudeProcess.stdout.on("data", (data) => {
+			const output = data.toString()
+			const lines = output.split("\n").filter((line: string) => line.trim() !== "")
+
+			for (const line of lines) {
+				dataQueue.push(line)
+			}
+		})
+
+		claudeProcess.stderr.on("data", (data) => {
+			errorOutput += data.toString()
+		})
+
+		claudeProcess.on("close", (code) => {
+			exitCode = code
+		})
+
+		claudeProcess.on("error", (error) => {
+			processError = error
+		})
+
+		// Usage is included with assistant messages,
+		// but cost is included in the result chunk
+		let usage: ApiStreamUsageChunk = {
+			type: "usage",
+			inputTokens: 0,
+			outputTokens: 0,
+			cacheReadTokens: 0,
+			cacheWriteTokens: 0,
+		}
+
+		while (exitCode !== 0 || dataQueue.length > 0) {
+			if (dataQueue.length === 0) {
+				await new Promise((resolve) => setImmediate(resolve))
+			}
+
+			if (exitCode !== null && exitCode !== 0) {
+				if (errorOutput) {
+					throw new Error(
+						t("common:errors.claudeCode.processExitedWithError", {
+							exitCode,
+							output: errorOutput.trim(),
+						}),
+					)
+				}
+				throw new Error(t("common:errors.claudeCode.processExited", { exitCode }))
+			}
+
+			const data = dataQueue.shift()
+			if (!data) {
+				continue
+			}
+
+			const chunk = this.attemptParseChunk(data)
+
+			if (!chunk) {
+				yield {
+					type: "text",
+					text: data || "",
+				}
+
+				continue
+			}
+
+			if (chunk.type === "system" && chunk.subtype === "init") {
+				continue
+			}
+
+			if (chunk.type === "assistant" && "message" in chunk) {
+				const message = chunk.message
+
+				if (message.stop_reason !== null && message.stop_reason !== "tool_use") {
+					const errorMessage =
+						message.content[0]?.text ||
+						t("common:errors.claudeCode.stoppedWithReason", { reason: message.stop_reason })
+
+					if (errorMessage.includes("Invalid model name")) {
+						throw new Error(errorMessage + `\n\n${t("common:errors.claudeCode.apiKeyModelPlanMismatch")}`)
+					}
+
+					throw new Error(errorMessage)
+				}
+
+				for (const content of message.content) {
+					if (content.type === "text") {
+						yield {
+							type: "text",
+							text: content.text,
+						}
+					} else {
+						console.warn("Unsupported content type:", content.type)
+					}
+				}
+
+				usage.inputTokens += message.usage.input_tokens
+				usage.outputTokens += message.usage.output_tokens
+				usage.cacheReadTokens = (usage.cacheReadTokens || 0) + (message.usage.cache_read_input_tokens || 0)
+				usage.cacheWriteTokens =
+					(usage.cacheWriteTokens || 0) + (message.usage.cache_creation_input_tokens || 0)
+
+				continue
+			}
+
+			if (chunk.type === "result" && "result" in chunk) {
+				// Only use the cost from the CLI if provided
+				// Don't calculate cost as it may be $0 for subscription users
+				usage.totalCost = chunk.cost_usd ?? 0
+
+				yield usage
+			}
+
+			if (processError) {
+				throw processError
+			}
+		}
+	}
+
+	getModel() {
+		const modelId = this.options.apiModelId
+		if (modelId && modelId in claudeCodeModels) {
+			const id = modelId as ClaudeCodeModelId
+			return { id, info: claudeCodeModels[id] }
+		}
+
+		return {
+			id: claudeCodeDefaultModelId,
+			info: claudeCodeModels[claudeCodeDefaultModelId],
+		}
+	}
+
+	// TODO: Validate instead of parsing
+	private attemptParseChunk(data: string): ClaudeCodeMessage | null {
+		try {
+			return JSON.parse(data)
+		} catch (error) {
+			console.error("Error parsing chunk:", error)
+			return null
+		}
+	}
+}

+ 1 - 0
src/api/providers/index.ts

@@ -2,6 +2,7 @@ export { AnthropicVertexHandler } from "./anthropic-vertex"
 export { AnthropicHandler } from "./anthropic"
 export { AwsBedrockHandler } from "./bedrock"
 export { ChutesHandler } from "./chutes"
+export { ClaudeCodeHandler } from "./claude-code"
 export { DeepSeekHandler } from "./deepseek"
 export { FakeAIHandler } from "./fake-ai"
 export { GeminiHandler } from "./gemini"

+ 7 - 1
src/api/transform/stream.ts

@@ -1,6 +1,12 @@
 export type ApiStream = AsyncGenerator<ApiStreamChunk>
 
-export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk | ApiStreamReasoningChunk
+export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk | ApiStreamReasoningChunk | ApiStreamError
+
+export interface ApiStreamError {
+	type: "error"
+	error: string
+	message: string
+}
 
 export interface ApiStreamTextChunk {
 	type: "text"

+ 14 - 2
src/i18n/locales/ca/common.json

@@ -66,7 +66,14 @@
 		"share_no_active_task": "No hi ha cap tasca activa per compartir",
 		"share_auth_required": "Es requereix autenticació. Si us plau, inicia sessió per compartir tasques.",
 		"share_not_enabled": "La compartició de tasques no està habilitada per a aquesta organització.",
-		"share_task_not_found": "Tasca no trobada o accés denegat."
+		"share_task_not_found": "Tasca no trobada o accés denegat.",
+		"claudeCode": {
+			"processExited": "El procés Claude Code ha sortit amb codi {{exitCode}}.",
+			"errorOutput": "Sortida d'error: {{output}}",
+			"processExitedWithError": "El procés Claude Code ha sortit amb codi {{exitCode}}. Sortida d'error: {{output}}",
+			"stoppedWithReason": "Claude Code s'ha aturat per la raó: {{reason}}",
+			"apiKeyModelPlanMismatch": "Les claus API i els plans de subscripció permeten models diferents. Assegura't que el model seleccionat estigui inclòs al teu pla."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "No s'ha seleccionat contingut de terminal",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Clau API de Groq",
-			"getGroqApiKey": "Obté la clau API de Groq"
+			"getGroqApiKey": "Obté la clau API de Groq",
+			"claudeCode": {
+				"pathLabel": "Ruta de Claude Code",
+				"description": "Ruta opcional a la teva CLI de Claude Code. Per defecte 'claude' si no s'estableix.",
+				"placeholder": "Per defecte: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/de/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Keine aktive Aufgabe zum Teilen",
 		"share_auth_required": "Authentifizierung erforderlich. Bitte melde dich an, um Aufgaben zu teilen.",
 		"share_not_enabled": "Aufgabenfreigabe ist für diese Organisation nicht aktiviert.",
-		"share_task_not_found": "Aufgabe nicht gefunden oder Zugriff verweigert."
+		"share_task_not_found": "Aufgabe nicht gefunden oder Zugriff verweigert.",
+		"claudeCode": {
+			"processExited": "Claude Code Prozess wurde mit Code {{exitCode}} beendet.",
+			"errorOutput": "Fehlerausgabe: {{output}}",
+			"processExitedWithError": "Claude Code Prozess wurde mit Code {{exitCode}} beendet. Fehlerausgabe: {{output}}",
+			"stoppedWithReason": "Claude Code wurde mit Grund gestoppt: {{reason}}",
+			"apiKeyModelPlanMismatch": "API-Schlüssel und Abonnement-Pläne erlauben verschiedene Modelle. Stelle sicher, dass das ausgewählte Modell in deinem Plan enthalten ist."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Kein Terminal-Inhalt ausgewählt",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Groq API-Schlüssel",
-			"getGroqApiKey": "Groq API-Schlüssel erhalten"
+			"getGroqApiKey": "Groq API-Schlüssel erhalten",
+			"claudeCode": {
+				"pathLabel": "Claude Code Pfad",
+				"description": "Optionaler Pfad zu deiner Claude Code CLI. Standardmäßig 'claude', falls nicht festgelegt.",
+				"placeholder": "Standard: claude"
+			}
 		}
 	},
 	"mdm": {

+ 8 - 1
src/i18n/locales/en/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "No active task to share",
 		"share_auth_required": "Authentication required. Please sign in to share tasks.",
 		"share_not_enabled": "Task sharing is not enabled for this organization.",
-		"share_task_not_found": "Task not found or access denied."
+		"share_task_not_found": "Task not found or access denied.",
+		"claudeCode": {
+			"processExited": "Claude Code process exited with code {{exitCode}}.",
+			"errorOutput": "Error output: {{output}}",
+			"processExitedWithError": "Claude Code process exited with code {{exitCode}}. Error output: {{output}}",
+			"stoppedWithReason": "Claude Code stopped with reason: {{reason}}",
+			"apiKeyModelPlanMismatch": "API keys and subscription plans allow different models. Make sure the selected model is included in your plan."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "No terminal content selected",

+ 14 - 2
src/i18n/locales/es/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "No hay tarea activa para compartir",
 		"share_auth_required": "Se requiere autenticación. Por favor, inicia sesión para compartir tareas.",
 		"share_not_enabled": "La compartición de tareas no está habilitada para esta organización.",
-		"share_task_not_found": "Tarea no encontrada o acceso denegado."
+		"share_task_not_found": "Tarea no encontrada o acceso denegado.",
+		"claudeCode": {
+			"processExited": "El proceso de Claude Code terminó con código {{exitCode}}.",
+			"errorOutput": "Salida de error: {{output}}",
+			"processExitedWithError": "El proceso de Claude Code terminó con código {{exitCode}}. Salida de error: {{output}}",
+			"stoppedWithReason": "Claude Code se detuvo por la razón: {{reason}}",
+			"apiKeyModelPlanMismatch": "Las claves API y los planes de suscripción permiten diferentes modelos. Asegúrate de que el modelo seleccionado esté incluido en tu plan."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "No hay contenido de terminal seleccionado",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Clave API de Groq",
-			"getGroqApiKey": "Obtener clave API de Groq"
+			"getGroqApiKey": "Obtener clave API de Groq",
+			"claudeCode": {
+				"pathLabel": "Ruta de Claude Code",
+				"description": "Ruta opcional a tu CLI de Claude Code. Por defecto 'claude' si no se establece.",
+				"placeholder": "Por defecto: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/fr/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Aucune tâche active à partager",
 		"share_auth_required": "Authentification requise. Veuillez vous connecter pour partager des tâches.",
 		"share_not_enabled": "Le partage de tâches n'est pas activé pour cette organisation.",
-		"share_task_not_found": "Tâche non trouvée ou accès refusé."
+		"share_task_not_found": "Tâche non trouvée ou accès refusé.",
+		"claudeCode": {
+			"processExited": "Le processus Claude Code s'est terminé avec le code {{exitCode}}.",
+			"errorOutput": "Sortie d'erreur : {{output}}",
+			"processExitedWithError": "Le processus Claude Code s'est terminé avec le code {{exitCode}}. Sortie d'erreur : {{output}}",
+			"stoppedWithReason": "Claude Code s'est arrêté pour la raison : {{reason}}",
+			"apiKeyModelPlanMismatch": "Les clés API et les plans d'abonnement permettent différents modèles. Assurez-vous que le modèle sélectionné est inclus dans votre plan."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Aucun contenu de terminal sélectionné",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Clé API Groq",
-			"getGroqApiKey": "Obtenir la clé API Groq"
+			"getGroqApiKey": "Obtenir la clé API Groq",
+			"claudeCode": {
+				"pathLabel": "Chemin de Claude Code",
+				"description": "Chemin optionnel vers votre CLI Claude Code. Par défaut 'claude' si non défini.",
+				"placeholder": "Par défaut : claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/hi/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "साझा करने के लिए कोई सक्रिय कार्य नहीं",
 		"share_auth_required": "प्रमाणीकरण आवश्यक है। कार्य साझा करने के लिए कृपया साइन इन करें।",
 		"share_not_enabled": "इस संगठन के लिए कार्य साझाकरण सक्षम नहीं है।",
-		"share_task_not_found": "कार्य नहीं मिला या पहुंच अस्वीकृत।"
+		"share_task_not_found": "कार्य नहीं मिला या पहुंच अस्वीकृत।",
+		"claudeCode": {
+			"processExited": "Claude Code प्रक्रिया कोड {{exitCode}} के साथ समाप्त हुई।",
+			"errorOutput": "त्रुटि आउटपुट: {{output}}",
+			"processExitedWithError": "Claude Code प्रक्रिया कोड {{exitCode}} के साथ समाप्त हुई। त्रुटि आउटपुट: {{output}}",
+			"stoppedWithReason": "Claude Code इस कारण से रुका: {{reason}}",
+			"apiKeyModelPlanMismatch": "API कुंजी और सब्सक्रिप्शन प्लान अलग-अलग मॉडल की अनुमति देते हैं। सुनिश्चित करें कि चयनित मॉडल आपकी योजना में शामिल है।"
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "कोई टर्मिनल सामग्री चयनित नहीं",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "ग्रोक एपीआई कुंजी",
-			"getGroqApiKey": "ग्रोक एपीआई कुंजी प्राप्त करें"
+			"getGroqApiKey": "ग्रोक एपीआई कुंजी प्राप्त करें",
+			"claudeCode": {
+				"pathLabel": "क्लाउड कोड पाथ",
+				"description": "आपके क्लाउड कोड CLI का वैकल्पिक पाथ। सेट न होने पर डिफ़ॉल्ट रूप से 'claude'。",
+				"placeholder": "डिफ़ॉल्ट: claude"
+			}
 		}
 	},
 	"mdm": {

+ 19 - 1
src/i18n/locales/id/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Tidak ada tugas aktif untuk dibagikan",
 		"share_auth_required": "Autentikasi diperlukan. Silakan masuk untuk berbagi tugas.",
 		"share_not_enabled": "Berbagi tugas tidak diaktifkan untuk organisasi ini.",
-		"share_task_not_found": "Tugas tidak ditemukan atau akses ditolak."
+		"share_task_not_found": "Tugas tidak ditemukan atau akses ditolak.",
+		"claudeCode": {
+			"processExited": "Proses Claude Code keluar dengan kode {{exitCode}}.",
+			"errorOutput": "Output error: {{output}}",
+			"processExitedWithError": "Proses Claude Code keluar dengan kode {{exitCode}}. Output error: {{output}}",
+			"stoppedWithReason": "Claude Code berhenti karena alasan: {{reason}}",
+			"apiKeyModelPlanMismatch": "Kunci API dan paket berlangganan memungkinkan model yang berbeda. Pastikan model yang dipilih termasuk dalam paket Anda."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Tidak ada konten terminal yang dipilih",
@@ -102,6 +109,17 @@
 		"task_prompt": "Apa yang harus Roo lakukan?",
 		"task_placeholder": "Ketik tugas kamu di sini"
 	},
+	"settings": {
+		"providers": {
+			"groqApiKey": "Kunci API Groq",
+			"getGroqApiKey": "Dapatkan Kunci API Groq",
+			"claudeCode": {
+				"pathLabel": "Jalur Claude Code",
+				"description": "Jalur opsional ke CLI Claude Code Anda. Defaultnya 'claude' jika tidak diatur.",
+				"placeholder": "Default: claude"
+			}
+		}
+	},
 	"mdm": {
 		"errors": {
 			"cloud_auth_required": "Organisasi kamu memerlukan autentikasi Roo Code Cloud. Silakan masuk untuk melanjutkan.",

+ 14 - 2
src/i18n/locales/it/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Nessuna attività attiva da condividere",
 		"share_auth_required": "Autenticazione richiesta. Accedi per condividere le attività.",
 		"share_not_enabled": "La condivisione delle attività non è abilitata per questa organizzazione.",
-		"share_task_not_found": "Attività non trovata o accesso negato."
+		"share_task_not_found": "Attività non trovata o accesso negato.",
+		"claudeCode": {
+			"processExited": "Il processo Claude Code è terminato con codice {{exitCode}}.",
+			"errorOutput": "Output di errore: {{output}}",
+			"processExitedWithError": "Il processo Claude Code è terminato con codice {{exitCode}}. Output di errore: {{output}}",
+			"stoppedWithReason": "Claude Code si è fermato per il motivo: {{reason}}",
+			"apiKeyModelPlanMismatch": "Le chiavi API e i piani di abbonamento consentono modelli diversi. Assicurati che il modello selezionato sia incluso nel tuo piano."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Nessun contenuto del terminale selezionato",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Chiave API Groq",
-			"getGroqApiKey": "Ottieni chiave API Groq"
+			"getGroqApiKey": "Ottieni chiave API Groq",
+			"claudeCode": {
+				"pathLabel": "Percorso Claude Code",
+				"description": "Percorso opzionale alla tua CLI Claude Code. Predefinito 'claude' se non impostato.",
+				"placeholder": "Predefinito: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/ja/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "共有するアクティブなタスクがありません",
 		"share_auth_required": "認証が必要です。タスクを共有するにはサインインしてください。",
 		"share_not_enabled": "この組織ではタスク共有が有効になっていません。",
-		"share_task_not_found": "タスクが見つからないか、アクセスが拒否されました。"
+		"share_task_not_found": "タスクが見つからないか、アクセスが拒否されました。",
+		"claudeCode": {
+			"processExited": "Claude Code プロセスがコード {{exitCode}} で終了しました。",
+			"errorOutput": "エラー出力:{{output}}",
+			"processExitedWithError": "Claude Code プロセスがコード {{exitCode}} で終了しました。エラー出力:{{output}}",
+			"stoppedWithReason": "Claude Code が理由により停止しました:{{reason}}",
+			"apiKeyModelPlanMismatch": "API キーとサブスクリプションプランでは異なるモデルが利用可能です。選択したモデルがプランに含まれていることを確認してください。"
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "選択されたターミナルコンテンツがありません",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Groq APIキー",
-			"getGroqApiKey": "Groq APIキーを取得"
+			"getGroqApiKey": "Groq APIキーを取得",
+			"claudeCode": {
+				"pathLabel": "Claude Code パス",
+				"description": "Claude Code CLI へのオプションのパス。設定されていない場合は、デフォルトで「claude」になります。",
+				"placeholder": "デフォルト: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/ko/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "공유할 활성 작업이 없습니다",
 		"share_auth_required": "인증이 필요합니다. 작업을 공유하려면 로그인하세요.",
 		"share_not_enabled": "이 조직에서는 작업 공유가 활성화되지 않았습니다.",
-		"share_task_not_found": "작업을 찾을 수 없거나 액세스가 거부되었습니다."
+		"share_task_not_found": "작업을 찾을 수 없거나 액세스가 거부되었습니다.",
+		"claudeCode": {
+			"processExited": "Claude Code 프로세스가 코드 {{exitCode}}로 종료되었습니다.",
+			"errorOutput": "오류 출력: {{output}}",
+			"processExitedWithError": "Claude Code 프로세스가 코드 {{exitCode}}로 종료되었습니다. 오류 출력: {{output}}",
+			"stoppedWithReason": "Claude Code가 다음 이유로 중지되었습니다: {{reason}}",
+			"apiKeyModelPlanMismatch": "API 키와 구독 플랜에서 다른 모델을 허용합니다. 선택한 모델이 플랜에 포함되어 있는지 확인하세요."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "선택된 터미널 내용이 없습니다",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Groq API 키",
-			"getGroqApiKey": "Groq API 키 받기"
+			"getGroqApiKey": "Groq API 키 받기",
+			"claudeCode": {
+				"pathLabel": "Claude Code 경로",
+				"description": "Claude Code CLI의 선택적 경로입니다. 설정되지 않은 경우 기본값은 'claude'입니다.",
+				"placeholder": "기본값: claude"
+			}
 		}
 	},
 	"mdm": {

+ 19 - 1
src/i18n/locales/nl/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Geen actieve taak om te delen",
 		"share_auth_required": "Authenticatie vereist. Log in om taken te delen.",
 		"share_not_enabled": "Taken delen is niet ingeschakeld voor deze organisatie.",
-		"share_task_not_found": "Taak niet gevonden of toegang geweigerd."
+		"share_task_not_found": "Taak niet gevonden of toegang geweigerd.",
+		"claudeCode": {
+			"processExited": "Claude Code proces beëindigd met code {{exitCode}}.",
+			"errorOutput": "Foutuitvoer: {{output}}",
+			"processExitedWithError": "Claude Code proces beëindigd met code {{exitCode}}. Foutuitvoer: {{output}}",
+			"stoppedWithReason": "Claude Code gestopt om reden: {{reason}}",
+			"apiKeyModelPlanMismatch": "API-sleutels en abonnementsplannen staan verschillende modellen toe. Zorg ervoor dat het geselecteerde model is opgenomen in je plan."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Geen terminalinhoud geselecteerd",
@@ -102,6 +109,17 @@
 		"task_prompt": "Wat moet Roo doen?",
 		"task_placeholder": "Typ hier je taak"
 	},
+	"settings": {
+		"providers": {
+			"groqApiKey": "Groq API-sleutel",
+			"getGroqApiKey": "Groq API-sleutel ophalen",
+			"claudeCode": {
+				"pathLabel": "Claude Code Pad",
+				"description": "Optioneel pad naar je Claude Code CLI. Standaard 'claude' indien niet ingesteld.",
+				"placeholder": "Standaard: claude"
+			}
+		}
+	},
 	"mdm": {
 		"errors": {
 			"cloud_auth_required": "Je organisatie vereist Roo Code Cloud-authenticatie. Log in om door te gaan.",

+ 14 - 2
src/i18n/locales/pl/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Brak aktywnego zadania do udostępnienia",
 		"share_auth_required": "Wymagana autoryzacja. Zaloguj się, aby udostępniać zadania.",
 		"share_not_enabled": "Udostępnianie zadań nie jest włączone dla tej organizacji.",
-		"share_task_not_found": "Zadanie nie znalezione lub dostęp odmówiony."
+		"share_task_not_found": "Zadanie nie znalezione lub dostęp odmówiony.",
+		"claudeCode": {
+			"processExited": "Proces Claude Code zakończył się kodem {{exitCode}}.",
+			"errorOutput": "Wyjście błędu: {{output}}",
+			"processExitedWithError": "Proces Claude Code zakończył się kodem {{exitCode}}. Wyjście błędu: {{output}}",
+			"stoppedWithReason": "Claude Code zatrzymał się z powodu: {{reason}}",
+			"apiKeyModelPlanMismatch": "Klucze API i plany subskrypcji pozwalają na różne modele. Upewnij się, że wybrany model jest zawarty w twoim planie."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Nie wybrano zawartości terminala",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Klucz API Groq",
-			"getGroqApiKey": "Uzyskaj klucz API Groq"
+			"getGroqApiKey": "Uzyskaj klucz API Groq",
+			"claudeCode": {
+				"pathLabel": "Ścieżka Claude Code",
+				"description": "Opcjonalna ścieżka do Twojego CLI Claude Code. Domyślnie 'claude', jeśli nie ustawiono.",
+				"placeholder": "Domyślnie: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/pt-BR/common.json

@@ -66,7 +66,14 @@
 		"share_no_active_task": "Nenhuma tarefa ativa para compartilhar",
 		"share_auth_required": "Autenticação necessária. Faça login para compartilhar tarefas.",
 		"share_not_enabled": "O compartilhamento de tarefas não está habilitado para esta organização.",
-		"share_task_not_found": "Tarefa não encontrada ou acesso negado."
+		"share_task_not_found": "Tarefa não encontrada ou acesso negado.",
+		"claudeCode": {
+			"processExited": "O processo Claude Code saiu com código {{exitCode}}.",
+			"errorOutput": "Saída de erro: {{output}}",
+			"processExitedWithError": "O processo Claude Code saiu com código {{exitCode}}. Saída de erro: {{output}}",
+			"stoppedWithReason": "Claude Code parou pela razão: {{reason}}",
+			"apiKeyModelPlanMismatch": "Chaves de API e planos de assinatura permitem modelos diferentes. Certifique-se de que o modelo selecionado esteja incluído no seu plano."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Nenhum conteúdo do terminal selecionado",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Chave de API Groq",
-			"getGroqApiKey": "Obter chave de API Groq"
+			"getGroqApiKey": "Obter chave de API Groq",
+			"claudeCode": {
+				"pathLabel": "Caminho do Claude Code",
+				"description": "Caminho opcional para sua CLI do Claude Code. Padrão 'claude' se não for definido.",
+				"placeholder": "Padrão: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/ru/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Нет активной задачи для совместного использования",
 		"share_auth_required": "Требуется аутентификация. Войдите в систему для совместного доступа к задачам.",
 		"share_not_enabled": "Совместный доступ к задачам не включен для этой организации.",
-		"share_task_not_found": "Задача не найдена или доступ запрещен."
+		"share_task_not_found": "Задача не найдена или доступ запрещен.",
+		"claudeCode": {
+			"processExited": "Процесс Claude Code завершился с кодом {{exitCode}}.",
+			"errorOutput": "Вывод ошибки: {{output}}",
+			"processExitedWithError": "Процесс Claude Code завершился с кодом {{exitCode}}. Вывод ошибки: {{output}}",
+			"stoppedWithReason": "Claude Code остановился по причине: {{reason}}",
+			"apiKeyModelPlanMismatch": "API-ключи и планы подписки позволяют использовать разные модели. Убедитесь, что выбранная модель включена в ваш план."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Не выбрано содержимое терминала",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Ключ API Groq",
-			"getGroqApiKey": "Получить ключ API Groq"
+			"getGroqApiKey": "Получить ключ API Groq",
+			"claudeCode": {
+				"pathLabel": "Путь к Claude Code",
+				"description": "Необязательный путь к вашему CLI Claude Code. По умолчанию 'claude', если не установлено.",
+				"placeholder": "По умолчанию: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/tr/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Paylaşılacak aktif görev yok",
 		"share_auth_required": "Kimlik doğrulama gerekli. Görevleri paylaşmak için lütfen giriş yapın.",
 		"share_not_enabled": "Bu kuruluş için görev paylaşımı etkinleştirilmemiş.",
-		"share_task_not_found": "Görev bulunamadı veya erişim reddedildi."
+		"share_task_not_found": "Görev bulunamadı veya erişim reddedildi.",
+		"claudeCode": {
+			"processExited": "Claude Code işlemi {{exitCode}} koduyla çıktı.",
+			"errorOutput": "Hata çıktısı: {{output}}",
+			"processExitedWithError": "Claude Code işlemi {{exitCode}} koduyla çıktı. Hata çıktısı: {{output}}",
+			"stoppedWithReason": "Claude Code şu nedenle durdu: {{reason}}",
+			"apiKeyModelPlanMismatch": "API anahtarları ve abonelik planları farklı modellere izin verir. Seçilen modelin planınıza dahil olduğundan emin olun."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Seçili terminal içeriği yok",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Groq API Anahtarı",
-			"getGroqApiKey": "Groq API Anahtarı Al"
+			"getGroqApiKey": "Groq API Anahtarı Al",
+			"claudeCode": {
+				"pathLabel": "Claude Code Yolu",
+				"description": "Claude Code CLI'nizin isteğe bağlı yolu. Ayarlanmazsa varsayılan olarak 'claude' olur.",
+				"placeholder": "Varsayılan: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/vi/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "Không có nhiệm vụ hoạt động để chia sẻ",
 		"share_auth_required": "Cần xác thực. Vui lòng đăng nhập để chia sẻ nhiệm vụ.",
 		"share_not_enabled": "Chia sẻ nhiệm vụ không được bật cho tổ chức này.",
-		"share_task_not_found": "Không tìm thấy nhiệm vụ hoặc truy cập bị từ chối."
+		"share_task_not_found": "Không tìm thấy nhiệm vụ hoặc truy cập bị từ chối.",
+		"claudeCode": {
+			"processExited": "Tiến trình Claude Code thoát với mã {{exitCode}}.",
+			"errorOutput": "Đầu ra lỗi: {{output}}",
+			"processExitedWithError": "Tiến trình Claude Code thoát với mã {{exitCode}}. Đầu ra lỗi: {{output}}",
+			"stoppedWithReason": "Claude Code dừng lại vì lý do: {{reason}}",
+			"apiKeyModelPlanMismatch": "Khóa API và gói đăng ký cho phép các mô hình khác nhau. Đảm bảo rằng mô hình đã chọn được bao gồm trong gói của bạn."
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "Không có nội dung terminal được chọn",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Khóa API Groq",
-			"getGroqApiKey": "Lấy khóa API Groq"
+			"getGroqApiKey": "Lấy khóa API Groq",
+			"claudeCode": {
+				"pathLabel": "Đường dẫn Claude Code",
+				"description": "Đường dẫn tùy chọn đến CLI Claude Code của bạn. Mặc định là 'claude' nếu không được đặt.",
+				"placeholder": "Mặc định: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/zh-CN/common.json

@@ -67,7 +67,14 @@
 		"share_no_active_task": "没有活跃任务可分享",
 		"share_auth_required": "需要身份验证。请登录以分享任务。",
 		"share_not_enabled": "此组织未启用任务分享功能。",
-		"share_task_not_found": "未找到任务或访问被拒绝。"
+		"share_task_not_found": "未找到任务或访问被拒绝。",
+		"claudeCode": {
+			"processExited": "Claude Code 进程退出,退出码:{{exitCode}}。",
+			"errorOutput": "错误输出:{{output}}",
+			"processExitedWithError": "Claude Code 进程退出,退出码:{{exitCode}}。错误输出:{{output}}",
+			"stoppedWithReason": "Claude Code 停止,原因:{{reason}}",
+			"apiKeyModelPlanMismatch": "API 密钥和订阅计划支持不同的模型。请确保所选模型包含在您的计划中。"
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "没有选择终端内容",
@@ -110,7 +117,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Groq API 密钥",
-			"getGroqApiKey": "获取 Groq API 密钥"
+			"getGroqApiKey": "获取 Groq API 密钥",
+			"claudeCode": {
+				"pathLabel": "Claude Code 路径",
+				"description": "Claude Code CLI 的可选路径。如果未设置,默认为 'claude'。",
+				"placeholder": "默认: claude"
+			}
 		}
 	},
 	"mdm": {

+ 14 - 2
src/i18n/locales/zh-TW/common.json

@@ -62,7 +62,14 @@
 		"share_no_active_task": "沒有活躍的工作可分享",
 		"share_auth_required": "需要身份驗證。請登入以分享工作。",
 		"share_not_enabled": "此組織未啟用工作分享功能。",
-		"share_task_not_found": "未找到工作或存取被拒絕。"
+		"share_task_not_found": "未找到工作或存取被拒絕。",
+		"claudeCode": {
+			"processExited": "Claude Code 程序退出,退出碼:{{exitCode}}。",
+			"errorOutput": "錯誤輸出:{{output}}",
+			"processExitedWithError": "Claude Code 程序退出,退出碼:{{exitCode}}。錯誤輸出:{{output}}",
+			"stoppedWithReason": "Claude Code 停止,原因:{{reason}}",
+			"apiKeyModelPlanMismatch": "API 金鑰和訂閱方案允許不同的模型。請確保所選模型包含在您的方案中。"
+		}
 	},
 	"warnings": {
 		"no_terminal_content": "沒有選擇終端機內容",
@@ -105,7 +112,12 @@
 	"settings": {
 		"providers": {
 			"groqApiKey": "Groq API 金鑰",
-			"getGroqApiKey": "取得 Groq API 金鑰"
+			"getGroqApiKey": "取得 Groq API 金鑰",
+			"claudeCode": {
+				"pathLabel": "Claude Code 路徑",
+				"description": "Claude Code CLI 的選用路徑。如果未設定,預設為 'claude'。",
+				"placeholder": "預設: claude"
+			}
 		}
 	},
 	"mdm": {

+ 44 - 0
src/integrations/claude-code/run.ts

@@ -0,0 +1,44 @@
+import * as vscode from "vscode"
+import Anthropic from "@anthropic-ai/sdk"
+import { execa } from "execa"
+
+export function runClaudeCode({
+	systemPrompt,
+	messages,
+	path,
+	modelId,
+}: {
+	systemPrompt: string
+	messages: Anthropic.Messages.MessageParam[]
+	path?: string
+	modelId?: string
+}) {
+	const claudePath = path || "claude"
+
+	// TODO: Is it worth using sessions? Where do we store the session ID?
+	const args = [
+		"-p",
+		JSON.stringify(messages),
+		"--system-prompt",
+		systemPrompt,
+		"--verbose",
+		"--output-format",
+		"stream-json",
+		// Cline will handle recursive calls
+		"--max-turns",
+		"1",
+	]
+
+	if (modelId) {
+		args.push("--model", modelId)
+	}
+
+	const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
+	return execa(claudePath, args, {
+		stdin: "ignore",
+		stdout: "pipe",
+		stderr: "pipe",
+		env: process.env,
+		cwd,
+	})
+}

+ 52 - 0
src/integrations/claude-code/types.ts

@@ -0,0 +1,52 @@
+type InitMessage = {
+	type: "system"
+	subtype: "init"
+	session_id: string
+	tools: string[]
+	mcp_servers: string[]
+}
+
+type ClaudeCodeContent = {
+	type: "text"
+	text: string
+}
+
+type AssistantMessage = {
+	type: "assistant"
+	message: {
+		id: string
+		type: "message"
+		role: "assistant"
+		model: string
+		content: ClaudeCodeContent[]
+		stop_reason: string | null
+		stop_sequence: null
+		usage: {
+			input_tokens: number
+			cache_creation_input_tokens?: number
+			cache_read_input_tokens?: number
+			output_tokens: number
+			service_tier: "standard"
+		}
+	}
+	session_id: string
+}
+
+type ErrorMessage = {
+	type: "error"
+}
+
+type ResultMessage = {
+	type: "result"
+	subtype: "success"
+	cost_usd: number
+	is_error: boolean
+	duration_ms: number
+	duration_api_ms: number
+	num_turns: number
+	result: string
+	total_cost: number
+	session_id: string
+}
+
+export type ClaudeCodeMessage = InitMessage | AssistantMessage | ErrorMessage | ResultMessage

+ 7 - 0
webview-ui/src/components/settings/ApiOptions.tsx

@@ -13,6 +13,7 @@ import {
 	litellmDefaultModelId,
 	openAiNativeDefaultModelId,
 	anthropicDefaultModelId,
+	claudeCodeDefaultModelId,
 	geminiDefaultModelId,
 	deepSeekDefaultModelId,
 	mistralDefaultModelId,
@@ -36,6 +37,7 @@ import {
 	Anthropic,
 	Bedrock,
 	Chutes,
+	ClaudeCode,
 	DeepSeek,
 	Gemini,
 	Glama,
@@ -254,6 +256,7 @@ const ApiOptions = ({
 				requesty: { field: "requestyModelId", default: requestyDefaultModelId },
 				litellm: { field: "litellmModelId", default: litellmDefaultModelId },
 				anthropic: { field: "apiModelId", default: anthropicDefaultModelId },
+				"claude-code": { field: "apiModelId", default: claudeCodeDefaultModelId },
 				"openai-native": { field: "apiModelId", default: openAiNativeDefaultModelId },
 				gemini: { field: "apiModelId", default: geminiDefaultModelId },
 				deepseek: { field: "apiModelId", default: deepSeekDefaultModelId },
@@ -383,6 +386,10 @@ const ApiOptions = ({
 				<Anthropic apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
 			)}
 
+			{selectedProvider === "claude-code" && (
+				<ClaudeCode apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
+			)}
+
 			{selectedProvider === "openai-native" && (
 				<OpenAI apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
 			)}

+ 3 - 0
webview-ui/src/components/settings/constants.ts

@@ -3,6 +3,7 @@ import {
 	type ModelInfo,
 	anthropicModels,
 	bedrockModels,
+	claudeCodeModels,
 	deepSeekModels,
 	geminiModels,
 	mistralModels,
@@ -15,6 +16,7 @@ import {
 
 export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
 	anthropic: anthropicModels,
+	"claude-code": claudeCodeModels,
 	bedrock: bedrockModels,
 	deepseek: deepSeekModels,
 	gemini: geminiModels,
@@ -29,6 +31,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
 export const PROVIDERS = [
 	{ value: "openrouter", label: "OpenRouter" },
 	{ value: "anthropic", label: "Anthropic" },
+	{ value: "claude-code", label: "Claude Code" },
 	{ value: "gemini", label: "Google Gemini" },
 	{ value: "deepseek", label: "DeepSeek" },
 	{ value: "openai-native", label: "OpenAI" },

+ 40 - 0
webview-ui/src/components/settings/providers/ClaudeCode.tsx

@@ -0,0 +1,40 @@
+import React from "react"
+import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
+import { type ProviderSettings } from "@roo-code/types"
+import { useAppTranslation } from "@src/i18n/TranslationContext"
+
+interface ClaudeCodeProps {
+	apiConfiguration: ProviderSettings
+	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+}
+
+export const ClaudeCode: React.FC<ClaudeCodeProps> = ({ apiConfiguration, setApiConfigurationField }) => {
+	const { t } = useAppTranslation()
+
+	const handleInputChange = (e: Event | React.FormEvent<HTMLElement>) => {
+		const element = e.target as HTMLInputElement
+		setApiConfigurationField("claudeCodePath", element.value)
+	}
+
+	return (
+		<div>
+			<VSCodeTextField
+				value={apiConfiguration?.claudeCodePath || ""}
+				style={{ width: "100%", marginTop: 3 }}
+				type="text"
+				onInput={handleInputChange}
+				placeholder={t("settings:providers.claudeCode.placeholder")}>
+				{t("settings:providers.claudeCode.pathLabel")}
+			</VSCodeTextField>
+
+			<p
+				style={{
+					fontSize: "12px",
+					marginTop: 3,
+					color: "var(--vscode-descriptionForeground)",
+				}}>
+				{t("settings:providers.claudeCode.description")}
+			</p>
+		</div>
+	)
+}

+ 1 - 0
webview-ui/src/components/settings/providers/index.ts

@@ -1,6 +1,7 @@
 export { Anthropic } from "./Anthropic"
 export { Bedrock } from "./Bedrock"
 export { Chutes } from "./Chutes"
+export { ClaudeCode } from "./ClaudeCode"
 export { DeepSeek } from "./DeepSeek"
 export { Gemini } from "./Gemini"
 export { Glama } from "./Glama"

+ 6 - 1
webview-ui/src/i18n/locales/ca/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Mitjà",
 			"low": "Baix"
 		},
-		"setReasoningLevel": "Activa l'esforç de raonament"
+		"setReasoningLevel": "Activa l'esforç de raonament",
+		"claudeCode": {
+			"pathLabel": "Ruta del Codi Claude",
+			"description": "Ruta opcional al teu CLI de Claude Code. Per defecte, 'claude' si no s'estableix.",
+			"placeholder": "Per defecte: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/de/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Mittel",
 			"low": "Niedrig"
 		},
-		"setReasoningLevel": "Denkaufwand aktivieren"
+		"setReasoningLevel": "Denkaufwand aktivieren",
+		"claudeCode": {
+			"pathLabel": "Claude-Code-Pfad",
+			"description": "Optionaler Pfad zu Ihrer Claude Code CLI. Standard ist 'claude', wenn nicht festgelegt.",
+			"placeholder": "Standard: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/en/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Medium",
 			"low": "Low"
 		},
-		"setReasoningLevel": "Enable Reasoning Effort"
+		"setReasoningLevel": "Enable Reasoning Effort",
+		"claudeCode": {
+			"pathLabel": "Claude Code Path",
+			"description": "Optional path to your Claude Code CLI. Defaults to 'claude' if not set.",
+			"placeholder": "Default: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/es/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Medio",
 			"low": "Bajo"
 		},
-		"setReasoningLevel": "Habilitar esfuerzo de razonamiento"
+		"setReasoningLevel": "Habilitar esfuerzo de razonamiento",
+		"claudeCode": {
+			"pathLabel": "Ruta de Claude Code",
+			"description": "Ruta opcional a su CLI de Claude Code. Por defecto, es 'claude' si no se establece.",
+			"placeholder": "Por defecto: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/fr/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Moyen",
 			"low": "Faible"
 		},
-		"setReasoningLevel": "Activer l'effort de raisonnement"
+		"setReasoningLevel": "Activer l'effort de raisonnement",
+		"claudeCode": {
+			"pathLabel": "Chemin du code Claude",
+			"description": "Chemin facultatif vers votre CLI Claude Code. La valeur par défaut est 'claude' si non défini.",
+			"placeholder": "Défaut : claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/hi/settings.json

@@ -303,7 +303,12 @@
 			"medium": "मध्यम",
 			"low": "निम्न"
 		},
-		"setReasoningLevel": "तर्क प्रयास सक्षम करें"
+		"setReasoningLevel": "तर्क प्रयास सक्षम करें",
+		"claudeCode": {
+			"pathLabel": "क्लाउड कोड पथ",
+			"description": "आपके क्लाउड कोड सीएलआई का वैकल्पिक पथ। यदि सेट नहीं है तो डिफ़ॉल्ट 'claude' है।",
+			"placeholder": "डिफ़ॉल्ट: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/id/settings.json

@@ -307,7 +307,12 @@
 			"medium": "Sedang",
 			"low": "Rendah"
 		},
-		"setReasoningLevel": "Aktifkan Upaya Reasoning"
+		"setReasoningLevel": "Aktifkan Upaya Reasoning",
+		"claudeCode": {
+			"pathLabel": "Jalur Kode Claude",
+			"description": "Jalur opsional ke Claude Code CLI Anda. Defaultnya adalah 'claude' jika tidak diatur.",
+			"placeholder": "Default: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/it/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Medio",
 			"low": "Basso"
 		},
-		"setReasoningLevel": "Abilita sforzo di ragionamento"
+		"setReasoningLevel": "Abilita sforzo di ragionamento",
+		"claudeCode": {
+			"pathLabel": "Percorso Claude Code",
+			"description": "Percorso facoltativo per la tua CLI Claude Code. Predefinito 'claude' se non impostato.",
+			"placeholder": "Predefinito: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/ja/settings.json

@@ -303,7 +303,12 @@
 			"medium": "中",
 			"low": "低"
 		},
-		"setReasoningLevel": "推論労力を有効にする"
+		"setReasoningLevel": "推論労力を有効にする",
+		"claudeCode": {
+			"pathLabel": "クロードコードパス",
+			"description": "Claude Code CLIへのオプションパス。設定されていない場合、デフォルトは「claude」です。",
+			"placeholder": "デフォルト:claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/ko/settings.json

@@ -303,7 +303,12 @@
 			"medium": "중간",
 			"low": "낮음"
 		},
-		"setReasoningLevel": "추론 노력 활성화"
+		"setReasoningLevel": "추론 노력 활성화",
+		"claudeCode": {
+			"pathLabel": "클로드 코드 경로",
+			"description": "Claude Code CLI의 선택적 경로입니다. 설정하지 않으면 'claude'가 기본값입니다.",
+			"placeholder": "기본값: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/nl/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Middel",
 			"low": "Laag"
 		},
-		"setReasoningLevel": "Redeneervermogen inschakelen"
+		"setReasoningLevel": "Redeneervermogen inschakelen",
+		"claudeCode": {
+			"pathLabel": "Claude Code Pad",
+			"description": "Optioneel pad naar uw Claude Code CLI. Standaard 'claude' als niet ingesteld.",
+			"placeholder": "Standaard: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/pl/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Średni",
 			"low": "Niski"
 		},
-		"setReasoningLevel": "Włącz wysiłek rozumowania"
+		"setReasoningLevel": "Włącz wysiłek rozumowania",
+		"claudeCode": {
+			"pathLabel": "Ścieżka Claude Code",
+			"description": "Opcjonalna ścieżka do Twojego CLI Claude Code. Domyślnie 'claude', jeśli nie ustawiono.",
+			"placeholder": "Domyślnie: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Médio",
 			"low": "Baixo"
 		},
-		"setReasoningLevel": "Habilitar esforço de raciocínio"
+		"setReasoningLevel": "Habilitar esforço de raciocínio",
+		"claudeCode": {
+			"pathLabel": "Caminho do Claude Code",
+			"description": "Caminho opcional para o seu Claude Code CLI. O padrão é 'claude' se não for definido.",
+			"placeholder": "Padrão: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/ru/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Средние",
 			"low": "Низкие"
 		},
-		"setReasoningLevel": "Включить усилие рассуждения"
+		"setReasoningLevel": "Включить усилие рассуждения",
+		"claudeCode": {
+			"pathLabel": "Путь к Claude Code",
+			"description": "Необязательный путь к вашему Claude Code CLI. По умолчанию используется 'claude', если не установлено.",
+			"placeholder": "По умолчанию: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/tr/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Orta",
 			"low": "Düşük"
 		},
-		"setReasoningLevel": "Akıl Yürütme Çabasını Etkinleştir"
+		"setReasoningLevel": "Akıl Yürütme Çabasını Etkinleştir",
+		"claudeCode": {
+			"pathLabel": "Claude Code Yolu",
+			"description": "Claude Code CLI'nize isteğe bağlı yol. Ayarlanmazsa varsayılan olarak 'claude' kullanılır.",
+			"placeholder": "Varsayılan: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/vi/settings.json

@@ -303,7 +303,12 @@
 			"medium": "Trung bình",
 			"low": "Thấp"
 		},
-		"setReasoningLevel": "Kích hoạt nỗ lực suy luận"
+		"setReasoningLevel": "Kích hoạt nỗ lực suy luận",
+		"claudeCode": {
+			"pathLabel": "Đường dẫn Claude Code",
+			"description": "Đường dẫn tùy chọn đến Claude Code CLI của bạn. Mặc định là 'claude' nếu không được đặt.",
+			"placeholder": "Mặc định: claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -303,7 +303,12 @@
 			"medium": "中",
 			"low": "低"
 		},
-		"setReasoningLevel": "启用推理工作量"
+		"setReasoningLevel": "启用推理工作量",
+		"claudeCode": {
+			"pathLabel": "Claude Code 路径",
+			"description": "您的 Claude Code CLI 的可选路径。如果未设置,则默认为 “claude”。",
+			"placeholder": "默认:claude"
+		}
 	},
 	"browser": {
 		"enable": {

+ 6 - 1
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -303,7 +303,12 @@
 			"medium": "中",
 			"low": "低"
 		},
-		"setReasoningLevel": "啟用推理工作量"
+		"setReasoningLevel": "啟用推理工作量",
+		"claudeCode": {
+			"pathLabel": "Claude Code 路徑",
+			"description": "可選的 Claude Code CLI 路徑。如果未設定,則預設為 'claude'。",
+			"placeholder": "預設:claude"
+		}
 	},
 	"browser": {
 		"enable": {