/** * Write tool — overwrites or creates a file inside the workspace. */ import fs from 'fs'; import path from 'path'; import type { PiTool } from './types.js'; import { safeResolve, displayPath } from './path-safety.js'; const MAX_BYTES = 1024 * 1024; // 1 MB cap to avoid runaway writes export const writeTool: PiTool = { name: 'Write', description: 'Create or overwrite a file in the workspace with the given content. Creates parent directories as needed.', inputSchema: { type: 'object', properties: { file_path: { type: 'string', description: 'Destination path. Relative paths resolve against the workspace root.' }, content: { type: 'string', description: 'Full file contents.' }, }, required: ['file_path', 'content'], }, async run(input, ctx) { let abs: string; try { abs = safeResolve(ctx.cwd, input?.file_path); } catch (err: any) { return { output: err.message, isError: true }; } const content = typeof input?.content === 'string' ? input.content : ''; if (content.length > MAX_BYTES) { return { output: `Content too large (${content.length} bytes; max ${MAX_BYTES}).`, isError: true }; } try { fs.mkdirSync(path.dirname(abs), { recursive: true }); fs.writeFileSync(abs, content, 'utf-8'); return { output: `Wrote ${content.length} bytes to ${displayPath(ctx.cwd, abs)}` }; } catch (err: any) { return { output: `Write failed: ${err.message}`, isError: true }; } }, };