@kage-core/kage-graph-mcp 1.3.0 → 1.4.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
@@ -39,6 +39,7 @@ Usage:
39
39
  kage branch --project <dir> [--json]
40
40
  kage metrics --project <dir> [--json]
41
41
  kage memory-access --project <dir> [--json]
42
+ kage activity --project <dir> [--json]
42
43
  kage memory-audit --project <dir> [--limit <n>] [--json]
43
44
  kage slots --project <dir> [--json]
44
45
  kage slots set --project <dir> --label <label> --content <text> [--description <text>] [--paths a,b] [--tags a,b] [--size-limit <n>] [--unpinned] [--json]
@@ -983,6 +984,19 @@ async function main() {
983
984
  }
984
985
  return;
985
986
  }
987
+ if (command === "activity") {
988
+ const result = (0, kernel_js_1.kageActivity)(projectArg(args));
989
+ if (args.includes("--json")) {
990
+ console.log(JSON.stringify(result, null, 2));
991
+ return;
992
+ }
993
+ console.log(`Kage activity: ${result.totals.events} events (${result.totals.recalls} recalls, ${result.totals.captures} captures); ${result.totals.recalls_7d} recalls in 7 days`);
994
+ console.log("\nRecent:");
995
+ for (const event of result.events.slice(0, 15)) {
996
+ console.log(`- ${event.at.slice(0, 16).replace("T", " ")} ${event.kind.padEnd(9)} ${event.title}`);
997
+ }
998
+ return;
999
+ }
986
1000
  if (command === "lifecycle" || command === "memory-lifecycle") {
987
1001
  const result = (0, kernel_js_1.kageMemoryLifecycle)(projectArg(args));
988
1002
  if (args.includes("--json")) {
package/dist/daemon.js CHANGED
@@ -570,6 +570,7 @@ async function startViewer(projectDir, options = {}) {
570
570
  const memoryAuditPath = (0, node_path_1.join)(reportsDir, "memory-audit.json");
571
571
  const handoffPath = (0, node_path_1.join)(reportsDir, "handoff.json");
572
572
  const lifecyclePath = (0, node_path_1.join)(reportsDir, "lifecycle.json");
573
+ const activityPath = (0, node_path_1.join)(reportsDir, "activity.json");
573
574
  const timelinePath = (0, node_path_1.join)(reportsDir, "timeline.json");
574
575
  const lineagePath = (0, node_path_1.join)(reportsDir, "lineage.json");
575
576
  const setupPath = (0, node_path_1.join)(reportsDir, "setup.json");
@@ -600,6 +601,7 @@ async function startViewer(projectDir, options = {}) {
600
601
  (0, node_fs_1.writeFileSync)(memoryAuditPath, JSON.stringify((0, kernel_js_1.kageMemoryAudit)(projectDir), null, 2));
601
602
  (0, node_fs_1.writeFileSync)(handoffPath, JSON.stringify((0, kernel_js_1.kageMemoryHandoff)(projectDir), null, 2));
602
603
  (0, node_fs_1.writeFileSync)(lifecyclePath, JSON.stringify((0, kernel_js_1.kageMemoryLifecycle)(projectDir), null, 2));
604
+ (0, node_fs_1.writeFileSync)(activityPath, JSON.stringify((0, kernel_js_1.kageActivity)(projectDir), null, 2));
603
605
  (0, node_fs_1.writeFileSync)(timelinePath, JSON.stringify((0, kernel_js_1.kageMemoryTimeline)(projectDir), null, 2));
604
606
  (0, node_fs_1.writeFileSync)(lineagePath, JSON.stringify((0, kernel_js_1.kageMemoryLineage)(projectDir), null, 2));
605
607
  (0, node_fs_1.writeFileSync)(setupPath, JSON.stringify((0, kernel_js_1.setupDoctor)(projectDir), null, 2));
@@ -609,7 +611,7 @@ async function startViewer(projectDir, options = {}) {
609
611
  catch {
610
612
  // non-fatal: viewer will show 404 for reports if generation fails
611
613
  }
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`;
614
+ 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)}&activity=${encodeURIComponent(activityPath)}&timeline=${encodeURIComponent(timelinePath)}&lineage=${encodeURIComponent(lineagePath)}&setup=${encodeURIComponent(setupPath)}&trust=${encodeURIComponent(trustPath)}&suppressed=${encodeURIComponent(suppressedPath)}&view=code`;
613
615
  const server = (0, node_http_1.createServer)((req, res) => {
614
616
  const requestUrl = new URL(req.url ?? "/", `http://${host}:${port}`);
615
617
  let filePath = null;
package/dist/kernel.js CHANGED
@@ -62,6 +62,7 @@ exports.makePacketId = makePacketId;
62
62
  exports.parseFrontmatter = parseFrontmatter;
63
63
  exports.kageMemoryAccess = kageMemoryAccess;
64
64
  exports.kageMemoryLifecycle = kageMemoryLifecycle;
65
+ exports.kageActivity = kageActivity;
65
66
  exports.kageMemoryReconciliation = kageMemoryReconciliation;
66
67
  exports.evaluateMemoryAdmission = evaluateMemoryAdmission;
67
68
  exports.validatePacket = validatePacket;
@@ -873,6 +874,8 @@ function kageMemoryLifecycle(projectDir) {
873
874
  const item = {
874
875
  packet_id: packet.id,
875
876
  title: packet.title,
877
+ summary: packet.summary ?? "",
878
+ body: packet.body ?? "",
876
879
  type: packet.type,
877
880
  status: packet.status,
878
881
  health: action.health,
@@ -957,6 +960,54 @@ function recordRecallAccess(projectDir, results) {
957
960
  // Recall should never fail because local access telemetry could not be updated.
958
961
  }
959
962
  }
963
+ const AUDIT_ACTIVITY_KIND = {
964
+ capture: "capture", approve: "capture", supersede: "supersede", deprecate: "deprecate",
965
+ update: "update", promote: "promote", feedback: "feedback",
966
+ };
967
+ function kageActivity(projectDir, options = {}) {
968
+ const limit = options.limit ?? 80;
969
+ const events = [];
970
+ let recalls = 0;
971
+ readMemoryAccessEntries(projectDir).forEach((entry) => {
972
+ (entry.recent ?? []).forEach((r) => {
973
+ if (!r || !r.at)
974
+ return;
975
+ recalls += 1;
976
+ events.push({ at: r.at, kind: "recall", title: entry.title, detail: `recalled · rank ${r.rank}` });
977
+ });
978
+ });
979
+ let captures = 0;
980
+ for (const audit of loadMemoryAuditEntries(projectDir)) {
981
+ const kind = AUDIT_ACTIVITY_KIND[audit.operation] ?? "other";
982
+ if (kind === "capture")
983
+ captures += 1;
984
+ const extra = audit.packet_titles.length > 1 ? ` (+${audit.packet_titles.length - 1} more)` : "";
985
+ events.push({ at: audit.timestamp, kind, title: (audit.packet_titles[0] ?? audit.operation) + extra, detail: audit.operation, actor: audit.actor });
986
+ }
987
+ events.sort((a, b) => (Date.parse(b.at) || 0) - (Date.parse(a.at) || 0));
988
+ const dayMap = new Map();
989
+ events.forEach((e) => { if (e.kind === "recall") {
990
+ const d = e.at.slice(0, 10);
991
+ dayMap.set(d, (dayMap.get(d) ?? 0) + 1);
992
+ } });
993
+ // Zero-fill the last 14 calendar days so the chart reads as a timeline, not a lone bar.
994
+ const daily = [];
995
+ for (let i = 13; i >= 0; i -= 1) {
996
+ const day = new Date(Date.now() - i * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
997
+ daily.push({ day, recalls: dayMap.get(day) ?? 0 });
998
+ }
999
+ const cutoff7 = Date.now() - 7 * 24 * 60 * 60 * 1000;
1000
+ const recalls7d = events.filter((e) => e.kind === "recall" && (Date.parse(e.at) || 0) >= cutoff7).length;
1001
+ return {
1002
+ schema_version: 1,
1003
+ project_dir: projectDir,
1004
+ generated_at: nowIso(),
1005
+ window_days: ACCESS_WINDOW_DAYS,
1006
+ totals: { events: events.length, recalls, captures, recalls_7d: recalls7d },
1007
+ daily,
1008
+ events: events.slice(0, limit),
1009
+ };
1010
+ }
960
1011
  function isGeneratedChangeMemory(packet) {
961
1012
  return packet.type === "workflow"
962
1013
  && packet.tags.includes("change-memory")
@@ -1122,7 +1173,7 @@ function staleMemoryReasons(projectDir, packet, fingerprintCache) {
1122
1173
  if (ageDays > ttlDays)
1123
1174
  reasons.push(`freshness ttl expired (${Math.floor(ageDays)}d old, ttl ${ttlDays}d)`);
1124
1175
  }
1125
- const paths = packet.paths.filter(meaningfulMemoryPath);
1176
+ const paths = packet.paths.filter((path) => meaningfulMemoryPath(path) && !isGroundingIgnored(projectDir, path));
1126
1177
  const missingPaths = paths.filter((path) => !(0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, path)));
1127
1178
  if (paths.length > 0 && missingPaths.length === paths.length) {
1128
1179
  reasons.push(`all referenced paths are missing: ${missingPaths.slice(0, 4).join(", ")}`);
@@ -1133,6 +1184,7 @@ function staleMemoryReasons(projectDir, packet, fingerprintCache) {
1133
1184
  if (freshness.path_fingerprint_policy === "source_hash_staleness") {
1134
1185
  const storedFingerprints = packetStoredPathFingerprints(packet);
1135
1186
  const changedPaths = storedFingerprints
1187
+ .filter((fingerprint) => !isGroundingIgnored(projectDir, fingerprint.path))
1136
1188
  .filter((fingerprint) => (0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, fingerprint.path)))
1137
1189
  .filter((fingerprint) => {
1138
1190
  const current = memoryPathFingerprint(projectDir, fingerprint.path, fingerprintCache);
@@ -2341,7 +2393,7 @@ function pathExistsInRepo(projectDir, packetPath) {
2341
2393
  }
2342
2394
  function packetGroundingWarnings(projectDir, packet, source) {
2343
2395
  const warnings = [];
2344
- const meaningfulPaths = packet.paths.filter((path) => path && path !== "root" && !shouldSkipRepoMemoryPath(path));
2396
+ const meaningfulPaths = packet.paths.filter((path) => path && path !== "root" && !shouldSkipRepoMemoryPath(path) && !isGroundingIgnored(projectDir, path));
2345
2397
  const missingPaths = meaningfulPaths.filter((path) => !pathExistsInRepo(projectDir, path));
2346
2398
  if (meaningfulPaths.length && missingPaths.length === meaningfulPaths.length) {
2347
2399
  warnings.push(`${source}: none of the referenced paths exist in this repo: ${missingPaths.join(", ")}`);
@@ -2805,6 +2857,54 @@ function readKageIgnore(projectDir) {
2805
2857
  .map((line) => line.trim())
2806
2858
  .filter((line) => line.length > 0 && !line.startsWith("#"));
2807
2859
  }
2860
+ // A repo can declare non-knowledge paths (e.g. a presentation/visualization layer)
2861
+ // in .kageignore. Those paths must not count as memory grounding: memory should never
2862
+ // be anchored to, or marked stale by, files the repo says are not knowledge-bearing.
2863
+ function normalizeRelPath(path) {
2864
+ return String(path).replace(/\\/g, "/").replace(/^\/+/, "");
2865
+ }
2866
+ function isGroundingIgnored(projectDir, path) {
2867
+ const patterns = readKageIgnore(projectDir);
2868
+ if (!patterns.length)
2869
+ return false;
2870
+ return isKageIgnored(normalizeRelPath(path), patterns);
2871
+ }
2872
+ // Strip .kageignore'd paths from a packet's grounding (paths, source refs, and
2873
+ // path fingerprints). Returns a new packet if anything changed, else null.
2874
+ function prunePacketGroundingPaths(packet, patterns) {
2875
+ if (!patterns.length)
2876
+ return null;
2877
+ const ignored = (p) => typeof p === "string" && isKageIgnored(normalizeRelPath(p), patterns);
2878
+ let changed = false;
2879
+ const paths = packet.paths.filter((p) => (ignored(p) ? ((changed = true), false) : true));
2880
+ const sourceRefs = packet.source_refs.map((ref) => {
2881
+ const next = { ...ref };
2882
+ if (ignored(next.path)) {
2883
+ delete next.path;
2884
+ changed = true;
2885
+ }
2886
+ if (Array.isArray(next.changed_files)) {
2887
+ const kept = next.changed_files.filter((f) => !ignored(f));
2888
+ if (kept.length !== next.changed_files.length) {
2889
+ next.changed_files = kept;
2890
+ changed = true;
2891
+ }
2892
+ }
2893
+ return next;
2894
+ });
2895
+ const freshness = { ...(packet.freshness ?? {}) };
2896
+ if (Array.isArray(freshness.path_fingerprints)) {
2897
+ const fps = freshness.path_fingerprints;
2898
+ const kept = fps.filter((f) => !ignored(f?.path));
2899
+ if (kept.length !== fps.length) {
2900
+ freshness.path_fingerprints = kept;
2901
+ changed = true;
2902
+ }
2903
+ }
2904
+ if (!changed)
2905
+ return null;
2906
+ return { ...packet, paths, source_refs: sourceRefs, freshness };
2907
+ }
2808
2908
  function wildcardPattern(pattern) {
2809
2909
  const escaped = pattern
2810
2910
  .replace(/[.+^${}()|[\]\\]/g, "\\$&")
@@ -5447,13 +5547,18 @@ function refreshPacketStaleness(projectDir) {
5447
5547
  const findings = [];
5448
5548
  let updated = 0;
5449
5549
  const fingerprintCache = new Map();
5550
+ const ignorePatterns = readKageIgnore(projectDir);
5450
5551
  for (const entry of loadPacketEntriesFromDir(packetsDir(projectDir))) {
5451
- const reasons = staleMemoryReasons(projectDir, entry.packet, fingerprintCache);
5452
- const oldQuality = (entry.packet.quality ?? {});
5453
- const oldFreshness = (entry.packet.freshness ?? {});
5552
+ // Drop any .kageignore'd grounding (presentation layers etc.) from the stored packet
5553
+ // so memory is never anchored to non-knowledge files.
5554
+ const pruned = prunePacketGroundingPaths(entry.packet, ignorePatterns);
5555
+ const packet = pruned ?? entry.packet;
5556
+ const reasons = staleMemoryReasons(projectDir, packet, fingerprintCache);
5557
+ const oldQuality = (packet.quality ?? {});
5558
+ const oldFreshness = (packet.freshness ?? {});
5454
5559
  let nextQuality;
5455
5560
  if (reasons.length) {
5456
- const finding = staleFinding(entry.packet, reasons);
5561
+ const finding = staleFinding(packet, reasons);
5457
5562
  findings.push(finding);
5458
5563
  nextQuality = {
5459
5564
  ...oldQuality,
@@ -5467,11 +5572,12 @@ function refreshPacketStaleness(projectDir) {
5467
5572
  nextQuality = rest;
5468
5573
  }
5469
5574
  const nextFreshness = oldFreshness;
5470
- const changed = JSON.stringify(oldQuality) !== JSON.stringify(nextQuality)
5575
+ const changed = pruned !== null
5576
+ || JSON.stringify(oldQuality) !== JSON.stringify(nextQuality)
5471
5577
  || JSON.stringify(oldFreshness) !== JSON.stringify(nextFreshness);
5472
5578
  if (changed) {
5473
5579
  writeJson(entry.path, {
5474
- ...entry.packet,
5580
+ ...packet,
5475
5581
  freshness: nextFreshness,
5476
5582
  quality: nextQuality,
5477
5583
  updated_at: nowIso(),
@@ -10897,8 +11003,11 @@ function capture(input) {
10897
11003
  };
10898
11004
  }
10899
11005
  const warnings = [];
10900
- const meaningfulPaths = (input.paths ?? [])
10901
- .filter((path) => path && meaningfulMemoryPath(path) && !shouldSkipRepoMemoryPath(path));
11006
+ // .kageignore'd paths (e.g. a presentation/visualization layer) are not knowledge-bearing,
11007
+ // so they never become grounding for a packet — dropped before validation and storage.
11008
+ const groundedPaths = (input.paths ?? []).filter((path) => path && !isGroundingIgnored(input.projectDir, path));
11009
+ const meaningfulPaths = groundedPaths
11010
+ .filter((path) => meaningfulMemoryPath(path) && !shouldSkipRepoMemoryPath(path));
10902
11011
  const missingPaths = meaningfulPaths.filter((path) => !pathExistsInRepo(input.projectDir, path));
10903
11012
  // Citation validation. Strict mode (agent-facing record_memory tools / CLI) rejects a
10904
11013
  // write whose every cited path is missing — the PRD's "reject if citations don't exist".
@@ -10937,7 +11046,7 @@ function capture(input) {
10937
11046
  status: "approved",
10938
11047
  confidence: DEFAULT_CONFIDENCE,
10939
11048
  tags: input.tags ?? [],
10940
- paths: input.paths ?? [],
11049
+ paths: groundedPaths,
10941
11050
  stack: input.stack ?? [],
10942
11051
  source_refs: [
10943
11052
  {
@@ -10949,7 +11058,7 @@ function capture(input) {
10949
11058
  freshness: {
10950
11059
  ttl_days: 365,
10951
11060
  last_verified_at: createdAt,
10952
- path_fingerprints: memoryPathFingerprints(input.projectDir, input.paths ?? []),
11061
+ path_fingerprints: memoryPathFingerprints(input.projectDir, groundedPaths),
10953
11062
  path_fingerprint_policy: "source_hash_staleness",
10954
11063
  verification: "repo_local_agent_capture",
10955
11064
  },
@@ -12509,11 +12618,14 @@ function prCheck(projectDir) {
12509
12618
  const tree = gitTree(projectDir);
12510
12619
  const codeInputHash = currentCodeGraphInputHash(projectDir);
12511
12620
  const memoryInputHash = knowledgeGraphInputHash(projectDir, codeInputHash);
12512
- const stalePackets = loadPacketsFromDir(packetsDir(projectDir))
12621
+ const fpCache = new Map();
12622
+ const staleEntries = loadPacketsFromDir(packetsDir(projectDir))
12513
12623
  .filter((packet) => packet.status === "approved" || packet.status === "pending")
12514
- .map((packet) => ({ packet, reasons: staleMemoryReasons(projectDir, packet) }))
12515
- .filter((entry) => entry.reasons.length)
12516
- .map((entry) => staleFinding(entry.packet, entry.reasons));
12624
+ .map((packet) => ({ packet, reasons: staleMemoryReasons(projectDir, packet, fpCache), hard: recallHardStaleReason(projectDir, packet, fpCache) !== null }))
12625
+ .filter((entry) => entry.reasons.length);
12626
+ const stalePackets = staleEntries.map((entry) => staleFinding(entry.packet, entry.reasons));
12627
+ const hardStaleCount = staleEntries.filter((entry) => entry.hard).length;
12628
+ const softStaleCount = staleEntries.length - hardStaleCount;
12517
12629
  const memoryPacketChanges = unique(rawStatus
12518
12630
  .split(/\r?\n/)
12519
12631
  .map(parsePorcelainPath)
@@ -12526,13 +12638,18 @@ function prCheck(projectDir) {
12526
12638
  const errors = [...validation.errors];
12527
12639
  const warnings = [...validation.warnings];
12528
12640
  const requiredActions = [];
12529
- if (stalePackets.length) {
12530
- errors.push(`${stalePackets.length} stale memory packet(s) require update, verification, or supersession.`);
12531
- requiredActions.push("Run kage refresh, then update or supersede stale packets.");
12641
+ // Block only on hard-stale memory (cited files deleted, ttl expired, reported
12642
+ // stale). Soft-stale ("linked code changed since capture") is normal during
12643
+ // active development surface it as a warning, don't fail the gate.
12644
+ if (hardStaleCount) {
12645
+ errors.push(`${hardStaleCount} memory packet(s) are hard-stale (deleted citations, expired ttl, or reported) and must be updated or superseded.`);
12646
+ requiredActions.push("Run kage compact (or kage gc), then update or supersede the affected packets.");
12647
+ }
12648
+ if (softStaleCount) {
12649
+ warnings.push(`${softStaleCount} memory packet(s) reference code that changed since capture — review with kage verify (not blocking).`);
12532
12650
  }
12533
12651
  if (reconciliation.unresolved_count > 0) {
12534
- errors.push(`${reconciliation.unresolved_count} memory reconciliation item(s) require agent update or supersession.`);
12535
- requiredActions.push(...reconciliation.items.slice(0, 5).map((item) => item.next_action));
12652
+ warnings.push(`${reconciliation.unresolved_count} memory reconciliation item(s) may need update after recent code changes (review on handoff; not blocking).`);
12536
12653
  }
12537
12654
  if (!codeGraphCurrent || !memoryGraphCurrent) {
12538
12655
  errors.push("Generated graph artifacts are missing or not current for this working tree content.");
@@ -12540,8 +12657,7 @@ function prCheck(projectDir) {
12540
12657
  }
12541
12658
  const distillableSessions = sessions.sessions.filter((session) => session.durable_observations > 0);
12542
12659
  if (distillableSessions.length) {
12543
- errors.push(`${distillableSessions.length} distillable session learning${distillableSessions.length === 1 ? "" : "s"} require review before merge.`);
12544
- requiredActions.push(...distillableSessions.slice(0, 5).map((session) => session.next_action));
12660
+ warnings.push(`${distillableSessions.length} distillable session learning${distillableSessions.length === 1 ? "" : "s"} pending review (run kage distill; not blocking).`);
12545
12661
  }
12546
12662
  if (!memoryPacketChanges.length && overlay.changed_files.some((path) => !path.startsWith(".agent_memory/"))) {
12547
12663
  warnings.push("No repo memory packet changed for this branch. If durable knowledge was learned, run kage propose --from-diff or kage learn.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kage-core/kage-graph-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Local-first repo memory, code graph, and recall MCP server for coding agents",
5
5
  "main": "dist/index.js",
6
6
  "files": [