remem-mcp 0.7.3 → 0.7.6

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 +1 @@
1
- {"version":3,"sources":["../src/codegraph/engine.ts"],"sourcesContent":["/**\n * CodeGraph engine: parse code, extract symbols, build call graph, impact analysis.\n *\n * Uses @kreuzberg/tree-sitter-language-pack for multi-language parsing.\n * Supports: TypeScript, JavaScript, Python, Go, Rust, Java, C, C++, C#.\n */\nimport { createHash } from \"node:crypto\";\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { extname, join, relative, sep } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { generateId } from \"../utils/ulid.js\";\n\n// Lazy-load the language pack (CommonJS interop)\nlet _pack: unknown = null;\nasync function getPack(): Promise<Record<string, unknown>> {\n if (!_pack) {\n const mod = await import(\"@kreuzberg/tree-sitter-language-pack\");\n // Handle both ESM default and CJS module.exports\n _pack = mod.default ?? mod;\n }\n return _pack as Record<string, unknown>;\n}\n\n/** Supported languages mapped to file extensions. */\nconst EXTENSION_MAP: Record<string, string> = {\n \".ts\": \"typescript\",\n \".tsx\": \"tsx\",\n \".js\": \"javascript\",\n \".jsx\": \"javascript\",\n \".mjs\": \"javascript\",\n \".cjs\": \"javascript\",\n \".py\": \"python\",\n \".go\": \"go\",\n \".rs\": \"rust\",\n \".java\": \"java\",\n \".c\": \"c\",\n \".h\": \"c\",\n \".cpp\": \"cpp\",\n \".cc\": \"cpp\",\n \".cxx\": \"cpp\",\n \".hpp\": \"cpp\",\n \".cs\": \"csharp\",\n};\n\n/** Languages we support for symbol extraction. */\nexport const SUPPORTED_LANGUAGES = new Set(Object.values(EXTENSION_MAP));\n\n/** Detect language from file extension. Returns null if unsupported. */\nexport function detectLanguage(filePath: string): string | null {\n const ext = extname(filePath).toLowerCase();\n return EXTENSION_MAP[ext] ?? null;\n}\n\n/** A symbol extracted from code. */\nexport interface SymbolInfo {\n id: string;\n name: string;\n kind: string;\n filePath: string;\n lineStart: number;\n lineEnd: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parentId: string | null;\n contentHash: string;\n}\n\n/** A call relationship. */\nexport interface CallInfo {\n callerId: string;\n calleeName: string;\n calleeId: string | null;\n line: number;\n kind: string;\n callType?: string;\n}\n\n/** An import relationship. */\nexport interface ImportInfo {\n filePath: string;\n symbolName: string;\n sourcePath: string | null;\n line: number;\n language: string;\n}\n\n/** Result of indexing a file. */\nexport interface IndexResult {\n file: string;\n language: string;\n symbols: number;\n calls: number;\n imports: number;\n skipped: boolean;\n}\n\n/** Impact analysis result. */\nexport interface ImpactResult {\n rootSymbol: SymbolInfo;\n affected: Array<{ symbol: SymbolInfo; depth: number; path: string[] }>;\n}\n\n/** Parse a file and extract symbols, calls, and imports. */\nexport async function parseFile(\n filePath: string,\n repoPath: string,\n): Promise<{ symbols: SymbolInfo[]; calls: CallInfo[]; imports: ImportInfo[] } | null> {\n const language = detectLanguage(filePath);\n if (!language) return null;\n\n const pack = (await getPack()) as {\n hasLanguage: (lang: string) => boolean;\n process: (\n source: string,\n opts: { language: string },\n ) => {\n structure: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n children?: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n }>;\n }>;\n imports: Array<{\n items?: string[];\n source?: string;\n span?: { startLine?: number };\n }>;\n } | null;\n };\n if (!pack.hasLanguage(language)) return null;\n\n let source: string;\n try {\n source = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n // Skip files that are too large — tree-sitter parsing + regex extraction\n // scales O(n) with file size, and very large files are usually generated/vendored\n const MAX_FILE_LINES = 3000;\n const MAX_FILE_BYTES = 200_000; // 200KB\n if (source.length > MAX_FILE_BYTES || source.split(\"\\n\").length > MAX_FILE_LINES) {\n return null;\n }\n\n const result = pack.process(source, { language });\n if (!result) return null;\n\n const relPath = relative(repoPath, filePath).split(sep).join(\"/\");\n const symbols: SymbolInfo[] = [];\n const calls: CallInfo[] = [];\n const imports: ImportInfo[] = [];\n\n // Cache source lines once — used for offset calculation in extractSymbols\n const sourceLines = source.split(\"\\n\");\n // Precompute cumulative line start offsets for O(1) line→offset lookup\n const lineOffsets: number[] = [0];\n for (let i = 0; i < sourceLines.length; i++) {\n lineOffsets.push(lineOffsets[i] + sourceLines[i].length + 1); // +1 for \\n\n }\n\n // Extract symbols from structure\n const extractSymbols = (\n items: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n children?: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n }>;\n }>,\n parentId: string | null,\n ) => {\n for (const item of items) {\n if (!item.name) continue;\n const id = generateId();\n const lineStart = (item.span?.startLine ?? 0) + 1; // 1-indexed\n const lineEnd = (item.span?.endLine ?? lineStart) + 1;\n // Use cached lineOffsets instead of source.split(\"\\n\") per symbol\n const bodyStart = item.span?.startLine !== undefined ? lineOffsets[item.span.startLine] : 0;\n const bodyEnd =\n item.span?.endLine !== undefined ? lineOffsets[item.span.endLine + 1] - 1 : source.length;\n const bodyText = source.slice(bodyStart, bodyEnd);\n symbols.push({\n id,\n name: item.name,\n kind: item.kind ?? \"unknown\",\n filePath: relPath,\n lineStart,\n lineEnd,\n language,\n signature: item.signature ?? null,\n docstring: item.docComment ?? null,\n parentId,\n contentHash: createHash(\"sha256\")\n .update(item.name + relPath + lineStart)\n .digest(\"hex\"),\n });\n\n // Extract calls within this symbol's body — single-pass on body text\n // Captures: foo(), obj.method(), <Component/>, Class.create()\n // Uses cached bodyText + offset→line map (no per-line regex)\n const bodyStartLine = lineStart;\n\n // Build offset→line map for this body using cached lineOffsets\n const offsetToLine = (offset: number): number => {\n // offset is relative to bodyText which starts at bodyStart in source\n const sourceOffset = bodyStart + offset;\n // Binary search in lineOffsets\n let lo = 0,\n hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (lineOffsets[mid] <= sourceOffset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1; // 1-indexed\n };\n\n // Track offsets already captured to avoid duplicates\n const capturedOffsets = new Set<number>();\n\n // Pass 1: JSX components <Component> — capitalized identifiers\n const jsxRegex = /<([A-Z][a-zA-Z0-9_$]*)\\b/g;\n let jMatch: RegExpExecArray | null;\n while ((jMatch = jsxRegex.exec(bodyText)) !== null) {\n const calleeName = jMatch[1];\n const offset = jMatch.index;\n if (!isKeywordOrSelf(calleeName, item.name) && !capturedOffsets.has(offset)) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: offsetToLine(offset),\n kind: \"call\",\n callType: \"jsx\",\n });\n capturedOffsets.add(offset);\n }\n }\n\n // Pass 2: Method calls (obj.method) — captures full qualified name\n const methodCallRegex = /\\b([a-zA-Z_$][a-zA-Z0-9_$]*)\\.([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\n let mMatch: RegExpExecArray | null;\n while ((mMatch = methodCallRegex.exec(bodyText)) !== null) {\n const receiver = mMatch[1];\n const methodName = mMatch[2];\n const calleeName = `${receiver}.${methodName}`;\n const offset = mMatch.index;\n if (\n !isKeywordOrSelf(methodName, item.name) &&\n !isKeywordOrSelf(receiver, item.name) &&\n !isStdlibCall(calleeName, language) &&\n !capturedOffsets.has(offset)\n ) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: offsetToLine(offset),\n kind: \"call\",\n callType: \"method\",\n });\n capturedOffsets.add(offset);\n }\n }\n\n // Pass 3: Simple calls (foo) — skip if offset already captured or stdlib\n const simpleCallRegex = /\\b([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\n let match: RegExpExecArray | null;\n while ((match = simpleCallRegex.exec(bodyText)) !== null) {\n const calleeName = match[1];\n const offset = match.index;\n if (\n !isKeywordOrSelf(calleeName, item.name) &&\n !isStdlibCall(calleeName, language) &&\n !capturedOffsets.has(offset)\n ) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: offsetToLine(offset),\n kind: \"call\",\n callType: \"direct\",\n });\n capturedOffsets.add(offset);\n }\n }\n\n // Recurse into children\n if (item.children && item.children.length > 0) {\n extractSymbols(item.children, id);\n }\n }\n };\n\n extractSymbols(result.structure ?? [], null);\n\n // Extract imports\n for (const imp of result.imports ?? []) {\n imports.push({\n filePath: relPath,\n symbolName: imp.items?.join(\", \") ?? imp.source ?? \"unknown\",\n sourcePath: imp.source ?? null,\n line: (imp.span?.startLine ?? 0) + 1,\n language,\n });\n }\n\n return { symbols, calls, imports };\n}\n\n/** Check if a name is a language keyword or the symbol itself (should be skipped in call extraction). */\nfunction isKeywordOrSelf(name: string, symbolName: string): boolean {\n if (name === symbolName) return true;\n const keywords = new Set([\n \"if\",\n \"for\",\n \"while\",\n \"switch\",\n \"return\",\n \"function\",\n \"def\",\n \"func\",\n \"fn\",\n \"print\",\n \"console\",\n \"require\",\n \"import\",\n \"export\",\n \"class\",\n \"struct\",\n \"enum\",\n \"interface\",\n \"type\",\n \"const\",\n \"let\",\n \"var\",\n \"new\",\n \"delete\",\n \"typeof\",\n \"instanceof\",\n \"void\",\n \"in\",\n \"of\",\n \"await\",\n \"async\",\n \"yield\",\n \"throw\",\n \"try\",\n \"catch\",\n \"finally\",\n \"break\",\n \"continue\",\n \"do\",\n \"else\",\n \"case\",\n \"default\",\n \"extends\",\n \"implements\",\n \"super\",\n \"this\",\n \"self\",\n \"true\",\n \"false\",\n \"null\",\n \"undefined\",\n \"nil\",\n \"None\",\n \"True\",\n \"False\",\n \"println\",\n \"printf\",\n \"fmt\",\n \"err\",\n \"panic\",\n \"recover\",\n ]);\n return keywords.has(name);\n}\n\n/** Stdlib prefixes per language — calls to these are skipped (not indexed). */\nconst STDLIB_PREFIXES: Record<string, Set<string>> = {\n go: new Set([\n \"fmt\",\n \"json\",\n \"os\",\n \"io\",\n \"strings\",\n \"strconv\",\n \"time\",\n \"errors\",\n \"sync\",\n \"context\",\n \"path\",\n \"filepath\",\n \"sort\",\n \"bytes\",\n \"bufio\",\n \"encoding\",\n \"net\",\n \"http\",\n \"url\",\n \"regexp\",\n \"math\",\n \"log\",\n \"reflect\",\n \"runtime\",\n \"unsafe\",\n \"atomic\",\n \"crypto\",\n \"hash\",\n \"base64\",\n \"hex\",\n \"ioutil\",\n \"filepath\",\n \"unicode\",\n \"testing\",\n \"flag\",\n \"env\",\n ]),\n typescript: new Set([\n \"console\",\n \"JSON\",\n \"Math\",\n \"Date\",\n \"Object\",\n \"Array\",\n \"String\",\n \"Number\",\n \"Boolean\",\n \"Promise\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"WeakMap\",\n \"WeakSet\",\n \"Error\",\n \"RegExp\",\n \"Buffer\",\n \"process\",\n \"setTimeout\",\n \"setInterval\",\n \"clearTimeout\",\n \"clearInterval\",\n \"fetch\",\n \"URL\",\n \"URLSearchParams\",\n \"FormData\",\n \"Headers\",\n \"Request\",\n \"Response\",\n \"AbortController\",\n \"Event\",\n \"EventTarget\",\n \"CustomEvent\",\n \"TextEncoder\",\n \"TextDecoder\",\n \"crypto\",\n \"performance\",\n \"queueMicrotask\",\n \"atob\",\n \"btoa\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"encodeURI\",\n \"decodeURI\",\n ]),\n javascript: new Set([\n \"console\",\n \"JSON\",\n \"Math\",\n \"Date\",\n \"Object\",\n \"Array\",\n \"String\",\n \"Number\",\n \"Boolean\",\n \"Promise\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"WeakMap\",\n \"WeakSet\",\n \"Error\",\n \"RegExp\",\n \"Buffer\",\n \"process\",\n \"setTimeout\",\n \"setInterval\",\n \"clearTimeout\",\n \"clearInterval\",\n \"fetch\",\n \"URL\",\n \"URLSearchParams\",\n \"FormData\",\n \"Headers\",\n \"Request\",\n \"Response\",\n \"AbortController\",\n \"Event\",\n \"EventTarget\",\n \"CustomEvent\",\n \"TextEncoder\",\n \"TextDecoder\",\n \"crypto\",\n \"performance\",\n \"queueMicrotask\",\n \"atob\",\n \"btoa\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"encodeURI\",\n \"decodeURI\",\n ]),\n python: new Set([\n \"print\",\n \"len\",\n \"range\",\n \"str\",\n \"int\",\n \"float\",\n \"bool\",\n \"list\",\n \"dict\",\n \"set\",\n \"tuple\",\n \"type\",\n \"isinstance\",\n \"issubclass\",\n \"id\",\n \"hash\",\n \"dir\",\n \"vars\",\n \"globals\",\n \"locals\",\n \"exec\",\n \"eval\",\n \"compile\",\n \"open\",\n \"input\",\n \"repr\",\n \"format\",\n \"chr\",\n \"ord\",\n \"hex\",\n \"oct\",\n \"bin\",\n \"abs\",\n \"min\",\n \"max\",\n \"sum\",\n \"round\",\n \"pow\",\n \"divmod\",\n \"sorted\",\n \"reversed\",\n \"enumerate\",\n \"zip\",\n \"map\",\n \"filter\",\n \"any\",\n \"all\",\n \"next\",\n \"iter\",\n \"getattr\",\n \"setattr\",\n \"hasattr\",\n \"delattr\",\n \"property\",\n \"staticmethod\",\n \"classmethod\",\n \"super\",\n \"object\",\n \"Exception\",\n \"ValueError\",\n \"TypeError\",\n \"KeyError\",\n \"IndexError\",\n \"AttributeError\",\n \"RuntimeError\",\n \"StopIteration\",\n \"os\",\n \"sys\",\n \"json\",\n \"re\",\n \"time\",\n \"datetime\",\n \"pathlib\",\n \"typing\",\n \"collections\",\n \"itertools\",\n \"functools\",\n \"subprocess\",\n \"argparse\",\n \"logging\",\n \"unittest\",\n \"asyncio\",\n \"threading\",\n \"multiprocessing\",\n \"pickle\",\n \"shutil\",\n \"tempfile\",\n \"glob\",\n \"fnmatch\",\n \"csv\",\n \"io\",\n \"base64\",\n \"hashlib\",\n \"hmac\",\n \"secrets\",\n \"uuid\",\n \"copy\",\n \"math\",\n \"random\",\n \"statistics\",\n \"decimal\",\n \"fractions\",\n \"sqlite3\",\n ]),\n rust: new Set([\n \"println\",\n \"print\",\n \"eprintln\",\n \"eprint\",\n \"format\",\n \"vec\",\n \"String\",\n \"Vec\",\n \"Option\",\n \"Result\",\n \"Some\",\n \"None\",\n \"Ok\",\n \"Err\",\n \"Box\",\n \"Rc\",\n \"Arc\",\n \"RefCell\",\n \"Cell\",\n \"Mutex\",\n \"RwLock\",\n \"HashMap\",\n \"HashSet\",\n \"BTreeMap\",\n \"BTreeSet\",\n \"VecDeque\",\n \"LinkedList\",\n \"std\",\n \"core\",\n \"alloc\",\n \"macro\",\n \"todo\",\n \"unimplemented\",\n \"panic\",\n \"assert\",\n \"assert_eq\",\n \"assert_ne\",\n \"debug_assert\",\n \"debug_assert_eq\",\n \"cfg\",\n \"env\",\n \"include\",\n \"concat\",\n \"stringify\",\n \"file\",\n \"line\",\n \"module_path\",\n \"column\",\n \"format_args\",\n \"write\",\n \"writeln\",\n ]),\n};\n\n/** Check if a callee name is a stdlib call for the given language. */\nfunction isStdlibCall(calleeName: string, language: string): boolean {\n const prefixes = STDLIB_PREFIXES[language];\n if (!prefixes) return false;\n // Check direct match (e.g. \"println\", \"len\")\n if (prefixes.has(calleeName)) return true;\n // Check prefix match (e.g. \"fmt.Printf\" → prefix \"fmt\")\n const dotIdx = calleeName.indexOf(\".\");\n if (dotIdx > 0) {\n const prefix = calleeName.slice(0, dotIdx);\n if (prefixes.has(prefix)) return true;\n }\n return false;\n}\n\n/** Index a single file into the database. */\nexport async function indexFile(\n db: Database,\n filePath: string,\n repoPath: string,\n teamId: string | null,\n): Promise<IndexResult> {\n const language = detectLanguage(filePath);\n if (!language) {\n return { file: filePath, language: \"unknown\", symbols: 0, calls: 0, imports: 0, skipped: true };\n }\n\n const parsed = await parseFile(filePath, repoPath);\n if (!parsed) {\n return { file: filePath, language, symbols: 0, calls: 0, imports: 0, skipped: true };\n }\n\n const relPath = relative(repoPath, filePath).split(sep).join(\"/\");\n const now = Date.now();\n\n // Delete existing symbols for this file\n const existingIds = db\n .prepare(\"SELECT id FROM symbols WHERE file_path = ? AND team_id IS ?\")\n .all(relPath, teamId) as { id: string }[];\n if (existingIds.length > 0) {\n const placeholders = existingIds.map(() => \"?\").join(\",\");\n db.prepare(`DELETE FROM calls WHERE caller_id IN (${placeholders})`).run(\n ...existingIds.map((e) => e.id),\n );\n db.prepare(\"DELETE FROM symbols WHERE file_path = ? AND team_id IS ?\").run(relPath, teamId);\n }\n db.prepare(\"DELETE FROM imports WHERE file_path = ? AND team_id IS ?\").run(relPath, teamId);\n\n // Insert symbols\n const symbolStmt = db.prepare(\n `INSERT INTO symbols (id, name, kind, file_path, line_start, line_end, language, signature, docstring, parent_id, team_id, repo_path, content_hash, module_path, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n const modulePath = relPath.replace(\n /\\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|c|cpp|cc|cxx|hpp|cs)$/,\n \"\",\n );\n for (const s of parsed.symbols) {\n symbolStmt.run(\n s.id,\n s.name,\n s.kind,\n s.filePath,\n s.lineStart,\n s.lineEnd,\n s.language,\n s.signature,\n s.docstring,\n s.parentId,\n teamId,\n repoPath,\n s.contentHash,\n modulePath,\n now,\n now,\n );\n }\n\n // Insert calls (callee_id resolved later by 6-strategy cascade in indexDirectory)\n const callStmt = db.prepare(\n `INSERT INTO calls (caller_id, callee_name, callee_id, line, kind, call_type, team_id) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n for (const c of parsed.calls) {\n callStmt.run(\n c.callerId,\n c.calleeName,\n null, // Will be resolved by resolveAllCalls() in indexDirectory\n c.line,\n c.kind,\n (c as { callType?: string }).callType ?? \"direct\",\n teamId,\n );\n }\n\n // Insert imports\n const importStmt = db.prepare(\n `INSERT INTO imports (file_path, symbol_name, source_path, line, language, team_id, repo_path) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n for (const imp of parsed.imports) {\n importStmt.run(\n imp.filePath,\n imp.symbolName,\n imp.sourcePath,\n imp.line,\n imp.language,\n teamId,\n repoPath,\n );\n }\n\n return {\n file: relPath,\n language,\n symbols: parsed.symbols.length,\n calls: parsed.calls.length,\n imports: parsed.imports.length,\n skipped: false,\n };\n}\n\n/** Index a directory recursively. */\nexport async function indexDirectory(\n db: Database,\n dirPath: string,\n repoPath: string,\n teamId: string | null,\n maxFiles = 10000,\n): Promise<IndexResult[]> {\n const results: IndexResult[] = [];\n const files: string[] = [];\n\n const walk = (dir: string) => {\n if (files.length >= maxFiles) return;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (files.length >= maxFiles) return;\n const fullPath = join(dir, entry);\n let stat: ReturnType<typeof statSync>;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n // Skip common ignore dirs\n if (\n entry === \"node_modules\" ||\n entry === \".git\" ||\n entry === \"dist\" ||\n entry === \"build\" ||\n entry === \"target\" ||\n entry === \"__pycache__\" ||\n entry === \".next\" ||\n entry === \"vendor\" ||\n entry === \".venv\" ||\n entry.startsWith(\".\")\n )\n continue;\n walk(fullPath);\n } else if (stat.isFile()) {\n if (detectLanguage(fullPath)) {\n files.push(fullPath);\n }\n }\n }\n };\n\n walk(dirPath);\n\n const indexedSymbolIds = new Set<string>();\n for (const file of files) {\n const result = await indexFile(db, file, repoPath, teamId);\n results.push(result);\n }\n\n // Collect symbol IDs from the just-indexed files — resolveAllCalls will only\n // resolve calls whose caller_id is in this set, avoiding re-processing 25K+\n // historical calls (fuzzy strategy is O(n²)).\n if (results.length > 0) {\n const indexedFiles = results.filter((r) => !r.skipped).map((r) => r.file);\n for (let i = 0; i < indexedFiles.length; i += 500) {\n const chunk = indexedFiles.slice(i, i + 500);\n const placeholders = chunk.map(() => \"?\").join(\",\");\n const rows = db\n .prepare(`SELECT id FROM symbols WHERE file_path IN (${placeholders})`)\n .all(...chunk) as { id: string }[];\n for (const r of rows) {\n indexedSymbolIds.add(r.id);\n }\n }\n }\n\n // Resolve callee IDs using 6-strategy cascade (import-map, same-module, unique-name, suffix, fuzzy)\n // Adapted from Codebase-Memory (arXiv:2603.27277)\n const { resolveAllCalls } = await import(\"./resolver.js\");\n const stats = resolveAllCalls(db, indexedSymbolIds);\n if (stats.total > 0) {\n console.error(\n `[remem-mcp] indexDirectory: resolved ${stats.resolved}/${stats.total} calls` +\n (Object.keys(stats.byStrategy).length > 0 ? ` (${JSON.stringify(stats.byStrategy)})` : \"\"),\n );\n }\n\n return results;\n}\n\n/** Search symbols by name or pattern. */\nexport function searchSymbols(\n db: Database,\n query: string,\n opts: {\n teamId?: string;\n kind?: string;\n language?: string;\n limit?: number;\n repoPath?: string;\n } = {},\n): SymbolInfo[] {\n const limit = opts.limit ?? 20;\n const pattern = `%${query}%`;\n let sql = \"SELECT * FROM symbols WHERE name LIKE ?\";\n const params: unknown[] = [pattern];\n if (opts.teamId !== undefined) {\n sql += \" AND team_id IS ?\";\n params.push(opts.teamId);\n }\n if (opts.repoPath !== undefined) {\n // Use LIKE prefix match so /path/to/src also matches repo_path /path/to\n sql += \" AND (repo_path = ? OR repo_path LIKE ?)\";\n params.push(opts.repoPath, `${opts.repoPath}%`);\n }\n if (opts.kind) {\n sql += \" AND kind = ?\";\n params.push(opts.kind);\n }\n if (opts.language) {\n sql += \" AND language = ?\";\n params.push(opts.language);\n }\n sql += \" LIMIT ?\";\n params.push(limit);\n\n const rows = db.prepare(sql).all(...params) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }>;\n\n return rows.map((r) => ({\n id: r.id,\n name: r.name,\n kind: r.kind,\n filePath: r.file_path,\n lineStart: r.line_start,\n lineEnd: r.line_end,\n language: r.language,\n signature: r.signature,\n docstring: r.docstring,\n parentId: r.parent_id,\n contentHash: r.content_hash,\n }));\n}\n\n/** Find all callers of a symbol (who calls X?). */\nexport function findCallers(\n db: Database,\n symbolId: string,\n opts: { limit?: number } = {},\n): Array<{ caller: SymbolInfo; line: number }> {\n const limit = opts.limit ?? 50;\n const rows = db\n .prepare(\n `SELECT s.*, c.line as call_line\n FROM calls c\n JOIN symbols s ON s.id = c.caller_id\n WHERE c.callee_id = ? OR c.callee_name = (SELECT name FROM symbols WHERE id = ?)\n LIMIT ?`,\n )\n .all(symbolId, symbolId, limit) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n call_line: number;\n }>;\n\n return rows.map((r) => ({\n caller: {\n id: r.id,\n name: r.name,\n kind: r.kind,\n filePath: r.file_path,\n lineStart: r.line_start,\n lineEnd: r.line_end,\n language: r.language,\n signature: r.signature,\n docstring: r.docstring,\n parentId: r.parent_id,\n contentHash: r.content_hash,\n },\n line: r.call_line,\n }));\n}\n\n/** Find all callees of a symbol (X calls whom?). */\nexport function findCallees(\n db: Database,\n symbolId: string,\n opts: { limit?: number } = {},\n): Array<{ callee: SymbolInfo | null; calleeName: string; line: number }> {\n const limit = opts.limit ?? 50;\n const rows = db\n .prepare(\n `SELECT c.callee_name, c.callee_id, c.line,\n s.id as sid, s.name as sname, s.kind as skind, s.file_path as sfile,\n s.line_start as sline_start, s.line_end as sline_end, s.language as slang,\n s.signature as ssig, s.docstring as sdoc, s.parent_id as sparent, s.content_hash as shash\n FROM calls c\n LEFT JOIN symbols s ON s.id = c.callee_id\n WHERE c.caller_id = ?\n LIMIT ?`,\n )\n .all(symbolId, limit) as Array<{\n callee_name: string;\n callee_id: string | null;\n line: number;\n sid: string | null;\n sname: string | null;\n skind: string | null;\n sfile: string | null;\n sline_start: number | null;\n sline_end: number | null;\n slang: string | null;\n ssig: string | null;\n sdoc: string | null;\n sparent: string | null;\n shash: string | null;\n }>;\n\n return rows.map((r) => ({\n callee: r.sid\n ? {\n id: r.sid,\n name: r.sname ?? \"\",\n kind: r.skind ?? \"\",\n filePath: r.sfile ?? \"\",\n lineStart: r.sline_start ?? 0,\n lineEnd: r.sline_end ?? 0,\n language: r.slang ?? \"\",\n signature: r.ssig,\n docstring: r.sdoc,\n parentId: r.sparent,\n contentHash: r.shash ?? \"\",\n }\n : null,\n calleeName: r.callee_name,\n line: r.line,\n }));\n}\n\n/** Impact analysis: BFS from a symbol through the caller chain.\n * Returns all symbols that might be affected if the given symbol changes. */\nexport function impactAnalysis(\n db: Database,\n symbolId: string,\n opts: { maxDepth?: number } = {},\n): ImpactResult {\n const maxDepth = opts.maxDepth ?? 5;\n\n const rootRow = db.prepare(\"SELECT * FROM symbols WHERE id = ?\").get(symbolId) as\n | {\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }\n | undefined;\n\n if (!rootRow) {\n throw new Error(`Symbol not found: ${symbolId}`);\n }\n\n const root: SymbolInfo = {\n id: rootRow.id,\n name: rootRow.name,\n kind: rootRow.kind,\n filePath: rootRow.file_path,\n lineStart: rootRow.line_start,\n lineEnd: rootRow.line_end,\n language: rootRow.language,\n signature: rootRow.signature,\n docstring: rootRow.docstring,\n parentId: rootRow.parent_id,\n contentHash: rootRow.content_hash,\n };\n\n const affected: Array<{ symbol: SymbolInfo; depth: number; path: string[] }> = [];\n const visited = new Set<string>([symbolId]);\n\n // BFS through callers (who calls X → who calls them → ...)\n const queue: Array<{ id: string; depth: number; path: string[] }> = [\n { id: symbolId, depth: 0, path: [root.name] },\n ];\n\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) continue;\n const { id, depth, path } = item;\n if (depth >= maxDepth) continue;\n\n const callers = findCallers(db, id, { limit: 100 });\n for (const { caller } of callers) {\n if (visited.has(caller.id)) continue;\n visited.add(caller.id);\n const newPath = [...path, caller.name];\n affected.push({ symbol: caller, depth: depth + 1, path: newPath });\n queue.push({ id: caller.id, depth: depth + 1, path: newPath });\n }\n }\n\n return { rootSymbol: root, affected };\n}\n\n/** List symbols in a file or directory. */\nexport function listSymbols(\n db: Database,\n filePath: string,\n opts: { teamId?: string; kind?: string; limit?: number; repoPath?: string } = {},\n): SymbolInfo[] {\n const limit = opts.limit ?? 100;\n\n // Convert absolute path to relative if repoPath is provided\n let relPath = filePath;\n if (opts.repoPath) {\n try {\n relPath = relative(opts.repoPath, filePath).split(sep).join(\"/\");\n } catch {\n // If relative() fails, use the original path\n }\n }\n\n let sql = \"SELECT * FROM symbols WHERE file_path LIKE ?\";\n const params: unknown[] = [`${relPath}%`];\n if (opts.teamId !== undefined) {\n sql += \" AND team_id IS ?\";\n params.push(opts.teamId);\n }\n if (opts.repoPath !== undefined) {\n // Use LIKE prefix match so /path/to/src also matches repo_path /path/to\n sql += \" AND (repo_path = ? OR repo_path LIKE ?)\";\n params.push(opts.repoPath, `${opts.repoPath}%`);\n }\n if (opts.kind) {\n sql += \" AND kind = ?\";\n params.push(opts.kind);\n }\n sql += \" ORDER BY line_start LIMIT ?\";\n params.push(limit);\n\n const rows = db.prepare(sql).all(...params) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }>;\n\n return rows.map((r) => ({\n id: r.id,\n name: r.name,\n kind: r.kind,\n filePath: r.file_path,\n lineStart: r.line_start,\n lineEnd: r.line_end,\n language: r.language,\n signature: r.signature,\n docstring: r.docstring,\n parentId: r.parent_id,\n contentHash: r.content_hash,\n }));\n}\n"],"mappings":"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QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,YAAY,oBAAI,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,YAAY,oBAAI,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,QAAQ,oBAAI,IAAI;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,MAAM,oBAAI,IAAI;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/codegraph/engine.ts"],"sourcesContent":["/**\n * CodeGraph engine: parse code, extract symbols, build call graph, impact analysis.\n *\n * Uses @kreuzberg/tree-sitter-language-pack for multi-language parsing.\n * Supports: TypeScript, JavaScript, Python, Go, Rust, Java, C, C++, C#.\n */\nimport { createHash } from \"node:crypto\";\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { extname, join, relative, sep } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { generateId } from \"../utils/ulid.js\";\n\n// Lazy-load the language pack (CommonJS interop)\nlet _pack: unknown = null;\nasync function getPack(): Promise<Record<string, unknown>> {\n if (!_pack) {\n const mod = await import(\"@kreuzberg/tree-sitter-language-pack\");\n // Handle both ESM default and CJS module.exports\n _pack = mod.default ?? mod;\n }\n return _pack as Record<string, unknown>;\n}\n\n/** Supported languages mapped to file extensions. */\nconst EXTENSION_MAP: Record<string, string> = {\n \".ts\": \"typescript\",\n \".tsx\": \"tsx\",\n \".js\": \"javascript\",\n \".jsx\": \"javascript\",\n \".mjs\": \"javascript\",\n \".cjs\": \"javascript\",\n \".py\": \"python\",\n \".go\": \"go\",\n \".rs\": \"rust\",\n \".java\": \"java\",\n \".c\": \"c\",\n \".h\": \"c\",\n \".cpp\": \"cpp\",\n \".cc\": \"cpp\",\n \".cxx\": \"cpp\",\n \".hpp\": \"cpp\",\n \".cs\": \"csharp\",\n};\n\n/** Languages we support for symbol extraction. */\nexport const SUPPORTED_LANGUAGES = new Set(Object.values(EXTENSION_MAP));\n\n/** Detect language from file extension. Returns null if unsupported. */\nexport function detectLanguage(filePath: string): string | null {\n const ext = extname(filePath).toLowerCase();\n return EXTENSION_MAP[ext] ?? null;\n}\n\n/** A symbol extracted from code. */\nexport interface SymbolInfo {\n id: string;\n name: string;\n kind: string;\n filePath: string;\n lineStart: number;\n lineEnd: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parentId: string | null;\n contentHash: string;\n}\n\n/** A call relationship. */\nexport interface CallInfo {\n callerId: string;\n calleeName: string;\n calleeId: string | null;\n line: number;\n kind: string;\n callType?: string;\n}\n\n/** An import relationship. */\nexport interface ImportInfo {\n filePath: string;\n symbolName: string;\n sourcePath: string | null;\n line: number;\n language: string;\n}\n\n/** Result of indexing a file. */\nexport interface IndexResult {\n file: string;\n language: string;\n symbols: number;\n calls: number;\n imports: number;\n skipped: boolean;\n}\n\n/** Impact analysis result. */\nexport interface ImpactResult {\n rootSymbol: SymbolInfo;\n affected: Array<{ symbol: SymbolInfo; depth: number; path: string[] }>;\n}\n\n/** Parse a file and extract symbols, calls, and imports. */\nexport async function parseFile(\n filePath: string,\n repoPath: string,\n): Promise<{ symbols: SymbolInfo[]; calls: CallInfo[]; imports: ImportInfo[] } | null> {\n const language = detectLanguage(filePath);\n if (!language) return null;\n\n const pack = (await getPack()) as {\n hasLanguage: (lang: string) => boolean;\n process: (\n source: string,\n opts: { language: string },\n ) => {\n structure: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n children?: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n }>;\n }>;\n imports: Array<{\n items?: string[];\n source?: string;\n span?: { startLine?: number };\n }>;\n } | null;\n };\n if (!pack.hasLanguage(language)) return null;\n\n let source: string;\n try {\n source = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n // Skip files that are too large — tree-sitter parsing + regex extraction\n // scales O(n) with file size, and very large files are usually generated/vendored\n const MAX_FILE_LINES = 3000;\n const MAX_FILE_BYTES = 200_000; // 200KB\n if (source.length > MAX_FILE_BYTES || source.split(\"\\n\").length > MAX_FILE_LINES) {\n return null;\n }\n\n const result = pack.process(source, { language });\n if (!result) return null;\n\n const relPath = relative(repoPath, filePath).split(sep).join(\"/\");\n const symbols: SymbolInfo[] = [];\n const calls: CallInfo[] = [];\n const imports: ImportInfo[] = [];\n\n // Cache source lines once — used for offset calculation in extractSymbols\n const sourceLines = source.split(\"\\n\");\n // Precompute cumulative line start offsets for O(1) line→offset lookup\n const lineOffsets: number[] = [0];\n for (let i = 0; i < sourceLines.length; i++) {\n lineOffsets.push(lineOffsets[i] + sourceLines[i].length + 1); // +1 for \\n\n }\n\n // Extract symbols from structure\n const extractSymbols = (\n items: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n children?: Array<{\n kind?: string;\n name?: string;\n span?: { startLine?: number; endLine?: number };\n signature?: string;\n docComment?: string;\n }>;\n }>,\n parentId: string | null,\n ) => {\n for (const item of items) {\n if (!item.name) continue;\n const id = generateId();\n const lineStart = (item.span?.startLine ?? 0) + 1; // 1-indexed\n const lineEnd = (item.span?.endLine ?? lineStart) + 1;\n // Use cached lineOffsets instead of source.split(\"\\n\") per symbol\n const bodyStart = item.span?.startLine !== undefined ? lineOffsets[item.span.startLine] : 0;\n const bodyEnd =\n item.span?.endLine !== undefined ? lineOffsets[item.span.endLine + 1] - 1 : source.length;\n const bodyText = source.slice(bodyStart, bodyEnd);\n symbols.push({\n id,\n name: item.name,\n kind: item.kind ?? \"unknown\",\n filePath: relPath,\n lineStart,\n lineEnd,\n language,\n signature: item.signature ?? null,\n docstring: item.docComment ?? null,\n parentId,\n contentHash: createHash(\"sha256\")\n .update(item.name + relPath + lineStart)\n .digest(\"hex\"),\n });\n\n // Extract calls within this symbol's body — single-pass on body text\n // Captures: foo(), obj.method(), <Component/>, Class.create()\n // Uses cached bodyText + offset→line map (no per-line regex)\n const bodyStartLine = lineStart;\n\n // Build offset→line map for this body using cached lineOffsets\n const offsetToLine = (offset: number): number => {\n // offset is relative to bodyText which starts at bodyStart in source\n const sourceOffset = bodyStart + offset;\n // Binary search in lineOffsets\n let lo = 0,\n hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (lineOffsets[mid] <= sourceOffset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1; // 1-indexed\n };\n\n // Track offsets already captured to avoid duplicates\n const capturedOffsets = new Set<number>();\n\n // Pass 1: JSX components <Component> — capitalized identifiers\n const jsxRegex = /<([A-Z][a-zA-Z0-9_$]*)\\b/g;\n let jMatch: RegExpExecArray | null;\n while ((jMatch = jsxRegex.exec(bodyText)) !== null) {\n const calleeName = jMatch[1];\n const offset = jMatch.index;\n if (!isKeywordOrSelf(calleeName, item.name) && !capturedOffsets.has(offset)) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: offsetToLine(offset),\n kind: \"call\",\n callType: \"jsx\",\n });\n capturedOffsets.add(offset);\n }\n }\n\n // Pass 2: Method calls (obj.method) — captures full qualified name\n const methodCallRegex = /\\b([a-zA-Z_$][a-zA-Z0-9_$]*)\\.([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\n let mMatch: RegExpExecArray | null;\n while ((mMatch = methodCallRegex.exec(bodyText)) !== null) {\n const receiver = mMatch[1];\n const methodName = mMatch[2];\n const calleeName = `${receiver}.${methodName}`;\n const offset = mMatch.index;\n if (\n !isKeywordOrSelf(methodName, item.name) &&\n !isKeywordOrSelf(receiver, item.name) &&\n !isStdlibCall(calleeName, language) &&\n !capturedOffsets.has(offset)\n ) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: offsetToLine(offset),\n kind: \"call\",\n callType: \"method\",\n });\n capturedOffsets.add(offset);\n }\n }\n\n // Pass 3: Simple calls (foo) — skip if offset already captured or stdlib\n const simpleCallRegex = /\\b([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\n let match: RegExpExecArray | null;\n while ((match = simpleCallRegex.exec(bodyText)) !== null) {\n const calleeName = match[1];\n const offset = match.index;\n if (\n !isKeywordOrSelf(calleeName, item.name) &&\n !isStdlibCall(calleeName, language) &&\n !capturedOffsets.has(offset)\n ) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: offsetToLine(offset),\n kind: \"call\",\n callType: \"direct\",\n });\n capturedOffsets.add(offset);\n }\n }\n\n // Recurse into children\n if (item.children && item.children.length > 0) {\n extractSymbols(item.children, id);\n }\n }\n };\n\n extractSymbols(result.structure ?? [], null);\n\n // Extract imports\n for (const imp of result.imports ?? []) {\n imports.push({\n filePath: relPath,\n symbolName: imp.items?.join(\", \") ?? imp.source ?? \"unknown\",\n sourcePath: imp.source ?? null,\n line: (imp.span?.startLine ?? 0) + 1,\n language,\n });\n }\n\n return { symbols, calls, imports };\n}\n\n/** Check if a name is a language keyword or the symbol itself (should be skipped in call extraction). */\nfunction isKeywordOrSelf(name: string, symbolName: string): boolean {\n if (name === symbolName) return true;\n const keywords = new Set([\n \"if\",\n \"for\",\n \"while\",\n \"switch\",\n \"return\",\n \"function\",\n \"def\",\n \"func\",\n \"fn\",\n \"print\",\n \"console\",\n \"require\",\n \"import\",\n \"export\",\n \"class\",\n \"struct\",\n \"enum\",\n \"interface\",\n \"type\",\n \"const\",\n \"let\",\n \"var\",\n \"new\",\n \"delete\",\n \"typeof\",\n \"instanceof\",\n \"void\",\n \"in\",\n \"of\",\n \"await\",\n \"async\",\n \"yield\",\n \"throw\",\n \"try\",\n \"catch\",\n \"finally\",\n \"break\",\n \"continue\",\n \"do\",\n \"else\",\n \"case\",\n \"default\",\n \"extends\",\n \"implements\",\n \"super\",\n \"this\",\n \"self\",\n \"true\",\n \"false\",\n \"null\",\n \"undefined\",\n \"nil\",\n \"None\",\n \"True\",\n \"False\",\n \"println\",\n \"printf\",\n \"fmt\",\n \"err\",\n \"panic\",\n \"recover\",\n ]);\n return keywords.has(name);\n}\n\n/** Stdlib prefixes per language — calls to these are skipped (not indexed). */\nconst STDLIB_PREFIXES: Record<string, Set<string>> = {\n go: new Set([\n \"fmt\",\n \"json\",\n \"os\",\n \"io\",\n \"strings\",\n \"strconv\",\n \"time\",\n \"errors\",\n \"sync\",\n \"context\",\n \"path\",\n \"filepath\",\n \"sort\",\n \"bytes\",\n \"bufio\",\n \"encoding\",\n \"net\",\n \"http\",\n \"url\",\n \"regexp\",\n \"math\",\n \"log\",\n \"reflect\",\n \"runtime\",\n \"unsafe\",\n \"atomic\",\n \"crypto\",\n \"hash\",\n \"base64\",\n \"hex\",\n \"ioutil\",\n \"filepath\",\n \"unicode\",\n \"testing\",\n \"flag\",\n \"env\",\n ]),\n typescript: new Set([\n \"console\",\n \"JSON\",\n \"Math\",\n \"Date\",\n \"Object\",\n \"Array\",\n \"String\",\n \"Number\",\n \"Boolean\",\n \"Promise\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"WeakMap\",\n \"WeakSet\",\n \"Error\",\n \"RegExp\",\n \"Buffer\",\n \"process\",\n \"setTimeout\",\n \"setInterval\",\n \"clearTimeout\",\n \"clearInterval\",\n \"fetch\",\n \"URL\",\n \"URLSearchParams\",\n \"FormData\",\n \"Headers\",\n \"Request\",\n \"Response\",\n \"AbortController\",\n \"Event\",\n \"EventTarget\",\n \"CustomEvent\",\n \"TextEncoder\",\n \"TextDecoder\",\n \"crypto\",\n \"performance\",\n \"queueMicrotask\",\n \"atob\",\n \"btoa\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"encodeURI\",\n \"decodeURI\",\n ]),\n javascript: new Set([\n \"console\",\n \"JSON\",\n \"Math\",\n \"Date\",\n \"Object\",\n \"Array\",\n \"String\",\n \"Number\",\n \"Boolean\",\n \"Promise\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"WeakMap\",\n \"WeakSet\",\n \"Error\",\n \"RegExp\",\n \"Buffer\",\n \"process\",\n \"setTimeout\",\n \"setInterval\",\n \"clearTimeout\",\n \"clearInterval\",\n \"fetch\",\n \"URL\",\n \"URLSearchParams\",\n \"FormData\",\n \"Headers\",\n \"Request\",\n \"Response\",\n \"AbortController\",\n \"Event\",\n \"EventTarget\",\n \"CustomEvent\",\n \"TextEncoder\",\n \"TextDecoder\",\n \"crypto\",\n \"performance\",\n \"queueMicrotask\",\n \"atob\",\n \"btoa\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"encodeURI\",\n \"decodeURI\",\n ]),\n python: new Set([\n \"print\",\n \"len\",\n \"range\",\n \"str\",\n \"int\",\n \"float\",\n \"bool\",\n \"list\",\n \"dict\",\n \"set\",\n \"tuple\",\n \"type\",\n \"isinstance\",\n \"issubclass\",\n \"id\",\n \"hash\",\n \"dir\",\n \"vars\",\n \"globals\",\n \"locals\",\n \"exec\",\n \"eval\",\n \"compile\",\n \"open\",\n \"input\",\n \"repr\",\n \"format\",\n \"chr\",\n \"ord\",\n \"hex\",\n \"oct\",\n \"bin\",\n \"abs\",\n \"min\",\n \"max\",\n \"sum\",\n \"round\",\n \"pow\",\n \"divmod\",\n \"sorted\",\n \"reversed\",\n \"enumerate\",\n \"zip\",\n \"map\",\n \"filter\",\n \"any\",\n \"all\",\n \"next\",\n \"iter\",\n \"getattr\",\n \"setattr\",\n \"hasattr\",\n \"delattr\",\n \"property\",\n \"staticmethod\",\n \"classmethod\",\n \"super\",\n \"object\",\n \"Exception\",\n \"ValueError\",\n \"TypeError\",\n \"KeyError\",\n \"IndexError\",\n \"AttributeError\",\n \"RuntimeError\",\n \"StopIteration\",\n \"os\",\n \"sys\",\n \"json\",\n \"re\",\n \"time\",\n \"datetime\",\n \"pathlib\",\n \"typing\",\n \"collections\",\n \"itertools\",\n \"functools\",\n \"subprocess\",\n \"argparse\",\n \"logging\",\n \"unittest\",\n \"asyncio\",\n \"threading\",\n \"multiprocessing\",\n \"pickle\",\n \"shutil\",\n \"tempfile\",\n \"glob\",\n \"fnmatch\",\n \"csv\",\n \"io\",\n \"base64\",\n \"hashlib\",\n \"hmac\",\n \"secrets\",\n \"uuid\",\n \"copy\",\n \"math\",\n \"random\",\n \"statistics\",\n \"decimal\",\n \"fractions\",\n \"sqlite3\",\n ]),\n rust: new Set([\n \"println\",\n \"print\",\n \"eprintln\",\n \"eprint\",\n \"format\",\n \"vec\",\n \"String\",\n \"Vec\",\n \"Option\",\n \"Result\",\n \"Some\",\n \"None\",\n \"Ok\",\n \"Err\",\n \"Box\",\n \"Rc\",\n \"Arc\",\n \"RefCell\",\n \"Cell\",\n \"Mutex\",\n \"RwLock\",\n \"HashMap\",\n \"HashSet\",\n \"BTreeMap\",\n \"BTreeSet\",\n \"VecDeque\",\n \"LinkedList\",\n \"std\",\n \"core\",\n \"alloc\",\n \"macro\",\n \"todo\",\n \"unimplemented\",\n \"panic\",\n \"assert\",\n \"assert_eq\",\n \"assert_ne\",\n \"debug_assert\",\n \"debug_assert_eq\",\n \"cfg\",\n \"env\",\n \"include\",\n \"concat\",\n \"stringify\",\n \"file\",\n \"line\",\n \"module_path\",\n \"column\",\n \"format_args\",\n \"write\",\n \"writeln\",\n ]),\n};\n\n/** Check if a callee name is a stdlib call for the given language. */\nfunction isStdlibCall(calleeName: string, language: string): boolean {\n const prefixes = STDLIB_PREFIXES[language];\n if (!prefixes) return false;\n // Check direct match (e.g. \"println\", \"len\")\n if (prefixes.has(calleeName)) return true;\n // Check prefix match (e.g. \"fmt.Printf\" → prefix \"fmt\")\n const dotIdx = calleeName.indexOf(\".\");\n if (dotIdx > 0) {\n const prefix = calleeName.slice(0, dotIdx);\n if (prefixes.has(prefix)) return true;\n }\n return false;\n}\n\n/** Index a single file into the database. */\nexport async function indexFile(\n db: Database,\n filePath: string,\n repoPath: string,\n teamId: string | null,\n): Promise<IndexResult> {\n const language = detectLanguage(filePath);\n if (!language) {\n return { file: filePath, language: \"unknown\", symbols: 0, calls: 0, imports: 0, skipped: true };\n }\n\n const parsed = await parseFile(filePath, repoPath);\n if (!parsed) {\n return { file: filePath, language, symbols: 0, calls: 0, imports: 0, skipped: true };\n }\n\n const relPath = relative(repoPath, filePath).split(sep).join(\"/\");\n const now = Date.now();\n\n // Delete existing symbols for this file\n const existingIds = db\n .prepare(\"SELECT id FROM symbols WHERE file_path = ? AND team_id IS ?\")\n .all(relPath, teamId) as { id: string }[];\n if (existingIds.length > 0) {\n const placeholders = existingIds.map(() => \"?\").join(\",\");\n db.prepare(`DELETE FROM calls WHERE caller_id IN (${placeholders})`).run(\n ...existingIds.map((e) => e.id),\n );\n db.prepare(\"DELETE FROM symbols WHERE file_path = ? AND team_id IS ?\").run(relPath, teamId);\n }\n db.prepare(\"DELETE FROM imports WHERE file_path = ? AND team_id IS ?\").run(relPath, teamId);\n\n // Insert symbols\n const symbolStmt = db.prepare(\n `INSERT INTO symbols (id, name, kind, file_path, line_start, line_end, language, signature, docstring, parent_id, team_id, repo_path, content_hash, module_path, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n const modulePath = relPath.replace(\n /\\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|c|cpp|cc|cxx|hpp|cs)$/,\n \"\",\n );\n for (const s of parsed.symbols) {\n symbolStmt.run(\n s.id,\n s.name,\n s.kind,\n s.filePath,\n s.lineStart,\n s.lineEnd,\n s.language,\n s.signature,\n s.docstring,\n s.parentId,\n teamId,\n repoPath,\n s.contentHash,\n modulePath,\n now,\n now,\n );\n }\n\n // Insert calls (callee_id resolved later by 6-strategy cascade in indexDirectory)\n const callStmt = db.prepare(\n `INSERT INTO calls (caller_id, callee_name, callee_id, line, kind, call_type, team_id) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n for (const c of parsed.calls) {\n callStmt.run(\n c.callerId,\n c.calleeName,\n null, // Will be resolved by resolveAllCalls() in indexDirectory\n c.line,\n c.kind,\n (c as { callType?: string }).callType ?? \"direct\",\n teamId,\n );\n }\n\n // Insert imports\n const importStmt = db.prepare(\n `INSERT INTO imports (file_path, symbol_name, source_path, line, language, team_id, repo_path) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n for (const imp of parsed.imports) {\n importStmt.run(\n imp.filePath,\n imp.symbolName,\n imp.sourcePath,\n imp.line,\n imp.language,\n teamId,\n repoPath,\n );\n }\n\n return {\n file: relPath,\n language,\n symbols: parsed.symbols.length,\n calls: parsed.calls.length,\n imports: parsed.imports.length,\n skipped: false,\n };\n}\n\n/** Index a directory recursively. */\nexport async function indexDirectory(\n db: Database,\n dirPath: string,\n repoPath: string,\n teamId: string | null,\n maxFiles = 10000,\n): Promise<IndexResult[]> {\n const results: IndexResult[] = [];\n const files: string[] = [];\n\n const walk = (dir: string) => {\n if (files.length >= maxFiles) return;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (files.length >= maxFiles) return;\n const fullPath = join(dir, entry);\n let stat: ReturnType<typeof statSync>;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n // Skip common ignore dirs\n if (\n entry === \"node_modules\" ||\n entry === \".git\" ||\n entry === \"dist\" ||\n entry === \"build\" ||\n entry === \"target\" ||\n entry === \"__pycache__\" ||\n entry === \".next\" ||\n entry === \"vendor\" ||\n entry === \".venv\" ||\n entry.startsWith(\".\")\n )\n continue;\n walk(fullPath);\n } else if (stat.isFile()) {\n if (detectLanguage(fullPath)) {\n files.push(fullPath);\n }\n }\n }\n };\n\n walk(dirPath);\n\n const indexedSymbolIds = new Set<string>();\n for (const file of files) {\n const result = await indexFile(db, file, repoPath, teamId);\n results.push(result);\n }\n\n // Collect symbol IDs from the just-indexed files — resolveAllCalls will only\n // resolve calls whose caller_id is in this set, avoiding re-processing 25K+\n // historical calls (fuzzy strategy is O(n²)).\n if (results.length > 0) {\n const indexedFiles = results.filter((r) => !r.skipped).map((r) => r.file);\n for (let i = 0; i < indexedFiles.length; i += 500) {\n const chunk = indexedFiles.slice(i, i + 500);\n const placeholders = chunk.map(() => \"?\").join(\",\");\n const rows = db\n .prepare(`SELECT id FROM symbols WHERE file_path IN (${placeholders})`)\n .all(...chunk) as { id: string }[];\n for (const r of rows) {\n indexedSymbolIds.add(r.id);\n }\n }\n }\n\n // Resolve callee IDs using 6-strategy cascade (import-map, same-module, unique-name, suffix, fuzzy)\n // Adapted from Codebase-Memory (arXiv:2603.27277)\n const { resolveAllCalls } = await import(\"./resolver.js\");\n const stats = resolveAllCalls(db, indexedSymbolIds);\n if (stats.total > 0) {\n console.error(\n `[remem-mcp] indexDirectory: resolved ${stats.resolved}/${stats.total} calls` +\n (Object.keys(stats.byStrategy).length > 0 ? ` (${JSON.stringify(stats.byStrategy)})` : \"\"),\n );\n }\n\n return results;\n}\n\n/** Search symbols by name or pattern. */\nexport function searchSymbols(\n db: Database,\n query: string,\n opts: {\n teamId?: string;\n kind?: string;\n language?: string;\n limit?: number;\n repoPath?: string;\n } = {},\n): SymbolInfo[] {\n const limit = opts.limit ?? 20;\n const pattern = `%${query}%`;\n let sql = \"SELECT * FROM symbols WHERE name LIKE ?\";\n const params: unknown[] = [pattern];\n if (opts.teamId !== undefined) {\n sql += \" AND team_id IS ?\";\n params.push(opts.teamId);\n }\n if (opts.repoPath !== undefined) {\n // Use LIKE prefix match so /path/to/src also matches repo_path /path/to\n sql += \" AND (repo_path = ? OR repo_path LIKE ?)\";\n params.push(opts.repoPath, `${opts.repoPath}%`);\n }\n if (opts.kind) {\n sql += \" AND kind = ?\";\n params.push(opts.kind);\n }\n if (opts.language) {\n sql += \" AND language = ?\";\n params.push(opts.language);\n }\n sql += \" LIMIT ?\";\n params.push(limit);\n\n const rows = db.prepare(sql).all(...params) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }>;\n\n return rows.map((r) => ({\n id: r.id,\n name: r.name,\n kind: r.kind,\n filePath: r.file_path,\n lineStart: r.line_start,\n lineEnd: r.line_end,\n language: r.language,\n signature: r.signature,\n docstring: r.docstring,\n parentId: r.parent_id,\n contentHash: r.content_hash,\n }));\n}\n\n/** Find all callers of a symbol (who calls X?). */\nexport function findCallers(\n db: Database,\n symbolId: string,\n opts: { limit?: number } = {},\n): Array<{ caller: SymbolInfo; line: number }> {\n const limit = opts.limit ?? 50;\n const rows = db\n .prepare(\n `SELECT s.*, c.line as call_line\n FROM calls c\n JOIN symbols s ON s.id = c.caller_id\n WHERE c.callee_id = ? OR c.callee_name = (SELECT name FROM symbols WHERE id = ?)\n LIMIT ?`,\n )\n .all(symbolId, symbolId, limit) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n call_line: number;\n }>;\n\n return rows.map((r) => ({\n caller: {\n id: r.id,\n name: r.name,\n kind: r.kind,\n filePath: r.file_path,\n lineStart: r.line_start,\n lineEnd: r.line_end,\n language: r.language,\n signature: r.signature,\n docstring: r.docstring,\n parentId: r.parent_id,\n contentHash: r.content_hash,\n },\n line: r.call_line,\n }));\n}\n\n/** Find all callees of a symbol (X calls whom?). */\nexport function findCallees(\n db: Database,\n symbolId: string,\n opts: { limit?: number } = {},\n): Array<{ callee: SymbolInfo | null; calleeName: string; line: number }> {\n const limit = opts.limit ?? 50;\n const rows = db\n .prepare(\n `SELECT c.callee_name, c.callee_id, c.line,\n s.id as sid, s.name as sname, s.kind as skind, s.file_path as sfile,\n s.line_start as sline_start, s.line_end as sline_end, s.language as slang,\n s.signature as ssig, s.docstring as sdoc, s.parent_id as sparent, s.content_hash as shash\n FROM calls c\n LEFT JOIN symbols s ON s.id = c.callee_id\n WHERE c.caller_id = ?\n LIMIT ?`,\n )\n .all(symbolId, limit) as Array<{\n callee_name: string;\n callee_id: string | null;\n line: number;\n sid: string | null;\n sname: string | null;\n skind: string | null;\n sfile: string | null;\n sline_start: number | null;\n sline_end: number | null;\n slang: string | null;\n ssig: string | null;\n sdoc: string | null;\n sparent: string | null;\n shash: string | null;\n }>;\n\n return rows.map((r) => ({\n callee: r.sid\n ? {\n id: r.sid,\n name: r.sname ?? \"\",\n kind: r.skind ?? \"\",\n filePath: r.sfile ?? \"\",\n lineStart: r.sline_start ?? 0,\n lineEnd: r.sline_end ?? 0,\n language: r.slang ?? \"\",\n signature: r.ssig,\n docstring: r.sdoc,\n parentId: r.sparent,\n contentHash: r.shash ?? \"\",\n }\n : null,\n calleeName: r.callee_name,\n line: r.line,\n }));\n}\n\n/** Impact analysis: BFS from a symbol through the caller chain.\n * Returns all symbols that might be affected if the given symbol changes. */\nexport function impactAnalysis(\n db: Database,\n symbolId: string,\n opts: { maxDepth?: number } = {},\n): ImpactResult {\n const maxDepth = opts.maxDepth ?? 5;\n\n const rootRow = db.prepare(\"SELECT * FROM symbols WHERE id = ?\").get(symbolId) as\n | {\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }\n | undefined;\n\n if (!rootRow) {\n throw new Error(`Symbol not found: ${symbolId}`);\n }\n\n const root: SymbolInfo = {\n id: rootRow.id,\n name: rootRow.name,\n kind: rootRow.kind,\n filePath: rootRow.file_path,\n lineStart: rootRow.line_start,\n lineEnd: rootRow.line_end,\n language: rootRow.language,\n signature: rootRow.signature,\n docstring: rootRow.docstring,\n parentId: rootRow.parent_id,\n contentHash: rootRow.content_hash,\n };\n\n const affected: Array<{ symbol: SymbolInfo; depth: number; path: string[] }> = [];\n const visited = new Set<string>([symbolId]);\n\n // BFS through callers (who calls X → who calls them → ...)\n const queue: Array<{ id: string; depth: number; path: string[] }> = [\n { id: symbolId, depth: 0, path: [root.name] },\n ];\n\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) continue;\n const { id, depth, path } = item;\n if (depth >= maxDepth) continue;\n\n const callers = findCallers(db, id, { limit: 100 });\n for (const { caller } of callers) {\n if (visited.has(caller.id)) continue;\n visited.add(caller.id);\n const newPath = [...path, caller.name];\n affected.push({ symbol: caller, depth: depth + 1, path: newPath });\n queue.push({ id: caller.id, depth: depth + 1, path: newPath });\n }\n }\n\n return { rootSymbol: root, affected };\n}\n\n/** List symbols in a file or directory. */\nexport function listSymbols(\n db: Database,\n filePath: string,\n opts: { teamId?: string; kind?: string; limit?: number; repoPath?: string } = {},\n): SymbolInfo[] {\n const limit = opts.limit ?? 100;\n\n // Convert absolute path to relative if repoPath is provided\n let relPath = filePath;\n if (opts.repoPath) {\n try {\n relPath = relative(opts.repoPath, filePath).split(sep).join(\"/\");\n } catch {\n // If relative() fails, use the original path\n }\n }\n\n let sql = \"SELECT * FROM symbols WHERE file_path LIKE ?\";\n const params: unknown[] = [`${relPath}%`];\n if (opts.teamId !== undefined) {\n sql += \" AND team_id IS ?\";\n params.push(opts.teamId);\n }\n if (opts.repoPath !== undefined) {\n // Use LIKE prefix match so /path/to/src also matches repo_path /path/to\n sql += \" AND (repo_path = ? OR repo_path LIKE ?)\";\n params.push(opts.repoPath, `${opts.repoPath}%`);\n }\n if (opts.kind) {\n sql += \" AND kind = ?\";\n params.push(opts.kind);\n }\n sql += \" ORDER BY line_start LIMIT ?\";\n params.push(limit);\n\n const rows = db.prepare(sql).all(...params) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }>;\n\n return rows.map((r) => ({\n id: r.id,\n name: r.name,\n kind: r.kind,\n filePath: r.file_path,\n lineStart: r.line_start,\n lineEnd: r.line_end,\n language: r.language,\n signature: r.signature,\n docstring: r.docstring,\n parentId: r.parent_id,\n contentHash: r.content_hash,\n }));\n}\n\n/** v17: Entry point patterns for each language. */\nconst ENTRY_POINT_PATTERNS: Record<string, RegExp[]> = {\n typescript: [\n /^(main|run|start|bootstrap|init)$/,\n /^(handle|get|post|put|delete|patch)([A-Z].*)?$/,\n /^(on)([A-Z].*)?$/,\n /Handler$/,\n /Controller$/,\n /Command$/,\n /Route$/,\n /Middleware$/,\n ],\n javascript: [\n /^(main|run|start|bootstrap|init)$/,\n /^(handle|get|post|put|delete|patch)([A-Z].*)?$/,\n /^(on)([A-Z].*)?$/,\n /Handler$/,\n /Controller$/,\n /Command$/,\n /Route$/,\n /Middleware$/,\n ],\n python: [\n /^(main|run|start|setup)$/,\n /^(handle|get|post|put|delete|patch)_/,\n /^on_/,\n /_handler$/,\n /_view$/,\n /_command$/,\n /_route$/,\n ],\n go: [\n /^(main|run|serve|start)$/,\n /^(handle|get|post|put|delete)([A-Z].*)?$/,\n /^on([A-Z].*)?$/,\n /Handler$/,\n /HandlerFunc$/,\n ],\n rust: [\n /^(main|run|start)$/,\n /^(handle|get|post|put|delete)_/,\n /_handler$/,\n ],\n java: [\n /^(main|run|start)$/,\n /^(handle|get|post|put|delete)/,\n /Handler$/,\n /Controller$/,\n /Service$/,\n /Servlet$/,\n ],\n csharp: [\n /^(Main|Run|Start)$/,\n /^(Handle|Get|Post|Put|Delete)/,\n /Handler$/,\n /Controller$/,\n ],\n c: [\n /^(main|run|start)$/,\n /^(handle|get|post|put|delete)_/,\n ],\n cpp: [\n /^(main|run|start)$/,\n /^(handle|get|post|put|delete)/,\n /Handler$/,\n ],\n};\n\n/**\n * v17: Find entry points in the codebase — main functions, HTTP handlers,\n * CLI commands, event handlers. Inspired by codegraph-ai's find_entry_points.\n *\n * An entry point is a function/method that is called by external infrastructure\n * (HTTP server, CLI framework, event loop) rather than by other code.\n */\nexport function findEntryPoints(\n db: Database,\n opts: { teamId?: string; repoPath?: string; limit?: number } = {},\n): SymbolInfo[] {\n const limit = opts.limit ?? 100;\n\n let sql = `SELECT * FROM symbols WHERE kind IN ('function', 'method', 'Function', 'Method', 'function_declaration', 'method_definition')`;\n const params: unknown[] = [];\n\n if (opts.teamId !== undefined) {\n sql += \" AND team_id IS ?\";\n params.push(opts.teamId);\n }\n if (opts.repoPath !== undefined) {\n sql += \" AND (repo_path = ? OR repo_path LIKE ?)\";\n params.push(opts.repoPath, `${opts.repoPath}%`);\n }\n\n sql += \" LIMIT 10000\";\n\n const rows = db.prepare(sql).all(...params) as Array<{\n id: string;\n name: string;\n kind: string;\n file_path: string;\n line_start: number;\n line_end: number;\n language: string;\n signature: string | null;\n docstring: string | null;\n parent_id: string | null;\n content_hash: string;\n }>;\n\n const entryPoints: SymbolInfo[] = [];\n for (const row of rows) {\n const patterns = ENTRY_POINT_PATTERNS[row.language] ?? ENTRY_POINT_PATTERNS[\"typescript\"];\n const isEntryPoint = patterns.some((p) => p.test(row.name));\n if (isEntryPoint) {\n entryPoints.push({\n id: row.id,\n name: row.name,\n kind: row.kind,\n filePath: row.file_path,\n lineStart: row.line_start,\n lineEnd: row.line_end,\n language: row.language,\n signature: row.signature,\n docstring: row.docstring,\n parentId: row.parent_id,\n contentHash: row.content_hash,\n });\n }\n }\n\n return entryPoints.slice(0, limit);\n}\n"],"mappings":";;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,kBAAkB;AAC3B,SAAS,aAAa,cAAc,gBAAgB;AACpD,SAAS,SAAS,MAAM,UAAU,WAAW;AAM7C,eAAe,UAA4C;AACzD,MAAI,CAAC,OAAO;AACV,UAAM,MAAM,MAAM,OAAO,sCAAsC;AAE/D,YAAQ,IAAI,WAAW;AAAA,EACzB;AACA,SAAO;AACT;AA2BO,SAAS,eAAe,UAAiC;AAC9D,QAAM,MAAM,QAAQ,QAAQ,EAAE,YAAY;AAC1C,SAAO,cAAc,GAAG,KAAK;AAC/B;AAqDA,eAAsB,UACpB,UACA,UACqF;AACrF,QAAM,WAAW,eAAe,QAAQ;AACxC,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,OAAQ,MAAM,QAAQ;AA2B5B,MAAI,CAAC,KAAK,YAAY,QAAQ,EAAG,QAAO;AAExC,MAAI;AACJ,MAAI;AACF,aAAS,aAAa,UAAU,OAAO;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AAIA,QAAM,iBAAiB;AACvB,QAAM,iBAAiB;AACvB,MAAI,OAAO,SAAS,kBAAkB,OAAO,MAAM,IAAI,EAAE,SAAS,gBAAgB;AAChF,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,KAAK,QAAQ,QAAQ,EAAE,SAAS,CAAC;AAChD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,UAAU,SAAS,UAAU,QAAQ,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AAChE,QAAM,UAAwB,CAAC;AAC/B,QAAM,QAAoB,CAAC;AAC3B,QAAM,UAAwB,CAAC;AAG/B,QAAM,cAAc,OAAO,MAAM,IAAI;AAErC,QAAM,cAAwB,CAAC,CAAC;AAChC,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,gBAAY,KAAK,YAAY,CAAC,IAAI,YAAY,CAAC,EAAE,SAAS,CAAC;AAAA,EAC7D;AAGA,QAAM,iBAAiB,CACrB,OAcA,aACG;AACH,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAK,KAAM;AAChB,YAAM,KAAK,WAAW;AACtB,YAAM,aAAa,KAAK,MAAM,aAAa,KAAK;AAChD,YAAM,WAAW,KAAK,MAAM,WAAW,aAAa;AAEpD,YAAM,YAAY,KAAK,MAAM,cAAc,SAAY,YAAY,KAAK,KAAK,SAAS,IAAI;AAC1F,YAAM,UACJ,KAAK,MAAM,YAAY,SAAY,YAAY,KAAK,KAAK,UAAU,CAAC,IAAI,IAAI,OAAO;AACrF,YAAM,WAAW,OAAO,MAAM,WAAW,OAAO;AAChD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,MAAM,KAAK;AAAA,QACX,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,KAAK,aAAa;AAAA,QAC7B,WAAW,KAAK,cAAc;AAAA,QAC9B;AAAA,QACA,aAAa,WAAW,QAAQ,EAC7B,OAAO,KAAK,OAAO,UAAU,SAAS,EACtC,OAAO,KAAK;AAAA,MACjB,CAAC;AAKD,YAAM,gBAAgB;AAGtB,YAAM,eAAe,CAAC,WAA2B;AAE/C,cAAM,eAAe,YAAY;AAEjC,YAAI,KAAK,GACP,KAAK,YAAY,SAAS;AAC5B,eAAO,KAAK,IAAI;AACd,gBAAM,MAAO,KAAK,KAAK,KAAM;AAC7B,cAAI,YAAY,GAAG,KAAK,aAAc,MAAK;AAAA,cACtC,MAAK,MAAM;AAAA,QAClB;AACA,eAAO,KAAK;AAAA,MACd;AAGA,YAAM,kBAAkB,oBAAI,IAAY;AAGxC,YAAM,WAAW;AACjB,UAAI;AACJ,cAAQ,SAAS,SAAS,KAAK,QAAQ,OAAO,MAAM;AAClD,cAAM,aAAa,OAAO,CAAC;AAC3B,cAAM,SAAS,OAAO;AACtB,YAAI,CAAC,gBAAgB,YAAY,KAAK,IAAI,KAAK,CAAC,gBAAgB,IAAI,MAAM,GAAG;AAC3E,gBAAM,KAAK;AAAA,YACT,UAAU;AAAA,YACV;AAAA,YACA,UAAU;AAAA,YACV,MAAM,aAAa,MAAM;AAAA,YACzB,MAAM;AAAA,YACN,UAAU;AAAA,UACZ,CAAC;AACD,0BAAgB,IAAI,MAAM;AAAA,QAC5B;AAAA,MACF;AAGA,YAAM,kBAAkB;AACxB,UAAI;AACJ,cAAQ,SAAS,gBAAgB,KAAK,QAAQ,OAAO,MAAM;AACzD,cAAM,WAAW,OAAO,CAAC;AACzB,cAAM,aAAa,OAAO,CAAC;AAC3B,cAAM,aAAa,GAAG,QAAQ,IAAI,UAAU;AAC5C,cAAM,SAAS,OAAO;AACtB,YACE,CAAC,gBAAgB,YAAY,KAAK,IAAI,KACtC,CAAC,gBAAgB,UAAU,KAAK,IAAI,KACpC,CAAC,aAAa,YAAY,QAAQ,KAClC,CAAC,gBAAgB,IAAI,MAAM,GAC3B;AACA,gBAAM,KAAK;AAAA,YACT,UAAU;AAAA,YACV;AAAA,YACA,UAAU;AAAA,YACV,MAAM,aAAa,MAAM;AAAA,YACzB,MAAM;AAAA,YACN,UAAU;AAAA,UACZ,CAAC;AACD,0BAAgB,IAAI,MAAM;AAAA,QAC5B;AAAA,MACF;AAGA,YAAM,kBAAkB;AACxB,UAAI;AACJ,cAAQ,QAAQ,gBAAgB,KAAK,QAAQ,OAAO,MAAM;AACxD,cAAM,aAAa,MAAM,CAAC;AAC1B,cAAM,SAAS,MAAM;AACrB,YACE,CAAC,gBAAgB,YAAY,KAAK,IAAI,KACtC,CAAC,aAAa,YAAY,QAAQ,KAClC,CAAC,gBAAgB,IAAI,MAAM,GAC3B;AACA,gBAAM,KAAK;AAAA,YACT,UAAU;AAAA,YACV;AAAA,YACA,UAAU;AAAA,YACV,MAAM,aAAa,MAAM;AAAA,YACzB,MAAM;AAAA,YACN,UAAU;AAAA,UACZ,CAAC;AACD,0BAAgB,IAAI,MAAM;AAAA,QAC5B;AAAA,MACF;AAGA,UAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;AAC7C,uBAAe,KAAK,UAAU,EAAE;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,OAAO,aAAa,CAAC,GAAG,IAAI;AAG3C,aAAW,OAAO,OAAO,WAAW,CAAC,GAAG;AACtC,YAAQ,KAAK;AAAA,MACX,UAAU;AAAA,MACV,YAAY,IAAI,OAAO,KAAK,IAAI,KAAK,IAAI,UAAU;AAAA,MACnD,YAAY,IAAI,UAAU;AAAA,MAC1B,OAAO,IAAI,MAAM,aAAa,KAAK;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,SAAS,OAAO,QAAQ;AACnC;AAGA,SAAS,gBAAgB,MAAc,YAA6B;AAClE,MAAI,SAAS,WAAY,QAAO;AAChC,QAAM,WAAW,oBAAI,IAAI;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,SAAO,SAAS,IAAI,IAAI;AAC1B;AAiTA,SAAS,aAAa,YAAoB,UAA2B;AACnE,QAAM,WAAW,gBAAgB,QAAQ;AACzC,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,SAAS,IAAI,UAAU,EAAG,QAAO;AAErC,QAAM,SAAS,WAAW,QAAQ,GAAG;AACrC,MAAI,SAAS,GAAG;AACd,UAAM,SAAS,WAAW,MAAM,GAAG,MAAM;AACzC,QAAI,SAAS,IAAI,MAAM,EAAG,QAAO;AAAA,EACnC;AACA,SAAO;AACT;AAGA,eAAsB,UACpB,IACA,UACA,UACA,QACsB;AACtB,QAAM,WAAW,eAAe,QAAQ;AACxC,MAAI,CAAC,UAAU;AACb,WAAO,EAAE,MAAM,UAAU,UAAU,WAAW,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,KAAK;AAAA,EAChG;AAEA,QAAM,SAAS,MAAM,UAAU,UAAU,QAAQ;AACjD,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,MAAM,UAAU,UAAU,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,KAAK;AAAA,EACrF;AAEA,QAAM,UAAU,SAAS,UAAU,QAAQ,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AAChE,QAAM,MAAM,KAAK,IAAI;AAGrB,QAAM,cAAc,GACjB,QAAQ,6DAA6D,EACrE,IAAI,SAAS,MAAM;AACtB,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,eAAe,YAAY,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AACxD,OAAG,QAAQ,yCAAyC,YAAY,GAAG,EAAE;AAAA,MACnE,GAAG,YAAY,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,IAChC;AACA,OAAG,QAAQ,0DAA0D,EAAE,IAAI,SAAS,MAAM;AAAA,EAC5F;AACA,KAAG,QAAQ,0DAA0D,EAAE,IAAI,SAAS,MAAM;AAG1F,QAAM,aAAa,GAAG;AAAA,IACpB;AAAA;AAAA,EAEF;AACA,QAAM,aAAa,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACA,aAAW,KAAK,OAAO,SAAS;AAC9B,eAAW;AAAA,MACT,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF;AAAA,MACA;AAAA,MACA,EAAE;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,GAAG;AAAA,IAClB;AAAA,EACF;AACA,aAAW,KAAK,OAAO,OAAO;AAC5B,aAAS;AAAA,MACP,EAAE;AAAA,MACF,EAAE;AAAA,MACF;AAAA;AAAA,MACA,EAAE;AAAA,MACF,EAAE;AAAA,MACD,EAA4B,YAAY;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,aAAa,GAAG;AAAA,IACpB;AAAA,EACF;AACA,aAAW,OAAO,OAAO,SAAS;AAChC,eAAW;AAAA,MACT,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,SAAS,OAAO,QAAQ;AAAA,IACxB,OAAO,OAAO,MAAM;AAAA,IACpB,SAAS,OAAO,QAAQ;AAAA,IACxB,SAAS;AAAA,EACX;AACF;AAGA,eAAsB,eACpB,IACA,SACA,UACA,QACA,WAAW,KACa;AACxB,QAAM,UAAyB,CAAC;AAChC,QAAM,QAAkB,CAAC;AAEzB,QAAM,OAAO,CAAC,QAAgB;AAC5B,QAAI,MAAM,UAAU,SAAU;AAC9B,QAAI;AACJ,QAAI;AACF,gBAAU,YAAY,GAAG;AAAA,IAC3B,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,UAAI,MAAM,UAAU,SAAU;AAC9B,YAAM,WAAW,KAAK,KAAK,KAAK;AAChC,UAAI;AACJ,UAAI;AACF,eAAO,SAAS,QAAQ;AAAA,MAC1B,QAAQ;AACN;AAAA,MACF;AACA,UAAI,KAAK,YAAY,GAAG;AAEtB,YACE,UAAU,kBACV,UAAU,UACV,UAAU,UACV,UAAU,WACV,UAAU,YACV,UAAU,iBACV,UAAU,WACV,UAAU,YACV,UAAU,WACV,MAAM,WAAW,GAAG;AAEpB;AACF,aAAK,QAAQ;AAAA,MACf,WAAW,KAAK,OAAO,GAAG;AACxB,YAAI,eAAe,QAAQ,GAAG;AAC5B,gBAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,OAAK,OAAO;AAEZ,QAAM,mBAAmB,oBAAI,IAAY;AACzC,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,MAAM,UAAU,IAAI,MAAM,UAAU,MAAM;AACzD,YAAQ,KAAK,MAAM;AAAA,EACrB;AAKA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,eAAe,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACxE,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK,KAAK;AACjD,YAAM,QAAQ,aAAa,MAAM,GAAG,IAAI,GAAG;AAC3C,YAAM,eAAe,MAAM,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAClD,YAAM,OAAO,GACV,QAAQ,8CAA8C,YAAY,GAAG,EACrE,IAAI,GAAG,KAAK;AACf,iBAAW,KAAK,MAAM;AACpB,yBAAiB,IAAI,EAAE,EAAE;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAIA,QAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,wBAAe;AACxD,QAAM,QAAQ,gBAAgB,IAAI,gBAAgB;AAClD,MAAI,MAAM,QAAQ,GAAG;AACnB,YAAQ;AAAA,MACN,wCAAwC,MAAM,QAAQ,IAAI,MAAM,KAAK,YAClE,OAAO,KAAK,MAAM,UAAU,EAAE,SAAS,IAAI,KAAK,KAAK,UAAU,MAAM,UAAU,CAAC,MAAM;AAAA,IAC3F;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,cACd,IACA,OACA,OAMI,CAAC,GACS;AACd,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,MAAM;AACV,QAAM,SAAoB,CAAC,OAAO;AAClC,MAAI,KAAK,WAAW,QAAW;AAC7B,WAAO;AACP,WAAO,KAAK,KAAK,MAAM;AAAA,EACzB;AACA,MAAI,KAAK,aAAa,QAAW;AAE/B,WAAO;AACP,WAAO,KAAK,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG;AAAA,EAChD;AACA,MAAI,KAAK,MAAM;AACb,WAAO;AACP,WAAO,KAAK,KAAK,IAAI;AAAA,EACvB;AACA,MAAI,KAAK,UAAU;AACjB,WAAO;AACP,WAAO,KAAK,KAAK,QAAQ;AAAA,EAC3B;AACA,SAAO;AACP,SAAO,KAAK,KAAK;AAEjB,QAAM,OAAO,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAc1C,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,MAAM,EAAE;AAAA,IACR,UAAU,EAAE;AAAA,IACZ,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,UAAU,EAAE;AAAA,IACZ,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,UAAU,EAAE;AAAA,IACZ,aAAa,EAAE;AAAA,EACjB,EAAE;AACJ;AAGO,SAAS,YACd,IACA,UACA,OAA2B,CAAC,GACiB;AAC7C,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,OAAO,GACV;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,IAAI,UAAU,UAAU,KAAK;AAehC,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,QAAQ;AAAA,MACN,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,MACZ,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX,UAAU,EAAE;AAAA,MACZ,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,UAAU,EAAE;AAAA,MACZ,aAAa,EAAE;AAAA,IACjB;AAAA,IACA,MAAM,EAAE;AAAA,EACV,EAAE;AACJ;AAGO,SAAS,YACd,IACA,UACA,OAA2B,CAAC,GAC4C;AACxE,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,OAAO,GACV;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQF,EACC,IAAI,UAAU,KAAK;AAiBtB,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,QAAQ,EAAE,MACN;AAAA,MACE,IAAI,EAAE;AAAA,MACN,MAAM,EAAE,SAAS;AAAA,MACjB,MAAM,EAAE,SAAS;AAAA,MACjB,UAAU,EAAE,SAAS;AAAA,MACrB,WAAW,EAAE,eAAe;AAAA,MAC5B,SAAS,EAAE,aAAa;AAAA,MACxB,UAAU,EAAE,SAAS;AAAA,MACrB,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,UAAU,EAAE;AAAA,MACZ,aAAa,EAAE,SAAS;AAAA,IAC1B,IACA;AAAA,IACJ,YAAY,EAAE;AAAA,IACd,MAAM,EAAE;AAAA,EACV,EAAE;AACJ;AAIO,SAAS,eACd,IACA,UACA,OAA8B,CAAC,GACjB;AACd,QAAM,WAAW,KAAK,YAAY;AAElC,QAAM,UAAU,GAAG,QAAQ,oCAAoC,EAAE,IAAI,QAAQ;AAgB7E,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qBAAqB,QAAQ,EAAE;AAAA,EACjD;AAEA,QAAM,OAAmB;AAAA,IACvB,IAAI,QAAQ;AAAA,IACZ,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,IACjB,UAAU,QAAQ;AAAA,IAClB,WAAW,QAAQ;AAAA,IACnB,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,EACvB;AAEA,QAAM,WAAyE,CAAC;AAChF,QAAM,UAAU,oBAAI,IAAY,CAAC,QAAQ,CAAC;AAG1C,QAAM,QAA8D;AAAA,IAClE,EAAE,IAAI,UAAU,OAAO,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE;AAAA,EAC9C;AAEA,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,OAAO,MAAM,MAAM;AACzB,QAAI,CAAC,KAAM;AACX,UAAM,EAAE,IAAI,OAAO,KAAK,IAAI;AAC5B,QAAI,SAAS,SAAU;AAEvB,UAAM,UAAU,YAAY,IAAI,IAAI,EAAE,OAAO,IAAI,CAAC;AAClD,eAAW,EAAE,OAAO,KAAK,SAAS;AAChC,UAAI,QAAQ,IAAI,OAAO,EAAE,EAAG;AAC5B,cAAQ,IAAI,OAAO,EAAE;AACrB,YAAM,UAAU,CAAC,GAAG,MAAM,OAAO,IAAI;AACrC,eAAS,KAAK,EAAE,QAAQ,QAAQ,OAAO,QAAQ,GAAG,MAAM,QAAQ,CAAC;AACjE,YAAM,KAAK,EAAE,IAAI,OAAO,IAAI,OAAO,QAAQ,GAAG,MAAM,QAAQ,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,SAAO,EAAE,YAAY,MAAM,SAAS;AACtC;AAGO,SAAS,YACd,IACA,UACA,OAA8E,CAAC,GACjE;AACd,QAAM,QAAQ,KAAK,SAAS;AAG5B,MAAI,UAAU;AACd,MAAI,KAAK,UAAU;AACjB,QAAI;AACF,gBAAU,SAAS,KAAK,UAAU,QAAQ,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AAAA,IACjE,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,MAAM;AACV,QAAM,SAAoB,CAAC,GAAG,OAAO,GAAG;AACxC,MAAI,KAAK,WAAW,QAAW;AAC7B,WAAO;AACP,WAAO,KAAK,KAAK,MAAM;AAAA,EACzB;AACA,MAAI,KAAK,aAAa,QAAW;AAE/B,WAAO;AACP,WAAO,KAAK,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG;AAAA,EAChD;AACA,MAAI,KAAK,MAAM;AACb,WAAO;AACP,WAAO,KAAK,KAAK,IAAI;AAAA,EACvB;AACA,SAAO;AACP,SAAO,KAAK,KAAK;AAEjB,QAAM,OAAO,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAc1C,SAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,MAAM,EAAE;AAAA,IACR,UAAU,EAAE;AAAA,IACZ,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,UAAU,EAAE;AAAA,IACZ,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,UAAU,EAAE;AAAA,IACZ,aAAa,EAAE;AAAA,EACjB,EAAE;AACJ;AA6EO,SAAS,gBACd,IACA,OAA+D,CAAC,GAClD;AACd,QAAM,QAAQ,KAAK,SAAS;AAE5B,MAAI,MAAM;AACV,QAAM,SAAoB,CAAC;AAE3B,MAAI,KAAK,WAAW,QAAW;AAC7B,WAAO;AACP,WAAO,KAAK,KAAK,MAAM;AAAA,EACzB;AACA,MAAI,KAAK,aAAa,QAAW;AAC/B,WAAO;AACP,WAAO,KAAK,KAAK,UAAU,GAAG,KAAK,QAAQ,GAAG;AAAA,EAChD;AAEA,SAAO;AAEP,QAAM,OAAO,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAc1C,QAAM,cAA4B,CAAC;AACnC,aAAW,OAAO,MAAM;AACtB,UAAM,WAAW,qBAAqB,IAAI,QAAQ,KAAK,qBAAqB,YAAY;AACxF,UAAM,eAAe,SAAS,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;AAC1D,QAAI,cAAc;AAChB,kBAAY,KAAK;AAAA,QACf,IAAI,IAAI;AAAA,QACR,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,UAAU,IAAI;AAAA,QACd,WAAW,IAAI;AAAA,QACf,SAAS,IAAI;AAAA,QACb,UAAU,IAAI;AAAA,QACd,WAAW,IAAI;AAAA,QACf,WAAW,IAAI;AAAA,QACf,UAAU,IAAI;AAAA,QACd,aAAa,IAAI;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,YAAY,MAAM,GAAG,KAAK;AACnC;AAl0CA,IAaI,OAWE,eAqBO,qBAkWP,iBAizBA;AAhsCN;AAAA;AAAA;AAUA;AAGA,IAAI,QAAiB;AAWrB,IAAM,gBAAwC;AAAA,MAC5C,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAGO,IAAM,sBAAsB,IAAI,IAAI,OAAO,OAAO,aAAa,CAAC;AAkWvE,IAAM,kBAA+C;AAAA,MACnD,IAAI,oBAAI,IAAI;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,YAAY,oBAAI,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,YAAY,oBAAI,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,QAAQ,oBAAI,IAAI;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,MAAM,oBAAI,IAAI;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAsgBA,IAAM,uBAAiD;AAAA,MACrD,YAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,YAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,IAAI;AAAA,QACF;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,GAAG;AAAA,QACD;AAAA,QACA;AAAA,MACF;AAAA,MACA,KAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA;","names":[]}
@@ -3,6 +3,7 @@ import {
3
3
  detectLanguage,
4
4
  findCallees,
5
5
  findCallers,
6
+ findEntryPoints,
6
7
  impactAnalysis,
7
8
  indexDirectory,
8
9
  indexFile,
@@ -10,7 +11,7 @@ import {
10
11
  listSymbols,
11
12
  parseFile,
12
13
  searchSymbols
13
- } from "./chunk-YU7WBJU5.js";
14
+ } from "./chunk-MPEN2X43.js";
14
15
  import "./chunk-XJGDYQ34.js";
15
16
  import "./chunk-ONQHLUGM.js";
16
17
  init_engine();
@@ -19,6 +20,7 @@ export {
19
20
  detectLanguage,
20
21
  findCallees,
21
22
  findCallers,
23
+ findEntryPoints,
22
24
  impactAnalysis,
23
25
  indexDirectory,
24
26
  indexFile,
@@ -26,4 +28,4 @@ export {
26
28
  parseFile,
27
29
  searchSymbols
28
30
  };
29
- //# sourceMappingURL=engine-S47HHGAO.js.map
31
+ //# sourceMappingURL=engine-LAY25SCA.js.map