remem-mcp 0.6.2 → 0.6.4
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 +85 -125
- package/dist/chunk-FA324IBN.js +903 -0
- package/dist/chunk-FA324IBN.js.map +1 -0
- package/dist/{chunk-ULMM6LKD.js → chunk-X4ECGLYH.js} +60 -16
- package/dist/chunk-X4ECGLYH.js.map +1 -0
- package/dist/chunk-XK4Y53J2.js +54 -0
- package/dist/chunk-XK4Y53J2.js.map +1 -0
- package/dist/{engine-EIOBO7ZE.js → engine-LVKGZHBY.js} +5 -2
- package/dist/index.js +1234 -185
- package/dist/index.js.map +1 -1
- package/dist/resolver-AVK7XKX7.js +300 -0
- package/dist/resolver-AVK7XKX7.js.map +1 -0
- package/dist/sdk.js +3 -2
- package/dist/storage/schema.sql +6 -2
- package/package.json +1 -2
- package/scripts/dogfood-claude-cli.sh +156 -0
- package/scripts/dogfood-codex.sh +145 -0
- package/skills/remem-mcp/SKILL.md +45 -91
- package/dist/chunk-SHLLMMMU.js +0 -480
- package/dist/chunk-SHLLMMMU.js.map +0 -1
- package/dist/chunk-ULMM6LKD.js.map +0 -1
- /package/dist/{engine-EIOBO7ZE.js.map → engine-LVKGZHBY.js.map} +0 -0
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__esm,
|
|
3
|
+
__export,
|
|
4
|
+
init_esm_shims
|
|
5
|
+
} from "./chunk-XK4Y53J2.js";
|
|
6
|
+
|
|
7
|
+
// src/utils/ulid.ts
|
|
8
|
+
import { ulid } from "ulid";
|
|
9
|
+
function generateId() {
|
|
10
|
+
return ulid();
|
|
11
|
+
}
|
|
12
|
+
var init_ulid = __esm({
|
|
13
|
+
"src/utils/ulid.ts"() {
|
|
14
|
+
"use strict";
|
|
15
|
+
init_esm_shims();
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// src/codegraph/engine.ts
|
|
20
|
+
var engine_exports = {};
|
|
21
|
+
__export(engine_exports, {
|
|
22
|
+
SUPPORTED_LANGUAGES: () => SUPPORTED_LANGUAGES,
|
|
23
|
+
detectLanguage: () => detectLanguage,
|
|
24
|
+
findCallees: () => findCallees,
|
|
25
|
+
findCallers: () => findCallers,
|
|
26
|
+
impactAnalysis: () => impactAnalysis,
|
|
27
|
+
indexDirectory: () => indexDirectory,
|
|
28
|
+
indexFile: () => indexFile,
|
|
29
|
+
listSymbols: () => listSymbols,
|
|
30
|
+
parseFile: () => parseFile,
|
|
31
|
+
searchSymbols: () => searchSymbols
|
|
32
|
+
});
|
|
33
|
+
import { createHash } from "crypto";
|
|
34
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
35
|
+
import { extname, join, relative, sep } from "path";
|
|
36
|
+
async function getPack() {
|
|
37
|
+
if (!_pack) {
|
|
38
|
+
const mod = await import("@kreuzberg/tree-sitter-language-pack");
|
|
39
|
+
_pack = mod.default ?? mod;
|
|
40
|
+
}
|
|
41
|
+
return _pack;
|
|
42
|
+
}
|
|
43
|
+
function detectLanguage(filePath) {
|
|
44
|
+
const ext = extname(filePath).toLowerCase();
|
|
45
|
+
return EXTENSION_MAP[ext] ?? null;
|
|
46
|
+
}
|
|
47
|
+
async function parseFile(filePath, repoPath) {
|
|
48
|
+
const language = detectLanguage(filePath);
|
|
49
|
+
if (!language) return null;
|
|
50
|
+
const pack = await getPack();
|
|
51
|
+
if (!pack.hasLanguage(language)) return null;
|
|
52
|
+
let source;
|
|
53
|
+
try {
|
|
54
|
+
source = readFileSync(filePath, "utf-8");
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const MAX_FILE_LINES = 3e3;
|
|
59
|
+
const MAX_FILE_BYTES = 2e5;
|
|
60
|
+
if (source.length > MAX_FILE_BYTES || source.split("\n").length > MAX_FILE_LINES) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const result = pack.process(source, { language });
|
|
64
|
+
if (!result) return null;
|
|
65
|
+
const relPath = relative(repoPath, filePath).split(sep).join("/");
|
|
66
|
+
const symbols = [];
|
|
67
|
+
const calls = [];
|
|
68
|
+
const imports = [];
|
|
69
|
+
const sourceLines = source.split("\n");
|
|
70
|
+
const lineOffsets = [0];
|
|
71
|
+
for (let i = 0; i < sourceLines.length; i++) {
|
|
72
|
+
lineOffsets.push(lineOffsets[i] + sourceLines[i].length + 1);
|
|
73
|
+
}
|
|
74
|
+
const extractSymbols = (items, parentId) => {
|
|
75
|
+
for (const item of items) {
|
|
76
|
+
if (!item.name) continue;
|
|
77
|
+
const id = generateId();
|
|
78
|
+
const lineStart = (item.span?.startLine ?? 0) + 1;
|
|
79
|
+
const lineEnd = (item.span?.endLine ?? lineStart) + 1;
|
|
80
|
+
const bodyStart = item.span?.startLine !== void 0 ? lineOffsets[item.span.startLine] : 0;
|
|
81
|
+
const bodyEnd = item.span?.endLine !== void 0 ? lineOffsets[item.span.endLine + 1] - 1 : source.length;
|
|
82
|
+
const bodyText = source.slice(bodyStart, bodyEnd);
|
|
83
|
+
symbols.push({
|
|
84
|
+
id,
|
|
85
|
+
name: item.name,
|
|
86
|
+
kind: item.kind ?? "unknown",
|
|
87
|
+
filePath: relPath,
|
|
88
|
+
lineStart,
|
|
89
|
+
lineEnd,
|
|
90
|
+
language,
|
|
91
|
+
signature: item.signature ?? null,
|
|
92
|
+
docstring: item.docComment ?? null,
|
|
93
|
+
parentId,
|
|
94
|
+
contentHash: createHash("sha256").update(item.name + relPath + lineStart).digest("hex")
|
|
95
|
+
});
|
|
96
|
+
const bodyStartLine = lineStart;
|
|
97
|
+
const offsetToLine = (offset) => {
|
|
98
|
+
const sourceOffset = bodyStart + offset;
|
|
99
|
+
let lo = 0, hi = lineOffsets.length - 1;
|
|
100
|
+
while (lo < hi) {
|
|
101
|
+
const mid = lo + hi + 1 >> 1;
|
|
102
|
+
if (lineOffsets[mid] <= sourceOffset) lo = mid;
|
|
103
|
+
else hi = mid - 1;
|
|
104
|
+
}
|
|
105
|
+
return lo + 1;
|
|
106
|
+
};
|
|
107
|
+
const capturedOffsets = /* @__PURE__ */ new Set();
|
|
108
|
+
const jsxRegex = /<([A-Z][a-zA-Z0-9_$]*)\b/g;
|
|
109
|
+
let jMatch;
|
|
110
|
+
while ((jMatch = jsxRegex.exec(bodyText)) !== null) {
|
|
111
|
+
const calleeName = jMatch[1];
|
|
112
|
+
const offset = jMatch.index;
|
|
113
|
+
if (!isKeywordOrSelf(calleeName, item.name) && !capturedOffsets.has(offset)) {
|
|
114
|
+
calls.push({
|
|
115
|
+
callerId: id,
|
|
116
|
+
calleeName,
|
|
117
|
+
calleeId: null,
|
|
118
|
+
line: offsetToLine(offset),
|
|
119
|
+
kind: "call",
|
|
120
|
+
callType: "jsx"
|
|
121
|
+
});
|
|
122
|
+
capturedOffsets.add(offset);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const methodCallRegex = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\.([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g;
|
|
126
|
+
let mMatch;
|
|
127
|
+
while ((mMatch = methodCallRegex.exec(bodyText)) !== null) {
|
|
128
|
+
const receiver = mMatch[1];
|
|
129
|
+
const methodName = mMatch[2];
|
|
130
|
+
const calleeName = `${receiver}.${methodName}`;
|
|
131
|
+
const offset = mMatch.index;
|
|
132
|
+
if (!isKeywordOrSelf(methodName, item.name) && !isKeywordOrSelf(receiver, item.name) && !isStdlibCall(calleeName, language) && !capturedOffsets.has(offset)) {
|
|
133
|
+
calls.push({
|
|
134
|
+
callerId: id,
|
|
135
|
+
calleeName,
|
|
136
|
+
calleeId: null,
|
|
137
|
+
line: offsetToLine(offset),
|
|
138
|
+
kind: "call",
|
|
139
|
+
callType: "method"
|
|
140
|
+
});
|
|
141
|
+
capturedOffsets.add(offset);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const simpleCallRegex = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g;
|
|
145
|
+
let match;
|
|
146
|
+
while ((match = simpleCallRegex.exec(bodyText)) !== null) {
|
|
147
|
+
const calleeName = match[1];
|
|
148
|
+
const offset = match.index;
|
|
149
|
+
if (!isKeywordOrSelf(calleeName, item.name) && !isStdlibCall(calleeName, language) && !capturedOffsets.has(offset)) {
|
|
150
|
+
calls.push({
|
|
151
|
+
callerId: id,
|
|
152
|
+
calleeName,
|
|
153
|
+
calleeId: null,
|
|
154
|
+
line: offsetToLine(offset),
|
|
155
|
+
kind: "call",
|
|
156
|
+
callType: "direct"
|
|
157
|
+
});
|
|
158
|
+
capturedOffsets.add(offset);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (item.children && item.children.length > 0) {
|
|
162
|
+
extractSymbols(item.children, id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
extractSymbols(result.structure ?? [], null);
|
|
167
|
+
for (const imp of result.imports ?? []) {
|
|
168
|
+
imports.push({
|
|
169
|
+
filePath: relPath,
|
|
170
|
+
symbolName: imp.items?.join(", ") ?? imp.source ?? "unknown",
|
|
171
|
+
sourcePath: imp.source ?? null,
|
|
172
|
+
line: (imp.span?.startLine ?? 0) + 1,
|
|
173
|
+
language
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return { symbols, calls, imports };
|
|
177
|
+
}
|
|
178
|
+
function isKeywordOrSelf(name, symbolName) {
|
|
179
|
+
if (name === symbolName) return true;
|
|
180
|
+
const keywords = /* @__PURE__ */ new Set([
|
|
181
|
+
"if",
|
|
182
|
+
"for",
|
|
183
|
+
"while",
|
|
184
|
+
"switch",
|
|
185
|
+
"return",
|
|
186
|
+
"function",
|
|
187
|
+
"def",
|
|
188
|
+
"func",
|
|
189
|
+
"fn",
|
|
190
|
+
"print",
|
|
191
|
+
"console",
|
|
192
|
+
"require",
|
|
193
|
+
"import",
|
|
194
|
+
"export",
|
|
195
|
+
"class",
|
|
196
|
+
"struct",
|
|
197
|
+
"enum",
|
|
198
|
+
"interface",
|
|
199
|
+
"type",
|
|
200
|
+
"const",
|
|
201
|
+
"let",
|
|
202
|
+
"var",
|
|
203
|
+
"new",
|
|
204
|
+
"delete",
|
|
205
|
+
"typeof",
|
|
206
|
+
"instanceof",
|
|
207
|
+
"void",
|
|
208
|
+
"in",
|
|
209
|
+
"of",
|
|
210
|
+
"await",
|
|
211
|
+
"async",
|
|
212
|
+
"yield",
|
|
213
|
+
"throw",
|
|
214
|
+
"try",
|
|
215
|
+
"catch",
|
|
216
|
+
"finally",
|
|
217
|
+
"break",
|
|
218
|
+
"continue",
|
|
219
|
+
"do",
|
|
220
|
+
"else",
|
|
221
|
+
"case",
|
|
222
|
+
"default",
|
|
223
|
+
"extends",
|
|
224
|
+
"implements",
|
|
225
|
+
"super",
|
|
226
|
+
"this",
|
|
227
|
+
"self",
|
|
228
|
+
"true",
|
|
229
|
+
"false",
|
|
230
|
+
"null",
|
|
231
|
+
"undefined",
|
|
232
|
+
"nil",
|
|
233
|
+
"None",
|
|
234
|
+
"True",
|
|
235
|
+
"False",
|
|
236
|
+
"println",
|
|
237
|
+
"printf",
|
|
238
|
+
"fmt",
|
|
239
|
+
"err",
|
|
240
|
+
"panic",
|
|
241
|
+
"recover"
|
|
242
|
+
]);
|
|
243
|
+
return keywords.has(name);
|
|
244
|
+
}
|
|
245
|
+
function isStdlibCall(calleeName, language) {
|
|
246
|
+
const prefixes = STDLIB_PREFIXES[language];
|
|
247
|
+
if (!prefixes) return false;
|
|
248
|
+
if (prefixes.has(calleeName)) return true;
|
|
249
|
+
const dotIdx = calleeName.indexOf(".");
|
|
250
|
+
if (dotIdx > 0) {
|
|
251
|
+
const prefix = calleeName.slice(0, dotIdx);
|
|
252
|
+
if (prefixes.has(prefix)) return true;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
async function indexFile(db, filePath, repoPath, teamId) {
|
|
257
|
+
const language = detectLanguage(filePath);
|
|
258
|
+
if (!language) {
|
|
259
|
+
return { file: filePath, language: "unknown", symbols: 0, calls: 0, imports: 0, skipped: true };
|
|
260
|
+
}
|
|
261
|
+
const parsed = await parseFile(filePath, repoPath);
|
|
262
|
+
if (!parsed) {
|
|
263
|
+
return { file: filePath, language, symbols: 0, calls: 0, imports: 0, skipped: true };
|
|
264
|
+
}
|
|
265
|
+
const relPath = relative(repoPath, filePath).split(sep).join("/");
|
|
266
|
+
const now = Date.now();
|
|
267
|
+
const existingIds = db.prepare("SELECT id FROM symbols WHERE file_path = ? AND team_id IS ?").all(relPath, teamId);
|
|
268
|
+
if (existingIds.length > 0) {
|
|
269
|
+
const placeholders = existingIds.map(() => "?").join(",");
|
|
270
|
+
db.prepare(`DELETE FROM calls WHERE caller_id IN (${placeholders})`).run(
|
|
271
|
+
...existingIds.map((e) => e.id)
|
|
272
|
+
);
|
|
273
|
+
db.prepare("DELETE FROM symbols WHERE file_path = ? AND team_id IS ?").run(relPath, teamId);
|
|
274
|
+
}
|
|
275
|
+
db.prepare("DELETE FROM imports WHERE file_path = ? AND team_id IS ?").run(relPath, teamId);
|
|
276
|
+
const symbolStmt = db.prepare(
|
|
277
|
+
`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)
|
|
278
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
279
|
+
);
|
|
280
|
+
const modulePath = relPath.replace(/\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|c|cpp|cc|cxx|hpp|cs)$/, "");
|
|
281
|
+
for (const s of parsed.symbols) {
|
|
282
|
+
symbolStmt.run(
|
|
283
|
+
s.id,
|
|
284
|
+
s.name,
|
|
285
|
+
s.kind,
|
|
286
|
+
s.filePath,
|
|
287
|
+
s.lineStart,
|
|
288
|
+
s.lineEnd,
|
|
289
|
+
s.language,
|
|
290
|
+
s.signature,
|
|
291
|
+
s.docstring,
|
|
292
|
+
s.parentId,
|
|
293
|
+
teamId,
|
|
294
|
+
repoPath,
|
|
295
|
+
s.contentHash,
|
|
296
|
+
modulePath,
|
|
297
|
+
now,
|
|
298
|
+
now
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const callStmt = db.prepare(
|
|
302
|
+
`INSERT INTO calls (caller_id, callee_name, callee_id, line, kind, call_type, team_id) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
303
|
+
);
|
|
304
|
+
for (const c of parsed.calls) {
|
|
305
|
+
callStmt.run(
|
|
306
|
+
c.callerId,
|
|
307
|
+
c.calleeName,
|
|
308
|
+
null,
|
|
309
|
+
// Will be resolved by resolveAllCalls() in indexDirectory
|
|
310
|
+
c.line,
|
|
311
|
+
c.kind,
|
|
312
|
+
c.callType ?? "direct",
|
|
313
|
+
teamId
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const importStmt = db.prepare(
|
|
317
|
+
`INSERT INTO imports (file_path, symbol_name, source_path, line, language, team_id, repo_path) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
318
|
+
);
|
|
319
|
+
for (const imp of parsed.imports) {
|
|
320
|
+
importStmt.run(
|
|
321
|
+
imp.filePath,
|
|
322
|
+
imp.symbolName,
|
|
323
|
+
imp.sourcePath,
|
|
324
|
+
imp.line,
|
|
325
|
+
imp.language,
|
|
326
|
+
teamId,
|
|
327
|
+
repoPath
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
file: relPath,
|
|
332
|
+
language,
|
|
333
|
+
symbols: parsed.symbols.length,
|
|
334
|
+
calls: parsed.calls.length,
|
|
335
|
+
imports: parsed.imports.length,
|
|
336
|
+
skipped: false
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async function indexDirectory(db, dirPath, repoPath, teamId, maxFiles = 1e4) {
|
|
340
|
+
const results = [];
|
|
341
|
+
const files = [];
|
|
342
|
+
const walk = (dir) => {
|
|
343
|
+
if (files.length >= maxFiles) return;
|
|
344
|
+
let entries;
|
|
345
|
+
try {
|
|
346
|
+
entries = readdirSync(dir);
|
|
347
|
+
} catch {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
for (const entry of entries) {
|
|
351
|
+
if (files.length >= maxFiles) return;
|
|
352
|
+
const fullPath = join(dir, entry);
|
|
353
|
+
let stat;
|
|
354
|
+
try {
|
|
355
|
+
stat = statSync(fullPath);
|
|
356
|
+
} catch {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (stat.isDirectory()) {
|
|
360
|
+
if (entry === "node_modules" || entry === ".git" || entry === "dist" || entry === "build" || entry === "target" || entry === "__pycache__" || entry === ".next" || entry === "vendor" || entry === ".venv" || entry.startsWith("."))
|
|
361
|
+
continue;
|
|
362
|
+
walk(fullPath);
|
|
363
|
+
} else if (stat.isFile()) {
|
|
364
|
+
if (detectLanguage(fullPath)) {
|
|
365
|
+
files.push(fullPath);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
walk(dirPath);
|
|
371
|
+
for (const file of files) {
|
|
372
|
+
const result = await indexFile(db, file, repoPath, teamId);
|
|
373
|
+
results.push(result);
|
|
374
|
+
}
|
|
375
|
+
const { resolveAllCalls } = await import("./resolver-AVK7XKX7.js");
|
|
376
|
+
const stats = resolveAllCalls(db);
|
|
377
|
+
if (stats.total > 0) {
|
|
378
|
+
console.error(
|
|
379
|
+
`[remem-mcp] indexDirectory: resolved ${stats.resolved}/${stats.total} calls` + (Object.keys(stats.byStrategy).length > 0 ? ` (${JSON.stringify(stats.byStrategy)})` : "")
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return results;
|
|
383
|
+
}
|
|
384
|
+
function searchSymbols(db, query, opts = {}) {
|
|
385
|
+
const limit = opts.limit ?? 20;
|
|
386
|
+
const pattern = `%${query}%`;
|
|
387
|
+
let sql = "SELECT * FROM symbols WHERE name LIKE ?";
|
|
388
|
+
const params = [pattern];
|
|
389
|
+
if (opts.teamId !== void 0) {
|
|
390
|
+
sql += " AND team_id IS ?";
|
|
391
|
+
params.push(opts.teamId);
|
|
392
|
+
}
|
|
393
|
+
if (opts.repoPath !== void 0) {
|
|
394
|
+
sql += " AND (repo_path = ? OR repo_path LIKE ?)";
|
|
395
|
+
params.push(opts.repoPath, `${opts.repoPath}%`);
|
|
396
|
+
}
|
|
397
|
+
if (opts.kind) {
|
|
398
|
+
sql += " AND kind = ?";
|
|
399
|
+
params.push(opts.kind);
|
|
400
|
+
}
|
|
401
|
+
if (opts.language) {
|
|
402
|
+
sql += " AND language = ?";
|
|
403
|
+
params.push(opts.language);
|
|
404
|
+
}
|
|
405
|
+
sql += " LIMIT ?";
|
|
406
|
+
params.push(limit);
|
|
407
|
+
const rows = db.prepare(sql).all(...params);
|
|
408
|
+
return rows.map((r) => ({
|
|
409
|
+
id: r.id,
|
|
410
|
+
name: r.name,
|
|
411
|
+
kind: r.kind,
|
|
412
|
+
filePath: r.file_path,
|
|
413
|
+
lineStart: r.line_start,
|
|
414
|
+
lineEnd: r.line_end,
|
|
415
|
+
language: r.language,
|
|
416
|
+
signature: r.signature,
|
|
417
|
+
docstring: r.docstring,
|
|
418
|
+
parentId: r.parent_id,
|
|
419
|
+
contentHash: r.content_hash
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
function findCallers(db, symbolId, opts = {}) {
|
|
423
|
+
const limit = opts.limit ?? 50;
|
|
424
|
+
const rows = db.prepare(
|
|
425
|
+
`SELECT s.*, c.line as call_line
|
|
426
|
+
FROM calls c
|
|
427
|
+
JOIN symbols s ON s.id = c.caller_id
|
|
428
|
+
WHERE c.callee_id = ? OR c.callee_name = (SELECT name FROM symbols WHERE id = ?)
|
|
429
|
+
LIMIT ?`
|
|
430
|
+
).all(symbolId, symbolId, limit);
|
|
431
|
+
return rows.map((r) => ({
|
|
432
|
+
caller: {
|
|
433
|
+
id: r.id,
|
|
434
|
+
name: r.name,
|
|
435
|
+
kind: r.kind,
|
|
436
|
+
filePath: r.file_path,
|
|
437
|
+
lineStart: r.line_start,
|
|
438
|
+
lineEnd: r.line_end,
|
|
439
|
+
language: r.language,
|
|
440
|
+
signature: r.signature,
|
|
441
|
+
docstring: r.docstring,
|
|
442
|
+
parentId: r.parent_id,
|
|
443
|
+
contentHash: r.content_hash
|
|
444
|
+
},
|
|
445
|
+
line: r.call_line
|
|
446
|
+
}));
|
|
447
|
+
}
|
|
448
|
+
function findCallees(db, symbolId, opts = {}) {
|
|
449
|
+
const limit = opts.limit ?? 50;
|
|
450
|
+
const rows = db.prepare(
|
|
451
|
+
`SELECT c.callee_name, c.callee_id, c.line,
|
|
452
|
+
s.id as sid, s.name as sname, s.kind as skind, s.file_path as sfile,
|
|
453
|
+
s.line_start as sline_start, s.line_end as sline_end, s.language as slang,
|
|
454
|
+
s.signature as ssig, s.docstring as sdoc, s.parent_id as sparent, s.content_hash as shash
|
|
455
|
+
FROM calls c
|
|
456
|
+
LEFT JOIN symbols s ON s.id = c.callee_id
|
|
457
|
+
WHERE c.caller_id = ?
|
|
458
|
+
LIMIT ?`
|
|
459
|
+
).all(symbolId, limit);
|
|
460
|
+
return rows.map((r) => ({
|
|
461
|
+
callee: r.sid ? {
|
|
462
|
+
id: r.sid,
|
|
463
|
+
name: r.sname ?? "",
|
|
464
|
+
kind: r.skind ?? "",
|
|
465
|
+
filePath: r.sfile ?? "",
|
|
466
|
+
lineStart: r.sline_start ?? 0,
|
|
467
|
+
lineEnd: r.sline_end ?? 0,
|
|
468
|
+
language: r.slang ?? "",
|
|
469
|
+
signature: r.ssig,
|
|
470
|
+
docstring: r.sdoc,
|
|
471
|
+
parentId: r.sparent,
|
|
472
|
+
contentHash: r.shash ?? ""
|
|
473
|
+
} : null,
|
|
474
|
+
calleeName: r.callee_name,
|
|
475
|
+
line: r.line
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
478
|
+
function impactAnalysis(db, symbolId, opts = {}) {
|
|
479
|
+
const maxDepth = opts.maxDepth ?? 5;
|
|
480
|
+
const rootRow = db.prepare("SELECT * FROM symbols WHERE id = ?").get(symbolId);
|
|
481
|
+
if (!rootRow) {
|
|
482
|
+
throw new Error(`Symbol not found: ${symbolId}`);
|
|
483
|
+
}
|
|
484
|
+
const root = {
|
|
485
|
+
id: rootRow.id,
|
|
486
|
+
name: rootRow.name,
|
|
487
|
+
kind: rootRow.kind,
|
|
488
|
+
filePath: rootRow.file_path,
|
|
489
|
+
lineStart: rootRow.line_start,
|
|
490
|
+
lineEnd: rootRow.line_end,
|
|
491
|
+
language: rootRow.language,
|
|
492
|
+
signature: rootRow.signature,
|
|
493
|
+
docstring: rootRow.docstring,
|
|
494
|
+
parentId: rootRow.parent_id,
|
|
495
|
+
contentHash: rootRow.content_hash
|
|
496
|
+
};
|
|
497
|
+
const affected = [];
|
|
498
|
+
const visited = /* @__PURE__ */ new Set([symbolId]);
|
|
499
|
+
const queue = [
|
|
500
|
+
{ id: symbolId, depth: 0, path: [root.name] }
|
|
501
|
+
];
|
|
502
|
+
while (queue.length > 0) {
|
|
503
|
+
const item = queue.shift();
|
|
504
|
+
if (!item) continue;
|
|
505
|
+
const { id, depth, path } = item;
|
|
506
|
+
if (depth >= maxDepth) continue;
|
|
507
|
+
const callers = findCallers(db, id, { limit: 100 });
|
|
508
|
+
for (const { caller } of callers) {
|
|
509
|
+
if (visited.has(caller.id)) continue;
|
|
510
|
+
visited.add(caller.id);
|
|
511
|
+
const newPath = [...path, caller.name];
|
|
512
|
+
affected.push({ symbol: caller, depth: depth + 1, path: newPath });
|
|
513
|
+
queue.push({ id: caller.id, depth: depth + 1, path: newPath });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return { rootSymbol: root, affected };
|
|
517
|
+
}
|
|
518
|
+
function listSymbols(db, filePath, opts = {}) {
|
|
519
|
+
const limit = opts.limit ?? 100;
|
|
520
|
+
let relPath = filePath;
|
|
521
|
+
if (opts.repoPath) {
|
|
522
|
+
try {
|
|
523
|
+
relPath = relative(opts.repoPath, filePath).split(sep).join("/");
|
|
524
|
+
} catch {
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
let sql = "SELECT * FROM symbols WHERE file_path LIKE ?";
|
|
528
|
+
const params = [`${relPath}%`];
|
|
529
|
+
if (opts.teamId !== void 0) {
|
|
530
|
+
sql += " AND team_id IS ?";
|
|
531
|
+
params.push(opts.teamId);
|
|
532
|
+
}
|
|
533
|
+
if (opts.repoPath !== void 0) {
|
|
534
|
+
sql += " AND (repo_path = ? OR repo_path LIKE ?)";
|
|
535
|
+
params.push(opts.repoPath, `${opts.repoPath}%`);
|
|
536
|
+
}
|
|
537
|
+
if (opts.kind) {
|
|
538
|
+
sql += " AND kind = ?";
|
|
539
|
+
params.push(opts.kind);
|
|
540
|
+
}
|
|
541
|
+
sql += " ORDER BY line_start LIMIT ?";
|
|
542
|
+
params.push(limit);
|
|
543
|
+
const rows = db.prepare(sql).all(...params);
|
|
544
|
+
return rows.map((r) => ({
|
|
545
|
+
id: r.id,
|
|
546
|
+
name: r.name,
|
|
547
|
+
kind: r.kind,
|
|
548
|
+
filePath: r.file_path,
|
|
549
|
+
lineStart: r.line_start,
|
|
550
|
+
lineEnd: r.line_end,
|
|
551
|
+
language: r.language,
|
|
552
|
+
signature: r.signature,
|
|
553
|
+
docstring: r.docstring,
|
|
554
|
+
parentId: r.parent_id,
|
|
555
|
+
contentHash: r.content_hash
|
|
556
|
+
}));
|
|
557
|
+
}
|
|
558
|
+
var _pack, EXTENSION_MAP, SUPPORTED_LANGUAGES, STDLIB_PREFIXES;
|
|
559
|
+
var init_engine = __esm({
|
|
560
|
+
"src/codegraph/engine.ts"() {
|
|
561
|
+
init_esm_shims();
|
|
562
|
+
init_ulid();
|
|
563
|
+
_pack = null;
|
|
564
|
+
EXTENSION_MAP = {
|
|
565
|
+
".ts": "typescript",
|
|
566
|
+
".tsx": "tsx",
|
|
567
|
+
".js": "javascript",
|
|
568
|
+
".jsx": "javascript",
|
|
569
|
+
".mjs": "javascript",
|
|
570
|
+
".cjs": "javascript",
|
|
571
|
+
".py": "python",
|
|
572
|
+
".go": "go",
|
|
573
|
+
".rs": "rust",
|
|
574
|
+
".java": "java",
|
|
575
|
+
".c": "c",
|
|
576
|
+
".h": "c",
|
|
577
|
+
".cpp": "cpp",
|
|
578
|
+
".cc": "cpp",
|
|
579
|
+
".cxx": "cpp",
|
|
580
|
+
".hpp": "cpp",
|
|
581
|
+
".cs": "csharp"
|
|
582
|
+
};
|
|
583
|
+
SUPPORTED_LANGUAGES = new Set(Object.values(EXTENSION_MAP));
|
|
584
|
+
STDLIB_PREFIXES = {
|
|
585
|
+
go: /* @__PURE__ */ new Set([
|
|
586
|
+
"fmt",
|
|
587
|
+
"json",
|
|
588
|
+
"os",
|
|
589
|
+
"io",
|
|
590
|
+
"strings",
|
|
591
|
+
"strconv",
|
|
592
|
+
"time",
|
|
593
|
+
"errors",
|
|
594
|
+
"sync",
|
|
595
|
+
"context",
|
|
596
|
+
"path",
|
|
597
|
+
"filepath",
|
|
598
|
+
"sort",
|
|
599
|
+
"bytes",
|
|
600
|
+
"bufio",
|
|
601
|
+
"encoding",
|
|
602
|
+
"net",
|
|
603
|
+
"http",
|
|
604
|
+
"url",
|
|
605
|
+
"regexp",
|
|
606
|
+
"math",
|
|
607
|
+
"log",
|
|
608
|
+
"reflect",
|
|
609
|
+
"runtime",
|
|
610
|
+
"unsafe",
|
|
611
|
+
"atomic",
|
|
612
|
+
"crypto",
|
|
613
|
+
"hash",
|
|
614
|
+
"base64",
|
|
615
|
+
"hex",
|
|
616
|
+
"ioutil",
|
|
617
|
+
"filepath",
|
|
618
|
+
"unicode",
|
|
619
|
+
"testing",
|
|
620
|
+
"flag",
|
|
621
|
+
"env"
|
|
622
|
+
]),
|
|
623
|
+
typescript: /* @__PURE__ */ new Set([
|
|
624
|
+
"console",
|
|
625
|
+
"JSON",
|
|
626
|
+
"Math",
|
|
627
|
+
"Date",
|
|
628
|
+
"Object",
|
|
629
|
+
"Array",
|
|
630
|
+
"String",
|
|
631
|
+
"Number",
|
|
632
|
+
"Boolean",
|
|
633
|
+
"Promise",
|
|
634
|
+
"Symbol",
|
|
635
|
+
"Map",
|
|
636
|
+
"Set",
|
|
637
|
+
"WeakMap",
|
|
638
|
+
"WeakSet",
|
|
639
|
+
"Error",
|
|
640
|
+
"RegExp",
|
|
641
|
+
"Buffer",
|
|
642
|
+
"process",
|
|
643
|
+
"setTimeout",
|
|
644
|
+
"setInterval",
|
|
645
|
+
"clearTimeout",
|
|
646
|
+
"clearInterval",
|
|
647
|
+
"fetch",
|
|
648
|
+
"URL",
|
|
649
|
+
"URLSearchParams",
|
|
650
|
+
"FormData",
|
|
651
|
+
"Headers",
|
|
652
|
+
"Request",
|
|
653
|
+
"Response",
|
|
654
|
+
"AbortController",
|
|
655
|
+
"Event",
|
|
656
|
+
"EventTarget",
|
|
657
|
+
"CustomEvent",
|
|
658
|
+
"TextEncoder",
|
|
659
|
+
"TextDecoder",
|
|
660
|
+
"crypto",
|
|
661
|
+
"performance",
|
|
662
|
+
"queueMicrotask",
|
|
663
|
+
"atob",
|
|
664
|
+
"btoa",
|
|
665
|
+
"parseInt",
|
|
666
|
+
"parseFloat",
|
|
667
|
+
"isNaN",
|
|
668
|
+
"isFinite",
|
|
669
|
+
"encodeURIComponent",
|
|
670
|
+
"decodeURIComponent",
|
|
671
|
+
"encodeURI",
|
|
672
|
+
"decodeURI"
|
|
673
|
+
]),
|
|
674
|
+
javascript: /* @__PURE__ */ new Set([
|
|
675
|
+
"console",
|
|
676
|
+
"JSON",
|
|
677
|
+
"Math",
|
|
678
|
+
"Date",
|
|
679
|
+
"Object",
|
|
680
|
+
"Array",
|
|
681
|
+
"String",
|
|
682
|
+
"Number",
|
|
683
|
+
"Boolean",
|
|
684
|
+
"Promise",
|
|
685
|
+
"Symbol",
|
|
686
|
+
"Map",
|
|
687
|
+
"Set",
|
|
688
|
+
"WeakMap",
|
|
689
|
+
"WeakSet",
|
|
690
|
+
"Error",
|
|
691
|
+
"RegExp",
|
|
692
|
+
"Buffer",
|
|
693
|
+
"process",
|
|
694
|
+
"setTimeout",
|
|
695
|
+
"setInterval",
|
|
696
|
+
"clearTimeout",
|
|
697
|
+
"clearInterval",
|
|
698
|
+
"fetch",
|
|
699
|
+
"URL",
|
|
700
|
+
"URLSearchParams",
|
|
701
|
+
"FormData",
|
|
702
|
+
"Headers",
|
|
703
|
+
"Request",
|
|
704
|
+
"Response",
|
|
705
|
+
"AbortController",
|
|
706
|
+
"Event",
|
|
707
|
+
"EventTarget",
|
|
708
|
+
"CustomEvent",
|
|
709
|
+
"TextEncoder",
|
|
710
|
+
"TextDecoder",
|
|
711
|
+
"crypto",
|
|
712
|
+
"performance",
|
|
713
|
+
"queueMicrotask",
|
|
714
|
+
"atob",
|
|
715
|
+
"btoa",
|
|
716
|
+
"parseInt",
|
|
717
|
+
"parseFloat",
|
|
718
|
+
"isNaN",
|
|
719
|
+
"isFinite",
|
|
720
|
+
"encodeURIComponent",
|
|
721
|
+
"decodeURIComponent",
|
|
722
|
+
"encodeURI",
|
|
723
|
+
"decodeURI"
|
|
724
|
+
]),
|
|
725
|
+
python: /* @__PURE__ */ new Set([
|
|
726
|
+
"print",
|
|
727
|
+
"len",
|
|
728
|
+
"range",
|
|
729
|
+
"str",
|
|
730
|
+
"int",
|
|
731
|
+
"float",
|
|
732
|
+
"bool",
|
|
733
|
+
"list",
|
|
734
|
+
"dict",
|
|
735
|
+
"set",
|
|
736
|
+
"tuple",
|
|
737
|
+
"type",
|
|
738
|
+
"isinstance",
|
|
739
|
+
"issubclass",
|
|
740
|
+
"id",
|
|
741
|
+
"hash",
|
|
742
|
+
"dir",
|
|
743
|
+
"vars",
|
|
744
|
+
"globals",
|
|
745
|
+
"locals",
|
|
746
|
+
"exec",
|
|
747
|
+
"eval",
|
|
748
|
+
"compile",
|
|
749
|
+
"open",
|
|
750
|
+
"input",
|
|
751
|
+
"repr",
|
|
752
|
+
"format",
|
|
753
|
+
"chr",
|
|
754
|
+
"ord",
|
|
755
|
+
"hex",
|
|
756
|
+
"oct",
|
|
757
|
+
"bin",
|
|
758
|
+
"abs",
|
|
759
|
+
"min",
|
|
760
|
+
"max",
|
|
761
|
+
"sum",
|
|
762
|
+
"round",
|
|
763
|
+
"pow",
|
|
764
|
+
"divmod",
|
|
765
|
+
"sorted",
|
|
766
|
+
"reversed",
|
|
767
|
+
"enumerate",
|
|
768
|
+
"zip",
|
|
769
|
+
"map",
|
|
770
|
+
"filter",
|
|
771
|
+
"any",
|
|
772
|
+
"all",
|
|
773
|
+
"next",
|
|
774
|
+
"iter",
|
|
775
|
+
"getattr",
|
|
776
|
+
"setattr",
|
|
777
|
+
"hasattr",
|
|
778
|
+
"delattr",
|
|
779
|
+
"property",
|
|
780
|
+
"staticmethod",
|
|
781
|
+
"classmethod",
|
|
782
|
+
"super",
|
|
783
|
+
"object",
|
|
784
|
+
"Exception",
|
|
785
|
+
"ValueError",
|
|
786
|
+
"TypeError",
|
|
787
|
+
"KeyError",
|
|
788
|
+
"IndexError",
|
|
789
|
+
"AttributeError",
|
|
790
|
+
"RuntimeError",
|
|
791
|
+
"StopIteration",
|
|
792
|
+
"os",
|
|
793
|
+
"sys",
|
|
794
|
+
"json",
|
|
795
|
+
"re",
|
|
796
|
+
"time",
|
|
797
|
+
"datetime",
|
|
798
|
+
"pathlib",
|
|
799
|
+
"typing",
|
|
800
|
+
"collections",
|
|
801
|
+
"itertools",
|
|
802
|
+
"functools",
|
|
803
|
+
"subprocess",
|
|
804
|
+
"argparse",
|
|
805
|
+
"logging",
|
|
806
|
+
"unittest",
|
|
807
|
+
"asyncio",
|
|
808
|
+
"threading",
|
|
809
|
+
"multiprocessing",
|
|
810
|
+
"pickle",
|
|
811
|
+
"shutil",
|
|
812
|
+
"tempfile",
|
|
813
|
+
"glob",
|
|
814
|
+
"fnmatch",
|
|
815
|
+
"csv",
|
|
816
|
+
"io",
|
|
817
|
+
"base64",
|
|
818
|
+
"hashlib",
|
|
819
|
+
"hmac",
|
|
820
|
+
"secrets",
|
|
821
|
+
"uuid",
|
|
822
|
+
"copy",
|
|
823
|
+
"math",
|
|
824
|
+
"random",
|
|
825
|
+
"statistics",
|
|
826
|
+
"decimal",
|
|
827
|
+
"fractions",
|
|
828
|
+
"sqlite3"
|
|
829
|
+
]),
|
|
830
|
+
rust: /* @__PURE__ */ new Set([
|
|
831
|
+
"println",
|
|
832
|
+
"print",
|
|
833
|
+
"eprintln",
|
|
834
|
+
"eprint",
|
|
835
|
+
"format",
|
|
836
|
+
"vec",
|
|
837
|
+
"String",
|
|
838
|
+
"Vec",
|
|
839
|
+
"Option",
|
|
840
|
+
"Result",
|
|
841
|
+
"Some",
|
|
842
|
+
"None",
|
|
843
|
+
"Ok",
|
|
844
|
+
"Err",
|
|
845
|
+
"Box",
|
|
846
|
+
"Rc",
|
|
847
|
+
"Arc",
|
|
848
|
+
"RefCell",
|
|
849
|
+
"Cell",
|
|
850
|
+
"Mutex",
|
|
851
|
+
"RwLock",
|
|
852
|
+
"HashMap",
|
|
853
|
+
"HashSet",
|
|
854
|
+
"BTreeMap",
|
|
855
|
+
"BTreeSet",
|
|
856
|
+
"VecDeque",
|
|
857
|
+
"LinkedList",
|
|
858
|
+
"std",
|
|
859
|
+
"core",
|
|
860
|
+
"alloc",
|
|
861
|
+
"macro",
|
|
862
|
+
"todo",
|
|
863
|
+
"unimplemented",
|
|
864
|
+
"panic",
|
|
865
|
+
"assert",
|
|
866
|
+
"assert_eq",
|
|
867
|
+
"assert_ne",
|
|
868
|
+
"debug_assert",
|
|
869
|
+
"debug_assert_eq",
|
|
870
|
+
"cfg",
|
|
871
|
+
"env",
|
|
872
|
+
"include",
|
|
873
|
+
"concat",
|
|
874
|
+
"stringify",
|
|
875
|
+
"file",
|
|
876
|
+
"line",
|
|
877
|
+
"module_path",
|
|
878
|
+
"column",
|
|
879
|
+
"format_args",
|
|
880
|
+
"write",
|
|
881
|
+
"writeln"
|
|
882
|
+
])
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
export {
|
|
888
|
+
generateId,
|
|
889
|
+
init_ulid,
|
|
890
|
+
SUPPORTED_LANGUAGES,
|
|
891
|
+
detectLanguage,
|
|
892
|
+
parseFile,
|
|
893
|
+
indexFile,
|
|
894
|
+
indexDirectory,
|
|
895
|
+
searchSymbols,
|
|
896
|
+
findCallers,
|
|
897
|
+
findCallees,
|
|
898
|
+
impactAnalysis,
|
|
899
|
+
listSymbols,
|
|
900
|
+
engine_exports,
|
|
901
|
+
init_engine
|
|
902
|
+
};
|
|
903
|
+
//# sourceMappingURL=chunk-FA324IBN.js.map
|