mirror of
https://github.com/n8n-io/n8n
synced 2026-04-21 15:47:20 +00:00
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Albert Alises <albert.alises@gmail.com> Co-authored-by: Jaakko Husso <jaakko@n8n.io> Co-authored-by: Dimitri Lavrenük <20122620+dlavrenuek@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Tuukka Kantola <Tuukkaa@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Co-authored-by: Raúl Gómez Morales <raul00gm@gmail.com> Co-authored-by: Elias Meire <elias@meire.dev> Co-authored-by: Dimitri Lavrenük <dimitri.lavrenuek@n8n.io> Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Co-authored-by: Mutasem Aldmour <mutasem@n8n.io>
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
import * as fs from 'node:fs/promises';
|
|
import { z } from 'zod';
|
|
|
|
import type { ToolDefinition } from '../types';
|
|
import { formatCallToolResult } from '../utils';
|
|
import { MAX_FILE_SIZE } from './constants';
|
|
import { buildFilesystemResource, resolveSafePath } from './fs-utils';
|
|
|
|
const inputSchema = z.object({
|
|
filePath: z.string().describe('File path relative to root'),
|
|
oldString: z.string().min(1).describe('Exact string to find and replace (first occurrence)'),
|
|
newString: z.string().describe('Replacement string'),
|
|
});
|
|
|
|
export const editFileTool: ToolDefinition<typeof inputSchema> = {
|
|
name: 'edit_file',
|
|
description:
|
|
'Apply a targeted search-and-replace to a file. Replaces the first occurrence of oldString with newString. Fails if oldString is not found.',
|
|
inputSchema,
|
|
annotations: {},
|
|
async getAffectedResources({ filePath }, { dir }) {
|
|
return [
|
|
await buildFilesystemResource(dir, filePath, 'filesystemWrite', `Edit file: ${filePath}`),
|
|
];
|
|
},
|
|
async execute({ filePath, oldString, newString }, { dir }) {
|
|
const resolvedPath = await resolveSafePath(dir, filePath);
|
|
|
|
const stat = await fs.stat(resolvedPath);
|
|
if (stat.size > MAX_FILE_SIZE) {
|
|
throw new Error(
|
|
`File too large: ${stat.size} bytes (max ${MAX_FILE_SIZE} bytes). Use write_file to replace the entire content.`,
|
|
);
|
|
}
|
|
|
|
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
|
|
if (!content.includes(oldString)) {
|
|
throw new Error(`oldString not found in file: ${filePath}`);
|
|
}
|
|
|
|
await fs.writeFile(resolvedPath, content.replace(oldString, newString), 'utf-8');
|
|
|
|
return formatCallToolResult({ path: filePath });
|
|
},
|
|
};
|