MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / execute

Method execute

src/tools/filesystem/edit.ts:21–134  ·  view source on GitHub ↗
(args: z.infer<typeof ArgsSchema>, ctx: ToolContext)

Source from the content-addressed store, hash-verified

19 argsSchema = ArgsSchema;
20
21 async execute(args: z.infer<typeof ArgsSchema>, ctx: ToolContext): Promise<ToolResult> {
22 const abs = path.isAbsolute(args.path) ? args.path : path.resolve(ctx.cwd, args.path);
23 const rel = path.relative(ctx.cwd, abs);
24
25 let content: string;
26 try {
27 content = await fs.readFile(abs, 'utf-8');
28 } catch (e: any) {
29 if (e.code === 'ENOENT') {
30 const { notFoundWithSuggestions } = await import('./suggest-paths.js');
31 const msg = await notFoundWithSuggestions(
32 ctx.cwd, args.path,
33 `[FILE_NOT_FOUND] ${args.path} doesn't exist. Use write_file to create it, or check the path with ls.`,
34 );
35 return { content: msg, isError: true };
36 }
37 return { content: `[ERROR] Cannot read ${args.path}: ${e.message}`, isError: true };
38 }
39
40 if (args.old_string === args.new_string) {
41 return { content: `[ERROR] old_string and new_string are identical — nothing to do.`, isError: true };
42 }
43
44 const { findMatch, reindentReplacement } = await import('./fuzzy-match.js');
45
46 let updated: string;
47 let occurrences: number;
48 let tierNote = '';
49
50 // replace_all keeps exact semantics (it's an explicit "every occurrence" op);
51 // fuzzy only applies to the single-match path where drift is the usual cause
52 // of failure.
53 if (args.replace_all) {
54 occurrences = content.split(args.old_string).length - 1;
55 if (occurrences === 0) {
56 return {
57 content: `[STRING_NOT_FOUND] old_string was not found in ${rel} (replace_all). ` +
58 `For a single fuzzy-tolerant edit, omit replace_all.`,
59 isError: true,
60 };
61 }
62 updated = content.split(args.old_string).join(args.new_string);
63 } else {
64 const match = findMatch(content, args.old_string, { allowFuzzy: true });
65 if (!match) {
66 // Helpful approximate-line hints, same as before.
67 const lines = content.split('\n');
68 const firstLine = args.old_string.split('\n')[0]?.trim();
69 const hints: string[] = [];
70 if (firstLine && firstLine.length > 3) {
71 for (let i = 0; i < lines.length; i++) {
72 if (lines[i]?.trim().startsWith(firstLine.slice(0, Math.min(40, firstLine.length)))) {
73 hints.push(` Line ${i + 1}: ${lines[i]?.trim().slice(0, 80)}`);
74 if (hints.length >= 3) break;
75 }
76 }
77 }
78 return {

Callers

nothing calls this directly

Calls 9

notFoundWithSuggestionsFunction · 0.85
findMatchFunction · 0.85
reindentReplacementFunction · 0.85
confirmEditFunction · 0.85
reviseResultFunction · 0.85
resolveMethod · 0.80
evaluateMethod · 0.80
pushMethod · 0.45
writeMethod · 0.45

Tested by

no test coverage detected