@kage-core/kage-graph-mcp 3.1.0 → 3.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
@@ -10,11 +10,13 @@ const node_process_1 = require("node:process");
10
10
  const daemon_js_1 = require("./daemon.js");
11
11
  const kernel_js_1 = require("./kernel.js");
12
12
  const graph_registry_js_1 = require("./graph-registry.js");
13
+ const check_js_1 = require("./check.js");
13
14
  const okf_js_1 = require("./okf.js");
14
15
  const CORE_USAGE = `Kage — code-grounded memory for coding agents
15
16
 
16
17
  Core commands:
17
18
  kage install [--project <dir>] one-shot: init + index + auto-wire detected agents
19
+ kage check [--project <dir>] verify CLAUDE.md/AGENTS.md/docs claims against the code — counted, not estimated
18
20
  kage scan --project <dir> 60-second truth report on any repo (zero setup)
19
21
  kage init --project <dir> create repo memory (.agent_memory only)
20
22
  kage index --project <dir> [--full] build/refresh code graph + indexes
@@ -32,6 +34,7 @@ const FULL_USAGE = `Kage — full command reference
32
34
 
33
35
  Usage:
34
36
  kage index --project <dir>
37
+ kage check [--project <dir>] [--json | --md] [--base <ref>] [--write-baseline] [--init-ci [--force]]
35
38
  kage scan --project <dir> [--json] [--scorecard [--out <file>]]
36
39
  kage demo [--project <dir>]
37
40
  kage install [--project <dir>] [--agents a,b] [--no-agents] [--json]
@@ -307,6 +310,41 @@ async function main() {
307
310
  console.log(`Indexes:\n${result.indexes.map((path) => ` - ${path}`).join("\n")}`);
308
311
  return;
309
312
  }
