/** * MemPalace text search — tokenized, field-weighted scoring. * * Design goals: * 1. Zero dependencies (no FTS index, no embedding model) * 2. Reasonable ranking out of the box: title-ish fields outrank body * 3. Tolerant to short queries (single keyword should still rank well) * 4. Tolerant to plural / case variations via simple stemming * * We tokenize on non-alphanumeric, lowercase everything, drop tokens * shorter than 2 chars and a small stopword list, and score each drawer * by summing per-token field weights: * * tags × 4 (semantic tags are the strongest signal) * room × 3 * wing × 2 * content × 1 (matches anywhere in body) * * Importance acts as a multiplier on the final score so the user can * boost high-signal drawers via the importance field. Recency also * lightly boosts via a half-life of 90 days. * * This is good enough until the store gets large; the search() entry * point is the swap-in for a real FTS / embedding search later. */ import type { Drawer, SearchOptions, SearchHit } from './types.js'; /** * Tokenize a string into lowercase, deduplicated, non-stopword tokens of * length ≥ 2. Very lightweight stemming: trailing 's' is stripped to fold * "drawer"/"drawers" together. Aggressive stemming (Porter, etc.) would * cost a lot for marginal recall gains at this scale. */ export declare function tokenize(s: string): string[]; /** * Top-level search. Filters by scope/wing/room/tags first, then scores * each remaining drawer and returns the top `limit` by score. */ export declare function searchDrawers(drawers: Drawer[], query: string, opts?: SearchOptions): SearchHit[];