convert list tool to Tool.defineEffect (#21899)

This commit is contained in:
Kit Langton
2026-04-10 15:38:52 -04:00
committed by GitHub
parent ce26120205
commit f63bdc8e08
+25 -13
View File
@@ -1,10 +1,12 @@
import z from "zod" import z from "zod"
import { Effect } from "effect"
import * as Stream from "effect/Stream"
import { Tool } from "./tool" import { Tool } from "./tool"
import * as path from "path" import * as path from "path"
import DESCRIPTION from "./ls.txt" import DESCRIPTION from "./ls.txt"
import { Instance } from "../project/instance" import { Instance } from "../project/instance"
import { Ripgrep } from "../file/ripgrep" import { Ripgrep } from "../file/ripgrep"
import { assertExternalDirectory } from "./external-directory" import { assertExternalDirectoryEffect } from "./external-directory"
export const IGNORE_PATTERNS = [ export const IGNORE_PATTERNS = [
"node_modules/", "node_modules/",
@@ -35,31 +37,39 @@ export const IGNORE_PATTERNS = [
const LIMIT = 100 const LIMIT = 100
export const ListTool = Tool.define("list", { export const ListTool = Tool.defineEffect(
"list",
Effect.gen(function* () {
const rg = yield* Ripgrep.Service
return {
description: DESCRIPTION, description: DESCRIPTION,
parameters: z.object({ parameters: z.object({
path: z.string().describe("The absolute path to the directory to list (must be absolute, not relative)").optional(), path: z.string().describe("The absolute path to the directory to list (must be absolute, not relative)").optional(),
ignore: z.array(z.string()).describe("List of glob patterns to ignore").optional(), ignore: z.array(z.string()).describe("List of glob patterns to ignore").optional(),
}), }),
async execute(params, ctx) { execute: (params: { path?: string; ignore?: string[] }, ctx: Tool.Context) =>
Effect.gen(function* () {
const searchPath = path.resolve(Instance.directory, params.path || ".") const searchPath = path.resolve(Instance.directory, params.path || ".")
await assertExternalDirectory(ctx, searchPath, { kind: "directory" }) yield* assertExternalDirectoryEffect(ctx, searchPath, { kind: "directory" })
await ctx.ask({ yield* Effect.promise(() =>
ctx.ask({
permission: "list", permission: "list",
patterns: [searchPath], patterns: [searchPath],
always: ["*"], always: ["*"],
metadata: { metadata: {
path: searchPath, path: searchPath,
}, },
}) }),
)
const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || []) const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || [])
const files = [] const files = yield* rg.files({ cwd: searchPath, glob: ignoreGlobs }).pipe(
for await (const file of Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs, signal: ctx.abort })) { Stream.take(LIMIT),
files.push(file) Stream.runCollect,
if (files.length >= LIMIT) break Effect.map((chunk) => [...chunk]),
} )
// Build directory structure // Build directory structure
const dirs = new Set<string>() const dirs = new Set<string>()
@@ -117,5 +127,7 @@ export const ListTool = Tool.define("list", {
}, },
output, output,
} }
}, }).pipe(Effect.orDie, Effect.runPromise),
}) }
}),
)