diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index c388c1a537..d40aa423d6 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -786,7 +786,9 @@ export class CustomModesManager { const relativePath = isGlobalMode ? path.relative(baseDir, filePath) : path.relative(path.join(baseDir, ".roo"), filePath) - rulesFiles.push({ relativePath, content: content.trim() }) + // Normalize path to use forward slashes for cross-platform compatibility + const normalizedRelativePath = relativePath.toPosix() + rulesFiles.push({ relativePath: normalizedRelativePath, content: content.trim() }) } } } diff --git a/src/core/config/__tests__/CustomModesManager.spec.ts b/src/core/config/__tests__/CustomModesManager.spec.ts index 682696fd03..aa7d6422d9 100644 --- a/src/core/config/__tests__/CustomModesManager.spec.ts +++ b/src/core/config/__tests__/CustomModesManager.spec.ts @@ -1699,5 +1699,59 @@ describe("CustomModesManager", () => { expect(result.yaml).toContain("global-test-mode") expect(result.yaml).toContain("Global rule content") }) + + it("should normalize paths to use forward slashes in exported YAML", async () => { + const roomodesContent = { + customModes: [ + { + slug: "test-mode", + name: "Test Mode", + roleDefinition: "Test Role", + groups: ["read"], + }, + ], + } + + ;(fileExistsAtPath as Mock).mockImplementation(async (path: string) => { + return path === mockRoomodes + }) + ;(fs.readFile as Mock).mockImplementation(async (path: string) => { + if (path === mockRoomodes) { + return yaml.stringify(roomodesContent) + } + if (path.includes("rules-test-mode")) { + return "Rule content" + } + throw new Error("File not found") + }) + ;(fs.stat as Mock).mockResolvedValue({ isDirectory: () => true }) + + // Mock readdir to return entries with subdirectories + ;(fs.readdir as Mock).mockResolvedValue([ + { name: "rule1.md", isFile: () => true }, + { name: "rule2.md", isFile: () => true }, + ]) + + const result = await manager.exportModeWithRules("test-mode") + + expect(result.success).toBe(true) + + // Parse the YAML to check the paths + const exportedData = yaml.parse(result.yaml!) + const rulesFiles = exportedData.customModes[0].rulesFiles + + // Verify that all paths use forward slashes + expect(rulesFiles).toBeDefined() + expect(rulesFiles.length).toBe(2) + + // Check that all paths use forward slashes + rulesFiles.forEach((file: any) => { + expect(file.relativePath).not.toContain("\\") + expect(file.relativePath).toMatch(/^rules-test-mode\//) + }) + + // Ensure no backslashes in the entire exported YAML + expect(result.yaml).not.toContain("\\") + }) }) })