From a6bfbf8bcb4b27ae02a8ae35c4b0fdd5a5417416 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 3 Nov 2024 11:34:28 -0500 Subject: [PATCH] Read from .clinerules and .cursorrules if present --- src/core/Cline.ts | 6 +----- src/core/prompts/system.ts | 39 +++++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index bf2324c..64ad0ec 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -757,11 +757,7 @@ export class Cline { } async *attemptApiRequest(previousApiReqIndex: number): ApiStream { - let systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false) - if (this.customInstructions && this.customInstructions.trim()) { - // altering the system prompt mid-task will break the prompt cache, but in the grand scheme this will not change often so it's better to not pollute user messages with it the way we have to with - systemPrompt += addCustomInstructions(this.customInstructions) - } + const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false) + await addCustomInstructions(this.customInstructions ?? '', cwd) // If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request if (previousApiReqIndex >= 0) { diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 763788e..abd27b7 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -1,6 +1,8 @@ import osName from "os-name" import defaultShell from "default-shell" import os from "os" +import fs from 'fs/promises' +import path from 'path' export const SYSTEM_PROMPT = async ( cwd: string, @@ -281,13 +283,44 @@ You accomplish a given task iteratively, breaking it down into clear steps and w 4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \`open index.html\` to show the website you've built. 5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.` -export function addCustomInstructions(customInstructions: string): string { - return ` +async function loadRuleFiles(cwd: string): Promise { + const ruleFiles = ['.clinerules', '.cursorrules'] + let combinedRules = '' + + for (const file of ruleFiles) { + try { + const content = await fs.readFile(path.join(cwd, file), 'utf-8') + if (content.trim()) { + combinedRules += `\n# Rules from ${file}:\n${content.trim()}\n` + } + } catch (err) { + // Silently skip if file doesn't exist + if ((err as NodeJS.ErrnoException).code !== 'ENOENT') { + throw err + } + } + } + + return combinedRules +} + +export async function addCustomInstructions(customInstructions: string, cwd: string): Promise { + const ruleFileContent = await loadRuleFiles(cwd) + const allInstructions = [customInstructions.trim()] + + if (ruleFileContent && ruleFileContent.trim()) { + allInstructions.push(ruleFileContent.trim()) + } + + const joinedInstructions = allInstructions.join('\n\n') + + return joinedInstructions ? ` ==== USER'S CUSTOM INSTRUCTIONS The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines. -${customInstructions.trim()}` +${joinedInstructions}` + : "" }