spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: src/tui/composer/fuzzy.ts4 * Description: Fuzzy subsequence matcher — exact-prefix bonus, boundary/consecutive scoring, matched-index output.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910export interface FuzzyResult {11 score: number;12 /** Indices of matched characters in the target (for underlining — §12: never color-only). */13 indices: number[];14}1516const BOUNDARY_CHARS = new Set(["/", "-", "_", ".", " ", ":"]);1718/**19 * Match `query` as a case-insensitive subsequence of `target`.20 * Scoring (fuzzysort-style): +1 per matched char, +3 consecutive, +2 at a21 * word boundary, large exact-prefix bonus, small penalty per unmatched22 * target character so shorter targets win ties. Returns null on no match.23 */24export function fuzzyMatch(query: string, target: string): FuzzyResult | null {25 if (query === "") return { score: 0, indices: [] };26 const q = query.toLowerCase();27 const t = target.toLowerCase();2829 const indices: number[] = [];30 let score = 0;31 let ti = 0;32 let prevMatch = -2;3334 for (let qi = 0; qi < q.length; qi++) {35 const ch = q[qi] as string;36 let found = -1;37 while (ti < t.length) {38 if (t[ti] === ch) {39 found = ti;40 break;41 }42 ti += 1;43 }44 if (found === -1) return null;45 indices.push(found);46 score += 1;47 if (found === prevMatch + 1) score += 3;48 if (found === 0 || BOUNDARY_CHARS.has(t[found - 1] as string)) score += 2;49 prevMatch = found;50 ti = found + 1;51 }5253 if (t.startsWith(q)) score += 100;54 score -= (t.length - q.length) * 0.05;55 return { score, indices };56}5758export interface FuzzyRanked<T> {59 item: T;60 score: number;61 indices: number[];62}6364/** Filter and rank `items` by fuzzy score against `key(item)`; stable for equal scores. */65export function fuzzyFilter<T>(66 query: string,67 items: readonly T[],68 key: (item: T) => string,69): FuzzyRanked<T>[] {70 const results: FuzzyRanked<T>[] = [];71 for (const item of items) {72 const match = fuzzyMatch(query, key(item));73 if (match !== null) results.push({ item, score: match.score, indices: match.indices });74 }75 // Stable sort: equal scores keep provider order.76 return results77 .map((r, i) => ({ r, i }))78 .sort((a, b) => b.r.score - a.r.score || a.i - b.i)79 .map((x) => x.r);80}81