From 1cbe7228da29d9a3bbe66dcf62f8628bb6d4e83e Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Tue, 18 Feb 2025 22:06:36 -0500 Subject: [PATCH] Create .roomodes file if needed --- .changeset/twelve-ways-peel.md | 5 ++ src/core/config/CustomModesManager.ts | 29 ++++++++--- .../__tests__/CustomModesManager.test.ts | 51 +++++++++++++++++++ 3 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 .changeset/twelve-ways-peel.md diff --git a/.changeset/twelve-ways-peel.md b/.changeset/twelve-ways-peel.md new file mode 100644 index 0000000000..7225c884c7 --- /dev/null +++ b/.changeset/twelve-ways-peel.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Create .roomodes file if needed diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index 70107d9e69..91b91c38ba 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -5,6 +5,7 @@ import { CustomModesSettingsSchema } from "./CustomModesSchema" import { ModeConfig } from "../../shared/modes" import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual } from "../../utils/path" +import { logger } from "../../utils/logging" const ROOMODES_FILENAME = ".roomodes" @@ -214,14 +215,26 @@ export class CustomModesManager { await this.context.globalState.update("customModes", mergedModes) return mergedModes } - async updateCustomMode(slug: string, config: ModeConfig): Promise { try { const isProjectMode = config.source === "project" - const targetPath = isProjectMode ? await this.getWorkspaceRoomodes() : await this.getCustomModesFilePath() + let targetPath: string - if (isProjectMode && !targetPath) { - throw new Error("No workspace folder found for project-specific mode") + if (isProjectMode) { + const workspaceFolders = vscode.workspace.workspaceFolders + if (!workspaceFolders || workspaceFolders.length === 0) { + logger.error("Failed to update project mode: No workspace folder found", { slug }) + throw new Error("No workspace folder found for project-specific mode") + } + const workspaceRoot = workspaceFolders[0].uri.fsPath + targetPath = path.join(workspaceRoot, ROOMODES_FILENAME) + const exists = await fileExistsAtPath(targetPath) + logger.info(`${exists ? "Updating" : "Creating"} project mode in ${ROOMODES_FILENAME}`, { + slug, + workspace: workspaceRoot, + }) + } else { + targetPath = await this.getCustomModesFilePath() } await this.queueWrite(async () => { @@ -231,7 +244,7 @@ export class CustomModesManager { source: isProjectMode ? ("project" as const) : ("global" as const), } - await this.updateModesInFile(targetPath!, (modes) => { + await this.updateModesInFile(targetPath, (modes) => { const updatedModes = modes.filter((m) => m.slug !== slug) updatedModes.push(modeWithSource) return updatedModes @@ -240,9 +253,9 @@ export class CustomModesManager { await this.refreshMergedState() }) } catch (error) { - vscode.window.showErrorMessage( - `Failed to update custom mode: ${error instanceof Error ? error.message : String(error)}`, - ) + const errorMessage = error instanceof Error ? error.message : String(error) + logger.error("Failed to update custom mode", { slug, error: errorMessage }) + vscode.window.showErrorMessage(`Failed to update custom mode: ${errorMessage}`) } } private async updateModesInFile(filePath: string, operation: (modes: ModeConfig[]) => ModeConfig[]): Promise { diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts index bbc070b0ba..3c8236e920 100644 --- a/src/core/config/__tests__/CustomModesManager.test.ts +++ b/src/core/config/__tests__/CustomModesManager.test.ts @@ -207,6 +207,57 @@ describe("CustomModesManager", () => { expect(mockOnUpdate).toHaveBeenCalled() }) + it("creates .roomodes file when adding project-specific mode", async () => { + const projectMode: ModeConfig = { + slug: "project-mode", + name: "Project Mode", + roleDefinition: "Project Role", + groups: ["read"], + source: "project", + } + + // Mock .roomodes to not exist initially + let roomodesContent: any = null + ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => { + return path === mockSettingsPath + }) + ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => { + if (path === mockSettingsPath) { + return JSON.stringify({ customModes: [] }) + } + if (path === mockRoomodes) { + if (!roomodesContent) { + throw new Error("File not found") + } + return JSON.stringify(roomodesContent) + } + throw new Error("File not found") + }) + ;(fs.writeFile as jest.Mock).mockImplementation(async (path: string, content: string) => { + if (path === mockRoomodes) { + roomodesContent = JSON.parse(content) + } + return Promise.resolve() + }) + + await manager.updateCustomMode("project-mode", projectMode) + + // Verify .roomodes was created with the project mode + expect(fs.writeFile).toHaveBeenCalledWith(mockRoomodes, expect.stringContaining("project-mode"), "utf-8") + + // Verify the content written to .roomodes + expect(roomodesContent).toEqual({ + customModes: [ + expect.objectContaining({ + slug: "project-mode", + name: "Project Mode", + roleDefinition: "Project Role", + source: "project", + }), + ], + }) + }) + it("queues write operations", async () => { const mode1: ModeConfig = { slug: "mode1",