@kage-core/kage-graph-mcp 2.2.5 → 2.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kernel.js +36 -9
- package/package.json +1 -1
package/dist/kernel.js
CHANGED
|
@@ -8089,9 +8089,15 @@ function kageRisk(projectDir, targets = [], changedFiles = []) {
|
|
|
8089
8089
|
const graph = readCurrentCodeGraph(projectDir) ?? buildCodeGraph(projectDir);
|
|
8090
8090
|
const graphPaths = new Set(graph.files.map((file) => file.path));
|
|
8091
8091
|
const dependents = codeDependents(graph);
|
|
8092
|
-
const
|
|
8092
|
+
const explicitTargets = targets.length > 0;
|
|
8093
|
+
const resolvedTargets = unique((explicitTargets ? targets : changedFiles.length ? changedFiles : gitChangedFiles(projectDir))
|
|
8093
8094
|
.map((path) => gitPathToProjectRelative(projectDir, path) ?? path)
|
|
8094
|
-
.filter((path) => path && !isNoisePath(path))
|
|
8095
|
+
.filter((path) => path && !isNoisePath(path))
|
|
8096
|
+
// Risk is a CODE assessment. When inferring targets from the working tree,
|
|
8097
|
+
// keep only files the code graph actually knows about — memory packets,
|
|
8098
|
+
// dotfiles, and docs have no dependents/hotspot signal and are pure noise
|
|
8099
|
+
// here. Explicitly-named targets are always honored.
|
|
8100
|
+
.filter((path) => explicitTargets || graphPaths.has(path)));
|
|
8095
8101
|
const warnings = [];
|
|
8096
8102
|
if (!gitHead(projectDir))
|
|
8097
8103
|
warnings.push("Git history is unavailable, so churn, ownership, and co-change signals may be empty.");
|
|
@@ -8533,6 +8539,12 @@ const TRUTH_COMMON_SYMBOL_NAMES = new Set([
|
|
|
8533
8539
|
"build", "parse", "load", "save", "update", "delete", "remove", "test", "validate",
|
|
8534
8540
|
"format", "process", "next", "send", "write", "read", "config", "helper", "util",
|
|
8535
8541
|
]);
|
|
8542
|
+
// Convention-named local closures that collide across unrelated code by idiom,
|
|
8543
|
+
// not duplication. Kept separate from common-names so the intent is clear.
|
|
8544
|
+
const TRUTH_DUPLICATE_NAME_DENYLIST = new Set([
|
|
8545
|
+
"decorator", "wrapper", "inner", "callback", "wrapped", "fn", "cb", "noop",
|
|
8546
|
+
"predicate", "comparator", "getter", "setter", "factory", "visit",
|
|
8547
|
+
]);
|
|
8536
8548
|
function truthExcludedPath(path) {
|
|
8537
8549
|
return /(^|\/)(tests?|__tests__|specs?|examples?|fixtures?|benchmarks?|mocks?|__mocks__|vendor|node_modules|dist|build)\//i.test(path)
|
|
8538
8550
|
|| /\.(test|spec)\.[^.]+$/i.test(path)
|
|
@@ -8619,7 +8631,14 @@ function truthReport(projectDir) {
|
|
|
8619
8631
|
// 1a. Duplicate implementations: same-name symbols spread across directories.
|
|
8620
8632
|
const symbolsByName = new Map();
|
|
8621
8633
|
for (const symbol of graph.symbols) {
|
|
8622
|
-
|
|
8634
|
+
// Methods are excluded: same-named methods on different classes (__init__,
|
|
8635
|
+
// to_dict, validate, ...) are normal polymorphism, not duplication. Only
|
|
8636
|
+
// top-level functions and classes can be genuine parallel implementations.
|
|
8637
|
+
if (!["function", "class"].includes(symbol.kind))
|
|
8638
|
+
continue;
|
|
8639
|
+
// Dunders and convention-named local closures (decorator/wrapper/inner/...)
|
|
8640
|
+
// collide by language idiom across unrelated code; never real duplicates.
|
|
8641
|
+
if (/^__.*__$/.test(symbol.name) || TRUTH_DUPLICATE_NAME_DENYLIST.has(symbol.name.toLowerCase()))
|
|
8623
8642
|
continue;
|
|
8624
8643
|
if (symbol.name.length < 4 || TRUTH_COMMON_SYMBOL_NAMES.has(symbol.name.toLowerCase()))
|
|
8625
8644
|
continue;
|
|
@@ -8641,14 +8660,16 @@ function truthReport(projectDir) {
|
|
|
8641
8660
|
signatureCounts.set(normalized, (signatureCounts.get(normalized) ?? 0) + 1);
|
|
8642
8661
|
}
|
|
8643
8662
|
const signatureMatch = [...signatureCounts.values()].some((count) => count >= 2);
|
|
8663
|
+
// A same-name-only collision across dirs is weak signal and the source of
|
|
8664
|
+
// most false positives. Require a matching signature to report it at all.
|
|
8665
|
+
if (!signatureMatch)
|
|
8666
|
+
continue;
|
|
8644
8667
|
const newestEpoch = Math.max(0, ...members.map((member) => fileNewestEpoch.get(member.path) ?? 0));
|
|
8645
8668
|
const recent = hasGit && newestEpoch > aiEraCutoff;
|
|
8646
8669
|
duplicateFindings.push({
|
|
8647
8670
|
kind: "duplicate_cluster",
|
|
8648
|
-
title: `${members[0].name} — ${paths.size} implementations across ${dirs.size} directories${recent ? " [
|
|
8649
|
-
detail:
|
|
8650
|
-
? "Same name AND near-identical signature: almost certainly parallel implementations of the same idea."
|
|
8651
|
-
: "Same name in unrelated directories: agents and humans may be solving the same problem twice.",
|
|
8671
|
+
title: `${members[0].name} — ${paths.size} implementations across ${dirs.size} directories${recent ? " [recently changed]" : ""}`,
|
|
8672
|
+
detail: "Same name and near-identical signature in unrelated directories — likely parallel implementations of the same idea, worth a look.",
|
|
8652
8673
|
evidence: members.slice(0, 5).map((member) => `${member.path}:${member.line} ${member.kind} ${member.signature.slice(0, 80)}`),
|
|
8653
8674
|
surprise: Math.min(100, 45 + paths.size * 8 + (signatureMatch ? 15 : 0) + (recent ? 20 : 0)),
|
|
8654
8675
|
});
|
|
@@ -9189,9 +9210,15 @@ function renderClaudeMemAuditReceipt(report) {
|
|
|
9189
9210
|
function kageReviewerSuggestions(projectDir, targets = [], changedFiles = []) {
|
|
9190
9211
|
const graph = readCurrentCodeGraph(projectDir) ?? buildCodeGraph(projectDir);
|
|
9191
9212
|
const graphPaths = new Set(graph.files.map((file) => file.path));
|
|
9192
|
-
const
|
|
9213
|
+
const explicitTargets = targets.length > 0;
|
|
9214
|
+
const resolvedTargets = unique((explicitTargets ? targets : changedFiles.length ? changedFiles : gitChangedFiles(projectDir))
|
|
9193
9215
|
.map((path) => gitPathToProjectRelative(projectDir, path) ?? path)
|
|
9194
|
-
.filter((path) => path && !isNoisePath(path))
|
|
9216
|
+
.filter((path) => path && !isNoisePath(path))
|
|
9217
|
+
// Risk is a CODE assessment. When inferring targets from the working tree,
|
|
9218
|
+
// keep only files the code graph actually knows about — memory packets,
|
|
9219
|
+
// dotfiles, and docs have no dependents/hotspot signal and are pure noise
|
|
9220
|
+
// here. Explicitly-named targets are always honored.
|
|
9221
|
+
.filter((path) => explicitTargets || graphPaths.has(path)));
|
|
9195
9222
|
const warnings = [];
|
|
9196
9223
|
if (!gitHead(projectDir))
|
|
9197
9224
|
warnings.push("Git history is unavailable, so reviewer suggestions cannot be computed.");
|