@kage-core/kage-graph-mcp 1.1.18 → 1.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -17
- package/dist/cli.js +8 -3
- package/dist/index.js +4 -3
- package/dist/kernel.js +215 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,18 +9,16 @@ This package exposes two surfaces:
|
|
|
9
9
|
|
|
10
10
|
## Latest Release
|
|
11
11
|
|
|
12
|
-
`1.1.
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
- `kage init` remains a packet-only bootstrap path; full graph generation stays
|
|
23
|
-
with `kage refresh` and `kage index`.
|
|
12
|
+
`1.1.20` publishes the large-repo indexing pass:
|
|
13
|
+
|
|
14
|
+
- repeated `kage refresh` calls reuse unchanged code graph artifacts by source
|
|
15
|
+
stat fingerprint, with `kage refresh --full` available for intentional clean
|
|
16
|
+
rebuilds.
|
|
17
|
+
- `kage code-index` prefers SCIP via `scip-typescript` plus the `scip` CLI when
|
|
18
|
+
those tools are installed, then falls back to Kage's built-in LSP-compatible
|
|
19
|
+
symbol index.
|
|
20
|
+
- read-only commands and MCP sessions reuse current graph artifacts instead of
|
|
21
|
+
rebuilding them when inputs are fresh.
|
|
24
22
|
|
|
25
23
|
`1.1.17` publishes content-based graph freshness:
|
|
26
24
|
|
|
@@ -116,6 +114,7 @@ kage policy --project /path/to/repo
|
|
|
116
114
|
kage doctor --project /path/to/repo
|
|
117
115
|
kage index --project /path/to/repo
|
|
118
116
|
kage refresh --project /path/to/repo
|
|
117
|
+
kage refresh --project /path/to/repo --full
|
|
119
118
|
kage branch --project /path/to/repo
|
|
120
119
|
kage code-index --project /path/to/repo
|
|
121
120
|
kage code-graph --project /path/to/repo
|
|
@@ -206,11 +205,13 @@ generic extraction in metrics and file parser coverage. This keeps installation
|
|
|
206
205
|
light while allowing teams to plug in the strongest indexer available for their
|
|
207
206
|
language stack.
|
|
208
207
|
|
|
209
|
-
`kage code-index --project <repo>`
|
|
210
|
-
|
|
208
|
+
`kage code-index --project <repo>` now tries the best external indexer first for
|
|
209
|
+
the common JS/TS case: if `scip-typescript` and the `scip` CLI are on the repo or
|
|
210
|
+
shell path, it writes `.agent_memory/code_index/scip.json` from the generated
|
|
211
|
+
SCIP index. If those tools are unavailable, it writes
|
|
212
|
+
`.agent_memory/code_index/lsp-symbols.json` using Kage's local parser. The CI,
|
|
211
213
|
PR, and sync workflows run it before refresh so the code graph has a committed
|
|
212
|
-
precise-index slot
|
|
213
|
-
artifacts from their preferred toolchain.
|
|
214
|
+
precise-index slot without making first-run setup depend on external binaries.
|
|
214
215
|
|
|
215
216
|
The memory graph follows the same product direction as temporal context graph
|
|
216
217
|
systems such as Graphiti: immutable ingestion episodes, derived entities and
|
|
@@ -258,7 +259,9 @@ Use `kage refresh --project <repo>` or the `kage_refresh` MCP tool after
|
|
|
258
259
|
meaningful file/content changes. Refresh rebuilds indexes, code graph, memory
|
|
259
260
|
graph, metrics, and stale-memory metadata. Memory is marked stale when status or
|
|
260
261
|
feedback says it is stale, its TTL expires, or grounded paths disappear. Pushes
|
|
261
|
-
and empty/same-tree commits do not need another refresh.
|
|
262
|
+
and empty/same-tree commits do not need another refresh. Use `--full` or
|
|
263
|
+
`kage_refresh` with `full: true` only when you intentionally want to bypass
|
|
264
|
+
unchanged-graph reuse and rebuild the code graph from scratch.
|
|
262
265
|
|
|
263
266
|
Use `kage gc --project <repo> --dry-run` to preview stale packet cleanup.
|
|
264
267
|
`kage gc --project <repo>` marks stale repo packets deprecated, rebuilds
|
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ Usage:
|
|
|
24
24
|
kage daemon status --project <dir> [--json]
|
|
25
25
|
kage daemon doctor --project <dir> [--json]
|
|
26
26
|
kage viewer --project <dir> [--port 3113]
|
|
27
|
-
kage refresh --project <dir> [--json]
|
|
27
|
+
kage refresh --project <dir> [--full] [--json]
|
|
28
28
|
kage gc --project <dir> [--dry-run] [--force] [--json]
|
|
29
29
|
kage pr summarize --project <dir> [--json]
|
|
30
30
|
kage pr check --project <dir> [--json]
|
|
@@ -335,7 +335,7 @@ async function main() {
|
|
|
335
335
|
return;
|
|
336
336
|
}
|
|
337
337
|
if (command === "refresh") {
|
|
338
|
-
const result = (0, kernel_js_1.refreshProject)(projectArg(args));
|
|
338
|
+
const result = (0, kernel_js_1.refreshProject)(projectArg(args), { full: args.includes("--full") });
|
|
339
339
|
if (args.includes("--json")) {
|
|
340
340
|
console.log(JSON.stringify(result, null, 2));
|
|
341
341
|
if (!result.ok)
|
|
@@ -506,7 +506,7 @@ async function main() {
|
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
508
|
if (command === "code-index") {
|
|
509
|
-
const result = (0, kernel_js_1.
|
|
509
|
+
const result = (0, kernel_js_1.writeCodeIndex)(projectArg(args));
|
|
510
510
|
if (args.includes("--json")) {
|
|
511
511
|
console.log(JSON.stringify(result, null, 2));
|
|
512
512
|
return;
|
|
@@ -516,6 +516,11 @@ async function main() {
|
|
|
516
516
|
console.log(`Path: ${result.path}`);
|
|
517
517
|
console.log(`Documents: ${result.documents}`);
|
|
518
518
|
console.log(`Symbols: ${result.symbols}`);
|
|
519
|
+
if (result.warnings.length) {
|
|
520
|
+
console.log("\nWarnings:");
|
|
521
|
+
for (const warning of result.warnings)
|
|
522
|
+
console.log(` - ${warning}`);
|
|
523
|
+
}
|
|
519
524
|
if (result.errors.length) {
|
|
520
525
|
console.log("\nErrors:");
|
|
521
526
|
for (const error of result.errors)
|
package/dist/index.js
CHANGED
|
@@ -153,6 +153,7 @@ function listTools() {
|
|
|
153
153
|
type: "object",
|
|
154
154
|
properties: {
|
|
155
155
|
project_dir: { type: "string" },
|
|
156
|
+
full: { type: "boolean", description: "Force a full code graph rebuild instead of reusing unchanged graph artifacts." },
|
|
156
157
|
},
|
|
157
158
|
required: ["project_dir"],
|
|
158
159
|
},
|
|
@@ -173,7 +174,7 @@ function listTools() {
|
|
|
173
174
|
},
|
|
174
175
|
{
|
|
175
176
|
name: "kage_code_index",
|
|
176
|
-
description: "Write
|
|
177
|
+
description: "Write external code index artifacts consumed by the code graph. Prefers SCIP when scip-typescript and scip are installed, then falls back to the built-in LSP-compatible symbol index.",
|
|
177
178
|
inputSchema: {
|
|
178
179
|
type: "object",
|
|
179
180
|
properties: {
|
|
@@ -725,7 +726,7 @@ async function callTool(name, args) {
|
|
|
725
726
|
};
|
|
726
727
|
}
|
|
727
728
|
if (name === "kage_code_index") {
|
|
728
|
-
const result = (0, kernel_js_1.
|
|
729
|
+
const result = (0, kernel_js_1.writeCodeIndex)(String(args?.project_dir ?? ""));
|
|
729
730
|
return {
|
|
730
731
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
731
732
|
isError: !result.ok,
|
|
@@ -752,7 +753,7 @@ async function callTool(name, args) {
|
|
|
752
753
|
};
|
|
753
754
|
}
|
|
754
755
|
if (name === "kage_refresh") {
|
|
755
|
-
const result = (0, kernel_js_1.refreshProject)(String(args?.project_dir ?? ""));
|
|
756
|
+
const result = (0, kernel_js_1.refreshProject)(String(args?.project_dir ?? ""), { full: Boolean(args?.full) });
|
|
756
757
|
return {
|
|
757
758
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
758
759
|
isError: !result.ok,
|
package/dist/kernel.js
CHANGED
|
@@ -64,6 +64,7 @@ exports.ensureMemoryDirs = ensureMemoryDirs;
|
|
|
64
64
|
exports.loadApprovedPackets = loadApprovedPackets;
|
|
65
65
|
exports.loadPendingPackets = loadPendingPackets;
|
|
66
66
|
exports.writeLspSymbolIndex = writeLspSymbolIndex;
|
|
67
|
+
exports.writeCodeIndex = writeCodeIndex;
|
|
67
68
|
exports.buildCodeGraph = buildCodeGraph;
|
|
68
69
|
exports.buildKnowledgeGraph = buildKnowledgeGraph;
|
|
69
70
|
exports.buildIndexes = buildIndexes;
|
|
@@ -1522,6 +1523,36 @@ function readCodeIndexManifest(projectDir) {
|
|
|
1522
1523
|
function listCodeFiles(projectDir) {
|
|
1523
1524
|
return codeIndexSelection(projectDir).files;
|
|
1524
1525
|
}
|
|
1526
|
+
function codeGraphStatFingerprint(projectDir, absoluteFiles) {
|
|
1527
|
+
const entries = [
|
|
1528
|
+
...absoluteFiles,
|
|
1529
|
+
...externalIndexFiles(projectDir).map((index) => index.path),
|
|
1530
|
+
...["package.json", "requirements.txt", "go.mod", "Cargo.toml"]
|
|
1531
|
+
.map((path) => (0, node_path_1.join)(projectDir, path))
|
|
1532
|
+
.filter((path) => (0, node_fs_1.existsSync)(path)),
|
|
1533
|
+
]
|
|
1534
|
+
.filter((path) => (0, node_fs_1.existsSync)(path))
|
|
1535
|
+
.map((path) => {
|
|
1536
|
+
const stats = (0, node_fs_1.statSync)(path);
|
|
1537
|
+
return `${projectRelative(projectDir, path)}:${stats.size}:${Math.round(stats.mtimeMs)}`;
|
|
1538
|
+
})
|
|
1539
|
+
.sort();
|
|
1540
|
+
return sha256Hex(entries.join("\n"));
|
|
1541
|
+
}
|
|
1542
|
+
function readCachedCodeGraph(projectDir, fingerprint) {
|
|
1543
|
+
const path = (0, node_path_1.join)(codeGraphDir(projectDir), "graph.json");
|
|
1544
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
1545
|
+
return null;
|
|
1546
|
+
try {
|
|
1547
|
+
const graph = readJson(path);
|
|
1548
|
+
if (readCodeIndexManifest(projectDir).fingerprint !== fingerprint)
|
|
1549
|
+
return null;
|
|
1550
|
+
return graph;
|
|
1551
|
+
}
|
|
1552
|
+
catch {
|
|
1553
|
+
return null;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1525
1556
|
function fileFactCacheDir(projectDir) {
|
|
1526
1557
|
return (0, node_path_1.join)(codeGraphDir(projectDir), "file-cache");
|
|
1527
1558
|
}
|
|
@@ -2147,6 +2178,8 @@ function externalSymbol(projectDir, parser, input) {
|
|
|
2147
2178
|
}
|
|
2148
2179
|
function parseKageExternalIndex(projectDir, parser, path) {
|
|
2149
2180
|
const raw = readJson(path);
|
|
2181
|
+
if (Array.isArray(raw.documents))
|
|
2182
|
+
return parseScipJsonObject(projectDir, raw);
|
|
2150
2183
|
const symbols = Array.isArray(raw.symbols)
|
|
2151
2184
|
? raw.symbols.flatMap((item) => isRecord(item) ? [externalSymbol(projectDir, parser, item)].filter(Boolean) : [])
|
|
2152
2185
|
: [];
|
|
@@ -2178,6 +2211,62 @@ function parseKageExternalIndex(projectDir, parser, path) {
|
|
|
2178
2211
|
: [];
|
|
2179
2212
|
return { symbols, imports, calls };
|
|
2180
2213
|
}
|
|
2214
|
+
function scipSymbolName(symbol) {
|
|
2215
|
+
const local = symbol.trim().split(/\s+/).at(-1) ?? symbol;
|
|
2216
|
+
const segment = local.split(/[\/#.`:]/).filter(Boolean).at(-1) ?? local;
|
|
2217
|
+
return segment.replace(/\(\)?$/, "").replace(/\.$/, "") || symbol;
|
|
2218
|
+
}
|
|
2219
|
+
function scipRangeLine(input) {
|
|
2220
|
+
if (Array.isArray(input) && typeof input[0] === "number")
|
|
2221
|
+
return Math.max(1, input[0] + 1);
|
|
2222
|
+
if (isRecord(input) && isRecord(input.start) && typeof input.start.line === "number")
|
|
2223
|
+
return Math.max(1, input.start.line + 1);
|
|
2224
|
+
return 1;
|
|
2225
|
+
}
|
|
2226
|
+
function parseScipJsonObject(projectDir, raw) {
|
|
2227
|
+
const symbols = [];
|
|
2228
|
+
const calls = [];
|
|
2229
|
+
const symbolInfo = new Map();
|
|
2230
|
+
const docs = Array.isArray(raw.documents) ? raw.documents : [];
|
|
2231
|
+
for (const doc of docs) {
|
|
2232
|
+
if (!isRecord(doc))
|
|
2233
|
+
continue;
|
|
2234
|
+
const rel = String(doc.relativePath ?? doc.relative_path ?? doc.path ?? doc.uri ?? "").replace(/^file:\/\//, "").replace(/\\/g, "/");
|
|
2235
|
+
if (!rel)
|
|
2236
|
+
continue;
|
|
2237
|
+
for (const item of Array.isArray(doc.symbols) ? doc.symbols : []) {
|
|
2238
|
+
if (isRecord(item) && typeof item.symbol === "string")
|
|
2239
|
+
symbolInfo.set(item.symbol, item);
|
|
2240
|
+
}
|
|
2241
|
+
for (const occurrence of Array.isArray(doc.occurrences) ? doc.occurrences : []) {
|
|
2242
|
+
if (!isRecord(occurrence) || typeof occurrence.symbol !== "string")
|
|
2243
|
+
continue;
|
|
2244
|
+
const role = Number(occurrence.symbolRoles ?? occurrence.symbol_roles ?? 0);
|
|
2245
|
+
const line = scipRangeLine(occurrence.range);
|
|
2246
|
+
const name = scipSymbolName(occurrence.symbol);
|
|
2247
|
+
if (!name || name === "local")
|
|
2248
|
+
continue;
|
|
2249
|
+
if ((role & 1) === 1) {
|
|
2250
|
+
const info = symbolInfo.get(occurrence.symbol) ?? {};
|
|
2251
|
+
const detail = Array.isArray(info.documentation) ? info.documentation.map(String).find(Boolean) : undefined;
|
|
2252
|
+
const symbol = externalSymbol(projectDir, "scip", {
|
|
2253
|
+
path: rel,
|
|
2254
|
+
name,
|
|
2255
|
+
kind: occurrence.syntaxKind ?? occurrence.syntax_kind ?? info.kind,
|
|
2256
|
+
line,
|
|
2257
|
+
signature: detail ?? name,
|
|
2258
|
+
exported: !occurrence.symbol.startsWith("local "),
|
|
2259
|
+
});
|
|
2260
|
+
if (symbol && !symbols.some((candidate) => candidate.id === symbol.id))
|
|
2261
|
+
symbols.push(symbol);
|
|
2262
|
+
}
|
|
2263
|
+
else {
|
|
2264
|
+
calls.push({ from_symbol: null, to_symbol: name, path: rel, line });
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
return { symbols, imports: [], calls };
|
|
2269
|
+
}
|
|
2181
2270
|
function parseLspDocumentSymbols(projectDir, path) {
|
|
2182
2271
|
const raw = readJson(path);
|
|
2183
2272
|
const docs = Array.isArray(raw) ? raw : isRecord(raw) && Array.isArray(raw.documents) ? raw.documents : [];
|
|
@@ -2250,9 +2339,119 @@ function writeLspSymbolIndex(projectDir) {
|
|
|
2250
2339
|
parser: "lsp",
|
|
2251
2340
|
documents: documents.length,
|
|
2252
2341
|
symbols: symbolCount,
|
|
2342
|
+
warnings: [],
|
|
2253
2343
|
errors,
|
|
2254
2344
|
};
|
|
2255
2345
|
}
|
|
2346
|
+
function executableOnPath(projectDir, command) {
|
|
2347
|
+
const local = (0, node_path_1.join)(projectDir, "node_modules", ".bin", command);
|
|
2348
|
+
if ((0, node_fs_1.existsSync)(local))
|
|
2349
|
+
return local;
|
|
2350
|
+
const localCmd = `${local}.cmd`;
|
|
2351
|
+
if ((0, node_fs_1.existsSync)(localCmd))
|
|
2352
|
+
return localCmd;
|
|
2353
|
+
for (const entry of (process.env.PATH ?? "").split(node_path_1.delimiter).filter(Boolean)) {
|
|
2354
|
+
const candidate = (0, node_path_1.join)(entry, command);
|
|
2355
|
+
if ((0, node_fs_1.existsSync)(candidate))
|
|
2356
|
+
return candidate;
|
|
2357
|
+
const cmdCandidate = `${candidate}.cmd`;
|
|
2358
|
+
if ((0, node_fs_1.existsSync)(cmdCandidate))
|
|
2359
|
+
return cmdCandidate;
|
|
2360
|
+
}
|
|
2361
|
+
return null;
|
|
2362
|
+
}
|
|
2363
|
+
function hasTypeScriptCode(projectDir) {
|
|
2364
|
+
return listCodeFiles(projectDir).some((path) => TS_AST_EXTENSIONS.has(extensionOf(path)));
|
|
2365
|
+
}
|
|
2366
|
+
function scipCliJson(scipCli, scipPath, projectDir) {
|
|
2367
|
+
try {
|
|
2368
|
+
return (0, node_child_process_1.execFileSync)(scipCli, ["print", "--json", scipPath], {
|
|
2369
|
+
cwd: projectDir,
|
|
2370
|
+
encoding: "utf8",
|
|
2371
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
catch {
|
|
2375
|
+
return (0, node_child_process_1.execFileSync)(scipCli, ["print", scipPath, "--json"], {
|
|
2376
|
+
cwd: projectDir,
|
|
2377
|
+
encoding: "utf8",
|
|
2378
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
function writeScipTypescriptIndex(projectDir) {
|
|
2383
|
+
if (!hasTypeScriptCode(projectDir))
|
|
2384
|
+
return null;
|
|
2385
|
+
const scipTypescript = executableOnPath(projectDir, "scip-typescript");
|
|
2386
|
+
if (!scipTypescript)
|
|
2387
|
+
return null;
|
|
2388
|
+
const scipCli = executableOnPath(projectDir, "scip");
|
|
2389
|
+
const outDir = (0, node_path_1.join)(memoryRoot(projectDir), "code_index");
|
|
2390
|
+
ensureDir(outDir);
|
|
2391
|
+
const scipPath = (0, node_path_1.join)(outDir, "index.scip");
|
|
2392
|
+
const outPath = (0, node_path_1.join)(outDir, "scip.json");
|
|
2393
|
+
const warnings = [];
|
|
2394
|
+
const errors = [];
|
|
2395
|
+
try {
|
|
2396
|
+
const args = ["index"];
|
|
2397
|
+
if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, "tsconfig.json")))
|
|
2398
|
+
args.push("--infer-tsconfig");
|
|
2399
|
+
(0, node_child_process_1.execFileSync)(scipTypescript, args, { cwd: projectDir, stdio: ["ignore", "pipe", "pipe"] });
|
|
2400
|
+
const generatedScipPath = (0, node_path_1.join)(projectDir, "index.scip");
|
|
2401
|
+
if ((0, node_fs_1.existsSync)(generatedScipPath))
|
|
2402
|
+
(0, node_fs_1.renameSync)(generatedScipPath, scipPath);
|
|
2403
|
+
if (!(0, node_fs_1.existsSync)(scipPath))
|
|
2404
|
+
throw new Error("scip-typescript completed but did not write index.scip");
|
|
2405
|
+
}
|
|
2406
|
+
catch (error) {
|
|
2407
|
+
errors.push(`scip-typescript failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2408
|
+
return { ok: false, project_dir: projectDir, path: scipPath, parser: "scip", documents: 0, symbols: 0, warnings, errors };
|
|
2409
|
+
}
|
|
2410
|
+
if (!scipCli) {
|
|
2411
|
+
warnings.push("scip-typescript wrote index.scip, but the scip CLI is not installed so Kage could not convert it into graph facts.");
|
|
2412
|
+
return { ok: false, project_dir: projectDir, path: scipPath, parser: "scip", documents: 0, symbols: 0, warnings, errors };
|
|
2413
|
+
}
|
|
2414
|
+
try {
|
|
2415
|
+
const raw = JSON.parse(scipCliJson(scipCli, scipPath, projectDir));
|
|
2416
|
+
const facts = parseScipJsonObject(projectDir, raw);
|
|
2417
|
+
writeJson(outPath, {
|
|
2418
|
+
schema_version: 1,
|
|
2419
|
+
generator: "scip-typescript",
|
|
2420
|
+
generated_at: nowIso(),
|
|
2421
|
+
source_artifact: (0, node_path_1.relative)(projectDir, scipPath).replace(/\\/g, "/"),
|
|
2422
|
+
symbols: facts.symbols,
|
|
2423
|
+
imports: facts.imports,
|
|
2424
|
+
calls: facts.calls,
|
|
2425
|
+
});
|
|
2426
|
+
return {
|
|
2427
|
+
ok: true,
|
|
2428
|
+
project_dir: projectDir,
|
|
2429
|
+
path: outPath,
|
|
2430
|
+
parser: "scip",
|
|
2431
|
+
documents: Array.isArray(raw.documents) ? raw.documents.length : 0,
|
|
2432
|
+
symbols: facts.symbols.length,
|
|
2433
|
+
warnings,
|
|
2434
|
+
errors,
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
catch (error) {
|
|
2438
|
+
errors.push(`scip conversion failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2439
|
+
return { ok: false, project_dir: projectDir, path: scipPath, parser: "scip", documents: 0, symbols: 0, warnings, errors };
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
function writeCodeIndex(projectDir) {
|
|
2443
|
+
const scip = writeScipTypescriptIndex(projectDir);
|
|
2444
|
+
if (scip?.ok)
|
|
2445
|
+
return scip;
|
|
2446
|
+
const lsp = writeLspSymbolIndex(projectDir);
|
|
2447
|
+
return {
|
|
2448
|
+
...lsp,
|
|
2449
|
+
warnings: [
|
|
2450
|
+
...(scip ? [...scip.warnings, ...scip.errors] : ["scip-typescript not found; used built-in LSP-compatible fallback."]),
|
|
2451
|
+
...lsp.warnings,
|
|
2452
|
+
],
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2256
2455
|
function parseLsif(projectDir, path) {
|
|
2257
2456
|
const docs = new Map();
|
|
2258
2457
|
const ranges = new Map();
|
|
@@ -2350,7 +2549,7 @@ function extractPackages(projectDir) {
|
|
|
2350
2549
|
}
|
|
2351
2550
|
return packages.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
|
|
2352
2551
|
}
|
|
2353
|
-
function buildCodeGraph(projectDir) {
|
|
2552
|
+
function buildCodeGraph(projectDir, options = {}) {
|
|
2354
2553
|
ensureMemoryDirs(projectDir);
|
|
2355
2554
|
const branch = gitBranch(projectDir);
|
|
2356
2555
|
const head = gitHead(projectDir);
|
|
@@ -2358,7 +2557,16 @@ function buildCodeGraph(projectDir) {
|
|
|
2358
2557
|
const mergeBase = gitMergeBase(projectDir);
|
|
2359
2558
|
const selection = codeIndexSelection(projectDir);
|
|
2360
2559
|
const absoluteFiles = selection.files;
|
|
2560
|
+
const fingerprint = codeGraphStatFingerprint(projectDir, absoluteFiles);
|
|
2561
|
+
const cachedGraph = options.force ? null : readCachedCodeGraph(projectDir, fingerprint);
|
|
2562
|
+
if (cachedGraph) {
|
|
2563
|
+
selection.manifest.cache = { hits: absoluteFiles.length, misses: 0 };
|
|
2564
|
+
selection.manifest.fingerprint = fingerprint;
|
|
2565
|
+
writeCodeIndexManifest(projectDir, selection.manifest);
|
|
2566
|
+
return cachedGraph;
|
|
2567
|
+
}
|
|
2361
2568
|
const inputHash = codeGraphInputHash(projectDir, absoluteFiles);
|
|
2569
|
+
selection.manifest.fingerprint = fingerprint;
|
|
2362
2570
|
writeCodeIndexManifest(projectDir, selection.manifest);
|
|
2363
2571
|
const knownFiles = new Set(absoluteFiles.map((path) => (0, node_path_1.relative)(projectDir, path).replace(/\\/g, "/")));
|
|
2364
2572
|
const files = [];
|
|
@@ -2944,9 +3152,9 @@ function currentOrBuildGraphs(projectDir) {
|
|
|
2944
3152
|
}
|
|
2945
3153
|
return buildGraphIndexes(projectDir);
|
|
2946
3154
|
}
|
|
2947
|
-
function buildGraphIndexes(projectDir) {
|
|
3155
|
+
function buildGraphIndexes(projectDir, options = {}) {
|
|
2948
3156
|
const written = buildPacketIndexes(projectDir);
|
|
2949
|
-
const codeGraph = buildCodeGraph(projectDir);
|
|
3157
|
+
const codeGraph = buildCodeGraph(projectDir, { force: options.forceCodeGraph });
|
|
2950
3158
|
const knowledgeGraph = buildKnowledgeGraph(projectDir, codeGraph);
|
|
2951
3159
|
const graphIndexPath = (0, node_path_1.join)(indexesDir(projectDir), "graph.json");
|
|
2952
3160
|
const codeGraphIndexPath = (0, node_path_1.join)(indexesDir(projectDir), "code-graph.json");
|
|
@@ -3001,7 +3209,7 @@ function indexProjectDetailed(projectDir, options = {}) {
|
|
|
3001
3209
|
const structure = createRepoStructurePacket(projectDir);
|
|
3002
3210
|
if (structure)
|
|
3003
3211
|
upsertGeneratedPacket(projectDir, structure);
|
|
3004
|
-
const built = options.graphs === false ? null : buildGraphIndexes(projectDir);
|
|
3212
|
+
const built = options.graphs === false ? null : buildGraphIndexes(projectDir, { forceCodeGraph: options.full });
|
|
3005
3213
|
const indexes = built?.indexes ?? buildPacketIndexes(projectDir);
|
|
3006
3214
|
return {
|
|
3007
3215
|
result: {
|
|
@@ -3075,15 +3283,15 @@ function refreshPacketStaleness(projectDir) {
|
|
|
3075
3283
|
}
|
|
3076
3284
|
return { findings, updated };
|
|
3077
3285
|
}
|
|
3078
|
-
function refreshProject(projectDir) {
|
|
3079
|
-
const detailedIndex = indexProjectDetailed(projectDir);
|
|
3286
|
+
function refreshProject(projectDir, options = {}) {
|
|
3287
|
+
const detailedIndex = indexProjectDetailed(projectDir, { full: options.full });
|
|
3080
3288
|
const index = detailedIndex.result;
|
|
3081
3289
|
let codeGraph = detailedIndex.codeGraph;
|
|
3082
3290
|
let knowledgeGraph = detailedIndex.knowledgeGraph;
|
|
3083
3291
|
const stale = refreshPacketStaleness(projectDir);
|
|
3084
3292
|
let indexes = index.indexes;
|
|
3085
3293
|
if (stale.updated > 0) {
|
|
3086
|
-
const rebuilt = buildGraphIndexes(projectDir);
|
|
3294
|
+
const rebuilt = buildGraphIndexes(projectDir, { forceCodeGraph: options.full });
|
|
3087
3295
|
codeGraph = rebuilt.codeGraph;
|
|
3088
3296
|
knowledgeGraph = rebuilt.knowledgeGraph;
|
|
3089
3297
|
indexes = rebuilt.indexes.map((path) => (0, node_path_1.relative)(projectDir, path));
|