refactor: separate mode and support prompts

- Rename customPrompts to customModePrompts for mode-specific prompts
- Add new customSupportPrompts type for support action prompts
- Update types to be more specific (CustomModePrompts and CustomSupportPrompts)
- Fix all related tests and component implementations
This commit is contained in:
sam hoang
2025-01-24 01:46:33 +07:00
parent 085d42873c
commit f86e96d157
13 changed files with 119 additions and 99 deletions

View File

@@ -809,7 +809,7 @@ export class Cline {
}) })
} }
const { browserViewportSize, mode, customPrompts, preferredLanguage } = const { browserViewportSize, mode, customModePrompts, preferredLanguage } =
(await this.providerRef.deref()?.getState()) ?? {} (await this.providerRef.deref()?.getState()) ?? {}
const { customModes } = (await this.providerRef.deref()?.getState()) ?? {} const { customModes } = (await this.providerRef.deref()?.getState()) ?? {}
const systemPrompt = await (async () => { const systemPrompt = await (async () => {
@@ -825,7 +825,7 @@ export class Cline {
this.diffStrategy, this.diffStrategy,
browserViewportSize, browserViewportSize,
mode, mode,
customPrompts, customModePrompts,
customModes, customModes,
this.customInstructions, this.customInstructions,
preferredLanguage, preferredLanguage,

View File

@@ -162,7 +162,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )
@@ -178,7 +178,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
"1280x800", // browserViewportSize "1280x800", // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )
@@ -196,7 +196,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )
@@ -212,7 +212,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )
@@ -228,7 +228,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
"900x600", // different viewport size "900x600", // different viewport size
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )
@@ -244,7 +244,7 @@ describe("SYSTEM_PROMPT", () => {
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
undefined, // globalCustomInstructions undefined, // globalCustomInstructions
undefined, // preferredLanguage undefined, // preferredLanguage
@@ -264,7 +264,7 @@ describe("SYSTEM_PROMPT", () => {
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
undefined, // globalCustomInstructions undefined, // globalCustomInstructions
undefined, // preferredLanguage undefined, // preferredLanguage
@@ -284,7 +284,7 @@ describe("SYSTEM_PROMPT", () => {
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
undefined, // globalCustomInstructions undefined, // globalCustomInstructions
undefined, // preferredLanguage undefined, // preferredLanguage
@@ -304,7 +304,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
defaultModeSlug, // mode defaultModeSlug, // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
undefined, // globalCustomInstructions undefined, // globalCustomInstructions
"Spanish", // preferredLanguage "Spanish", // preferredLanguage
@@ -334,7 +334,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
"custom-mode", // mode "custom-mode", // mode
undefined, // customPrompts undefined, // customModePrompts
customModes, // customModes customModes, // customModes
"Global instructions", // globalCustomInstructions "Global instructions", // globalCustomInstructions
) )
@@ -351,7 +351,7 @@ describe("SYSTEM_PROMPT", () => {
}) })
it("should use promptComponent roleDefinition when available", async () => { it("should use promptComponent roleDefinition when available", async () => {
const customPrompts = { const customModePrompts = {
[defaultModeSlug]: { [defaultModeSlug]: {
roleDefinition: "Custom prompt role definition", roleDefinition: "Custom prompt role definition",
customInstructions: "Custom prompt instructions", customInstructions: "Custom prompt instructions",
@@ -366,7 +366,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, undefined,
undefined, undefined,
defaultModeSlug, defaultModeSlug,
customPrompts, customModePrompts,
undefined, undefined,
) )
@@ -377,7 +377,7 @@ describe("SYSTEM_PROMPT", () => {
}) })
it("should fallback to modeConfig roleDefinition when promptComponent has no roleDefinition", async () => { it("should fallback to modeConfig roleDefinition when promptComponent has no roleDefinition", async () => {
const customPrompts = { const customModePrompts = {
[defaultModeSlug]: { [defaultModeSlug]: {
customInstructions: "Custom prompt instructions", customInstructions: "Custom prompt instructions",
// No roleDefinition provided // No roleDefinition provided
@@ -392,7 +392,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, undefined,
undefined, undefined,
defaultModeSlug, defaultModeSlug,
customPrompts, customModePrompts,
undefined, undefined,
) )
@@ -432,7 +432,7 @@ describe("addCustomInstructions", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
"architect", // mode "architect", // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )
@@ -448,7 +448,7 @@ describe("addCustomInstructions", () => {
undefined, // diffStrategy undefined, // diffStrategy
undefined, // browserViewportSize undefined, // browserViewportSize
"ask", // mode "ask", // mode
undefined, // customPrompts undefined, // customModePrompts
undefined, // customModes undefined, // customModes
) )

View File

@@ -1,7 +1,7 @@
import { import {
Mode, Mode,
modes, modes,
CustomPrompts, CustomModePrompts,
PromptComponent, PromptComponent,
getRoleDefinition, getRoleDefinition,
defaultModeSlug, defaultModeSlug,
@@ -97,7 +97,7 @@ export const SYSTEM_PROMPT = async (
diffStrategy?: DiffStrategy, diffStrategy?: DiffStrategy,
browserViewportSize?: string, browserViewportSize?: string,
mode: Mode = defaultModeSlug, mode: Mode = defaultModeSlug,
customPrompts?: CustomPrompts, customModePrompts?: CustomModePrompts,
customModes?: ModeConfig[], customModes?: ModeConfig[],
globalCustomInstructions?: string, globalCustomInstructions?: string,
preferredLanguage?: string, preferredLanguage?: string,
@@ -115,7 +115,7 @@ export const SYSTEM_PROMPT = async (
} }
// Check if it's a custom mode // Check if it's a custom mode
const promptComponent = getPromptComponent(customPrompts?.[mode]) const promptComponent = getPromptComponent(customModePrompts?.[mode])
// Get full mode config from custom modes or fall back to built-in modes // Get full mode config from custom modes or fall back to built-in modes
const currentMode = getModeBySlug(mode, customModes) || modes.find((m) => m.slug === mode) || modes[0] const currentMode = getModeBySlug(mode, customModes) || modes.find((m) => m.slug === mode) || modes[0]

View File

@@ -22,7 +22,7 @@ import { WebviewMessage } from "../../shared/WebviewMessage"
import { import {
Mode, Mode,
modes, modes,
CustomPrompts, CustomModePrompts,
PromptComponent, PromptComponent,
ModeConfig, ModeConfig,
defaultModeSlug, defaultModeSlug,
@@ -40,7 +40,7 @@ import { singleCompletionHandler } from "../../utils/single-completion-handler"
import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git" import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git"
import { ConfigManager } from "../config/ConfigManager" import { ConfigManager } from "../config/ConfigManager"
import { CustomModesManager } from "../config/CustomModesManager" import { CustomModesManager } from "../config/CustomModesManager"
import { supportPrompt } from "../../shared/support-prompt" import { CustomSupportPrompts, supportPrompt } from "../../shared/support-prompt"
import { ACTION_NAMES } from "../CodeActionProvider" import { ACTION_NAMES } from "../CodeActionProvider"
@@ -111,7 +111,8 @@ type GlobalStateKey =
| "vsCodeLmModelSelector" | "vsCodeLmModelSelector"
| "mode" | "mode"
| "modeApiConfigs" | "modeApiConfigs"
| "customPrompts" | "customModePrompts"
| "customSupportPrompts"
| "enhancementApiConfigId" | "enhancementApiConfigId"
| "experimentalDiffStrategy" | "experimentalDiffStrategy"
| "autoApprovalEnabled" | "autoApprovalEnabled"
@@ -203,9 +204,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
return return
} }
const { customPrompts } = await visibleProvider.getState() const { customSupportPrompts } = await visibleProvider.getState()
const prompt = supportPrompt.create(promptType, params, customPrompts) const prompt = supportPrompt.create(promptType, params, customSupportPrompts)
await visibleProvider.initClineWithTask(prompt) await visibleProvider.initClineWithTask(prompt)
} }
@@ -296,7 +297,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.clearTask() await this.clearTask()
const { const {
apiConfiguration, apiConfiguration,
customPrompts, customModePrompts,
diffEnabled, diffEnabled,
fuzzyMatchThreshold, fuzzyMatchThreshold,
mode, mode,
@@ -304,7 +305,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
experimentalDiffStrategy, experimentalDiffStrategy,
} = await this.getState() } = await this.getState()
const modePrompt = customPrompts?.[mode] as PromptComponent const modePrompt = customModePrompts?.[mode] as PromptComponent
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
this.cline = new Cline( this.cline = new Cline(
@@ -324,7 +325,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.clearTask() await this.clearTask()
const { const {
apiConfiguration, apiConfiguration,
customPrompts, customModePrompts,
diffEnabled, diffEnabled,
fuzzyMatchThreshold, fuzzyMatchThreshold,
mode, mode,
@@ -332,7 +333,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
experimentalDiffStrategy, experimentalDiffStrategy,
} = await this.getState() } = await this.getState()
const modePrompt = customPrompts?.[mode] as PromptComponent const modePrompt = customModePrompts?.[mode] as PromptComponent
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
this.cline = new Cline( this.cline = new Cline(
@@ -817,14 +818,14 @@ export class ClineProvider implements vscode.WebviewViewProvider {
return return
} }
const existingPrompts = (await this.getGlobalState("customPrompts")) || {} const existingPrompts = (await this.getGlobalState("customSupportPrompts")) || {}
const updatedPrompts = { const updatedPrompts = {
...existingPrompts, ...existingPrompts,
...message.values, ...message.values,
} }
await this.updateGlobalState("customPrompts", updatedPrompts) await this.updateGlobalState("customSupportPrompts", updatedPrompts)
await this.postStateToWebview() await this.postStateToWebview()
} catch (error) { } catch (error) {
console.error("Error update support prompt:", error) console.error("Error update support prompt:", error)
@@ -837,10 +838,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
return return
} }
const existingPrompts = ((await this.getGlobalState("customPrompts")) || {}) as Record< const existingPrompts = ((await this.getGlobalState("customSupportPrompts")) ||
string, {}) as Record<string, any>
any
>
const updatedPrompts = { const updatedPrompts = {
...existingPrompts, ...existingPrompts,
@@ -848,7 +847,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
updatedPrompts[message.text] = undefined updatedPrompts[message.text] = undefined
await this.updateGlobalState("customPrompts", updatedPrompts) await this.updateGlobalState("customSupportPrompts", updatedPrompts)
await this.postStateToWebview() await this.postStateToWebview()
} catch (error) { } catch (error) {
console.error("Error reset support prompt:", error) console.error("Error reset support prompt:", error)
@@ -857,21 +856,21 @@ export class ClineProvider implements vscode.WebviewViewProvider {
break break
case "updatePrompt": case "updatePrompt":
if (message.promptMode && message.customPrompt !== undefined) { if (message.promptMode && message.customPrompt !== undefined) {
const existingPrompts = (await this.getGlobalState("customPrompts")) || {} const existingPrompts = (await this.getGlobalState("customModePrompts")) || {}
const updatedPrompts = { const updatedPrompts = {
...existingPrompts, ...existingPrompts,
[message.promptMode]: message.customPrompt, [message.promptMode]: message.customPrompt,
} }
await this.updateGlobalState("customPrompts", updatedPrompts) await this.updateGlobalState("customModePrompts", updatedPrompts)
// Get current state and explicitly include customPrompts // Get current state and explicitly include customModePrompts
const currentState = await this.getState() const currentState = await this.getState()
const stateWithPrompts = { const stateWithPrompts = {
...currentState, ...currentState,
customPrompts: updatedPrompts, customModePrompts: updatedPrompts,
} }
// Post state with prompts // Post state with prompts
@@ -981,8 +980,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
case "enhancePrompt": case "enhancePrompt":
if (message.text) { if (message.text) {
try { try {
const { apiConfiguration, customPrompts, listApiConfigMeta, enhancementApiConfigId } = const {
await this.getState() apiConfiguration,
customSupportPrompts,
listApiConfigMeta,
enhancementApiConfigId,
} = await this.getState()
// Try to get enhancement config first, fall back to current config // Try to get enhancement config first, fall back to current config
let configToUse: ApiConfiguration = apiConfiguration let configToUse: ApiConfiguration = apiConfiguration
@@ -1003,7 +1006,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
{ {
userInput: message.text, userInput: message.text,
}, },
customPrompts, customSupportPrompts,
), ),
) )
@@ -1024,7 +1027,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
try { try {
const { const {
apiConfiguration, apiConfiguration,
customPrompts, customModePrompts,
customInstructions, customInstructions,
preferredLanguage, preferredLanguage,
browserViewportSize, browserViewportSize,
@@ -1054,7 +1057,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
diffStrategy, diffStrategy,
browserViewportSize ?? "900x600", browserViewportSize ?? "900x600",
mode, mode,
customPrompts, customModePrompts,
customModes, customModes,
customInstructions, customInstructions,
preferredLanguage, preferredLanguage,
@@ -1802,7 +1805,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
currentApiConfigName, currentApiConfigName,
listApiConfigMeta, listApiConfigMeta,
mode, mode,
customPrompts, customModePrompts,
customSupportPrompts,
enhancementApiConfigId, enhancementApiConfigId,
experimentalDiffStrategy, experimentalDiffStrategy,
autoApprovalEnabled, autoApprovalEnabled,
@@ -1841,7 +1845,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
currentApiConfigName: currentApiConfigName ?? "default", currentApiConfigName: currentApiConfigName ?? "default",
listApiConfigMeta: listApiConfigMeta ?? [], listApiConfigMeta: listApiConfigMeta ?? [],
mode: mode ?? defaultModeSlug, mode: mode ?? defaultModeSlug,
customPrompts: customPrompts ?? {}, customModePrompts: customModePrompts ?? {},
customSupportPrompts: customSupportPrompts ?? {},
enhancementApiConfigId, enhancementApiConfigId,
experimentalDiffStrategy: experimentalDiffStrategy ?? false, experimentalDiffStrategy: experimentalDiffStrategy ?? false,
autoApprovalEnabled: autoApprovalEnabled ?? false, autoApprovalEnabled: autoApprovalEnabled ?? false,
@@ -1961,7 +1966,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
vsCodeLmModelSelector, vsCodeLmModelSelector,
mode, mode,
modeApiConfigs, modeApiConfigs,
customPrompts, customModePrompts,
customSupportPrompts,
enhancementApiConfigId, enhancementApiConfigId,
experimentalDiffStrategy, experimentalDiffStrategy,
autoApprovalEnabled, autoApprovalEnabled,
@@ -2026,7 +2032,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("vsCodeLmModelSelector") as Promise<vscode.LanguageModelChatSelector | undefined>, this.getGlobalState("vsCodeLmModelSelector") as Promise<vscode.LanguageModelChatSelector | undefined>,
this.getGlobalState("mode") as Promise<Mode | undefined>, this.getGlobalState("mode") as Promise<Mode | undefined>,
this.getGlobalState("modeApiConfigs") as Promise<Record<Mode, string> | undefined>, this.getGlobalState("modeApiConfigs") as Promise<Record<Mode, string> | undefined>,
this.getGlobalState("customPrompts") as Promise<CustomPrompts | undefined>, this.getGlobalState("customModePrompts") as Promise<CustomModePrompts | undefined>,
this.getGlobalState("customSupportPrompts") as Promise<CustomSupportPrompts | undefined>,
this.getGlobalState("enhancementApiConfigId") as Promise<string | undefined>, this.getGlobalState("enhancementApiConfigId") as Promise<string | undefined>,
this.getGlobalState("experimentalDiffStrategy") as Promise<boolean | undefined>, this.getGlobalState("experimentalDiffStrategy") as Promise<boolean | undefined>,
this.getGlobalState("autoApprovalEnabled") as Promise<boolean | undefined>, this.getGlobalState("autoApprovalEnabled") as Promise<boolean | undefined>,
@@ -2137,7 +2144,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
currentApiConfigName: currentApiConfigName ?? "default", currentApiConfigName: currentApiConfigName ?? "default",
listApiConfigMeta: listApiConfigMeta ?? [], listApiConfigMeta: listApiConfigMeta ?? [],
modeApiConfigs: modeApiConfigs ?? ({} as Record<Mode, string>), modeApiConfigs: modeApiConfigs ?? ({} as Record<Mode, string>),
customPrompts: customPrompts ?? {}, customModePrompts: customModePrompts ?? {},
customSupportPrompts: customSupportPrompts ?? {},
enhancementApiConfigId, enhancementApiConfigId,
experimentalDiffStrategy: experimentalDiffStrategy ?? false, experimentalDiffStrategy: experimentalDiffStrategy ?? false,
autoApprovalEnabled: autoApprovalEnabled ?? false, autoApprovalEnabled: autoApprovalEnabled ?? false,

View File

@@ -555,7 +555,7 @@ describe("ClineProvider", () => {
architect: "existing architect prompt", architect: "existing architect prompt",
} }
;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => { ;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => {
if (key === "customPrompts") { if (key === "customModePrompts") {
return existingPrompts return existingPrompts
} }
return undefined return undefined
@@ -569,7 +569,7 @@ describe("ClineProvider", () => {
}) })
// Verify state was updated correctly // Verify state was updated correctly
expect(mockContext.globalState.update).toHaveBeenCalledWith("customPrompts", { expect(mockContext.globalState.update).toHaveBeenCalledWith("customModePrompts", {
...existingPrompts, ...existingPrompts,
code: "new code prompt", code: "new code prompt",
}) })
@@ -579,7 +579,7 @@ describe("ClineProvider", () => {
expect.objectContaining({ expect.objectContaining({
type: "state", type: "state",
state: expect.objectContaining({ state: expect.objectContaining({
customPrompts: { customModePrompts: {
...existingPrompts, ...existingPrompts,
code: "new code prompt", code: "new code prompt",
}, },
@@ -588,17 +588,17 @@ describe("ClineProvider", () => {
) )
}) })
test("customPrompts defaults to empty object", async () => { test("customModePrompts defaults to empty object", async () => {
// Mock globalState.get to return undefined for customPrompts // Mock globalState.get to return undefined for customModePrompts
;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => { ;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => {
if (key === "customPrompts") { if (key === "customModePrompts") {
return undefined return undefined
} }
return null return null
}) })
const state = await provider.getState() const state = await provider.getState()
expect(state.customPrompts).toEqual({}) expect(state.customModePrompts).toEqual({})
}) })
test("uses mode-specific custom instructions in Cline initialization", async () => { test("uses mode-specific custom instructions in Cline initialization", async () => {
@@ -611,7 +611,7 @@ describe("ClineProvider", () => {
jest.spyOn(provider, "getState").mockResolvedValue({ jest.spyOn(provider, "getState").mockResolvedValue({
apiConfiguration: mockApiConfig, apiConfiguration: mockApiConfig,
customPrompts: { customModePrompts: {
code: { customInstructions: modeCustomInstructions }, code: { customInstructions: modeCustomInstructions },
}, },
mode: "code", mode: "code",
@@ -651,7 +651,7 @@ describe("ClineProvider", () => {
}, },
} }
mockContext.globalState.get = jest.fn((key: string) => { mockContext.globalState.get = jest.fn((key: string) => {
if (key === "customPrompts") { if (key === "customModePrompts") {
return existingPrompts return existingPrompts
} }
return undefined return undefined
@@ -668,7 +668,7 @@ describe("ClineProvider", () => {
}) })
// Verify state was updated correctly // Verify state was updated correctly
expect(mockContext.globalState.update).toHaveBeenCalledWith("customPrompts", { expect(mockContext.globalState.update).toHaveBeenCalledWith("customModePrompts", {
code: { code: {
roleDefinition: "Code role", roleDefinition: "Code role",
customInstructions: "New instructions", customInstructions: "New instructions",
@@ -978,7 +978,7 @@ describe("ClineProvider", () => {
apiModelId: "test-model", apiModelId: "test-model",
openRouterModelInfo: { supportsComputerUse: true }, openRouterModelInfo: { supportsComputerUse: true },
}, },
customPrompts: {}, customModePrompts: {},
mode: "code", mode: "code",
mcpEnabled: false, mcpEnabled: false,
browserViewportSize: "900x600", browserViewportSize: "900x600",
@@ -1007,7 +1007,7 @@ describe("ClineProvider", () => {
}), }),
"900x600", // browserViewportSize "900x600", // browserViewportSize
"code", // mode "code", // mode
{}, // customPrompts {}, // customModePrompts
{}, // customModes {}, // customModes
undefined, // effectiveInstructions undefined, // effectiveInstructions
undefined, // preferredLanguage undefined, // preferredLanguage
@@ -1027,7 +1027,7 @@ describe("ClineProvider", () => {
apiModelId: "test-model", apiModelId: "test-model",
openRouterModelInfo: { supportsComputerUse: true }, openRouterModelInfo: { supportsComputerUse: true },
}, },
customPrompts: {}, customModePrompts: {},
mode: "code", mode: "code",
mcpEnabled: false, mcpEnabled: false,
browserViewportSize: "900x600", browserViewportSize: "900x600",
@@ -1056,7 +1056,7 @@ describe("ClineProvider", () => {
}), }),
"900x600", // browserViewportSize "900x600", // browserViewportSize
"code", // mode "code", // mode
{}, // customPrompts {}, // customModePrompts
{}, // customModes {}, // customModes
undefined, // effectiveInstructions undefined, // effectiveInstructions
undefined, // preferredLanguage undefined, // preferredLanguage
@@ -1071,7 +1071,7 @@ describe("ClineProvider", () => {
apiProvider: "openrouter", apiProvider: "openrouter",
openRouterModelInfo: { supportsComputerUse: true }, openRouterModelInfo: { supportsComputerUse: true },
}, },
customPrompts: { customModePrompts: {
architect: { customInstructions: "Architect mode instructions" }, architect: { customInstructions: "Architect mode instructions" },
}, },
mode: "architect", mode: "architect",

View File

@@ -4,7 +4,8 @@ import { ApiConfiguration, ApiProvider, ModelInfo } from "./api"
import { HistoryItem } from "./HistoryItem" import { HistoryItem } from "./HistoryItem"
import { McpServer } from "./mcp" import { McpServer } from "./mcp"
import { GitCommit } from "../utils/git" import { GitCommit } from "../utils/git"
import { Mode, CustomPrompts, ModeConfig } from "./modes" import { Mode, CustomModePrompts, ModeConfig } from "./modes"
import { CustomSupportPrompts } from "./support-prompt"
export interface LanguageModelChatSelector { export interface LanguageModelChatSelector {
vendor?: string vendor?: string
@@ -82,7 +83,8 @@ export interface ExtensionState {
currentApiConfigName?: string currentApiConfigName?: string
listApiConfigMeta?: ApiConfigMeta[] listApiConfigMeta?: ApiConfigMeta[]
customInstructions?: string customInstructions?: string
customPrompts?: CustomPrompts customModePrompts?: CustomModePrompts
customSupportPrompts?: CustomSupportPrompts
alwaysAllowReadOnly?: boolean alwaysAllowReadOnly?: boolean
alwaysAllowWrite?: boolean alwaysAllowWrite?: boolean
alwaysAllowExecute?: boolean alwaysAllowExecute?: boolean

View File

@@ -97,18 +97,18 @@ describe("Code Action Prompts", () => {
it("should return custom template when provided", () => { it("should return custom template when provided", () => {
const customTemplate = "Custom template for explaining code" const customTemplate = "Custom template for explaining code"
const customPrompts = { const customSupportPrompts = {
EXPLAIN: customTemplate, EXPLAIN: customTemplate,
} }
const template = supportPrompt.get(customPrompts, "EXPLAIN") const template = supportPrompt.get(customSupportPrompts, "EXPLAIN")
expect(template).toBe(customTemplate) expect(template).toBe(customTemplate)
}) })
it("should return default template when custom prompts does not include type", () => { it("should return default template when custom prompts does not include type", () => {
const customPrompts = { const customSupportPrompts = {
SOMETHING_ELSE: "Other template", SOMETHING_ELSE: "Other template",
} }
const template = supportPrompt.get(customPrompts, "EXPLAIN") const template = supportPrompt.get(customSupportPrompts, "EXPLAIN")
expect(template).toBe(supportPrompt.default.EXPLAIN) expect(template).toBe(supportPrompt.default.EXPLAIN)
}) })
}) })
@@ -116,7 +116,7 @@ describe("Code Action Prompts", () => {
describe("create with custom prompts", () => { describe("create with custom prompts", () => {
it("should use custom template when provided", () => { it("should use custom template when provided", () => {
const customTemplate = "Custom template for ${filePath}" const customTemplate = "Custom template for ${filePath}"
const customPrompts = { const customSupportPrompts = {
EXPLAIN: customTemplate, EXPLAIN: customTemplate,
} }
@@ -126,7 +126,7 @@ describe("Code Action Prompts", () => {
filePath: testFilePath, filePath: testFilePath,
selectedText: testCode, selectedText: testCode,
}, },
customPrompts, customSupportPrompts,
) )
expect(prompt).toContain(`Custom template for ${testFilePath}`) expect(prompt).toContain(`Custom template for ${testFilePath}`)
@@ -134,7 +134,7 @@ describe("Code Action Prompts", () => {
}) })
it("should use default template when custom prompts does not include type", () => { it("should use default template when custom prompts does not include type", () => {
const customPrompts = { const customSupportPrompts = {
EXPLAIN: "Other template", EXPLAIN: "Other template",
} }
@@ -144,7 +144,7 @@ describe("Code Action Prompts", () => {
filePath: testFilePath, filePath: testFilePath,
selectedText: testCode, selectedText: testCode,
}, },
customPrompts, customSupportPrompts,
) )
expect(prompt).toContain("Other template") expect(prompt).toContain("Other template")

View File

@@ -18,8 +18,8 @@ export type PromptComponent = {
customInstructions?: string customInstructions?: string
} }
export type CustomPrompts = { export type CustomModePrompts = {
[key: string]: PromptComponent | undefined | string [key: string]: PromptComponent | undefined
} }
// Helper to get all tools for a mode // Helper to get all tools for a mode
@@ -141,7 +141,7 @@ export function isToolAllowedForMode(
} }
// Create the mode-specific default prompts // Create the mode-specific default prompts
export const defaultPrompts: Readonly<CustomPrompts> = Object.freeze( export const defaultPrompts: Readonly<CustomModePrompts> = Object.freeze(
Object.fromEntries(modes.map((mode) => [mode.slug, { roleDefinition: mode.roleDefinition }])), Object.fromEntries(modes.map((mode) => [mode.slug, { roleDefinition: mode.roleDefinition }])),
) )

View File

@@ -84,11 +84,11 @@ type SupportPromptType = keyof typeof defaultTemplates
export const supportPrompt = { export const supportPrompt = {
default: defaultTemplates, default: defaultTemplates,
get: (customPrompts: Record<string, any> | undefined, type: SupportPromptType): string => { get: (customSupportPrompts: Record<string, any> | undefined, type: SupportPromptType): string => {
return customPrompts?.[type] ?? defaultTemplates[type] return customSupportPrompts?.[type] ?? defaultTemplates[type]
}, },
create: (type: SupportPromptType, params: PromptParams, customPrompts?: Record<string, any>): string => { create: (type: SupportPromptType, params: PromptParams, customSupportPrompts?: Record<string, any>): string => {
const template = supportPrompt.get(customPrompts, type) const template = supportPrompt.get(customSupportPrompts, type)
return createPrompt(template, params) return createPrompt(template, params)
}, },
} as const } as const
@@ -102,3 +102,7 @@ export const supportPromptLabels: Record<SupportPromptType, string> = {
IMPROVE: "Improve Code", IMPROVE: "Improve Code",
ENHANCE: "Enhance Prompt", ENHANCE: "Enhance Prompt",
} as const } as const
export type CustomSupportPrompts = {
[key: string]: string | undefined
}

View File

@@ -1,7 +1,7 @@
import { render, fireEvent, screen } from "@testing-library/react" import { render, fireEvent, screen } from "@testing-library/react"
import { useExtensionState } from "../../../context/ExtensionStateContext" import { useExtensionState } from "../../../context/ExtensionStateContext"
import AutoApproveMenu from "../AutoApproveMenu" import AutoApproveMenu from "../AutoApproveMenu"
import { codeMode, defaultPrompts } from "../../../../../src/shared/modes" import { defaultModeSlug, defaultPrompts } from "../../../../../src/shared/modes"
// Mock the ExtensionStateContext hook // Mock the ExtensionStateContext hook
jest.mock("../../../context/ExtensionStateContext") jest.mock("../../../context/ExtensionStateContext")
@@ -29,8 +29,9 @@ describe("AutoApproveMenu", () => {
requestDelaySeconds: 5, requestDelaySeconds: 5,
currentApiConfigName: "default", currentApiConfigName: "default",
listApiConfigMeta: [], listApiConfigMeta: [],
mode: codeMode, mode: defaultModeSlug,
customPrompts: defaultPrompts, customModePrompts: defaultPrompts,
customSupportPrompts: {},
enhancementApiConfigId: "", enhancementApiConfigId: "",
didHydrateState: true, didHydrateState: true,
showWelcome: false, showWelcome: false,

View File

@@ -23,7 +23,8 @@ type PromptsViewProps = {
const PromptsView = ({ onDone }: PromptsViewProps) => { const PromptsView = ({ onDone }: PromptsViewProps) => {
const { const {
customPrompts, customModePrompts,
customSupportPrompts,
listApiConfigMeta, listApiConfigMeta,
enhancementApiConfigId, enhancementApiConfigId,
setEnhancementApiConfigId, setEnhancementApiConfigId,
@@ -50,7 +51,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
// Direct update functions // Direct update functions
const updateAgentPrompt = useCallback( const updateAgentPrompt = useCallback(
(mode: Mode, promptData: PromptComponent) => { (mode: Mode, promptData: PromptComponent) => {
const existingPrompt = customPrompts?.[mode] as PromptComponent const existingPrompt = customModePrompts?.[mode] as PromptComponent
const updatedPrompt = { ...existingPrompt, ...promptData } const updatedPrompt = { ...existingPrompt, ...promptData }
// Only include properties that differ from defaults // Only include properties that differ from defaults
@@ -64,7 +65,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
customPrompt: updatedPrompt, customPrompt: updatedPrompt,
}) })
}, },
[customPrompts], [customModePrompts],
) )
const updateCustomMode = useCallback((slug: string, modeConfig: ModeConfig) => { const updateCustomMode = useCallback((slug: string, modeConfig: ModeConfig) => {
@@ -261,7 +262,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
const handleAgentReset = (modeSlug: string) => { const handleAgentReset = (modeSlug: string) => {
// Only reset role definition for built-in modes // Only reset role definition for built-in modes
const existingPrompt = customPrompts?.[modeSlug] as PromptComponent const existingPrompt = customModePrompts?.[modeSlug] as PromptComponent
updateAgentPrompt(modeSlug, { updateAgentPrompt(modeSlug, {
...existingPrompt, ...existingPrompt,
roleDefinition: undefined, roleDefinition: undefined,
@@ -276,7 +277,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
} }
const getSupportPromptValue = (type: SupportPromptType): string => { const getSupportPromptValue = (type: SupportPromptType): string => {
return supportPrompt.get(customPrompts, type) return supportPrompt.get(customSupportPrompts, type)
} }
const handleTestEnhancement = () => { const handleTestEnhancement = () => {
@@ -556,7 +557,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
<VSCodeTextArea <VSCodeTextArea
value={(() => { value={(() => {
const customMode = findModeBySlug(mode, customModes) const customMode = findModeBySlug(mode, customModes)
const prompt = customPrompts?.[mode] as PromptComponent const prompt = customModePrompts?.[mode] as PromptComponent
return customMode?.roleDefinition ?? prompt?.roleDefinition ?? getRoleDefinition(mode) return customMode?.roleDefinition ?? prompt?.roleDefinition ?? getRoleDefinition(mode)
})()} })()}
onChange={(e) => { onChange={(e) => {
@@ -673,7 +674,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
<VSCodeTextArea <VSCodeTextArea
value={(() => { value={(() => {
const customMode = findModeBySlug(mode, customModes) const customMode = findModeBySlug(mode, customModes)
const prompt = customPrompts?.[mode] as PromptComponent const prompt = customModePrompts?.[mode] as PromptComponent
return customMode?.customInstructions ?? prompt?.customInstructions ?? "" return customMode?.customInstructions ?? prompt?.customInstructions ?? ""
})()} })()}
onChange={(e) => { onChange={(e) => {
@@ -689,7 +690,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
}) })
} else { } else {
// For built-in modes, update the prompts // For built-in modes, update the prompts
const existingPrompt = customPrompts?.[mode] as PromptComponent const existingPrompt = customModePrompts?.[mode] as PromptComponent
updateAgentPrompt(mode, { updateAgentPrompt(mode, {
...existingPrompt, ...existingPrompt,
customInstructions: value.trim() || undefined, customInstructions: value.trim() || undefined,

View File

@@ -12,7 +12,7 @@ jest.mock("../../../utils/vscode", () => ({
})) }))
const mockExtensionState = { const mockExtensionState = {
customPrompts: {}, customModePrompts: {},
listApiConfigMeta: [ listApiConfigMeta: [
{ id: "config1", name: "Config 1" }, { id: "config1", name: "Config 1" },
{ id: "config2", name: "Config 2" }, { id: "config2", name: "Config 2" },

View File

@@ -14,7 +14,8 @@ import { convertTextMateToHljs } from "../utils/textMateToHljs"
import { findLastIndex } from "../../../src/shared/array" import { findLastIndex } from "../../../src/shared/array"
import { McpServer } from "../../../src/shared/mcp" import { McpServer } from "../../../src/shared/mcp"
import { checkExistKey } from "../../../src/shared/checkExistApiConfig" import { checkExistKey } from "../../../src/shared/checkExistApiConfig"
import { Mode, CustomPrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "../../../src/shared/modes" import { Mode, CustomModePrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "../../../src/shared/modes"
import { CustomSupportPrompts } from "../../../src/shared/support-prompt"
export interface ExtensionStateContextType extends ExtensionState { export interface ExtensionStateContextType extends ExtensionState {
didHydrateState: boolean didHydrateState: boolean
@@ -57,7 +58,8 @@ export interface ExtensionStateContextType extends ExtensionState {
onUpdateApiConfig: (apiConfig: ApiConfiguration) => void onUpdateApiConfig: (apiConfig: ApiConfiguration) => void
mode: Mode mode: Mode
setMode: (value: Mode) => void setMode: (value: Mode) => void
setCustomPrompts: (value: CustomPrompts) => void setCustomModePrompts: (value: CustomModePrompts) => void
setCustomSupportPrompts: (value: CustomSupportPrompts) => void
enhancementApiConfigId?: string enhancementApiConfigId?: string
setEnhancementApiConfigId: (value: string) => void setEnhancementApiConfigId: (value: string) => void
experimentalDiffStrategy: boolean experimentalDiffStrategy: boolean
@@ -93,7 +95,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
currentApiConfigName: "default", currentApiConfigName: "default",
listApiConfigMeta: [], listApiConfigMeta: [],
mode: defaultModeSlug, mode: defaultModeSlug,
customPrompts: defaultPrompts, customModePrompts: defaultPrompts,
customSupportPrompts: {},
enhancementApiConfigId: "", enhancementApiConfigId: "",
experimentalDiffStrategy: false, experimentalDiffStrategy: false,
autoApprovalEnabled: false, autoApprovalEnabled: false,
@@ -270,7 +273,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setListApiConfigMeta, setListApiConfigMeta,
onUpdateApiConfig, onUpdateApiConfig,
setMode: (value: Mode) => setState((prevState) => ({ ...prevState, mode: value })), setMode: (value: Mode) => setState((prevState) => ({ ...prevState, mode: value })),
setCustomPrompts: (value) => setState((prevState) => ({ ...prevState, customPrompts: value })), setCustomModePrompts: (value) => setState((prevState) => ({ ...prevState, customModePrompts: value })),
setCustomSupportPrompts: (value) => setState((prevState) => ({ ...prevState, customSupportPrompts: value })),
setEnhancementApiConfigId: (value) => setEnhancementApiConfigId: (value) =>
setState((prevState) => ({ ...prevState, enhancementApiConfigId: value })), setState((prevState) => ({ ...prevState, enhancementApiConfigId: value })),
setExperimentalDiffStrategy: (value) => setExperimentalDiffStrategy: (value) =>