mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Allow architect mode to write md files
This commit is contained in:
@@ -5,16 +5,40 @@ import { TOOL_GROUPS, ToolGroup } from "../../shared/tool-groups"
|
||||
// Create a schema for valid tool groups using the keys of TOOL_GROUPS
|
||||
const ToolGroupSchema = z.enum(Object.keys(TOOL_GROUPS) as [ToolGroup, ...ToolGroup[]])
|
||||
|
||||
// Schema for group options with regex validation
|
||||
const GroupOptionsSchema = z.object({
|
||||
fileRegex: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine(
|
||||
(pattern) => {
|
||||
if (!pattern) return true // Optional, so empty is valid
|
||||
try {
|
||||
new RegExp(pattern)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{ message: "Invalid regular expression pattern" },
|
||||
),
|
||||
})
|
||||
|
||||
// Schema for a group entry - either a tool group string or a tuple of [group, options]
|
||||
const GroupEntrySchema = z.union([ToolGroupSchema, z.tuple([ToolGroupSchema, GroupOptionsSchema])])
|
||||
|
||||
// Schema for array of groups
|
||||
const GroupsArraySchema = z
|
||||
.array(ToolGroupSchema)
|
||||
.array(GroupEntrySchema)
|
||||
.min(1, "At least one tool group is required")
|
||||
.refine(
|
||||
(groups) => {
|
||||
const seen = new Set()
|
||||
return groups.every((group) => {
|
||||
if (seen.has(group)) return false
|
||||
seen.add(group)
|
||||
// For tuples, check the group name (first element)
|
||||
const groupName = Array.isArray(group) ? group[0] : group
|
||||
if (seen.has(groupName)) return false
|
||||
seen.add(groupName)
|
||||
return true
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,122 +1,72 @@
|
||||
import { validateCustomMode } from "../CustomModesSchema"
|
||||
import { ModeConfig } from "../../../shared/modes"
|
||||
import { ZodError } from "zod"
|
||||
import { CustomModeSchema } from "../CustomModesSchema"
|
||||
|
||||
describe("CustomModesSchema", () => {
|
||||
describe("validateCustomMode", () => {
|
||||
test("accepts valid mode configuration", () => {
|
||||
const validMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: ["read"] as const,
|
||||
} satisfies ModeConfig
|
||||
describe("CustomModeSchema", () => {
|
||||
it("validates a basic mode configuration", () => {
|
||||
const validMode = {
|
||||
slug: "test-mode",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: ["read", "browser"],
|
||||
}
|
||||
|
||||
expect(() => validateCustomMode(validMode)).not.toThrow()
|
||||
})
|
||||
expect(() => CustomModeSchema.parse(validMode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("accepts mode with multiple groups", () => {
|
||||
const validMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: ["read", "edit", "browser"] as const,
|
||||
} satisfies ModeConfig
|
||||
it("validates a mode with file restrictions", () => {
|
||||
const modeWithFileRestrictions = {
|
||||
slug: "markdown-editor",
|
||||
name: "Markdown Editor",
|
||||
roleDefinition: "Markdown editing mode",
|
||||
groups: ["read", ["edit", { fileRegex: "\\.md$" }], "browser"],
|
||||
}
|
||||
|
||||
expect(() => validateCustomMode(validMode)).not.toThrow()
|
||||
})
|
||||
expect(() => CustomModeSchema.parse(modeWithFileRestrictions)).not.toThrow()
|
||||
})
|
||||
|
||||
test("accepts mode with optional customInstructions", () => {
|
||||
const validMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
customInstructions: "Custom instructions",
|
||||
groups: ["read"] as const,
|
||||
} satisfies ModeConfig
|
||||
it("validates file regex patterns", () => {
|
||||
const validPatterns = ["\\.md$", ".*\\.txt$", "[a-z]+\\.js$"]
|
||||
const invalidPatterns = ["[", "(unclosed", "\\"]
|
||||
|
||||
expect(() => validateCustomMode(validMode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("rejects missing required fields", () => {
|
||||
const invalidModes = [
|
||||
{}, // All fields missing
|
||||
{ name: "Test" }, // Missing most fields
|
||||
{
|
||||
name: "Test",
|
||||
roleDefinition: "Role",
|
||||
}, // Missing slug and groups
|
||||
]
|
||||
|
||||
invalidModes.forEach((invalidMode) => {
|
||||
expect(() => validateCustomMode(invalidMode)).toThrow(ZodError)
|
||||
})
|
||||
})
|
||||
|
||||
test("rejects invalid slug format", () => {
|
||||
const invalidMode = {
|
||||
slug: "not@a@valid@slug",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: ["read"] as const,
|
||||
} satisfies Omit<ModeConfig, "slug"> & { slug: string }
|
||||
|
||||
expect(() => validateCustomMode(invalidMode)).toThrow(ZodError)
|
||||
expect(() => validateCustomMode(invalidMode)).toThrow("Slug must contain only letters numbers and dashes")
|
||||
})
|
||||
|
||||
test("rejects empty strings in required fields", () => {
|
||||
const emptyNameMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: ["read"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
const emptyRoleMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "",
|
||||
groups: ["read"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => validateCustomMode(emptyNameMode)).toThrow("Name is required")
|
||||
expect(() => validateCustomMode(emptyRoleMode)).toThrow("Role definition is required")
|
||||
})
|
||||
|
||||
test("rejects invalid group configurations", () => {
|
||||
const invalidGroupMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: ["not-a-valid-group"] as any,
|
||||
validPatterns.forEach((pattern) => {
|
||||
const mode = {
|
||||
slug: "test",
|
||||
name: "Test",
|
||||
roleDefinition: "Test",
|
||||
groups: ["read", ["edit", { fileRegex: pattern }]],
|
||||
}
|
||||
|
||||
expect(() => validateCustomMode(invalidGroupMode)).toThrow(ZodError)
|
||||
expect(() => CustomModeSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("rejects empty groups array", () => {
|
||||
const invalidMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
groups: [] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => validateCustomMode(invalidMode)).toThrow("At least one tool group is required")
|
||||
})
|
||||
|
||||
test("handles null and undefined gracefully", () => {
|
||||
expect(() => validateCustomMode(null)).toThrow(ZodError)
|
||||
expect(() => validateCustomMode(undefined)).toThrow(ZodError)
|
||||
})
|
||||
|
||||
test("rejects non-object inputs", () => {
|
||||
const invalidInputs = [42, "string", true, [], () => {}]
|
||||
|
||||
invalidInputs.forEach((input) => {
|
||||
expect(() => validateCustomMode(input)).toThrow(ZodError)
|
||||
})
|
||||
invalidPatterns.forEach((pattern) => {
|
||||
const mode = {
|
||||
slug: "test",
|
||||
name: "Test",
|
||||
roleDefinition: "Test",
|
||||
groups: ["read", ["edit", { fileRegex: pattern }]],
|
||||
}
|
||||
expect(() => CustomModeSchema.parse(mode)).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
it("prevents duplicate groups", () => {
|
||||
const modeWithDuplicates = {
|
||||
slug: "test",
|
||||
name: "Test",
|
||||
roleDefinition: "Test",
|
||||
groups: ["read", "read", ["edit", { fileRegex: "\\.md$" }], ["edit", { fileRegex: "\\.txt$" }]],
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(modeWithDuplicates)).toThrow(/Duplicate groups/)
|
||||
})
|
||||
|
||||
it("requires at least one group", () => {
|
||||
const modeWithNoGroups = {
|
||||
slug: "test",
|
||||
name: "Test",
|
||||
roleDefinition: "Test",
|
||||
groups: [],
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(modeWithNoGroups)).toThrow(/At least one tool group is required/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -232,6 +232,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -517,6 +520,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -802,6 +808,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -1135,6 +1144,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -1834,6 +1846,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -2167,6 +2182,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -2513,6 +2531,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- You should use apply_diff instead of write_to_file when making changes to existing files since it is much faster and easier to apply a diff than to write the entire file again. Only use write_to_file to edit files when apply_diff has failed repeatedly to apply the diff.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -2798,6 +2819,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -2909,7 +2933,7 @@ Mock generic rules"
|
||||
`;
|
||||
|
||||
exports[`addCustomInstructions should generate correct prompt for architect mode 1`] = `
|
||||
"You are Roo, a software architecture expert specializing in analyzing codebases, identifying patterns, and providing high-level technical guidance. You excel at understanding complex systems, evaluating architectural decisions, and suggesting improvements while maintaining a read-only approach to the codebase. Make sure to help the user come up with a solid implementation plan for their project and don't rush to switch to implementing code.
|
||||
"You are Roo, a software architecture expert specializing in analyzing codebases, identifying patterns, and providing high-level technical guidance. You excel at understanding complex systems, evaluating architectural decisions, and suggesting improvements. You can edit markdown documentation files to help document architectural decisions and patterns.
|
||||
|
||||
====
|
||||
|
||||
@@ -3002,6 +3026,43 @@ Example: Requesting to list all top level source code definitions in the current
|
||||
<path>.</path>
|
||||
</list_code_definition_names>
|
||||
|
||||
## write_to_file
|
||||
Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to write to (relative to the current working directory /test/path)
|
||||
- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
|
||||
- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
|
||||
Usage:
|
||||
<write_to_file>
|
||||
<path>File path here</path>
|
||||
<content>
|
||||
Your file content here
|
||||
</content>
|
||||
<line_count>total number of lines in the file, including empty lines</line_count>
|
||||
</write_to_file>
|
||||
|
||||
Example: Requesting to write to frontend-config.json
|
||||
<write_to_file>
|
||||
<path>frontend-config.json</path>
|
||||
<content>
|
||||
{
|
||||
"apiEndpoint": "https://api.example.com",
|
||||
"theme": {
|
||||
"primaryColor": "#007bff",
|
||||
"secondaryColor": "#6c757d",
|
||||
"fontFamily": "Arial, sans-serif"
|
||||
},
|
||||
"features": {
|
||||
"darkMode": true,
|
||||
"notifications": true,
|
||||
"analytics": false
|
||||
},
|
||||
"version": "1.0.0"
|
||||
}
|
||||
</content>
|
||||
<line_count>14</line_count>
|
||||
</write_to_file>
|
||||
|
||||
## ask_followup_question
|
||||
Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.
|
||||
Parameters:
|
||||
@@ -3089,6 +3150,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
@@ -3323,6 +3387,9 @@ RULES
|
||||
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.
|
||||
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
||||
- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool.
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
|
||||
@@ -8,6 +8,7 @@ export function getRulesSection(
|
||||
supportsComputerUse: boolean,
|
||||
diffStrategy?: DiffStrategy,
|
||||
context?: vscode.ExtensionContext,
|
||||
diffEnabled?: boolean,
|
||||
): string {
|
||||
const settingsDir = context ? path.join(context.globalStorageUri.fsPath, "settings") : "<settings directory>"
|
||||
const customModesPath = path.join(settingsDir, "cline_custom_modes.json")
|
||||
@@ -26,6 +27,9 @@ ${
|
||||
? "- You should use apply_diff instead of write_to_file when making changes to existing files since it is much faster and easier to apply a diff than to write the entire file again. Only use write_to_file to edit files when apply_diff has failed repeatedly to apply the diff."
|
||||
: "- When you want to modify a file, use the write_to_file tool directly with the desired content. You do not need to display the content before using the tool."
|
||||
}
|
||||
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
|
||||
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
||||
- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.
|
||||
- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.
|
||||
|
||||
@@ -78,7 +78,7 @@ ${getCapabilitiesSection(cwd, supportsComputerUse, mcpHub, effectiveDiffStrategy
|
||||
|
||||
${modesSection}
|
||||
|
||||
${getRulesSection(cwd, supportsComputerUse, diffStrategy, context)}
|
||||
${getRulesSection(cwd, supportsComputerUse, effectiveDiffStrategy, context)}
|
||||
|
||||
${getSystemInfoSection(cwd, mode, customModeConfigs)}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getUseMcpToolDescription } from "./use-mcp-tool"
|
||||
import { getAccessMcpResourceDescription } from "./access-mcp-resource"
|
||||
import { DiffStrategy } from "../../diff/DiffStrategy"
|
||||
import { McpHub } from "../../../services/mcp/McpHub"
|
||||
import { Mode, ModeConfig, getModeConfig, isToolAllowedForMode } from "../../../shared/modes"
|
||||
import { Mode, ModeConfig, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes"
|
||||
import { ToolName, getToolName, getToolOptions, TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS } from "../../../shared/tool-groups"
|
||||
import { ToolArgs } from "./types"
|
||||
|
||||
@@ -53,19 +53,33 @@ export function getToolDescriptionsForMode(
|
||||
const tools = new Set<string>()
|
||||
|
||||
// Add tools from mode's groups
|
||||
config.groups.forEach((group) => {
|
||||
TOOL_GROUPS[group].forEach((tool) => {
|
||||
if (isToolAllowedForMode(tool as ToolName, mode, customModes ?? [])) {
|
||||
tools.add(tool)
|
||||
}
|
||||
})
|
||||
config.groups.forEach((groupEntry) => {
|
||||
const groupName = getGroupName(groupEntry)
|
||||
const toolGroup = TOOL_GROUPS[groupName]
|
||||
if (toolGroup) {
|
||||
toolGroup.forEach((tool) => {
|
||||
if (isToolAllowedForMode(tool as ToolName, mode, customModes ?? [])) {
|
||||
tools.add(tool)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Filter out apply_diff if diffStrategy is not provided
|
||||
if (!diffStrategy) {
|
||||
tools.delete("apply_diff")
|
||||
}
|
||||
|
||||
// Add always available tools
|
||||
ALWAYS_AVAILABLE_TOOLS.forEach((tool) => tools.add(tool))
|
||||
|
||||
// Map tool descriptions for allowed tools
|
||||
const descriptions = Array.from(tools).map((toolName) => {
|
||||
// Skip apply_diff tool description if diffStrategy is not provided
|
||||
if (toolName === "apply_diff" && !diffStrategy) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const descriptionFn = toolDescriptionMap[toolName]
|
||||
if (!descriptionFn) {
|
||||
return undefined
|
||||
|
||||
107
src/shared/__tests__/modes.test.ts
Normal file
107
src/shared/__tests__/modes.test.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { isToolAllowedForMode, FileRestrictionError, ModeConfig } from "../modes"
|
||||
|
||||
describe("isToolAllowedForMode", () => {
|
||||
const customModes: ModeConfig[] = [
|
||||
{
|
||||
slug: "markdown-editor",
|
||||
name: "Markdown Editor",
|
||||
roleDefinition: "You are a markdown editor",
|
||||
groups: ["read", ["edit", { fileRegex: "\\.md$" }], "browser"],
|
||||
},
|
||||
{
|
||||
slug: "css-editor",
|
||||
name: "CSS Editor",
|
||||
roleDefinition: "You are a CSS editor",
|
||||
groups: ["read", ["edit", { fileRegex: "\\.css$" }], "browser"],
|
||||
},
|
||||
]
|
||||
|
||||
it("allows always available tools", () => {
|
||||
expect(isToolAllowedForMode("ask_followup_question", "markdown-editor", customModes)).toBe(true)
|
||||
expect(isToolAllowedForMode("attempt_completion", "markdown-editor", customModes)).toBe(true)
|
||||
})
|
||||
|
||||
it("allows unrestricted tools", () => {
|
||||
expect(isToolAllowedForMode("read_file", "markdown-editor", customModes)).toBe(true)
|
||||
expect(isToolAllowedForMode("browser_action", "markdown-editor", customModes)).toBe(true)
|
||||
})
|
||||
|
||||
describe("file restrictions", () => {
|
||||
it("allows editing matching files", () => {
|
||||
// Test markdown editor mode
|
||||
const mdResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, "test.md")
|
||||
expect(mdResult).toBe(true)
|
||||
|
||||
// Test CSS editor mode
|
||||
const cssResult = isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, "styles.css")
|
||||
expect(cssResult).toBe(true)
|
||||
})
|
||||
|
||||
it("rejects editing non-matching files", () => {
|
||||
// Test markdown editor mode with non-markdown file
|
||||
const mdError = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, "test.js")
|
||||
expect(mdError).toBeInstanceOf(FileRestrictionError)
|
||||
expect((mdError as FileRestrictionError).message).toContain("\\.md$")
|
||||
|
||||
// Test CSS editor mode with non-CSS file
|
||||
const cssError = isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, "test.js")
|
||||
expect(cssError).toBeInstanceOf(FileRestrictionError)
|
||||
expect((cssError as FileRestrictionError).message).toContain("\\.css$")
|
||||
})
|
||||
|
||||
it("requires file path for restricted edit operations", () => {
|
||||
const result = isToolAllowedForMode("write_to_file", "markdown-editor", customModes)
|
||||
expect(result).toBeInstanceOf(FileRestrictionError)
|
||||
expect((result as FileRestrictionError).message).toContain("\\.md$")
|
||||
})
|
||||
|
||||
it("applies restrictions to both write_to_file and apply_diff", () => {
|
||||
// Test write_to_file
|
||||
const writeResult = isToolAllowedForMode(
|
||||
"write_to_file",
|
||||
"markdown-editor",
|
||||
customModes,
|
||||
undefined,
|
||||
"test.md",
|
||||
)
|
||||
expect(writeResult).toBe(true)
|
||||
|
||||
// Test apply_diff
|
||||
const diffResult = isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, "test.md")
|
||||
expect(diffResult).toBe(true)
|
||||
|
||||
// Test both with non-matching file
|
||||
const writeError = isToolAllowedForMode(
|
||||
"write_to_file",
|
||||
"markdown-editor",
|
||||
customModes,
|
||||
undefined,
|
||||
"test.js",
|
||||
)
|
||||
expect(writeError).toBeInstanceOf(FileRestrictionError)
|
||||
|
||||
const diffError = isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, "test.js")
|
||||
expect(diffError).toBeInstanceOf(FileRestrictionError)
|
||||
})
|
||||
})
|
||||
|
||||
it("handles non-existent modes", () => {
|
||||
expect(isToolAllowedForMode("write_to_file", "non-existent", customModes)).toBe(false)
|
||||
})
|
||||
|
||||
it("respects tool requirements", () => {
|
||||
const toolRequirements = {
|
||||
write_to_file: false,
|
||||
}
|
||||
|
||||
expect(isToolAllowedForMode("write_to_file", "markdown-editor", customModes, toolRequirements)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("FileRestrictionError", () => {
|
||||
it("formats error message correctly", () => {
|
||||
const error = new FileRestrictionError("Markdown Editor", "\\.md$")
|
||||
expect(error.message).toBe("This mode (Markdown Editor) can only edit files matching the pattern: \\.md$")
|
||||
expect(error.name).toBe("FileRestrictionError")
|
||||
})
|
||||
})
|
||||
@@ -3,13 +3,22 @@ import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tool-groups"
|
||||
// Mode types
|
||||
export type Mode = string
|
||||
|
||||
// Group options type
|
||||
export type GroupOptions = {
|
||||
fileRegex?: string // Regular expression pattern
|
||||
description?: string // Human-readable description of the pattern
|
||||
}
|
||||
|
||||
// Group entry can be either a string or tuple with options
|
||||
export type GroupEntry = ToolGroup | readonly [ToolGroup, GroupOptions]
|
||||
|
||||
// Mode configuration type
|
||||
export type ModeConfig = {
|
||||
slug: string
|
||||
name: string
|
||||
roleDefinition: string
|
||||
customInstructions?: string
|
||||
groups: readonly ToolGroup[] // Now uses groups instead of tools array
|
||||
groups: readonly GroupEntry[] // Now supports both simple strings and tuples with options
|
||||
}
|
||||
|
||||
// Mode-specific prompts only
|
||||
@@ -22,13 +31,35 @@ export type CustomModePrompts = {
|
||||
[key: string]: PromptComponent | undefined
|
||||
}
|
||||
|
||||
// Helper to extract group name regardless of format
|
||||
export function getGroupName(group: GroupEntry): ToolGroup {
|
||||
return Array.isArray(group) ? group[0] : group
|
||||
}
|
||||
|
||||
// Helper to get group options if they exist
|
||||
function getGroupOptions(group: GroupEntry): GroupOptions | undefined {
|
||||
return Array.isArray(group) ? group[1] : undefined
|
||||
}
|
||||
|
||||
// Helper to check if a file path matches a regex pattern
|
||||
export function doesFileMatchRegex(filePath: string, pattern: string): boolean {
|
||||
try {
|
||||
const regex = new RegExp(pattern)
|
||||
return regex.test(filePath)
|
||||
} catch (error) {
|
||||
console.error(`Invalid regex pattern: ${pattern}`, error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get all tools for a mode
|
||||
export function getToolsForMode(groups: readonly ToolGroup[]): string[] {
|
||||
export function getToolsForMode(groups: readonly GroupEntry[]): string[] {
|
||||
const tools = new Set<string>()
|
||||
|
||||
// Add tools from each group
|
||||
groups.forEach((group) => {
|
||||
TOOL_GROUPS[group].forEach((tool) => tools.add(tool))
|
||||
const groupName = getGroupName(group)
|
||||
TOOL_GROUPS[groupName].forEach((tool) => tools.add(tool))
|
||||
})
|
||||
|
||||
// Always add required tools
|
||||
@@ -50,8 +81,8 @@ export const modes: readonly ModeConfig[] = [
|
||||
slug: "architect",
|
||||
name: "Architect",
|
||||
roleDefinition:
|
||||
"You are Roo, a software architecture expert specializing in analyzing codebases, identifying patterns, and providing high-level technical guidance. You excel at understanding complex systems, evaluating architectural decisions, and suggesting improvements while maintaining a read-only approach to the codebase. Make sure to help the user come up with a solid implementation plan for their project and don't rush to switch to implementing code.",
|
||||
groups: ["read", "browser", "mcp"],
|
||||
"You are Roo, a software architecture expert specializing in analyzing codebases, identifying patterns, and providing high-level technical guidance. You excel at understanding complex systems, evaluating architectural decisions, and suggesting improvements. You can edit markdown documentation files to help document architectural decisions and patterns.",
|
||||
groups: ["read", ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], "browser", "mcp"],
|
||||
},
|
||||
{
|
||||
slug: "ask",
|
||||
@@ -113,12 +144,21 @@ export function isCustomMode(slug: string, customModes?: ModeConfig[]): boolean
|
||||
return !!customModes?.some((mode) => mode.slug === slug)
|
||||
}
|
||||
|
||||
// Custom error class for file restrictions
|
||||
export class FileRestrictionError extends Error {
|
||||
constructor(mode: string, pattern: string) {
|
||||
super(`This mode (${mode}) can only edit files matching the pattern: ${pattern}`)
|
||||
this.name = "FileRestrictionError"
|
||||
}
|
||||
}
|
||||
|
||||
export function isToolAllowedForMode(
|
||||
tool: string,
|
||||
modeSlug: string,
|
||||
customModes: ModeConfig[],
|
||||
toolRequirements?: Record<string, boolean>,
|
||||
): boolean {
|
||||
filePath?: string, // Optional file path for checking regex patterns
|
||||
): boolean | FileRestrictionError {
|
||||
// Always allow these tools
|
||||
if (ALWAYS_AVAILABLE_TOOLS.includes(tool as any)) {
|
||||
return true
|
||||
@@ -136,8 +176,33 @@ export function isToolAllowedForMode(
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if tool is in any of the mode's groups
|
||||
return mode.groups.some((group) => TOOL_GROUPS[group].includes(tool as string))
|
||||
// Check if tool is in any of the mode's groups and respects any group options
|
||||
for (const group of mode.groups) {
|
||||
const groupName = getGroupName(group)
|
||||
const options = getGroupOptions(group)
|
||||
|
||||
// If the tool isn't in this group, continue to next group
|
||||
if (!TOOL_GROUPS[groupName].includes(tool)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If there are no options, allow the tool
|
||||
if (!options) {
|
||||
return true
|
||||
}
|
||||
|
||||
// For the edit group, check file regex if specified
|
||||
if (groupName === "edit" && options.fileRegex) {
|
||||
if (!filePath || !doesFileMatchRegex(filePath, options.fileRegex)) {
|
||||
return new FileRestrictionError(mode.name, options.fileRegex)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Create the mode-specific default prompts
|
||||
|
||||
@@ -8,7 +8,14 @@ import {
|
||||
VSCodeCheckbox,
|
||||
} from "@vscode/webview-ui-toolkit/react"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import { Mode, PromptComponent, getRoleDefinition, getAllModes, ModeConfig } from "../../../../src/shared/modes"
|
||||
import {
|
||||
Mode,
|
||||
PromptComponent,
|
||||
getRoleDefinition,
|
||||
getAllModes,
|
||||
ModeConfig,
|
||||
GroupEntry,
|
||||
} from "../../../../src/shared/modes"
|
||||
import {
|
||||
supportPrompt,
|
||||
SupportPromptType,
|
||||
@@ -26,6 +33,11 @@ type PromptsViewProps = {
|
||||
onDone: () => void
|
||||
}
|
||||
|
||||
// Helper to get group name regardless of format
|
||||
function getGroupName(group: GroupEntry): ToolGroup {
|
||||
return Array.isArray(group) ? group[0] : group
|
||||
}
|
||||
|
||||
const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
const {
|
||||
customModePrompts,
|
||||
@@ -131,7 +143,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
const [newModeSlug, setNewModeSlug] = useState("")
|
||||
const [newModeRoleDefinition, setNewModeRoleDefinition] = useState("")
|
||||
const [newModeCustomInstructions, setNewModeCustomInstructions] = useState("")
|
||||
const [newModeGroups, setNewModeGroups] = useState<readonly ToolGroup[]>(availableGroups)
|
||||
const [newModeGroups, setNewModeGroups] = useState<GroupEntry[]>(availableGroups)
|
||||
|
||||
// Reset form fields when dialog opens
|
||||
useEffect(() => {
|
||||
@@ -219,11 +231,11 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
const target = (e as CustomEvent)?.detail?.target || (e.target as HTMLInputElement)
|
||||
const checked = target.checked
|
||||
const oldGroups = customMode?.groups || []
|
||||
let newGroups: readonly ToolGroup[]
|
||||
let newGroups: GroupEntry[]
|
||||
if (checked) {
|
||||
newGroups = [...oldGroups, group]
|
||||
} else {
|
||||
newGroups = oldGroups.filter((g) => g !== group)
|
||||
newGroups = oldGroups.filter((g) => getGroupName(g) !== group)
|
||||
}
|
||||
if (customMode) {
|
||||
updateCustomMode(customMode.slug, {
|
||||
@@ -639,8 +651,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
const isCustomMode = findModeBySlug(mode, customModes)
|
||||
const customMode = isCustomMode
|
||||
const isGroupEnabled = isCustomMode
|
||||
? customMode?.groups?.includes(group)
|
||||
: currentMode?.groups?.includes(group)
|
||||
? customMode?.groups?.some((g) => getGroupName(g) === group)
|
||||
: currentMode?.groups?.some((g) => getGroupName(g) === group)
|
||||
|
||||
return (
|
||||
<VSCodeCheckbox
|
||||
@@ -649,6 +661,30 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
onChange={handleGroupChange(group, Boolean(isCustomMode), customMode)}
|
||||
disabled={!isCustomMode}>
|
||||
{GROUP_DISPLAY_NAMES[group]}
|
||||
{group === "edit" && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
marginTop: "2px",
|
||||
}}>
|
||||
Allowed files:{" "}
|
||||
{(() => {
|
||||
const currentMode = getCurrentMode()
|
||||
const editGroup = currentMode?.groups?.find(
|
||||
(g) =>
|
||||
Array.isArray(g) &&
|
||||
g[0] === "edit" &&
|
||||
g[1]?.fileRegex,
|
||||
)
|
||||
if (!Array.isArray(editGroup)) return "all files"
|
||||
return (
|
||||
editGroup[1].description ||
|
||||
`/${editGroup[1].fileRegex}/`
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
</VSCodeCheckbox>
|
||||
)
|
||||
})}
|
||||
@@ -664,7 +700,18 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
{(() => {
|
||||
const currentMode = getCurrentMode()
|
||||
const enabledGroups = currentMode?.groups || []
|
||||
return enabledGroups.map((group) => GROUP_DISPLAY_NAMES[group]).join(", ")
|
||||
return enabledGroups
|
||||
.map((group) => {
|
||||
const groupName = getGroupName(group)
|
||||
const displayName = GROUP_DISPLAY_NAMES[groupName]
|
||||
if (Array.isArray(group) && group[1]?.fileRegex) {
|
||||
const description =
|
||||
group[1].description || `/${group[1].fileRegex}/`
|
||||
return `${displayName} (${description})`
|
||||
}
|
||||
return displayName
|
||||
})
|
||||
.join(", ")
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
@@ -1050,7 +1097,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
{availableGroups.map((group) => (
|
||||
<VSCodeCheckbox
|
||||
key={group}
|
||||
checked={newModeGroups.includes(group)}
|
||||
checked={newModeGroups.some((g) => getGroupName(g) === group)}
|
||||
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
|
||||
const target =
|
||||
(e as CustomEvent)?.detail?.target || (e.target as HTMLInputElement)
|
||||
@@ -1058,7 +1105,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
if (checked) {
|
||||
setNewModeGroups([...newModeGroups, group])
|
||||
} else {
|
||||
setNewModeGroups(newModeGroups.filter((g) => g !== group))
|
||||
setNewModeGroups(
|
||||
newModeGroups.filter((g) => getGroupName(g) !== group),
|
||||
)
|
||||
}
|
||||
}}>
|
||||
{GROUP_DISPLAY_NAMES[group]}
|
||||
|
||||
Reference in New Issue
Block a user