diff --git a/.changeset/brave-moons-refuse.md b/.changeset/brave-moons-refuse.md new file mode 100644 index 0000000000..8c2a8c7397 --- /dev/null +++ b/.changeset/brave-moons-refuse.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Remove the non-functional "Use compact prompt" toggle from LM Studio provider settings diff --git a/apps/vscode/proto/cline/state.proto b/apps/vscode/proto/cline/state.proto index 435dbb811b..4af1edbf47 100644 --- a/apps/vscode/proto/cline/state.proto +++ b/apps/vscode/proto/cline/state.proto @@ -112,6 +112,8 @@ message Secrets { // in src/shared/storage/state-keys.ts and use the scripts/generate-state-proto.mjs // script to regenerate this list. message Settings { + reserved 150; // was custom_prompt (removed - compact prompt setting no longer supported) + reserved "custom_prompt"; optional string lite_llm_base_url = 1; optional bool lite_llm_use_prompt_cache = 2; optional string anthropic_base_url = 4; @@ -254,7 +256,6 @@ message Settings { optional string preferred_language = 145; optional PlanActMode mode = 147; optional FocusChainSettings focus_chain_settings = 149; - optional string custom_prompt = 150; optional bool hooks_enabled = 152; optional bool subagents_enabled = 153; optional bool background_edit_enabled = 155; @@ -392,7 +393,8 @@ message UpdateSettingsRequest { reserved 38; // was skills_enabled (removed - now always enabled) reserved 43; // was lazy_teammate_mode_enabled (removed) reserved 12; // was terminal_output_line_limit (removed; SDK command output limits are character-based) - reserved "native_tool_call_enabled", "cline_web_tools_enabled", "enable_parallel_tool_calling", "double_check_completion_enabled"; + reserved 19; // was custom_prompt (removed - compact prompt setting no longer supported) + reserved "native_tool_call_enabled", "cline_web_tools_enabled", "enable_parallel_tool_calling", "double_check_completion_enabled", "custom_prompt"; Metadata metadata = 1; optional ModelsApiConfiguration api_configuration = 2; @@ -409,7 +411,6 @@ message UpdateSettingsRequest { reserved 16; // was strict_plan_mode_enabled (removed) optional FocusChainSettings focus_chain_settings = 17; optional bool use_auto_condense = 18; - optional string custom_prompt = 19; optional BrowserSettingsUpdate browser_settings = 20; optional string default_terminal_profile = 21; optional bool yolo_mode_toggled = 22; diff --git a/apps/vscode/scripts/generate-state-proto.mjs b/apps/vscode/scripts/generate-state-proto.mjs index 32875fb0c2..f70ad0a1b6 100644 --- a/apps/vscode/scripts/generate-state-proto.mjs +++ b/apps/vscode/scripts/generate-state-proto.mjs @@ -297,28 +297,86 @@ function parseProtoMessageFieldNumbers(protoContent, messageName) { } /** - * Load field number mappings from existing proto file + * Parse `reserved` statements from an existing proto message definition so they + * survive regeneration (removed fields must keep their numbers/names reserved). + */ +function parseProtoMessageReservedStatements(protoContent, messageName) { + const messageRegex = new RegExp(`message\\s+${messageName}\\s*\\{([^}]*(?:\\{[^}]*\\}[^}]*)*)\\}`, "s") + const match = protoContent.match(messageRegex) + + if (!match) { + return [] + } + + return [...match[1].matchAll(/^[ \t]*(reserved\b[^\n]*)$/gm)].map((m) => m[1].trim()) +} + +/** + * Extract the set of field numbers covered by `reserved` statements + * (single numbers, comma lists, and `N to M` ranges; name reservations are ignored). + */ +function parseReservedFieldNumbers(reservedStatements) { + const reservedNumbers = new Set() + + for (const statement of reservedStatements) { + // Strip the keyword, trailing semicolon, and any comment + const body = statement + .replace(/^reserved\b/, "") + .replace(/;.*$/, "") + .trim() + + // Name reservations (quoted) carry no numbers + if (body.includes('"') || body.includes("'")) { + continue + } + + for (const part of body.split(",")) { + const rangeMatch = part.trim().match(/^(\d+)\s+to\s+(\d+|max)$/) + if (rangeMatch) { + const start = Number.parseInt(rangeMatch[1], 10) + // "to max" reserves everything upward; represent with Infinity + const end = rangeMatch[2] === "max" ? Number.POSITIVE_INFINITY : Number.parseInt(rangeMatch[2], 10) + if (end === Number.POSITIVE_INFINITY) { + throw new Error(`"reserved ${part.trim()}" leaves no field numbers available for new fields`) + } + for (let n = start; n <= end; n++) { + reservedNumbers.add(n) + } + } else if (/^\d+$/.test(part.trim())) { + reservedNumbers.add(Number.parseInt(part.trim(), 10)) + } + } + } + + return reservedNumbers +} + +/** + * Load field number mappings and reserved statements from existing proto file */ async function loadFieldNumbersFromProto() { try { const protoContent = await fs.readFile(STATE_PROTO_PATH, "utf-8") const secrets = parseProtoMessageFieldNumbers(protoContent, "Secrets") const settings = parseProtoMessageFieldNumbers(protoContent, "Settings") + const secretsReserved = parseProtoMessageReservedStatements(protoContent, "Secrets") + const settingsReserved = parseProtoMessageReservedStatements(protoContent, "Settings") console.log(` Found ${Object.keys(secrets).length} existing Secrets fields`) console.log(` Found ${Object.keys(settings).length} existing Settings fields`) - return { Secrets: secrets, Settings: settings } + return { Secrets: secrets, Settings: settings, SecretsReserved: secretsReserved, SettingsReserved: settingsReserved } } catch { // Proto file doesn't exist, start fresh - return { Secrets: {}, Settings: {} } + return { Secrets: {}, Settings: {}, SecretsReserved: [], SettingsReserved: [] } } } /** - * Assign field numbers, preserving existing assignments and adding new ones + * Assign field numbers, preserving existing assignments and adding new ones. + * Reserved numbers are never handed out to new fields. */ -function assignFieldNumbers(fields, existingNumbers, startNumber = 1) { +function assignFieldNumbers(fields, existingNumbers, startNumber = 1, reservedNumbers = new Set()) { const result = {} let nextNumber = startNumber @@ -347,13 +405,21 @@ function assignFieldNumbers(fields, existingNumbers, startNumber = 1) { const existingFieldNumber = normalizedFieldNumber ?? rawFieldNumber if (existingFieldNumber !== undefined) { + if (reservedNumbers.has(existingFieldNumber)) { + throw new Error( + `Field "${field.name}" uses number ${existingFieldNumber}, which is also covered by a reserved statement. Remove the field or the reservation.`, + ) + } result[field.name] = existingFieldNumber } } - // Assign new numbers for new fields + // Assign new numbers for new fields, skipping reserved numbers for (const field of fields) { if (result[field.name] === undefined) { + while (reservedNumbers.has(nextNumber)) { + nextNumber++ + } result[field.name] = nextNumber++ } } @@ -364,9 +430,14 @@ function assignFieldNumbers(fields, existingNumbers, startNumber = 1) { /** * Generate proto message definition */ -function generateProtoMessage(messageName, fields, fieldNumbers) { +function generateProtoMessage(messageName, fields, fieldNumbers, reservedStatements = []) { const lines = [`message ${messageName} {`] + // Re-emit reserved statements so removed field numbers/names stay reserved + for (const statement of reservedStatements) { + lines.push(` ${statement}`) + } + // Sort fields by field number for consistent output const sortedFields = [...fields].sort((a, b) => fieldNumbers[a.name] - fieldNumbers[b.name]) @@ -385,13 +456,13 @@ function generateProtoMessage(messageName, fields, fieldNumbers) { /** * Generate Secrets message from SECRETS_KEYS */ -function generateSecretsMessage(secretsKeys, fieldNumbers) { +function generateSecretsMessage(secretsKeys, fieldNumbers, reservedStatements) { const fields = secretsKeys.map((key) => ({ name: key, protoType: "string", })) - return generateProtoMessage("Secrets", fields, fieldNumbers) + return generateProtoMessage("Secrets", fields, fieldNumbers, reservedStatements) } /** @@ -430,17 +501,28 @@ async function main() { // Load existing field numbers from proto file const existingFieldNumbers = await loadFieldNumbersFromProto() - // Assign field numbers (preserving existing, adding new ones) + // Assign field numbers (preserving existing, adding new ones, never reusing reserved ones) const secretsFieldNumbers = assignFieldNumbers( secretsKeys.map((k) => ({ name: k })), existingFieldNumbers.Secrets, 1, + parseReservedFieldNumbers(existingFieldNumbers.SecretsReserved), + ) + const settingsFieldNumbers = assignFieldNumbers( + settingsFields, + existingFieldNumbers.Settings, + 1, + parseReservedFieldNumbers(existingFieldNumbers.SettingsReserved), ) - const settingsFieldNumbers = assignFieldNumbers(settingsFields, existingFieldNumbers.Settings, 1) // Generate messages - const secretsMessage = generateSecretsMessage(secretsKeys, secretsFieldNumbers) - const settingsMessage = generateProtoMessage("Settings", settingsFields, settingsFieldNumbers) + const secretsMessage = generateSecretsMessage(secretsKeys, secretsFieldNumbers, existingFieldNumbers.SecretsReserved) + const settingsMessage = generateProtoMessage( + "Settings", + settingsFields, + settingsFieldNumbers, + existingFieldNumbers.SettingsReserved, + ) // Read existing proto file let protoContent = await fs.readFile(STATE_PROTO_PATH, "utf-8") diff --git a/apps/vscode/src/core/api/index.ts b/apps/vscode/src/core/api/index.ts index 5f13dcd24d..cc443c9916 100644 --- a/apps/vscode/src/core/api/index.ts +++ b/apps/vscode/src/core/api/index.ts @@ -19,5 +19,4 @@ export interface ApiProviderInfo { providerId: string model: ApiHandlerModel mode: Mode - customPrompt?: string // "compact" } diff --git a/apps/vscode/src/core/controller/state/getStateToPostToWebview.ts b/apps/vscode/src/core/controller/state/getStateToPostToWebview.ts index 65889e71e4..e6fc131be6 100644 --- a/apps/vscode/src/core/controller/state/getStateToPostToWebview.ts +++ b/apps/vscode/src/core/controller/state/getStateToPostToWebview.ts @@ -63,7 +63,6 @@ export async function getStateToPostToWebview(controller: { const isNewUser = stateManager.getGlobalStateKey("isNewUser") const welcomeViewCompleted = !!stateManager.getGlobalStateKey("welcomeViewCompleted") - const customPrompt = stateManager.getGlobalSettingsKey("customPrompt") const mcpResponsesCollapsed = stateManager.getGlobalStateKey("mcpResponsesCollapsed") const maxConsecutiveMistakes = stateManager.getGlobalSettingsKey("maxConsecutiveMistakes") const favoritedModelIds = stateManager.getGlobalStateKey("favoritedModelIds") @@ -152,7 +151,6 @@ export async function getStateToPostToWebview(controller: { onboardingModels, mcpResponsesCollapsed, maxConsecutiveMistakes, - customPrompt, taskHistory: processedTaskHistory, shouldShowAnnouncement, favoritedModelIds, diff --git a/apps/vscode/src/core/controller/state/updateSettings.ts b/apps/vscode/src/core/controller/state/updateSettings.ts index b118cb22b5..57824bd226 100644 --- a/apps/vscode/src/core/controller/state/updateSettings.ts +++ b/apps/vscode/src/core/controller/state/updateSettings.ts @@ -188,12 +188,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett setCompactionStrategyGlobally(strategy) } - // Update custom prompt choice - if (request.customPrompt !== undefined) { - const value = request.customPrompt === "compact" ? "compact" : undefined - controller.stateManager.setGlobalState("customPrompt", value) - } - // Update browser settings if (request.browserSettings !== undefined) { // Get current browser settings to preserve fields not in the request diff --git a/apps/vscode/src/core/controller/state/updateSettingsCli.ts b/apps/vscode/src/core/controller/state/updateSettingsCli.ts index 35270c69fa..544ef66883 100644 --- a/apps/vscode/src/core/controller/state/updateSettingsCli.ts +++ b/apps/vscode/src/core/controller/state/updateSettingsCli.ts @@ -37,7 +37,6 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS planModeReasoningEffort, actModeReasoningEffort, mode, - customPrompt, planModeApiProvider, actModeApiProvider, // Fields requiring special logic (telemetry, merging, etc.) @@ -96,10 +95,6 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS controller.stateManager.setGlobalState("mode", converted) } - if (customPrompt === "compact") { - controller.stateManager.setGlobalState("customPrompt", "compact") - } - if (planModeApiProvider !== undefined) { const converted = convertProtoToApiProvider(planModeApiProvider) controller.stateManager.setGlobalState("planModeApiProvider", converted) diff --git a/apps/vscode/src/core/controller/state/updateTaskSettings.ts b/apps/vscode/src/core/controller/state/updateTaskSettings.ts index 08889f3acd..a1c98271f7 100644 --- a/apps/vscode/src/core/controller/state/updateTaskSettings.ts +++ b/apps/vscode/src/core/controller/state/updateTaskSettings.ts @@ -37,7 +37,6 @@ export async function updateTaskSettings(controller: Controller, request: Update planModeReasoningEffort, actModeReasoningEffort, mode, - customPrompt, planModeApiProvider, actModeApiProvider, // Fields requiring special logic @@ -87,10 +86,6 @@ export async function updateTaskSettings(controller: Controller, request: Update controller.stateManager.setTaskSettings(taskId, "mode", converted) } - if (customPrompt === "compact") { - controller.stateManager.setTaskSettings(taskId, "customPrompt", "compact") - } - if (planModeApiProvider !== undefined) { const converted = convertProtoToApiProvider(planModeApiProvider) controller.stateManager.setTaskSettings(taskId, "planModeApiProvider", converted) diff --git a/apps/vscode/src/core/controller/task/newTask.ts b/apps/vscode/src/core/controller/task/newTask.ts index 7431a5d752..0e53825843 100644 --- a/apps/vscode/src/core/controller/task/newTask.ts +++ b/apps/vscode/src/core/controller/task/newTask.ts @@ -60,9 +60,6 @@ export async function newTask(controller: Controller, request: NewTaskRequest): ...(request.taskSettings?.mode !== undefined && { mode: convertPlanActMode(request.taskSettings.mode), }), - ...(request.taskSettings?.customPrompt === "compact" && { - customPrompt: "compact", - }), ...(request.taskSettings?.planModeApiProvider !== undefined && { planModeApiProvider: convertProtoToApiProvider(request.taskSettings.planModeApiProvider), }), diff --git a/apps/vscode/src/shared/ExtensionMessage.ts b/apps/vscode/src/shared/ExtensionMessage.ts index 26c0434349..28ac65cdcb 100644 --- a/apps/vscode/src/shared/ExtensionMessage.ts +++ b/apps/vscode/src/shared/ExtensionMessage.ts @@ -117,7 +117,6 @@ export interface ExtensionState { compactionStrategy?: string subagentsEnabled?: boolean worktreesEnabled?: ClineFeatureSetting - customPrompt?: string favoritedModelIds: string[] // NEW: Add workspace information workspaceRoots: WorkspaceRoot[] diff --git a/apps/vscode/src/shared/storage/state-keys.ts b/apps/vscode/src/shared/storage/state-keys.ts index f8045b1462..aa1ec1112a 100644 --- a/apps/vscode/src/shared/storage/state-keys.ts +++ b/apps/vscode/src/shared/storage/state-keys.ts @@ -282,7 +282,6 @@ const USER_SETTINGS_FIELDS = { preferredLanguage: { default: "English" as string }, mode: { default: "act" as Mode }, focusChainSettings: { default: DEFAULT_FOCUS_CHAIN_SETTINGS as FocusChainSettings }, - customPrompt: { default: undefined as "compact" | undefined }, backgroundEditEnabled: { default: false as boolean }, optOutOfRemoteConfig: { default: false as boolean }, showFeatureTips: { default: true as boolean }, diff --git a/apps/vscode/webview-ui/src/components/settings/UseCustomPromptCheckbox.tsx b/apps/vscode/webview-ui/src/components/settings/UseCustomPromptCheckbox.tsx deleted file mode 100644 index eac4d5c7f7..0000000000 --- a/apps/vscode/webview-ui/src/components/settings/UseCustomPromptCheckbox.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" -import React, { useCallback, useState } from "react" -import { useExtensionState } from "@/context/ExtensionStateContext" -import { updateSetting } from "./utils/settingsHandlers" - -interface CustomPromptCheckboxProps { - providerId: string -} - -/** - * Checkbox to enable or disable the use of a compact prompt for local models providers. - */ -const UseCustomPromptCheckbox: React.FC = ({ providerId }) => { - const { customPrompt } = useExtensionState() - const [isCompactPromptEnabled, setIsCompactPromptEnabled] = useState(customPrompt === "compact") - - const toggleCompactPrompt = useCallback((isChecked: boolean) => { - setIsCompactPromptEnabled(isChecked) - updateSetting("customPrompt", isChecked ? "compact" : "") - }, []) - - return ( -
- toggleCompactPrompt(!isCompactPromptEnabled)}> - Use compact prompt - -
- A system prompt optimized for smaller context window (e.g. 8k or less). -
- - Does not support Mcp and Focus Chain -
-
-
- ) -} - -export default UseCustomPromptCheckbox diff --git a/apps/vscode/webview-ui/src/components/settings/providers/LMStudioProvider.tsx b/apps/vscode/webview-ui/src/components/settings/providers/LMStudioProvider.tsx index bcaddb2ccb..f7404fc981 100644 --- a/apps/vscode/webview-ui/src/components/settings/providers/LMStudioProvider.tsx +++ b/apps/vscode/webview-ui/src/components/settings/providers/LMStudioProvider.tsx @@ -3,7 +3,6 @@ import type { Mode } from "@shared/storage/types" import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { useCallback, useEffect, useMemo, useState } from "react" import { useInterval } from "react-use" -import UseCustomPromptCheckbox from "@/components/settings/UseCustomPromptCheckbox" import { useExtensionState } from "@/context/ExtensionStateContext" import { useProviderConfig } from "@/hooks/useProviderConfig" import { useProviderModelSelection } from "@/hooks/useProviderModelSelection" @@ -194,8 +193,6 @@ export const LMStudioProvider = ({ currentMode }: LMStudioProviderProps) => { value={String(currentLoadedContext ?? lmStudioMaxTokens ?? "0")} /> - -
LM Studio allows you to run models locally on your computer. For instructions on how to get started, see their diff --git a/apps/vscode/webview-ui/src/context/ExtensionStateContext.tsx b/apps/vscode/webview-ui/src/context/ExtensionStateContext.tsx index bb945e0cc7..ad6542486d 100644 --- a/apps/vscode/webview-ui/src/context/ExtensionStateContext.tsx +++ b/apps/vscode/webview-ui/src/context/ExtensionStateContext.tsx @@ -299,7 +299,6 @@ export const ExtensionStateContextProvider: React.FC<{ onboardingModels: undefined, mcpResponsesCollapsed: false, // Default value (expanded), will be overwritten by extension state yoloModeToggled: false, - customPrompt: undefined, useAutoCondense: false, compactionStrategy: "basic", subagentsEnabled: false,