Create .roomodes file if needed

This commit is contained in:
Matt Rubens 2025-02-18 22:06:36 -05:00
parent 9df50b4c8d
commit 1cbe7228da
3 changed files with 77 additions and 8 deletions

View file

@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Create .roomodes file if needed

View file

@ -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<void> {
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<void> {

View file

@ -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",