fix(core): report truncated glob results (#38631)

This commit is contained in:
Aiden Cline 2026-07-24 10:17:37 -05:00 committed by GitHub
commit c64d813347
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 12 deletions

View file

@ -25,11 +25,16 @@ export const Input = Schema.Struct({
}) })
export const Output = Schema.Array(FileSystem.Entry) export const Output = Schema.Array(FileSystem.Entry)
type ModelOutput = typeof Output.Encoded type EncodedOutput = typeof Output.Encoded
/** Format raw search results into the concise line-oriented output models expect. */ /** Format raw search results into the concise line-oriented output models expect. */
export const toModelOutput = (output: ModelOutput) => { export const toModelContent = (entries: EncodedOutput, truncated = false) => {
const lines = output.length === 0 ? ["No files found"] : output.map((item) => item.path) const lines = entries.length === 0 ? ["No files found"] : entries.map((item) => item.path)
if (truncated)
lines.push(
"",
`(Results are truncated: showing first ${entries.length} results. Consider using a more specific path or pattern.)`,
)
return lines.join("\n") return lines.join("\n")
} }
@ -74,11 +79,12 @@ export const Plugin = {
Effect.fail(new ToolFailure({ message: `Search path does not exist: ${input.path ?? "."}` })), Effect.fail(new ToolFailure({ message: `Search path does not exist: ${input.path ?? "."}` })),
), ),
) )
return yield* ripgrep const limit = input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT
const entries = yield* ripgrep
.glob({ .glob({
cwd, cwd,
pattern: input.pattern, pattern: input.pattern,
limit: input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT, limit: limit + 1,
}) })
.pipe( .pipe(
Effect.map((result) => Effect.map((result) =>
@ -90,13 +96,15 @@ export const Plugin = {
), ),
), ),
) )
return { entries: entries.slice(0, limit), truncated: entries.length > limit }
}).pipe( }).pipe(
Effect.map((output) => ({ Effect.map((result) => ({
output, output: result.entries,
content: toModelOutput( content: toModelContent(
output.map((entry) => ({ ...entry, path: path.resolve(location.directory, entry.path) })), result.entries.map((entry) => ({ ...entry, path: path.resolve(location.directory, entry.path) })),
result.truncated,
), ),
metadata: { count: output.length }, metadata: { count: result.entries.length, truncated: result.truncated },
})), })),
Effect.mapError((error) => Effect.mapError((error) =>
error instanceof ToolFailure error instanceof ToolFailure

View file

@ -86,13 +86,16 @@ describe("search tools", () => {
const glob = yield* executeTool(registry, call("glob", { pattern: "*" })) const glob = yield* executeTool(registry, call("glob", { pattern: "*" }))
const grep = yield* executeTool(registry, call("grep", { pattern: "needle" })) const grep = yield* executeTool(registry, call("grep", { pattern: "needle" }))
expect(glob.metadata).toEqual({ count: FileSystem.DEFAULT_SEARCH_LIMIT }) expect(glob.metadata).toEqual({ count: FileSystem.DEFAULT_SEARCH_LIMIT, truncated: true })
expect(grep.metadata).toEqual({ matches: FileSystem.DEFAULT_SEARCH_LIMIT }) expect(grep.metadata).toEqual({ matches: FileSystem.DEFAULT_SEARCH_LIMIT })
expect(glob.content).toHaveLength(1) expect(glob.content).toHaveLength(1)
expect(grep.content).toHaveLength(1) expect(grep.content).toHaveLength(1)
const globText = glob.content?.[0]?.type === "text" ? glob.content[0].text : "" const globText = glob.content?.[0]?.type === "text" ? glob.content[0].text : ""
const grepText = grep.content?.[0]?.type === "text" ? grep.content[0].text : "" const grepText = grep.content?.[0]?.type === "text" ? grep.content[0].text : ""
expect(globText.split("\n")).toHaveLength(FileSystem.DEFAULT_SEARCH_LIMIT) expect(globText.split("\n")).toHaveLength(FileSystem.DEFAULT_SEARCH_LIMIT + 2)
expect(globText).toEndWith(
`(Results are truncated: showing first ${FileSystem.DEFAULT_SEARCH_LIMIT} results. Consider using a more specific path or pattern.)`,
)
expect(grepText).toStartWith(`Found ${FileSystem.DEFAULT_SEARCH_LIMIT} matches\n`) expect(grepText).toStartWith(`Found ${FileSystem.DEFAULT_SEARCH_LIMIT} matches\n`)
}), }),
) )