Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/types/src/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ export const modeConfigSchema = z.object({
customInstructions: z.string().optional(),
groups: groupEntryArraySchema,
source: z.enum(["global", "project"]).optional(),
/**
* When provided, replaces the middle sections of the generated system prompt
* (CAPABILITIES, TOOL USE, OBJECTIVE, etc.) with this custom content.
* The final prompt will be: roleDefinition + systemPrompt + customInstructions
*/
systemPrompt: z.string().optional(),
})

export type ModeConfig = z.infer<typeof modeConfigSchema>
Expand Down
171 changes: 171 additions & 0 deletions src/core/prompts/__tests__/system-prompt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,177 @@ describe("SYSTEM_PROMPT", () => {
expect(prompt).toContain("OBJECTIVE")
})

describe("systemPrompt override", () => {
it("should use systemPrompt from mode config when provided", async () => {
const customSystemPrompt = "This is a custom system prompt that replaces the default sections."

const customModes: ModeConfig[] = [
{
slug: "minimal-mode",
name: "Minimal Mode",
roleDefinition: "You are a minimal assistant.",
customInstructions: "Be concise.",
groups: ["mcp"] as const,
systemPrompt: customSystemPrompt,
},
]

const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false,
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
"minimal-mode", // mode
undefined, // customModePrompts
customModes, // customModes
"Global instructions", // globalCustomInstructions
undefined, // diffEnabled
experiments,
true, // enableMcpServerCreation
undefined, // language
undefined, // rooIgnoreInstructions
undefined, // partialReadsEnabled
)

// Should contain the custom systemPrompt
expect(prompt).toContain(customSystemPrompt)

// Should contain role definition at the top
expect(prompt).toContain("You are a minimal assistant.")
expect(prompt.indexOf("You are a minimal assistant.")).toBeLessThan(prompt.indexOf(customSystemPrompt))

// Should NOT contain the default sections (TOOL USE, CAPABILITIES, OBJECTIVE, etc.)
expect(prompt).not.toContain("TOOL USE")
expect(prompt).not.toContain("CAPABILITIES")
expect(prompt).not.toContain("OBJECTIVE")
expect(prompt).not.toContain("MARKDOWN RULES")
})

it("should include customInstructions when using systemPrompt override", async () => {
const customSystemPrompt = "Custom system prompt content."
const modeCustomInstructions = "Be concise."

const customModes: ModeConfig[] = [
{
slug: "minimal-mode",
name: "Minimal Mode",
roleDefinition: "You are a minimal assistant.",
customInstructions: modeCustomInstructions,
groups: ["mcp"] as const,
systemPrompt: customSystemPrompt,
},
]

const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false,
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
"minimal-mode", // mode
undefined, // customModePrompts
customModes, // customModes
"Global instructions", // globalCustomInstructions
undefined, // diffEnabled
experiments,
true, // enableMcpServerCreation
undefined, // language
undefined, // rooIgnoreInstructions
undefined, // partialReadsEnabled
)

// Should contain the customInstructions
expect(prompt).toContain(modeCustomInstructions)

// Custom instructions should come after the systemPrompt
expect(prompt.indexOf(customSystemPrompt)).toBeLessThan(prompt.indexOf(modeCustomInstructions))
})

it("should use default sections when systemPrompt is not provided", async () => {
const customModes: ModeConfig[] = [
{
slug: "regular-mode",
name: "Regular Mode",
roleDefinition: "You are a regular assistant.",
customInstructions: "Follow all guidelines.",
groups: ["read"] as const,
// No systemPrompt provided
},
]

const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false,
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
"regular-mode", // mode
undefined, // customModePrompts
customModes, // customModes
undefined, // globalCustomInstructions
undefined, // diffEnabled
experiments,
true, // enableMcpServerCreation
undefined, // language
undefined, // rooIgnoreInstructions
undefined, // partialReadsEnabled
)

// Should contain the default sections
expect(prompt).toContain("TOOL USE")
expect(prompt).toContain("CAPABILITIES")
expect(prompt).toContain("OBJECTIVE")
})

it("should prioritize file-based system prompt over config systemPrompt", async () => {
// Mock fs to return a file-based system prompt
const fsMock = vi.mocked(await import("fs/promises"))
fsMock.readFile.mockResolvedValueOnce("File-based system prompt content.")

const customSystemPrompt = "Config-based system prompt content."

const customModes: ModeConfig[] = [
{
slug: "priority-mode",
name: "Priority Mode",
roleDefinition: "You are a priority assistant.",
customInstructions: "Test instructions.",
groups: ["read"] as const,
systemPrompt: customSystemPrompt,
},
]

const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false,
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
"priority-mode", // mode
undefined, // customModePrompts
customModes, // customModes
undefined, // globalCustomInstructions
undefined, // diffEnabled
experiments,
true, // enableMcpServerCreation
undefined, // language
undefined, // rooIgnoreInstructions
undefined, // partialReadsEnabled
)

// Should contain the file-based system prompt
expect(prompt).toContain("File-based system prompt content.")

// Should NOT contain the config-based systemPrompt
expect(prompt).not.toContain(customSystemPrompt)
})
})

afterAll(() => {
vi.restoreAllMocks()
})
Expand Down
28 changes: 28 additions & 0 deletions src/core/prompts/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,34 @@ export const SYSTEM_PROMPT = async (

${fileCustomSystemPrompt}

${customInstructions}`
}

// If the mode config has a systemPrompt defined, use it as custom middle content
if (currentMode.systemPrompt) {
const { roleDefinition, baseInstructions: baseInstructionsForConfig } = getModeSelection(
mode,
promptComponent,
customModes,
)

const customInstructions = await addCustomInstructions(
baseInstructionsForConfig,
globalCustomInstructions || "",
cwd,
mode,
{
language: language ?? formatLanguage(vscode.env.language),
rooIgnoreInstructions,
settings,
},
)

// For config-based systemPrompt, use the simplified structure
return `${roleDefinition}

${currentMode.systemPrompt}

${customInstructions}`
}

Expand Down
Loading