project-librarian 0.4.0 → 0.4.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.ko.md +49 -9
- package/README.md +44 -4
- package/dist/agent-surfaces.js +67 -0
- package/dist/args.js +94 -89
- package/dist/code-index/evidence.js +79 -0
- package/dist/code-index/extractors/config.js +58 -0
- package/dist/code-index/extractors/light-languages.js +52 -0
- package/dist/code-index/extractors/registry.js +137 -0
- package/dist/code-index/extractors/shared.js +36 -0
- package/dist/code-index/extractors/tree-sitter.js +319 -0
- package/dist/code-index/extractors/types.js +2 -0
- package/dist/code-index/extractors/typescript.js +211 -0
- package/dist/code-index/incremental.js +16 -0
- package/dist/code-index/index-health.js +164 -0
- package/dist/code-index/modes.js +309 -0
- package/dist/code-index/ownership.js +183 -0
- package/dist/code-index/reports.js +322 -0
- package/dist/code-index/schema.js +196 -0
- package/dist/code-index/search.js +153 -0
- package/dist/code-index-file-policy.js +21 -27
- package/dist/code-index.js +167 -1850
- package/dist/init-project-wiki.js +105 -66
- package/dist/install-skill.js +3 -3
- package/dist/mcp-server.js +60 -6
- package/dist/migration.js +10 -6
- package/dist/modes.js +121 -63
- package/dist/path-ignore-policy.js +30 -0
- package/dist/wiki-corpus.js +25 -0
- package/dist/wiki-diagnostics.js +19 -0
- package/dist/wiki-files.js +2 -1
- package/dist/wiki-graph.js +1 -2
- package/package.json +15 -3
package/dist/code-index.js
CHANGED
|
@@ -33,25 +33,17 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.CodeEvidenceIndexUnavailableError = exports.codeContextPackTruncationNotice = exports.codeContextPackCharCap = void 0;
|
|
36
|
+
exports.CodeEvidenceIndexUnavailableError = exports.codeContextPackTruncationNotice = exports.codeContextPackCharCap = exports.codeIndexSnapshot = exports.workspaceSummary = exports.workspaceDependencyGraph = exports.searchSymbols = exports.ownershipInfo = exports.ownershipContext = exports.matchedCodeownerRules = exports.evidenceCoverage = exports.codeownerRules = void 0;
|
|
37
37
|
exports.codeIndexStaleness = codeIndexStaleness;
|
|
38
|
-
exports.
|
|
39
|
-
exports.matchedCodeownerRules = matchedCodeownerRules;
|
|
40
|
-
exports.ownershipContext = ownershipContext;
|
|
41
|
-
exports.ownershipInfo = ownershipInfo;
|
|
42
|
-
exports.evidenceCoverage = evidenceCoverage;
|
|
43
|
-
exports.workspaceSummary = workspaceSummary;
|
|
44
|
-
exports.workspaceDependencyGraph = workspaceDependencyGraph;
|
|
45
|
-
exports.codeReportMetadata = codeReportMetadata;
|
|
46
|
-
exports.searchSymbols = searchSymbols;
|
|
38
|
+
exports.codeIndexHealth = codeIndexHealth;
|
|
47
39
|
exports.codeImpact = codeImpact;
|
|
48
40
|
exports.codeContextPack = codeContextPack;
|
|
49
|
-
exports.codeIndexSnapshot = codeIndexSnapshot;
|
|
50
41
|
exports.openCodeEvidenceDatabaseForServing = openCodeEvidenceDatabaseForServing;
|
|
51
42
|
exports.runCodeIndexMode = runCodeIndexMode;
|
|
52
43
|
exports.runCodeQueryMode = runCodeQueryMode;
|
|
53
44
|
exports.runCodeReportMode = runCodeReportMode;
|
|
54
45
|
exports.runCodeStatusMode = runCodeStatusMode;
|
|
46
|
+
exports.runCodeIndexHealthMode = runCodeIndexHealthMode;
|
|
55
47
|
exports.runCodeFilesMode = runCodeFilesMode;
|
|
56
48
|
exports.runCodeImpactMode = runCodeImpactMode;
|
|
57
49
|
exports.runCodeContextPackMode = runCodeContextPackMode;
|
|
@@ -61,64 +53,30 @@ exports.isCodeEvidenceModeFor = isCodeEvidenceModeFor;
|
|
|
61
53
|
const crypto = __importStar(require("node:crypto"));
|
|
62
54
|
const fs = __importStar(require("node:fs"));
|
|
63
55
|
const path = __importStar(require("node:path"));
|
|
64
|
-
const ts = __importStar(require("typescript"));
|
|
65
56
|
const args_1 = require("./args");
|
|
66
57
|
const code_index_db_1 = require("./code-index-db");
|
|
67
58
|
const code_index_file_policy_1 = require("./code-index-file-policy");
|
|
68
|
-
const
|
|
59
|
+
const evidence_1 = require("./code-index/evidence");
|
|
60
|
+
const registry_1 = require("./code-index/extractors/registry");
|
|
61
|
+
const shared_1 = require("./code-index/extractors/shared");
|
|
62
|
+
const index_health_1 = require("./code-index/index-health");
|
|
63
|
+
const modes_1 = require("./code-index/modes");
|
|
64
|
+
const ownership_1 = require("./code-index/ownership");
|
|
65
|
+
Object.defineProperty(exports, "codeownerRules", { enumerable: true, get: function () { return ownership_1.codeownerRules; } });
|
|
66
|
+
Object.defineProperty(exports, "matchedCodeownerRules", { enumerable: true, get: function () { return ownership_1.matchedCodeownerRules; } });
|
|
67
|
+
Object.defineProperty(exports, "ownershipContext", { enumerable: true, get: function () { return ownership_1.ownershipContext; } });
|
|
68
|
+
Object.defineProperty(exports, "ownershipInfo", { enumerable: true, get: function () { return ownership_1.ownershipInfo; } });
|
|
69
|
+
const reports_1 = require("./code-index/reports");
|
|
70
|
+
Object.defineProperty(exports, "evidenceCoverage", { enumerable: true, get: function () { return reports_1.evidenceCoverage; } });
|
|
71
|
+
Object.defineProperty(exports, "workspaceDependencyGraph", { enumerable: true, get: function () { return reports_1.workspaceDependencyGraph; } });
|
|
72
|
+
Object.defineProperty(exports, "workspaceSummary", { enumerable: true, get: function () { return reports_1.workspaceSummary; } });
|
|
73
|
+
const schema_1 = require("./code-index/schema");
|
|
74
|
+
Object.defineProperty(exports, "codeIndexSnapshot", { enumerable: true, get: function () { return schema_1.codeIndexSnapshot; } });
|
|
75
|
+
const search_1 = require("./code-index/search");
|
|
76
|
+
Object.defineProperty(exports, "searchSymbols", { enumerable: true, get: function () { return search_1.searchSymbols; } });
|
|
69
77
|
const workspace_1 = require("./workspace");
|
|
70
78
|
exports.codeContextPackCharCap = 4000;
|
|
71
79
|
exports.codeContextPackTruncationNotice = "[truncated - refine the query]";
|
|
72
|
-
const codeIndexSchemaVersion = "3";
|
|
73
|
-
const httpMethods = new Set(["all", "delete", "get", "patch", "post", "put"]);
|
|
74
|
-
const treeSitterGrammarPackages = {
|
|
75
|
-
"tree-sitter-c": "@sengac/tree-sitter-c",
|
|
76
|
-
"tree-sitter-cpp": "@sengac/tree-sitter-cpp",
|
|
77
|
-
"tree-sitter-csharp": "@sengac/tree-sitter-c-sharp",
|
|
78
|
-
"tree-sitter-go": "@sengac/tree-sitter-go",
|
|
79
|
-
"tree-sitter-java": "@sengac/tree-sitter-java",
|
|
80
|
-
"tree-sitter-javascript": "@sengac/tree-sitter-javascript",
|
|
81
|
-
"tree-sitter-kotlin": "@sengac/tree-sitter-kotlin",
|
|
82
|
-
"tree-sitter-php": "@sengac/tree-sitter-php",
|
|
83
|
-
"tree-sitter-python": "@sengac/tree-sitter-python",
|
|
84
|
-
"tree-sitter-rust": "@sengac/tree-sitter-rust",
|
|
85
|
-
"tree-sitter-swift": "@sengac/tree-sitter-swift",
|
|
86
|
-
};
|
|
87
|
-
const codeReportSectionAliases = {
|
|
88
|
-
config: "configs",
|
|
89
|
-
configs: "configs",
|
|
90
|
-
coverage: "coverage",
|
|
91
|
-
dependencies: "hotspots",
|
|
92
|
-
dependency: "hotspots",
|
|
93
|
-
dependency_hotspots: "hotspots",
|
|
94
|
-
edge: "edges",
|
|
95
|
-
edge_summary: "edges",
|
|
96
|
-
edges: "edges",
|
|
97
|
-
evidence: "coverage",
|
|
98
|
-
evidence_coverage: "coverage",
|
|
99
|
-
hotspot: "hotspots",
|
|
100
|
-
hotspots: "hotspots",
|
|
101
|
-
language: "languages",
|
|
102
|
-
language_profile_summary: "languages",
|
|
103
|
-
languages: "languages",
|
|
104
|
-
ownership: "ownership",
|
|
105
|
-
ownership_summary: "ownership",
|
|
106
|
-
parser: "parsers",
|
|
107
|
-
parser_backend_summary: "parsers",
|
|
108
|
-
parser_backends: "parsers",
|
|
109
|
-
parsers: "parsers",
|
|
110
|
-
route: "routes",
|
|
111
|
-
route_inventory: "routes",
|
|
112
|
-
routes: "routes",
|
|
113
|
-
workspace: "workspaces",
|
|
114
|
-
workspace_graph: "workspace-graph",
|
|
115
|
-
"workspace-graph": "workspace-graph",
|
|
116
|
-
workspacegraph: "workspace-graph",
|
|
117
|
-
monorepo: "workspace-graph",
|
|
118
|
-
monorepo_graph: "workspace-graph",
|
|
119
|
-
workspace_summary: "workspaces",
|
|
120
|
-
workspaces: "workspaces",
|
|
121
|
-
};
|
|
122
80
|
function fail(message) {
|
|
123
81
|
console.error(message);
|
|
124
82
|
process.exit(1);
|
|
@@ -152,907 +110,48 @@ function selectedCodeParserMode() {
|
|
|
152
110
|
return "tree-sitter";
|
|
153
111
|
fail(`invalid --code-parser: ${args_1.codeParser}; expected one of: default, tree-sitter`);
|
|
154
112
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
const language = (0, code_index_file_policy_1.fileLanguage)(relativePath);
|
|
158
|
-
if (language === "c")
|
|
159
|
-
return "tree-sitter-c";
|
|
160
|
-
if (language === "cpp")
|
|
161
|
-
return "tree-sitter-cpp";
|
|
162
|
-
if (language === "csharp")
|
|
163
|
-
return "tree-sitter-csharp";
|
|
164
|
-
if ([".js", ".jsx", ".cjs", ".mjs"].includes(extension))
|
|
165
|
-
return "tree-sitter-javascript";
|
|
166
|
-
if ([".ts", ".mts", ".cts"].includes(extension))
|
|
167
|
-
return "tree-sitter-typescript";
|
|
168
|
-
if (extension === ".tsx")
|
|
169
|
-
return "tree-sitter-tsx";
|
|
170
|
-
if (language === "java")
|
|
171
|
-
return "tree-sitter-java";
|
|
172
|
-
if (language === "kotlin")
|
|
173
|
-
return "tree-sitter-kotlin";
|
|
174
|
-
if (language === "php")
|
|
175
|
-
return "tree-sitter-php";
|
|
176
|
-
if (language === "python")
|
|
177
|
-
return "tree-sitter-python";
|
|
178
|
-
if (language === "go")
|
|
179
|
-
return "tree-sitter-go";
|
|
180
|
-
if (language === "rust")
|
|
181
|
-
return "tree-sitter-rust";
|
|
182
|
-
if (language === "swift")
|
|
183
|
-
return "tree-sitter-swift";
|
|
184
|
-
if (language === "config")
|
|
185
|
-
return "config";
|
|
186
|
-
return "inventory-only";
|
|
113
|
+
function normalizedMtimeMs(stat) {
|
|
114
|
+
return Number(stat.mtimeMs.toFixed(3));
|
|
187
115
|
}
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if ((0, code_index_file_policy_1.fileLanguage)(relativePath) === "go")
|
|
196
|
-
return "go-light";
|
|
197
|
-
if ((0, code_index_file_policy_1.fileLanguage)(relativePath) === "config")
|
|
198
|
-
return "config";
|
|
199
|
-
return "inventory-only";
|
|
116
|
+
function readCodeFileFingerprint(relativePath) {
|
|
117
|
+
const stat = fs.statSync((0, workspace_1.abs)(relativePath));
|
|
118
|
+
return {
|
|
119
|
+
mtimeMs: normalizedMtimeMs(stat),
|
|
120
|
+
path: relativePath,
|
|
121
|
+
size: stat.size,
|
|
122
|
+
};
|
|
200
123
|
}
|
|
201
124
|
function readCodeFile(relativePath, parserMode = "default") {
|
|
202
125
|
const text = fs.readFileSync((0, workspace_1.abs)(relativePath), "utf8");
|
|
126
|
+
const fingerprint = readCodeFileFingerprint(relativePath);
|
|
127
|
+
const language = (0, code_index_file_policy_1.fileLanguage)(relativePath) || "config";
|
|
203
128
|
return {
|
|
204
|
-
bytes:
|
|
129
|
+
bytes: fingerprint.size,
|
|
205
130
|
hash: crypto.createHash("sha256").update(text).digest("hex"),
|
|
206
|
-
language
|
|
131
|
+
language,
|
|
207
132
|
lines: text.length === 0 ? 0 : text.split(/\r?\n/).length,
|
|
133
|
+
mtimeMs: fingerprint.mtimeMs,
|
|
208
134
|
path: relativePath,
|
|
209
|
-
profile: extractionProfile(relativePath, parserMode),
|
|
135
|
+
profile: (0, registry_1.extractionProfile)(relativePath, language, parserMode),
|
|
136
|
+
size: fingerprint.size,
|
|
210
137
|
text,
|
|
211
138
|
};
|
|
212
139
|
}
|
|
213
|
-
|
|
214
|
-
return text.slice(0, index).split(/\r?\n/).length;
|
|
215
|
-
}
|
|
216
|
-
function scriptKindForPath(relativePath) {
|
|
217
|
-
const extension = path.extname(relativePath).toLowerCase();
|
|
218
|
-
if (extension === ".tsx")
|
|
219
|
-
return ts.ScriptKind.TSX;
|
|
220
|
-
if (extension === ".jsx")
|
|
221
|
-
return ts.ScriptKind.JSX;
|
|
222
|
-
if ([".ts", ".mts", ".cts"].includes(extension))
|
|
223
|
-
return ts.ScriptKind.TS;
|
|
224
|
-
return ts.ScriptKind.JS;
|
|
225
|
-
}
|
|
226
|
-
function setupDatabase(database) {
|
|
227
|
-
database.exec(`
|
|
228
|
-
PRAGMA journal_mode = WAL;
|
|
229
|
-
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
230
|
-
CREATE TABLE files (
|
|
231
|
-
path TEXT PRIMARY KEY,
|
|
232
|
-
language TEXT NOT NULL,
|
|
233
|
-
profile TEXT NOT NULL,
|
|
234
|
-
kind TEXT NOT NULL,
|
|
235
|
-
bytes INTEGER NOT NULL,
|
|
236
|
-
lines INTEGER NOT NULL,
|
|
237
|
-
hash TEXT NOT NULL
|
|
238
|
-
);
|
|
239
|
-
CREATE TABLE symbols (
|
|
240
|
-
id INTEGER PRIMARY KEY,
|
|
241
|
-
name TEXT NOT NULL,
|
|
242
|
-
kind TEXT NOT NULL,
|
|
243
|
-
file_path TEXT NOT NULL,
|
|
244
|
-
line INTEGER NOT NULL,
|
|
245
|
-
signature TEXT NOT NULL
|
|
246
|
-
);
|
|
247
|
-
CREATE TABLE imports (
|
|
248
|
-
id INTEGER PRIMARY KEY,
|
|
249
|
-
from_file TEXT NOT NULL,
|
|
250
|
-
to_ref TEXT NOT NULL,
|
|
251
|
-
imported TEXT NOT NULL,
|
|
252
|
-
line INTEGER NOT NULL,
|
|
253
|
-
raw TEXT NOT NULL
|
|
254
|
-
);
|
|
255
|
-
CREATE TABLE routes (
|
|
256
|
-
id INTEGER PRIMARY KEY,
|
|
257
|
-
method TEXT NOT NULL,
|
|
258
|
-
route TEXT NOT NULL,
|
|
259
|
-
file_path TEXT NOT NULL,
|
|
260
|
-
line INTEGER NOT NULL,
|
|
261
|
-
handler TEXT NOT NULL
|
|
262
|
-
);
|
|
263
|
-
CREATE TABLE configs (
|
|
264
|
-
id INTEGER PRIMARY KEY,
|
|
265
|
-
key TEXT NOT NULL,
|
|
266
|
-
value TEXT NOT NULL,
|
|
267
|
-
file_path TEXT NOT NULL,
|
|
268
|
-
line INTEGER NOT NULL
|
|
269
|
-
);
|
|
270
|
-
CREATE TABLE edges (
|
|
271
|
-
id INTEGER PRIMARY KEY,
|
|
272
|
-
kind TEXT NOT NULL,
|
|
273
|
-
source_kind TEXT NOT NULL,
|
|
274
|
-
source TEXT NOT NULL,
|
|
275
|
-
target_kind TEXT NOT NULL,
|
|
276
|
-
target TEXT NOT NULL,
|
|
277
|
-
file_path TEXT NOT NULL,
|
|
278
|
-
line INTEGER NOT NULL,
|
|
279
|
-
evidence TEXT NOT NULL
|
|
280
|
-
);
|
|
281
|
-
CREATE VIRTUAL TABLE files_fts USING fts5(path, language, profile, content);
|
|
282
|
-
CREATE VIRTUAL TABLE symbols_fts USING fts5(name, kind, file_path, signature);
|
|
283
|
-
CREATE INDEX idx_symbols_file ON symbols(file_path);
|
|
284
|
-
CREATE INDEX idx_symbols_name ON symbols(name);
|
|
285
|
-
CREATE INDEX idx_imports_from ON imports(from_file);
|
|
286
|
-
CREATE INDEX idx_routes_path ON routes(route);
|
|
287
|
-
CREATE INDEX idx_configs_file ON configs(file_path);
|
|
288
|
-
CREATE INDEX idx_edges_source ON edges(source_kind, source);
|
|
289
|
-
CREATE INDEX idx_edges_target ON edges(target_kind, target);
|
|
290
|
-
CREATE INDEX idx_edges_kind ON edges(kind);
|
|
291
|
-
`);
|
|
292
|
-
}
|
|
293
|
-
function createIndexStatements(database) {
|
|
294
|
-
return {
|
|
295
|
-
deleteConfig: database.prepare("DELETE FROM configs WHERE file_path = ?"),
|
|
296
|
-
deleteEdge: database.prepare("DELETE FROM edges WHERE file_path = ?"),
|
|
297
|
-
deleteFile: database.prepare("DELETE FROM files WHERE path = ?"),
|
|
298
|
-
deleteFileFts: database.prepare("DELETE FROM files_fts WHERE path = ?"),
|
|
299
|
-
deleteImport: database.prepare("DELETE FROM imports WHERE from_file = ?"),
|
|
300
|
-
deleteRoute: database.prepare("DELETE FROM routes WHERE file_path = ?"),
|
|
301
|
-
deleteSymbol: database.prepare("DELETE FROM symbols WHERE file_path = ?"),
|
|
302
|
-
deleteSymbolFts: database.prepare("DELETE FROM symbols_fts WHERE file_path = ?"),
|
|
303
|
-
insertConfig: database.prepare("INSERT INTO configs (key, value, file_path, line) VALUES (?, ?, ?, ?)"),
|
|
304
|
-
insertEdge: database.prepare("INSERT INTO edges (kind, source_kind, source, target_kind, target, file_path, line, evidence) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"),
|
|
305
|
-
insertFile: database.prepare("INSERT INTO files (path, language, profile, kind, bytes, lines, hash) VALUES (?, ?, ?, ?, ?, ?, ?)"),
|
|
306
|
-
insertFileFts: database.prepare("INSERT INTO files_fts (path, language, profile, content) VALUES (?, ?, ?, ?)"),
|
|
307
|
-
insertImport: database.prepare("INSERT INTO imports (from_file, to_ref, imported, line, raw) VALUES (?, ?, ?, ?, ?)"),
|
|
308
|
-
insertMeta: database.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"),
|
|
309
|
-
insertRoute: database.prepare("INSERT INTO routes (method, route, file_path, line, handler) VALUES (?, ?, ?, ?, ?)"),
|
|
310
|
-
insertSymbol: database.prepare("INSERT INTO symbols (name, kind, file_path, line, signature) VALUES (?, ?, ?, ?, ?)"),
|
|
311
|
-
insertSymbolFts: database.prepare("INSERT INTO symbols_fts (name, kind, file_path, signature) VALUES (?, ?, ?, ?)"),
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
function removeIndexedFile(filePath, statements) {
|
|
315
|
-
statements.deleteConfig.run(filePath);
|
|
316
|
-
statements.deleteEdge.run(filePath);
|
|
317
|
-
statements.deleteImport.run(filePath);
|
|
318
|
-
statements.deleteRoute.run(filePath);
|
|
319
|
-
statements.deleteSymbol.run(filePath);
|
|
320
|
-
statements.deleteSymbolFts.run(filePath);
|
|
321
|
-
statements.deleteFileFts.run(filePath);
|
|
322
|
-
statements.deleteFile.run(filePath);
|
|
323
|
-
}
|
|
324
|
-
const treeSitterParsers = new Map();
|
|
325
|
-
function requireTreeSitterPackage(packageName) {
|
|
326
|
-
try {
|
|
327
|
-
return require(packageName);
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
331
|
-
fail(`--code-parser tree-sitter requires optional package ${packageName}; install project optional dependencies with npm install. Error: ${message}`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function treeSitterGrammarForProfile(profile) {
|
|
335
|
-
if (profile === "tree-sitter-typescript" || profile === "tree-sitter-tsx") {
|
|
336
|
-
const grammars = requireTreeSitterPackage("@sengac/tree-sitter-typescript");
|
|
337
|
-
const grammar = profile === "tree-sitter-tsx" ? grammars.tsx : grammars.typescript;
|
|
338
|
-
if (!grammar)
|
|
339
|
-
fail(`tree-sitter-typescript did not expose the expected ${profile === "tree-sitter-tsx" ? "tsx" : "typescript"} grammar`);
|
|
340
|
-
return grammar;
|
|
341
|
-
}
|
|
342
|
-
const packageName = treeSitterGrammarPackages[profile];
|
|
343
|
-
if (packageName) {
|
|
344
|
-
const grammarModule = requireTreeSitterPackage(packageName);
|
|
345
|
-
const grammar = profile === "tree-sitter-php"
|
|
346
|
-
? grammarModule.php ?? grammarModule.php_only
|
|
347
|
-
: grammarModule;
|
|
348
|
-
if (!grammar)
|
|
349
|
-
fail(`${packageName} did not expose a Tree-sitter grammar for ${profile}`);
|
|
350
|
-
return grammar;
|
|
351
|
-
}
|
|
352
|
-
fail(`missing Tree-sitter grammar for profile: ${profile}`);
|
|
353
|
-
}
|
|
354
|
-
function treeSitterParserForProfile(profile) {
|
|
355
|
-
const cached = treeSitterParsers.get(profile);
|
|
356
|
-
if (cached)
|
|
357
|
-
return cached;
|
|
358
|
-
const Parser = requireTreeSitterPackage("@sengac/tree-sitter");
|
|
359
|
-
const parser = new Parser();
|
|
360
|
-
parser.setLanguage(treeSitterGrammarForProfile(profile));
|
|
361
|
-
treeSitterParsers.set(profile, parser);
|
|
362
|
-
return parser;
|
|
363
|
-
}
|
|
364
|
-
function treeSitterLine(node) {
|
|
365
|
-
return node.startPosition.row + 1;
|
|
366
|
-
}
|
|
367
|
-
function treeSitterFieldText(node, fieldName) {
|
|
368
|
-
return node.childForFieldName(fieldName)?.text ?? "";
|
|
369
|
-
}
|
|
370
|
-
function treeSitterSignature(node) {
|
|
371
|
-
return oneLine(node.text);
|
|
372
|
-
}
|
|
373
|
-
function unquoteLiteral(text) {
|
|
374
|
-
const trimmed = text.trim();
|
|
375
|
-
if (trimmed.length >= 2) {
|
|
376
|
-
const first = trimmed[0];
|
|
377
|
-
const last = trimmed[trimmed.length - 1];
|
|
378
|
-
if ((first === "\"" && last === "\"") || (first === "'" && last === "'") || (first === "`" && last === "`"))
|
|
379
|
-
return trimmed.slice(1, -1);
|
|
380
|
-
}
|
|
381
|
-
return trimmed;
|
|
382
|
-
}
|
|
383
|
-
function forEachNamedTreeSitterNode(node, visit) {
|
|
384
|
-
visit(node);
|
|
385
|
-
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
386
|
-
const child = node.namedChild(index);
|
|
387
|
-
if (child)
|
|
388
|
-
forEachNamedTreeSitterNode(child, visit);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
function firstNamedChildOfType(node, type) {
|
|
392
|
-
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
393
|
-
const child = node.namedChild(index);
|
|
394
|
-
if (child?.type === type)
|
|
395
|
-
return child;
|
|
396
|
-
}
|
|
397
|
-
return null;
|
|
398
|
-
}
|
|
399
|
-
function treeSitterModuleSpecifier(node) {
|
|
400
|
-
const source = treeSitterFieldText(node, "source");
|
|
401
|
-
if (source)
|
|
402
|
-
return unquoteLiteral(source);
|
|
403
|
-
const raw = node.text;
|
|
404
|
-
return raw.match(/\bfrom\s*["'`]([^"'`]+)["'`]/)?.[1]
|
|
405
|
-
?? raw.match(/^\s*import\s*["'`]([^"'`]+)["'`]/)?.[1]
|
|
406
|
-
?? raw.match(/\brequire\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/)?.[1]
|
|
407
|
-
?? "";
|
|
408
|
-
}
|
|
409
|
-
function indexTreeSitterJavaScriptLike(file, statements) {
|
|
410
|
-
const tree = treeSitterParserForProfile(file.profile).parse(file.text);
|
|
411
|
-
function visit(node, context) {
|
|
412
|
-
let nextContext = context;
|
|
413
|
-
if (node.type === "function_declaration") {
|
|
414
|
-
const name = treeSitterFieldText(node, "name");
|
|
415
|
-
insertSymbol(statements, name, "function", file, treeSitterLine(node), treeSitterSignature(node));
|
|
416
|
-
if (name)
|
|
417
|
-
nextContext = name;
|
|
418
|
-
}
|
|
419
|
-
else if (node.type === "class_declaration") {
|
|
420
|
-
const name = treeSitterFieldText(node, "name");
|
|
421
|
-
insertSymbol(statements, name, "class", file, treeSitterLine(node), treeSitterSignature(node));
|
|
422
|
-
if (name)
|
|
423
|
-
nextContext = name;
|
|
424
|
-
}
|
|
425
|
-
else if (node.type === "method_definition" || node.type === "method_signature") {
|
|
426
|
-
const name = treeSitterFieldText(node, "name");
|
|
427
|
-
insertSymbol(statements, name, "method", file, treeSitterLine(node), treeSitterSignature(node));
|
|
428
|
-
if (name)
|
|
429
|
-
nextContext = name;
|
|
430
|
-
}
|
|
431
|
-
else if (node.type === "interface_declaration") {
|
|
432
|
-
insertSymbol(statements, treeSitterFieldText(node, "name"), "interface", file, treeSitterLine(node), treeSitterSignature(node));
|
|
433
|
-
}
|
|
434
|
-
else if (node.type === "type_alias_declaration") {
|
|
435
|
-
insertSymbol(statements, treeSitterFieldText(node, "name"), "type", file, treeSitterLine(node), treeSitterSignature(node));
|
|
436
|
-
}
|
|
437
|
-
else if (node.type === "enum_declaration") {
|
|
438
|
-
insertSymbol(statements, treeSitterFieldText(node, "name"), "enum", file, treeSitterLine(node), treeSitterSignature(node));
|
|
439
|
-
}
|
|
440
|
-
else if (node.type === "variable_declarator") {
|
|
441
|
-
const name = treeSitterFieldText(node, "name");
|
|
442
|
-
const valueType = node.childForFieldName("value")?.type ?? "";
|
|
443
|
-
const symbolKind = ["arrow_function", "function", "function_expression"].includes(valueType) ? "function" : "variable";
|
|
444
|
-
insertSymbol(statements, name, symbolKind, file, treeSitterLine(node), treeSitterSignature(node));
|
|
445
|
-
if (symbolKind === "function" && name)
|
|
446
|
-
nextContext = name;
|
|
447
|
-
}
|
|
448
|
-
else if (node.type === "import_statement" || node.type === "export_statement") {
|
|
449
|
-
const toRef = treeSitterModuleSpecifier(node);
|
|
450
|
-
if (toRef) {
|
|
451
|
-
const imported = node.text.match(/^\s*import\s+(.+?)\s+from\s*["'`]/)?.[1] ?? node.text.match(/^\s*export\s+(.+?)\s+from\s*["'`]/)?.[1] ?? "";
|
|
452
|
-
statements.insertImport.run(file.path, toRef, oneLine(imported), treeSitterLine(node), treeSitterSignature(node));
|
|
453
|
-
insertEdge(statements, node.type === "export_statement" ? "export" : "import", "file", file.path, "module", toRef, file, treeSitterLine(node), treeSitterSignature(node));
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
else if (node.type === "call_expression") {
|
|
457
|
-
const raw = node.text;
|
|
458
|
-
const routeMatch = raw.match(/^(?:app|router|server)\.(get|post|put|patch|delete|all)\s*\(\s*["'`]([^"'`]+)["'`]\s*,\s*([^,)]+)/);
|
|
459
|
-
if (routeMatch) {
|
|
460
|
-
const method = (routeMatch[1] ?? "").toUpperCase();
|
|
461
|
-
const route = routeMatch[2] ?? "";
|
|
462
|
-
const handler = oneLine(routeMatch[3] ?? "");
|
|
463
|
-
statements.insertRoute.run(method, route, file.path, treeSitterLine(node), handler);
|
|
464
|
-
insertEdge(statements, "route_to_handler", "route", `${method} ${route}`, "symbol", handler, file, treeSitterLine(node), treeSitterSignature(node));
|
|
465
|
-
}
|
|
466
|
-
const requireRef = treeSitterModuleSpecifier(node);
|
|
467
|
-
if (requireRef) {
|
|
468
|
-
statements.insertImport.run(file.path, requireRef, "", treeSitterLine(node), treeSitterSignature(node));
|
|
469
|
-
insertEdge(statements, "import", "file", file.path, "module", requireRef, file, treeSitterLine(node), treeSitterSignature(node));
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
const target = treeSitterFieldText(node, "function");
|
|
473
|
-
insertEdge(statements, "call", context ? "symbol" : "file", context || file.path, "symbol", target, file, treeSitterLine(node), treeSitterSignature(node));
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
477
|
-
const child = node.namedChild(index);
|
|
478
|
-
if (child)
|
|
479
|
-
visit(child, nextContext);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
visit(tree.rootNode, "");
|
|
483
|
-
}
|
|
484
|
-
function indexTreeSitterPython(file, statements) {
|
|
485
|
-
const tree = treeSitterParserForProfile(file.profile).parse(file.text);
|
|
486
|
-
forEachNamedTreeSitterNode(tree.rootNode, (node) => {
|
|
487
|
-
if (node.type === "function_definition") {
|
|
488
|
-
const name = treeSitterFieldText(node, "name");
|
|
489
|
-
insertSymbol(statements, name, "function", file, treeSitterLine(node), treeSitterSignature(node));
|
|
490
|
-
}
|
|
491
|
-
else if (node.type === "class_definition") {
|
|
492
|
-
const name = treeSitterFieldText(node, "name");
|
|
493
|
-
insertSymbol(statements, name, "class", file, treeSitterLine(node), treeSitterSignature(node));
|
|
494
|
-
}
|
|
495
|
-
else if (node.type === "import_statement" || node.type === "import_from_statement") {
|
|
496
|
-
const raw = node.text.trim();
|
|
497
|
-
const fromMatch = raw.match(/^from\s+([A-Za-z0-9_.$]+)\s+import\s+(.+)$/);
|
|
498
|
-
const importMatch = raw.match(/^import\s+(.+)$/);
|
|
499
|
-
const toRef = fromMatch?.[1] ?? importMatch?.[1] ?? "";
|
|
500
|
-
const imported = fromMatch?.[2] ?? "";
|
|
501
|
-
if (toRef) {
|
|
502
|
-
statements.insertImport.run(file.path, toRef, imported.trim(), treeSitterLine(node), raw);
|
|
503
|
-
insertEdge(statements, "import", "file", file.path, "module", toRef, file, treeSitterLine(node), raw);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
function indexTreeSitterGo(file, statements) {
|
|
509
|
-
const tree = treeSitterParserForProfile(file.profile).parse(file.text);
|
|
510
|
-
forEachNamedTreeSitterNode(tree.rootNode, (node) => {
|
|
511
|
-
if (node.type === "function_declaration") {
|
|
512
|
-
const name = treeSitterFieldText(node, "name");
|
|
513
|
-
insertSymbol(statements, name, "function", file, treeSitterLine(node), treeSitterSignature(node));
|
|
514
|
-
}
|
|
515
|
-
else if (node.type === "method_declaration") {
|
|
516
|
-
const name = treeSitterFieldText(node, "name");
|
|
517
|
-
insertSymbol(statements, name, "method", file, treeSitterLine(node), treeSitterSignature(node));
|
|
518
|
-
}
|
|
519
|
-
else if (node.type === "type_declaration") {
|
|
520
|
-
const spec = firstNamedChildOfType(node, "type_spec");
|
|
521
|
-
const name = spec ? treeSitterFieldText(spec, "name") : "";
|
|
522
|
-
insertSymbol(statements, name, "type", file, treeSitterLine(node), treeSitterSignature(node));
|
|
523
|
-
}
|
|
524
|
-
else if (node.type === "const_declaration" || node.type === "var_declaration") {
|
|
525
|
-
const spec = firstNamedChildOfType(node, node.type === "const_declaration" ? "const_spec" : "var_spec");
|
|
526
|
-
const name = spec ? treeSitterFieldText(spec, "name") : "";
|
|
527
|
-
insertSymbol(statements, name, node.type === "const_declaration" ? "constant" : "variable", file, treeSitterLine(node), treeSitterSignature(node));
|
|
528
|
-
}
|
|
529
|
-
else if (node.type === "import_spec") {
|
|
530
|
-
const toRef = unquoteLiteral(treeSitterFieldText(node, "path") || (node.text.match(/"([^"]+)"/)?.[1] ?? ""));
|
|
531
|
-
const imported = treeSitterFieldText(node, "name");
|
|
532
|
-
insertGoImport(file, statements, toRef, imported, treeSitterLine(node), treeSitterSignature(node));
|
|
533
|
-
}
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
function treeSitterGenericLanguage(file) {
|
|
537
|
-
const normalized = file.profile.replace(/^tree-sitter-/, "");
|
|
538
|
-
if (["c", "cpp", "csharp", "java", "kotlin", "php", "rust", "swift"].includes(normalized))
|
|
539
|
-
return normalized;
|
|
540
|
-
fail(`unsupported generic Tree-sitter profile: ${file.profile}`);
|
|
541
|
-
}
|
|
542
|
-
function symbolNameFromPatterns(text, patterns) {
|
|
543
|
-
for (const pattern of patterns) {
|
|
544
|
-
const match = text.match(pattern);
|
|
545
|
-
if (match?.[1])
|
|
546
|
-
return match[1];
|
|
547
|
-
}
|
|
548
|
-
return "";
|
|
549
|
-
}
|
|
550
|
-
function treeSitterGenericSymbol(node, language) {
|
|
551
|
-
const raw = node.text;
|
|
552
|
-
const fieldName = treeSitterFieldText(node, "name");
|
|
553
|
-
const patterns = {
|
|
554
|
-
c: [
|
|
555
|
-
[["function_definition"], "function", [/\b([A-Za-z_]\w*)\s*\([^;{}]*\)\s*\{/]],
|
|
556
|
-
[["struct_specifier"], "struct", [/\bstruct\s+([A-Za-z_]\w*)/]],
|
|
557
|
-
[["enum_specifier"], "enum", [/\benum\s+([A-Za-z_]\w*)/]],
|
|
558
|
-
],
|
|
559
|
-
cpp: [
|
|
560
|
-
[["function_definition"], "function", [/\b([A-Za-z_]\w*)\s*\([^;{}]*\)\s*(?:const\s*)?\{/]],
|
|
561
|
-
[["class_specifier"], "class", [/\bclass\s+([A-Za-z_]\w*)/, /\bstruct\s+([A-Za-z_]\w*)/]],
|
|
562
|
-
[["namespace_definition"], "namespace", [/\bnamespace\s+([A-Za-z_]\w*)/]],
|
|
563
|
-
[["enum_specifier"], "enum", [/\benum(?:\s+class)?\s+([A-Za-z_]\w*)/]],
|
|
564
|
-
],
|
|
565
|
-
csharp: [
|
|
566
|
-
[["method_declaration", "constructor_declaration"], "method", [/\b([A-Za-z_]\w*)\s*\(/]],
|
|
567
|
-
[["class_declaration"], "class", [/\bclass\s+([A-Za-z_]\w*)/]],
|
|
568
|
-
[["interface_declaration"], "interface", [/\binterface\s+([A-Za-z_]\w*)/]],
|
|
569
|
-
[["struct_declaration"], "struct", [/\bstruct\s+([A-Za-z_]\w*)/]],
|
|
570
|
-
[["enum_declaration"], "enum", [/\benum\s+([A-Za-z_]\w*)/]],
|
|
571
|
-
],
|
|
572
|
-
java: [
|
|
573
|
-
[["method_declaration", "constructor_declaration"], "method", [/\b([A-Za-z_]\w*)\s*\(/]],
|
|
574
|
-
[["class_declaration"], "class", [/\bclass\s+([A-Za-z_]\w*)/]],
|
|
575
|
-
[["interface_declaration"], "interface", [/\binterface\s+([A-Za-z_]\w*)/]],
|
|
576
|
-
[["enum_declaration"], "enum", [/\benum\s+([A-Za-z_]\w*)/]],
|
|
577
|
-
],
|
|
578
|
-
kotlin: [
|
|
579
|
-
[["function_declaration"], "function", [/\bfun\s+([A-Za-z_]\w*)/]],
|
|
580
|
-
[["class_declaration"], "class", [/\bclass\s+([A-Za-z_]\w*)/, /\binterface\s+([A-Za-z_]\w*)/]],
|
|
581
|
-
[["object_declaration"], "object", [/\bobject\s+([A-Za-z_]\w*)/]],
|
|
582
|
-
],
|
|
583
|
-
php: [
|
|
584
|
-
[["function_definition"], "function", [/\bfunction\s+([A-Za-z_]\w*)/]],
|
|
585
|
-
[["method_declaration"], "method", [/\bfunction\s+([A-Za-z_]\w*)/]],
|
|
586
|
-
[["class_declaration"], "class", [/\bclass\s+([A-Za-z_]\w*)/]],
|
|
587
|
-
[["interface_declaration"], "interface", [/\binterface\s+([A-Za-z_]\w*)/]],
|
|
588
|
-
[["trait_declaration"], "trait", [/\btrait\s+([A-Za-z_]\w*)/]],
|
|
589
|
-
],
|
|
590
|
-
rust: [
|
|
591
|
-
[["function_item"], "function", [/\bfn\s+([A-Za-z_]\w*)/]],
|
|
592
|
-
[["struct_item"], "struct", [/\bstruct\s+([A-Za-z_]\w*)/]],
|
|
593
|
-
[["enum_item"], "enum", [/\benum\s+([A-Za-z_]\w*)/]],
|
|
594
|
-
[["trait_item"], "trait", [/\btrait\s+([A-Za-z_]\w*)/]],
|
|
595
|
-
[["impl_item"], "impl", [/\bimpl(?:\s*<[^>]+>)?\s+([A-Za-z_]\w*)/]],
|
|
596
|
-
],
|
|
597
|
-
swift: [
|
|
598
|
-
[["function_declaration"], "function", [/\bfunc\s+([A-Za-z_]\w*)/]],
|
|
599
|
-
[["class_declaration"], "class", [/\bclass\s+([A-Za-z_]\w*)/]],
|
|
600
|
-
[["struct_declaration"], "struct", [/\bstruct\s+([A-Za-z_]\w*)/]],
|
|
601
|
-
[["protocol_declaration"], "protocol", [/\bprotocol\s+([A-Za-z_]\w*)/]],
|
|
602
|
-
[["enum_declaration"], "enum", [/\benum\s+([A-Za-z_]\w*)/]],
|
|
603
|
-
],
|
|
604
|
-
};
|
|
605
|
-
for (const [types, kind, regexes] of patterns[language] ?? []) {
|
|
606
|
-
if (!types.includes(node.type))
|
|
607
|
-
continue;
|
|
608
|
-
const name = fieldName || symbolNameFromPatterns(raw, regexes);
|
|
609
|
-
return name ? { kind, name } : null;
|
|
610
|
-
}
|
|
611
|
-
return null;
|
|
612
|
-
}
|
|
613
|
-
function treeSitterGenericImport(node, language) {
|
|
614
|
-
const raw = node.text.trim();
|
|
615
|
-
const importTypes = {
|
|
616
|
-
c: ["preproc_include"],
|
|
617
|
-
cpp: ["preproc_include", "using_declaration", "namespace_alias_definition"],
|
|
618
|
-
csharp: ["using_directive"],
|
|
619
|
-
java: ["import_declaration"],
|
|
620
|
-
kotlin: ["import_header"],
|
|
621
|
-
php: ["namespace_use_declaration"],
|
|
622
|
-
rust: ["use_declaration"],
|
|
623
|
-
swift: ["import_declaration"],
|
|
624
|
-
};
|
|
625
|
-
if (!(importTypes[language] ?? []).includes(node.type))
|
|
626
|
-
return null;
|
|
627
|
-
const toRef = raw.match(/#include\s*[<"]([^>"]+)[>"]/)?.[1]
|
|
628
|
-
?? raw.match(/\bimport\s+([A-Za-z0-9_.*.$\\/-]+)/)?.[1]
|
|
629
|
-
?? raw.match(/\busing\s+([A-Za-z0-9_.*.$\\/-]+)/)?.[1]
|
|
630
|
-
?? raw.match(/\buse\s+([A-Za-z0-9_:{}*,\s]+);?/)?.[1]?.replace(/\s+/g, " ").trim()
|
|
631
|
-
?? "";
|
|
632
|
-
return toRef ? { imported: "", kind: language === "rust" ? "use" : "import", toRef } : null;
|
|
633
|
-
}
|
|
634
|
-
function indexTreeSitterGeneric(file, statements) {
|
|
635
|
-
const language = treeSitterGenericLanguage(file);
|
|
636
|
-
const tree = treeSitterParserForProfile(file.profile).parse(file.text);
|
|
637
|
-
forEachNamedTreeSitterNode(tree.rootNode, (node) => {
|
|
638
|
-
const symbol = treeSitterGenericSymbol(node, language);
|
|
639
|
-
if (symbol)
|
|
640
|
-
insertSymbol(statements, symbol.name, symbol.kind, file, treeSitterLine(node), treeSitterSignature(node));
|
|
641
|
-
const imported = treeSitterGenericImport(node, language);
|
|
642
|
-
if (imported) {
|
|
643
|
-
statements.insertImport.run(file.path, imported.toRef, imported.imported, treeSitterLine(node), treeSitterSignature(node));
|
|
644
|
-
insertEdge(statements, imported.kind, "file", file.path, "module", imported.toRef, file, treeSitterLine(node), treeSitterSignature(node));
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
const extractionBackends = [
|
|
649
|
-
{
|
|
650
|
-
id: "typescript-compiler",
|
|
651
|
-
index: indexJavaScriptLike,
|
|
652
|
-
label: "TypeScript compiler API",
|
|
653
|
-
profile: "typescript-ast",
|
|
654
|
-
strength: "structural",
|
|
655
|
-
},
|
|
656
|
-
{
|
|
657
|
-
id: "regex-light",
|
|
658
|
-
index: indexPythonLight,
|
|
659
|
-
label: "Python lightweight regex",
|
|
660
|
-
profile: "python-light",
|
|
661
|
-
strength: "light",
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
id: "regex-light",
|
|
665
|
-
index: indexGoLight,
|
|
666
|
-
label: "Go lightweight regex",
|
|
667
|
-
profile: "go-light",
|
|
668
|
-
strength: "light",
|
|
669
|
-
},
|
|
670
|
-
{
|
|
671
|
-
id: "tree-sitter-javascript",
|
|
672
|
-
index: indexTreeSitterJavaScriptLike,
|
|
673
|
-
label: "Tree-sitter JavaScript grammar",
|
|
674
|
-
profile: "tree-sitter-javascript",
|
|
675
|
-
strength: "structural",
|
|
676
|
-
},
|
|
677
|
-
{
|
|
678
|
-
id: "tree-sitter-typescript",
|
|
679
|
-
index: indexTreeSitterJavaScriptLike,
|
|
680
|
-
label: "Tree-sitter TypeScript grammar",
|
|
681
|
-
profile: "tree-sitter-typescript",
|
|
682
|
-
strength: "structural",
|
|
683
|
-
},
|
|
684
|
-
{
|
|
685
|
-
id: "tree-sitter-typescript",
|
|
686
|
-
index: indexTreeSitterJavaScriptLike,
|
|
687
|
-
label: "Tree-sitter TSX grammar",
|
|
688
|
-
profile: "tree-sitter-tsx",
|
|
689
|
-
strength: "structural",
|
|
690
|
-
},
|
|
691
|
-
{
|
|
692
|
-
id: "tree-sitter-python",
|
|
693
|
-
index: indexTreeSitterPython,
|
|
694
|
-
label: "Tree-sitter Python grammar",
|
|
695
|
-
profile: "tree-sitter-python",
|
|
696
|
-
strength: "structural",
|
|
697
|
-
},
|
|
698
|
-
{
|
|
699
|
-
id: "tree-sitter-go",
|
|
700
|
-
index: indexTreeSitterGo,
|
|
701
|
-
label: "Tree-sitter Go grammar",
|
|
702
|
-
profile: "tree-sitter-go",
|
|
703
|
-
strength: "structural",
|
|
704
|
-
},
|
|
705
|
-
{
|
|
706
|
-
id: "tree-sitter-c",
|
|
707
|
-
index: indexTreeSitterGeneric,
|
|
708
|
-
label: "Tree-sitter C grammar",
|
|
709
|
-
profile: "tree-sitter-c",
|
|
710
|
-
strength: "structural",
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
id: "tree-sitter-cpp",
|
|
714
|
-
index: indexTreeSitterGeneric,
|
|
715
|
-
label: "Tree-sitter C++ grammar",
|
|
716
|
-
profile: "tree-sitter-cpp",
|
|
717
|
-
strength: "structural",
|
|
718
|
-
},
|
|
719
|
-
{
|
|
720
|
-
id: "tree-sitter-csharp",
|
|
721
|
-
index: indexTreeSitterGeneric,
|
|
722
|
-
label: "Tree-sitter C# grammar",
|
|
723
|
-
profile: "tree-sitter-csharp",
|
|
724
|
-
strength: "structural",
|
|
725
|
-
},
|
|
726
|
-
{
|
|
727
|
-
id: "tree-sitter-java",
|
|
728
|
-
index: indexTreeSitterGeneric,
|
|
729
|
-
label: "Tree-sitter Java grammar",
|
|
730
|
-
profile: "tree-sitter-java",
|
|
731
|
-
strength: "structural",
|
|
732
|
-
},
|
|
733
|
-
{
|
|
734
|
-
id: "tree-sitter-kotlin",
|
|
735
|
-
index: indexTreeSitterGeneric,
|
|
736
|
-
label: "Tree-sitter Kotlin grammar",
|
|
737
|
-
profile: "tree-sitter-kotlin",
|
|
738
|
-
strength: "structural",
|
|
739
|
-
},
|
|
740
|
-
{
|
|
741
|
-
id: "tree-sitter-php",
|
|
742
|
-
index: indexTreeSitterGeneric,
|
|
743
|
-
label: "Tree-sitter PHP grammar",
|
|
744
|
-
profile: "tree-sitter-php",
|
|
745
|
-
strength: "structural",
|
|
746
|
-
},
|
|
747
|
-
{
|
|
748
|
-
id: "tree-sitter-rust",
|
|
749
|
-
index: indexTreeSitterGeneric,
|
|
750
|
-
label: "Tree-sitter Rust grammar",
|
|
751
|
-
profile: "tree-sitter-rust",
|
|
752
|
-
strength: "structural",
|
|
753
|
-
},
|
|
754
|
-
{
|
|
755
|
-
id: "tree-sitter-swift",
|
|
756
|
-
index: indexTreeSitterGeneric,
|
|
757
|
-
label: "Tree-sitter Swift grammar",
|
|
758
|
-
profile: "tree-sitter-swift",
|
|
759
|
-
strength: "structural",
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
id: "config-key-value",
|
|
763
|
-
index: (file, statements) => indexConfigs(file, statements.insertConfig),
|
|
764
|
-
label: "Configuration key/value extractor",
|
|
765
|
-
profile: "config",
|
|
766
|
-
strength: "config",
|
|
767
|
-
},
|
|
768
|
-
{
|
|
769
|
-
id: "inventory-only",
|
|
770
|
-
index: () => undefined,
|
|
771
|
-
label: "Inventory-only file listing",
|
|
772
|
-
profile: "inventory-only",
|
|
773
|
-
strength: "inventory",
|
|
774
|
-
},
|
|
775
|
-
];
|
|
776
|
-
const extractionBackendsByProfile = new Map(extractionBackends.map((backend) => [backend.profile, backend]));
|
|
140
|
+
const extractionBackendRegistry = (0, registry_1.createExtractionBackendRegistry)(fail);
|
|
777
141
|
function extractionBackendForProfile(profile) {
|
|
778
|
-
|
|
779
|
-
if (!backend)
|
|
780
|
-
fail(`missing extraction backend for profile: ${profile}`);
|
|
781
|
-
return backend;
|
|
142
|
+
return extractionBackendRegistry.backendForProfile(profile);
|
|
782
143
|
}
|
|
783
144
|
function indexCodeFile(file, statements) {
|
|
784
|
-
statements.insertFile.run(file.path, file.language, file.profile, file.language === "config" ? "config" : "source", file.bytes, file.lines, file.hash);
|
|
145
|
+
statements.insertFile.run(file.path, file.language, file.profile, file.language === "config" ? "config" : "source", file.bytes, file.lines, file.hash, file.mtimeMs, file.size);
|
|
785
146
|
statements.insertFileFts.run(file.path, file.language, file.profile, file.text);
|
|
786
147
|
extractionBackendForProfile(file.profile).index(file, statements);
|
|
787
148
|
}
|
|
788
|
-
function writeIndexMetadata(scopes, parserMode, statements) {
|
|
789
|
-
statements.insertMeta.run("schema_version", codeIndexSchemaVersion);
|
|
790
|
-
statements.insertMeta.run("updated_at", new Date().toISOString());
|
|
791
|
-
statements.insertMeta.run("root", workspace_1.root);
|
|
792
|
-
statements.insertMeta.run("scopes", scopes.join(", "));
|
|
793
|
-
statements.insertMeta.run("scopes_json", JSON.stringify(scopes));
|
|
794
|
-
statements.insertMeta.run("parser_mode", parserMode);
|
|
795
|
-
statements.insertMeta.run("terminology", "code evidence index");
|
|
796
|
-
}
|
|
797
|
-
function oneLine(text) {
|
|
798
|
-
return text.replace(/\s+/g, " ").trim().slice(0, 240);
|
|
799
|
-
}
|
|
800
149
|
function printRows(rows) {
|
|
801
150
|
console.log(JSON.stringify(rows, null, 2));
|
|
802
151
|
}
|
|
803
152
|
function printJson(value) {
|
|
804
153
|
console.log(JSON.stringify(value, null, 2));
|
|
805
154
|
}
|
|
806
|
-
function tsLine(sourceFile, node) {
|
|
807
|
-
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
808
|
-
}
|
|
809
|
-
function nodeName(node, sourceFile) {
|
|
810
|
-
if (ts.isIdentifier(node))
|
|
811
|
-
return node.text;
|
|
812
|
-
if (ts.isStringLiteral(node) || ts.isNumericLiteral(node))
|
|
813
|
-
return node.text;
|
|
814
|
-
if (ts.isPrivateIdentifier(node))
|
|
815
|
-
return node.text;
|
|
816
|
-
return oneLine(node.getText(sourceFile));
|
|
817
|
-
}
|
|
818
|
-
function propertyNameText(name, sourceFile) {
|
|
819
|
-
if (!name)
|
|
820
|
-
return "";
|
|
821
|
-
return nodeName(name, sourceFile);
|
|
822
|
-
}
|
|
823
|
-
function callTarget(expression, sourceFile) {
|
|
824
|
-
if (ts.isIdentifier(expression))
|
|
825
|
-
return expression.text;
|
|
826
|
-
if (ts.isPropertyAccessExpression(expression))
|
|
827
|
-
return oneLine(expression.getText(sourceFile));
|
|
828
|
-
if (ts.isElementAccessExpression(expression))
|
|
829
|
-
return oneLine(expression.getText(sourceFile));
|
|
830
|
-
return oneLine(expression.getText(sourceFile));
|
|
831
|
-
}
|
|
832
|
-
function insertSymbol(statements, name, kind, file, line, signature) {
|
|
833
|
-
if (!name)
|
|
834
|
-
return;
|
|
835
|
-
statements.insertSymbol.run(name, kind, file.path, line, signature);
|
|
836
|
-
statements.insertSymbolFts.run(name, kind, file.path, signature);
|
|
837
|
-
}
|
|
838
|
-
function insertEdge(statements, kind, sourceKind, source, targetKind, target, file, line, evidence) {
|
|
839
|
-
if (!target)
|
|
840
|
-
return;
|
|
841
|
-
statements.insertEdge.run(kind, sourceKind, source, targetKind, target, file.path, line, evidence);
|
|
842
|
-
}
|
|
843
|
-
function importBindingText(importClause, sourceFile) {
|
|
844
|
-
if (!importClause)
|
|
845
|
-
return "";
|
|
846
|
-
const names = [];
|
|
847
|
-
if (importClause.name)
|
|
848
|
-
names.push(importClause.name.text);
|
|
849
|
-
const namedBindings = importClause.namedBindings;
|
|
850
|
-
if (namedBindings && ts.isNamespaceImport(namedBindings))
|
|
851
|
-
names.push(`* as ${namedBindings.name.text}`);
|
|
852
|
-
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
853
|
-
for (const element of namedBindings.elements)
|
|
854
|
-
names.push(element.name.text);
|
|
855
|
-
}
|
|
856
|
-
return names.join(", ") || oneLine(importClause.getText(sourceFile));
|
|
857
|
-
}
|
|
858
|
-
function stringArg(node) {
|
|
859
|
-
if (!node)
|
|
860
|
-
return "";
|
|
861
|
-
return ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) ? node.text : "";
|
|
862
|
-
}
|
|
863
|
-
function handlerArg(node, sourceFile) {
|
|
864
|
-
if (!node)
|
|
865
|
-
return "";
|
|
866
|
-
return callTarget(node, sourceFile);
|
|
867
|
-
}
|
|
868
|
-
function routeFromCall(node, sourceFile) {
|
|
869
|
-
if (!ts.isPropertyAccessExpression(node.expression))
|
|
870
|
-
return null;
|
|
871
|
-
const method = node.expression.name.text.toLowerCase();
|
|
872
|
-
if (!httpMethods.has(method))
|
|
873
|
-
return null;
|
|
874
|
-
const receiver = node.expression.expression;
|
|
875
|
-
if (!ts.isIdentifier(receiver) || !["app", "router", "server"].includes(receiver.text))
|
|
876
|
-
return null;
|
|
877
|
-
const route = stringArg(node.arguments[0]);
|
|
878
|
-
if (!route)
|
|
879
|
-
return null;
|
|
880
|
-
return {
|
|
881
|
-
handler: handlerArg(node.arguments[1], sourceFile),
|
|
882
|
-
method: method.toUpperCase(),
|
|
883
|
-
route,
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
function routeFromDecorator(node, sourceFile) {
|
|
887
|
-
const decorators = ts.canHaveDecorators(node) ? ts.getDecorators(node) ?? [] : [];
|
|
888
|
-
const routes = [];
|
|
889
|
-
for (const decorator of decorators) {
|
|
890
|
-
const expression = decorator.expression;
|
|
891
|
-
if (!ts.isCallExpression(expression))
|
|
892
|
-
continue;
|
|
893
|
-
const callee = expression.expression;
|
|
894
|
-
if (!ts.isIdentifier(callee))
|
|
895
|
-
continue;
|
|
896
|
-
const method = callee.text.toLowerCase();
|
|
897
|
-
if (!httpMethods.has(method))
|
|
898
|
-
continue;
|
|
899
|
-
routes.push({ method: method.toUpperCase(), route: stringArg(expression.arguments[0]) || "/" });
|
|
900
|
-
}
|
|
901
|
-
return routes;
|
|
902
|
-
}
|
|
903
|
-
function signatureFor(node, sourceFile) {
|
|
904
|
-
return oneLine(node.getText(sourceFile));
|
|
905
|
-
}
|
|
906
|
-
function indexJavaScriptLike(file, statements) {
|
|
907
|
-
const sourceFile = ts.createSourceFile(file.path, file.text, ts.ScriptTarget.Latest, true, scriptKindForPath(file.path));
|
|
908
|
-
function visit(node, context) {
|
|
909
|
-
let nextContext = context;
|
|
910
|
-
if (ts.isFunctionDeclaration(node)) {
|
|
911
|
-
const name = node.name?.text ?? "";
|
|
912
|
-
insertSymbol(statements, name, "function", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
913
|
-
if (name)
|
|
914
|
-
nextContext = name;
|
|
915
|
-
}
|
|
916
|
-
else if (ts.isClassDeclaration(node)) {
|
|
917
|
-
const name = node.name?.text ?? "";
|
|
918
|
-
insertSymbol(statements, name, "class", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
919
|
-
if (name)
|
|
920
|
-
nextContext = name;
|
|
921
|
-
}
|
|
922
|
-
else if (ts.isInterfaceDeclaration(node)) {
|
|
923
|
-
insertSymbol(statements, node.name.text, "interface", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
924
|
-
}
|
|
925
|
-
else if (ts.isTypeAliasDeclaration(node)) {
|
|
926
|
-
insertSymbol(statements, node.name.text, "type", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
927
|
-
}
|
|
928
|
-
else if (ts.isEnumDeclaration(node)) {
|
|
929
|
-
insertSymbol(statements, node.name.text, "enum", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
930
|
-
}
|
|
931
|
-
else if (ts.isMethodDeclaration(node)) {
|
|
932
|
-
const name = propertyNameText(node.name, sourceFile);
|
|
933
|
-
insertSymbol(statements, name, "method", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
934
|
-
for (const route of routeFromDecorator(node, sourceFile)) {
|
|
935
|
-
statements.insertRoute.run(route.method, route.route, file.path, tsLine(sourceFile, node), name);
|
|
936
|
-
insertEdge(statements, "route_to_handler", "route", `${route.method} ${route.route}`, "symbol", name, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
937
|
-
}
|
|
938
|
-
if (name)
|
|
939
|
-
nextContext = name;
|
|
940
|
-
}
|
|
941
|
-
else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
942
|
-
const symbolKind = node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) ? "function" : "variable";
|
|
943
|
-
insertSymbol(statements, node.name.text, symbolKind, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
944
|
-
if (symbolKind === "function")
|
|
945
|
-
nextContext = node.name.text;
|
|
946
|
-
}
|
|
947
|
-
else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
948
|
-
const imported = importBindingText(node.importClause, sourceFile);
|
|
949
|
-
statements.insertImport.run(file.path, node.moduleSpecifier.text, imported, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
950
|
-
insertEdge(statements, "import", "file", file.path, "module", node.moduleSpecifier.text, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
951
|
-
}
|
|
952
|
-
else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
953
|
-
const exported = node.exportClause ? oneLine(node.exportClause.getText(sourceFile)) : "";
|
|
954
|
-
statements.insertImport.run(file.path, node.moduleSpecifier.text, exported, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
955
|
-
insertEdge(statements, "export", "file", file.path, "module", node.moduleSpecifier.text, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
956
|
-
}
|
|
957
|
-
else if (ts.isCallExpression(node)) {
|
|
958
|
-
const route = routeFromCall(node, sourceFile);
|
|
959
|
-
if (route) {
|
|
960
|
-
statements.insertRoute.run(route.method, route.route, file.path, tsLine(sourceFile, node), route.handler);
|
|
961
|
-
insertEdge(statements, "route_to_handler", "route", `${route.method} ${route.route}`, "symbol", route.handler, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
962
|
-
}
|
|
963
|
-
if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
964
|
-
const moduleName = stringArg(node.arguments[0]);
|
|
965
|
-
if (moduleName) {
|
|
966
|
-
statements.insertImport.run(file.path, moduleName, "", tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
967
|
-
insertEdge(statements, "import", "file", file.path, "module", moduleName, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
else {
|
|
971
|
-
insertEdge(statements, "call", context ? "symbol" : "file", context || file.path, "symbol", callTarget(node.expression, sourceFile), file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
ts.forEachChild(node, (child) => visit(child, nextContext));
|
|
975
|
-
}
|
|
976
|
-
visit(sourceFile, "");
|
|
977
|
-
}
|
|
978
|
-
function insertMatches(file, regex, insert) {
|
|
979
|
-
for (const match of file.text.matchAll(regex)) {
|
|
980
|
-
insert(match, lineNumber(file.text, match.index ?? 0));
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
function indexPythonLight(file, statements) {
|
|
984
|
-
const symbolPatterns = [
|
|
985
|
-
[/^\s*def\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/gm, "function", (match) => `def ${match[1] ?? ""}(${match[2] ?? ""})`],
|
|
986
|
-
[/^\s*class\s+([A-Za-z_]\w*)/gm, "class", (match) => `class ${match[1] ?? ""}`],
|
|
987
|
-
];
|
|
988
|
-
for (const [regex, kind, signature] of symbolPatterns) {
|
|
989
|
-
insertMatches(file, regex, (match, line) => insertSymbol(statements, match[1] ?? "", kind, file, line, signature(match)));
|
|
990
|
-
}
|
|
991
|
-
const importPatterns = [
|
|
992
|
-
[/^\s*from\s+([A-Za-z0-9_.$]+)\s+import\s+(.+)$/gm, (match) => [match[1] ?? "", match[2] ?? ""]],
|
|
993
|
-
[/^\s*import\s+([A-Za-z0-9_.$,\s]+)$/gm, (match) => [match[1] ?? "", ""]],
|
|
994
|
-
];
|
|
995
|
-
for (const [regex, fields] of importPatterns) {
|
|
996
|
-
insertMatches(file, regex, (match, line) => {
|
|
997
|
-
const [toRef, imported] = fields(match);
|
|
998
|
-
statements.insertImport.run(file.path, toRef, imported.trim(), line, match[0].trim());
|
|
999
|
-
insertEdge(statements, "import", "file", file.path, "module", toRef, file, line, match[0].trim());
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
function insertGoImport(file, statements, toRef, imported, line, raw) {
|
|
1004
|
-
if (!toRef)
|
|
1005
|
-
return;
|
|
1006
|
-
statements.insertImport.run(file.path, toRef, imported, line, raw);
|
|
1007
|
-
insertEdge(statements, "import", "file", file.path, "module", toRef, file, line, raw);
|
|
1008
|
-
}
|
|
1009
|
-
function indexGoLight(file, statements) {
|
|
1010
|
-
const symbolPatterns = [
|
|
1011
|
-
[/^\s*func\s*\(\s*[^)]*\)\s*([A-Za-z_]\w*)\s*\(([^)]*)\)/gm, "method", (match) => match[1] ?? "", (match) => `func (...) ${match[1] ?? ""}(${match[2] ?? ""})`],
|
|
1012
|
-
[/^\s*func\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/gm, "function", (match) => match[1] ?? "", (match) => `func ${match[1] ?? ""}(${match[2] ?? ""})`],
|
|
1013
|
-
[/^\s*type\s+([A-Za-z_]\w*)\s+(struct|interface)?/gm, "type", (match) => match[1] ?? "", (match) => `type ${match[1] ?? ""} ${match[2] ?? ""}`.trim()],
|
|
1014
|
-
[/^\s*const\s+([A-Za-z_]\w*)\b/gm, "constant", (match) => match[1] ?? "", (match) => `const ${match[1] ?? ""}`],
|
|
1015
|
-
[/^\s*var\s+([A-Za-z_]\w*)\b/gm, "variable", (match) => match[1] ?? "", (match) => `var ${match[1] ?? ""}`],
|
|
1016
|
-
];
|
|
1017
|
-
for (const [regex, kind, name, signature] of symbolPatterns) {
|
|
1018
|
-
insertMatches(file, regex, (match, line) => insertSymbol(statements, name(match), kind, file, line, signature(match)));
|
|
1019
|
-
}
|
|
1020
|
-
insertMatches(file, /^\s*import\s+(?:(?:([A-Za-z_]\w*|[_.])\s+)?\"([^\"]+)\"|`([^`]+)`)/gm, (match, line) => {
|
|
1021
|
-
const imported = match[1] ?? "";
|
|
1022
|
-
const toRef = match[2] ?? match[3] ?? "";
|
|
1023
|
-
insertGoImport(file, statements, toRef, imported, line, match[0].trim());
|
|
1024
|
-
});
|
|
1025
|
-
insertMatches(file, /^\s*import\s*\(([\s\S]*?)^\s*\)/gm, (blockMatch) => {
|
|
1026
|
-
const block = blockMatch[1] ?? "";
|
|
1027
|
-
const blockStart = blockMatch.index ?? 0;
|
|
1028
|
-
for (const lineMatch of block.matchAll(/^\s*(?:([A-Za-z_]\w*|[_.])\s+)?\"([^\"]+)\"/gm)) {
|
|
1029
|
-
const imported = lineMatch[1] ?? "";
|
|
1030
|
-
const toRef = lineMatch[2] ?? "";
|
|
1031
|
-
const line = lineNumber(file.text, blockStart + (lineMatch.index ?? 0));
|
|
1032
|
-
insertGoImport(file, statements, toRef, imported, line, lineMatch[0].trim());
|
|
1033
|
-
}
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
function indexConfigs(file, insertConfig) {
|
|
1037
|
-
if (path.basename(file.path) === "package.json") {
|
|
1038
|
-
try {
|
|
1039
|
-
const parsed = JSON.parse(file.text);
|
|
1040
|
-
for (const [name, value] of Object.entries(parsed.scripts ?? {}))
|
|
1041
|
-
insertConfig.run(`script:${name}`, value, file.path, 1);
|
|
1042
|
-
for (const [name, value] of Object.entries(parsed.dependencies ?? {}))
|
|
1043
|
-
insertConfig.run(`dependency:${name}`, value, file.path, 1);
|
|
1044
|
-
for (const [name, value] of Object.entries(parsed.devDependencies ?? {}))
|
|
1045
|
-
insertConfig.run(`devDependency:${name}`, value, file.path, 1);
|
|
1046
|
-
}
|
|
1047
|
-
catch {
|
|
1048
|
-
insertConfig.run("parse-error", "package.json is not valid JSON", file.path, 1);
|
|
1049
|
-
}
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
|
-
insertMatches(file, /^\s*([A-Za-z0-9_.-]+)\s*[:=]\s*(.+)$/gm, (match, line) => {
|
|
1053
|
-
insertConfig.run(match[1] ?? "", (match[2] ?? "").trim(), file.path, line);
|
|
1054
|
-
});
|
|
1055
|
-
}
|
|
1056
155
|
function codeScopes() {
|
|
1057
156
|
const scopes = args_1.codeIndexScopes.length > 0 ? args_1.codeIndexScopes : ["."];
|
|
1058
157
|
return scopes.map((scope) => normalizeProjectRelative(scope, "--code-scope"));
|
|
@@ -1067,59 +166,6 @@ function requireExistingIndex() {
|
|
|
1067
166
|
process.exit(1);
|
|
1068
167
|
}
|
|
1069
168
|
}
|
|
1070
|
-
function readMetaValue(database, key) {
|
|
1071
|
-
const rows = database.prepare("SELECT value FROM meta WHERE key = ?").all(key);
|
|
1072
|
-
const value = rows[0]?.value;
|
|
1073
|
-
return typeof value === "string" ? value : "";
|
|
1074
|
-
}
|
|
1075
|
-
function indexedScopes(database) {
|
|
1076
|
-
const scopesJson = readMetaValue(database, "scopes_json");
|
|
1077
|
-
if (scopesJson) {
|
|
1078
|
-
try {
|
|
1079
|
-
const parsed = JSON.parse(scopesJson);
|
|
1080
|
-
if (Array.isArray(parsed) && parsed.every((scope) => typeof scope === "string"))
|
|
1081
|
-
return parsed;
|
|
1082
|
-
}
|
|
1083
|
-
catch {
|
|
1084
|
-
// Fall back to the legacy comma-separated scope metadata below.
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
return readMetaValue(database, "scopes")
|
|
1088
|
-
.split(",")
|
|
1089
|
-
.map((scope) => scope.trim())
|
|
1090
|
-
.filter(Boolean);
|
|
1091
|
-
}
|
|
1092
|
-
function indexedParserMode(database) {
|
|
1093
|
-
const mode = readMetaValue(database, "parser_mode");
|
|
1094
|
-
return mode === "tree-sitter" ? "tree-sitter" : "default";
|
|
1095
|
-
}
|
|
1096
|
-
function scopesMatch(left, right) {
|
|
1097
|
-
return left.length === right.length && left.every((scope, index) => scope === right[index]);
|
|
1098
|
-
}
|
|
1099
|
-
function incrementalCompatibility(database, scopes, parserMode) {
|
|
1100
|
-
const existingSchemaVersion = readMetaValue(database, "schema_version");
|
|
1101
|
-
if (existingSchemaVersion !== codeIndexSchemaVersion) {
|
|
1102
|
-
return {
|
|
1103
|
-
compatible: false,
|
|
1104
|
-
reason: `existing schema version ${existingSchemaVersion || "(missing)"} does not match ${codeIndexSchemaVersion}`,
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
const existingScopes = indexedScopes(database);
|
|
1108
|
-
if (!scopesMatch(existingScopes, scopes)) {
|
|
1109
|
-
return {
|
|
1110
|
-
compatible: false,
|
|
1111
|
-
reason: `indexed scopes do not match requested scopes: indexed [${existingScopes.join(", ")}], requested [${scopes.join(", ")}]`,
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
const existingParserMode = indexedParserMode(database);
|
|
1115
|
-
if (existingParserMode !== parserMode) {
|
|
1116
|
-
return {
|
|
1117
|
-
compatible: false,
|
|
1118
|
-
reason: `indexed parser mode ${existingParserMode} does not match requested parser mode ${parserMode}`,
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
return { compatible: true, reason: "" };
|
|
1122
|
-
}
|
|
1123
169
|
function removeDatabaseFiles(databasePath) {
|
|
1124
170
|
for (const filePath of [databasePath, `${databasePath}-wal`, `${databasePath}-shm`]) {
|
|
1125
171
|
if (fs.existsSync(filePath))
|
|
@@ -1127,27 +173,30 @@ function removeDatabaseFiles(databasePath) {
|
|
|
1127
173
|
}
|
|
1128
174
|
}
|
|
1129
175
|
function codeIndexStaleness(database) {
|
|
1130
|
-
const scopes = indexedScopes(database);
|
|
1131
|
-
const parserMode = indexedParserMode(database);
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
const currentHash = current.get(filePath);
|
|
1141
|
-
if (!currentHash)
|
|
1142
|
-
deleted += 1;
|
|
1143
|
-
else if (currentHash !== hash)
|
|
1144
|
-
changed += 1;
|
|
1145
|
-
}
|
|
176
|
+
const scopes = (0, schema_1.indexedScopes)(database);
|
|
177
|
+
const parserMode = (0, schema_1.indexedParserMode)(database);
|
|
178
|
+
const currentFiles = (0, code_index_file_policy_1.discoverCodeFiles)(scopes.length > 0 ? scopes : ["."]).map(readCodeFileFingerprint);
|
|
179
|
+
const currentPaths = new Set(currentFiles.map((file) => file.path));
|
|
180
|
+
const indexedRows = database.prepare("SELECT path, hash, mtime_ms, size FROM files").all();
|
|
181
|
+
const indexed = new Map(indexedRows.map((row) => [String(row.path), {
|
|
182
|
+
hash: String(row.hash),
|
|
183
|
+
mtimeMs: Number(row.mtime_ms),
|
|
184
|
+
size: Number(row.size),
|
|
185
|
+
}]));
|
|
1146
186
|
let added = 0;
|
|
1147
|
-
|
|
1148
|
-
|
|
187
|
+
let changed = 0;
|
|
188
|
+
for (const file of currentFiles) {
|
|
189
|
+
const existing = indexed.get(file.path);
|
|
190
|
+
if (!existing) {
|
|
1149
191
|
added += 1;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (existing.mtimeMs === file.mtimeMs && existing.size === file.size)
|
|
195
|
+
continue;
|
|
196
|
+
if (readCodeFile(file.path, parserMode).hash !== existing.hash)
|
|
197
|
+
changed += 1;
|
|
1150
198
|
}
|
|
199
|
+
const deleted = indexedRows.filter((row) => !currentPaths.has(String(row.path))).length;
|
|
1151
200
|
return {
|
|
1152
201
|
added,
|
|
1153
202
|
changed,
|
|
@@ -1155,616 +204,67 @@ function codeIndexStaleness(database) {
|
|
|
1155
204
|
stale: added > 0 || changed > 0 || deleted > 0,
|
|
1156
205
|
};
|
|
1157
206
|
}
|
|
1158
|
-
function
|
|
1159
|
-
const
|
|
207
|
+
function codeIndexHealth() {
|
|
208
|
+
const databasePath = codeEvidenceDatabasePath();
|
|
209
|
+
return (0, index_health_1.inspectCodeIndexHealth)({
|
|
210
|
+
absolutePath: databasePath.absolutePath,
|
|
211
|
+
defaultScopes: codeScopes(),
|
|
212
|
+
discoverCodeFiles: code_index_file_policy_1.discoverCodeFiles,
|
|
213
|
+
expectedSchemaVersion: schema_1.codeIndexSchemaVersion,
|
|
214
|
+
openDatabase,
|
|
215
|
+
relativePath: databasePath.relativePath,
|
|
216
|
+
smallRepoThreshold: code_index_file_policy_1.SMALL_REPO_FILE_THRESHOLD,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
function warnIfCodeIndexStale(database, staleness = codeIndexStaleness(database)) {
|
|
1160
220
|
if (!staleness.stale)
|
|
1161
221
|
return;
|
|
1162
222
|
console.error(`code evidence index may be stale: ${staleness.changed} changed, ${staleness.added} added, ${staleness.deleted} deleted; rerun --code-index`);
|
|
1163
223
|
}
|
|
1164
|
-
function
|
|
1165
|
-
const parts = (0, workspace_1.normalizePath)(filePath).split("/").filter(Boolean);
|
|
1166
|
-
if (parts.length === 0)
|
|
1167
|
-
return ".";
|
|
1168
|
-
if (["apps", "libs", "packages", "services"].includes(parts[0] ?? "") && parts[1])
|
|
1169
|
-
return `${parts[0]}/${parts[1]}`;
|
|
1170
|
-
return parts[0] ?? ".";
|
|
1171
|
-
}
|
|
1172
|
-
function readJsonObject(relativePath) {
|
|
1173
|
-
try {
|
|
1174
|
-
const parsed = JSON.parse(fs.readFileSync((0, workspace_1.abs)(relativePath), "utf8"));
|
|
1175
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
1176
|
-
}
|
|
1177
|
-
catch {
|
|
1178
|
-
return null;
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
function workspacePatternsFromRootPackage() {
|
|
1182
|
-
const rootPackage = readJsonObject("package.json");
|
|
1183
|
-
const workspaces = rootPackage?.workspaces;
|
|
1184
|
-
if (Array.isArray(workspaces))
|
|
1185
|
-
return workspaces.filter((value) => typeof value === "string");
|
|
1186
|
-
if (workspaces && typeof workspaces === "object" && !Array.isArray(workspaces)) {
|
|
1187
|
-
const packages = workspaces.packages;
|
|
1188
|
-
if (Array.isArray(packages))
|
|
1189
|
-
return packages.filter((value) => typeof value === "string");
|
|
1190
|
-
}
|
|
1191
|
-
return [];
|
|
1192
|
-
}
|
|
1193
|
-
function workspacePatternCandidates(pattern) {
|
|
1194
|
-
const normalized = (0, workspace_1.normalizePath)(pattern).replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1195
|
-
if (!normalized || normalized.includes(".."))
|
|
1196
|
-
return [];
|
|
1197
|
-
if (!normalized.includes("*"))
|
|
1198
|
-
return [normalized];
|
|
1199
|
-
const starIndex = normalized.indexOf("*");
|
|
1200
|
-
const prefix = normalized.slice(0, starIndex).replace(/\/+$/, "");
|
|
1201
|
-
const suffix = normalized.slice(starIndex + 1).replace(/^\/+/, "");
|
|
1202
|
-
const base = prefix || ".";
|
|
1203
|
-
const basePath = (0, workspace_1.abs)(base);
|
|
1204
|
-
if (!fs.existsSync(basePath) || !fs.statSync(basePath).isDirectory())
|
|
1205
|
-
return [];
|
|
1206
|
-
return fs.readdirSync(basePath, { withFileTypes: true })
|
|
1207
|
-
.filter((entry) => entry.isDirectory())
|
|
1208
|
-
.map((entry) => (0, workspace_1.normalizePath)(path.join(base, entry.name, suffix)))
|
|
1209
|
-
.filter((candidate) => fs.existsSync((0, workspace_1.abs)(candidate)) && fs.statSync((0, workspace_1.abs)(candidate)).isDirectory());
|
|
1210
|
-
}
|
|
1211
|
-
function workspacePackages() {
|
|
1212
|
-
const packages = new Map();
|
|
1213
|
-
for (const pattern of workspacePatternsFromRootPackage()) {
|
|
1214
|
-
for (const candidate of workspacePatternCandidates(pattern)) {
|
|
1215
|
-
const packageJsonPath = (0, workspace_1.normalizePath)(path.join(candidate, "package.json"));
|
|
1216
|
-
if (!fs.existsSync((0, workspace_1.abs)(packageJsonPath)))
|
|
1217
|
-
continue;
|
|
1218
|
-
const packageJson = readJsonObject(packageJsonPath);
|
|
1219
|
-
const packageName = typeof packageJson?.name === "string" ? packageJson.name : candidate;
|
|
1220
|
-
packages.set(candidate, {
|
|
1221
|
-
name: packageName,
|
|
1222
|
-
root: candidate,
|
|
1223
|
-
source: "package.json workspaces",
|
|
1224
|
-
workspace_pattern: pattern,
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
return Array.from(packages.values()).sort((left, right) => left.root.localeCompare(right.root));
|
|
1229
|
-
}
|
|
1230
|
-
function matchingWorkspace(filePath, workspaces) {
|
|
1231
|
-
const normalized = (0, workspace_1.normalizePath)(filePath);
|
|
1232
|
-
return workspaces
|
|
1233
|
-
.filter((workspace) => normalized === workspace.root || normalized.startsWith(`${workspace.root}/`))
|
|
1234
|
-
.sort((left, right) => right.root.length - left.root.length)[0] ?? null;
|
|
1235
|
-
}
|
|
1236
|
-
function codeownerRules() {
|
|
1237
|
-
const files = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"];
|
|
1238
|
-
const rules = [];
|
|
1239
|
-
for (const filePath of files) {
|
|
1240
|
-
if (!fs.existsSync((0, workspace_1.abs)(filePath)))
|
|
1241
|
-
continue;
|
|
1242
|
-
const lines = fs.readFileSync((0, workspace_1.abs)(filePath), "utf8").split(/\r?\n/);
|
|
1243
|
-
lines.forEach((lineText, index) => {
|
|
1244
|
-
const trimmed = lineText.trim();
|
|
1245
|
-
if (!trimmed || trimmed.startsWith("#"))
|
|
1246
|
-
return;
|
|
1247
|
-
const parts = trimmed.split(/\s+/);
|
|
1248
|
-
const pattern = parts[0] ?? "";
|
|
1249
|
-
const owners = parts.slice(1);
|
|
1250
|
-
if (!pattern || owners.length === 0)
|
|
1251
|
-
return;
|
|
1252
|
-
rules.push({ file_path: filePath, line: index + 1, owners, pattern });
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
|
-
return rules;
|
|
1256
|
-
}
|
|
1257
|
-
function codeownerPatternRegex(pattern) {
|
|
1258
|
-
const normalized = (0, workspace_1.normalizePath)(pattern).replace(/^\/+/, "");
|
|
1259
|
-
const source = normalized
|
|
1260
|
-
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
|
|
1261
|
-
.replace(/\*\*/g, ".*")
|
|
1262
|
-
.replace(/\*/g, "[^/]*");
|
|
1263
|
-
if (normalized.endsWith("/"))
|
|
1264
|
-
return new RegExp(`^${source}.*$`);
|
|
1265
|
-
return new RegExp(`^${source}(?:/.*)?$`);
|
|
1266
|
-
}
|
|
1267
|
-
function codeownerPatternMatches(pattern, filePath) {
|
|
1268
|
-
const normalized = (0, workspace_1.normalizePath)(pattern).replace(/^\/+/, "");
|
|
1269
|
-
const target = (0, workspace_1.normalizePath)(filePath);
|
|
1270
|
-
if (normalized === "*")
|
|
1271
|
-
return true;
|
|
1272
|
-
if (normalized.startsWith("*."))
|
|
1273
|
-
return path.basename(target).endsWith(normalized.slice(1));
|
|
1274
|
-
return codeownerPatternRegex(normalized).test(target);
|
|
1275
|
-
}
|
|
1276
|
-
function matchingCodeowners(filePath, rules) {
|
|
1277
|
-
const matches = rules.filter((rule) => codeownerPatternMatches(rule.pattern, filePath));
|
|
1278
|
-
return matches[matches.length - 1]?.owners ?? [];
|
|
1279
|
-
}
|
|
1280
|
-
// Return every CODEOWNERS rule that matches a path, in file order. The last entry
|
|
1281
|
-
// is the effective owner under last-match-wins; earlier entries are overridden.
|
|
1282
|
-
// Reuses the same matcher as matchingCodeowners so precedence answers stay
|
|
1283
|
-
// consistent with --code-report / --code-impact.
|
|
1284
|
-
function matchedCodeownerRules(filePath, rules) {
|
|
1285
|
-
return rules.filter((rule) => codeownerPatternMatches(rule.pattern, filePath));
|
|
1286
|
-
}
|
|
1287
|
-
function ownershipContext() {
|
|
1288
|
-
return {
|
|
1289
|
-
codeownerRules: codeownerRules(),
|
|
1290
|
-
workspaces: workspacePackages(),
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
function ownershipInfo(filePath, context) {
|
|
1294
|
-
const workspace = matchingWorkspace(filePath, context.workspaces);
|
|
1295
|
-
const owners = matchingCodeowners(filePath, context.codeownerRules);
|
|
1296
|
-
return {
|
|
1297
|
-
codeowners: owners.join(", "),
|
|
1298
|
-
owner: workspace?.root ?? pathOwnerKey(filePath),
|
|
1299
|
-
owner_source: workspace ? "workspace" : "path",
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
function incrementOwnerField(owners, context, filePath, field, increment = 1) {
|
|
1303
|
-
const info = ownershipInfo(filePath, context);
|
|
1304
|
-
const key = info.owner;
|
|
1305
|
-
const current = owners.get(key) ?? {
|
|
1306
|
-
bytes: 0,
|
|
1307
|
-
codeowners: info.codeowners,
|
|
1308
|
-
configs: 0,
|
|
1309
|
-
file_count: 0,
|
|
1310
|
-
imports: 0,
|
|
1311
|
-
languages: "",
|
|
1312
|
-
lines: 0,
|
|
1313
|
-
owner: key,
|
|
1314
|
-
owner_source: info.owner_source,
|
|
1315
|
-
routes: 0,
|
|
1316
|
-
symbols: 0,
|
|
1317
|
-
};
|
|
1318
|
-
if (info.codeowners && !current.codeowners.split(", ").includes(info.codeowners))
|
|
1319
|
-
current.codeowners = current.codeowners ? `${current.codeowners}; ${info.codeowners}` : info.codeowners;
|
|
1320
|
-
current[field] += increment;
|
|
1321
|
-
owners.set(key, current);
|
|
1322
|
-
}
|
|
1323
|
-
function evidenceCoverage(database) {
|
|
1324
|
-
const rows = database.prepare(`
|
|
1325
|
-
SELECT 'files' AS table_name, count(*) AS rows FROM files
|
|
1326
|
-
UNION ALL SELECT 'symbols', count(*) FROM symbols
|
|
1327
|
-
UNION ALL SELECT 'imports', count(*) FROM imports
|
|
1328
|
-
UNION ALL SELECT 'routes', count(*) FROM routes
|
|
1329
|
-
UNION ALL SELECT 'configs', count(*) FROM configs
|
|
1330
|
-
UNION ALL SELECT 'edges', count(*) FROM edges
|
|
1331
|
-
`).all();
|
|
1332
|
-
return Object.fromEntries(rows.map((row) => [String(row.table_name), Number(row.rows ?? 0)]));
|
|
1333
|
-
}
|
|
1334
|
-
function ownershipSummary(database) {
|
|
1335
|
-
const files = database.prepare("SELECT path, language, profile, lines, bytes FROM files ORDER BY path").all();
|
|
1336
|
-
const context = ownershipContext();
|
|
1337
|
-
const owners = new Map();
|
|
1338
|
-
const ownerLanguages = new Map();
|
|
1339
|
-
for (const row of files) {
|
|
1340
|
-
const filePath = String(row.path);
|
|
1341
|
-
const key = ownershipInfo(filePath, context).owner;
|
|
1342
|
-
incrementOwnerField(owners, context, filePath, "file_count");
|
|
1343
|
-
incrementOwnerField(owners, context, filePath, "lines", Number(row.lines ?? 0));
|
|
1344
|
-
incrementOwnerField(owners, context, filePath, "bytes", Number(row.bytes ?? 0));
|
|
1345
|
-
const languages = ownerLanguages.get(key) ?? new Set();
|
|
1346
|
-
languages.add(String(row.language));
|
|
1347
|
-
ownerLanguages.set(key, languages);
|
|
1348
|
-
}
|
|
1349
|
-
for (const row of database.prepare("SELECT file_path, count(*) AS count FROM symbols GROUP BY file_path").all())
|
|
1350
|
-
incrementOwnerField(owners, context, String(row.file_path), "symbols", Number(row.count ?? 0));
|
|
1351
|
-
for (const row of database.prepare("SELECT file_path, count(*) AS count FROM routes GROUP BY file_path").all())
|
|
1352
|
-
incrementOwnerField(owners, context, String(row.file_path), "routes", Number(row.count ?? 0));
|
|
1353
|
-
for (const row of database.prepare("SELECT from_file, count(*) AS count FROM imports GROUP BY from_file").all())
|
|
1354
|
-
incrementOwnerField(owners, context, String(row.from_file), "imports", Number(row.count ?? 0));
|
|
1355
|
-
for (const row of database.prepare("SELECT file_path, count(*) AS count FROM configs GROUP BY file_path").all())
|
|
1356
|
-
incrementOwnerField(owners, context, String(row.file_path), "configs", Number(row.count ?? 0));
|
|
1357
|
-
return Array.from(owners.values()).map((owner) => ({
|
|
1358
|
-
...owner,
|
|
1359
|
-
languages: Array.from(ownerLanguages.get(String(owner.owner)) ?? []).sort().join(", "),
|
|
1360
|
-
})).sort((left, right) => right.file_count - left.file_count || left.owner.localeCompare(right.owner)).slice(0, 25);
|
|
1361
|
-
}
|
|
1362
|
-
function languageProfileSummary(database) {
|
|
1363
|
-
return database.prepare("SELECT language, profile, count(*) AS files, sum(lines) AS lines, sum(bytes) AS bytes FROM files GROUP BY language, profile ORDER BY files DESC, language").all();
|
|
1364
|
-
}
|
|
1365
|
-
function parserBackendSummary(database) {
|
|
1366
|
-
return database.prepare("SELECT language, profile, count(*) AS files, sum(lines) AS lines, sum(bytes) AS bytes FROM files GROUP BY language, profile ORDER BY files DESC, language").all().map((row) => {
|
|
1367
|
-
const profile = String(row.profile);
|
|
1368
|
-
const backend = extractionBackendForProfile(profile);
|
|
1369
|
-
return {
|
|
1370
|
-
language: row.language,
|
|
1371
|
-
profile,
|
|
1372
|
-
backend: backend.id,
|
|
1373
|
-
label: backend.label,
|
|
1374
|
-
extraction_strength: backend.strength,
|
|
1375
|
-
files: row.files,
|
|
1376
|
-
lines: row.lines,
|
|
1377
|
-
bytes: row.bytes,
|
|
1378
|
-
};
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
function workspaceSummary(database) {
|
|
1382
|
-
const context = ownershipContext();
|
|
1383
|
-
const counts = new Map();
|
|
1384
|
-
for (const workspace of context.workspaces) {
|
|
1385
|
-
counts.set(workspace.root, { ...workspace, bytes: 0, files: 0, lines: 0 });
|
|
1386
|
-
}
|
|
1387
|
-
for (const row of database.prepare("SELECT path, lines, bytes FROM files ORDER BY path").all()) {
|
|
1388
|
-
const workspace = matchingWorkspace(String(row.path), context.workspaces);
|
|
1389
|
-
if (!workspace)
|
|
1390
|
-
continue;
|
|
1391
|
-
const current = counts.get(workspace.root) ?? { ...workspace, bytes: 0, files: 0, lines: 0 };
|
|
1392
|
-
current.files += 1;
|
|
1393
|
-
current.lines += Number(row.lines ?? 0);
|
|
1394
|
-
current.bytes += Number(row.bytes ?? 0);
|
|
1395
|
-
counts.set(workspace.root, current);
|
|
1396
|
-
}
|
|
1397
|
-
return {
|
|
1398
|
-
workspace_packages: Array.from(counts.values()).sort((left, right) => left.root.localeCompare(right.root)),
|
|
1399
|
-
codeowners: context.codeownerRules.map((rule) => ({
|
|
1400
|
-
file_path: rule.file_path,
|
|
1401
|
-
line: rule.line,
|
|
1402
|
-
pattern: rule.pattern,
|
|
1403
|
-
owners: rule.owners.join(", "),
|
|
1404
|
-
})),
|
|
1405
|
-
};
|
|
1406
|
-
}
|
|
1407
|
-
function packageManagerFromLockfile(filePath) {
|
|
1408
|
-
const base = path.basename(filePath);
|
|
1409
|
-
if (base === "package-lock.json" || base === "npm-shrinkwrap.json")
|
|
1410
|
-
return "npm";
|
|
1411
|
-
if (base === "pnpm-lock.yaml")
|
|
1412
|
-
return "pnpm";
|
|
1413
|
-
if (base === "yarn.lock")
|
|
1414
|
-
return "yarn";
|
|
1415
|
-
if (base === "bun.lockb" || base === "bun.lock")
|
|
1416
|
-
return "bun";
|
|
1417
|
-
return "unknown";
|
|
1418
|
-
}
|
|
1419
|
-
function workspaceDependencyGraph() {
|
|
1420
|
-
const workspaces = workspacePackages();
|
|
1421
|
-
const byName = new Map(workspaces.map((workspace) => [workspace.name, workspace]));
|
|
1422
|
-
const lockfiles = ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"]
|
|
1423
|
-
.filter((filePath) => fs.existsSync((0, workspace_1.abs)(filePath)))
|
|
1424
|
-
.map((filePath) => ({ file_path: filePath, package_manager: packageManagerFromLockfile(filePath), scope: "root" }));
|
|
1425
|
-
const workspaceRows = [];
|
|
1426
|
-
const internalEdges = [];
|
|
1427
|
-
const externalDependencies = new Map();
|
|
1428
|
-
for (const workspace of workspaces) {
|
|
1429
|
-
const packageJsonPath = (0, workspace_1.normalizePath)(path.join(workspace.root, "package.json"));
|
|
1430
|
-
const packageJson = readJsonObject(packageJsonPath);
|
|
1431
|
-
const dependencyFields = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
|
1432
|
-
const dependencyCounts = {};
|
|
1433
|
-
const workspaceInternalEdges = [];
|
|
1434
|
-
for (const field of dependencyFields) {
|
|
1435
|
-
const dependencies = packageJson?.[field];
|
|
1436
|
-
if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies))
|
|
1437
|
-
continue;
|
|
1438
|
-
for (const [dependencyName, version] of Object.entries(dependencies)) {
|
|
1439
|
-
dependencyCounts[field] = (dependencyCounts[field] ?? 0) + 1;
|
|
1440
|
-
const target = byName.get(dependencyName);
|
|
1441
|
-
if (target) {
|
|
1442
|
-
const edge = {
|
|
1443
|
-
from_workspace: workspace.root,
|
|
1444
|
-
from_package: workspace.name,
|
|
1445
|
-
to_workspace: target.root,
|
|
1446
|
-
to_package: target.name,
|
|
1447
|
-
dependency_type: field,
|
|
1448
|
-
version: typeof version === "string" ? version : String(version),
|
|
1449
|
-
};
|
|
1450
|
-
internalEdges.push(edge);
|
|
1451
|
-
workspaceInternalEdges.push(edge);
|
|
1452
|
-
}
|
|
1453
|
-
else {
|
|
1454
|
-
const key = `${dependencyName}\0${field}`;
|
|
1455
|
-
const current = externalDependencies.get(key) ?? { dependency: dependencyName, dependency_type: field, workspaces: new Set() };
|
|
1456
|
-
current.workspaces.add(workspace.root);
|
|
1457
|
-
externalDependencies.set(key, current);
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
for (const lockfileName of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"]) {
|
|
1462
|
-
const lockfilePath = (0, workspace_1.normalizePath)(path.join(workspace.root, lockfileName));
|
|
1463
|
-
if (fs.existsSync((0, workspace_1.abs)(lockfilePath))) {
|
|
1464
|
-
lockfiles.push({ file_path: lockfilePath, package_manager: packageManagerFromLockfile(lockfilePath), scope: workspace.root });
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
workspaceRows.push({
|
|
1468
|
-
name: workspace.name,
|
|
1469
|
-
root: workspace.root,
|
|
1470
|
-
dependency_counts: dependencyCounts,
|
|
1471
|
-
internal_dependency_count: workspaceInternalEdges.length,
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
return {
|
|
1475
|
-
workspace_count: workspaces.length,
|
|
1476
|
-
package_managers: Array.from(new Set(lockfiles.map((lockfile) => lockfile.package_manager))).sort(),
|
|
1477
|
-
lockfiles,
|
|
1478
|
-
workspaces: workspaceRows.sort((left, right) => String(left.root).localeCompare(String(right.root))),
|
|
1479
|
-
internal_dependencies: internalEdges.sort((left, right) => String(left.from_workspace).localeCompare(String(right.from_workspace)) || String(left.to_workspace).localeCompare(String(right.to_workspace))),
|
|
1480
|
-
external_dependency_hotspots: Array.from(externalDependencies.values()).map((entry) => ({
|
|
1481
|
-
dependency: entry.dependency,
|
|
1482
|
-
dependency_type: entry.dependency_type,
|
|
1483
|
-
workspace_count: entry.workspaces.size,
|
|
1484
|
-
workspaces: Array.from(entry.workspaces).sort().join(", "),
|
|
1485
|
-
})).sort((left, right) => right.workspace_count - left.workspace_count || left.dependency.localeCompare(right.dependency)).slice(0, 100),
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
function routeInventory(database) {
|
|
1489
|
-
return database.prepare("SELECT method, route, file_path, line, handler FROM routes ORDER BY file_path, line LIMIT 100").all();
|
|
1490
|
-
}
|
|
1491
|
-
function dependencyHotspots(database) {
|
|
1492
|
-
return {
|
|
1493
|
-
imports: database.prepare("SELECT to_ref, count(DISTINCT from_file) AS importing_files, count(*) AS reference_count FROM imports GROUP BY to_ref ORDER BY importing_files DESC, reference_count DESC, to_ref LIMIT 50").all(),
|
|
1494
|
-
package_dependencies: database.prepare("SELECT substr(key, 12) AS package, value AS version, file_path FROM configs WHERE key LIKE 'dependency:%' ORDER BY file_path, package LIMIT 100").all(),
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
|
-
function configInventory(database) {
|
|
1498
|
-
return database.prepare("SELECT key, value, file_path, line FROM configs WHERE key LIKE 'script:%' OR key LIKE 'dependency:%' OR key LIKE 'devDependency:%' ORDER BY file_path, key LIMIT 150").all();
|
|
1499
|
-
}
|
|
1500
|
-
function edgeSummary(database) {
|
|
1501
|
-
return {
|
|
1502
|
-
by_kind: database.prepare("SELECT kind, count(*) AS edges FROM edges GROUP BY kind ORDER BY edges DESC, kind").all(),
|
|
1503
|
-
fanout: database.prepare("SELECT source_kind, source, kind, count(DISTINCT target) AS targets, file_path FROM edges GROUP BY source_kind, source, kind, file_path ORDER BY targets DESC, source LIMIT 50").all(),
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
function codeReportSectionData(database, section) {
|
|
1507
|
-
switch (section) {
|
|
1508
|
-
case "coverage":
|
|
1509
|
-
return evidenceCoverage(database);
|
|
1510
|
-
case "ownership":
|
|
1511
|
-
return ownershipSummary(database);
|
|
1512
|
-
case "languages":
|
|
1513
|
-
return languageProfileSummary(database);
|
|
1514
|
-
case "parsers":
|
|
1515
|
-
return parserBackendSummary(database);
|
|
1516
|
-
case "workspaces":
|
|
1517
|
-
return workspaceSummary(database);
|
|
1518
|
-
case "workspace-graph":
|
|
1519
|
-
return workspaceDependencyGraph();
|
|
1520
|
-
case "routes":
|
|
1521
|
-
return routeInventory(database);
|
|
1522
|
-
case "hotspots":
|
|
1523
|
-
return dependencyHotspots(database);
|
|
1524
|
-
case "configs":
|
|
1525
|
-
return configInventory(database);
|
|
1526
|
-
case "edges":
|
|
1527
|
-
return edgeSummary(database);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
function codeReportMetadata(database) {
|
|
224
|
+
function codeReportRuntime(database, options = {}) {
|
|
1531
225
|
const databasePath = codeEvidenceDatabasePath();
|
|
1532
|
-
const staleness = codeIndexStaleness(database);
|
|
1533
226
|
return {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
added: staleness.added,
|
|
1543
|
-
deleted: staleness.deleted,
|
|
227
|
+
databaseRelativePath: databasePath.relativePath,
|
|
228
|
+
parserBackendForProfile: (profile) => {
|
|
229
|
+
const backend = extractionBackendForProfile(profile);
|
|
230
|
+
return {
|
|
231
|
+
id: backend.id,
|
|
232
|
+
label: backend.label,
|
|
233
|
+
strength: backend.strength,
|
|
234
|
+
};
|
|
1544
235
|
},
|
|
236
|
+
staleness: options.staleness ?? codeIndexStaleness(database),
|
|
1545
237
|
};
|
|
1546
238
|
}
|
|
1547
|
-
function
|
|
1548
|
-
return {
|
|
1549
|
-
...codeReportMetadata(database),
|
|
1550
|
-
report_sections: ["evidence_coverage", "ownership_summary", "language_profile_summary", "parser_backend_summary", "workspace_summary", "workspace_dependency_graph", "route_inventory", "dependency_hotspots", "config_inventory", "edge_summary"],
|
|
1551
|
-
evidence_coverage: evidenceCoverage(database),
|
|
1552
|
-
ownership_summary: ownershipSummary(database),
|
|
1553
|
-
language_profile_summary: languageProfileSummary(database),
|
|
1554
|
-
parser_backend_summary: parserBackendSummary(database),
|
|
1555
|
-
workspace_summary: workspaceSummary(database),
|
|
1556
|
-
workspace_dependency_graph: workspaceDependencyGraph(),
|
|
1557
|
-
route_inventory: routeInventory(database),
|
|
1558
|
-
dependency_hotspots: dependencyHotspots(database),
|
|
1559
|
-
config_inventory: configInventory(database),
|
|
1560
|
-
edge_summary: edgeSummary(database),
|
|
1561
|
-
};
|
|
1562
|
-
}
|
|
1563
|
-
function selectedCodeReportSection() {
|
|
1564
|
-
const requested = args_1.codeReportSection.trim().toLowerCase();
|
|
1565
|
-
if (!requested || requested === "all" || requested === "full")
|
|
1566
|
-
return "";
|
|
1567
|
-
const section = codeReportSectionAliases[requested];
|
|
1568
|
-
if (!section) {
|
|
1569
|
-
const valid = ["coverage", "ownership", "languages", "parsers", "workspaces", "workspace-graph", "routes", "hotspots", "configs", "edges"].join(", ");
|
|
1570
|
-
fail(`invalid --code-report-section: ${args_1.codeReportSection}; expected one of: ${valid}`);
|
|
1571
|
-
}
|
|
1572
|
-
return section;
|
|
1573
|
-
}
|
|
1574
|
-
function codeReportForRequestedSection(database) {
|
|
1575
|
-
const section = selectedCodeReportSection();
|
|
1576
|
-
if (!section)
|
|
1577
|
-
return codeReport(database);
|
|
1578
|
-
return {
|
|
1579
|
-
...codeReportMetadata(database),
|
|
1580
|
-
section,
|
|
1581
|
-
data: codeReportSectionData(database, section),
|
|
1582
|
-
};
|
|
1583
|
-
}
|
|
1584
|
-
function escapeLikeTerm(term) {
|
|
1585
|
-
return term.replace(/[\\%_]/g, (match) => `\\${match}`);
|
|
1586
|
-
}
|
|
1587
|
-
function containsLikePattern(term) {
|
|
1588
|
-
return `%${escapeLikeTerm(term)}%`;
|
|
1589
|
-
}
|
|
1590
|
-
function prefixLikePattern(term) {
|
|
1591
|
-
return `${escapeLikeTerm(term)}%`;
|
|
1592
|
-
}
|
|
1593
|
-
function ftsPrefixQuery(term) {
|
|
1594
|
-
const tokens = Array.from(new Set(term.match(/[\p{L}\p{N}_]+/gu) ?? []));
|
|
1595
|
-
return tokens.slice(0, 8).map((token) => `"${token.replace(/"/g, "\"\"")}"*`).join(" AND ");
|
|
1596
|
-
}
|
|
1597
|
-
function stringValue(row, key) {
|
|
1598
|
-
const value = row[key];
|
|
1599
|
-
return typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
1600
|
-
}
|
|
1601
|
-
function addRankedRow(rowsByKey, row, key, score) {
|
|
1602
|
-
const current = rowsByKey.get(key);
|
|
1603
|
-
if (!current || score > current.score)
|
|
1604
|
-
rowsByKey.set(key, { row, score });
|
|
1605
|
-
}
|
|
1606
|
-
function rankedRows(rowsByKey, limit, stableKeys) {
|
|
1607
|
-
return Array.from(rowsByKey.values())
|
|
1608
|
-
.sort((left, right) => {
|
|
1609
|
-
const scoreDelta = right.score - left.score;
|
|
1610
|
-
if (scoreDelta !== 0)
|
|
1611
|
-
return scoreDelta;
|
|
1612
|
-
for (const key of stableKeys) {
|
|
1613
|
-
const compared = stringValue(left.row, key).localeCompare(stringValue(right.row, key));
|
|
1614
|
-
if (compared !== 0)
|
|
1615
|
-
return compared;
|
|
1616
|
-
}
|
|
1617
|
-
return 0;
|
|
1618
|
-
})
|
|
1619
|
-
.slice(0, limit)
|
|
1620
|
-
.map((ranked) => ranked.row);
|
|
1621
|
-
}
|
|
1622
|
-
function searchFiles(database, term, limit = 25) {
|
|
1623
|
-
const normalized = term.trim();
|
|
1624
|
-
if (!normalized)
|
|
1625
|
-
return [];
|
|
1626
|
-
const contains = containsLikePattern(normalized);
|
|
1627
|
-
const prefix = prefixLikePattern(normalized);
|
|
1628
|
-
const rowsByKey = new Map();
|
|
1629
|
-
const exactRows = database.prepare("SELECT path, language, profile, lines, bytes FROM files WHERE path = ? ORDER BY path LIMIT ?").all(normalized, limit);
|
|
1630
|
-
exactRows.forEach((row) => addRankedRow(rowsByKey, row, stringValue(row, "path"), 900));
|
|
1631
|
-
const prefixRows = database.prepare("SELECT path, language, profile, lines, bytes FROM files WHERE path LIKE ? ESCAPE '\\' ORDER BY path LIMIT ?").all(prefix, limit);
|
|
1632
|
-
prefixRows.forEach((row) => addRankedRow(rowsByKey, row, stringValue(row, "path"), 750));
|
|
1633
|
-
const ftsQuery = ftsPrefixQuery(normalized);
|
|
1634
|
-
if (ftsQuery) {
|
|
1635
|
-
const ftsRows = database.prepare(`
|
|
1636
|
-
SELECT files.path, files.language, files.profile, files.lines, files.bytes
|
|
1637
|
-
FROM files_fts
|
|
1638
|
-
JOIN files ON files.path = files_fts.path
|
|
1639
|
-
WHERE files_fts MATCH ?
|
|
1640
|
-
ORDER BY bm25(files_fts, 8.0, 1.0, 1.0, 0.25), files.path
|
|
1641
|
-
LIMIT ?
|
|
1642
|
-
`).all(ftsQuery, limit);
|
|
1643
|
-
ftsRows.forEach((row, index) => addRankedRow(rowsByKey, row, stringValue(row, "path"), 650 - index));
|
|
1644
|
-
}
|
|
1645
|
-
const containsRows = database.prepare("SELECT path, language, profile, lines, bytes FROM files WHERE path LIKE ? ESCAPE '\\' ORDER BY path LIMIT ?").all(contains, limit);
|
|
1646
|
-
containsRows.forEach((row) => addRankedRow(rowsByKey, row, stringValue(row, "path"), 500));
|
|
1647
|
-
return rankedRows(rowsByKey, limit, ["path"]);
|
|
1648
|
-
}
|
|
1649
|
-
function symbolKey(row) {
|
|
1650
|
-
return [
|
|
1651
|
-
stringValue(row, "file_path"),
|
|
1652
|
-
stringValue(row, "line"),
|
|
1653
|
-
stringValue(row, "kind"),
|
|
1654
|
-
stringValue(row, "name"),
|
|
1655
|
-
stringValue(row, "signature"),
|
|
1656
|
-
].join("\u0000");
|
|
1657
|
-
}
|
|
1658
|
-
function searchSymbols(database, term, limit = 50) {
|
|
1659
|
-
const normalized = term.trim();
|
|
1660
|
-
if (!normalized)
|
|
1661
|
-
return [];
|
|
1662
|
-
const contains = containsLikePattern(normalized);
|
|
1663
|
-
const prefix = prefixLikePattern(normalized);
|
|
1664
|
-
const rowsByKey = new Map();
|
|
1665
|
-
const exactRows = database.prepare(`
|
|
1666
|
-
SELECT name, kind, file_path, line, signature
|
|
1667
|
-
FROM symbols
|
|
1668
|
-
WHERE name = ? OR signature = ?
|
|
1669
|
-
ORDER BY file_path, line
|
|
1670
|
-
LIMIT ?
|
|
1671
|
-
`).all(normalized, normalized, limit);
|
|
1672
|
-
exactRows.forEach((row) => addRankedRow(rowsByKey, row, symbolKey(row), 1000));
|
|
1673
|
-
const prefixRows = database.prepare(`
|
|
1674
|
-
SELECT name, kind, file_path, line, signature
|
|
1675
|
-
FROM symbols
|
|
1676
|
-
WHERE name LIKE ? ESCAPE '\\' OR signature LIKE ? ESCAPE '\\'
|
|
1677
|
-
ORDER BY file_path, line
|
|
1678
|
-
LIMIT ?
|
|
1679
|
-
`).all(prefix, prefix, limit);
|
|
1680
|
-
prefixRows.forEach((row) => addRankedRow(rowsByKey, row, symbolKey(row), 850));
|
|
1681
|
-
const ftsQuery = ftsPrefixQuery(normalized);
|
|
1682
|
-
if (ftsQuery) {
|
|
1683
|
-
const ftsRows = database.prepare(`
|
|
1684
|
-
SELECT symbols.name, symbols.kind, symbols.file_path, symbols.line, symbols.signature
|
|
1685
|
-
FROM symbols_fts
|
|
1686
|
-
JOIN symbols
|
|
1687
|
-
ON symbols.name = symbols_fts.name
|
|
1688
|
-
AND symbols.kind = symbols_fts.kind
|
|
1689
|
-
AND symbols.file_path = symbols_fts.file_path
|
|
1690
|
-
AND symbols.signature = symbols_fts.signature
|
|
1691
|
-
WHERE symbols_fts MATCH ?
|
|
1692
|
-
ORDER BY bm25(symbols_fts, 8.0, 1.0, 4.0, 2.0), symbols.file_path, symbols.line
|
|
1693
|
-
LIMIT ?
|
|
1694
|
-
`).all(ftsQuery, limit);
|
|
1695
|
-
ftsRows.forEach((row, index) => addRankedRow(rowsByKey, row, symbolKey(row), 700 - index));
|
|
1696
|
-
}
|
|
1697
|
-
const containsRows = database.prepare(`
|
|
1698
|
-
SELECT name, kind, file_path, line, signature
|
|
1699
|
-
FROM symbols
|
|
1700
|
-
WHERE name LIKE ? ESCAPE '\\' OR signature LIKE ? ESCAPE '\\' OR file_path LIKE ? ESCAPE '\\'
|
|
1701
|
-
ORDER BY file_path, line
|
|
1702
|
-
LIMIT ?
|
|
1703
|
-
`).all(contains, contains, contains, limit);
|
|
1704
|
-
containsRows.forEach((row) => addRankedRow(rowsByKey, row, symbolKey(row), 500));
|
|
1705
|
-
return rankedRows(rowsByKey, limit, ["file_path", "line", "kind", "name", "signature"]);
|
|
1706
|
-
}
|
|
1707
|
-
function codeImpact(database, target) {
|
|
239
|
+
function codeImpact(database, target, options = {}) {
|
|
1708
240
|
const normalized = target.trim();
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
...routeMatches.map((row) => String(row.file_path)),
|
|
1722
|
-
...importMatches.map((row) => String(row.from_file)),
|
|
1723
|
-
...outgoingEdges.map((row) => String(row.file_path)),
|
|
1724
|
-
...incomingEdges.map((row) => String(row.file_path)),
|
|
1725
|
-
...routeEdges.map((row) => String(row.file_path)),
|
|
1726
|
-
].filter(Boolean))).sort();
|
|
1727
|
-
const ownership = ownershipContext();
|
|
1728
|
-
const impactedOwners = new Map();
|
|
1729
|
-
for (const filePath of relatedFilePaths) {
|
|
1730
|
-
const info = ownershipInfo(filePath, ownership);
|
|
1731
|
-
const current = impactedOwners.get(info.owner) ?? {
|
|
1732
|
-
codeowners: new Set(),
|
|
1733
|
-
files: 0,
|
|
1734
|
-
owner: info.owner,
|
|
1735
|
-
owner_source: info.owner_source,
|
|
1736
|
-
sample_files: [],
|
|
1737
|
-
};
|
|
1738
|
-
current.files += 1;
|
|
1739
|
-
if (current.sample_files.length < 10)
|
|
1740
|
-
current.sample_files.push(filePath);
|
|
1741
|
-
if (info.codeowners) {
|
|
1742
|
-
for (const owner of info.codeowners.split(", ").filter(Boolean))
|
|
1743
|
-
current.codeowners.add(owner);
|
|
1744
|
-
}
|
|
1745
|
-
impactedOwners.set(info.owner, current);
|
|
1746
|
-
}
|
|
241
|
+
const evidence = (0, evidence_1.collectCodeEvidence)(database, normalized, {
|
|
242
|
+
edgeLimit: 100,
|
|
243
|
+
fileLimit: 25,
|
|
244
|
+
includeEdgeEvidenceMatches: false,
|
|
245
|
+
includeOwnerCodeowners: true,
|
|
246
|
+
includeRouteEdges: true,
|
|
247
|
+
importLimit: 75,
|
|
248
|
+
ownerSampleLimit: 10,
|
|
249
|
+
routeEdgeLimit: 100,
|
|
250
|
+
routeLimit: 50,
|
|
251
|
+
symbolLimit: 50,
|
|
252
|
+
});
|
|
1747
253
|
return {
|
|
1748
|
-
...codeReportMetadata(database),
|
|
254
|
+
...(0, reports_1.codeReportMetadata)(database, codeReportRuntime(database, options)),
|
|
1749
255
|
target,
|
|
1750
256
|
matches: {
|
|
1751
|
-
files:
|
|
1752
|
-
symbols:
|
|
1753
|
-
routes:
|
|
1754
|
-
imports:
|
|
257
|
+
files: evidence.files,
|
|
258
|
+
symbols: evidence.symbols,
|
|
259
|
+
routes: evidence.routes,
|
|
260
|
+
imports: evidence.imports,
|
|
1755
261
|
},
|
|
1756
262
|
edges: {
|
|
1757
|
-
outgoing: outgoingEdges,
|
|
1758
|
-
incoming: incomingEdges,
|
|
1759
|
-
routes: routeEdges,
|
|
263
|
+
outgoing: evidence.outgoingEdges,
|
|
264
|
+
incoming: evidence.incomingEdges,
|
|
265
|
+
routes: evidence.routeEdges,
|
|
1760
266
|
},
|
|
1761
|
-
impacted_owners:
|
|
1762
|
-
owner: owner.owner,
|
|
1763
|
-
owner_source: owner.owner_source,
|
|
1764
|
-
files: owner.files,
|
|
1765
|
-
codeowners: Array.from(owner.codeowners).sort().join(", "),
|
|
1766
|
-
sample_files: owner.sample_files,
|
|
1767
|
-
})).sort((left, right) => right.files - left.files || left.owner.localeCompare(right.owner)),
|
|
267
|
+
impacted_owners: evidence.owners,
|
|
1768
268
|
};
|
|
1769
269
|
}
|
|
1770
270
|
function sampleLines(items, limit, render) {
|
|
@@ -1804,81 +304,44 @@ function codeContextScaleLine(fileCount) {
|
|
|
1804
304
|
: `scale large (${fileCount} indexed files >= ${code_index_file_policy_1.SMALL_REPO_FILE_THRESHOLD}); indexed traversal is useful for impact-style context`;
|
|
1805
305
|
}
|
|
1806
306
|
function structuralSignature(value) {
|
|
1807
|
-
const signature = oneLine(String(value ?? ""));
|
|
307
|
+
const signature = (0, shared_1.oneLine)(String(value ?? ""));
|
|
1808
308
|
const bodyStart = signature.indexOf("{");
|
|
1809
309
|
return bodyStart >= 0 ? signature.slice(0, bodyStart).trimEnd() : signature;
|
|
1810
310
|
}
|
|
1811
|
-
function
|
|
1812
|
-
return Array.from(new Set(values.filter(Boolean))).sort();
|
|
1813
|
-
}
|
|
1814
|
-
function codeContextPack(database, query) {
|
|
311
|
+
function codeContextPack(database, query, options = {}) {
|
|
1815
312
|
const normalized = query.trim();
|
|
1816
313
|
if (!normalized)
|
|
1817
314
|
return 'Code context pack: missing query; use --code-context-pack "path-or-symbol-or-route".';
|
|
1818
|
-
const
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
]);
|
|
1833
|
-
const ownership = ownershipContext();
|
|
1834
|
-
const ownerRows = new Map();
|
|
1835
|
-
for (const filePath of relatedFilePaths) {
|
|
1836
|
-
const info = ownershipInfo(filePath, ownership);
|
|
1837
|
-
const current = ownerRows.get(info.owner) ?? { files: 0, owner: info.owner, owner_source: info.owner_source, sample_files: [] };
|
|
1838
|
-
current.files += 1;
|
|
1839
|
-
if (current.sample_files.length < 4)
|
|
1840
|
-
current.sample_files.push(filePath);
|
|
1841
|
-
ownerRows.set(info.owner, current);
|
|
1842
|
-
}
|
|
1843
|
-
const owners = Array.from(ownerRows.values()).sort((left, right) => right.files - left.files || left.owner.localeCompare(right.owner));
|
|
1844
|
-
const staleness = codeIndexStaleness(database);
|
|
1845
|
-
const coverage = evidenceCoverage(database);
|
|
315
|
+
const evidence = (0, evidence_1.collectCodeEvidence)(database, normalized, {
|
|
316
|
+
edgeLimit: 30,
|
|
317
|
+
fileLimit: 12,
|
|
318
|
+
includeEdgeEvidenceMatches: true,
|
|
319
|
+
includeOwnerCodeowners: false,
|
|
320
|
+
includeRouteEdges: false,
|
|
321
|
+
importLimit: 30,
|
|
322
|
+
ownerSampleLimit: 4,
|
|
323
|
+
routeEdgeLimit: 0,
|
|
324
|
+
routeLimit: 20,
|
|
325
|
+
symbolLimit: 20,
|
|
326
|
+
});
|
|
327
|
+
const staleness = options.staleness ?? codeIndexStaleness(database);
|
|
328
|
+
const coverage = (0, reports_1.evidenceCoverage)(database);
|
|
1846
329
|
const staleLabel = staleness.stale
|
|
1847
330
|
? `STALE ${staleness.changed} changed, ${staleness.added} added, ${staleness.deleted} deleted`
|
|
1848
331
|
: "fresh";
|
|
1849
332
|
const lines = [
|
|
1850
|
-
`Code context pack "${normalized}": ${files.length} file matches, ${symbols.length} symbols, ${routes.length} routes, ${imports.length} imports, ${incomingEdges.length} incoming / ${outgoingEdges.length} outgoing edges; index ${staleLabel}; ${codeContextScaleLine(Number(coverage.files ?? 0))}.`,
|
|
333
|
+
`Code context pack "${normalized}": ${evidence.files.length} file matches, ${evidence.symbols.length} symbols, ${evidence.routes.length} routes, ${evidence.imports.length} imports, ${evidence.incomingEdges.length} incoming / ${evidence.outgoingEdges.length} outgoing edges; index ${staleLabel}; ${codeContextScaleLine(Number(coverage.files ?? 0))}.`,
|
|
1851
334
|
"Evidence is structural only: paths, lines, signatures, routes, imports, edges, and owners; no source snippets are included.",
|
|
1852
335
|
];
|
|
1853
|
-
pushBudgetedSection(lines, "Files:", files, 8, (row) => ` file-match ${String(row.path)} (${String(row.language)}, ${String(row.profile)}, ${Number(row.lines ?? 0)} lines)`);
|
|
1854
|
-
pushBudgetedSection(lines, "Symbols:", symbols, 12, (row) => ` symbol-match ${String(row.file_path)}:${String(row.line)} ${String(row.kind)} ${String(row.name)} - ${structuralSignature(row.signature)}`);
|
|
1855
|
-
pushBudgetedSection(lines, "Routes:", routes, 8, (row) => ` route-match ${String(row.method)} ${String(row.route)} -> ${String(row.handler)} (${String(row.file_path)}:${String(row.line)})`);
|
|
1856
|
-
pushBudgetedSection(lines, "Imports:", imports, 8, (row) => ` import-match ${String(row.from_file)}:${String(row.line)} -> ${String(row.to_ref)}${row.imported ? ` (${String(row.imported)})` : ""}`);
|
|
1857
|
-
pushBudgetedSection(lines, "Incoming edges:", incomingEdges, 8, (row) => ` edge-in ${String(row.kind)} ${String(row.source)} -> ${String(row.target)} (${String(row.file_path)}:${String(row.line)})`);
|
|
1858
|
-
pushBudgetedSection(lines, "Outgoing edges:", outgoingEdges, 8, (row) => ` edge-out ${String(row.kind)} ${String(row.source)} -> ${String(row.target)} (${String(row.file_path)}:${String(row.line)})`);
|
|
1859
|
-
pushBudgetedSection(lines, "Owners:", owners, 6, (row) => ` owner ${row.owner} (${row.owner_source}, ${row.files} files): ${row.sample_files.join(", ")}`);
|
|
336
|
+
pushBudgetedSection(lines, "Files:", evidence.files, 8, (row) => ` file-match ${String(row.path)} (${String(row.language)}, ${String(row.profile)}, ${Number(row.lines ?? 0)} lines)`);
|
|
337
|
+
pushBudgetedSection(lines, "Symbols:", evidence.symbols, 12, (row) => ` symbol-match ${String(row.file_path)}:${String(row.line)} ${String(row.kind)} ${String(row.name)} - ${structuralSignature(row.signature)}`);
|
|
338
|
+
pushBudgetedSection(lines, "Routes:", evidence.routes, 8, (row) => ` route-match ${String(row.method)} ${String(row.route)} -> ${String(row.handler)} (${String(row.file_path)}:${String(row.line)})`);
|
|
339
|
+
pushBudgetedSection(lines, "Imports:", evidence.imports, 8, (row) => ` import-match ${String(row.from_file)}:${String(row.line)} -> ${String(row.to_ref)}${row.imported ? ` (${String(row.imported)})` : ""}`);
|
|
340
|
+
pushBudgetedSection(lines, "Incoming edges:", evidence.incomingEdges, 8, (row) => ` edge-in ${String(row.kind)} ${String(row.source)} -> ${String(row.target)} (${String(row.file_path)}:${String(row.line)})`);
|
|
341
|
+
pushBudgetedSection(lines, "Outgoing edges:", evidence.outgoingEdges, 8, (row) => ` edge-out ${String(row.kind)} ${String(row.source)} -> ${String(row.target)} (${String(row.file_path)}:${String(row.line)})`);
|
|
342
|
+
pushBudgetedSection(lines, "Owners:", evidence.owners, 6, (row) => ` owner ${row.owner} (${row.owner_source}, ${row.files} files): ${row.sample_files.join(", ")}`);
|
|
1860
343
|
return finalizeCodeContextPack(lines.join("\n"));
|
|
1861
344
|
}
|
|
1862
|
-
function snapshotRows(database, sql) {
|
|
1863
|
-
return database.prepare(sql).all().map((row) => {
|
|
1864
|
-
const normalized = {};
|
|
1865
|
-
for (const key of Object.keys(row).sort()) {
|
|
1866
|
-
const value = row[key];
|
|
1867
|
-
normalized[key] = typeof value === "string" || typeof value === "number" || value === null ? value : String(value);
|
|
1868
|
-
}
|
|
1869
|
-
return normalized;
|
|
1870
|
-
});
|
|
1871
|
-
}
|
|
1872
|
-
function codeIndexSnapshot(database) {
|
|
1873
|
-
return {
|
|
1874
|
-
configs: snapshotRows(database, "SELECT file_path, line, key, value FROM configs ORDER BY file_path, line, key, value"),
|
|
1875
|
-
edges: snapshotRows(database, "SELECT file_path, line, kind, source_kind, source, target_kind, target, evidence FROM edges ORDER BY file_path, line, kind, source, target, evidence"),
|
|
1876
|
-
files: snapshotRows(database, "SELECT path, language, profile, kind, lines, bytes FROM files ORDER BY path"),
|
|
1877
|
-
imports: snapshotRows(database, "SELECT from_file, line, to_ref, imported, raw FROM imports ORDER BY from_file, line, to_ref, imported, raw"),
|
|
1878
|
-
routes: snapshotRows(database, "SELECT file_path, line, method, route, handler FROM routes ORDER BY file_path, line, method, route, handler"),
|
|
1879
|
-
symbols: snapshotRows(database, "SELECT file_path, line, kind, name, signature FROM symbols ORDER BY file_path, line, kind, name, signature"),
|
|
1880
|
-
};
|
|
1881
|
-
}
|
|
1882
345
|
// Error thrown when the code-evidence index is missing or schema-incompatible.
|
|
1883
346
|
// The MCP server catches this to return an isError tool result (tools/list still
|
|
1884
347
|
// works); CLI modes keep their own process.exit path via requireExistingIndex.
|
|
@@ -1902,16 +365,16 @@ function openCodeEvidenceDatabaseForServing() {
|
|
|
1902
365
|
const database = openDatabase(databasePath.absolutePath);
|
|
1903
366
|
let schemaVersion = "";
|
|
1904
367
|
try {
|
|
1905
|
-
schemaVersion = readMetaValue(database, "schema_version");
|
|
368
|
+
schemaVersion = (0, schema_1.readMetaValue)(database, "schema_version");
|
|
1906
369
|
}
|
|
1907
370
|
catch (error) {
|
|
1908
371
|
database.close();
|
|
1909
372
|
const message = error instanceof Error ? error.message : String(error);
|
|
1910
373
|
throw new CodeEvidenceIndexUnavailableError(`code evidence index at ${databasePath.relativePath} is not readable; rebuild with \`project-librarian --code-index\`. Error: ${message}`);
|
|
1911
374
|
}
|
|
1912
|
-
if (schemaVersion !== codeIndexSchemaVersion) {
|
|
375
|
+
if (schemaVersion !== schema_1.codeIndexSchemaVersion) {
|
|
1913
376
|
database.close();
|
|
1914
|
-
throw new CodeEvidenceIndexUnavailableError(
|
|
377
|
+
throw new CodeEvidenceIndexUnavailableError((0, index_health_1.formatCodeIndexHealthRemediation)(codeIndexHealth()));
|
|
1915
378
|
}
|
|
1916
379
|
database.exec("PRAGMA query_only = ON");
|
|
1917
380
|
return { database, relativePath: databasePath.relativePath };
|
|
@@ -1922,203 +385,57 @@ function prepareOutputPath() {
|
|
|
1922
385
|
(0, workspace_1.mkdirp)(code_index_file_policy_1.codeEvidenceDirectory);
|
|
1923
386
|
fs.writeFileSync((0, workspace_1.abs)(`${code_index_file_policy_1.codeEvidenceDirectory}/.gitignore`), "*\n!.gitignore\n");
|
|
1924
387
|
}
|
|
388
|
+
function codeIndexModeRuntime() {
|
|
389
|
+
return {
|
|
390
|
+
codeContextPack,
|
|
391
|
+
codeEvidenceDatabasePath,
|
|
392
|
+
codeImpact,
|
|
393
|
+
codeIndexHealth,
|
|
394
|
+
codeIndexStaleness,
|
|
395
|
+
codeReportForRequestedSection: (database, requestedSection, options) => (0, reports_1.codeReportForRequestedSection)(database, requestedSection, codeReportRuntime(database, options)),
|
|
396
|
+
codeScopes,
|
|
397
|
+
fail,
|
|
398
|
+
indexCodeFile,
|
|
399
|
+
openDatabase,
|
|
400
|
+
prepareOutputPath,
|
|
401
|
+
readCodeFileFingerprint,
|
|
402
|
+
readCodeFile,
|
|
403
|
+
removeDatabaseFiles,
|
|
404
|
+
requireExistingIndex,
|
|
405
|
+
selectedCodeParserMode,
|
|
406
|
+
warnIfCodeIndexStale,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
1925
409
|
function runCodeIndexMode() {
|
|
1926
|
-
|
|
1927
|
-
const scopes = codeScopes();
|
|
1928
|
-
const parserMode = selectedCodeParserMode();
|
|
1929
|
-
// Scale gate before ANY write or database work: below the measured threshold
|
|
1930
|
-
// the build halts with the evidence-citing warning unless --acknowledge-small-repo
|
|
1931
|
-
// was passed (2026-06-12 scale-aware guidance decision).
|
|
1932
|
-
const discoveredFiles = (0, code_index_file_policy_1.discoverCodeFiles)(scopes);
|
|
1933
|
-
const scaleGate = (0, code_index_file_policy_1.smallRepoCodeIndexGate)(discoveredFiles.length, args_1.acknowledgeSmallRepoMode);
|
|
1934
|
-
if (!scaleGate.proceed)
|
|
1935
|
-
fail(scaleGate.warning);
|
|
1936
|
-
const existingIndex = fs.existsSync(databasePath.absolutePath);
|
|
1937
|
-
if (args_1.codeIndexIncrementalMode && !existingIndex) {
|
|
1938
|
-
fail(`--incremental requires an existing compatible code evidence index: ${databasePath.relativePath}`);
|
|
1939
|
-
}
|
|
1940
|
-
let incremental = false;
|
|
1941
|
-
if (existingIndex && !args_1.codeIndexFullMode) {
|
|
1942
|
-
let compatibility = { compatible: false, reason: "compatibility was not checked" };
|
|
1943
|
-
const existingDatabase = openDatabase(databasePath.absolutePath);
|
|
1944
|
-
try {
|
|
1945
|
-
compatibility = incrementalCompatibility(existingDatabase, scopes, parserMode);
|
|
1946
|
-
}
|
|
1947
|
-
finally {
|
|
1948
|
-
existingDatabase.close();
|
|
1949
|
-
}
|
|
1950
|
-
incremental = !args_1.codeIndexFullMode && compatibility.compatible;
|
|
1951
|
-
if (args_1.codeIndexIncrementalMode && !compatibility.compatible)
|
|
1952
|
-
fail(`--incremental cannot update ${databasePath.relativePath}: ${compatibility.reason}`);
|
|
1953
|
-
}
|
|
1954
|
-
prepareOutputPath();
|
|
1955
|
-
if (!incremental)
|
|
1956
|
-
removeDatabaseFiles(databasePath.absolutePath);
|
|
1957
|
-
const database = openDatabase(databasePath.absolutePath);
|
|
1958
|
-
try {
|
|
1959
|
-
if (!incremental)
|
|
1960
|
-
setupDatabase(database);
|
|
1961
|
-
const statements = createIndexStatements(database);
|
|
1962
|
-
const currentFiles = discoveredFiles.map((filePath) => readCodeFile(filePath, parserMode));
|
|
1963
|
-
const currentByPath = new Map(currentFiles.map((file) => [file.path, file]));
|
|
1964
|
-
const indexed = incremental ? new Map(database.prepare("SELECT path, hash FROM files").all().map((row) => [String(row.path), String(row.hash)])) : new Map();
|
|
1965
|
-
const deletedPaths = incremental ? Array.from(indexed.keys()).filter((filePath) => !currentByPath.has(filePath)) : [];
|
|
1966
|
-
const reindexedFiles = incremental
|
|
1967
|
-
? currentFiles.filter((file) => indexed.get(file.path) !== file.hash)
|
|
1968
|
-
: currentFiles;
|
|
1969
|
-
const unchangedFiles = incremental ? currentFiles.length - reindexedFiles.length : 0;
|
|
1970
|
-
database.exec("BEGIN");
|
|
1971
|
-
if (!incremental)
|
|
1972
|
-
statements.insertMeta.run("created_at", new Date().toISOString());
|
|
1973
|
-
writeIndexMetadata(scopes, parserMode, statements);
|
|
1974
|
-
for (const filePath of deletedPaths)
|
|
1975
|
-
removeIndexedFile(filePath, statements);
|
|
1976
|
-
for (const file of reindexedFiles) {
|
|
1977
|
-
if (incremental && indexed.has(file.path))
|
|
1978
|
-
removeIndexedFile(file.path, statements);
|
|
1979
|
-
indexCodeFile(file, statements);
|
|
1980
|
-
}
|
|
1981
|
-
database.exec("COMMIT");
|
|
1982
|
-
console.log("Project wiki code evidence index complete.");
|
|
1983
|
-
console.log(`database: ${databasePath.relativePath}`);
|
|
1984
|
-
console.log(`mode: ${incremental ? "incremental" : "full"}`);
|
|
1985
|
-
console.log(`parser_mode: ${parserMode}`);
|
|
1986
|
-
console.log(`scopes: ${scopes.join(", ")}`);
|
|
1987
|
-
console.log(`files: ${currentFiles.length}`);
|
|
1988
|
-
console.log(`reindexed_files: ${reindexedFiles.length}`);
|
|
1989
|
-
console.log(`deleted_files: ${deletedPaths.length}`);
|
|
1990
|
-
console.log(`unchanged_files: ${unchangedFiles}`);
|
|
1991
|
-
}
|
|
1992
|
-
catch (error) {
|
|
1993
|
-
try {
|
|
1994
|
-
database.exec("ROLLBACK");
|
|
1995
|
-
}
|
|
1996
|
-
catch {
|
|
1997
|
-
// Ignore rollback failures after setup errors.
|
|
1998
|
-
}
|
|
1999
|
-
throw error;
|
|
2000
|
-
}
|
|
2001
|
-
finally {
|
|
2002
|
-
database.close();
|
|
2003
|
-
}
|
|
410
|
+
(0, modes_1.runCodeIndexMode)(codeIndexModeRuntime());
|
|
2004
411
|
}
|
|
2005
412
|
function runCodeQueryMode() {
|
|
2006
|
-
|
|
2007
|
-
console.error("missing SQL: use --code-query \"select ...\"");
|
|
2008
|
-
process.exit(1);
|
|
2009
|
-
}
|
|
2010
|
-
requireExistingIndex();
|
|
2011
|
-
if (!(0, code_index_sql_1.isReadOnlySql)(args_1.codeQuerySql)) {
|
|
2012
|
-
console.error("code queries must be read-only SQL starting with SELECT or WITH");
|
|
2013
|
-
process.exit(1);
|
|
2014
|
-
}
|
|
2015
|
-
const database = openDatabase(codeEvidenceDatabasePath().absolutePath);
|
|
2016
|
-
try {
|
|
2017
|
-
database.exec("PRAGMA query_only = ON");
|
|
2018
|
-
warnIfCodeIndexStale(database);
|
|
2019
|
-
printRows(database.prepare(args_1.codeQuerySql).all());
|
|
2020
|
-
}
|
|
2021
|
-
finally {
|
|
2022
|
-
database.close();
|
|
2023
|
-
}
|
|
413
|
+
(0, modes_1.runCodeQueryMode)(codeIndexModeRuntime());
|
|
2024
414
|
}
|
|
2025
415
|
function runCodeReportMode() {
|
|
2026
|
-
|
|
2027
|
-
const database = openDatabase(codeEvidenceDatabasePath().absolutePath);
|
|
2028
|
-
try {
|
|
2029
|
-
warnIfCodeIndexStale(database);
|
|
2030
|
-
printJson(codeReportForRequestedSection(database));
|
|
2031
|
-
}
|
|
2032
|
-
finally {
|
|
2033
|
-
database.close();
|
|
2034
|
-
}
|
|
416
|
+
(0, modes_1.runCodeReportMode)(codeIndexModeRuntime());
|
|
2035
417
|
}
|
|
2036
418
|
function runCodeStatusMode() {
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
SELECT 'files' AS metric, count(*) AS value FROM files
|
|
2042
|
-
UNION ALL SELECT 'symbols', count(*) FROM symbols
|
|
2043
|
-
UNION ALL SELECT 'imports', count(*) FROM imports
|
|
2044
|
-
UNION ALL SELECT 'routes', count(*) FROM routes
|
|
2045
|
-
UNION ALL SELECT 'edges', count(*) FROM edges
|
|
2046
|
-
UNION ALL SELECT 'configs', count(*) FROM configs
|
|
2047
|
-
`).all();
|
|
2048
|
-
const staleness = codeIndexStaleness(database);
|
|
2049
|
-
rows.push({ metric: "stale_files", value: staleness.added + staleness.changed + staleness.deleted }, { metric: "stale_changed_files", value: staleness.changed }, { metric: "stale_added_files", value: staleness.added }, { metric: "stale_deleted_files", value: staleness.deleted });
|
|
2050
|
-
printRows(rows);
|
|
2051
|
-
}
|
|
2052
|
-
finally {
|
|
2053
|
-
database.close();
|
|
2054
|
-
}
|
|
419
|
+
(0, modes_1.runCodeStatusMode)(codeIndexModeRuntime());
|
|
420
|
+
}
|
|
421
|
+
function runCodeIndexHealthMode() {
|
|
422
|
+
(0, modes_1.runCodeIndexHealthMode)(codeIndexModeRuntime());
|
|
2055
423
|
}
|
|
2056
424
|
function runCodeFilesMode() {
|
|
2057
|
-
|
|
2058
|
-
const database = openDatabase(codeEvidenceDatabasePath().absolutePath);
|
|
2059
|
-
try {
|
|
2060
|
-
warnIfCodeIndexStale(database);
|
|
2061
|
-
printRows(database.prepare("SELECT path, language, profile, kind, lines, bytes FROM files ORDER BY path").all());
|
|
2062
|
-
}
|
|
2063
|
-
finally {
|
|
2064
|
-
database.close();
|
|
2065
|
-
}
|
|
425
|
+
(0, modes_1.runCodeFilesMode)(codeIndexModeRuntime());
|
|
2066
426
|
}
|
|
2067
427
|
function runCodeImpactMode() {
|
|
2068
|
-
|
|
2069
|
-
console.error("missing impact target: use --code-impact \"path-or-symbol-or-module\"");
|
|
2070
|
-
process.exit(1);
|
|
2071
|
-
}
|
|
2072
|
-
requireExistingIndex();
|
|
2073
|
-
const database = openDatabase(codeEvidenceDatabasePath().absolutePath);
|
|
2074
|
-
try {
|
|
2075
|
-
warnIfCodeIndexStale(database);
|
|
2076
|
-
printJson(codeImpact(database, args_1.codeImpactTarget.trim()));
|
|
2077
|
-
}
|
|
2078
|
-
finally {
|
|
2079
|
-
database.close();
|
|
2080
|
-
}
|
|
428
|
+
(0, modes_1.runCodeImpactMode)(codeIndexModeRuntime());
|
|
2081
429
|
}
|
|
2082
430
|
function runCodeContextPackMode() {
|
|
2083
|
-
|
|
2084
|
-
console.error("missing context pack query: use --code-context-pack \"path-or-symbol-or-route\"");
|
|
2085
|
-
process.exit(1);
|
|
2086
|
-
}
|
|
2087
|
-
requireExistingIndex();
|
|
2088
|
-
const database = openDatabase(codeEvidenceDatabasePath().absolutePath);
|
|
2089
|
-
try {
|
|
2090
|
-
warnIfCodeIndexStale(database);
|
|
2091
|
-
console.log(codeContextPack(database, args_1.codeContextPackTarget.trim()));
|
|
2092
|
-
}
|
|
2093
|
-
finally {
|
|
2094
|
-
database.close();
|
|
2095
|
-
}
|
|
431
|
+
(0, modes_1.runCodeContextPackMode)(codeIndexModeRuntime());
|
|
2096
432
|
}
|
|
2097
433
|
function runCodeSearchSymbolMode() {
|
|
2098
|
-
|
|
2099
|
-
console.error("missing symbol search term: use --code-search-symbol \"term\"");
|
|
2100
|
-
process.exit(1);
|
|
2101
|
-
}
|
|
2102
|
-
requireExistingIndex();
|
|
2103
|
-
const database = openDatabase(codeEvidenceDatabasePath().absolutePath);
|
|
2104
|
-
try {
|
|
2105
|
-
warnIfCodeIndexStale(database);
|
|
2106
|
-
printRows(searchSymbols(database, args_1.codeSearchSymbol.trim()));
|
|
2107
|
-
}
|
|
2108
|
-
finally {
|
|
2109
|
-
database.close();
|
|
2110
|
-
}
|
|
434
|
+
(0, modes_1.runCodeSearchSymbolMode)(codeIndexModeRuntime());
|
|
2111
435
|
}
|
|
2112
436
|
function isCodeEvidenceMode() {
|
|
2113
|
-
return
|
|
437
|
+
return (0, modes_1.isCodeEvidenceMode)();
|
|
2114
438
|
}
|
|
2115
439
|
function isCodeEvidenceModeFor(flags) {
|
|
2116
|
-
return
|
|
2117
|
-
|| flags.codeIndexMode
|
|
2118
|
-
|| Boolean(flags.codeQuerySql)
|
|
2119
|
-
|| flags.codeReportMode
|
|
2120
|
-
|| flags.codeStatusMode
|
|
2121
|
-
|| flags.codeFilesMode
|
|
2122
|
-
|| flags.codeImpactMode
|
|
2123
|
-
|| Boolean(flags.codeSearchSymbol);
|
|
440
|
+
return (0, modes_1.isCodeEvidenceModeFor)(flags);
|
|
2124
441
|
}
|