@kage-core/kage-graph-mcp 2.2.1 → 2.2.3
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/cli.js +57 -13
- package/dist/kernel.js +60 -5
- package/package.json +1 -1
- package/viewer/index.html +12 -23
package/dist/cli.js
CHANGED
|
@@ -14,8 +14,8 @@ const CORE_USAGE = `Kage — code-grounded memory for coding agents
|
|
|
14
14
|
|
|
15
15
|
Core commands:
|
|
16
16
|
kage install [--project <dir>] one-shot: init + index + auto-wire detected agents
|
|
17
|
-
kage demo 30-second trust demo (temp dir)
|
|
18
17
|
kage scan --project <dir> 60-second truth report on any repo (zero setup)
|
|
18
|
+
kage demo watch the reject/withhold loop run in a sandbox
|
|
19
19
|
kage init --project <dir> create repo memory (.agent_memory only)
|
|
20
20
|
kage index --project <dir> [--full] build/refresh code graph + indexes
|
|
21
21
|
kage recall "<query>" --project <dir> grounded recall from repo memory
|
|
@@ -72,6 +72,7 @@ Usage:
|
|
|
72
72
|
kage slots delete --project <dir> --label <label> [--json]
|
|
73
73
|
kage handoff --project <dir> [--json]
|
|
74
74
|
kage lifecycle --project <dir> [--json]
|
|
75
|
+
kage reverify --project <dir> --packet <id> [--json]
|
|
75
76
|
kage reconcile --project <dir> [--session <id>] [--json]
|
|
76
77
|
kage timeline --project <dir> [--days <n>] [--json]
|
|
77
78
|
kage lineage --project <dir> [--json]
|
|
@@ -338,24 +339,46 @@ async function main() {
|
|
|
338
339
|
console.log(JSON.stringify(result, null, 2));
|
|
339
340
|
return;
|
|
340
341
|
}
|
|
341
|
-
console.log("Kage demo —
|
|
342
|
-
console.log(`
|
|
343
|
-
console.log("
|
|
342
|
+
console.log("Kage demo — a live experiment in a sandbox repo. Nothing here is canned:");
|
|
343
|
+
console.log(`every step below ran for real, just now, in ${result.project_dir}\n`);
|
|
344
|
+
console.log(" Created a small repo: src/auth.ts, src/payments.ts, src/legacy-retry.ts");
|
|
345
|
+
console.log(` Captured ${result.captured.length} memories, each citing real files — accepted and fingerprinted.\n`);
|
|
346
|
+
console.log("1. Then we tried to save a memory citing a file that does NOT exist:");
|
|
344
347
|
if (result.rejected_hallucination) {
|
|
345
|
-
console.log(` ✗ "${result.rejected_hallucination.title}"
|
|
348
|
+
console.log(` ✗ "${result.rejected_hallucination.title}" — REFUSED at write time:`);
|
|
346
349
|
console.log(` ${result.rejected_hallucination.error}\n`);
|
|
347
350
|
}
|
|
348
|
-
console.log("2.
|
|
351
|
+
console.log("2. Then we DELETED src/legacy-retry.ts and asked for recall again:");
|
|
349
352
|
for (const w of result.withheld)
|
|
350
|
-
console.log(` ⊘ ${w.title}\n ${w.reason}`);
|
|
351
|
-
console.log("\n3.
|
|
353
|
+
console.log(` ⊘ "${w.title}" — WITHHELD\n ${w.reason}`);
|
|
354
|
+
console.log("\n3. What recall actually returns now — only memory that still checks out:");
|
|
352
355
|
for (const t of result.recalled)
|
|
353
356
|
console.log(` ✓ ${t}`);
|
|
354
|
-
console.log(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
console.log("\n Checks: write-time rejection ✓ · stale withholding ✓ · grounded recall ✓");
|
|
358
|
+
// The sandbox proves the mechanism; the runner's own repo makes it matter.
|
|
359
|
+
const here = process.cwd();
|
|
360
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(here, ".git")) && !takeArg(args, "--project")) {
|
|
361
|
+
console.log("\nThat was a sandbox. This is YOUR repo — scanning (read-only, ~a minute)...\n");
|
|
362
|
+
try {
|
|
363
|
+
const report = (0, kernel_js_1.truthReport)(here);
|
|
364
|
+
console.log(` ${report.headline}`);
|
|
365
|
+
for (const finding of report.findings.slice(0, 3)) {
|
|
366
|
+
console.log(` • ${finding.title}`);
|
|
367
|
+
}
|
|
368
|
+
if (report.findings.length > 3)
|
|
369
|
+
console.log(` …and ${report.findings.length - 3} more.`);
|
|
370
|
+
console.log("\n Full report: npx -y @kage-core/kage-graph-mcp scan --project .");
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
console.log(" (scan skipped — run it yourself: npx -y @kage-core/kage-graph-mcp scan --project .)");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.log("\nNow point it at a real repo:");
|
|
378
|
+
console.log(" npx -y @kage-core/kage-graph-mcp scan --project . the Truth Report — what your repo is hiding");
|
|
379
|
+
}
|
|
380
|
+
console.log("\nWire it in (one command, auto-detects your agents):");
|
|
381
|
+
console.log(" npx -y @kage-core/kage-graph-mcp install");
|
|
359
382
|
return;
|
|
360
383
|
}
|
|
361
384
|
if (command === "init") {
|
|
@@ -1342,6 +1365,27 @@ async function main() {
|
|
|
1342
1365
|
}
|
|
1343
1366
|
return;
|
|
1344
1367
|
}
|
|
1368
|
+
if (command === "reverify") {
|
|
1369
|
+
const packetId = takeArg(args, "--packet");
|
|
1370
|
+
if (!packetId)
|
|
1371
|
+
usage();
|
|
1372
|
+
const result = (0, kernel_js_1.reverifyMemory)(projectArg(args), packetId);
|
|
1373
|
+
if (args.includes("--json")) {
|
|
1374
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1375
|
+
}
|
|
1376
|
+
else if (result.ok) {
|
|
1377
|
+
console.log(`Reverified ${result.packet_id}`);
|
|
1378
|
+
console.log(` grounding refreshed for ${result.refreshed_paths.length} path(s)${result.was_stale ? " · stale flag cleared" : ""}`);
|
|
1379
|
+
if (result.missing_paths.length)
|
|
1380
|
+
console.log(` dropped missing path(s): ${result.missing_paths.join(", ")}`);
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
console.log(`Reverify failed: ${result.errors.join("; ")}`);
|
|
1384
|
+
}
|
|
1385
|
+
if (!result.ok)
|
|
1386
|
+
process.exit(2);
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1345
1389
|
if (command === "reconcile" || command === "memory-reconcile" || command === "memory-reconciliation") {
|
|
1346
1390
|
const result = (0, kernel_js_1.kageMemoryReconciliation)(projectArg(args), {
|
|
1347
1391
|
sessionId: takeArg(args, "--session"),
|
package/dist/kernel.js
CHANGED
|
@@ -175,6 +175,7 @@ exports.remediationFor = remediationFor;
|
|
|
175
175
|
exports.approvePending = approvePending;
|
|
176
176
|
exports.rejectPending = rejectPending;
|
|
177
177
|
exports.changelog = changelog;
|
|
178
|
+
exports.reverifyMemory = reverifyMemory;
|
|
178
179
|
exports.supersedeMemory = supersedeMemory;
|
|
179
180
|
exports.kageMemoryLineage = kageMemoryLineage;
|
|
180
181
|
exports.kageMemoryTimeline = kageMemoryTimeline;
|
|
@@ -1554,7 +1555,7 @@ function reconciliationInstruction(items) {
|
|
|
1554
1555
|
return [
|
|
1555
1556
|
"Memory reconciliation required before final response.",
|
|
1556
1557
|
"You changed code that is linked to existing repo memory. Update the memory yourself; do not push this to the user as a manual inbox chore.",
|
|
1557
|
-
"
|
|
1558
|
+
"If the memory's claim is unchanged and only the cited code moved, run kage reverify --packet <id> to refresh its grounding in place. Otherwise call kage_learn to save the new behavior and kage_supersede when replacing the old packet, or mark stale only if the memory is no longer trusted.",
|
|
1558
1559
|
...lines,
|
|
1559
1560
|
].join("\n");
|
|
1560
1561
|
}
|
|
@@ -8801,7 +8802,10 @@ function truthReport(projectDir) {
|
|
|
8801
8802
|
// Path claims only count in prose; fenced content is sample output.
|
|
8802
8803
|
for (const candidate of entry.fence === null ? truthDocPathCandidates(entry.text) : []) {
|
|
8803
8804
|
const key = `path:${candidate}`;
|
|
8804
|
-
if
|
|
8805
|
+
// A cited path is a lie only if it resolves nowhere: docs use both
|
|
8806
|
+
// repo-root-relative paths and links relative to the doc's own dir.
|
|
8807
|
+
const resolves = (0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, candidate)) || (0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, (0, node_path_1.dirname)(entry.doc), candidate));
|
|
8808
|
+
if (seenLies.has(key) || resolves)
|
|
8805
8809
|
continue;
|
|
8806
8810
|
seenLies.add(key);
|
|
8807
8811
|
docLieFindings.push({
|
|
@@ -8883,9 +8887,9 @@ function truthReport(projectDir) {
|
|
|
8883
8887
|
findings,
|
|
8884
8888
|
warnings,
|
|
8885
8889
|
next_actions: [
|
|
8886
|
-
"kage
|
|
8887
|
-
"
|
|
8888
|
-
"kage
|
|
8890
|
+
"npx -y @kage-core/kage-graph-mcp install one command: creates repo memory + wires your agents (Claude Code, Codex, Cursor, ...)",
|
|
8891
|
+
"then just work — agents capture learnings and recall them, verified against this code",
|
|
8892
|
+
"kage gains --project . the receipt: what the memory loop saved you this week",
|
|
8889
8893
|
],
|
|
8890
8894
|
};
|
|
8891
8895
|
}
|
|
@@ -15675,6 +15679,57 @@ function packetSupersessionReason(packet) {
|
|
|
15675
15679
|
const evidence = edge ? packetEdgeValue(edge, "evidence") : "";
|
|
15676
15680
|
return evidence || "This memory was superseded by newer repo knowledge.";
|
|
15677
15681
|
}
|
|
15682
|
+
// Re-verify a still-true packet in place: re-check cited paths, refresh
|
|
15683
|
+
// fingerprints and last_verified_at, and clear stale flags. The alternative to
|
|
15684
|
+
// supersede churn when code changed but the memory's claim did not. Refuses
|
|
15685
|
+
// when ALL cited evidence is gone — that memory needs supersede or stale, not
|
|
15686
|
+
// a rubber stamp.
|
|
15687
|
+
function reverifyMemory(projectDir, packetId) {
|
|
15688
|
+
ensureMemoryDirs(projectDir);
|
|
15689
|
+
const result = {
|
|
15690
|
+
ok: false,
|
|
15691
|
+
project_dir: projectDir,
|
|
15692
|
+
packet_id: packetId,
|
|
15693
|
+
refreshed_paths: [],
|
|
15694
|
+
missing_paths: [],
|
|
15695
|
+
was_stale: false,
|
|
15696
|
+
errors: [],
|
|
15697
|
+
};
|
|
15698
|
+
const entries = loadPacketEntriesFromDir(packetsDir(projectDir));
|
|
15699
|
+
const entry = entries.find((item) => item.packet.id === packetId);
|
|
15700
|
+
if (!entry) {
|
|
15701
|
+
result.errors.push(`Packet not found: ${packetId}`);
|
|
15702
|
+
return result;
|
|
15703
|
+
}
|
|
15704
|
+
const packet = entry.packet;
|
|
15705
|
+
const quality = (packet.quality ?? {});
|
|
15706
|
+
result.was_stale = quality.stale === true;
|
|
15707
|
+
const citedPaths = unique([
|
|
15708
|
+
...packet.paths,
|
|
15709
|
+
...packetStoredPathFingerprints(packet).map((fingerprint) => fingerprint.path),
|
|
15710
|
+
]).filter(fingerprintableMemoryPath);
|
|
15711
|
+
result.missing_paths = citedPaths.filter((path) => !(0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, path)));
|
|
15712
|
+
if (citedPaths.length && result.missing_paths.length === citedPaths.length) {
|
|
15713
|
+
result.errors.push("All cited paths are gone — reverify would rubber-stamp dead evidence. Use kage supersede with a replacement, or mark the packet stale.");
|
|
15714
|
+
return result;
|
|
15715
|
+
}
|
|
15716
|
+
const presentPaths = citedPaths.filter((path) => !result.missing_paths.includes(path));
|
|
15717
|
+
const now = nowIso();
|
|
15718
|
+
const freshness = { ...(packet.freshness ?? {}) };
|
|
15719
|
+
freshness.path_fingerprints = memoryPathFingerprints(projectDir, presentPaths);
|
|
15720
|
+
freshness.last_verified_at = now;
|
|
15721
|
+
const { stale: _stale, stale_reasons: _staleReasons, suggested_action: _suggestedAction, ...nextQuality } = quality;
|
|
15722
|
+
writeJson(entry.path, {
|
|
15723
|
+
...packet,
|
|
15724
|
+
paths: presentPaths.length ? presentPaths : packet.paths,
|
|
15725
|
+
freshness,
|
|
15726
|
+
quality: { ...nextQuality, reverified_at: now },
|
|
15727
|
+
updated_at: now,
|
|
15728
|
+
});
|
|
15729
|
+
result.refreshed_paths = presentPaths;
|
|
15730
|
+
result.ok = true;
|
|
15731
|
+
return result;
|
|
15732
|
+
}
|
|
15678
15733
|
function supersedeMemory(projectDir, oldPacketId, replacementPacketId, reason = "") {
|
|
15679
15734
|
ensureMemoryDirs(projectDir);
|
|
15680
15735
|
const trimmedReason = reason.trim() || "Newer repo memory supersedes this packet.";
|
package/package.json
CHANGED
package/viewer/index.html
CHANGED
|
@@ -6,35 +6,24 @@
|
|
|
6
6
|
<title>Kage — memory dashboard</title>
|
|
7
7
|
<style>
|
|
8
8
|
/* V2 "receipts" theme. Tokens mirror docs/assets/site.css so the dashboard
|
|
9
|
-
matches the site:
|
|
10
|
-
|
|
9
|
+
matches the site: dark-first product look, editorial serif display type,
|
|
10
|
+
verified-green for gains. One look everywhere. */
|
|
11
11
|
:root {
|
|
12
|
-
color-scheme:
|
|
13
|
-
--bg: #
|
|
14
|
-
--line: #
|
|
15
|
-
--ink: #
|
|
16
|
-
--gain: #
|
|
17
|
-
--code: #
|
|
18
|
-
--warn-soft: rgba(
|
|
19
|
-
--code-bg: #
|
|
20
|
-
--tip-bg: #
|
|
12
|
+
color-scheme: dark;
|
|
13
|
+
--bg: #121413; --paper: #1a1d1b; --paper-2: #212522;
|
|
14
|
+
--line: #2c302c; --line-strong: #41463f;
|
|
15
|
+
--ink: #edefe9; --muted: #a4aba1; --faint: #767d74;
|
|
16
|
+
--gain: #43c98a; --gain-strong: #5fdca0; --gain-soft: rgba(67, 201, 138, 0.12);
|
|
17
|
+
--code: #6fb4e8; --memory: #b095e8; --warn: #d9a93f; --danger: #e07a8c;
|
|
18
|
+
--warn-soft: rgba(217, 169, 63, 0.10);
|
|
19
|
+
--code-bg: #0e1110; --code-text: #cfe0d4;
|
|
20
|
+
--tip-bg: #212522; --shadow: 0 14px 40px rgba(0, 0, 0, 0.5);
|
|
21
21
|
--sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
22
22
|
--serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
|
|
23
23
|
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
24
24
|
--r-control: 4px; --r-panel: 6px; --r-card: 8px;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
:root {
|
|
28
|
-
--bg: #121413; --paper: #1a1d1b; --paper-2: #212522;
|
|
29
|
-
--line: #2c302c; --line-strong: #41463f;
|
|
30
|
-
--ink: #edefe9; --muted: #a4aba1; --faint: #767d74;
|
|
31
|
-
--gain: #43c98a; --gain-strong: #5fdca0; --gain-soft: rgba(67, 201, 138, 0.12);
|
|
32
|
-
--code: #6fb4e8; --memory: #b095e8; --warn: #d9a93f; --danger: #e07a8c;
|
|
33
|
-
--warn-soft: rgba(217, 169, 63, 0.10);
|
|
34
|
-
--code-bg: #0e1110; --code-text: #cfe0d4;
|
|
35
|
-
--tip-bg: #212522; --shadow: 0 14px 40px rgba(0, 0, 0, 0.5);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
26
|
+
/* Dark-first: one product look (light tokens retired 2026-06-12). */
|
|
38
27
|
* { box-sizing: border-box; }
|
|
39
28
|
html, body { margin: 0; }
|
|
40
29
|
body { background: var(--bg); color: var(--ink); font: 400 14px/1.55 var(--sans); -webkit-font-smoothing: antialiased; }
|