@aicodepulse/core 0.1.0
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/dist/index.d.ts +142 -0
- package/dist/index.js +1344 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1344 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var DEFAULT_CONFIG = {
|
|
3
|
+
exclude: ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "*.min.js"],
|
|
4
|
+
maxFileSizeBytes: 512 * 1024,
|
|
5
|
+
defaultBudgetTokens: 4e3,
|
|
6
|
+
layerWeights: {
|
|
7
|
+
repo_overview: 0.1,
|
|
8
|
+
directory_map: 0.2,
|
|
9
|
+
symbol_table: 0.45,
|
|
10
|
+
import_graph: 0.15,
|
|
11
|
+
focus: 0.1
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/storage/Database.ts
|
|
16
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
17
|
+
var SCHEMA_VERSION = 1;
|
|
18
|
+
var SCHEMA_SQL = `
|
|
19
|
+
CREATE TABLE IF NOT EXISTS index_meta (
|
|
20
|
+
key TEXT PRIMARY KEY,
|
|
21
|
+
value TEXT NOT NULL
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS file_records (
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
path TEXT NOT NULL UNIQUE,
|
|
27
|
+
language TEXT NOT NULL,
|
|
28
|
+
content_hash TEXT NOT NULL,
|
|
29
|
+
size_bytes INTEGER NOT NULL,
|
|
30
|
+
lines_total INTEGER NOT NULL,
|
|
31
|
+
indexed_at INTEGER NOT NULL,
|
|
32
|
+
is_deleted INTEGER NOT NULL DEFAULT 0
|
|
33
|
+
);
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_file_language ON file_records(language);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_file_deleted ON file_records(is_deleted);
|
|
36
|
+
|
|
37
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
38
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
+
file_id INTEGER NOT NULL REFERENCES file_records(id) ON DELETE CASCADE,
|
|
40
|
+
file_path TEXT NOT NULL,
|
|
41
|
+
name TEXT NOT NULL,
|
|
42
|
+
kind TEXT NOT NULL,
|
|
43
|
+
line INTEGER NOT NULL,
|
|
44
|
+
end_line INTEGER NOT NULL,
|
|
45
|
+
is_exported INTEGER NOT NULL DEFAULT 0,
|
|
46
|
+
signature TEXT,
|
|
47
|
+
doc_comment TEXT,
|
|
48
|
+
parent_name TEXT
|
|
49
|
+
);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_symbol_file ON symbols(file_id);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_symbol_name ON symbols(name);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_symbol_exported ON symbols(is_exported);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS import_edges (
|
|
55
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
56
|
+
from_file_id INTEGER NOT NULL REFERENCES file_records(id) ON DELETE CASCADE,
|
|
57
|
+
from_path TEXT NOT NULL,
|
|
58
|
+
to_path TEXT,
|
|
59
|
+
to_package TEXT,
|
|
60
|
+
imported_names TEXT NOT NULL,
|
|
61
|
+
is_type_only INTEGER NOT NULL DEFAULT 0
|
|
62
|
+
);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS idx_import_from ON import_edges(from_file_id);
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_import_to ON import_edges(to_path);
|
|
65
|
+
`;
|
|
66
|
+
function openDatabase(dbPath) {
|
|
67
|
+
const db = new BetterSqlite3(dbPath);
|
|
68
|
+
db.pragma("journal_mode = WAL");
|
|
69
|
+
db.pragma("foreign_keys = ON");
|
|
70
|
+
runMigrations(db);
|
|
71
|
+
return db;
|
|
72
|
+
}
|
|
73
|
+
function runMigrations(db) {
|
|
74
|
+
db.exec(SCHEMA_SQL);
|
|
75
|
+
const existing = db.prepare("SELECT value FROM index_meta WHERE key = 'schema_version'").get();
|
|
76
|
+
if (!existing) {
|
|
77
|
+
db.prepare("INSERT INTO index_meta (key, value) VALUES ('schema_version', ?)").run(String(SCHEMA_VERSION));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/storage/FileRepository.ts
|
|
82
|
+
function rowToFile(row) {
|
|
83
|
+
return {
|
|
84
|
+
id: row.id,
|
|
85
|
+
path: row.path,
|
|
86
|
+
language: row.language,
|
|
87
|
+
contentHash: row.content_hash,
|
|
88
|
+
sizeBytes: row.size_bytes,
|
|
89
|
+
linesTotal: row.lines_total,
|
|
90
|
+
indexedAt: row.indexed_at,
|
|
91
|
+
isDeleted: Boolean(row.is_deleted)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
var FileRepository = class {
|
|
95
|
+
constructor(db) {
|
|
96
|
+
this.db = db;
|
|
97
|
+
}
|
|
98
|
+
db;
|
|
99
|
+
upsert(file) {
|
|
100
|
+
const result = this.db.prepare(`
|
|
101
|
+
INSERT INTO file_records (path, language, content_hash, size_bytes, lines_total, indexed_at, is_deleted)
|
|
102
|
+
VALUES (@path, @language, @contentHash, @sizeBytes, @linesTotal, @indexedAt, @isDeleted)
|
|
103
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
104
|
+
language = excluded.language,
|
|
105
|
+
content_hash = excluded.content_hash,
|
|
106
|
+
size_bytes = excluded.size_bytes,
|
|
107
|
+
lines_total = excluded.lines_total,
|
|
108
|
+
indexed_at = excluded.indexed_at,
|
|
109
|
+
is_deleted = excluded.is_deleted
|
|
110
|
+
`).run({
|
|
111
|
+
path: file.path,
|
|
112
|
+
language: file.language,
|
|
113
|
+
contentHash: file.contentHash,
|
|
114
|
+
sizeBytes: file.sizeBytes,
|
|
115
|
+
linesTotal: file.linesTotal,
|
|
116
|
+
indexedAt: file.indexedAt,
|
|
117
|
+
isDeleted: file.isDeleted ? 1 : 0
|
|
118
|
+
});
|
|
119
|
+
return result.lastInsertRowid;
|
|
120
|
+
}
|
|
121
|
+
getByPath(path) {
|
|
122
|
+
const row = this.db.prepare("SELECT * FROM file_records WHERE path = ?").get(path);
|
|
123
|
+
return row ? rowToFile(row) : null;
|
|
124
|
+
}
|
|
125
|
+
markDeleted(path) {
|
|
126
|
+
this.db.prepare("UPDATE file_records SET is_deleted = 1 WHERE path = ?").run(path);
|
|
127
|
+
}
|
|
128
|
+
getAllActive() {
|
|
129
|
+
const rows = this.db.prepare("SELECT * FROM file_records WHERE is_deleted = 0 ORDER BY path").all();
|
|
130
|
+
return rows.map(rowToFile);
|
|
131
|
+
}
|
|
132
|
+
getLanguageStats() {
|
|
133
|
+
return this.db.prepare(`
|
|
134
|
+
SELECT language, COUNT(*) as count
|
|
135
|
+
FROM file_records WHERE is_deleted = 0
|
|
136
|
+
GROUP BY language ORDER BY count DESC
|
|
137
|
+
`).all();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/storage/SymbolRepository.ts
|
|
142
|
+
function rowToSymbol(row) {
|
|
143
|
+
return {
|
|
144
|
+
id: row.id,
|
|
145
|
+
fileId: row.file_id,
|
|
146
|
+
filePath: row.file_path,
|
|
147
|
+
name: row.name,
|
|
148
|
+
kind: row.kind,
|
|
149
|
+
line: row.line,
|
|
150
|
+
endLine: row.end_line,
|
|
151
|
+
isExported: Boolean(row.is_exported),
|
|
152
|
+
signature: row.signature,
|
|
153
|
+
docComment: row.doc_comment,
|
|
154
|
+
parentName: row.parent_name
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function rowToImport(row) {
|
|
158
|
+
return {
|
|
159
|
+
id: row.id,
|
|
160
|
+
fromFileId: row.from_file_id,
|
|
161
|
+
fromPath: row.from_path,
|
|
162
|
+
toPath: row.to_path,
|
|
163
|
+
toPackage: row.to_package,
|
|
164
|
+
importedNames: JSON.parse(row.imported_names),
|
|
165
|
+
isTypeOnly: Boolean(row.is_type_only)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
var SymbolRepository = class {
|
|
169
|
+
constructor(db) {
|
|
170
|
+
this.db = db;
|
|
171
|
+
}
|
|
172
|
+
db;
|
|
173
|
+
insertSymbols(fileId, symbols) {
|
|
174
|
+
const stmt = this.db.prepare(`
|
|
175
|
+
INSERT INTO symbols (file_id, file_path, name, kind, line, end_line, is_exported, signature, doc_comment, parent_name)
|
|
176
|
+
VALUES (@fileId, @filePath, @name, @kind, @line, @endLine, @isExported, @signature, @docComment, @parentName)
|
|
177
|
+
`);
|
|
178
|
+
for (const s of symbols) {
|
|
179
|
+
stmt.run({
|
|
180
|
+
fileId: s.fileId,
|
|
181
|
+
filePath: s.filePath,
|
|
182
|
+
name: s.name,
|
|
183
|
+
kind: s.kind,
|
|
184
|
+
line: s.line,
|
|
185
|
+
endLine: s.endLine,
|
|
186
|
+
isExported: s.isExported ? 1 : 0,
|
|
187
|
+
signature: s.signature,
|
|
188
|
+
docComment: s.docComment,
|
|
189
|
+
parentName: s.parentName
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
insertImports(fileId, edges) {
|
|
194
|
+
const stmt = this.db.prepare(`
|
|
195
|
+
INSERT INTO import_edges (from_file_id, from_path, to_path, to_package, imported_names, is_type_only)
|
|
196
|
+
VALUES (@fromFileId, @fromPath, @toPath, @toPackage, @importedNames, @isTypeOnly)
|
|
197
|
+
`);
|
|
198
|
+
for (const e of edges) {
|
|
199
|
+
stmt.run({
|
|
200
|
+
fromFileId: e.fromFileId,
|
|
201
|
+
fromPath: e.fromPath,
|
|
202
|
+
toPath: e.toPath,
|
|
203
|
+
toPackage: e.toPackage,
|
|
204
|
+
importedNames: JSON.stringify(e.importedNames),
|
|
205
|
+
isTypeOnly: e.isTypeOnly ? 1 : 0
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
deleteByFileId(fileId) {
|
|
210
|
+
this.db.prepare("DELETE FROM symbols WHERE file_id = ?").run(fileId);
|
|
211
|
+
this.db.prepare("DELETE FROM import_edges WHERE from_file_id = ?").run(fileId);
|
|
212
|
+
}
|
|
213
|
+
getExportedByFile(filePath) {
|
|
214
|
+
const rows = this.db.prepare(
|
|
215
|
+
"SELECT * FROM symbols WHERE file_path = ? AND is_exported = 1 ORDER BY line"
|
|
216
|
+
).all(filePath);
|
|
217
|
+
return rows.map(rowToSymbol);
|
|
218
|
+
}
|
|
219
|
+
getAllExported() {
|
|
220
|
+
const rows = this.db.prepare(
|
|
221
|
+
"SELECT * FROM symbols WHERE is_exported = 1 ORDER BY file_path, line"
|
|
222
|
+
).all();
|
|
223
|
+
return rows.map(rowToSymbol);
|
|
224
|
+
}
|
|
225
|
+
search(query, limit = 20) {
|
|
226
|
+
const rows = this.db.prepare(
|
|
227
|
+
"SELECT * FROM symbols WHERE name LIKE ? AND is_exported = 1 LIMIT ?"
|
|
228
|
+
).all(`%${query}%`, limit);
|
|
229
|
+
return rows.map(rowToSymbol);
|
|
230
|
+
}
|
|
231
|
+
getImportsFrom(filePath) {
|
|
232
|
+
const rows = this.db.prepare(
|
|
233
|
+
"SELECT * FROM import_edges WHERE from_path = ?"
|
|
234
|
+
).all(filePath);
|
|
235
|
+
return rows.map(rowToImport);
|
|
236
|
+
}
|
|
237
|
+
getImportersOf(filePath) {
|
|
238
|
+
const rows = this.db.prepare(
|
|
239
|
+
"SELECT DISTINCT from_path FROM import_edges WHERE to_path = ?"
|
|
240
|
+
).all(filePath);
|
|
241
|
+
return rows.map((r) => r.from_path);
|
|
242
|
+
}
|
|
243
|
+
getTopImportedFiles(limit = 20) {
|
|
244
|
+
return this.db.prepare(`
|
|
245
|
+
SELECT to_path as path, COUNT(*) as importerCount
|
|
246
|
+
FROM import_edges WHERE to_path IS NOT NULL
|
|
247
|
+
GROUP BY to_path ORDER BY importerCount DESC LIMIT ?
|
|
248
|
+
`).all(limit);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/storage/MetaRepository.ts
|
|
253
|
+
var MetaRepository = class {
|
|
254
|
+
constructor(db) {
|
|
255
|
+
this.db = db;
|
|
256
|
+
}
|
|
257
|
+
db;
|
|
258
|
+
get(key) {
|
|
259
|
+
const row = this.db.prepare("SELECT value FROM index_meta WHERE key = ?").get(key);
|
|
260
|
+
return row?.value ?? null;
|
|
261
|
+
}
|
|
262
|
+
set(key, value) {
|
|
263
|
+
this.db.prepare("INSERT OR REPLACE INTO index_meta (key, value) VALUES (?, ?)").run(key, value);
|
|
264
|
+
}
|
|
265
|
+
getIndexMeta() {
|
|
266
|
+
const fileCount = this.db.prepare("SELECT COUNT(*) as c FROM file_records WHERE is_deleted = 0").get().c;
|
|
267
|
+
const symbolCount = this.db.prepare("SELECT COUNT(*) as c FROM symbols").get().c;
|
|
268
|
+
return {
|
|
269
|
+
schemaVersion: Number(this.get("schema_version") ?? 1),
|
|
270
|
+
lastIndexedCommit: this.get("last_indexed_commit") ?? "",
|
|
271
|
+
lastIndexedAt: Number(this.get("last_indexed_at") ?? 0),
|
|
272
|
+
repoRoot: this.get("repo_root") ?? "",
|
|
273
|
+
totalFiles: fileCount,
|
|
274
|
+
totalSymbols: symbolCount
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
saveIndexMeta(commit, repoRoot) {
|
|
278
|
+
this.set("last_indexed_commit", commit);
|
|
279
|
+
this.set("last_indexed_at", String(Date.now()));
|
|
280
|
+
this.set("repo_root", repoRoot);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/indexer/Indexer.ts
|
|
285
|
+
import Parser from "tree-sitter";
|
|
286
|
+
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
287
|
+
import { join as join2 } from "path";
|
|
288
|
+
|
|
289
|
+
// src/parser/ParserRegistry.ts
|
|
290
|
+
import { createRequire } from "module";
|
|
291
|
+
var req = createRequire(import.meta.url);
|
|
292
|
+
var registry = null;
|
|
293
|
+
function buildRegistry() {
|
|
294
|
+
const map = /* @__PURE__ */ new Map();
|
|
295
|
+
const entries = [
|
|
296
|
+
[[".js", ".mjs", ".cjs"], "javascript", "tree-sitter-javascript"],
|
|
297
|
+
[[".ts", ".tsx", ".mts", ".cts"], "typescript", "tree-sitter-typescript", (m) => m.typescript],
|
|
298
|
+
[[".py", ".pyw"], "python", "tree-sitter-python"],
|
|
299
|
+
[[".go"], "go", "tree-sitter-go"],
|
|
300
|
+
[[".rs"], "rust", "tree-sitter-rust"],
|
|
301
|
+
[[".java"], "java", "tree-sitter-java"],
|
|
302
|
+
[[".c", ".h"], "c", "tree-sitter-c"],
|
|
303
|
+
[[".cpp", ".cc", ".cxx", ".hpp", ".hxx"], "cpp", "tree-sitter-cpp"],
|
|
304
|
+
[[".cs"], "csharp", "tree-sitter-c-sharp"],
|
|
305
|
+
[[".rb"], "ruby", "tree-sitter-ruby"],
|
|
306
|
+
[[".php"], "php", "tree-sitter-php", (m) => m.php],
|
|
307
|
+
[[".sh", ".bash"], "bash", "tree-sitter-bash"],
|
|
308
|
+
[[".kt", ".kts"], "kotlin", "tree-sitter-kotlin"],
|
|
309
|
+
[[".swift"], "swift", "tree-sitter-swift"]
|
|
310
|
+
];
|
|
311
|
+
for (const [exts, name, pkg, extract] of entries) {
|
|
312
|
+
try {
|
|
313
|
+
const mod = req(pkg);
|
|
314
|
+
const lang = extract ? extract(mod) : mod;
|
|
315
|
+
const config = { language: lang, extensions: exts, name };
|
|
316
|
+
for (const ext of exts) map.set(ext, config);
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return map;
|
|
321
|
+
}
|
|
322
|
+
function getRegistry() {
|
|
323
|
+
if (!registry) registry = buildRegistry();
|
|
324
|
+
return registry;
|
|
325
|
+
}
|
|
326
|
+
function getLanguageForExtension(ext) {
|
|
327
|
+
return getRegistry().get(ext) ?? null;
|
|
328
|
+
}
|
|
329
|
+
function extensionFromPath(filePath) {
|
|
330
|
+
const dot = filePath.lastIndexOf(".");
|
|
331
|
+
return dot === -1 ? "" : filePath.slice(dot).toLowerCase();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/parser/languages/typescript.ts
|
|
335
|
+
var typescriptQueries = {
|
|
336
|
+
symbols: `
|
|
337
|
+
(export_statement
|
|
338
|
+
(function_declaration name: (identifier) @name) @node
|
|
339
|
+
(#set! kind "function"))
|
|
340
|
+
|
|
341
|
+
(export_statement
|
|
342
|
+
(class_declaration name: (type_identifier) @name) @node
|
|
343
|
+
(#set! kind "class"))
|
|
344
|
+
|
|
345
|
+
(export_statement
|
|
346
|
+
(interface_declaration name: (type_identifier) @name) @node
|
|
347
|
+
(#set! kind "interface"))
|
|
348
|
+
|
|
349
|
+
(export_statement
|
|
350
|
+
(type_alias_declaration name: (type_identifier) @name) @node
|
|
351
|
+
(#set! kind "type"))
|
|
352
|
+
|
|
353
|
+
(export_statement
|
|
354
|
+
(enum_declaration name: (identifier) @name) @node
|
|
355
|
+
(#set! kind "enum"))
|
|
356
|
+
|
|
357
|
+
(export_statement
|
|
358
|
+
(lexical_declaration
|
|
359
|
+
(variable_declarator name: (identifier) @name)) @node
|
|
360
|
+
(#set! kind "variable"))
|
|
361
|
+
|
|
362
|
+
(export_statement
|
|
363
|
+
declaration: (function_declaration name: (identifier) @name) @node
|
|
364
|
+
(#set! kind "function"))
|
|
365
|
+
|
|
366
|
+
(export_default_declaration
|
|
367
|
+
(function_declaration name: (identifier) @name) @node
|
|
368
|
+
(#set! kind "function"))
|
|
369
|
+
|
|
370
|
+
(export_default_declaration
|
|
371
|
+
(class_declaration name: (type_identifier) @name) @node
|
|
372
|
+
(#set! kind "class"))
|
|
373
|
+
`,
|
|
374
|
+
imports: `
|
|
375
|
+
(import_statement
|
|
376
|
+
source: (string (string_fragment) @source)
|
|
377
|
+
(import_clause
|
|
378
|
+
(named_imports
|
|
379
|
+
(import_specifier name: (identifier) @specifier)))) @node
|
|
380
|
+
|
|
381
|
+
(import_statement
|
|
382
|
+
source: (string (string_fragment) @source)) @node
|
|
383
|
+
`
|
|
384
|
+
};
|
|
385
|
+
var javascriptQueries = typescriptQueries;
|
|
386
|
+
|
|
387
|
+
// src/parser/languages/python.ts
|
|
388
|
+
var pythonQueries = {
|
|
389
|
+
symbols: `
|
|
390
|
+
(function_definition name: (identifier) @name) @node
|
|
391
|
+
|
|
392
|
+
(class_definition name: (identifier) @name) @node
|
|
393
|
+
|
|
394
|
+
(expression_statement
|
|
395
|
+
(assignment
|
|
396
|
+
left: (identifier) @name)) @node
|
|
397
|
+
`,
|
|
398
|
+
imports: `
|
|
399
|
+
(import_statement
|
|
400
|
+
name: (dotted_name) @source) @node
|
|
401
|
+
|
|
402
|
+
(import_from_statement
|
|
403
|
+
module_name: (dotted_name) @source
|
|
404
|
+
name: (import_from_as_names
|
|
405
|
+
(dotted_name) @specifier)) @node
|
|
406
|
+
|
|
407
|
+
(import_from_statement
|
|
408
|
+
module_name: (dotted_name) @source) @node
|
|
409
|
+
`
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// src/parser/languages/go.ts
|
|
413
|
+
var goQueries = {
|
|
414
|
+
symbols: `
|
|
415
|
+
(function_declaration name: (identifier) @name) @node
|
|
416
|
+
|
|
417
|
+
(method_declaration name: (field_identifier) @name) @node
|
|
418
|
+
|
|
419
|
+
(type_declaration
|
|
420
|
+
(type_spec name: (type_identifier) @name)) @node
|
|
421
|
+
|
|
422
|
+
(var_declaration
|
|
423
|
+
(var_spec name: (identifier) @name)) @node
|
|
424
|
+
|
|
425
|
+
(const_declaration
|
|
426
|
+
(const_spec name: (identifier) @name)) @node
|
|
427
|
+
`,
|
|
428
|
+
imports: `
|
|
429
|
+
(import_spec path: (interpreted_string_literal) @source) @node
|
|
430
|
+
`
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/parser/languages/rust.ts
|
|
434
|
+
var rustQueries = {
|
|
435
|
+
symbols: `
|
|
436
|
+
(function_item name: (identifier) @name) @node
|
|
437
|
+
|
|
438
|
+
(struct_item name: (type_identifier) @name) @node
|
|
439
|
+
|
|
440
|
+
(enum_item name: (type_identifier) @name) @node
|
|
441
|
+
|
|
442
|
+
(trait_item name: (type_identifier) @name) @node
|
|
443
|
+
|
|
444
|
+
(impl_item type: (type_identifier) @name) @node
|
|
445
|
+
|
|
446
|
+
(type_item name: (type_identifier) @name) @node
|
|
447
|
+
|
|
448
|
+
(const_item name: (identifier) @name) @node
|
|
449
|
+
`,
|
|
450
|
+
imports: `
|
|
451
|
+
(use_declaration
|
|
452
|
+
argument: (scoped_identifier path: (identifier) @source)) @node
|
|
453
|
+
|
|
454
|
+
(extern_crate_declaration name: (identifier) @source) @node
|
|
455
|
+
`
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// src/parser/languages/java.ts
|
|
459
|
+
var javaQueries = {
|
|
460
|
+
symbols: `
|
|
461
|
+
(class_declaration name: (identifier) @name) @node
|
|
462
|
+
|
|
463
|
+
(interface_declaration name: (identifier) @name) @node
|
|
464
|
+
|
|
465
|
+
(enum_declaration name: (identifier) @name) @node
|
|
466
|
+
|
|
467
|
+
(method_declaration name: (identifier) @name) @node
|
|
468
|
+
|
|
469
|
+
(field_declaration
|
|
470
|
+
declarator: (variable_declarator name: (identifier) @name)) @node
|
|
471
|
+
`,
|
|
472
|
+
imports: `
|
|
473
|
+
(import_declaration (scoped_identifier) @source) @node
|
|
474
|
+
`
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/parser/languages/c.ts
|
|
478
|
+
var cQueries = {
|
|
479
|
+
symbols: `
|
|
480
|
+
(function_definition
|
|
481
|
+
declarator: (function_declarator
|
|
482
|
+
declarator: (identifier) @name)) @node
|
|
483
|
+
|
|
484
|
+
(declaration
|
|
485
|
+
declarator: (function_declarator
|
|
486
|
+
declarator: (identifier) @name)) @node
|
|
487
|
+
|
|
488
|
+
(struct_specifier name: (type_identifier) @name) @node
|
|
489
|
+
|
|
490
|
+
(enum_specifier name: (type_identifier) @name) @node
|
|
491
|
+
|
|
492
|
+
(type_definition
|
|
493
|
+
declarator: (type_identifier) @name) @node
|
|
494
|
+
`,
|
|
495
|
+
imports: `
|
|
496
|
+
(preproc_include path: (string_literal) @source) @node
|
|
497
|
+
(preproc_include path: (system_lib_string) @source) @node
|
|
498
|
+
`
|
|
499
|
+
};
|
|
500
|
+
var cppQueries = {
|
|
501
|
+
symbols: `
|
|
502
|
+
(function_definition
|
|
503
|
+
declarator: (function_declarator
|
|
504
|
+
declarator: (identifier) @name)) @node
|
|
505
|
+
|
|
506
|
+
(function_definition
|
|
507
|
+
declarator: (function_declarator
|
|
508
|
+
declarator: (qualified_identifier name: (identifier) @name))) @node
|
|
509
|
+
|
|
510
|
+
(class_specifier name: (type_identifier) @name) @node
|
|
511
|
+
|
|
512
|
+
(struct_specifier name: (type_identifier) @name) @node
|
|
513
|
+
|
|
514
|
+
(enum_specifier name: (type_identifier) @name) @node
|
|
515
|
+
|
|
516
|
+
(namespace_definition name: (identifier) @name) @node
|
|
517
|
+
`,
|
|
518
|
+
imports: `
|
|
519
|
+
(preproc_include path: (string_literal) @source) @node
|
|
520
|
+
(preproc_include path: (system_lib_string) @source) @node
|
|
521
|
+
`
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
// src/parser/languages/csharp.ts
|
|
525
|
+
var csharpQueries = {
|
|
526
|
+
symbols: `
|
|
527
|
+
(class_declaration name: (identifier) @name) @node
|
|
528
|
+
|
|
529
|
+
(interface_declaration name: (identifier) @name) @node
|
|
530
|
+
|
|
531
|
+
(enum_declaration name: (identifier) @name) @node
|
|
532
|
+
|
|
533
|
+
(struct_declaration name: (identifier) @name) @node
|
|
534
|
+
|
|
535
|
+
(method_declaration name: (identifier) @name) @node
|
|
536
|
+
|
|
537
|
+
(property_declaration name: (identifier) @name) @node
|
|
538
|
+
|
|
539
|
+
(record_declaration name: (identifier) @name) @node
|
|
540
|
+
`,
|
|
541
|
+
imports: `
|
|
542
|
+
(using_directive (qualified_name) @source) @node
|
|
543
|
+
(using_directive (identifier) @source) @node
|
|
544
|
+
`
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// src/parser/languages/ruby.ts
|
|
548
|
+
var rubyQueries = {
|
|
549
|
+
symbols: `
|
|
550
|
+
(method name: (identifier) @name) @node
|
|
551
|
+
|
|
552
|
+
(singleton_method name: (identifier) @name) @node
|
|
553
|
+
|
|
554
|
+
(class name: (constant) @name) @node
|
|
555
|
+
|
|
556
|
+
(module name: (constant) @name) @node
|
|
557
|
+
`,
|
|
558
|
+
imports: `
|
|
559
|
+
(call
|
|
560
|
+
method: (identifier) @method
|
|
561
|
+
arguments: (argument_list (string (string_content) @source))
|
|
562
|
+
(#match? @method "^(require|require_relative|load)$")) @node
|
|
563
|
+
`
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// src/parser/languages/php.ts
|
|
567
|
+
var phpQueries = {
|
|
568
|
+
symbols: `
|
|
569
|
+
(function_definition name: (name) @name) @node
|
|
570
|
+
|
|
571
|
+
(class_declaration name: (name) @name) @node
|
|
572
|
+
|
|
573
|
+
(interface_declaration name: (name) @name) @node
|
|
574
|
+
|
|
575
|
+
(trait_declaration name: (name) @name) @node
|
|
576
|
+
|
|
577
|
+
(enum_declaration name: (name) @name) @node
|
|
578
|
+
|
|
579
|
+
(method_declaration name: (name) @name) @node
|
|
580
|
+
`,
|
|
581
|
+
imports: `
|
|
582
|
+
(namespace_use_declaration
|
|
583
|
+
(namespace_use_clause (qualified_name) @source)) @node
|
|
584
|
+
|
|
585
|
+
(require_expression (string (string_value) @source)) @node
|
|
586
|
+
(include_expression (string (string_value) @source)) @node
|
|
587
|
+
`
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// src/parser/languages/bash.ts
|
|
591
|
+
var bashQueries = {
|
|
592
|
+
symbols: `
|
|
593
|
+
(function_definition name: (word) @name) @node
|
|
594
|
+
`,
|
|
595
|
+
imports: `
|
|
596
|
+
(command
|
|
597
|
+
name: (command_name (word) @cmd)
|
|
598
|
+
argument: (word) @source
|
|
599
|
+
(#match? @cmd "^(source|\\.)$")) @node
|
|
600
|
+
`
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// src/parser/languages/kotlin.ts
|
|
604
|
+
var kotlinQueries = {
|
|
605
|
+
symbols: `
|
|
606
|
+
(function_declaration (simple_identifier) @name) @node
|
|
607
|
+
|
|
608
|
+
(class_declaration (type_identifier) @name) @node
|
|
609
|
+
|
|
610
|
+
(object_declaration (type_identifier) @name) @node
|
|
611
|
+
|
|
612
|
+
(interface_declaration (type_identifier) @name) @node
|
|
613
|
+
`,
|
|
614
|
+
imports: `
|
|
615
|
+
(import_header (identifier) @source) @node
|
|
616
|
+
`
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// src/parser/languages/swift.ts
|
|
620
|
+
var swiftQueries = {
|
|
621
|
+
symbols: `
|
|
622
|
+
(function_declaration name: (simple_identifier) @name) @node
|
|
623
|
+
|
|
624
|
+
(class_declaration name: (type_identifier) @name) @node
|
|
625
|
+
|
|
626
|
+
(struct_declaration name: (type_identifier) @name) @node
|
|
627
|
+
|
|
628
|
+
(enum_declaration name: (type_identifier) @name) @node
|
|
629
|
+
|
|
630
|
+
(protocol_declaration name: (type_identifier) @name) @node
|
|
631
|
+
|
|
632
|
+
(typealias_declaration name: (type_identifier) @name) @node
|
|
633
|
+
`,
|
|
634
|
+
imports: `
|
|
635
|
+
(import_declaration (identifier) @source) @node
|
|
636
|
+
`
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// src/parser/SymbolExtractor.ts
|
|
640
|
+
var LANGUAGE_QUERIES = {
|
|
641
|
+
typescript: typescriptQueries,
|
|
642
|
+
javascript: javascriptQueries,
|
|
643
|
+
python: pythonQueries,
|
|
644
|
+
go: goQueries,
|
|
645
|
+
rust: rustQueries,
|
|
646
|
+
java: javaQueries,
|
|
647
|
+
c: cQueries,
|
|
648
|
+
cpp: cppQueries,
|
|
649
|
+
csharp: csharpQueries,
|
|
650
|
+
ruby: rubyQueries,
|
|
651
|
+
php: phpQueries,
|
|
652
|
+
bash: bashQueries,
|
|
653
|
+
kotlin: kotlinQueries,
|
|
654
|
+
swift: swiftQueries
|
|
655
|
+
};
|
|
656
|
+
var KIND_DEFAULTS = {
|
|
657
|
+
function_definition: "function",
|
|
658
|
+
function_declaration: "function",
|
|
659
|
+
function_item: "function",
|
|
660
|
+
method_declaration: "method",
|
|
661
|
+
method_definition: "method",
|
|
662
|
+
class_declaration: "class",
|
|
663
|
+
class_definition: "class",
|
|
664
|
+
class_specifier: "class",
|
|
665
|
+
class_item: "class",
|
|
666
|
+
interface_declaration: "interface",
|
|
667
|
+
interface_item: "interface",
|
|
668
|
+
type_alias_declaration: "type",
|
|
669
|
+
type_item: "type",
|
|
670
|
+
type_definition: "type",
|
|
671
|
+
enum_declaration: "enum",
|
|
672
|
+
enum_item: "enum",
|
|
673
|
+
struct_item: "class",
|
|
674
|
+
struct_specifier: "class",
|
|
675
|
+
trait_item: "interface",
|
|
676
|
+
trait_declaration: "interface",
|
|
677
|
+
export_statement: "variable",
|
|
678
|
+
lexical_declaration: "variable",
|
|
679
|
+
var_declaration: "variable",
|
|
680
|
+
const_item: "constant",
|
|
681
|
+
const_declaration: "constant"
|
|
682
|
+
};
|
|
683
|
+
function inferKind(node) {
|
|
684
|
+
const type = node.type;
|
|
685
|
+
if (KIND_DEFAULTS[type]) return KIND_DEFAULTS[type];
|
|
686
|
+
for (const child of node.children) {
|
|
687
|
+
if (KIND_DEFAULTS[child.type]) return KIND_DEFAULTS[child.type];
|
|
688
|
+
}
|
|
689
|
+
return "variable";
|
|
690
|
+
}
|
|
691
|
+
function extractDocComment(node) {
|
|
692
|
+
const prev = node.previousNamedSibling;
|
|
693
|
+
if (!prev) return null;
|
|
694
|
+
const text = prev.text;
|
|
695
|
+
if (text.startsWith("/**") || text.startsWith("///") || text.startsWith("#")) {
|
|
696
|
+
const line = text.split("\n")[0].replace(/^[/*#\s]+/, "").trim();
|
|
697
|
+
return line || null;
|
|
698
|
+
}
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
function extractSignature(node, name) {
|
|
702
|
+
const params = node.childForFieldName("parameters") ?? node.children.find((c) => c.type === "formal_parameters" || c.type === "parameters");
|
|
703
|
+
if (!params) return null;
|
|
704
|
+
const retType = node.childForFieldName("return_type") ?? node.childForFieldName("type");
|
|
705
|
+
const sig = retType ? `${params.text}: ${retType.text}` : params.text;
|
|
706
|
+
return sig.length > 80 ? sig.slice(0, 77) + "..." : sig;
|
|
707
|
+
}
|
|
708
|
+
function extractSymbols(tree, filePath, fileId, language, source) {
|
|
709
|
+
const queries = LANGUAGE_QUERIES[language];
|
|
710
|
+
if (!queries) return [];
|
|
711
|
+
const symbols = [];
|
|
712
|
+
const lines = source.split("\n");
|
|
713
|
+
const isExportedLang = ["typescript", "javascript"].includes(language);
|
|
714
|
+
function walk(node, depth = 0) {
|
|
715
|
+
const nodeType = node.type;
|
|
716
|
+
const nameNode = node.childForFieldName("name");
|
|
717
|
+
if (nameNode && KIND_DEFAULTS[nodeType]) {
|
|
718
|
+
const name = nameNode.text;
|
|
719
|
+
const isExported = isExportedLang ? node.parent?.type === "export_statement" || node.parent?.type === "export_default_declaration" : name.length > 0 && name[0] === name[0].toUpperCase();
|
|
720
|
+
if (name && name.length > 0) {
|
|
721
|
+
symbols.push({
|
|
722
|
+
fileId,
|
|
723
|
+
filePath,
|
|
724
|
+
name,
|
|
725
|
+
kind: inferKind(node),
|
|
726
|
+
line: node.startPosition.row + 1,
|
|
727
|
+
endLine: node.endPosition.row + 1,
|
|
728
|
+
isExported,
|
|
729
|
+
signature: extractSignature(node, name),
|
|
730
|
+
docComment: extractDocComment(node),
|
|
731
|
+
parentName: null
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
for (const child of node.children) {
|
|
736
|
+
if (depth < 6) walk(child, depth + 1);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
walk(tree.rootNode);
|
|
740
|
+
return symbols;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/parser/ImportExtractor.ts
|
|
744
|
+
import { resolve, dirname, extname } from "path";
|
|
745
|
+
function resolveImportPath(source, fromFile) {
|
|
746
|
+
if (source.startsWith(".")) {
|
|
747
|
+
const dir = dirname(fromFile);
|
|
748
|
+
let resolved = resolve(dir, source);
|
|
749
|
+
if (!extname(resolved)) resolved += ".ts";
|
|
750
|
+
return { toPath: resolved, toPackage: null };
|
|
751
|
+
}
|
|
752
|
+
const parts = source.split("/");
|
|
753
|
+
const pkg = source.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
754
|
+
return { toPath: null, toPackage: pkg };
|
|
755
|
+
}
|
|
756
|
+
function walkForImports(node, filePath, fileId, language, edges, depth = 0) {
|
|
757
|
+
if (depth > 4) return;
|
|
758
|
+
const type = node.type;
|
|
759
|
+
if (type === "import_statement") {
|
|
760
|
+
const sourceNode = node.children.find((c) => c.type === "string");
|
|
761
|
+
if (sourceNode) {
|
|
762
|
+
const raw = sourceNode.text.replace(/['"]/g, "");
|
|
763
|
+
const { toPath, toPackage } = resolveImportPath(raw, filePath);
|
|
764
|
+
const isTypeOnly = node.children.some((c) => c.text === "type");
|
|
765
|
+
const names = [];
|
|
766
|
+
const namedImports = node.children.find((c) => c.type === "import_clause");
|
|
767
|
+
if (namedImports) {
|
|
768
|
+
const named = namedImports.children.find((c) => c.type === "named_imports");
|
|
769
|
+
if (named) {
|
|
770
|
+
for (const spec of named.children) {
|
|
771
|
+
if (spec.type === "import_specifier") {
|
|
772
|
+
const n = spec.childForFieldName("name");
|
|
773
|
+
if (n) names.push(n.text);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (names.length === 0) names.push("*");
|
|
778
|
+
}
|
|
779
|
+
edges.push({ fromFileId: fileId, fromPath: filePath, toPath, toPackage, importedNames: names, isTypeOnly });
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (language === "python") {
|
|
783
|
+
if (type === "import_statement") {
|
|
784
|
+
const name = node.children.find((c) => c.type === "dotted_name");
|
|
785
|
+
if (name) {
|
|
786
|
+
const mod = name.text.replace(/\./g, "/");
|
|
787
|
+
edges.push({ fromFileId: fileId, fromPath: filePath, toPath: null, toPackage: mod, importedNames: ["*"], isTypeOnly: false });
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (type === "import_from_statement") {
|
|
791
|
+
const mod = node.childForFieldName("module_name")?.text ?? "";
|
|
792
|
+
const names = [];
|
|
793
|
+
const nameList = node.children.find((c) => c.type === "import_from_as_names");
|
|
794
|
+
if (nameList) {
|
|
795
|
+
for (const c of nameList.children) {
|
|
796
|
+
if (c.type === "dotted_name") names.push(c.text);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (names.length === 0) names.push("*");
|
|
800
|
+
const isRelative = mod.startsWith(".");
|
|
801
|
+
const { toPath, toPackage } = isRelative ? resolveImportPath(mod, filePath) : { toPath: null, toPackage: mod };
|
|
802
|
+
edges.push({ fromFileId: fileId, fromPath: filePath, toPath, toPackage, importedNames: names, isTypeOnly: false });
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (language === "go" && type === "import_spec") {
|
|
806
|
+
const pathNode = node.childForFieldName("path");
|
|
807
|
+
if (pathNode) {
|
|
808
|
+
const raw = pathNode.text.replace(/"/g, "");
|
|
809
|
+
edges.push({ fromFileId: fileId, fromPath: filePath, toPath: null, toPackage: raw, importedNames: ["*"], isTypeOnly: false });
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (language === "rust" && type === "use_declaration") {
|
|
813
|
+
const arg = node.children.find((c) => c.type !== "use" && c.type !== ";");
|
|
814
|
+
if (arg) {
|
|
815
|
+
edges.push({ fromFileId: fileId, fromPath: filePath, toPath: null, toPackage: arg.text, importedNames: ["*"], isTypeOnly: false });
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
for (const child of node.children) {
|
|
819
|
+
walkForImports(child, filePath, fileId, language, edges, depth + 1);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
function extractImports(tree, filePath, fileId, language) {
|
|
823
|
+
const edges = [];
|
|
824
|
+
walkForImports(tree.rootNode, filePath, fileId, language, edges);
|
|
825
|
+
return edges;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// src/indexer/HashUtil.ts
|
|
829
|
+
import { createHash } from "crypto";
|
|
830
|
+
function hashContent(content) {
|
|
831
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/indexer/FileWalker.ts
|
|
835
|
+
import { glob } from "glob";
|
|
836
|
+
import Ignore from "ignore";
|
|
837
|
+
import { readFileSync, existsSync } from "fs";
|
|
838
|
+
import { join } from "path";
|
|
839
|
+
async function walkFiles(repoRoot, config) {
|
|
840
|
+
const ig = Ignore.default();
|
|
841
|
+
const gitignorePath = join(repoRoot, ".gitignore");
|
|
842
|
+
if (existsSync(gitignorePath)) {
|
|
843
|
+
ig.add(readFileSync(gitignorePath, "utf8"));
|
|
844
|
+
}
|
|
845
|
+
ig.add(config.exclude);
|
|
846
|
+
const pattern = "**/*";
|
|
847
|
+
const files = await glob(pattern, {
|
|
848
|
+
cwd: repoRoot,
|
|
849
|
+
nodir: true,
|
|
850
|
+
dot: false,
|
|
851
|
+
absolute: false
|
|
852
|
+
});
|
|
853
|
+
const result = [];
|
|
854
|
+
for (const rel of files) {
|
|
855
|
+
if (ig.ignores(rel)) continue;
|
|
856
|
+
result.push({
|
|
857
|
+
absolutePath: join(repoRoot, rel),
|
|
858
|
+
relativePath: rel.replace(/\\/g, "/")
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
return result;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/indexer/GitDiff.ts
|
|
865
|
+
import { execSync } from "child_process";
|
|
866
|
+
function getHeadCommit(repoRoot) {
|
|
867
|
+
try {
|
|
868
|
+
return execSync("git rev-parse HEAD", { cwd: repoRoot, encoding: "utf8" }).trim();
|
|
869
|
+
} catch {
|
|
870
|
+
return "";
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
function getChangedFiles(repoRoot, sinceCommit) {
|
|
874
|
+
if (!sinceCommit) return null;
|
|
875
|
+
try {
|
|
876
|
+
const output = execSync(
|
|
877
|
+
`git diff --name-status ${sinceCommit} HEAD`,
|
|
878
|
+
{ cwd: repoRoot, encoding: "utf8" }
|
|
879
|
+
);
|
|
880
|
+
const changes = [];
|
|
881
|
+
for (const line of output.trim().split("\n")) {
|
|
882
|
+
if (!line) continue;
|
|
883
|
+
const parts = line.split(" ");
|
|
884
|
+
const rawStatus = parts[0].charAt(0);
|
|
885
|
+
const status = ["M", "A", "D", "R"].includes(rawStatus) ? rawStatus : "M";
|
|
886
|
+
if (status === "R") {
|
|
887
|
+
changes.push({ status: "D", path: parts[1] });
|
|
888
|
+
changes.push({ status: "A", path: parts[2] });
|
|
889
|
+
} else {
|
|
890
|
+
changes.push({ status, path: parts[1] });
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return changes;
|
|
894
|
+
} catch {
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// src/indexer/Indexer.ts
|
|
900
|
+
var Indexer = class {
|
|
901
|
+
constructor(db, repoRoot, config) {
|
|
902
|
+
this.db = db;
|
|
903
|
+
this.repoRoot = repoRoot;
|
|
904
|
+
this.config = config;
|
|
905
|
+
this.files = new FileRepository(db);
|
|
906
|
+
this.symbols = new SymbolRepository(db);
|
|
907
|
+
this.meta = new MetaRepository(db);
|
|
908
|
+
}
|
|
909
|
+
db;
|
|
910
|
+
repoRoot;
|
|
911
|
+
config;
|
|
912
|
+
parser = new Parser();
|
|
913
|
+
files;
|
|
914
|
+
symbols;
|
|
915
|
+
meta;
|
|
916
|
+
async runFull() {
|
|
917
|
+
const start = Date.now();
|
|
918
|
+
const allFiles = await walkFiles(this.repoRoot, this.config);
|
|
919
|
+
let added = 0, updated = 0, skipped = 0;
|
|
920
|
+
const transaction = this.db.transaction(() => {
|
|
921
|
+
for (const { absolutePath, relativePath } of allFiles) {
|
|
922
|
+
const result = this.indexFile(absolutePath, relativePath);
|
|
923
|
+
if (result === "added") added++;
|
|
924
|
+
else if (result === "updated") updated++;
|
|
925
|
+
else skipped++;
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
transaction();
|
|
929
|
+
const commit = getHeadCommit(this.repoRoot);
|
|
930
|
+
this.meta.saveIndexMeta(commit, this.repoRoot);
|
|
931
|
+
const metaInfo = this.meta.getIndexMeta();
|
|
932
|
+
return {
|
|
933
|
+
filesAdded: added,
|
|
934
|
+
filesUpdated: updated,
|
|
935
|
+
filesDeleted: 0,
|
|
936
|
+
filesSkipped: skipped,
|
|
937
|
+
symbolsTotal: metaInfo.totalSymbols,
|
|
938
|
+
durationMs: Date.now() - start,
|
|
939
|
+
mode: "full"
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
async runIncremental() {
|
|
943
|
+
const start = Date.now();
|
|
944
|
+
const lastCommit = this.meta.get("last_indexed_commit") ?? "";
|
|
945
|
+
const currentCommit = getHeadCommit(this.repoRoot);
|
|
946
|
+
if (!lastCommit || lastCommit === currentCommit) {
|
|
947
|
+
if (!lastCommit) return this.runFull();
|
|
948
|
+
return { filesAdded: 0, filesUpdated: 0, filesDeleted: 0, filesSkipped: 0, symbolsTotal: this.meta.getIndexMeta().totalSymbols, durationMs: 0, mode: "incremental" };
|
|
949
|
+
}
|
|
950
|
+
const changes = getChangedFiles(this.repoRoot, lastCommit);
|
|
951
|
+
if (!changes) return this.runFull();
|
|
952
|
+
let added = 0, updated = 0, deleted = 0, skipped = 0;
|
|
953
|
+
const transaction = this.db.transaction(() => {
|
|
954
|
+
for (const change of changes) {
|
|
955
|
+
const absPath = join2(this.repoRoot, change.path);
|
|
956
|
+
if (change.status === "D") {
|
|
957
|
+
const existing = this.files.getByPath(change.path);
|
|
958
|
+
if (existing) {
|
|
959
|
+
this.symbols.deleteByFileId(existing.id);
|
|
960
|
+
this.files.markDeleted(change.path);
|
|
961
|
+
deleted++;
|
|
962
|
+
}
|
|
963
|
+
} else {
|
|
964
|
+
const result = this.indexFile(absPath, change.path);
|
|
965
|
+
if (result === "added") added++;
|
|
966
|
+
else if (result === "updated") updated++;
|
|
967
|
+
else skipped++;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
transaction();
|
|
972
|
+
this.meta.saveIndexMeta(currentCommit, this.repoRoot);
|
|
973
|
+
const metaInfo = this.meta.getIndexMeta();
|
|
974
|
+
return {
|
|
975
|
+
filesAdded: added,
|
|
976
|
+
filesUpdated: updated,
|
|
977
|
+
filesDeleted: deleted,
|
|
978
|
+
filesSkipped: skipped,
|
|
979
|
+
symbolsTotal: metaInfo.totalSymbols,
|
|
980
|
+
durationMs: Date.now() - start,
|
|
981
|
+
mode: "incremental"
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
indexFile(absolutePath, relativePath) {
|
|
985
|
+
let stat;
|
|
986
|
+
try {
|
|
987
|
+
stat = statSync(absolutePath);
|
|
988
|
+
} catch {
|
|
989
|
+
return "skipped";
|
|
990
|
+
}
|
|
991
|
+
if (stat.size > this.config.maxFileSizeBytes) return "skipped";
|
|
992
|
+
const ext = extensionFromPath(relativePath);
|
|
993
|
+
let content;
|
|
994
|
+
try {
|
|
995
|
+
content = readFileSync2(absolutePath, "utf8");
|
|
996
|
+
} catch {
|
|
997
|
+
return "skipped";
|
|
998
|
+
}
|
|
999
|
+
const contentHash = hashContent(content);
|
|
1000
|
+
const existing = this.files.getByPath(relativePath);
|
|
1001
|
+
if (existing && existing.contentHash === contentHash && !existing.isDeleted) {
|
|
1002
|
+
return "skipped";
|
|
1003
|
+
}
|
|
1004
|
+
const isNew = !existing;
|
|
1005
|
+
const lines = content.split("\n").length;
|
|
1006
|
+
const langConfig = null;
|
|
1007
|
+
const fileId = this.files.upsert({
|
|
1008
|
+
path: relativePath,
|
|
1009
|
+
language: "unknown",
|
|
1010
|
+
contentHash,
|
|
1011
|
+
sizeBytes: stat.size,
|
|
1012
|
+
linesTotal: lines,
|
|
1013
|
+
indexedAt: Date.now(),
|
|
1014
|
+
isDeleted: false
|
|
1015
|
+
});
|
|
1016
|
+
const record = this.files.getByPath(relativePath);
|
|
1017
|
+
if (!record) return "skipped";
|
|
1018
|
+
this.symbols.deleteByFileId(record.id);
|
|
1019
|
+
return isNew ? "added" : "updated";
|
|
1020
|
+
}
|
|
1021
|
+
async parseAndIndex(absolutePath, relativePath) {
|
|
1022
|
+
const langConfig = getLanguageForExtension(extensionFromPath(relativePath));
|
|
1023
|
+
if (!langConfig) return;
|
|
1024
|
+
let content;
|
|
1025
|
+
try {
|
|
1026
|
+
content = readFileSync2(absolutePath, "utf8");
|
|
1027
|
+
} catch {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
this.parser.setLanguage(langConfig.language);
|
|
1031
|
+
const tree = this.parser.parse(content);
|
|
1032
|
+
const record = this.files.getByPath(relativePath);
|
|
1033
|
+
if (!record) return;
|
|
1034
|
+
this.files.upsert({ ...record, language: langConfig.name });
|
|
1035
|
+
const symbols = extractSymbols(tree, relativePath, record.id, langConfig.name, content);
|
|
1036
|
+
const imports = extractImports(tree, relativePath, record.id, langConfig.name);
|
|
1037
|
+
this.symbols.deleteByFileId(record.id);
|
|
1038
|
+
if (symbols.length > 0) this.symbols.insertSymbols(record.id, symbols);
|
|
1039
|
+
if (imports.length > 0) this.symbols.insertImports(record.id, imports);
|
|
1040
|
+
}
|
|
1041
|
+
async runFullWithParsing(onProgress) {
|
|
1042
|
+
const start = Date.now();
|
|
1043
|
+
const allFiles = await walkFiles(this.repoRoot, this.config);
|
|
1044
|
+
const transaction = this.db.transaction(() => {
|
|
1045
|
+
for (const { absolutePath, relativePath } of allFiles) {
|
|
1046
|
+
this.indexFile(absolutePath, relativePath);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
transaction();
|
|
1050
|
+
const parseable = allFiles.filter((f) => {
|
|
1051
|
+
const ext = extensionFromPath(f.relativePath);
|
|
1052
|
+
return ext !== "";
|
|
1053
|
+
});
|
|
1054
|
+
let done = 0;
|
|
1055
|
+
for (const { absolutePath, relativePath } of parseable) {
|
|
1056
|
+
await this.parseAndIndex(absolutePath, relativePath);
|
|
1057
|
+
done++;
|
|
1058
|
+
onProgress?.(done, parseable.length);
|
|
1059
|
+
}
|
|
1060
|
+
const commit = getHeadCommit(this.repoRoot);
|
|
1061
|
+
this.meta.saveIndexMeta(commit, this.repoRoot);
|
|
1062
|
+
const metaInfo = this.meta.getIndexMeta();
|
|
1063
|
+
return {
|
|
1064
|
+
filesAdded: parseable.length,
|
|
1065
|
+
filesUpdated: 0,
|
|
1066
|
+
filesDeleted: 0,
|
|
1067
|
+
filesSkipped: allFiles.length - parseable.length,
|
|
1068
|
+
symbolsTotal: metaInfo.totalSymbols,
|
|
1069
|
+
durationMs: Date.now() - start,
|
|
1070
|
+
mode: "full"
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
async runIncrementalWithParsing(onProgress) {
|
|
1074
|
+
const start = Date.now();
|
|
1075
|
+
const lastCommit = this.meta.get("last_indexed_commit") ?? "";
|
|
1076
|
+
const currentCommit = getHeadCommit(this.repoRoot);
|
|
1077
|
+
if (!lastCommit) return this.runFullWithParsing(onProgress);
|
|
1078
|
+
if (lastCommit === currentCommit) {
|
|
1079
|
+
const metaInfo2 = this.meta.getIndexMeta();
|
|
1080
|
+
return { filesAdded: 0, filesUpdated: 0, filesDeleted: 0, filesSkipped: 0, symbolsTotal: metaInfo2.totalSymbols, durationMs: 0, mode: "incremental" };
|
|
1081
|
+
}
|
|
1082
|
+
const changes = getChangedFiles(this.repoRoot, lastCommit);
|
|
1083
|
+
if (!changes) return this.runFullWithParsing(onProgress);
|
|
1084
|
+
let added = 0, updated = 0, deleted = 0;
|
|
1085
|
+
const toReparse = [];
|
|
1086
|
+
for (const change of changes) {
|
|
1087
|
+
const absPath = join2(this.repoRoot, change.path);
|
|
1088
|
+
if (change.status === "D") {
|
|
1089
|
+
const existing = this.files.getByPath(change.path);
|
|
1090
|
+
if (existing) {
|
|
1091
|
+
this.symbols.deleteByFileId(existing.id);
|
|
1092
|
+
this.files.markDeleted(change.path);
|
|
1093
|
+
deleted++;
|
|
1094
|
+
}
|
|
1095
|
+
} else {
|
|
1096
|
+
const result = this.indexFile(absPath, change.path);
|
|
1097
|
+
if (result !== "skipped") {
|
|
1098
|
+
toReparse.push({ abs: absPath, rel: change.path });
|
|
1099
|
+
if (result === "added") added++;
|
|
1100
|
+
else updated++;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
let done = 0;
|
|
1105
|
+
for (const { abs, rel } of toReparse) {
|
|
1106
|
+
await this.parseAndIndex(abs, rel);
|
|
1107
|
+
done++;
|
|
1108
|
+
onProgress?.(done, toReparse.length);
|
|
1109
|
+
}
|
|
1110
|
+
this.meta.saveIndexMeta(currentCommit, this.repoRoot);
|
|
1111
|
+
const metaInfo = this.meta.getIndexMeta();
|
|
1112
|
+
return {
|
|
1113
|
+
filesAdded: added,
|
|
1114
|
+
filesUpdated: updated,
|
|
1115
|
+
filesDeleted: deleted,
|
|
1116
|
+
filesSkipped: 0,
|
|
1117
|
+
symbolsTotal: metaInfo.totalSymbols,
|
|
1118
|
+
durationMs: Date.now() - start,
|
|
1119
|
+
mode: "incremental"
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// src/context/BudgetAllocator.ts
|
|
1125
|
+
function allocateBudget(totalTokens, config, hasFocusPath, layers) {
|
|
1126
|
+
const weights = { ...config.layerWeights };
|
|
1127
|
+
if (!hasFocusPath) {
|
|
1128
|
+
const focusWeight = weights.focus;
|
|
1129
|
+
delete weights.focus;
|
|
1130
|
+
const remaining = layers.filter((l) => l !== "focus");
|
|
1131
|
+
const total = remaining.reduce((s, l) => s + (weights[l] ?? 0), 0);
|
|
1132
|
+
for (const l of remaining) {
|
|
1133
|
+
weights[l] = (weights[l] ?? 0) / total * (1 - focusWeight / 1) + (l === "symbol_table" ? focusWeight : 0);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const activeLayers = hasFocusPath ? layers : layers.filter((l) => l !== "focus");
|
|
1137
|
+
const activeTotal = activeLayers.reduce((s, l) => s + (weights[l] ?? 0), 0);
|
|
1138
|
+
return activeLayers.map((layer) => ({
|
|
1139
|
+
layer,
|
|
1140
|
+
tokens: Math.floor(totalTokens * (weights[layer] ?? 0) / activeTotal)
|
|
1141
|
+
}));
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// src/context/TokenCounter.ts
|
|
1145
|
+
import { encode } from "gpt-tokenizer";
|
|
1146
|
+
function countTokens(text) {
|
|
1147
|
+
return encode(text).length;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/context/layers/RepoOverviewLayer.ts
|
|
1151
|
+
import { basename } from "path";
|
|
1152
|
+
function renderRepoOverview(db, budget) {
|
|
1153
|
+
const meta = new MetaRepository(db).getIndexMeta();
|
|
1154
|
+
const files = new FileRepository(db);
|
|
1155
|
+
const langStats = files.getLanguageStats();
|
|
1156
|
+
const repoName = basename(meta.repoRoot) || "unknown";
|
|
1157
|
+
const lastIndexed = meta.lastIndexedAt ? new Date(meta.lastIndexedAt).toISOString().slice(0, 10) : "never";
|
|
1158
|
+
const langSummary = langStats.slice(0, 8).map((l) => `${l.language}: ${l.count}`).join(", ");
|
|
1159
|
+
const content = [
|
|
1160
|
+
`# Repository: ${repoName}`,
|
|
1161
|
+
`Files: ${meta.totalFiles} | Symbols: ${meta.totalSymbols} | Last indexed: ${lastIndexed}`,
|
|
1162
|
+
`Languages: ${langSummary}`
|
|
1163
|
+
].join("\n");
|
|
1164
|
+
return { content, truncated: countTokens(content) > budget };
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/context/layers/DirectoryMapLayer.ts
|
|
1168
|
+
function renderTree(paths, maxLines) {
|
|
1169
|
+
const dirs = /* @__PURE__ */ new Map();
|
|
1170
|
+
for (const p of paths) {
|
|
1171
|
+
const slash = p.lastIndexOf("/");
|
|
1172
|
+
const dir = slash === -1 ? "." : p.slice(0, slash);
|
|
1173
|
+
if (!dirs.has(dir)) dirs.set(dir, []);
|
|
1174
|
+
dirs.get(dir).push(slash === -1 ? p : p.slice(slash + 1));
|
|
1175
|
+
}
|
|
1176
|
+
const lines = [];
|
|
1177
|
+
const topLevel = /* @__PURE__ */ new Map();
|
|
1178
|
+
for (const p of paths) {
|
|
1179
|
+
const top = p.split("/")[0];
|
|
1180
|
+
topLevel.set(top, (topLevel.get(top) ?? 0) + 1);
|
|
1181
|
+
}
|
|
1182
|
+
for (const [dir, count] of [...topLevel.entries()].sort()) {
|
|
1183
|
+
if (lines.length >= maxLines) {
|
|
1184
|
+
lines.push("...");
|
|
1185
|
+
break;
|
|
1186
|
+
}
|
|
1187
|
+
lines.push(`${dir}/ (${count} files)`);
|
|
1188
|
+
}
|
|
1189
|
+
return lines.join("\n");
|
|
1190
|
+
}
|
|
1191
|
+
function renderDirectoryMap(db, budget) {
|
|
1192
|
+
const allFiles = new FileRepository(db).getAllActive();
|
|
1193
|
+
const paths = allFiles.map((f) => f.path);
|
|
1194
|
+
const charsPerToken = 4;
|
|
1195
|
+
const maxLines = Math.floor(budget * charsPerToken / 30);
|
|
1196
|
+
const content = renderTree(paths, maxLines);
|
|
1197
|
+
return { content, truncated: countTokens(content) > budget };
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/context/layers/SymbolTableLayer.ts
|
|
1201
|
+
function renderSymbolTable(db, budget) {
|
|
1202
|
+
const repo = new SymbolRepository(db);
|
|
1203
|
+
const symbols = repo.getAllExported();
|
|
1204
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1205
|
+
for (const s of symbols) {
|
|
1206
|
+
if (!byFile.has(s.filePath)) byFile.set(s.filePath, []);
|
|
1207
|
+
byFile.get(s.filePath).push(s);
|
|
1208
|
+
}
|
|
1209
|
+
const sorted = [...byFile.entries()].sort((a, b) => b[1].length - a[1].length);
|
|
1210
|
+
const lines = [];
|
|
1211
|
+
let usedTokens = 0;
|
|
1212
|
+
let truncated = false;
|
|
1213
|
+
for (const [file, syms] of sorted) {
|
|
1214
|
+
const header = `### ${file}`;
|
|
1215
|
+
const symLines = syms.map((s) => {
|
|
1216
|
+
const sig = s.signature ? `${s.name}${s.signature}` : s.name;
|
|
1217
|
+
const doc = s.docComment ? ` \u2014 ${s.docComment}` : "";
|
|
1218
|
+
return ` ${s.kind} ${sig}${doc}`;
|
|
1219
|
+
});
|
|
1220
|
+
const block = [header, ...symLines].join("\n");
|
|
1221
|
+
const blockTokens = countTokens(block);
|
|
1222
|
+
if (usedTokens + blockTokens > budget) {
|
|
1223
|
+
truncated = true;
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
lines.push(block);
|
|
1227
|
+
usedTokens += blockTokens;
|
|
1228
|
+
}
|
|
1229
|
+
return { content: lines.join("\n\n"), truncated };
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// src/context/layers/ImportGraphLayer.ts
|
|
1233
|
+
function renderImportGraph(db, budget) {
|
|
1234
|
+
const repo = new SymbolRepository(db);
|
|
1235
|
+
const hubs = repo.getTopImportedFiles(30);
|
|
1236
|
+
const lines = ["### Most-imported modules"];
|
|
1237
|
+
let usedTokens = countTokens(lines[0]);
|
|
1238
|
+
let truncated = false;
|
|
1239
|
+
for (const { path, importerCount } of hubs) {
|
|
1240
|
+
const line = ` ${path} \u2190 imported by ${importerCount} files`;
|
|
1241
|
+
const t = countTokens(line);
|
|
1242
|
+
if (usedTokens + t > budget) {
|
|
1243
|
+
truncated = true;
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
lines.push(line);
|
|
1247
|
+
usedTokens += t;
|
|
1248
|
+
}
|
|
1249
|
+
return { content: lines.join("\n"), truncated };
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/context/layers/FocusLayer.ts
|
|
1253
|
+
function renderFocusLayer(db, focusPath, budget) {
|
|
1254
|
+
const symRepo = new SymbolRepository(db);
|
|
1255
|
+
const fileRepo = new FileRepository(db);
|
|
1256
|
+
const allFiles = fileRepo.getAllActive().filter((f) => f.path.startsWith(focusPath));
|
|
1257
|
+
const lines = [`### Focus: ${focusPath}`];
|
|
1258
|
+
let usedTokens = countTokens(lines[0]);
|
|
1259
|
+
let truncated = false;
|
|
1260
|
+
for (const file of allFiles) {
|
|
1261
|
+
const syms = symRepo.getExportedByFile(file.path);
|
|
1262
|
+
if (syms.length === 0) continue;
|
|
1263
|
+
const importers = symRepo.getImportersOf(file.path);
|
|
1264
|
+
const fileLines = [` ${file.path}:`];
|
|
1265
|
+
for (const s of syms) {
|
|
1266
|
+
const sig = s.signature ? `${s.name}${s.signature}` : s.name;
|
|
1267
|
+
fileLines.push(` ${s.kind} ${sig}`);
|
|
1268
|
+
}
|
|
1269
|
+
if (importers.length > 0) {
|
|
1270
|
+
fileLines.push(` \u2190 imported by: ${importers.slice(0, 3).join(", ")}${importers.length > 3 ? ` +${importers.length - 3} more` : ""}`);
|
|
1271
|
+
}
|
|
1272
|
+
const block = fileLines.join("\n");
|
|
1273
|
+
const t = countTokens(block);
|
|
1274
|
+
if (usedTokens + t > budget) {
|
|
1275
|
+
truncated = true;
|
|
1276
|
+
break;
|
|
1277
|
+
}
|
|
1278
|
+
lines.push(block);
|
|
1279
|
+
usedTokens += t;
|
|
1280
|
+
}
|
|
1281
|
+
return { content: lines.join("\n"), truncated };
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/context/ContextGenerator.ts
|
|
1285
|
+
var DEFAULT_LAYERS = ["repo_overview", "directory_map", "symbol_table", "import_graph", "focus"];
|
|
1286
|
+
function renderLayer(db, layer, budget, focusPath) {
|
|
1287
|
+
switch (layer) {
|
|
1288
|
+
case "repo_overview":
|
|
1289
|
+
return renderRepoOverview(db, budget);
|
|
1290
|
+
case "directory_map":
|
|
1291
|
+
return renderDirectoryMap(db, budget);
|
|
1292
|
+
case "symbol_table":
|
|
1293
|
+
return renderSymbolTable(db, budget);
|
|
1294
|
+
case "import_graph":
|
|
1295
|
+
return renderImportGraph(db, budget);
|
|
1296
|
+
case "focus":
|
|
1297
|
+
return focusPath ? renderFocusLayer(db, focusPath, budget) : { content: "", truncated: false };
|
|
1298
|
+
default:
|
|
1299
|
+
return { content: "", truncated: false };
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function wrapMarkdown(layers) {
|
|
1303
|
+
return layers.map((l) => l.content).filter(Boolean).join("\n\n");
|
|
1304
|
+
}
|
|
1305
|
+
function wrapXML(layers) {
|
|
1306
|
+
const inner = layers.filter((l) => l.content).map((l) => ` <${l.layer}>
|
|
1307
|
+
${l.content}
|
|
1308
|
+
</${l.layer}>`).join("\n");
|
|
1309
|
+
return `<codebase_context>
|
|
1310
|
+
${inner}
|
|
1311
|
+
</codebase_context>`;
|
|
1312
|
+
}
|
|
1313
|
+
function generateContext(db, request, config) {
|
|
1314
|
+
const layers = request.layers ?? DEFAULT_LAYERS;
|
|
1315
|
+
const hasFocus = Boolean(request.focusPath);
|
|
1316
|
+
const budgets = allocateBudget(request.budgetTokens, config, hasFocus, layers);
|
|
1317
|
+
let remainingSurplus = 0;
|
|
1318
|
+
const results = [];
|
|
1319
|
+
for (const { layer, tokens } of budgets) {
|
|
1320
|
+
const layerBudget = tokens + remainingSurplus;
|
|
1321
|
+
const { content, truncated } = renderLayer(db, layer, layerBudget, request.focusPath);
|
|
1322
|
+
const used = countTokens(content);
|
|
1323
|
+
remainingSurplus = truncated ? 0 : layerBudget - used;
|
|
1324
|
+
results.push({ layer, tokensUsed: used, content, truncated });
|
|
1325
|
+
}
|
|
1326
|
+
const rendered = request.format === "xml" ? wrapXML(results) : wrapMarkdown(results);
|
|
1327
|
+
const totalTokens = countTokens(rendered);
|
|
1328
|
+
return {
|
|
1329
|
+
totalTokens,
|
|
1330
|
+
budgetTokens: request.budgetTokens,
|
|
1331
|
+
layers: results,
|
|
1332
|
+
rendered
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
export {
|
|
1336
|
+
DEFAULT_CONFIG,
|
|
1337
|
+
FileRepository,
|
|
1338
|
+
Indexer,
|
|
1339
|
+
MetaRepository,
|
|
1340
|
+
SymbolRepository,
|
|
1341
|
+
generateContext,
|
|
1342
|
+
openDatabase
|
|
1343
|
+
};
|
|
1344
|
+
//# sourceMappingURL=index.js.map
|