remem-mcp 0.6.8 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  __esm,
3
3
  init_esm_shims
4
- } from "./chunk-XK4Y53J2.js";
4
+ } from "./chunk-ONQHLUGM.js";
5
5
 
6
6
  // src/codegraph/resolver.ts
7
- function resolveCall(calleeName, callerFilePath, registry, importMap) {
7
+ function resolveCall(calleeName, callerFilePath, registry, importMap, fuzzyEnabled = true) {
8
8
  const parts = calleeName.split(".");
9
9
  if (parts.length >= 2) {
10
10
  const prefix = parts[0];
@@ -43,9 +43,7 @@ function resolveCall(calleeName, callerFilePath, registry, importMap) {
43
43
  if (symSame) {
44
44
  return { calleeId: symSame.id, confidence: 0.9, strategy: "same-module" };
45
45
  }
46
- const sameModuleSimple = registry.reverseLookup(calleeName).find(
47
- (s) => registry.getModuleForFile(s.file_path) === callerModule
48
- );
46
+ const sameModuleSimple = registry.reverseLookup(calleeName).find((s) => registry.getModuleForFile(s.file_path) === callerModule);
49
47
  if (sameModuleSimple) {
50
48
  return { calleeId: sameModuleSimple.id, confidence: 0.9, strategy: "same-module" };
51
49
  }
@@ -78,9 +76,11 @@ function resolveCall(calleeName, callerFilePath, registry, importMap) {
78
76
  }
79
77
  return { calleeId: best.id, confidence: 0.55, strategy: "suffix-match" };
80
78
  }
81
- const fuzzyMatch = fuzzyLookup(calleeName, registry);
82
- if (fuzzyMatch) {
83
- return { calleeId: fuzzyMatch.id, confidence: 0.35, strategy: "fuzzy" };
79
+ if (fuzzyEnabled) {
80
+ const fuzzyMatch = fuzzyLookup(calleeName, registry);
81
+ if (fuzzyMatch) {
82
+ return { calleeId: fuzzyMatch.id, confidence: 0.35, strategy: "fuzzy" };
83
+ }
84
84
  }
85
85
  return { calleeId: null, confidence: 0, strategy: "unresolved" };
86
86
  }
@@ -91,25 +91,60 @@ function shouldSkipResolution(calleeName) {
91
91
  if (parts.length >= 2 && parts[0].length <= 1) return true;
92
92
  return false;
93
93
  }
94
- function resolveAllCalls(db) {
94
+ function resolveAllCalls(db, callerIds) {
95
95
  try {
96
96
  db.exec("CREATE INDEX IF NOT EXISTS idx_calls_callee_name ON calls(callee_name)");
97
97
  db.exec("CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name)");
98
98
  db.exec("CREATE INDEX IF NOT EXISTS idx_symbols_module ON symbols(module_path)");
99
99
  } catch {
100
100
  }
101
- const symbols = db.prepare("SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols").all();
101
+ let symbols;
102
+ if (callerIds && callerIds.size > 0) {
103
+ const callerIdList = [...callerIds];
104
+ const batchLangs = /* @__PURE__ */ new Set();
105
+ for (let i = 0; i < callerIdList.length; i += 500) {
106
+ const chunk = callerIdList.slice(i, i + 500);
107
+ const placeholders = chunk.map(() => "?").join(",");
108
+ const rows = db.prepare(`SELECT DISTINCT language FROM symbols WHERE id IN (${placeholders})`).all(...chunk);
109
+ for (const r of rows) batchLangs.add(r.language);
110
+ }
111
+ if (batchLangs.size > 0) {
112
+ const langPlaceholders = [...batchLangs].map(() => "?").join(",");
113
+ symbols = db.prepare(`SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols WHERE language IN (${langPlaceholders})`).all(...batchLangs);
114
+ } else {
115
+ symbols = db.prepare("SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols").all();
116
+ }
117
+ } else {
118
+ symbols = db.prepare("SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols").all();
119
+ }
102
120
  const registry = new FunctionRegistry(symbols);
121
+ const fuzzyEnabled = symbols.length <= 500;
103
122
  const imports = db.prepare("SELECT file_path, symbol_name, source_path FROM imports").all();
104
123
  const importMap = new ImportMap(imports);
105
- const calls = db.prepare("SELECT id, caller_id, callee_name, callee_id, line, call_type FROM calls WHERE callee_id IS NULL").all();
124
+ let calls;
125
+ if (callerIds && callerIds.size > 0) {
126
+ const idList = [...callerIds];
127
+ calls = [];
128
+ for (let i = 0; i < idList.length; i += 500) {
129
+ const chunk = idList.slice(i, i + 500);
130
+ const placeholders = chunk.map(() => "?").join(",");
131
+ const rows = db.prepare(
132
+ `SELECT id, caller_id, callee_name, callee_id, line, call_type FROM calls WHERE callee_id IS NULL AND caller_id IN (${placeholders})`
133
+ ).all(...chunk);
134
+ calls.push(...rows);
135
+ }
136
+ } else {
137
+ calls = db.prepare(
138
+ "SELECT id, caller_id, callee_name, callee_id, line, call_type FROM calls WHERE callee_id IS NULL"
139
+ ).all();
140
+ }
106
141
  const stats = { total: calls.length, resolved: 0, byStrategy: {} };
107
142
  let skipped = 0;
108
143
  const callerFiles = /* @__PURE__ */ new Map();
109
- const callerIds = [...new Set(calls.map((c) => c.caller_id))];
110
- if (callerIds.length > 0) {
111
- for (let i = 0; i < callerIds.length; i += 500) {
112
- const chunk = callerIds.slice(i, i + 500);
144
+ const callerIdsFromCalls = [...new Set(calls.map((c) => c.caller_id))];
145
+ if (callerIdsFromCalls.length > 0) {
146
+ for (let i = 0; i < callerIdsFromCalls.length; i += 500) {
147
+ const chunk = callerIdsFromCalls.slice(i, i + 500);
113
148
  const placeholders = chunk.map(() => "?").join(",");
114
149
  const rows = db.prepare(`SELECT id, file_path FROM symbols WHERE id IN (${placeholders})`).all(...chunk);
115
150
  for (const r of rows) {
@@ -117,9 +152,7 @@ function resolveAllCalls(db) {
117
152
  }
118
153
  }
119
154
  }
120
- const updateStmt = db.prepare(
121
- "UPDATE calls SET callee_id = ?, confidence = ? WHERE id = ?"
122
- );
155
+ const updateStmt = db.prepare("UPDATE calls SET callee_id = ?, confidence = ? WHERE id = ?");
123
156
  const batch = db.transaction(() => {
124
157
  for (const call of calls) {
125
158
  if (shouldSkipResolution(call.callee_name)) {
@@ -127,7 +160,7 @@ function resolveAllCalls(db) {
127
160
  continue;
128
161
  }
129
162
  const callerFile = callerFiles.get(call.caller_id) ?? "";
130
- const result = resolveCall(call.callee_name, callerFile, registry, importMap);
163
+ const result = resolveCall(call.callee_name, callerFile, registry, importMap, fuzzyEnabled);
131
164
  if (result.calleeId) {
132
165
  updateStmt.run(result.calleeId, result.confidence, call.id);
133
166
  stats.resolved++;
@@ -297,4 +330,4 @@ export {
297
330
  resolveAllCalls,
298
331
  resolveCall
299
332
  };
300
- //# sourceMappingURL=resolver-AVK7XKX7.js.map
333
+ //# sourceMappingURL=resolver-UHU4MBQX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/codegraph/resolver.ts"],"sourcesContent":["/**\n * Call resolver: 6-strategy cascade for resolving callee names to symbol IDs.\n *\n * Adapted from Codebase-Memory (arXiv:2603.27277, MIT, DeusData 2026).\n * https://github.com/DeusData/codebase-memory-mcp\n *\n * Strategies (in priority order):\n * 1. Import map (0.95): Split callee into prefix.suffix; look up prefix in file's import map\n * 2. Import map suffix (0.85): Suffix-based matching against import-resolved module paths\n * 3. Same module (0.90): Prefix callee with enclosing file's module path\n * 4. Unique name (0.75): Simple name lookup, accept if exactly 1 candidate\n * 5. Suffix match (0.55): Multiple candidates, select by suffix + import-distance\n * 6. Fuzzy (0.30-0.40): String similarity — last resort\n */\n\nimport type { Database } from \"better-sqlite3\";\n\nexport interface SymbolRow {\n id: string;\n name: string;\n kind: string;\n file_path: string;\n module_path: string | null;\n language: string;\n parent_id: string | null;\n}\n\nexport interface CallRow {\n id: number;\n caller_id: string;\n callee_name: string;\n callee_id: string | null;\n line: number;\n call_type: string;\n}\n\nexport interface ImportRow {\n file_path: string;\n symbol_name: string;\n source_path: string | null;\n}\n\nexport interface ResolutionResult {\n calleeId: string | null;\n confidence: number;\n strategy: string;\n}\n\n/**\n * FunctionRegistry indexes all symbol definitions by:\n * - qualified name (exact map): \"module_path/SymbolName\" → SymbolRow\n * - simple name (reverse index): \"SymbolName\" → SymbolRow[]\n */\nexport class FunctionRegistry {\n private exactMap = new Map<string, SymbolRow>();\n private reverseIndex = new Map<string, SymbolRow[]>();\n private fileModules = new Map<string, string>(); // file_path → module_path\n\n constructor(symbols: SymbolRow[]) {\n for (const sym of symbols) {\n // Build module_path from file_path if not set\n const modulePath = sym.module_path ?? deriveModulePath(sym.file_path);\n this.fileModules.set(sym.file_path, modulePath);\n\n // Exact map: module_path/name\n const qualifiedName = `${modulePath}/${sym.name}`;\n this.exactMap.set(qualifiedName, sym);\n // Also index by file_path/name for same-file resolution\n this.exactMap.set(`${sym.file_path}/${sym.name}`, sym);\n\n // Reverse index: simple name → all matching symbols\n const existing = this.reverseIndex.get(sym.name);\n if (existing) {\n existing.push(sym);\n } else {\n this.reverseIndex.set(sym.name, [sym]);\n }\n }\n }\n\n /** Look up by qualified name (module_path/name). */\n exactLookup(qualifiedName: string): SymbolRow | undefined {\n return this.exactMap.get(qualifiedName);\n }\n\n /** Look up by simple name — returns all candidates. */\n reverseLookup(simpleName: string): SymbolRow[] {\n return this.reverseIndex.get(simpleName) ?? [];\n }\n\n /** Get all reverse index entries (for fuzzy lookup). */\n reverseLookupEntries(): IterableIterator<[string, SymbolRow[]]> {\n return this.reverseIndex.entries();\n }\n\n /** Get module_path for a file. */\n getModuleForFile(filePath: string): string {\n return this.fileModules.get(filePath) ?? deriveModulePath(filePath);\n }\n}\n\n/**\n * ImportMap tracks what each file imports: { prefix → module_path }.\n * Built from the imports table.\n */\nexport class ImportMap {\n private fileImports = new Map<string, Map<string, string>>();\n\n constructor(imports: ImportRow[]) {\n for (const imp of imports) {\n if (!imp.source_path) continue;\n let fileMap = this.fileImports.get(imp.file_path);\n if (!fileMap) {\n fileMap = new Map();\n this.fileImports.set(imp.file_path, fileMap);\n }\n // Map: symbol_name (or prefix) → source module_path\n const prefix = imp.symbol_name.split(\".\")[0] ?? imp.symbol_name;\n const modulePath = deriveModulePath(imp.source_path);\n fileMap.set(prefix, modulePath);\n fileMap.set(imp.symbol_name, modulePath);\n }\n }\n\n /** Look up module path for an import prefix in a given file. */\n lookup(filePath: string, prefix: string): string | undefined {\n return this.fileImports.get(filePath)?.get(prefix);\n }\n\n /** Get all imports for a file. */\n getImportsForFile(filePath: string): Map<string, string> | undefined {\n return this.fileImports.get(filePath);\n }\n}\n\n/**\n * Resolve a callee name to a symbol ID using the 6-strategy cascade.\n * Returns { calleeId, confidence, strategy } or { calleeId: null, confidence: 0, strategy: \"unresolved\" }.\n */\nexport function resolveCall(\n calleeName: string,\n callerFilePath: string,\n registry: FunctionRegistry,\n importMap: ImportMap,\n fuzzyEnabled = true,\n): ResolutionResult {\n // Strategy 1: Import map (0.95)\n // Split callee into prefix.suffix; look up prefix in file's import map\n const parts = calleeName.split(\".\");\n if (parts.length >= 2) {\n const prefix = parts[0];\n const suffix = parts.slice(1).join(\".\");\n const modulePath = importMap.lookup(callerFilePath, prefix);\n if (modulePath) {\n const qualifiedName = `${modulePath}/${suffix}`;\n const sym = registry.exactLookup(qualifiedName);\n if (sym) {\n return { calleeId: sym.id, confidence: 0.95, strategy: \"import-map\" };\n }\n // Also try full callee name in the resolved module\n const fullQualified = `${modulePath}/${calleeName}`;\n const symFull = registry.exactLookup(fullQualified);\n if (symFull) {\n return { calleeId: symFull.id, confidence: 0.95, strategy: \"import-map\" };\n }\n }\n }\n\n // Strategy 2: Import map suffix (0.85)\n // Try suffix-based matching against import-resolved module paths\n if (parts.length >= 2) {\n const prefix = parts[0];\n const suffix = parts[parts.length - 1];\n const modulePath = importMap.lookup(callerFilePath, prefix);\n if (modulePath) {\n const candidates = registry.reverseLookup(suffix);\n for (const c of candidates) {\n const cModule = registry.getModuleForFile(c.file_path);\n if (cModule === modulePath || cModule.endsWith(`/${modulePath}`)) {\n return { calleeId: c.id, confidence: 0.85, strategy: \"import-map-suffix\" };\n }\n }\n }\n }\n\n // Strategy 3: Same module (0.90)\n // Prefix callee with enclosing file's module path\n const callerModule = registry.getModuleForFile(callerFilePath);\n const sameModuleQualified = `${callerModule}/${calleeName}`;\n const symSame = registry.exactLookup(sameModuleQualified);\n if (symSame) {\n return { calleeId: symSame.id, confidence: 0.9, strategy: \"same-module\" };\n }\n // Also try just the simple name in same module\n const sameModuleSimple = registry\n .reverseLookup(calleeName)\n .find((s) => registry.getModuleForFile(s.file_path) === callerModule);\n if (sameModuleSimple) {\n return { calleeId: sameModuleSimple.id, confidence: 0.9, strategy: \"same-module\" };\n }\n\n // Strategy 4: Unique name (0.75)\n // Simple name lookup, accept if exactly 1 candidate project-wide\n const candidates = registry.reverseLookup(calleeName);\n if (candidates.length === 1) {\n return { calleeId: candidates[0].id, confidence: 0.75, strategy: \"unique-name\" };\n }\n\n // Strategy 5: Suffix match (0.55)\n // Multiple candidates, select by suffix match with import-distance scoring\n if (candidates.length > 1) {\n // Prefer candidates in modules reachable from this file's imports\n const fileImports = importMap.getImportsForFile(callerFilePath);\n if (fileImports) {\n for (const c of candidates) {\n const cModule = registry.getModuleForFile(c.file_path);\n for (const [, impModule] of fileImports) {\n if (cModule === impModule || cModule.startsWith(`${impModule}/`)) {\n return { calleeId: c.id, confidence: 0.55, strategy: \"suffix-match\" };\n }\n }\n }\n }\n // Fallback: pick first candidate (nearest by file path similarity)\n const callerDir = callerFilePath.slice(0, callerFilePath.lastIndexOf(\"/\"));\n let best = candidates[0];\n let bestDist = Infinity;\n for (const c of candidates) {\n const cDir = c.file_path.slice(0, c.file_path.lastIndexOf(\"/\"));\n const dist = pathDistance(callerDir, cDir);\n if (dist < bestDist) {\n bestDist = dist;\n best = c;\n }\n }\n return { calleeId: best.id, confidence: 0.55, strategy: \"suffix-match\" };\n }\n\n // Strategy 6: Fuzzy (0.30-0.40)\n // String similarity — last resort. Skip when registry is large (O(n) per call).\n if (fuzzyEnabled) {\n const fuzzyMatch = fuzzyLookup(calleeName, registry);\n if (fuzzyMatch) {\n return { calleeId: fuzzyMatch.id, confidence: 0.35, strategy: \"fuzzy\" };\n }\n }\n\n return { calleeId: null, confidence: 0, strategy: \"unresolved\" };\n}\n\n/** Names too short or builtin to bother resolving. */\nconst SKIP_NAMES = new Set([\n \"T\",\n \"L\",\n \"R\",\n \"S\",\n \"P\",\n \"C\",\n \"D\",\n \"E\",\n \"F\",\n \"M\",\n \"N\",\n \"V\",\n \"X\",\n \"Y\",\n \"String\",\n \"Array\",\n \"Object\",\n \"Number\",\n \"Boolean\",\n \"Map\",\n \"Set\",\n \"Date\",\n \"Error\",\n \"RegExp\",\n \"Promise\",\n \"Symbol\",\n \"Buffer\",\n \"Vec\",\n \"Box\",\n \"Option\",\n \"Result\",\n \"Some\",\n \"None\",\n \"Ok\",\n \"Err\",\n \"List\",\n \"Dict\",\n \"Tuple\",\n]);\n\n/** Check if a callee name should be skipped (too short or builtin type). */\nfunction shouldSkipResolution(calleeName: string): boolean {\n if (calleeName.length <= 2) return true;\n if (SKIP_NAMES.has(calleeName)) return true;\n // Skip single-letter + dot patterns like \"T.foo\"\n const parts = calleeName.split(\".\");\n if (parts.length >= 2 && parts[0].length <= 1) return true;\n return false;\n}\n\n/**\n * Run call resolution on unresolved calls in the database.\n * Called after indexDirectory() completes.\n *\n * If callerIds is provided, only resolve calls whose caller_id is in the set\n * (i.e., calls from the just-indexed batch). This avoids re-resolving 25K+\n * historical calls on every index run — the O(n²) fuzzy strategy would hang.\n * If callerIds is omitted, resolve all unresolved calls (legacy behavior).\n */\nexport function resolveAllCalls(\n db: Database,\n callerIds?: Set<string>,\n): {\n total: number;\n resolved: number;\n byStrategy: Record<string, number>;\n} {\n // Create index for faster lookups (idempotent)\n try {\n db.exec(\"CREATE INDEX IF NOT EXISTS idx_calls_callee_name ON calls(callee_name)\");\n db.exec(\"CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name)\");\n db.exec(\"CREATE INDEX IF NOT EXISTS idx_symbols_module ON symbols(module_path)\");\n } catch {}\n\n // Load symbols into FunctionRegistry.\n // When resolving a batch (callerIds provided), only load symbols matching\n // the batch's languages — this avoids scanning 4800+ symbols from other\n // repos/languages during fuzzy lookup (O(n) per call).\n let symbols: SymbolRow[];\n if (callerIds && callerIds.size > 0) {\n // Determine languages of the caller symbols\n const callerIdList = [...callerIds];\n const batchLangs = new Set<string>();\n for (let i = 0; i < callerIdList.length; i += 500) {\n const chunk = callerIdList.slice(i, i + 500);\n const placeholders = chunk.map(() => \"?\").join(\",\");\n const rows = db\n .prepare(`SELECT DISTINCT language FROM symbols WHERE id IN (${placeholders})`)\n .all(...chunk) as { language: string }[];\n for (const r of rows) batchLangs.add(r.language);\n }\n if (batchLangs.size > 0) {\n const langPlaceholders = [...batchLangs].map(() => \"?\").join(\",\");\n symbols = db\n .prepare(`SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols WHERE language IN (${langPlaceholders})`)\n .all(...batchLangs) as SymbolRow[];\n } else {\n symbols = db\n .prepare(\"SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols\")\n .all() as SymbolRow[];\n }\n } else {\n symbols = db\n .prepare(\"SELECT id, name, kind, file_path, module_path, language, parent_id FROM symbols\")\n .all() as SymbolRow[];\n }\n const registry = new FunctionRegistry(symbols);\n const fuzzyEnabled = symbols.length <= 500;\n\n // Load all imports into ImportMap\n const imports = db\n .prepare(\"SELECT file_path, symbol_name, source_path FROM imports\")\n .all() as ImportRow[];\n const importMap = new ImportMap(imports);\n\n // Get unresolved calls — skip short/builtin names to save resolution time.\n // If callerIds is provided, only resolve calls from the just-indexed batch\n // (avoids re-resolving 25K+ historical calls — fuzzy is O(n²)).\n let calls: CallRow[];\n if (callerIds && callerIds.size > 0) {\n const idList = [...callerIds];\n calls = [];\n // Batch in chunks of 500 to avoid SQLite param limit\n for (let i = 0; i < idList.length; i += 500) {\n const chunk = idList.slice(i, i + 500);\n const placeholders = chunk.map(() => \"?\").join(\",\");\n const rows = db\n .prepare(\n `SELECT id, caller_id, callee_name, callee_id, line, call_type FROM calls WHERE callee_id IS NULL AND caller_id IN (${placeholders})`,\n )\n .all(...chunk) as CallRow[];\n calls.push(...rows);\n }\n } else {\n calls = db\n .prepare(\n \"SELECT id, caller_id, callee_name, callee_id, line, call_type FROM calls WHERE callee_id IS NULL\",\n )\n .all() as CallRow[];\n }\n\n const stats = { total: calls.length, resolved: 0, byStrategy: {} as Record<string, number> };\n let skipped = 0;\n\n // Get caller file paths — batch query\n const callerFiles = new Map<string, string>();\n const callerIdsFromCalls = [...new Set(calls.map((c) => c.caller_id))];\n if (callerIdsFromCalls.length > 0) {\n // Batch in chunks of 500 to avoid SQLite param limit\n for (let i = 0; i < callerIdsFromCalls.length; i += 500) {\n const chunk = callerIdsFromCalls.slice(i, i + 500);\n const placeholders = chunk.map(() => \"?\").join(\",\");\n const rows = db\n .prepare(`SELECT id, file_path FROM symbols WHERE id IN (${placeholders})`)\n .all(...chunk) as { id: string; file_path: string }[];\n for (const r of rows) {\n callerFiles.set(r.id, r.file_path);\n }\n }\n }\n\n // Resolve each call — skip short/builtin names\n const updateStmt = db.prepare(\"UPDATE calls SET callee_id = ?, confidence = ? WHERE id = ?\");\n const batch = db.transaction(() => {\n for (const call of calls) {\n if (shouldSkipResolution(call.callee_name)) {\n skipped++;\n continue;\n }\n const callerFile = callerFiles.get(call.caller_id) ?? \"\";\n const result = resolveCall(call.callee_name, callerFile, registry, importMap, fuzzyEnabled);\n if (result.calleeId) {\n updateStmt.run(result.calleeId, result.confidence, call.id);\n stats.resolved++;\n stats.byStrategy[result.strategy] = (stats.byStrategy[result.strategy] ?? 0) + 1;\n }\n }\n });\n batch();\n\n if (skipped > 0) {\n console.error(`[remem-mcp] resolveAllCalls: skipped ${skipped} short/builtin names`);\n }\n\n return stats;\n}\n\n/** Derive a module path from a file path: \"src/storage/sqlite.ts\" → \"src/storage/sqlite\" */\nfunction deriveModulePath(filePath: string): string {\n return filePath.replace(/\\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|c|cpp|cc|cxx|hpp|cs)$/, \"\");\n}\n\n/** Calculate directory path distance for suffix-match scoring. */\nfunction pathDistance(a: string, b: string): number {\n if (a === b) return 0;\n const aParts = a.split(\"/\");\n const bParts = b.split(\"/\");\n let common = 0;\n for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {\n if (aParts[i] === bParts[i]) common++;\n else break;\n }\n return aParts.length + bParts.length - 2 * common;\n}\n\n/** Fuzzy lookup: find symbol with most similar name. */\nfunction fuzzyLookup(name: string, registry: FunctionRegistry): SymbolRow | null {\n let best: SymbolRow | null = null;\n let bestScore = 0;\n for (const [symName, candidates] of registry.reverseLookupEntries()) {\n const score = similarity(name, symName);\n if (score > bestScore && score > 0.6) {\n bestScore = score;\n best = candidates[0];\n }\n }\n return best;\n}\n\n/** Simple string similarity (Jaccard on character bigrams). */\nfunction similarity(a: string, b: string): number {\n if (a === b) return 1;\n const aBigrams = bigrams(a);\n const bBigrams = bigrams(b);\n const intersection = aBigrams.filter((bg) => bBigrams.includes(bg)).length;\n const union = aBigrams.length + bBigrams.length - intersection;\n return union > 0 ? intersection / union : 0;\n}\n\nfunction bigrams(s: string): string[] {\n const result: string[] = [];\n for (let i = 0; i < s.length - 1; i++) {\n result.push(s.slice(i, i + 2));\n }\n return result;\n}\n"],"mappings":"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","names":["candidates"]}
package/dist/sdk.js CHANGED
@@ -1,14 +1,12 @@
1
1
  import {
2
- LocalEmbedder,
3
2
  Memory,
4
- SQLiteBackend,
5
3
  classifyGlobalContent,
6
4
  findOutdatedPages,
7
5
  getWikiPage,
8
6
  ingestDirectory,
9
7
  ingestFile,
10
8
  searchWiki
11
- } from "./chunk-X4ECGLYH.js";
9
+ } from "./chunk-ITMR6THY.js";
12
10
  import {
13
11
  SUPPORTED_LANGUAGES,
14
12
  detectLanguage,
@@ -19,8 +17,13 @@ import {
19
17
  indexFile,
20
18
  listSymbols,
21
19
  searchSymbols
22
- } from "./chunk-FA324IBN.js";
23
- import "./chunk-XK4Y53J2.js";
20
+ } from "./chunk-YU7WBJU5.js";
21
+ import {
22
+ LocalEmbedder,
23
+ SQLiteBackend
24
+ } from "./chunk-4WCDAEPP.js";
25
+ import "./chunk-XJGDYQ34.js";
26
+ import "./chunk-ONQHLUGM.js";
24
27
  export {
25
28
  LocalEmbedder,
26
29
  Memory,