@kage-core/kage-graph-mcp 1.1.29 → 1.1.30
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 +2 -0
- package/dist/index.js +1 -1
- package/dist/kernel.js +75 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,8 @@ Restart your agent once after setup so MCP tools reload.
|
|
|
38
38
|
and code explanations
|
|
39
39
|
- a code graph for files, symbols, imports, calls, routes, tests, and packages,
|
|
40
40
|
including generic call/test signals and mixed-language framework routes
|
|
41
|
+
- conservative cleanup candidates for unreferenced files, unused exports, and
|
|
42
|
+
internal-looking unused symbols
|
|
41
43
|
- memory-code links so project knowledge points at the code it affects
|
|
42
44
|
- decision intelligence for why-memory coverage, stale/weak packets, and
|
|
43
45
|
important files that still lack linked repo knowledge
|
package/dist/index.js
CHANGED
|
@@ -221,7 +221,7 @@ function listTools() {
|
|
|
221
221
|
},
|
|
222
222
|
{
|
|
223
223
|
name: "kage_cleanup_candidates",
|
|
224
|
-
description: "Find conservative cleanup candidates from Kage's code graph. Reports unreferenced source files with confidence and reasons; never auto-deletes.",
|
|
224
|
+
description: "Find conservative cleanup candidates from Kage's code graph. Reports unreferenced source files, unused exports, and internal-looking unused symbols with confidence and reasons; never auto-deletes.",
|
|
225
225
|
inputSchema: {
|
|
226
226
|
type: "object",
|
|
227
227
|
properties: {
|
package/dist/kernel.js
CHANGED
|
@@ -5501,6 +5501,26 @@ function hasRuntimePathReference(projectDir, graph, target) {
|
|
|
5501
5501
|
}
|
|
5502
5502
|
return false;
|
|
5503
5503
|
}
|
|
5504
|
+
function cleanupSymbolKind(symbol) {
|
|
5505
|
+
return ["function", "method", "class", "constant"].includes(symbol.kind);
|
|
5506
|
+
}
|
|
5507
|
+
function symbolCleanupCandidate(symbol, kind, reasons, score, coveredByTests, git) {
|
|
5508
|
+
return {
|
|
5509
|
+
path: symbol.path,
|
|
5510
|
+
kind,
|
|
5511
|
+
symbol_id: symbol.id,
|
|
5512
|
+
symbol_name: symbol.name,
|
|
5513
|
+
line: symbol.line,
|
|
5514
|
+
confidence: cleanupConfidence(score),
|
|
5515
|
+
score,
|
|
5516
|
+
reasons,
|
|
5517
|
+
inbound_imports: 0,
|
|
5518
|
+
source_inbound_imports: 0,
|
|
5519
|
+
outbound_imports: 0,
|
|
5520
|
+
covered_by_tests: coveredByTests,
|
|
5521
|
+
last_commit_at: git?.last_commit_at ?? null,
|
|
5522
|
+
};
|
|
5523
|
+
}
|
|
5504
5524
|
function kageCleanupCandidates(projectDir) {
|
|
5505
5525
|
const graph = readCurrentCodeGraph(projectDir) ?? buildCodeGraph(projectDir);
|
|
5506
5526
|
const fileByPath = new Map(graph.files.map((file) => [file.path, file]));
|
|
@@ -5525,6 +5545,7 @@ function kageCleanupCandidates(projectDir) {
|
|
|
5525
5545
|
warnings.push("Git history is unavailable, so cleanup confidence does not use recency.");
|
|
5526
5546
|
const candidates = [];
|
|
5527
5547
|
const skippedRuntimeReferences = [];
|
|
5548
|
+
const wholeFileCandidates = new Set();
|
|
5528
5549
|
for (const file of graph.files) {
|
|
5529
5550
|
if (file.kind !== "source")
|
|
5530
5551
|
continue;
|
|
@@ -5577,6 +5598,60 @@ function kageCleanupCandidates(projectDir) {
|
|
|
5577
5598
|
covered_by_tests: coveredByTests,
|
|
5578
5599
|
last_commit_at: git?.last_commit_at ?? null,
|
|
5579
5600
|
});
|
|
5601
|
+
wholeFileCandidates.add(file.path);
|
|
5602
|
+
}
|
|
5603
|
+
const calledSymbols = new Set(graph.calls.map((call) => call.to_symbol));
|
|
5604
|
+
const routeHandlers = new Set(graph.routes.map((route) => route.handler_symbol).filter((value) => Boolean(value)));
|
|
5605
|
+
const coveredSymbolNames = new Set(graph.tests.map((test) => test.covers_symbol?.toLowerCase()).filter((value) => Boolean(value)));
|
|
5606
|
+
const symbolsByPath = new Map();
|
|
5607
|
+
for (const symbol of graph.symbols.filter(cleanupSymbolKind)) {
|
|
5608
|
+
const list = symbolsByPath.get(symbol.path) ?? [];
|
|
5609
|
+
list.push(symbol);
|
|
5610
|
+
symbolsByPath.set(symbol.path, list);
|
|
5611
|
+
}
|
|
5612
|
+
const importedNamesByPath = new Map();
|
|
5613
|
+
for (const edge of graph.imports) {
|
|
5614
|
+
if (!edge.to_path || !edge.imported.length)
|
|
5615
|
+
continue;
|
|
5616
|
+
const names = importedNamesByPath.get(edge.to_path) ?? new Set();
|
|
5617
|
+
for (const name of edge.imported)
|
|
5618
|
+
names.add(name);
|
|
5619
|
+
importedNamesByPath.set(edge.to_path, names);
|
|
5620
|
+
}
|
|
5621
|
+
for (const file of graph.files) {
|
|
5622
|
+
if (file.kind !== "source" || wholeFileCandidates.has(file.path))
|
|
5623
|
+
continue;
|
|
5624
|
+
if (isEntrypointLike(file.path) || routeFiles.has(file.path))
|
|
5625
|
+
continue;
|
|
5626
|
+
const fileSymbols = symbolsByPath.get(file.path) ?? [];
|
|
5627
|
+
if (!fileSymbols.length)
|
|
5628
|
+
continue;
|
|
5629
|
+
const git = hasGit ? gitFileSignal(projectDir, file.path, graphPaths) : null;
|
|
5630
|
+
const coveredByTests = hasTestCoverage(file.path, graph);
|
|
5631
|
+
const importedNames = importedNamesByPath.get(file.path) ?? new Set();
|
|
5632
|
+
const exportedSymbols = fileSymbols.filter((symbol) => symbol.export);
|
|
5633
|
+
const hasMatchedNamedExport = exportedSymbols.some((symbol) => importedNames.has(symbol.name));
|
|
5634
|
+
for (const symbol of fileSymbols) {
|
|
5635
|
+
const symbolReferenced = calledSymbols.has(symbol.id) || routeHandlers.has(symbol.id) || coveredSymbolNames.has(symbol.name.toLowerCase());
|
|
5636
|
+
if (symbolReferenced)
|
|
5637
|
+
continue;
|
|
5638
|
+
if (symbol.export) {
|
|
5639
|
+
if (!hasMatchedNamedExport || importedNames.has(symbol.name))
|
|
5640
|
+
continue;
|
|
5641
|
+
candidates.push(symbolCleanupCandidate(symbol, "unused_export", [
|
|
5642
|
+
`export "${symbol.name}" is not imported by current named import edges`,
|
|
5643
|
+
"symbol is not a known call target, route handler, or covered test target",
|
|
5644
|
+
"file has at least one other exported symbol imported by name",
|
|
5645
|
+
], 0.62, coveredByTests, git));
|
|
5646
|
+
}
|
|
5647
|
+
else if (/^_[A-Za-z0-9_]+/.test(symbol.name)) {
|
|
5648
|
+
candidates.push(symbolCleanupCandidate(symbol, "unused_internal_symbol", [
|
|
5649
|
+
`internal-looking symbol "${symbol.name}" has no known call edge`,
|
|
5650
|
+
"symbol is not a route handler or covered test target",
|
|
5651
|
+
"candidate is review input only; dynamic references may exist",
|
|
5652
|
+
], 0.5, coveredByTests, git));
|
|
5653
|
+
}
|
|
5654
|
+
}
|
|
5580
5655
|
}
|
|
5581
5656
|
candidates.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
|
|
5582
5657
|
return {
|