Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit d399085

Browse files
committed
fix: add browser tool group and rulesFiles property to roomodes schema
- Add "browser" to ToolGroup enum (deprecated but accepted for backward compat) - Add RuleFile definition with relativePath and content properties - Add optional rulesFiles array property to CustomMode - Add 6 new tests covering browser groups and rulesFiles validation
1 parent cfc8a55 commit d399085

2 files changed

Lines changed: 138 additions & 2 deletions

File tree

schemas/roomodes.json

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
"definitions": {
1919
"ToolGroup": {
2020
"type": "string",
21-
"enum": ["read", "edit", "command", "mcp", "modes"],
22-
"description": "A tool group name that grants the mode access to a set of tools."
21+
"enum": ["read", "edit", "browser", "command", "mcp", "modes"],
22+
"description": "A tool group name that grants the mode access to a set of tools. Note: 'browser' is deprecated but still accepted for backward compatibility."
2323
},
2424
"GroupOptions": {
2525
"type": "object",
@@ -48,6 +48,22 @@
4848
"description": "A tool group permission entry. Either a simple tool group name string, or a [toolGroupName, options] tuple for groups with file restrictions.",
4949
"oneOf": [{ "$ref": "#/definitions/ToolGroup" }, { "$ref": "#/definitions/GroupEntryTuple" }]
5050
},
51+
"RuleFile": {
52+
"type": "object",
53+
"description": "A rules file associated with a mode, used during import/export.",
54+
"required": ["relativePath", "content"],
55+
"additionalProperties": false,
56+
"properties": {
57+
"relativePath": {
58+
"type": "string",
59+
"description": "The relative file path for the rules file."
60+
},
61+
"content": {
62+
"type": "string",
63+
"description": "The text content of the rules file."
64+
}
65+
}
66+
},
5167
"CustomMode": {
5268
"type": "object",
5369
"description": "A custom mode definition.",
@@ -92,6 +108,13 @@
92108
"type": "string",
93109
"enum": ["global", "project"],
94110
"description": "Where this mode was defined. Automatically set by Roo Code."
111+
},
112+
"rulesFiles": {
113+
"type": "array",
114+
"description": "Rules files associated with this mode, used during import/export.",
115+
"items": {
116+
"$ref": "#/definitions/RuleFile"
117+
}
95118
}
96119
}
97120
}

src/utils/__tests__/roomodes-schema.spec.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,119 @@ describe("roomodes JSON schema", () => {
273273
expect(valid).toBe(true)
274274
})
275275

276+
it("should accept the browser tool group (deprecated but valid)", () => {
277+
const config = {
278+
customModes: [
279+
{
280+
slug: "browser-mode",
281+
name: "Browser Mode",
282+
roleDefinition: "A mode that uses the browser tool group.",
283+
groups: ["read", "browser", "command"],
284+
},
285+
],
286+
}
287+
288+
const valid = validate(config)
289+
expect(validate.errors).toBeNull()
290+
expect(valid).toBe(true)
291+
})
292+
293+
it("should accept a browser tuple group entry", () => {
294+
const config = {
295+
customModes: [
296+
{
297+
slug: "browser-tuple",
298+
name: "Browser Tuple",
299+
roleDefinition: "A mode with browser tuple.",
300+
groups: [["browser", { fileRegex: "\\.html$", description: "HTML files only" }]],
301+
},
302+
],
303+
}
304+
305+
const valid = validate(config)
306+
expect(validate.errors).toBeNull()
307+
expect(valid).toBe(true)
308+
})
309+
310+
it("should accept a mode with rulesFiles", () => {
311+
const config = {
312+
customModes: [
313+
{
314+
slug: "rules-mode",
315+
name: "Rules Mode",
316+
roleDefinition: "A mode with rules files.",
317+
groups: ["read"],
318+
rulesFiles: [
319+
{
320+
relativePath: "rule1.md",
321+
content: "# Rule 1\nFollow this rule.",
322+
},
323+
{
324+
relativePath: "subfolder/rule2.md",
325+
content: "# Rule 2\nFollow this other rule.",
326+
},
327+
],
328+
},
329+
],
330+
}
331+
332+
const valid = validate(config)
333+
expect(validate.errors).toBeNull()
334+
expect(valid).toBe(true)
335+
})
336+
337+
it("should accept a mode with empty rulesFiles array", () => {
338+
const config = {
339+
customModes: [
340+
{
341+
slug: "empty-rules",
342+
name: "Empty Rules",
343+
roleDefinition: "A mode with empty rules files.",
344+
groups: ["read"],
345+
rulesFiles: [],
346+
},
347+
],
348+
}
349+
350+
const valid = validate(config)
351+
expect(validate.errors).toBeNull()
352+
expect(valid).toBe(true)
353+
})
354+
355+
it("should reject rulesFiles entries missing required fields", () => {
356+
const config = {
357+
customModes: [
358+
{
359+
slug: "bad-rules",
360+
name: "Bad Rules",
361+
roleDefinition: "A mode with invalid rules files.",
362+
groups: ["read"],
363+
rulesFiles: [{ relativePath: "rule1.md" }],
364+
},
365+
],
366+
}
367+
368+
const valid = validate(config)
369+
expect(valid).toBe(false)
370+
})
371+
372+
it("should reject rulesFiles entries with extra properties", () => {
373+
const config = {
374+
customModes: [
375+
{
376+
slug: "extra-rules",
377+
name: "Extra Rules",
378+
roleDefinition: "A mode with extra rule properties.",
379+
groups: ["read"],
380+
rulesFiles: [{ relativePath: "rule1.md", content: "content", extra: true }],
381+
},
382+
],
383+
}
384+
385+
const valid = validate(config)
386+
expect(valid).toBe(false)
387+
})
388+
276389
it("should accept multiple modes", () => {
277390
const config = {
278391
customModes: [

0 commit comments

Comments
 (0)