summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/tools/write-file.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-04 21:21:20 +0900
committerAdam Malczewski <[email protected]>2026-06-04 21:21:20 +0900
commit394f1ed37ce860da6fdc385769bf29f9737105cd (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /packages/core/src/tools/write-file.ts
parent81a9cdbadf8c9d940d4fe9a2a0de607dee1f5f1a (diff)
downloaddispatch-394f1ed37ce860da6fdc385769bf29f9737105cd.tar.gz
dispatch-394f1ed37ce860da6fdc385769bf29f9737105cd.zip
chore: genesis — remove all files to rebuild from scratch (arch rewrite)
Diffstat (limited to 'packages/core/src/tools/write-file.ts')
-rw-r--r--packages/core/src/tools/write-file.ts66
1 files changed, 0 insertions, 66 deletions
diff --git a/packages/core/src/tools/write-file.ts b/packages/core/src/tools/write-file.ts
deleted file mode 100644
index 8a73352..0000000
--- a/packages/core/src/tools/write-file.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { mkdir, writeFile } from "node:fs/promises";
-import { dirname } from "node:path";
-import { z } from "zod";
-import type { ToolDefinition } from "../types/index.js";
-import { canonicalize } from "./path-utils.js";
-
-/**
- * Optional hook invoked AFTER a successful write, with the canonicalized
- * absolute path of the file just written. Its returned string (when non-empty)
- * is appended to the tool result. This is how LSP diagnostics are surfaced
- * back to the model on write without coupling `@dispatch/core`'s tools to the
- * API layer or the LSP manager — the host wires an implementation that touches
- * the file through the LSP and formats any diagnostics. Errors thrown here are
- * swallowed so a flaky LSP never fails the write itself.
- */
-export type AfterWriteHook = (absolutePath: string) => Promise<string>;
-
-export function createWriteFileTool(
- workingDirectory: string,
- onAfterWrite?: AfterWriteHook,
-): ToolDefinition {
- return {
- name: "write_file",
- description: "Write content to a file relative to the working directory.",
- parameters: z.object({
- path: z.string().describe("Path to the file, relative to the working directory"),
- content: z.string().describe("Content to write to the file"),
- }),
- execute: async (args: Record<string, unknown>): Promise<string> => {
- const filePath = args.path as string;
- const content = args.content as string;
- // Canonicalize so a workdir-relative path that resolves through
- // symlinks to outside the workdir is detected and blocked. The
- // canonicalize walks up to the nearest existing ancestor when the
- // leaf doesn't exist (typical for write_file), so a path like
- // `workdir/escape-link/new-file.txt` where `escape-link` symlinks
- // to /etc still resolves through the symlink and is caught here.
- const absolutePath = await canonicalize(workingDirectory, filePath);
- const absoluteWorkDir = await canonicalize(workingDirectory);
-
- if (absolutePath !== absoluteWorkDir && !absolutePath.startsWith(`${absoluteWorkDir}/`)) {
- return `Error: Path "${filePath}" is outside the working directory.`;
- }
-
- try {
- await mkdir(dirname(absolutePath), { recursive: true });
- await writeFile(absolutePath, content, "utf8");
- } catch (err) {
- return `Error writing file: ${err instanceof Error ? err.message : String(err)}`;
- }
-
- let result = `Successfully wrote to "${filePath}".`;
- // Post-write hook (e.g. LSP diagnostics). Best-effort: never let a
- // hook failure turn a successful write into an error.
- if (onAfterWrite) {
- try {
- const extra = await onAfterWrite(absolutePath);
- if (extra) result += `\n\n${extra}`;
- } catch {
- /* ignore — diagnostics are advisory */
- }
- }
- return result;
- },
- };
-}