Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/core/config/CustomModesManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() })
}
}
}
Expand Down
54 changes: 54 additions & 0 deletions src/core/config/__tests__/CustomModesManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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("\\")
})
})
})