diff --git a/schemas/roomodes.json b/schemas/roomodes.json index 14e6b53b4b..90b6f55fa0 100644 --- a/schemas/roomodes.json +++ b/schemas/roomodes.json @@ -18,8 +18,8 @@ "definitions": { "ToolGroup": { "type": "string", - "enum": ["read", "edit", "command", "mcp", "modes"], - "description": "A tool group name that grants the mode access to a set of tools." + "enum": ["read", "edit", "browser", "command", "mcp", "modes"], + "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." }, "GroupOptions": { "type": "object", @@ -48,6 +48,22 @@ "description": "A tool group permission entry. Either a simple tool group name string, or a [toolGroupName, options] tuple for groups with file restrictions.", "oneOf": [{ "$ref": "#/definitions/ToolGroup" }, { "$ref": "#/definitions/GroupEntryTuple" }] }, + "RuleFile": { + "type": "object", + "description": "A rules file associated with a mode, used during import/export.", + "required": ["relativePath", "content"], + "additionalProperties": false, + "properties": { + "relativePath": { + "type": "string", + "description": "The relative file path for the rules file." + }, + "content": { + "type": "string", + "description": "The text content of the rules file." + } + } + }, "CustomMode": { "type": "object", "description": "A custom mode definition.", @@ -92,6 +108,13 @@ "type": "string", "enum": ["global", "project"], "description": "Where this mode was defined. Automatically set by Roo Code." + }, + "rulesFiles": { + "type": "array", + "description": "Rules files associated with this mode, used during import/export.", + "items": { + "$ref": "#/definitions/RuleFile" + } } } } diff --git a/src/utils/__tests__/roomodes-schema.spec.ts b/src/utils/__tests__/roomodes-schema.spec.ts index 6e410a40bc..52784ee430 100644 --- a/src/utils/__tests__/roomodes-schema.spec.ts +++ b/src/utils/__tests__/roomodes-schema.spec.ts @@ -273,6 +273,119 @@ describe("roomodes JSON schema", () => { expect(valid).toBe(true) }) + it("should accept the browser tool group (deprecated but valid)", () => { + const config = { + customModes: [ + { + slug: "browser-mode", + name: "Browser Mode", + roleDefinition: "A mode that uses the browser tool group.", + groups: ["read", "browser", "command"], + }, + ], + } + + const valid = validate(config) + expect(validate.errors).toBeNull() + expect(valid).toBe(true) + }) + + it("should accept a browser tuple group entry", () => { + const config = { + customModes: [ + { + slug: "browser-tuple", + name: "Browser Tuple", + roleDefinition: "A mode with browser tuple.", + groups: [["browser", { fileRegex: "\\.html$", description: "HTML files only" }]], + }, + ], + } + + const valid = validate(config) + expect(validate.errors).toBeNull() + expect(valid).toBe(true) + }) + + it("should accept a mode with rulesFiles", () => { + const config = { + customModes: [ + { + slug: "rules-mode", + name: "Rules Mode", + roleDefinition: "A mode with rules files.", + groups: ["read"], + rulesFiles: [ + { + relativePath: "rule1.md", + content: "# Rule 1\nFollow this rule.", + }, + { + relativePath: "subfolder/rule2.md", + content: "# Rule 2\nFollow this other rule.", + }, + ], + }, + ], + } + + const valid = validate(config) + expect(validate.errors).toBeNull() + expect(valid).toBe(true) + }) + + it("should accept a mode with empty rulesFiles array", () => { + const config = { + customModes: [ + { + slug: "empty-rules", + name: "Empty Rules", + roleDefinition: "A mode with empty rules files.", + groups: ["read"], + rulesFiles: [], + }, + ], + } + + const valid = validate(config) + expect(validate.errors).toBeNull() + expect(valid).toBe(true) + }) + + it("should reject rulesFiles entries missing required fields", () => { + const config = { + customModes: [ + { + slug: "bad-rules", + name: "Bad Rules", + roleDefinition: "A mode with invalid rules files.", + groups: ["read"], + rulesFiles: [{ relativePath: "rule1.md" }], + }, + ], + } + + const valid = validate(config) + expect(valid).toBe(false) + }) + + it("should reject rulesFiles entries with extra properties", () => { + const config = { + customModes: [ + { + slug: "extra-rules", + name: "Extra Rules", + roleDefinition: "A mode with extra rule properties.", + groups: ["read"], + rulesFiles: [{ relativePath: "rule1.md", content: "content", extra: true }], + }, + ], + } + + const valid = validate(config) + expect(valid).toBe(false) + }) + it("should accept multiple modes", () => { const config = { customModes: [