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

View File

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

View File

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

View File

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