313
+ if (command === "check") {
314
+ const checkTarget = (0, node_path_1.resolve)(projectArg(args));
315
+ if (args.includes("--init-ci")) {
316
+ const workflowPath = (0, node_path_1.join)(checkTarget, ".github", "workflows", "kage-check.yml");
317
+ if ((0, node_fs_1.existsSync)(workflowPath) && !args.includes("--force")) {
318
+ console.log(`${workflowPath} already exists — rerun with --force to overwrite.`);
319
+ process.exit(2);
320
+ }
321
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(workflowPath), { recursive: true });
322
+ (0, node_fs_1.writeFileSync)(workflowPath, (0, check_js_1.kageCheckWorkflowYaml)(), "utf8");
323
+ console.log(`Wrote ${workflowPath}`);
324
+ console.log("Every PR now gets a drift check: it comments and fails only when the diff breaks a documented claim.");
325
+ return;
326
+ }
327
+ let report;
328
+ try {
329
+ report = (0, check_js_1.driftCheck)(checkTarget, { base: takeArg(args, "--base") });
330
+ }
331
+ catch (error) {
332
+ console.error(`kage check failed: ${error instanceof Error ? error.message : String(error)}`);
333
+ process.exit(2);
334
+ }
335
+ if (args.includes("--write-baseline")) {
336
+ const written = (0, check_js_1.writeCheckBaseline)(checkTarget, report);
337
+ console.log(`Baseline written: ${written} (${report.confirmed.length} finding(s) accepted — future runs gate only on new drift)`);
338
+ return;
339
+ }
340
+ if (args.includes("--json"))
341
+ console.log(JSON.stringify(report, null, 2));
342
+ else if (args.includes("--md"))
343
+ console.log((0, check_js_1.checkReportMarkdown)(report));
344
+ else
345
+ console.log((0, check_js_1.formatCheckReport)(report));
346
+ process.exit(report.totals.confirmed > 0 ? 1 : 0);
347
+ }
310
348
  if (command === "scan") {
311
349
  const scanTarget = (0, node_path_1.resolve)(projectArg(args));
312
350
  if (scanTarget === (0, node_os_1.homedir)()) {
@@ -1577,13 +1615,18 @@ async function main() {
1577
1615
  const packetId = takeArg(args, "--packet");
1578
1616
  if (!packetId)
1579
1617
  usage();
1580
- const result = (0, kernel_js_1.reverifyMemory)(projectArg(args), packetId);
1618
+ const result = (0, kernel_js_1.reverifyMemory)(projectArg(args), packetId, {
1619
+ evidence: takeArg(args, "--evidence"),
1620
+ verifiedBy: takeArg(args, "--verified-by"),
1621
+ });
1581
1622
  if (args.includes("--json")) {
1582
1623
  console.log(JSON.stringify(result, null, 2));
1583
1624
  }
1584
1625
  else if (result.ok) {
1585
1626
  console.log(`Reverified ${result.packet_id}`);
1586
1627
  console.log(` grounding refreshed for ${result.refreshed_paths.length} path(s)${result.was_stale ? " · stale flag cleared" : ""}`);
1628
+ if (result.changed_paths.length)
1629
+ console.log(` evidence recorded for changed path(s): ${result.changed_paths.join(", ")}`);
1587
1630
  if (result.missing_paths.length)
1588
1631
  console.log(` dropped missing path(s): ${result.missing_paths.join(", ")}`);
1589
1632
  }
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
8
8
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
9
9
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
10
10
  const kernel_js_1 = require("./kernel.js");
11
+ const check_js_1 = require("./check.js");
11
12
  const graph_registry_js_1 = require("./graph-registry.js");
12
13
  const BASE_URL = "https://raw.githubusercontent.com/kage-core/kage-graph/master";
13
14
  async function fetchText(url) {
@@ -91,6 +92,7 @@ const KAGE_WORKFLOW_TEXT = "Kage memory workflow (this tool performs no action;
91
92
  // mode (KAGE_TOOLS=full) or via the CLI. Keeping the default small enough that
92
93
  // the client always-loads it removes the per-call ToolSearch round-trip.
93
94
  exports.CORE_TOOLS = new Set([
95
+ "kage_check",
94
96
  "kage_context",
95
97
  "kage_learn",
96
98
  "kage_supersede",
@@ -536,6 +538,19 @@ function listTools() {
536
538
  required: ["project_dir"],
537
539
  },
538
540
  },
541
+ {
542
+ name: "kage_check",
543
+ description: "Verify the claims in agent-context files (CLAUDE.md, AGENTS.md, .cursor/rules, README, docs) against the code: cited paths, npm scripts, make targets, CLI subcommands. Reports confirmed drift / verified true / unverifiable — every number is a reproducible check, never an estimate. Pass base to gate only drift introduced since that ref.",
544
+ annotations: { title: "Verify agent-context files against the code", readOnlyHint: true },
545
+ inputSchema: {
546
+ type: "object",
547
+ properties: {
548
+ project_dir: { type: "string", description: "Absolute path to the repository root." },
549
+ base: { type: "string", description: "Optional git ref: only report drift attributable to changes since this ref (diff-aware mode for PRs)." },
550
+ },
551
+ required: ["project_dir"],
552
+ },
553
+ },
539
554
  {
540
555
  name: "kage_memory_reconcile",
541
556
  description: "Return agent-owned memory reconciliation work when source files linked to existing memory changed. Agents must update, supersede, or mark stale memory before final handoff.",
@@ -1097,10 +1112,12 @@ async function callTool(name, args) {
1097
1112
  reconciliation.unresolved_count ? `\n## Memory Reconciliation\n${reconciliation.agent_instruction}` : "",
1098
1113
  `\n_${validationText}_`,
1099
1114
  ].filter(Boolean).join("");
1100
- // Visible receipt: surface what the harness saved today so agents relay it. Kept
1101
- // outside the size cap so it always survives.
1115
+ // Receipt of counts only: served/withheld are observable events; token or
1116
+ // dollar "savings" were estimates with no counterfactual and do not ship.
1102
1117
  const gains = (0, kernel_js_1.valueSummary)(projectDir).today;
1103
- const gainsLine = `\n\nGains: ~${(0, kernel_js_1.formatTokenCount)(gains.tokens_saved)} tokens saved this session · stale memories withheld: ${gains.stale_withheld}`;
1118
+ const gainsLine = gains.stale_withheld > 0
1119
+ ? `\n\n_${gains.stale_withheld} stale memor${gains.stale_withheld === 1 ? "y" : "ies"} withheld today (cited code changed; run \`kage doctor\` to review)._`
1120
+ : "";
1104
1121
  // Backstop: per-field clamping + graph dedup keep this compact in practice, but never
1105
1122
  // let a pathological repo overflow the MCP response again. ~24k chars ≈ 6k tokens.
1106
1123
  const MAX_CONTEXT_CHARS = 24000;
@@ -1122,11 +1139,11 @@ async function callTool(name, args) {
1122
1139
  if (docsSection)
1123
1140
  result.context_block = `${result.context_block}\n\n${docsSection}`;
1124
1141
  }
1125
- // Visible receipt: in text mode, surface what this recall saved so the agent
1126
- // can relay it. Value is otherwise invisible; an unseen win is a churned user.
1142
+ // Counts only: stale-withheld is an observable event; estimated token
1143
+ // savings are not a measurement and do not ship.
1127
1144
  const receipt = result.value_receipt;
1128
- const gainsLine = receipt && (receipt.tokens_saved > 0 || receipt.stale_withheld > 0)
1129
- ? `\n\nGains: ~${(0, kernel_js_1.formatTokenCount)(receipt.tokens_saved)} tokens saved by this recall${receipt.stale_withheld ? ` · stale memories withheld: ${receipt.stale_withheld}` : ""}`
1145
+ const gainsLine = receipt && receipt.stale_withheld > 0
1146
+ ? `\n\n_${receipt.stale_withheld} stale memor${receipt.stale_withheld === 1 ? "y" : "ies"} withheld by this recall (cited code changed)._`
1130
1147
  : "";
1131
1148
  return {
1132
1149
  content: [{ type: "text", text: args?.json || args?.explain ? JSON.stringify(result, null, 2) : `${result.context_block}${gainsLine}` }],
@@ -1385,6 +1402,14 @@ async function callTool(name, args) {
1385
1402
  isError: !result.ok,
1386
1403
  };
1387
1404
  }
1405
+ if (name === "kage_check") {
1406
+ const report = (0, check_js_1.driftCheck)(String(args?.project_dir ?? ""), {
1407
+ base: typeof args?.base === "string" ? args.base : undefined,
1408
+ });
1409
+ return {
1410
+ content: [{ type: "text", text: (0, check_js_1.formatCheckReport)(report) }],
1411
+ };
1412
+ }
1388
1413
  if (name === "kage_quality") {
1389
1414
  const result = (0, kernel_js_1.qualityReport)(String(args?.project_dir ?? ""));
1390
1415
  return {