Browse Source

fix: normalize Windows paths to forward slashes in mode export (#6308)

Co-authored-by: Roo Code <[email protected]>
roomote[bot] 5 months ago
parent
commit
b27673e41f

+ 3 - 1
src/core/config/CustomModesManager.ts

@@ -786,7 +786,9 @@ export class CustomModesManager {
 								const relativePath = isGlobalMode
 								const relativePath = isGlobalMode
 									? path.relative(baseDir, filePath)
 									? path.relative(baseDir, filePath)
 									: path.relative(path.join(baseDir, ".roo"), 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() })
 							}
 							}
 						}
 						}
 					}
 					}

+ 54 - 0
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-test-mode")
 			expect(result.yaml).toContain("Global rule content")
 			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("\\")
+		})
 	})
 	})
 })
 })