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

Method execute

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

Source from the content-addressed store, hash-verified

18 argsSchema = ArgsSchema;
19
20 async execute(args: z.infer<typeof ArgsSchema>, ctx: ToolContext): Promise<ToolResult> {
21 const abs = path.isAbsolute(args.path) ? args.path : path.resolve(ctx.cwd, args.path);
22
23 // Safety: refuse to write outside cwd unless explicitly absolute and user-approved
24 const rel = path.relative(ctx.cwd, abs);
25 if (rel.startsWith('..') && !path.isAbsolute(args.path)) {
26 return { content: `[ERROR] Refusing to write outside cwd: ${args.path}`, isError: true };
27 }
28
29 // Refuse to overwrite binary files (would corrupt utf-8 conversion)
30 let before: string | null = null;
31 try {
32 const stat = await fs.stat(abs);
33 // Read as buffer first to check binary-ness
34 const buf = await fs.readFile(abs);
35 if (isBinaryBuffer(buf)) {
36 return {
37 content: `[BINARY_FILE_REFUSED] ${rel} is an existing binary file (${(stat.size / 1024).toFixed(1)}KB). write_file refuses to overwrite binary files because utf-8 round-tripping would corrupt them. If you really mean to replace this file, delete it first with: shell({ command: "rm '${rel}'", ... })`,
38 isError: true,
39 };
40 }
41 before = buf.toString('utf-8');
42 } catch (e: any) {
43 if (e.code !== 'ENOENT') {
44 // not a missing-file error
45 return { content: `[ERROR] Cannot inspect ${args.path}: ${e.message}`, isError: true };
46 }
47 // File doesn't exist — that's fine, we're creating it
48 }
49
50 // Permission check
51 const permReq = { tool: 'write_file', operation: rel, description: before ? `Overwrite ${rel}` : `Create ${rel}` };
52 const decision = ctx.permissions.evaluate(permReq);
53 if (decision === 'deny') {
54 return { content: `[PERMISSION_DENIED] Cannot write to ${args.path} (blocked by policy)`, isError: true };
55 }
56 let contentToWrite = args.content;
57 if (decision === 'ask') {
58 const sizeNote = (args.content.length > 64 * 1024)
59 ? ` [large file: ${(args.content.length / 1024).toFixed(1)}KB — diff display truncated]`
60 : '';
61 const dec = await confirmEdit(ctx, {
62 rel, before, after: args.content, absPath: abs, permReq,
63 label: (before ? `Overwrite ${rel}?` : `Create ${rel}?`) + sizeNote,
64 });
65 if (dec.kind === 'reject') {
66 return { content: `[USER_REJECTED] User declined to write ${args.path}`, isError: true };
67 }
68 if (dec.kind === 'revise') return reviseResult(rel);
69 contentToWrite = dec.content; // may be the user-edited version from [E] Edit
70 }
71
72 // Execute through transaction. `base` is the snapshot this overwrite was
73 // computed against (null = create). Re-checked after the approval pause.
74 try {
75 await ctx.transaction.write(abs, contentToWrite, { base: before, label: rel });
76 } catch (e: any) {
77 if (typeof e?.message === 'string' && e.message.startsWith('[FILE_CHANGED]')) {

Callers

nothing calls this directly

Calls 6

isBinaryBufferFunction · 0.85
confirmEditFunction · 0.85
reviseResultFunction · 0.85
resolveMethod · 0.80
evaluateMethod · 0.80
writeMethod · 0.45

Tested by

no test coverage detected