remem-mcp 0.5.17 → 0.6.2

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.
package/README.md CHANGED
@@ -2,14 +2,28 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/remem-mcp.svg)](https://www.npmjs.com/package/remem-mcp)
4
4
  [![GitHub stars](https://img.shields.io/github/stars/tinhien11/remem-mcp.svg)](https://github.com/tinhien11/remem-mcp)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Benchmark](https://img.shields.io/badge/AMB-100%2F100%2F100-brightgreen)](https://github.com/tinhien11/remem-mcp)
5
7
 
6
8
  > Your coding agent stops repeating the same mistakes.
7
9
 
8
10
  Local memory that survives context compaction — learns from every error, injects fixes before the next attempt, and syncs to your git repo so your whole team shares it.
9
11
 
12
+ **One command setup. No API key. No cloud. No database server. Just a SQLite file.**
13
+
10
14
  <video src="https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/demo-learning-loop.mp4" controls muted width="100%"></video>
11
15
 
12
- ![Demo (GIF)](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/demo-learning-loop.gif)
16
+ ![Demo: Error learning loop](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/demo-learning-loop.gif)
17
+
18
+ ### See it in action
19
+
20
+ | Error learning loop | CodeGraph search | Web viewer |
21
+ |---|---|---|
22
+ | ![Error learning](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/demo-learning-loop.gif) | ![CodeGraph](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/demo-codegraph.gif) | ![Viewer](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/viewer-demo.gif) |
23
+
24
+ | Viewer: overview | CodeGraph: callers | CodeGraph: search |
25
+ |---|---|---|
26
+ | ![Overview](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/viewer-overview.png) | ![Callers](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/viewer-codegraph-callers.png) | ![Search](https://raw.githubusercontent.com/tinhien11/remem-mcp/main/docs/screenshots/viewer-codegraph-search.png) |
13
27
 
14
28
  ---
15
29
 
@@ -27,7 +41,7 @@ npx remem-mcp demo-codegraph # Live CodeGraph demo on facebook/react
27
41
  npx remem-mcp status # One dashboard: everything at a glance
28
42
  ```
29
43
 
30
- The demo creates a real TypeScript project, runs real `npm run build`, captures real TS2307 errors, and shows the full learning loop — capture → inject → fix → upvote → cross-project inheritance. No hardcoded strings.
44
+ The demo creates a real TypeScript project, runs real `npm run build`, captures real TS2307 errors, and shows the full learning loop — capture → inject → fix → zero retries. No hardcoded strings.
31
45
 
32
46
  ---
33
47
 
@@ -60,7 +74,7 @@ npx remem-mcp install-hooks
60
74
  <details>
61
75
  <summary>Cursor</summary>
62
76
 
63
- [![Install in Cursor](https://img.shields.io/badge/Cursor-Install-blue)](cursor://anysphere.cursor-deeplink/mcp/install?name=remem-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInRkYWktbWVtb3J5LW1jcCJdfQ==)
77
+ [![Install in Cursor](https://img.shields.io/badge/Cursor-Install-blue)](cursor://anysphere.cursor-deeplink/mcp/install?name=remem-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInJlbWVtLW1jcCJdfQ==)
64
78
 
65
79
  Or add to `~/.cursor/mcp.json`:
66
80
  ```json
@@ -91,7 +105,7 @@ command = "npx"
91
105
  args = ["-y", "remem-mcp"]
92
106
 
93
107
  [mcp_servers.remem-mcp.env]
94
- TDAI_GLOBAL_SESSION_KEY = "global"
108
+ REMEM_GLOBAL_SESSION_KEY = "global"
95
109
  ```
96
110
 
97
111
  Then run `npx remem-mcp install-hooks`.
@@ -124,30 +138,32 @@ All run via lifecycle hooks. The agent doesn't need to call any tool.
124
138
 
125
139
  When the automatic loops aren't enough, use these for deeper code navigation.
126
140
 
127
- ```bash
128
- # 1. Index your codebase (one-time, rerun after major changes)
129
- npx remem-mcp index --path src --repo .
141
+ **CodeGraph** — symbol search, callers/callees, impact analysis. **Auto-indexes on first use** — just call `codegraph_search` and it indexes `src/` automatically. No manual `codegraph_index` needed.
130
142
 
131
- # 2. Search symbols (auto-scoped to current directory)
143
+ ```bash
144
+ # Search symbols (auto-indexes src/ on first call)
132
145
  npx remem-mcp search-code --query "parseTar"
133
146
  # → parseTar at src/parse.ts:22
134
147
 
135
- # 3. List symbols in a file
148
+ # List symbols in a file
136
149
  npx remem-mcp list-code src/reporters/fancy.ts
137
150
  # → Class L49-135 FancyReporter
138
151
  # → Method L86-134 formatLogObj
139
152
 
140
- # 4. Trace callers / callees / impact (use symbol ID from step 2)
153
+ # Trace callers / callees / impact (use symbol ID from search)
141
154
  npx remem-mcp callers 01KZXPPHF93TS4HV8FWCSSK36A
142
155
  npx remem-mcp impact 01KZXPPHF93TS4HV8FWCSSK36A
143
156
 
157
+ # Manual re-index (only needed after major changes)
158
+ npx remem-mcp index --path src --repo .
159
+
144
160
  # Wiki + viewer
145
161
  npx remem-mcp wiki ingest --path docs # Index markdown docs + ADRs
146
162
  npx remem-mcp wiki outdated # Find outdated wiki pages
147
163
  npx remem-mcp viewer # Web UI at localhost:7331
148
164
  ```
149
165
 
150
- - **CodeGraph** — symbol search, callers/callees, impact analysis. Auto-scoped to your project — no cross-project contamination.
166
+ - **CodeGraph** — symbol search, callers/callees, impact analysis. Auto-indexes on first `codegraph_search` call. Auto-scoped to your project.
151
167
  - **Wiki** — markdown docs, ADRs, outdated detection.
152
168
  - **Search** — hybrid BM25 + sqlite-vec vector search with RRF fusion. `explain_recall` shows scores.
153
169
 
@@ -175,13 +191,14 @@ All settings have defaults. Config file is optional: `~/.config/remem-mcp/config
175
191
 
176
192
  | Setting | Env var | Default |
177
193
  |---|---|---|
178
- | DB path | `TDAI_DB_PATH` | `~/.local/share/remem-mcp/memory.db` |
179
- | Cross-project memory | `TDAI_GLOBAL_SESSION_KEY` | _(unset)_ |
180
- | Cross-project errors | `TDAI_GLOBAL_ERRORS` | _(unset, set to `1`)_ |
181
- | Suppress hook feedback | `TDAI_QUIET` | _(unset, set to `1`)_ |
182
- | Retro window (days) | `TDAI_RETRO_DAYS` | `7` |
183
- | Core-only mode (disable advanced tools) | `TDAI_CORE_ONLY` | _(unset, set to `1`)_ |
184
- | LLM API key (pipeline) | `TDAI_LLM_API_KEY` | _(unset)_ |
194
+ | DB path | `REMEM_DB_PATH` | `~/.local/share/remem-mcp/memory.db` |
195
+ | Cross-project memory | `REMEM_GLOBAL_SESSION_KEY` | _(unset)_ |
196
+ | Cross-project errors | `REMEM_GLOBAL_ERRORS` | _(unset, set to `1`)_ |
197
+ | Auto-global classification | `auto_global=true` on capture | _(off)_ |
198
+ | Suppress hook feedback | `REMEM_QUIET` | _(unset, set to `1`)_ |
199
+ | Retro window (days) | `REMEM_RETRO_DAYS` | `7` |
200
+ | Core-only mode (disable advanced tools) | `REMEM_CORE_ONLY` | _(unset, set to `1`)_ |
201
+ | LLM API key (pipeline) | `REMEM_LLM_API_KEY` | _(unset)_ |
185
202
 
186
203
  **Team sharing** — `npx remem-mcp sync-export` writes `.remem-mcp/memory-export.jsonl`. Commit it to git. Team members get the same memory on `git pull` (auto-imports on startup).
187
204
 
@@ -201,21 +218,23 @@ const results = await memory.recall("storage decision");
201
218
 
202
219
  ## Benchmark
203
220
 
204
- remem-mcp is evaluated against the same benchmarks as TencentDB Agent Memory, plus the Agent Memory Benchmark (AMB) suite.
205
-
206
- | Benchmark | remem-mcp | TencentDB Agent Memory | Without memory |
207
- |---|---|---|---|
208
- | **AMB Layer 1** (basic recall) | **100** | | |
209
- | **AMB Layer 2** (multi-session) | **100** | — | — |
210
- | **AMB Layer 3** (scale + distractors) | **100** | — | — |
211
- | **LoCoMo** (long conversation QA) | **85** | — | — |
212
- | **PersonaMem** (personalization) | **80** | 76 | 48 |
213
- | **LongMemEval** (long-term memory, ICLR 2025) | **92** | — | — |
214
-
215
- - **PersonaMem** — [bowen-upenn/PersonaMem](https://github.com/bowen-upenn/PersonaMem) (588 questions, 20 personas, multiple-choice QA). TencentDB reports 76% with memory enabled, 48% without. remem-mcp scores **80%** using a search-recall proxy (no LLM API key needed).
216
- - **LoCoMo** — long conversation multi-hop QA (19 sessions, 400+ turns). remem-mcp scores **85%** with keyword-heuristic scoring.
217
- - **AMB** — Agent Memory Benchmark (L1: 56 recall tests, L2: 5 multi-session scenarios, L3: 1K+ memories with distractors). remem-mcp scores **100/100/100**.
218
- - **LongMemEval** — [xiaowu0162/LongMemEval](https://github.com/xiaowu0162/LongMemEval) (ICLR 2025, 500 questions, 5 memory abilities: temporal reasoning, multi-session, knowledge update, single-session recall, abstention). remem-mcp scores **92%** on the oracle variant.
221
+ remem-mcp is evaluated against the same benchmarks as TencentDB Agent Memory and Mem0, plus the Agent Memory Benchmark (AMB) suite.
222
+
223
+ > **Note:** LoCoMo, PersonaMem, and LongMemEval scores use custom adapters with keyword-heuristic scoring (not official LLM-as-judge runners). AMB uses the official CLI. See [scripts/bench-all.sh](scripts/bench-all.sh) for methodology.
224
+
225
+ | Benchmark | remem-mcp | TencentDB Agent Memory | Mem0 | Without memory |
226
+ |---|---|---|---|---|
227
+ | **AMB Layer 1** (basic recall) | **100** | — | — | — |
228
+ | **AMB Layer 2** (multi-session) | **100** | — | — | — |
229
+ | **AMB Layer 3** (scale + distractors) | **100** | | | — |
230
+ | **LoCoMo** (long conversation QA) | **95** | — | 92.5 | — |
231
+ | **PersonaMem** (personalization) | **100** | 76 | — | 48 |
232
+ | **LongMemEval** (long-term memory, ICLR 2025) | **96** | | 94.4 | |
233
+
234
+ - **PersonaMem** — [bowen-upenn/PersonaMem](https://github.com/bowen-upenn/PersonaMem) (588 questions, 20 personas, multiple-choice QA). TencentDB reports 76% with memory enabled, 48% without. remem-mcp scores **100%** using a search-recall proxy (no LLM API key needed).
235
+ - **LoCoMo** — long conversation multi-hop QA (19 sessions, 400+ turns). Mem0 reports 92.5%. remem-mcp scores **95%** with keyword + semantic-similarity scoring.
236
+ - **AMB** — Agent Memory Benchmark (L1: 56 recall tests, L2: 5 multi-session scenarios, L3: 1K+ memories with distractors). remem-mcp scores **100/100/100** using the official AMB CLI.
237
+ - **LongMemEval** — [xiaowu0162/LongMemEval](https://github.com/xiaowu0162/LongMemEval) (ICLR 2025, 500 questions, 5 memory abilities: temporal reasoning, multi-session, knowledge update, single-session recall, abstention). Mem0 reports 94.4%. remem-mcp scores **96%** on the oracle variant.
219
238
 
220
239
  Run the benchmarks:
221
240
 
@@ -2,8 +2,13 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __esm = (fn, res) => function __init() {
6
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ var __esm = (fn, res, err) => function __init() {
6
+ if (err) throw err[0];
7
+ try {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ } catch (e) {
10
+ throw err = [e], e;
11
+ }
7
12
  };
8
13
  var __export = (target, all) => {
9
14
  for (var name in all)
@@ -289,8 +294,8 @@ function searchSymbols(db, query, opts = {}) {
289
294
  params.push(opts.teamId);
290
295
  }
291
296
  if (opts.repoPath !== void 0) {
292
- sql += " AND repo_path = ?";
293
- params.push(opts.repoPath);
297
+ sql += " AND (repo_path = ? OR repo_path LIKE ?)";
298
+ params.push(opts.repoPath, `${opts.repoPath}%`);
294
299
  }
295
300
  if (opts.kind) {
296
301
  sql += " AND kind = ?";
@@ -429,8 +434,8 @@ function listSymbols(db, filePath, opts = {}) {
429
434
  params.push(opts.teamId);
430
435
  }
431
436
  if (opts.repoPath !== void 0) {
432
- sql += " AND repo_path = ?";
433
- params.push(opts.repoPath);
437
+ sql += " AND (repo_path = ? OR repo_path LIKE ?)";
438
+ params.push(opts.repoPath, `${opts.repoPath}%`);
434
439
  }
435
440
  if (opts.kind) {
436
441
  sql += " AND kind = ?";
@@ -472,4 +477,4 @@ export {
472
477
  impactAnalysis,
473
478
  listSymbols
474
479
  };
475
- //# sourceMappingURL=chunk-RITPZHIB.js.map
480
+ //# sourceMappingURL=chunk-SHLLMMMU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/codegraph/engine.ts","../src/utils/ulid.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\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}\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 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 // 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 const _bodyText = source.slice(\n item.span?.startLine !== undefined\n ? source.split(\"\\n\").slice(0, item.span.startLine).join(\"\\n\").length + 1\n : 0,\n item.span?.endLine !== undefined\n ? source\n .split(\"\\n\")\n .slice(0, item.span.endLine + 1)\n .join(\"\\n\").length\n : source.length,\n );\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\n // We do a simple regex-based call extraction for now\n const callRegex = /\\b([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(/g;\n let match: RegExpExecArray | null;\n const bodyStartLine = lineStart;\n const bodyLines = source.split(\"\\n\").slice(lineStart - 1, lineEnd);\n for (let i = 0; i < bodyLines.length; i++) {\n const line = bodyLines[i];\n callRegex.lastIndex = 0;\n match = callRegex.exec(line);\n while (match !== null) {\n const calleeName = match[1];\n // Skip language keywords and the symbol itself\n const isKeyword =\n calleeName === item.name ||\n calleeName === \"if\" ||\n calleeName === \"for\" ||\n calleeName === \"while\" ||\n calleeName === \"switch\" ||\n calleeName === \"return\" ||\n calleeName === \"function\" ||\n calleeName === \"def\" ||\n calleeName === \"func\" ||\n calleeName === \"fn\" ||\n calleeName === \"print\" ||\n calleeName === \"console\";\n if (!isKeyword) {\n calls.push({\n callerId: id,\n calleeName,\n calleeId: null,\n line: bodyStartLine + i,\n kind: \"call\",\n });\n }\n match = callRegex.exec(line);\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/** 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, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\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 now,\n now,\n );\n }\n\n // Resolve callee IDs and insert calls\n const callStmt = db.prepare(\n `INSERT INTO calls (caller_id, callee_name, callee_id, line, kind, team_id) VALUES (?, ?, ?, ?, ?, ?)`,\n );\n for (const c of parsed.calls) {\n // Try to resolve callee by name within the same repo\n const callee = db\n .prepare(\"SELECT id FROM symbols WHERE name = ? AND team_id IS ? LIMIT 1\")\n .get(c.calleeName, teamId) as { id: string } | undefined;\n callStmt.run(c.callerId, c.calleeName, callee?.id ?? null, c.line, c.kind, teamId);\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 for (const file of files) {\n const result = await indexFile(db, file, repoPath, teamId);\n results.push(result);\n }\n\n // Re-resolve callee IDs now that all symbols are indexed\n const unresolved = db\n .prepare(\"SELECT id, callee_name FROM calls WHERE callee_id IS NULL\")\n .all() as { id: number; callee_name: string }[];\n if (unresolved.length > 0) {\n const updateStmt = db.prepare(\"UPDATE calls SET callee_id = ? WHERE id = ?\");\n for (const u of unresolved) {\n const callee = db\n .prepare(\"SELECT id FROM symbols WHERE name = ? AND team_id IS ? LIMIT 1\")\n .get(u.callee_name, teamId) as { id: string } | undefined;\n if (callee) {\n updateStmt.run(callee.id, u.id);\n }\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","import { ulid } from \"ulid\";\n\n/**\n * Generate a sortable unique ID.\n * Uses ULID (Universally Unique Lexicographically Sortable Identifier).\n */\nexport function generateId(): string {\n return ulid();\n}\n"],"mappings":"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","names":["path"]}