Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions packages/pi-fff/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export { SCAN_TIMEOUT_MS } from "./sdk";

const DEFAULT_GREP_LIMIT = 20;
const DEFAULT_FIND_LIMIT = 30;
const GREP_PAGE_SIZE_MAX = 50;
const GREP_CONTEXT_MAX = 20;
const GREP_MAX_LINE_LENGTH = 500;
const MENTION_MAX_RESULTS = 20;

Expand Down Expand Up @@ -128,6 +130,13 @@ function truncateLine(line: string, max = GREP_MAX_LINE_LENGTH): string {
return trimmed.length <= max ? trimmed : `${trimmed.slice(0, max)}...`;
}

// Clamp caller-supplied context to a non-negative bounded integer so a large
// value cannot multiply output size past the model window.
function clampContext(context: number | undefined): number {
if (!context || context < 0) return 0;
return Math.min(Math.floor(context), GREP_CONTEXT_MAX);
}

const HOT_FRECENCY = 25;
const WARM_FRECENCY = 20;

Expand Down Expand Up @@ -691,7 +700,9 @@ export default function fffExtension(pi: ExtensionAPI) {
}),
),
context: Type.Optional(
Type.Number({ description: "Context lines before+after each match" }),
Type.Number({
description: `Context lines before+after each match (0-${GREP_CONTEXT_MAX})`,
}),
),
limit: Type.Optional(
Type.Number({
Expand Down Expand Up @@ -724,6 +735,10 @@ export default function fffExtension(pi: ExtensionAPI) {

const picker = aux ? aux.finder : await ensureFinder(activeCwd);
const effectiveLimit = Math.max(1, params.limit ?? DEFAULT_GREP_LIMIT);
// pageSize caps TOTAL matches across all files; maxMatchesPerFile alone
// only caps per-file, so limit=5 could still return a full SDK page.
const pageSize = Math.min(effectiveLimit, GREP_PAGE_SIZE_MAX);
const context = clampContext(params.context);
const query = aux
? aux.query
: buildQuery(params.path, pattern, params.exclude, activeCwd);
Expand Down Expand Up @@ -771,10 +786,11 @@ export default function fffExtension(pi: ExtensionAPI) {
const grepResult = picker.grep(query, {
mode,
smartCase,
maxMatchesPerFile: Math.min(effectiveLimit, 50),
maxMatchesPerFile: pageSize,
pageSize,
cursor: (params.cursor ? getCursor(params.cursor) : null) ?? null,
beforeContext: params.context ?? 0,
afterContext: params.context ?? 0,
beforeContext: context,
afterContext: context,
classifyDefinitions: true,
timeBudgetMs: GREP_TIME_BUDGET_MS,
});
Expand Down Expand Up @@ -803,7 +819,8 @@ export default function fffExtension(pi: ExtensionAPI) {
const fuzzy = picker.grep(fuzzyQuery, {
mode: "fuzzy",
smartCase,
maxMatchesPerFile: Math.min(effectiveLimit, 50),
maxMatchesPerFile: pageSize,
pageSize,
Comment on lines +822 to +823

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not emit a broken fuzzy cursor.

pageSize can now make the fuzzy fallback return nextCursor. The next request skips the fallback because params.cursor is set, then resumes the normal picker.grep path with the exact query and non-fuzzy mode. It does not replay fuzzyQuery with mode: "fuzzy". The native wrapper also forwards only cursor._offset in packages/fff-node/src/finder.ts Lines 368-386.

Store the fuzzy mode and query with the cursor, or suppress cursors for fuzzy results. Add a regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/pi-fff/src/index.ts` around lines 822 - 823, Prevent fuzzy fallback
results from emitting a cursor that cannot resume the same fuzzy query: update
the pagination flow around the fuzzy search and cursor construction near
maxMatchesPerFile/pageSize to either persist the fuzzy query and mode in the
cursor and restore them on the next request, or suppress nextCursor for fuzzy
results. Add a regression test covering a paginated fuzzy fallback and verifying
subsequent requests do not switch to the normal exact-query picker.grep path.

cursor: null,
beforeContext: 0,
afterContext: 0,
Expand Down Expand Up @@ -1015,7 +1032,11 @@ export default function fffExtension(pi: ExtensionAPI) {
constraints: Type.Optional(
Type.String({ description: "File filter, e.g. '*.{ts,tsx} !test/'" }),
),
context: Type.Optional(Type.Number({ description: "Context lines before+after" })),
context: Type.Optional(
Type.Number({
description: `Context lines before+after (0-${GREP_CONTEXT_MAX})`,
}),
),
limit: Type.Optional(
Type.Number({
description: `Max matches (default ${DEFAULT_GREP_LIMIT})`,
Expand Down Expand Up @@ -1044,15 +1065,18 @@ export default function fffExtension(pi: ExtensionAPI) {

const f = await ensureFinder(activeCwd);
const effectiveLimit = Math.max(1, params.limit ?? DEFAULT_GREP_LIMIT);
const pageSize = Math.min(effectiveLimit, GREP_PAGE_SIZE_MAX);
const context = clampContext(params.context);

const grepResult = f.multiGrep({
patterns: params.patterns,
constraints: params.constraints,
maxMatchesPerFile: Math.min(effectiveLimit, 50),
maxMatchesPerFile: pageSize,
pageSize,
smartCase: true,
cursor: (params.cursor ? getCursor(params.cursor) : null) ?? null,
beforeContext: params.context ?? 0,
afterContext: params.context ?? 0,
beforeContext: context,
afterContext: context,
});

if (!grepResult.ok) throw new Error(grepResult.error);
Expand Down
Loading