/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/core/src/tool/grep.ts
137 строк
5 KB
James Long
refactor(core): remove tool layer exports (#34622)
30 июн 2026, 18:18
Не верифицирован
30 июн 2026, 18:18
5691f36
Код
Авторство
О чём код?
export * as GrepTool from "./grep" import { ToolFailure } from "@opencode-ai/llm" import { Effect, Layer, Schema } from "effect" import path from "path" import { makeLocationNode } from "../effect/app-node" import { FileSystem } from "../filesystem" import { FSUtil } from "../fs-util" import { Location } from "../location" import { PermissionV2 } from "../permission" import { Ripgrep } from "../ripgrep" import { RelativePath } from "../schema" import { ToolRegistry } from "./registry" import { Tool } from "./tool" import { Tools } from "./tools" export const name = "grep" export const Input = Schema.Struct({ pattern: FileSystem.GrepInput.fields.pattern.annotate({ description: "Regex pattern to search for in file contents", }), path: RelativePath.pipe(Schema.optional).annotate({ description: "Relative directory to search. Defaults to the active Location.", }), include: FileSystem.GrepInput.fields.include.annotate({ description: 'File glob to include in the search (for example, "*.js" or "*.{ts,tsx}")', }), limit: FileSystem.GrepInput.fields.limit.annotate({ description: "Maximum matches to return", }), }) export const Output = Schema.Array(FileSystem.Match) type ModelOutput = typeof Output.Encoded /** Format raw search matches into the familiar concise model output. */ export const toModelOutput = (output: ModelOutput) => { const lines = output.length === 0 ? ["No files found"] : [`Found ${output.length} matches`] let current = "" for (const match of output) { if (current !== match.entry.path) { if (current) lines.push("") current = match.entry.path lines.push(`${match.entry.path}:`) } lines.push(` Line ${match.line}: ${match.text}`) } return lines.join("\n") } /** Grep leaf that defaults its filesystem root to the active Location. */ const layer = Layer.effectDiscard( Effect.gen(function* () { const tools = yield* Tools.Service const fs = yield* FSUtil.Service const ripgrep = yield* Ripgrep.Service const location = yield* Location.Service const permission = yield* PermissionV2.Service yield* tools .register({ [name]: Tool.make({ description: "Search file contents by regular expression within the active Location or an absolute managed tool-output file. Use a path to narrow the search, include to filter files by glob, and limit to bound the match count. Returns concise file resources, line numbers, and bounded line previews.", input: Input, output: Output, toModelOutput: ({ output }) => [ { type: "text", text: toModelOutput( output.map((match) => ({ ...match, entry: { ...match.entry, path: path.resolve(location.directory, match.entry.path) }, })), ), }, ], execute: (input, context) => Effect.gen(function* () { yield* permission.assert({ action: name, resources: [input.pattern], save: ["*"], metadata: { root: ".", path: input.path, include: input.include, limit: input.limit, }, sessionID: context.sessionID, agent: context.agent, source: { type: "tool", messageID: context.assistantMessageID, callID: context.toolCallID }, }) const target = path.resolve(location.directory, input.path ?? ".") const info = yield* fs.stat(target).pipe(Effect.catch(() => Effect.succeed(undefined))) return yield* ripgrep .grep({ cwd: info?.type === "Directory" ? target : path.dirname(target), pattern: input.pattern, file: info?.type === "File" ? path.basename(target) : undefined, include: input.include, limit: input.limit ?? Number.MAX_SAFE_INTEGER, }) .pipe( Effect.map((result) => result.map((match) => FileSystem.Match.make({ ...match, entry: FileSystem.Entry.make({ ...match.entry, path: RelativePath.make( path.relative( location.directory, path.resolve( info?.type === "Directory" ? target : path.dirname(target), match.entry.path, ), ), ), }), }), ), ), ) }).pipe(Effect.mapError(() => new ToolFailure({ message: `Unable to grep for ${input.pattern}` }))), }), }) .pipe(Effect.orDie) }), ) export const node = makeLocationNode({ name: "tool/grep", layer, deps: [ToolRegistry.node, FSUtil.node, Ripgrep.node, Location.node, PermissionV2.node], })