mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-21 18:26:09 +00:00
fix(core): bound default search results (#37154)
This commit is contained in:
@@ -31,6 +31,8 @@ export type ListInput = typeof ListInput.Type
|
||||
|
||||
export { FindInput }
|
||||
|
||||
export const DEFAULT_SEARCH_LIMIT = 100
|
||||
|
||||
export class GlobInput extends Schema.Class<GlobInput>("FileSystem.GlobInput")({
|
||||
pattern: Schema.String,
|
||||
path: RelativePath.pipe(Schema.optional),
|
||||
|
||||
@@ -56,7 +56,7 @@ export const ripgrepLayer = Layer.effect(
|
||||
.glob({
|
||||
cwd,
|
||||
pattern: input.pattern,
|
||||
limit: input.limit ?? Number.MAX_SAFE_INTEGER,
|
||||
limit: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT,
|
||||
})
|
||||
.pipe(
|
||||
Effect.map((result) =>
|
||||
@@ -81,7 +81,7 @@ export const ripgrepLayer = Layer.effect(
|
||||
pattern: input.pattern,
|
||||
file: info.type === "File" ? path.basename(target) : undefined,
|
||||
include: input.include,
|
||||
limit: input.limit ?? Number.MAX_SAFE_INTEGER,
|
||||
limit: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT,
|
||||
})
|
||||
.pipe(
|
||||
Effect.map((result) =>
|
||||
@@ -150,7 +150,7 @@ export const fffLayer = Layer.effect(
|
||||
const prefix = input.path?.replaceAll("\\", "/").replace(/\/$/, "")
|
||||
const found = result.value.glob(prefix ? `${prefix}/${input.pattern}` : input.pattern, {
|
||||
pageIndex: 0,
|
||||
pageSize: input.limit,
|
||||
pageSize: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT,
|
||||
})
|
||||
if (!found.ok) throw found.error
|
||||
return found.value.items.map((item) =>
|
||||
@@ -167,7 +167,7 @@ export const fffLayer = Layer.effect(
|
||||
[prefix ? `${prefix}/**` : undefined, input.include, input.pattern]
|
||||
.filter((value) => value !== undefined)
|
||||
.join(" "),
|
||||
{ mode: "regex", pageSize: input.limit, timeBudgetMs: 1_500 },
|
||||
{ mode: "regex", pageSize: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT, timeBudgetMs: 1_500 },
|
||||
)
|
||||
if (!found.ok) throw found.error
|
||||
return found.value.items.map((match) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ export const Input = Schema.Struct({
|
||||
description: "Relative directory to search. Defaults to the active Location.",
|
||||
}),
|
||||
limit: FileSystem.GlobInput.fields.limit.annotate({
|
||||
description: "Maximum results to return",
|
||||
description: `Maximum results to return (default: ${FileSystem.DEFAULT_SEARCH_LIMIT})`,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -86,7 +86,7 @@ export const Plugin = {
|
||||
.glob({
|
||||
cwd,
|
||||
pattern: input.pattern,
|
||||
limit: input.limit ?? Number.MAX_SAFE_INTEGER,
|
||||
limit: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT,
|
||||
})
|
||||
.pipe(
|
||||
Effect.map((result) =>
|
||||
|
||||
@@ -25,7 +25,7 @@ export const Input = Schema.Struct({
|
||||
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",
|
||||
description: `Maximum matches to return (default: ${FileSystem.DEFAULT_SEARCH_LIMIT})`,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -106,7 +106,7 @@ export const Plugin = {
|
||||
pattern: input.pattern,
|
||||
file: info?.type === "File" ? path.basename(target) : undefined,
|
||||
include: input.include,
|
||||
limit: input.limit ?? Number.MAX_SAFE_INTEGER,
|
||||
limit: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT,
|
||||
})
|
||||
.pipe(
|
||||
Effect.map((result) =>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { makeLocationNode } from "@opencode-ai/core/effect/app-node"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { FileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { PermissionV2 } from "@opencode-ai/core/permission"
|
||||
@@ -16,7 +19,7 @@ import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
|
||||
import { location } from "./fixture/location"
|
||||
import { tmpdir } from "./fixture/tmpdir"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { executeTool, registerToolPlugin, toolIdentity } from "./lib/tool"
|
||||
import { executeTool, registerToolPlugin, settleTool, toolIdentity } from "./lib/tool"
|
||||
|
||||
const globToolNode = makeLocationNode({
|
||||
name: "test/glob-tool-plugin",
|
||||
@@ -66,6 +69,32 @@ const call = (name: "glob" | "grep", input: unknown) => ({
|
||||
const it = testEffect(Layer.empty)
|
||||
|
||||
describe("search tools", () => {
|
||||
it.live("bounds omitted glob and grep limits", () =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.promise(() => tmpdir()),
|
||||
(tmp) =>
|
||||
Effect.gen(function* () {
|
||||
yield* Effect.promise(() =>
|
||||
Promise.all(
|
||||
Array.from({ length: FileSystem.DEFAULT_SEARCH_LIMIT + 1 }, (_, index) =>
|
||||
fs.writeFile(path.join(tmp.path, `${index}.txt`), "needle\n"),
|
||||
),
|
||||
),
|
||||
)
|
||||
yield* withTools(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
const glob = yield* settleTool(registry, call("glob", { pattern: "*" }))
|
||||
const grep = yield* settleTool(registry, call("grep", { pattern: "needle" }))
|
||||
|
||||
expect(glob.output?.structured).toHaveLength(FileSystem.DEFAULT_SEARCH_LIMIT)
|
||||
expect(grep.output?.structured).toHaveLength(FileSystem.DEFAULT_SEARCH_LIMIT)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
||||
),
|
||||
)
|
||||
|
||||
for (const name of ["glob", "grep"] as const) {
|
||||
it.live(`${name} reports a missing search path`, () =>
|
||||
Effect.acquireUseRelease(
|
||||
|
||||
Reference in New Issue
Block a user