-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Custom Tool descriptions #4388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom Tool descriptions #4388
Conversation
Add ability to Override tools descriptions system prompt. Example usage add your description in .roo/tools/read_file.md it will override default tool description it supports args param replacement
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
|
Hey @aganonki, I checked your implementation and I see a couple of issues that should be considered: Security Concern: Performance Impact: Type Safety: Also I think that this is closely related to the Footgun system prompt we should probably consider adding a similar warning when an override is active. |
KJ7LNW
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review below:
| * @param file - The filename to read from the .roo directory | ||
| * @returns A promise that resolves to the file content or empty string | ||
| */ | ||
| export async function readToolOverride(cwd: string, file: string): Promise<string> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to integrate with existing rule directory scanning in src/services/roo-config/index.ts
| const overrideContent = await readToolOverrideWithArgs(args.cwd, "execute_command", args) | ||
| return overrideContent || getExecuteCommandDescription(args) | ||
| }, | ||
| read_file: async (args) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a huge amount of duplication here you should create a helper function to simplify this
if you can use the existing toolDescriptionMap without changing it, when you can map your changes in directly.
| * @param args - The tool arguments object containing values to interpolate | ||
| * @returns The content with interpolated values | ||
| */ | ||
| function interpolateToolDescription(content: string, args: any): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to do something like this for safety to scope exactly what you want to pass these to the expression for interpolation/evaluation, which provides additional safety:
function evaluateExpression(expr: string, context: Record<string, any>): any {
// Create a new function with the context object names
const func = new Function(...Object.keys(context), `return ${expr}`)
// Execute the function with the context values
return func(...Object.values(context))
}
...
const eval_context = {
// imports
os,
osName,
defaultShell,
// Helper functions
getMcpHubServerStatus,
getMcpHubServerNames,
// Resolved MCP values
mcpServersPath,
mcpSettingsFilePath,
// All template variables
...variables
}
...
/**
* Processes template literals in instruction files.
*
* Template Format:
* - ${ expr } - Will be evaluated and replaced with the result
* Example: ${ await mcpHub.getMcpServersPath() }
*
* - \${expr} - Will be skipped (not evaluated)
* Example: `console.log(\${value})`
*
* This allows template expressions to be used for dynamic content while preserving
* template literals in code examples. The key differences are:
* 1. Template expressions use a space after ${
* 2. Code examples escape the $ with a backslash
*
* The negative lookbehind (?<!\\) in the regex ensures we only match unescaped ${
* expressions, leaving escaped ones untouched.
*/
function interpolateTemplate(template: string): string {
return template.replace(/(?<!\\)\$\{([^}]+)\}/g, (match, expr) => {
try {
const result = evaluateExpression(expr.trim(), eval_context)
return String(result)
} catch (error) {
console.warn(`Failed to evaluate: ${expr}`)
return match
}
})
}This is from an old PR that I used in production for several months, which implements exactly what you are doing, but never got merged:
[ https://github.com/KJ7LNW/cline/blob/63c1e45cffb697cc3b58bde9d9851dbf4f497c7c/src/core/prompts/system.ts#L64-L136 ]
| */ | ||
| export async function readToolOverrideWithArgs(cwd: string, file: string, args: any): Promise<string> { | ||
| const content = await readToolOverride(cwd, file) | ||
| if (!content) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
be careful here because "" could be intentional: what if I have an empty file because I need to blank out the tool's instructions completely? I have run into cases where I really need to remove the instructions for a tool, so an empty file should be valid.
| function interpolateToolDescription(content: string, args: any): string { | ||
| // Replace ${args.property.method()} or ${args.property} patterns with actual values | ||
| return content.replace(/\$\{args\.([^}]+)\}/g, (match, expression) => { | ||
| try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure you support escaping symbols so that users can embed ${ foo } without breaking things as \${ foo }
see above for an example with a negative look ahead.
|
Good work, I have also wanted this feature, especially for testing changes to tool prompts and test if the change was useful before creating a prompt PR.
Okay but please make it dismissable: the reminder is annoying.
shrug. Unless something has changed recently, we already do this for the entire
|
|
stale |
Add ability to Override tools descriptions system prompt. Example usage - add your description in .roo/tools/read_file.md it will override default tool description it supports args param replacement with "
Related GitHub Issue
Closes: #4010
Description
Adds file based check in .roo/tools/
now it will look for {toolname.md} and if found replace current description with new one from file.
This allows users customize tool usage for specific models.
Some Models like GPT 4.1 and Sonnet 4 are great and require little guidance with tool usage so descriptions can be reduced drastically to achieve same effect.
Test Procedure
For ex. new file:
.roo/tools/read_file.md:
Type of Change
srcor test files.Pre-Submission Checklist
npm run lint).console.log) has been removed.npm test).mainbranch.npm run changesetif this PR includes user-facing changes or dependency updates.Screenshots / Videos
Documentation Updates
Needs new section for advanced users how to customize tool descriptions. Check Test Procedure similar steps how to change descriptions. This also needs needs Warning note that improper custom description will affect or even break ability to call tools.
Additional Notes
.roo/tools/read_file.md it will override default tool description
this supports original args param replacement with data from args for any property
Get in Touch
Roo discord: Basas
Important
Add feature to override tool descriptions with markdown files, supporting argument interpolation, and update related logic and tests.
.roo/tools/.${args}syntax.readToolOverrideWithArgs()intools.tsreads and interpolates tool descriptions.getToolDescriptionsForMode()intools/index.tsupdated to use overrides.generatePrompt()insystem.tsupdated to include overridden descriptions.tool-overrides.test.tsto verify override functionality and argument interpolation.toolDescriptionMapintools/index.tsto handle async description fetching.This description was created by
for 73ef45e. You can customize this summary. It will automatically update as commits are pushed.