added edit tool make precise edits

This commit is contained in:
Arjun 2026-01-16 15:41:24 +05:30
parent 8029877792
commit de6e9d85dd
3 changed files with 62 additions and 10 deletions

View file

@ -88,12 +88,12 @@ When a user asks for ANY task that might require external capabilities (web sear
## Workspace Access & Scope
- You have full read/write access inside \`\${BASE_DIR}\` (this resolves to the user's \`~/.rowboat\` directory). Create folders, files, and agents there using builtin tools or allowed shell commands—don't wait for the user to do it manually.
- If a user mentions a different root (e.g., \`~/.rowboatx\` or another path), clarify whether they meant the Rowboat workspace and propose the equivalent path you can act on. Only refuse if they explicitly insist on an inaccessible location.
- Prefer builtin file tools (\`workspace-writeFile\`, \`workspace-remove\`, \`workspace-readdir\`) for workspace changes. Reserve refusal or "you do it" responses for cases that are truly outside the Rowboat sandbox.
- Prefer builtin file tools (\`workspace-edit\` for modifications, \`workspace-writeFile\` for new files, \`workspace-remove\`, \`workspace-readdir\`) for workspace changes. Reserve refusal or "you do it" responses for cases that are truly outside the Rowboat sandbox.
## Builtin Tools vs Shell Commands
**IMPORTANT**: Rowboat provides builtin tools that are internal and do NOT require security allowlist entries:
- \`workspace-readFile\`, \`workspace-writeFile\`, \`workspace-remove\` - File operations
- \`workspace-readFile\`, \`workspace-writeFile\`, \`workspace-edit\`, \`workspace-remove\` - File operations
- \`workspace-readdir\`, \`workspace-exists\`, \`workspace-stat\` - Directory exploration
- \`workspace-mkdir\`, \`workspace-rename\`, \`workspace-copy\` - File/directory management
- \`analyzeAgent\` - Agent analysis

View file

@ -168,6 +168,7 @@ The Rowboat copilot has access to special builtin tools that regular agents don'
- \`workspace-readdir\` - List directory contents (supports recursive exploration)
- \`workspace-readFile\` - Read file contents
- \`workspace-writeFile\` - Create or update file contents
- \`workspace-edit\` - Make precise edits by replacing specific text (safer than full rewrites)
- \`workspace-remove\` - Remove files or directories
- \`workspace-exists\` - Check if a file or directory exists
- \`workspace-stat\` - Get file/directory statistics

View file

@ -156,14 +156,14 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
mkdirp: z.boolean().optional().describe('Create parent directories if needed (default: true)'),
expectedEtag: z.string().optional().describe('ETag to check for concurrent modifications (conflict detection)'),
}),
execute: async ({
path: relPath,
data,
encoding,
atomic,
mkdirp,
expectedEtag
}: {
execute: async ({
path: relPath,
data,
encoding,
atomic,
mkdirp,
expectedEtag
}: {
path: string;
data: string;
encoding?: 'utf8' | 'base64' | 'binary';
@ -186,6 +186,57 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
},
},
'workspace-edit': {
description: 'Make precise edits to a file by replacing specific text. Safer than rewriting entire files - produces smaller diffs and reduces risk of data loss.',
inputSchema: z.object({
path: z.string().min(1).describe('Workspace-relative file path'),
oldString: z.string().describe('Exact text to find and replace'),
newString: z.string().describe('Replacement text'),
replaceAll: z.boolean().optional().describe('Replace all occurrences (default: false, fails if not unique)'),
}),
execute: async ({
path: relPath,
oldString,
newString,
replaceAll = false
}: {
path: string;
oldString: string;
newString: string;
replaceAll?: boolean;
}) => {
try {
const result = await workspace.readFile(relPath, 'utf8');
const content = result.data;
const occurrences = content.split(oldString).length - 1;
if (occurrences === 0) {
return { error: 'oldString not found in file' };
}
if (occurrences > 1 && !replaceAll) {
return {
error: `oldString found ${occurrences} times. Use replaceAll: true or provide more context to make it unique.`
};
}
const newContent = replaceAll
? content.replaceAll(oldString, newString)
: content.replace(oldString, newString);
await workspace.writeFile(relPath, newContent, { encoding: 'utf8' });
return {
success: true,
replacements: replaceAll ? occurrences : 1
};
} catch (error) {
return { error: error instanceof Error ? error.message : 'Unknown error' };
}
},
},
'workspace-mkdir': {
description: 'Create a directory in the workspace',
inputSchema: z.object({