@kage-core/kage-graph-mcp 1.1.38 → 1.3.0

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 CHANGED
@@ -12,6 +12,7 @@ function usage() {
12
12
 
13
13
  Usage:
14
14
  kage index --project <dir>
15
+ kage demo [--project <dir>]
15
16
  kage init --project <dir>
16
17
  kage policy --project <dir>
17
18
  kage doctor --project <dir>
@@ -29,6 +30,9 @@ Usage:
29
30
  kage hook uninstall --project <dir> [--json]
30
31
  kage refresh --project <dir> [--full] [--json]
31
32
  kage gc --project <dir> [--dry-run] [--force] [--json]
33
+ kage compact --project <dir> [--dry-run] [--json]
34
+ kage verify --project <dir> [--id <packet-id>] [--json]
35
+ kage suppressed --project <dir> [--json]
32
36
  kage pr summarize --project <dir> [--json]
33
37
  kage pr check --project <dir> [--json]
34
38
  kage upgrade [--dry-run]
@@ -58,6 +62,7 @@ Usage:
58
62
  kage inbox --project <dir> [--json]
59
63
  kage quality --project <dir> [--json]
60
64
  kage benchmark --project <dir> [--json]
65
+ kage benchmark --trust --project <dir> [--json]
61
66
  kage benchmark --memory-quality [--json]
62
67
  kage benchmark --scale [--sizes 240,1000,5000] [--json]
63
68
  kage benchmark --project <dir> --compare --task <task> [--json]
@@ -74,14 +79,14 @@ Usage:
74
79
  kage graph "<query>" --project <dir> [--json]
75
80
  kage graph-registry --project <dir> [--json]
76
81
  kage embeddings build --project <dir> [--model Xenova/all-MiniLM-L6-v2] [--json]
77
- kage recall "<query>" --project <dir> [--json] [--explain] [--embeddings]
82
+ kage recall "<query>" --project <dir> [--json] [--explain] [--embeddings] [--max-context-tokens <n>] [--structural-hops <n>]
78
83
  kage observe --project <dir> --event <json>
79
84
  kage sessions --project <dir> [--json]
80
85
  kage replay --project <dir> [--session <id>] [--limit <n>] [--json]
81
86
  kage distill --project <dir> --session <id>
82
- kage learn --project <dir> --learning <text> [--title <title>] [--type <type>] [--evidence <text>] [--verified-by <text>] [--tags a,b] [--paths a,b]
87
+ kage learn --project <dir> --learning <text> [--title <title>] [--type <type>] [--evidence <text>] [--verified-by <text>] [--tags a,b] [--paths a,b] [--graph-nodes a,b] [--allow-missing-paths]
83
88
  kage feedback --project <dir> --packet <packet-id> --kind helpful|wrong|stale
84
- kage capture --project <dir> --title <title> --body <body> [--type <type>] [--summary <summary>] [--tags a,b] [--paths a,b] [--stack a,b]
89
+ kage capture --project <dir> --title <title> --body <body> [--type <type>] [--summary <summary>] [--tags a,b] [--paths a,b] [--stack a,b] [--graph-nodes a,b] [--allow-missing-paths]
85
90
  kage propose --project <dir> --from-diff
86
91
  kage review-artifact --project <dir>
87
92
  kage promote --project <dir> --public <packet-id>
@@ -174,6 +179,30 @@ async function main() {
174
179
  console.log(`Indexes:\n${result.indexes.map((path) => ` - ${path}`).join("\n")}`);
175
180
  return;
176
181
  }
182
+ if (command === "demo") {
183
+ const demoDir = takeArg(args, "--project") ?? `${process.cwd()}/kage-demo`;
184
+ const result = (0, kernel_js_1.runDemo)(demoDir);
185
+ if (args.includes("--json")) {
186
+ console.log(JSON.stringify(result, null, 2));
187
+ return;
188
+ }
189
+ console.log("Kage demo — can you trust your agent's memory?\n");
190
+ console.log(`Seeded ${result.captured.length} grounded memories in ${result.project_dir}\n`);
191
+ console.log("1. Hallucinated citation — REJECTED on write:");
192
+ if (result.rejected_hallucination) {
193
+ console.log(` ✗ "${result.rejected_hallucination.title}"`);
194
+ console.log(` ${result.rejected_hallucination.error}\n`);
195
+ }
196
+ console.log("2. Stale memory (cited file deleted) — WITHHELD from recall:");
197
+ for (const w of result.withheld)
198
+ console.log(` ⊘ ${w.title}\n ${w.reason}`);
199
+ console.log("\n3. Recall returns only grounded, current memory:");
200
+ for (const t of result.recalled)
201
+ console.log(` ✓ ${t}`);
202
+ console.log(`\nTrust score: ${result.trust_score}/100`);
203
+ console.log(`\nSee it in the viewer: ${result.viewer_command}`);
204
+ return;
205
+ }
177
206
  if (command === "init") {
178
207
  const result = (0, kernel_js_1.initProject)(projectArg(args));
179
208
  console.log(`Initialized Kage memory for ${result.index.projectDir}`);
@@ -404,6 +433,80 @@ async function main() {
404
433
  }
405
434
  return;
406
435
  }
436
+ if (command === "compact") {
437
+ const project = projectArg(args);
438
+ const dryRun = args.includes("--dry-run");
439
+ const result = (0, kernel_js_1.compactProject)(project, { dryRun });
440
+ if (args.includes("--json")) {
441
+ console.log(JSON.stringify(result, null, 2));
442
+ return;
443
+ }
444
+ const label = dryRun ? " [dry-run]" : "";
445
+ console.log(`Kage compact${label} — scanned ${result.total_scanned} packets`);
446
+ if (result.pruned_citations.length) {
447
+ console.log(`\nPruned dead citations (${result.pruned_citations.length}):`);
448
+ for (const p of result.pruned_citations)
449
+ console.log(` ✂ ${p.title} — removed ${p.removed_paths.join(", ")}`);
450
+ }
451
+ if (result.deprecated.length) {
452
+ console.log(`\nDeprecated stale (${result.deprecated.length}):`);
453
+ for (const p of result.deprecated)
454
+ console.log(` ✗ ${p.title} — ${p.reason}`);
455
+ }
456
+ if (result.duplicate_clusters.length) {
457
+ console.log(`\nDuplicate clusters to merge (${result.duplicate_clusters.length}) — review with kage_compact / kage supersede:`);
458
+ for (const cluster of result.duplicate_clusters) {
459
+ console.log(` ~${cluster.score} similarity:`);
460
+ for (const member of cluster.packets)
461
+ console.log(` • ${member.title} (${member.id})`);
462
+ }
463
+ }
464
+ if (!result.pruned_citations.length && !result.deprecated.length && !result.duplicate_clusters.length) {
465
+ console.log("Nothing to compact — memory is clean.");
466
+ }
467
+ return;
468
+ }
469
+ if (command === "verify") {
470
+ const project = projectArg(args);
471
+ const idFlag = args.indexOf("--id");
472
+ const id = idFlag >= 0 ? args[idFlag + 1] : undefined;
473
+ const result = (0, kernel_js_1.verifyCitations)(project, { id });
474
+ if (args.includes("--json")) {
475
+ console.log(JSON.stringify(result, null, 2));
476
+ if (!result.ok)
477
+ process.exit(2);
478
+ return;
479
+ }
480
+ if (!result.ok) {
481
+ console.log(`Verification failed:\n${result.errors.map((error) => ` - ${error}`).join("\n")}`);
482
+ process.exit(2);
483
+ }
484
+ console.log(`Kage verify — ${result.checked} packet(s): ${result.valid} valid, ${result.stale} stale, ${result.ungrounded} ungrounded`);
485
+ for (const entry of result.packets.filter((p) => p.stale || !p.grounded)) {
486
+ const flags = [entry.stale ? `stale:${entry.stale_severity}` : "", entry.grounded ? "" : "ungrounded"].filter(Boolean).join(", ");
487
+ console.log(` ⚠ ${entry.title} [${flags}]`);
488
+ if (entry.missing_paths.length)
489
+ console.log(` missing: ${entry.missing_paths.join(", ")}`);
490
+ for (const reason of entry.stale_reasons)
491
+ console.log(` - ${reason}`);
492
+ }
493
+ return;
494
+ }
495
+ if (command === "suppressed") {
496
+ const result = (0, kernel_js_1.kageSuppressedMemory)(projectArg(args));
497
+ if (args.includes("--json")) {
498
+ console.log(JSON.stringify(result, null, 2));
499
+ return;
500
+ }
501
+ console.log(`Kage suppressed memory — ${result.count} packet(s) currently withheld from recall`);
502
+ for (const item of result.items) {
503
+ console.log(` ⊘ ${item.title}`);
504
+ console.log(` ${item.reason}`);
505
+ }
506
+ if (!result.count)
507
+ console.log(" (none — all recallable memory is grounded and current)");
508
+ return;
509
+ }
407
510
  if (command === "refresh") {
408
511
  const result = (0, kernel_js_1.refreshProject)(projectArg(args), { full: args.includes("--full") });
409
512
  if (args.includes("--json")) {
@@ -1258,6 +1361,24 @@ async function main() {
1258
1361
  return;
1259
1362
  }
1260
1363
  if (command === "benchmark") {
1364
+ if (args.includes("--trust")) {
1365
+ const result = (0, kernel_js_1.benchmarkTrust)(projectArg(args));
1366
+ if (args.includes("--json")) {
1367
+ console.log(JSON.stringify(result, null, 2));
1368
+ if (!result.ok)
1369
+ process.exitCode = 1;
1370
+ return;
1371
+ }
1372
+ console.log("Kage Trust Benchmark — can this memory be trusted?");
1373
+ console.log(`Trust score: ${result.trust_score}/100 (${result.ok ? "PASS" : "REVIEW"})`);
1374
+ console.log(` Hallucinated-citation rejection: ${result.metrics.hallucinated_citation_rejection_rate}% (${result.detail.hallucination.rejected}/${result.detail.hallucination.attempted})`);
1375
+ console.log(` Stale-memory exclusion: ${result.metrics.stale_memory_exclusion_rate}% (${result.detail.staleness.excluded_after}/${result.detail.staleness.recallable_before})`);
1376
+ console.log(` Live grounding rate: ${result.metrics.live_grounding_rate}% (${result.detail.live_memory.grounded}/${result.detail.live_memory.checked} packets)`);
1377
+ console.log(` Wrong-advice prevented: ${result.metrics.wrong_advice_prevented_rate}%`);
1378
+ if (!result.ok)
1379
+ process.exitCode = 1;
1380
+ return;
1381
+ }
1261
1382
  if (args.includes("--memory-quality")) {
1262
1383
  const result = (0, kernel_js_1.benchmarkCodingMemoryQuality)({
1263
1384
  topK: Number(takeArg(args, "--top-k") ?? 10),
@@ -1377,12 +1498,17 @@ async function main() {
1377
1498
  tags: listArg(takeArg(args, "--tags")),
1378
1499
  paths: listArg(takeArg(args, "--paths")),
1379
1500
  stack: listArg(takeArg(args, "--stack")),
1501
+ graphNodes: listArg(takeArg(args, "--graph-nodes")),
1502
+ allowMissingPaths: args.includes("--allow-missing-paths"),
1503
+ strictCitations: true,
1380
1504
  });
1381
1505
  if (!result.ok) {
1382
1506
  console.error(`Learning capture blocked:\n${result.errors.map((error) => ` - ${error}`).join("\n")}`);
1383
1507
  process.exit(2);
1384
1508
  }
1385
1509
  console.log(`Captured session learning: ${result.path}`);
1510
+ if (result.warnings?.length)
1511
+ console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
1386
1512
  console.log("Repo-local memory is written immediately. Promotion to org/global still requires explicit review.");
1387
1513
  return;
1388
1514
  }
@@ -1565,9 +1691,11 @@ async function main() {
1565
1691
  const query = firstPositional(args);
1566
1692
  if (!query)
1567
1693
  usage();
1694
+ const maxContextTokens = args.includes("--max-context-tokens") ? numberArg(args, "--max-context-tokens", 0) : undefined;
1695
+ const structuralHops = args.includes("--structural-hops") ? numberArg(args, "--structural-hops", 2) : undefined;
1568
1696
  const result = args.includes("--embeddings")
1569
1697
  ? await (0, kernel_js_1.recallWithEmbeddings)(projectArg(args), query, 5, args.includes("--explain"))
1570
- : (0, kernel_js_1.recall)(projectArg(args), query, 5, args.includes("--explain"));
1698
+ : (0, kernel_js_1.recall)(projectArg(args), query, 5, args.includes("--explain"), { maxContextTokens, structuralHops });
1571
1699
  if (args.includes("--json"))
1572
1700
  console.log(JSON.stringify(result, null, 2));
1573
1701
  else
@@ -1711,6 +1839,9 @@ async function main() {
1711
1839
  tags: listArg(takeArg(args, "--tags")),
1712
1840
  paths: listArg(takeArg(args, "--paths")),
1713
1841
  stack: listArg(takeArg(args, "--stack")),
1842
+ graphNodes: listArg(takeArg(args, "--graph-nodes")),
1843
+ allowMissingPaths: args.includes("--allow-missing-paths"),
1844
+ strictCitations: true,
1714
1845
  };
1715
1846
  const result = (0, kernel_js_1.capture)(input);
1716
1847
  if (!result.ok) {
@@ -1718,6 +1849,8 @@ async function main() {
1718
1849
  process.exit(2);
1719
1850
  }
1720
1851
  console.log(`Captured repo-local packet: ${result.path}`);
1852
+ if (result.warnings?.length)
1853
+ console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
1721
1854
  console.log("Repo-local memory is written immediately. Promotion to org/global still requires explicit review.");
1722
1855
  return;
1723
1856
  }
package/dist/daemon.js CHANGED
@@ -573,6 +573,8 @@ async function startViewer(projectDir, options = {}) {
573
573
  const timelinePath = (0, node_path_1.join)(reportsDir, "timeline.json");
574
574
  const lineagePath = (0, node_path_1.join)(reportsDir, "lineage.json");
575
575
  const setupPath = (0, node_path_1.join)(reportsDir, "setup.json");
576
+ const trustPath = (0, node_path_1.join)(reportsDir, "trust.json");
577
+ const suppressedPath = (0, node_path_1.join)(reportsDir, "suppressed.json");
576
578
  // Pre-generate lightweight JSON reports so the viewer can load them directly.
577
579
  try {
578
580
  (0, node_fs_1.mkdirSync)(reportsDir, { recursive: true });
@@ -601,11 +603,13 @@ async function startViewer(projectDir, options = {}) {
601
603
  (0, node_fs_1.writeFileSync)(timelinePath, JSON.stringify((0, kernel_js_1.kageMemoryTimeline)(projectDir), null, 2));
602
604
  (0, node_fs_1.writeFileSync)(lineagePath, JSON.stringify((0, kernel_js_1.kageMemoryLineage)(projectDir), null, 2));
603
605
  (0, node_fs_1.writeFileSync)(setupPath, JSON.stringify((0, kernel_js_1.setupDoctor)(projectDir), null, 2));
606
+ (0, node_fs_1.writeFileSync)(trustPath, JSON.stringify((0, kernel_js_1.benchmarkTrust)(projectDir), null, 2));
607
+ (0, node_fs_1.writeFileSync)(suppressedPath, JSON.stringify((0, kernel_js_1.kageSuppressedMemory)(projectDir), null, 2));
604
608
  }
605
609
  catch {
606
610
  // non-fatal: viewer will show 404 for reports if generation fails
607
611
  }
608
- const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&profile=${encodeURIComponent(profilePath)}&xray=${encodeURIComponent(xrayPath)}&capabilities=${encodeURIComponent(capabilitiesPath)}&slots=${encodeURIComponent(slotsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&sessions=${encodeURIComponent(sessionsPath)}&replay=${encodeURIComponent(replayPath)}&memoryAccess=${encodeURIComponent(memoryAccessPath)}&memoryAudit=${encodeURIComponent(memoryAuditPath)}&handoff=${encodeURIComponent(handoffPath)}&lifecycle=${encodeURIComponent(lifecyclePath)}&timeline=${encodeURIComponent(timelinePath)}&lineage=${encodeURIComponent(lineagePath)}&setup=${encodeURIComponent(setupPath)}&view=code`;
612
+ const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&profile=${encodeURIComponent(profilePath)}&xray=${encodeURIComponent(xrayPath)}&capabilities=${encodeURIComponent(capabilitiesPath)}&slots=${encodeURIComponent(slotsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&sessions=${encodeURIComponent(sessionsPath)}&replay=${encodeURIComponent(replayPath)}&memoryAccess=${encodeURIComponent(memoryAccessPath)}&memoryAudit=${encodeURIComponent(memoryAuditPath)}&handoff=${encodeURIComponent(handoffPath)}&lifecycle=${encodeURIComponent(lifecyclePath)}&timeline=${encodeURIComponent(timelinePath)}&lineage=${encodeURIComponent(lineagePath)}&setup=${encodeURIComponent(setupPath)}&trust=${encodeURIComponent(trustPath)}&suppressed=${encodeURIComponent(suppressedPath)}&view=code`;
609
613
  const server = (0, node_http_1.createServer)((req, res) => {
610
614
  const requestUrl = new URL(req.url ?? "/", `http://${host}:${port}`);
611
615
  let filePath = null;
package/dist/index.js CHANGED
@@ -152,6 +152,8 @@ function listTools() {
152
152
  explain: { type: "boolean" },
153
153
  embeddings: { type: "boolean" },
154
154
  json: { type: "boolean" },
155
+ max_context_tokens: { type: "number" },
156
+ structural_hops: { type: "number", description: "If >0, append a bounded N-hop code-graph blast radius seeded from the recalled memory's files." },
155
157
  },
156
158
  required: ["query", "project_dir"],
157
159
  },
@@ -588,7 +590,7 @@ function listTools() {
588
590
  type: "object",
589
591
  properties: {
590
592
  project_dir: { type: "string" },
591
- mode: { type: "string", enum: ["project", "memory_quality", "memory_scale"] },
593
+ mode: { type: "string", enum: ["project", "trust", "memory_quality", "memory_scale"] },
592
594
  sizes: { type: "array", items: { type: "number" } },
593
595
  top_k: { type: "number" },
594
596
  },
@@ -668,7 +670,7 @@ function listTools() {
668
670
  },
669
671
  {
670
672
  name: "kage_learn",
671
- description: "Capture an actual reusable learning from the current session as repo-local memory. Prefer this over diff proposal when the agent knows what was learned.",
673
+ description: "Capture an actual reusable learning from the current session as repo-local memory. Prefer this over diff proposal when the agent knows what was learned. Capture is rejected if every referenced path is missing from the repo; set allow_missing_paths to record anyway (e.g. a file you are about to create).",
672
674
  inputSchema: {
673
675
  type: "object",
674
676
  properties: {
@@ -681,13 +683,15 @@ function listTools() {
681
683
  tags: { type: "array", items: { type: "string" } },
682
684
  paths: { type: "array", items: { type: "string" } },
683
685
  stack: { type: "array", items: { type: "string" } },
686
+ graph_nodes: { type: "array", items: { type: "string" } },
687
+ allow_missing_paths: { type: "boolean" },
684
688
  },
685
689
  required: ["project_dir", "learning"],
686
690
  },
687
691
  },
688
692
  {
689
693
  name: "kage_capture",
690
- description: "Create a repo-local Kage memory packet immediately. Org/global promotion still requires explicit human review.",
694
+ description: "Create a repo-local Kage memory packet immediately. Org/global promotion still requires explicit human review. Capture is rejected if every referenced path is missing from the repo; set allow_missing_paths to record anyway.",
691
695
  inputSchema: {
692
696
  type: "object",
693
697
  properties: {
@@ -699,10 +703,36 @@ function listTools() {
699
703
  tags: { type: "array", items: { type: "string" } },
700
704
  paths: { type: "array", items: { type: "string" } },
701
705
  stack: { type: "array", items: { type: "string" } },
706
+ graph_nodes: { type: "array", items: { type: "string" }, description: "Code-graph node references (symbol/route/file) this memory is about." },
707
+ allow_missing_paths: { type: "boolean" },
702
708
  },
703
709
  required: ["project_dir", "title", "body"],
704
710
  },
705
711
  },
712
+ {
713
+ name: "kage_verify_citations",
714
+ description: "Verify that a memory packet's cited file paths still exist and that the memory is not stale, before trusting it. Pass an id to check one packet, or omit to audit all approved repo memory. Returns grounding and staleness for each.",
715
+ inputSchema: {
716
+ type: "object",
717
+ properties: {
718
+ project_dir: { type: "string" },
719
+ id: { type: "string" },
720
+ },
721
+ required: ["project_dir"],
722
+ },
723
+ },
724
+ {
725
+ name: "kage_compact",
726
+ description: "Consolidate repo memory: prune dead citations, deprecate hard-stale packets, and surface near-duplicate clusters to merge (via kage_supersede). Defaults to a dry run; pass dry_run=false to apply pruning/deprecation. Duplicate merging stays an agent decision — no hosted LLM is used.",
727
+ inputSchema: {
728
+ type: "object",
729
+ properties: {
730
+ project_dir: { type: "string" },
731
+ dry_run: { type: "boolean" },
732
+ },
733
+ required: ["project_dir"],
734
+ },
735
+ },
706
736
  {
707
737
  name: "kage_observe",
708
738
  description: "Store an automatic local observation event from an agent session. Observations are privacy-scanned, deduplicated, and never published automatically.",
@@ -1092,9 +1122,11 @@ async function callTool(name, args) {
1092
1122
  };
1093
1123
  }
1094
1124
  if (name === "kage_recall") {
1125
+ const maxContextTokens = typeof args?.max_context_tokens === "number" ? args.max_context_tokens : undefined;
1126
+ const structuralHops = typeof args?.structural_hops === "number" ? args.structural_hops : undefined;
1095
1127
  const result = args?.embeddings
1096
1128
  ? await (0, kernel_js_1.recallWithEmbeddings)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain))
1097
- : (0, kernel_js_1.recall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain));
1129
+ : (0, kernel_js_1.recall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain), { maxContextTokens, structuralHops });
1098
1130
  return {
1099
1131
  content: [{ type: "text", text: args?.json || args?.explain ? JSON.stringify(result, null, 2) : result.context_block }],
1100
1132
  };
@@ -1344,14 +1376,16 @@ async function callTool(name, args) {
1344
1376
  }
1345
1377
  if (name === "kage_benchmark") {
1346
1378
  const mode = String(args?.mode ?? "project");
1347
- const result = mode === "memory_quality"
1348
- ? (0, kernel_js_1.benchmarkCodingMemoryQuality)({ topK: Number(args?.top_k ?? 10) })
1349
- : mode === "memory_scale"
1350
- ? (0, kernel_js_1.benchmarkMemoryScale)({
1351
- sizes: Array.isArray(args?.sizes) ? args.sizes.map(Number) : undefined,
1352
- topK: Number(args?.top_k ?? 10),
1353
- })
1354
- : (0, kernel_js_1.benchmarkProject)(String(args?.project_dir ?? ""));
1379
+ const result = mode === "trust"
1380
+ ? (0, kernel_js_1.benchmarkTrust)(String(args?.project_dir ?? ""))
1381
+ : mode === "memory_quality"
1382
+ ? (0, kernel_js_1.benchmarkCodingMemoryQuality)({ topK: Number(args?.top_k ?? 10) })
1383
+ : mode === "memory_scale"
1384
+ ? (0, kernel_js_1.benchmarkMemoryScale)({
1385
+ sizes: Array.isArray(args?.sizes) ? args.sizes.map(Number) : undefined,
1386
+ topK: Number(args?.top_k ?? 10),
1387
+ })
1388
+ : (0, kernel_js_1.benchmarkProject)(String(args?.project_dir ?? ""));
1355
1389
  return {
1356
1390
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1357
1391
  };
@@ -1403,13 +1437,17 @@ async function callTool(name, args) {
1403
1437
  tags: arrayArg(args?.tags),
1404
1438
  paths: arrayArg(args?.paths),
1405
1439
  stack: arrayArg(args?.stack),
1440
+ graphNodes: arrayArg(args?.graph_nodes),
1441
+ allowMissingPaths: Boolean(args?.allow_missing_paths),
1442
+ strictCitations: true,
1406
1443
  });
1444
+ const learnWarnings = result.warnings?.length ? `\nWarnings:\n${result.warnings.map((warning) => `- ${warning}`).join("\n")}` : "";
1407
1445
  return {
1408
1446
  content: [
1409
1447
  {
1410
1448
  type: "text",
1411
1449
  text: result.ok
1412
- ? `Captured session learning: ${result.path}\nRepo-local memory is written immediately. Org/global promotion still requires explicit review.`
1450
+ ? `Captured session learning: ${result.path}\nRepo-local memory is written immediately. Org/global promotion still requires explicit review.${learnWarnings}`
1413
1451
  : `Learning capture blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
1414
1452
  },
1415
1453
  ],
@@ -1426,19 +1464,41 @@ async function callTool(name, args) {
1426
1464
  tags: arrayArg(args?.tags),
1427
1465
  paths: arrayArg(args?.paths),
1428
1466
  stack: arrayArg(args?.stack),
1467
+ graphNodes: arrayArg(args?.graph_nodes),
1468
+ allowMissingPaths: Boolean(args?.allow_missing_paths),
1469
+ strictCitations: true,
1429
1470
  });
1471
+ const captureWarnings = result.warnings?.length ? `\nWarnings:\n${result.warnings.map((warning) => `- ${warning}`).join("\n")}` : "";
1430
1472
  return {
1431
1473
  content: [
1432
1474
  {
1433
1475
  type: "text",
1434
1476
  text: result.ok
1435
- ? `Captured repo-local packet: ${result.path}\nOrg/global promotion still requires explicit review.`
1477
+ ? `Captured repo-local packet: ${result.path}\nOrg/global promotion still requires explicit review.${captureWarnings}`
1436
1478
  : `Capture blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
1437
1479
  },
1438
1480
  ],
1439
1481
  isError: !result.ok,
1440
1482
  };
1441
1483
  }
1484
+ if (name === "kage_verify_citations") {
1485
+ const result = (0, kernel_js_1.verifyCitations)(String(args?.project_dir ?? ""), {
1486
+ id: args?.id ? String(args.id) : undefined,
1487
+ });
1488
+ return {
1489
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1490
+ isError: !result.ok,
1491
+ };
1492
+ }
1493
+ if (name === "kage_compact") {
1494
+ const result = (0, kernel_js_1.compactProject)(String(args?.project_dir ?? ""), {
1495
+ dryRun: args?.dry_run === undefined ? true : Boolean(args.dry_run),
1496
+ });
1497
+ return {
1498
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1499
+ isError: !result.ok,
1500
+ };
1501
+ }
1442
1502
  if (name === "kage_observe") {
1443
1503
  const projectDir = String(args?.project_dir ?? "");
1444
1504
  const event = { ...args };
@@ -1634,5 +1694,16 @@ async function main() {
1634
1694
  await server.connect(transport);
1635
1695
  }
1636
1696
  if (require.main === module) {
1697
+ const firstArg = process.argv[2];
1698
+ if (firstArg && !firstArg.startsWith("-")) {
1699
+ // A positional subcommand (demo, init, recall, ...) means the user wants the
1700
+ // kage CLI, not the MCP stdio server. Delegate so a single
1701
+ // `npx @kage-core/kage-graph-mcp <command>` works like the `kage` binary.
1702
+ // MCP clients launch with no args (or flags), which falls through to the server.
1703
+ const { spawnSync } = require("node:child_process");
1704
+ const { join } = require("node:path");
1705
+ const result = spawnSync(process.execPath, [join(__dirname, "cli.js"), ...process.argv.slice(2)], { stdio: "inherit" });
1706
+ process.exit(result.status ?? 0);
1707
+ }
1637
1708
  main().catch(console.error);
1638
1709
  }