From 97e969a86fccdf384e164f4fc9c55147ad288d32 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 5 Aug 2025 12:01:19 +0000 Subject: [PATCH] fix: update remaining files to use vscode-aware wrapper functions - Update custom-system-prompt.ts to use async getSystemPromptFilePath - Update ClineProvider.ts to handle async change - Update MarketplaceManager.ts to use getProjectRooDirectoryForCwd wrapper - Update webviewMessageHandler.ts to use wrapper (already done) This ensures all .roo path constructions properly check for workspace folders --- .../prompts/sections/custom-system-prompt.ts | 10 +-- src/core/webview/ClineProvider.ts | 2 +- .../marketplace/MarketplaceManager.ts | 4 +- .../roo-config/__tests__/index.spec.ts | 14 ++-- src/services/roo-config/wrapper.ts | 72 ++++++++++++++++++- 5 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/core/prompts/sections/custom-system-prompt.ts b/src/core/prompts/sections/custom-system-prompt.ts index f401000bb5..536311e632 100644 --- a/src/core/prompts/sections/custom-system-prompt.ts +++ b/src/core/prompts/sections/custom-system-prompt.ts @@ -2,6 +2,7 @@ import fs from "fs/promises" import path from "path" import { Mode } from "../../../shared/modes" import { fileExistsAtPath } from "../../../utils/fs" +import { getProjectRooDirectoryForCwd } from "../../../services/roo-config/wrapper" export type PromptVariables = { workspace?: string @@ -45,8 +46,9 @@ async function safeReadFile(filePath: string): Promise { /** * Get the path to a system prompt file for a specific mode */ -export function getSystemPromptFilePath(cwd: string, mode: Mode): string { - return path.join(cwd, ".roo", `system-prompt-${mode}`) +export async function getSystemPromptFilePath(cwd: string, mode: Mode): Promise { + const rooDir = await getProjectRooDirectoryForCwd(cwd) + return path.join(rooDir, `system-prompt-${mode}`) } /** @@ -54,7 +56,7 @@ export function getSystemPromptFilePath(cwd: string, mode: Mode): string { * If the file doesn't exist, returns an empty string */ export async function loadSystemPromptFile(cwd: string, mode: Mode, variables: PromptVariables): Promise { - const filePath = getSystemPromptFilePath(cwd, mode) + const filePath = await getSystemPromptFilePath(cwd, mode) const rawContent = await safeReadFile(filePath) if (!rawContent) { return "" @@ -67,7 +69,7 @@ export async function loadSystemPromptFile(cwd: string, mode: Mode, variables: P * Ensures the .roo directory exists, creating it if necessary */ export async function ensureRooDirectory(cwd: string): Promise { - const rooDir = path.join(cwd, ".roo") + const rooDir = await getProjectRooDirectoryForCwd(cwd) // Check if directory already exists if (await fileExistsAtPath(rooDir)) { diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index c4f146bd34..7f5df0a776 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1538,7 +1538,7 @@ export class ClineProvider * Checks if there is a file-based system prompt override for the given mode */ async hasFileBasedSystemPromptOverride(mode: Mode): Promise { - const promptFilePath = getSystemPromptFilePath(this.cwd, mode) + const promptFilePath = await getSystemPromptFilePath(this.cwd, mode) return await fileExistsAtPath(promptFilePath) } diff --git a/src/services/marketplace/MarketplaceManager.ts b/src/services/marketplace/MarketplaceManager.ts index 800eba62e0..7aac63ba5d 100644 --- a/src/services/marketplace/MarketplaceManager.ts +++ b/src/services/marketplace/MarketplaceManager.ts @@ -12,6 +12,7 @@ import { GlobalFileNames } from "../../shared/globalFileNames" import { ensureSettingsDirectoryExists } from "../../utils/globalContext" import { t } from "../../i18n" import type { CustomModesManager } from "../../core/config/CustomModesManager" +import { getProjectRooDirectoryForCwd } from "../../services/roo-config/wrapper" import { RemoteConfigLoader } from "./RemoteConfigLoader" import { SimpleInstaller } from "./SimpleInstaller" @@ -270,7 +271,8 @@ export class MarketplaceManager { } // Check MCPs in .roo/mcp.json - const projectMcpPath = path.join(workspaceFolder.uri.fsPath, ".roo", "mcp.json") + const rooDir = await getProjectRooDirectoryForCwd(workspaceFolder.uri.fsPath) + const projectMcpPath = path.join(rooDir, "mcp.json") try { const content = await fs.readFile(projectMcpPath, "utf-8") const data = JSON.parse(content) diff --git a/src/services/roo-config/__tests__/index.spec.ts b/src/services/roo-config/__tests__/index.spec.ts index 42828ca822..5ec8cc8bb3 100644 --- a/src/services/roo-config/__tests__/index.spec.ts +++ b/src/services/roo-config/__tests__/index.spec.ts @@ -35,16 +35,12 @@ vi.mock("vscode", () => ({ }, })) -import { - getGlobalRooDirectory, - getProjectRooDirectoryForCwd, - directoryExists, - fileExists, - readFileIfExists, - getRooDirectoriesForCwd, - loadConfiguration, -} from "../index" +import { getGlobalRooDirectory, directoryExists, fileExists, readFileIfExists, loadConfiguration } from "../index" import { findWorkspaceWithRoo } from "../vscode-utils" +import { getProjectRooDirectoryForCwd, getRooDirectoriesForCwd, setVscodeUtils } from "../wrapper" + +// Initialize the wrapper with vscode utilities for testing +setVscodeUtils({ findWorkspaceWithRoo }) describe("RooConfigService", () => { beforeEach(() => { diff --git a/src/services/roo-config/wrapper.ts b/src/services/roo-config/wrapper.ts index d592fcf9c3..0a98bd948b 100644 --- a/src/services/roo-config/wrapper.ts +++ b/src/services/roo-config/wrapper.ts @@ -1,5 +1,10 @@ import * as path from "path" -import { getProjectRooDirectoryForCwd as getProjectRooDirectoryBase } from "./index" +import { + getProjectRooDirectoryForCwd as getProjectRooDirectoryBase, + getGlobalRooDirectory, + getRooDirectoriesForCwd as getRooDirectoriesBase, + loadConfiguration as loadConfigurationBase, +} from "./index" // This will be set by the extension during activation let vscodeUtils: { findWorkspaceWithRoo: () => any } | undefined @@ -28,3 +33,68 @@ export function getProjectRooDirectoryForCwd(cwd: string): string { // Fall back to base implementation return getProjectRooDirectoryBase(cwd) } + +/** + * Gets the ordered list of .roo directories to check (global first, then project-local) + * This wrapper uses the vscode-aware getProjectRooDirectoryForCwd + */ +export function getRooDirectoriesForCwd(cwd: string): string[] { + const directories: string[] = [] + + // Add global directory first + directories.push(getGlobalRooDirectory()) + + // Add project-local directory second (using wrapper version) + directories.push(getProjectRooDirectoryForCwd(cwd)) + + return directories +} + +/** + * Loads configuration from multiple .roo directories with project overriding global + * This wrapper uses the vscode-aware getProjectRooDirectoryForCwd + */ +export async function loadConfiguration( + relativePath: string, + cwd: string, +): Promise<{ + global: string | null + project: string | null + merged: string +}> { + // Use the wrapper version of getProjectRooDirectoryForCwd + const globalDir = getGlobalRooDirectory() + const projectDir = getProjectRooDirectoryForCwd(cwd) + + const globalFilePath = path.join(globalDir, relativePath) + const projectFilePath = path.join(projectDir, relativePath) + + // Import readFileIfExists from index + const { readFileIfExists } = await import("./index") + + // Read global configuration + const globalContent = await readFileIfExists(globalFilePath) + + // Read project-local configuration + const projectContent = await readFileIfExists(projectFilePath) + + // Merge configurations - project overrides global + let merged = "" + + if (globalContent) { + merged += globalContent + } + + if (projectContent) { + if (merged) { + merged += "\n\n# Project-specific rules (override global):\n\n" + } + merged += projectContent + } + + return { + global: globalContent, + project: projectContent, + merged: merged || "", + } +}