-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: add global system prompt override support #6815
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import fs from "fs/promises" | ||
| import path from "path" | ||
| import os from "os" | ||
| import { Mode } from "../../../shared/modes" | ||
| import { fileExistsAtPath } from "../../../utils/fs" | ||
|
|
||
|
|
@@ -50,15 +51,34 @@ export function getSystemPromptFilePath(cwd: string, mode: Mode): string { | |
| } | ||
|
|
||
| /** | ||
| * Loads custom system prompt from a file at .roo/system-prompt-[mode slug] | ||
| * If the file doesn't exist, returns an empty string | ||
| * Get the path to a global system prompt file for a specific mode | ||
| * Located in the user's home directory under .roo/system-prompt-[mode slug] | ||
| */ | ||
| export function getGlobalSystemPromptFilePath(mode: Mode): string { | ||
| return path.join(os.homedir(), ".roo", `system-prompt-${mode}`) | ||
| } | ||
|
|
||
| /** | ||
| * Loads custom system prompt from a file, checking in the following order: | ||
| * 1. Local project: .roo/system-prompt-[mode slug] | ||
| * 2. Global (home directory): ~/.roo/system-prompt-[mode slug] | ||
| * If neither file exists, returns an empty string | ||
| */ | ||
| export async function loadSystemPromptFile(cwd: string, mode: Mode, variables: PromptVariables): Promise<string> { | ||
| const filePath = getSystemPromptFilePath(cwd, mode) | ||
| const rawContent = await safeReadFile(filePath) | ||
| // First, check for local project-specific system prompt | ||
| const localFilePath = getSystemPromptFilePath(cwd, mode) | ||
| let rawContent = await safeReadFile(localFilePath) | ||
|
|
||
| // If no local file exists, check for global system prompt | ||
| if (!rawContent) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The implementation reads the local file even when it's empty before checking the global file. Could we optimize this by checking file existence first, then only reading if the file exists and is non-empty? This would avoid unnecessary file reads for empty files. |
||
| const globalFilePath = getGlobalSystemPromptFilePath(mode) | ||
| rawContent = await safeReadFile(globalFilePath) | ||
| } | ||
|
|
||
| if (!rawContent) { | ||
| return "" | ||
| } | ||
|
|
||
| const interpolatedContent = interpolatePromptContent(rawContent, variables) | ||
| return interpolatedContent | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,7 +78,7 @@ import { ContextProxy } from "../config/ContextProxy" | |
| import { ProviderSettingsManager } from "../config/ProviderSettingsManager" | ||
| import { CustomModesManager } from "../config/CustomModesManager" | ||
| import { Task, TaskOptions } from "../task/Task" | ||
| import { getSystemPromptFilePath } from "../prompts/sections/custom-system-prompt" | ||
| import { getSystemPromptFilePath, getGlobalSystemPromptFilePath } from "../prompts/sections/custom-system-prompt" | ||
|
|
||
| import { webviewMessageHandler } from "./webviewMessageHandler" | ||
| import { getNonce } from "./getNonce" | ||
|
|
@@ -1480,10 +1480,18 @@ export class ClineProvider | |
|
|
||
| /** | ||
| * Checks if there is a file-based system prompt override for the given mode | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a comment here explaining the precedence order (local > global) for future maintainers. Something like: |
||
| * Checks both local project directory and global home directory | ||
| */ | ||
| async hasFileBasedSystemPromptOverride(mode: Mode): Promise<boolean> { | ||
| const promptFilePath = getSystemPromptFilePath(this.cwd, mode) | ||
| return await fileExistsAtPath(promptFilePath) | ||
| // Check local project directory first | ||
| const localPromptFilePath = getSystemPromptFilePath(this.cwd, mode) | ||
| if (await fileExistsAtPath(localPromptFilePath)) { | ||
| return true | ||
| } | ||
|
|
||
| // Check global home directory | ||
| const globalPromptFilePath = getGlobalSystemPromptFilePath(mode) | ||
| return await fileExistsAtPath(globalPromptFilePath) | ||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mock setup pattern here is clean, but could benefit from a comment explaining why mocks must come before imports for future test writers. This is a TypeScript/Vitest specific requirement that might not be obvious.