Skip to content
Merged
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
29 changes: 11 additions & 18 deletions src/core/prompts/sections/__tests__/custom-instructions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ describe("loadRuleFiles", () => {
mockedFs.readFile.mockResolvedValue(" content with spaces ")
const result = await loadRuleFiles("/fake/path")
expect(mockedFs.readFile).toHaveBeenCalled()
expect(result).toBe(
"\n# Rules from .clinerules:\ncontent with spaces\n" +
"\n# Rules from .cursorrules:\ncontent with spaces\n" +
"\n# Rules from .windsurfrules:\ncontent with spaces\n",
)
expect(result).toBe("\n# Rules from .roorules:\ncontent with spaces\n")
})

it("should handle ENOENT error", async () => {
Expand Down Expand Up @@ -49,22 +45,19 @@ describe("loadRuleFiles", () => {
jest.clearAllMocks()
})

it("should combine content from multiple rule files when they exist", async () => {
it("should not combine content from multiple rule files when they exist", async () => {
mockedFs.readFile.mockImplementation(((filePath: string | Buffer | URL | number) => {
if (filePath.toString().endsWith(".roorules")) {
return Promise.resolve("roo rules content")
}
if (filePath.toString().endsWith(".clinerules")) {
return Promise.resolve("cline rules content")
}
if (filePath.toString().endsWith(".cursorrules")) {
return Promise.resolve("cursor rules content")
}
return Promise.reject({ code: "ENOENT" })
}) as any)

const result = await loadRuleFiles("/fake/path")
expect(result).toBe(
"\n# Rules from .clinerules:\ncline rules content\n" +
"\n# Rules from .cursorrules:\ncursor rules content\n",
)
expect(result).toBe("\n# Rules from .roorules:\nroo rules content\n")
})

it("should handle when no rule files exist", async () => {
Expand All @@ -86,17 +79,17 @@ describe("loadRuleFiles", () => {

it("should skip directories with same name as rule files", async () => {
mockedFs.readFile.mockImplementation(((filePath: string | Buffer | URL | number) => {
if (filePath.toString().endsWith(".clinerules")) {
if (filePath.toString().endsWith(".roorules")) {
return Promise.reject({ code: "EISDIR" })
}
if (filePath.toString().endsWith(".cursorrules")) {
return Promise.resolve("cursor rules content")
if (filePath.toString().endsWith(".clinerules")) {
return Promise.reject({ code: "EISDIR" })
}
return Promise.reject({ code: "ENOENT" })
}) as any)

const result = await loadRuleFiles("/fake/path")
expect(result).toBe("\n# Rules from .cursorrules:\ncursor rules content\n")
expect(result).toBe("")
})
})

Expand All @@ -121,7 +114,7 @@ describe("addCustomInstructions", () => {
expect(result).toContain("(es)") // Check for language code in parentheses
expect(result).toContain("Global Instructions:\nglobal instructions")
expect(result).toContain("Mode-specific Instructions:\nmode instructions")
expect(result).toContain("Rules from .clinerules-test-mode:\nmode specific rules")
expect(result).toContain("Rules from .roorules-test-mode:\nmode specific rules")
})

it("should return empty string when no instructions provided", async () => {
Expand Down
24 changes: 16 additions & 8 deletions src/core/prompts/sections/custom-instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ async function safeReadFile(filePath: string): Promise<string> {
}

export async function loadRuleFiles(cwd: string): Promise<string> {
const ruleFiles = [".clinerules", ".cursorrules", ".windsurfrules"]
let combinedRules = ""
const ruleFiles = [".roorules", ".clinerules"]

for (const file of ruleFiles) {
const content = await safeReadFile(path.join(cwd, file))
if (content) {
combinedRules += `\n# Rules from ${file}:\n${content}\n`
return `\n# Rules from ${file}:\n${content}\n`
}
}

return combinedRules
return ""
}

export async function addCustomInstructions(
Expand All @@ -41,9 +40,19 @@ export async function addCustomInstructions(

// Load mode-specific rules if mode is provided
let modeRuleContent = ""
let usedRuleFile = ""
if (mode) {
const modeRuleFile = `.clinerules-${mode}`
modeRuleContent = await safeReadFile(path.join(cwd, modeRuleFile))
const rooModeRuleFile = `.roorules-${mode}`
modeRuleContent = await safeReadFile(path.join(cwd, rooModeRuleFile))
if (modeRuleContent) {
usedRuleFile = rooModeRuleFile
} else {
const clineModeRuleFile = `.clinerules-${mode}`
modeRuleContent = await safeReadFile(path.join(cwd, clineModeRuleFile))
if (modeRuleContent) {
usedRuleFile = clineModeRuleFile
}
}
}

// Add language preference if provided
Expand All @@ -69,8 +78,7 @@ export async function addCustomInstructions(

// Add mode-specific rules first if they exist
if (modeRuleContent && modeRuleContent.trim()) {
const modeRuleFile = `.clinerules-${mode}`
rules.push(`# Rules from ${modeRuleFile}:\n${modeRuleContent}`)
rules.push(`# Rules from ${usedRuleFile}:\n${modeRuleContent}`)
}

if (options.rooIgnoreInstructions) {
Expand Down
7 changes: 1 addition & 6 deletions webview-ui/src/components/history/HistoryView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import { BatchDeleteTaskDialog } from "./BatchDeleteTaskDialog"
import prettyBytes from "pretty-bytes"
import { Virtuoso } from "react-virtuoso"

import {
VSCodeTextField,
VSCodeRadioGroup,
VSCodeRadio,
VSCodeCheckbox,
} from "@vscode/webview-ui-toolkit/react"
import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"

import { vscode } from "@/utils/vscode"
import { formatLargeNumber, formatDate } from "@/utils/format"
Expand Down
12 changes: 6 additions & 6 deletions webview-ui/src/components/prompts/PromptsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,10 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
<div className="font-bold">{t("prompts:tools.title")}</div>
{findModeBySlug(mode, customModes) && (
<Button
variant="ghost"
size="icon"
onClick={() => setIsToolsEditMode(!isToolsEditMode)}
title={
variant="ghost"
size="icon"
onClick={() => setIsToolsEditMode(!isToolsEditMode)}
title={
isToolsEditMode
? t("prompts:tools.doneEditing")
: t("prompts:tools.editTools")
Expand Down Expand Up @@ -798,7 +798,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
// Open or create an empty file
vscode.postMessage({
type: "openFile",
text: `./.clinerules-${currentMode.slug}`,
text: `./.roorules-${currentMode.slug}`,
values: {
create: true,
content: "",
Expand Down Expand Up @@ -935,7 +935,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
onClick={() =>
vscode.postMessage({
type: "openFile",
text: "./.clinerules",
text: "./.roorules",
values: {
create: true,
content: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,18 +246,16 @@ describe("ApiConfigManager", () => {
// Click the select component to open the dropdown
const selectButton = screen.getByTestId("select-component")
fireEvent.click(selectButton)

// Find all command items and click the one with "Another Config"
const commandItems = document.querySelectorAll('.command-item')
const commandItems = document.querySelectorAll(".command-item")
// Find the item with "Another Config" text
const anotherConfigItem = Array.from(commandItems).find(
item => item.textContent?.includes("Another Config")
)

const anotherConfigItem = Array.from(commandItems).find((item) => item.textContent?.includes("Another Config"))

if (!anotherConfigItem) {
throw new Error("Could not find 'Another Config' option")
}

fireEvent.click(anotherConfigItem)

expect(mockOnSelectConfig).toHaveBeenCalledWith("Another Config")
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/ca/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "Instruccions personalitzades específiques del mode (opcional)",
"resetToDefault": "Restablir a valors predeterminats",
"description": "Afegiu directrius de comportament específiques per al mode {{modeName}}.",
"loadFromFile": "Les instruccions personalitzades específiques per al mode {{mode}} també es poden carregar des de <span>.clinerules-{{slug}}</span> al vostre espai de treball."
"loadFromFile": "Les instruccions personalitzades específiques per al mode {{mode}} també es poden carregar des de <span>.roorules-{{slug}}</span> al vostre espai de treball (.clinerules-{{slug}} està obsolet i deixarà de funcionar aviat)."
},
"globalCustomInstructions": {
"title": "Instruccions personalitzades per a tots els modes",
"description": "Aquestes instruccions s'apliquen a tots els modes. Proporcionen un conjunt bàsic de comportaments que es poden millorar amb instruccions específiques de cada mode a continuació.\nSi voleu que Roo pensi i parli en un idioma diferent al de la visualització del vostre editor ({{language}}), podeu especificar-ho aquí.",
"loadFromFile": "Les instruccions també es poden carregar des de <span>.clinerules</span> al vostre espai de treball."
"loadFromFile": "Les instruccions també es poden carregar des de <span>.roorules</span> al vostre espai de treball (.clinerules està obsolet i deixarà de funcionar aviat)."
},
"systemPrompt": {
"preview": "Previsualització del prompt del sistema",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/de/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "Modusspezifische benutzerdefinierte Anweisungen (optional)",
"resetToDefault": "Auf Standardwerte zurücksetzen",
"description": "Fügen Sie verhaltensspezifische Richtlinien für den Modus {{modeName}} hinzu.",
"loadFromFile": "Benutzerdefinierte Anweisungen für den Modus {{mode}} können auch aus <span>.clinerules-{{slug}}</span> in deinem Arbeitsbereich geladen werden."
"loadFromFile": "Benutzerdefinierte Anweisungen für den Modus {{mode}} können auch aus <span>.roorules-{{slug}}</span> in deinem Arbeitsbereich geladen werden (.clinerules-{{slug}} ist veraltet und wird bald nicht mehr funktionieren)."
},
"globalCustomInstructions": {
"title": "Benutzerdefinierte Anweisungen für alle Modi",
"description": "Diese Anweisungen gelten für alle Modi. Sie bieten einen grundlegenden Satz von Verhaltensweisen, die durch modusspezifische Anweisungen unten erweitert werden können.\nWenn du möchtest, dass Roo in einer anderen Sprache als deiner Editor-Anzeigesprache ({{language}}) denkt und spricht, kannst du das hier angeben.",
"loadFromFile": "Anweisungen können auch aus <span>.clinerules</span> in deinem Arbeitsbereich geladen werden."
"loadFromFile": "Anweisungen können auch aus <span>.roorules</span> in deinem Arbeitsbereich geladen werden (.clinerules ist veraltet und wird bald nicht mehr funktionieren)."
},
"systemPrompt": {
"preview": "System-Prompt Vorschau",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/en/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "Mode-specific Custom Instructions (optional)",
"resetToDefault": "Reset to default",
"description": "Add behavioral guidelines specific to {{modeName}} mode.",
"loadFromFile": "Custom instructions specific to {{mode}} mode can also be loaded from <span>.clinerules-{{slug}}</span> in your workspace."
"loadFromFile": "Custom instructions specific to {{mode}} mode can also be loaded from <span>.roorules-{{slug}}</span> in your workspace (.clinerules-{{slug}} is deprecated and will stop working soon)."
},
"globalCustomInstructions": {
"title": "Custom Instructions for All Modes",
"description": "These instructions apply to all modes. They provide a base set of behaviors that can be enhanced by mode-specific instructions below.\nIf you would like Roo to think and speak in a different language than your editor display language ({{language}}), you can specify it here.",
"loadFromFile": "Instructions can also be loaded from <span>.clinerules</span> in your workspace."
"loadFromFile": "Instructions can also be loaded from <span>.roorules</span> in your workspace (.clinerules is deprecated and will stop working soon)."
},
"systemPrompt": {
"preview": "Preview System Prompt",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/es/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "Instrucciones personalizadas para el modo (opcional)",
"resetToDefault": "Restablecer a valores predeterminados",
"description": "Agrega directrices de comportamiento específicas para el modo {{modeName}}.",
"loadFromFile": "Las instrucciones personalizadas para el modo {{mode}} también se pueden cargar desde <span>.clinerules-{{slug}}</span> en tu espacio de trabajo."
"loadFromFile": "Las instrucciones personalizadas para el modo {{mode}} también se pueden cargar desde <span>.roorules-{{slug}}</span> en tu espacio de trabajo (.clinerules-{{slug}} está obsoleto y dejará de funcionar pronto)."
},
"globalCustomInstructions": {
"title": "Instrucciones personalizadas para todos los modos",
"description": "Estas instrucciones se aplican a todos los modos. Proporcionan un conjunto base de comportamientos que pueden ser mejorados por instrucciones específicas de cada modo.\nSi quieres que Roo piense y hable en un idioma diferente al idioma de visualización de tu editor ({{language}}), puedes especificarlo aquí.",
"loadFromFile": "Las instrucciones también se pueden cargar desde <span>.clinerules</span> en tu espacio de trabajo."
"loadFromFile": "Las instrucciones también se pueden cargar desde <span>.roorules</span> en tu espacio de trabajo (.clinerules está obsoleto y dejará de funcionar pronto)."
},
"systemPrompt": {
"preview": "Vista previa de la solicitud del sistema",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/fr/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "Instructions personnalisées spécifiques au mode (optionnel)",
"resetToDefault": "Réinitialiser aux valeurs par défaut",
"description": "Ajoutez des directives comportementales spécifiques au mode {{modeName}}.",
"loadFromFile": "Les instructions personnalisées spécifiques au mode {{mode}} peuvent également être chargées depuis <span>.clinerules-{{slug}}</span> dans votre espace de travail."
"loadFromFile": "Les instructions personnalisées spécifiques au mode {{mode}} peuvent également être chargées depuis <span>.roorules-{{slug}}</span> dans votre espace de travail (.clinerules-{{slug}} est obsolète et cessera de fonctionner bientôt)."
},
"globalCustomInstructions": {
"title": "Instructions personnalisées pour tous les modes",
"description": "Ces instructions s'appliquent à tous les modes. Elles fournissent un ensemble de comportements de base qui peuvent être améliorés par des instructions spécifiques au mode ci-dessous.\nSi vous souhaitez que Roo pense et parle dans une langue différente de celle de votre éditeur ({{language}}), vous pouvez le spécifier ici.",
"loadFromFile": "Les instructions peuvent également être chargées depuis <span>.clinerules</span> dans votre espace de travail."
"loadFromFile": "Les instructions peuvent également être chargées depuis <span>.roorules</span> dans votre espace de travail (.clinerules est obsolète et cessera de fonctionner bientôt)."
},
"systemPrompt": {
"preview": "Aperçu du prompt système",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/hi/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "मोड-विशिष्ट कस्टम निर्देश (वैकल्पिक)",
"resetToDefault": "डिफ़ॉल्ट पर रीसेट करें",
"description": "{{modeName}} मोड के लिए विशिष्ट व्यवहार दिशानिर्देश जोड़ें।",
"loadFromFile": "{{mode}} मोड के लिए विशिष्ट कस्टम निर्देश आपके वर्कस्पेस में <span>.clinerules-{{slug}}</span> से भी लोड किए जा सकते हैं।"
"loadFromFile": "{{mode}} मोड के लिए विशिष्ट कस्टम निर्देश आपके वर्कस्पेस में <span>.roorules-{{slug}}</span> से भी लोड किए जा सकते हैं (.clinerules-{{slug}} पुराना हो गया है और जल्द ही काम करना बंद कर देगा)।"
},
"globalCustomInstructions": {
"title": "सभी मोड्स के लिए कस्टम निर्देश",
"description": "ये निर्देश सभी मोड्स पर लागू होते हैं। वे व्यवहारों का एक आधार सेट प्रदान करते हैं जिन्हें नीचे दिए गए मोड-विशिष्ट निर्देशों द्वारा बढ़ाया जा सकता है।\nयदि आप चाहते हैं कि Roo आपके एडिटर की प्रदर्शन भाषा ({{language}}) से अलग भाषा में सोचे और बोले, तो आप यहां इसे निर्दिष्ट कर सकते हैं।",
"loadFromFile": "निर्देश आपके वर्कस्पेस में <span>.clinerules</span> से भी लोड किए जा सकते हैं।"
"loadFromFile": "निर्देश आपके वर्कस्पेस में <span>.roorules</span> से भी लोड किए जा सकते हैं (.clinerules पुराना हो गया है और जल्द ही काम करना बंद कर देगा)।"
},
"systemPrompt": {
"preview": "सिस्टम प्रॉम्प्ट का पूर्वावलोकन",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/it/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "Istruzioni personalizzate specifiche per la modalità (opzionale)",
"resetToDefault": "Ripristina predefiniti",
"description": "Aggiungi linee guida comportamentali specifiche per la modalità {{modeName}}.",
"loadFromFile": "Le istruzioni personalizzate specifiche per la modalità {{mode}} possono essere caricate anche da <span>.clinerules-{{slug}}</span> nel tuo spazio di lavoro."
"loadFromFile": "Le istruzioni personalizzate specifiche per la modalità {{mode}} possono essere caricate anche da <span>.roorules-{{slug}}</span> nel tuo spazio di lavoro (.clinerules-{{slug}} è obsoleto e smetterà di funzionare presto)."
},
"globalCustomInstructions": {
"title": "Istruzioni personalizzate per tutte le modalità",
"description": "Queste istruzioni si applicano a tutte le modalità. Forniscono un insieme base di comportamenti che possono essere migliorati dalle istruzioni specifiche per modalità qui sotto.\nSe desideri che Roo pensi e parli in una lingua diversa dalla lingua di visualizzazione del tuo editor ({{language}}), puoi specificarlo qui.",
"loadFromFile": "Le istruzioni possono essere caricate anche da <span>.clinerules</span> nel tuo spazio di lavoro."
"loadFromFile": "Le istruzioni possono essere caricate anche da <span>.roorules</span> nel tuo spazio di lavoro (.clinerules è obsoleto e smetterà di funzionare presto)."
},
"systemPrompt": {
"preview": "Anteprima prompt di sistema",
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/i18n/locales/ja/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
"title": "モード固有のカスタム指示(オプション)",
"resetToDefault": "デフォルトにリセット",
"description": "{{modeName}}モードに特化した行動ガイドラインを追加します。",
"loadFromFile": "{{mode}}モード固有のカスタム指示は、ワークスペースの<span>.clinerules-{{slug}}</span>からも読み込めます。"
"loadFromFile": "{{mode}}モード固有のカスタム指示は、ワークスペースの<span>.roorules-{{slug}}</span>からも読み込めます(.clinerules-{{slug}}は非推奨であり、まもなく機能しなくなります)。"
},
"globalCustomInstructions": {
"title": "すべてのモードのカスタム指示",
"description": "これらの指示はすべてのモードに適用されます。モード固有の指示で強化できる基本的な動作セットを提供します。\nRooにエディタの表示言語({{language}})とは異なる言語で考えたり話したりさせたい場合は、ここで指定できます。",
"loadFromFile": "指示はワークスペースの<span>.clinerules</span>からも読み込めます。"
"loadFromFile": "指示はワークスペースの<span>.roorules</span>からも読み込めます(.clinerules は非推奨であり、まもなく機能しなくなります)。"
},
"systemPrompt": {
"preview": "システムプロンプトのプレビュー",
Expand Down
Loading