/** * In-memory BM25 over the capability index — the leg that always works. * * Deliberately not the repo's lexical retriever: that one shells out to ripgrep * over files on disk, and capabilities are a few hundred short strings held in * memory. It is also not optional. The dense leg needs a binary that may not be * installed and a store that may still be building, so if retrieval depended on * it, "find me a tool that sends email" would work on some machines and not * others. This leg makes the floor deterministic and dependency-free; dense is * strictly additive on top. * * The tokenizer does the load-bearing work here. Tool names are the query terms * that matter most and they arrive as `mcp_github_create_pull_request` or * `createPullRequest`, so a naive whitespace split would make the single most * common query shape — a name the model half-remembers — the one thing BM25 * cannot match. */ import type { CapabilityDoc } from "./registry.js"; /** * Split identifiers the way a person reads them: `mcp_github_create_pr` and * `createPullRequest` both yield their parts, and the original token is kept so * an exact name still scores as an exact match. * * Singular forms are emitted *alongside* the originals rather than replacing * them, which is the same bargain the identifier splitting makes: an exact * token still matches exactly, and a near miss now matches too. */ export declare function tokenize(text: string): string[]; export interface LexicalHit { id: string; score: number; } /** * A built BM25 index. Cheap enough to rebuild whenever the capability set * changes — a few hundred short documents — so there is no invalidation story * to get wrong. */ export declare class LexicalIndex { private readonly postings; private readonly docFreq; private readonly avgLength; constructor(docs: readonly CapabilityDoc[]); get size(): number; /** Top `k` documents for `query`, best first. Documents scoring zero are omitted. */ search(query: string, k?: number): LexicalHit[]; } //# sourceMappingURL=lexical.d.ts.map