refactor(effect): move read tool onto defineEffect

pull/21016/head
Kit Langton 2026-04-02 22:54:01 -04:00
parent 288eb044cb
commit 62f1421120
2 changed files with 214 additions and 194 deletions

View File

@ -1,4 +1,5 @@
import z from "zod" import z from "zod"
import { Effect } from "effect"
import { createReadStream } from "fs" import { createReadStream } from "fs"
import * as fs from "fs/promises" import * as fs from "fs/promises"
import * as path from "path" import * as path from "path"
@ -18,14 +19,18 @@ const MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`
const MAX_BYTES = 50 * 1024 const MAX_BYTES = 50 * 1024
const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB` const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`
export const ReadTool = Tool.define("read", { const parameters = z.object({
description: DESCRIPTION,
parameters: z.object({
filePath: z.string().describe("The absolute path to the file or directory to read"), filePath: z.string().describe("The absolute path to the file or directory to read"),
offset: z.coerce.number().describe("The line number to start reading from (1-indexed)").optional(), offset: z.coerce.number().describe("The line number to start reading from (1-indexed)").optional(),
limit: z.coerce.number().describe("The maximum number of lines to read (defaults to 2000)").optional(), limit: z.coerce.number().describe("The maximum number of lines to read (defaults to 2000)").optional(),
}), })
async execute(params, ctx) {
export const ReadTool = Tool.defineEffect(
"read",
Effect.succeed({
description: DESCRIPTION,
parameters,
async execute(params: z.infer<typeof parameters>, ctx) {
if (params.offset !== undefined && params.offset < 1) { if (params.offset !== undefined && params.offset < 1) {
throw new Error("offset must be greater than or equal to 1") throw new Error("offset must be greater than or equal to 1")
} }
@ -216,7 +221,7 @@ export const ReadTool = Tool.define("read", {
output += "\n</content>" output += "\n</content>"
// just warms the lsp client // just warms the lsp client
LSP.touchFile(filepath, false) await LSP.touchFile(filepath, false)
await FileTime.read(ctx.sessionID, filepath) await FileTime.read(ctx.sessionID, filepath)
if (instructions.length > 0) { if (instructions.length > 0) {
@ -233,7 +238,8 @@ export const ReadTool = Tool.define("read", {
}, },
} }
}, },
}) }),
)
async function isBinaryFile(filepath: string, fileSize: number): Promise<boolean> { async function isBinaryFile(filepath: string, fileSize: number): Promise<boolean> {
const ext = path.extname(filepath).toLowerCase() const ext = path.extname(filepath).toLowerCase()

View File

@ -1,7 +1,9 @@
import { afterEach, describe, expect, test } from "bun:test" import { afterEach, describe, expect, test } from "bun:test"
import { Effect } from "effect"
import path from "path" import path from "path"
import { ReadTool } from "../../src/tool/read"
import { Instance } from "../../src/project/instance" import { Instance } from "../../src/project/instance"
import { ReadTool as ReadToolFx } from "../../src/tool/read"
import { Tool } from "../../src/tool/tool"
import { Filesystem } from "../../src/util/filesystem" import { Filesystem } from "../../src/util/filesystem"
import { tmpdir } from "../fixture/fixture" import { tmpdir } from "../fixture/fixture"
import { Permission } from "../../src/permission" import { Permission } from "../../src/permission"
@ -25,6 +27,18 @@ const ctx = {
ask: async () => {}, ask: async () => {},
} }
const ReadTool = {
init: async () => ({
execute: (args: Tool.InferParameters<typeof ReadToolFx>, ctx: Tool.Context) =>
Effect.runPromise(
ReadToolFx.pipe(
Effect.flatMap((tool) => Effect.promise(() => tool.init())),
Effect.flatMap((tool) => Effect.promise(() => tool.execute(args, ctx))),
),
),
}),
}
const full = (p: string) => (process.platform === "win32" ? Filesystem.normalizePath(p) : p) const full = (p: string) => (process.platform === "win32" ? Filesystem.normalizePath(p) : p)
const glob = (p: string) => const glob = (p: string) =>
process.platform === "win32" ? Filesystem.normalizePathPattern(p) : p.replaceAll("\\", "/") process.platform === "win32" ? Filesystem.normalizePathPattern(p) : p.replaceAll("\\", "/")