From 1bdaccbd5f127146978195540388d51e146a069c Mon Sep 17 00:00:00 2001 From: Janni Turunen Date: Mon, 23 Feb 2026 21:17:19 +0200 Subject: [PATCH] feat(hashline): create file on hashline_read of non-existent path (#324) --- packages/opencode/src/tool/hashline_read.ts | 34 +++++++++---------- .../opencode/test/tool/hashline_read.test.ts | 21 ++++++++---- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/packages/opencode/src/tool/hashline_read.ts b/packages/opencode/src/tool/hashline_read.ts index beece02d25d..ed11654a6d8 100644 --- a/packages/opencode/src/tool/hashline_read.ts +++ b/packages/opencode/src/tool/hashline_read.ts @@ -1,5 +1,5 @@ import z from "zod" -import * as fs from "fs" +import * as fs from "fs/promises" import * as path from "path" import { Tool } from "./tool" import { LSP } from "../lsp" @@ -48,23 +48,21 @@ export const HashlineReadTool = Tool.define("hashline_read", { }) if (!stat) { - const dir = path.dirname(filepath) - const base = path.basename(filepath) - - const dirEntries = fs.readdirSync(dir) - const suggestions = dirEntries - .filter( - (entry) => - entry.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(entry.toLowerCase()), - ) - .map((entry) => path.join(dir, entry)) - .slice(0, 3) - - if (suggestions.length > 0) { - throw new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`) + await fs.mkdir(path.dirname(filepath), { recursive: true }) + await Bun.write(filepath, "") + + FileTime.read(ctx.sessionID, filepath) + FileTime.hashlineRead(ctx.sessionID, filepath) + + return { + title, + output: [`${filepath}`, `file`, ""].join("\n") + "\n\n\n(File created successfully - empty and ready for editing)", + metadata: { + preview: "", + truncated: false, + loaded: [], + }, } - - throw new Error(`File not found: ${filepath}`) } if (stat.isDirectory()) { @@ -192,4 +190,4 @@ async function isBinaryFile(filepath: string, file: Bun.BunFile): Promise 0.3 -} \ No newline at end of file +} diff --git a/packages/opencode/test/tool/hashline_read.test.ts b/packages/opencode/test/tool/hashline_read.test.ts index 17ba3a62421..3234015157e 100644 --- a/packages/opencode/test/tool/hashline_read.test.ts +++ b/packages/opencode/test/tool/hashline_read.test.ts @@ -109,17 +109,24 @@ describe("tool.hashline_read binary file detection", () => { }) describe("tool.hashline_read non-existent file", () => { - test("returns error for non-existent file", async () => { + test("creates non-existent file and returns successfully", async () => { await using tmp = await tmpdir({}) await Instance.provide({ directory: tmp.path, fn: async () => { const hashlineRead = await HashlineReadTool.init() - const error = await hashlineRead - .execute({ filePath: path.join(tmp.path, "nonexistent.txt") }, ctx) - .catch((e) => e) - expect(error).toBeInstanceOf(Error) - expect(error.message).toContain("File not found") + const filePath = path.join(tmp.path, "nonexistent.txt") + + expect(await Bun.file(filePath).exists()).toBe(false) + + const result = await hashlineRead.execute({ filePath }, ctx) + + expect(result).toBeDefined() + expect(await Bun.file(filePath).exists()).toBe(true) + expect(result.output).toContain(`${filePath}`) + expect(result.output).toContain("file") + expect(result.output).toContain("(File created successfully - empty and ready for editing)") + expect(result.metadata.preview).toBe("") }, }) }) @@ -265,4 +272,4 @@ describe("tool.hashline_read CJK byte counting", () => { }, }) }) -}) \ No newline at end of file +})