diff --git a/.changeset/write-text-file-drop-echo.md b/.changeset/write-text-file-drop-echo.md new file mode 100644 index 00000000..16acee78 --- /dev/null +++ b/.changeset/write-text-file-drop-echo.md @@ -0,0 +1,6 @@ +--- +"@perstack/base": patch +"perstack": patch +--- + +Replace echoed text with bytesWritten in writeTextFile tool result diff --git a/apps/base/src/tools/write-text-file.test.ts b/apps/base/src/tools/write-text-file.test.ts index 1bf791f0..99bab03e 100644 --- a/apps/base/src/tools/write-text-file.test.ts +++ b/apps/base/src/tools/write-text-file.test.ts @@ -2,7 +2,6 @@ import { afterEach, describe, expect, it } from "bun:test" import { existsSync } from "node:fs" import fs from "node:fs/promises" import { join } from "node:path" -import { MAX_TOOL_OUTPUT_CHARS } from "@perstack/core" import { validatePath } from "../lib/path.js" import { writeTextFile } from "./write-text-file.js" @@ -19,7 +18,7 @@ describe("writeTextFile tool", () => { expect(existsSync(testFile)).toBe(true) expect(await fs.readFile(testFile, "utf-8")).toBe(content) expect(result.path).toBe(await validatePath(testFile)) - expect(result.text).toBe(content) + expect(result.bytesWritten).toBe(Buffer.byteLength(content, "utf-8")) }) it("overwrites existing file", async () => { @@ -28,7 +27,7 @@ describe("writeTextFile tool", () => { const result = await writeTextFile({ path: testFile, text: newContent }) expect(await fs.readFile(testFile, "utf-8")).toBe(newContent) expect(result.path).toBe(await validatePath(testFile)) - expect(result.text).toBe(newContent) + expect(result.bytesWritten).toBe(Buffer.byteLength(newContent, "utf-8")) }) it("creates file in nested directory", async () => { @@ -38,23 +37,22 @@ describe("writeTextFile tool", () => { const result = await writeTextFile({ path: nestedFile, text: content }) expect(existsSync(nestedFile)).toBe(true) expect(await fs.readFile(nestedFile, "utf-8")).toBe(content) - expect(result.text).toBe(content) + expect(result.bytesWritten).toBe(Buffer.byteLength(content, "utf-8")) }) it("handles empty content", async () => { const result = await writeTextFile({ path: testFile, text: "" }) expect(existsSync(testFile)).toBe(true) expect(await fs.readFile(testFile, "utf-8")).toBe("") - expect(result.text).toBe("") + expect(result.bytesWritten).toBe(0) }) - it("truncates echoed text exceeding 30K characters", async () => { - const largeText = "B".repeat(50_000) - const result = await writeTextFile({ path: testFile, text: largeText }) - expect(result.text.length).toBeLessThanOrEqual(MAX_TOOL_OUTPUT_CHARS) - expect(result.text).toContain("... [truncated:") - // File itself should contain the full content - expect(await fs.readFile(testFile, "utf-8")).toBe(largeText) + it("returns correct bytesWritten for multi-byte characters", async () => { + const content = "hello world \u00e9\u00e0\u00fc" + const result = await writeTextFile({ path: testFile, text: content }) + expect(await fs.readFile(testFile, "utf-8")).toBe(content) + expect(result.bytesWritten).toBe(Buffer.byteLength(content, "utf-8")) + expect(result.bytesWritten).toBeGreaterThan(content.length) }) it("throws error if existing file is not writable", async () => { diff --git a/apps/base/src/tools/write-text-file.ts b/apps/base/src/tools/write-text-file.ts index 784554a0..6f613dbb 100644 --- a/apps/base/src/tools/write-text-file.ts +++ b/apps/base/src/tools/write-text-file.ts @@ -1,7 +1,6 @@ import { mkdir, stat } from "node:fs/promises" import { dirname } from "node:path" import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" -import { truncateText } from "@perstack/core" import { z } from "zod/v4" import { validatePath } from "../lib/path.js" import { safeWriteFile } from "../lib/safe-file.js" @@ -19,7 +18,7 @@ export async function writeTextFile(input: { path: string; text: string }) { await safeWriteFile(validatedPath, text) return { path: validatedPath, - text: truncateText(text), + bytesWritten: Buffer.byteLength(text, "utf-8"), } }