refactor(core): unify filesystem search service (#31566)

This commit is contained in:
Dax
2026-06-09 20:38:02 -04:00
committed by GitHub
parent 83df7eb86a
commit 80e328b6c1
57 changed files with 965 additions and 2855 deletions
+15 -40
View File
@@ -2,13 +2,11 @@ import path from "path"
import { Effect, Schema } from "effect"
import { InstanceState } from "@/effect/instance-state"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Search } from "@opencode-ai/core/filesystem/search"
import { Ripgrep } from "@opencode-ai/core/ripgrep"
import { assertExternalDirectoryEffect } from "./external-directory"
import DESCRIPTION from "./grep.txt"
import * as Tool from "./tool"
const MAX_LINE_LENGTH = 2000
export const Parameters = Schema.Struct({
pattern: Schema.String.annotate({ description: "The regex pattern to search for in file contents" }),
path: Schema.optional(Schema.String).annotate({
@@ -23,8 +21,7 @@ export const GrepTool = Tool.define(
"grep",
Effect.gen(function* () {
const fs = yield* FSUtil.Service
const searchSvc = yield* Search.Service
const ripgrep = yield* Ripgrep.Service
return {
description: DESCRIPTION,
parameters: Parameters,
@@ -63,30 +60,27 @@ export const GrepTool = Tool.define(
const search = FSUtil.resolve(requested)
const info = yield* fs.stat(search).pipe(Effect.catch(() => Effect.succeed(undefined)))
const cwd = info?.type === "Directory" ? search : path.dirname(search)
const file = info?.type === "Directory" ? undefined : [path.relative(cwd, search)]
const result = yield* searchSvc.search({
const result = yield* ripgrep.grep({
cwd,
pattern: params.pattern,
glob: params.include ? [params.include] : undefined,
file,
signal: ctx.abort,
include: params.include,
limit: 100,
})
if (result.items.length === 0) return empty
if (result.length === 0) return empty
const rows = result.items.map((item) => ({
path: FSUtil.resolve(path.isAbsolute(item.path.text) ? item.path.text : path.join(cwd, item.path.text)),
line: item.line_number,
text: item.lines.text,
const rows = result.map((item) => ({
path: path.resolve(cwd, item.entry.path),
line: item.line,
text: item.text,
}))
const limit = 100
const truncated = rows.length > limit
const final = truncated ? rows.slice(0, limit) : rows
const truncated = rows.length === limit
const final = rows
if (final.length === 0) return empty
const total = rows.length
const hasMore = truncated || result.hasNextPage
const hasMore = truncated || result.length === limit
const output = [`Found ${total} matches${hasMore ? " (more matches available)" : ""}`]
let current = ""
@@ -96,31 +90,12 @@ export const GrepTool = Tool.define(
current = match.path
output.push(`${match.path}:`)
}
const text =
match.text.length > MAX_LINE_LENGTH ? match.text.substring(0, MAX_LINE_LENGTH) + "..." : match.text
output.push(` Line ${match.line}: ${text}`)
output.push(` Line ${match.line}: ${match.text}`)
}
if (truncated) {
output.push("")
output.push(
`(Results truncated: showing ${limit} of ${total} matches (${total - limit} hidden). Consider using a more specific path or pattern.)`,
)
}
if (result.hasNextPage) {
output.push("")
output.push(`(Results truncated. Consider using a more specific path or pattern.)`)
}
if (result.partial) {
output.push("")
output.push("(Some paths were inaccessible and skipped)")
}
if (result.regexFallbackError) {
output.push("")
output.push(`(Regex fallback: ${result.regexFallbackError})`)
output.push("(Results truncated. Consider using a more specific path or pattern.)")
}
return {