@kage-core/kage-graph-mcp 2.5.2 → 2.5.4
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 +10 -20
- package/dist/kernel.js +12 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1406,35 +1406,25 @@ async function main() {
|
|
|
1406
1406
|
}
|
|
1407
1407
|
if (command === "prompt-context") {
|
|
1408
1408
|
// Top-of-task recall for an ambient UserPromptSubmit hook: recall on the user's prompt
|
|
1409
|
-
// and emit the memory
|
|
1410
|
-
// Silent when nothing relevant is found, so hooks can gate on empty output.
|
|
1409
|
+
// and emit the memory. Silent when nothing relevant is found, so hooks can gate on empty.
|
|
1411
1410
|
const query = takeArg(args, "--query") ?? firstPositional(args);
|
|
1412
1411
|
if (!query)
|
|
1413
1412
|
usage();
|
|
1414
1413
|
const project = projectArg(args);
|
|
1415
1414
|
const result = (0, kernel_js_1.recall)(project, query, 5, false, {});
|
|
1416
|
-
// Surface any nudges the kage-watcher wrote (marks them surfaced so each shows once) —
|
|
1417
|
-
// this is what makes recall/nudges felt instead of silently sitting in a file.
|
|
1418
|
-
const nudges = (0, kernel_js_1.surfacePendingNudges)(project);
|
|
1419
1415
|
if (args.includes("--json")) {
|
|
1420
|
-
console.log(JSON.stringify(
|
|
1416
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1421
1417
|
return;
|
|
1422
1418
|
}
|
|
1423
|
-
|
|
1424
|
-
if (result.results.length) {
|
|
1425
|
-
// Lead with felt behavior, not a vanity/gameable token number: tell the agent these are
|
|
1426
|
-
// verified team memories to follow. (The tokens/$ ledger lives in `kage gains`.) Keep
|
|
1427
|
-
// only the trust-relevant stale-withheld signal here.
|
|
1428
|
-
const plural = result.results.length === 1 ? "y" : "ies";
|
|
1429
|
-
const withheld = result.value_receipt?.stale_withheld ?? 0;
|
|
1430
|
-
parts.push(result.context_block);
|
|
1431
|
-
parts.push(`_${result.results.length} verified team memor${plural} above — follow them.${withheld ? ` ${withheld} stale memor${withheld === 1 ? "y" : "ies"} withheld (code changed under them).` : ""}_`);
|
|
1432
|
-
}
|
|
1433
|
-
if (nudges.block)
|
|
1434
|
-
parts.push(nudges.block.trimStart());
|
|
1435
|
-
if (!parts.length)
|
|
1419
|
+
if (!result.results.length)
|
|
1436
1420
|
return;
|
|
1437
|
-
|
|
1421
|
+
// Lead with felt behavior, not a vanity/gameable token number: tell the agent these are
|
|
1422
|
+
// verified team memories to follow. (The tokens/$ ledger lives in `kage gains`.) Keep
|
|
1423
|
+
// only the trust-relevant stale-withheld signal here.
|
|
1424
|
+
const plural = result.results.length === 1 ? "y" : "ies";
|
|
1425
|
+
const withheld = result.value_receipt?.stale_withheld ?? 0;
|
|
1426
|
+
console.log(result.context_block);
|
|
1427
|
+
console.log(`_${result.results.length} verified team memor${plural} above — follow them.${withheld ? ` ${withheld} stale memor${withheld === 1 ? "y" : "ies"} withheld (code changed under them).` : ""}_`);
|
|
1438
1428
|
return;
|
|
1439
1429
|
}
|
|
1440
1430
|
if (command === "memory-access") {
|
package/dist/kernel.js
CHANGED
|
@@ -102,7 +102,6 @@ exports.compactProject = compactProject;
|
|
|
102
102
|
exports.installAgentPolicy = installAgentPolicy;
|
|
103
103
|
exports.createDenseEmbeddingProvider = createDenseEmbeddingProvider;
|
|
104
104
|
exports.buildEmbeddingIndex = buildEmbeddingIndex;
|
|
105
|
-
exports.surfacePendingNudges = surfacePendingNudges;
|
|
106
105
|
exports.recall = recall;
|
|
107
106
|
exports.recallWithEmbeddings = recallWithEmbeddings;
|
|
108
107
|
exports.queryCodeGraph = queryCodeGraph;
|
|
@@ -1203,6 +1202,17 @@ function replayTokensSaved(packets, contextBlock) {
|
|
|
1203
1202
|
return Math.max(0, discovery - estimateTokens(contextBlock));
|
|
1204
1203
|
}
|
|
1205
1204
|
const FILE_CONTEXT_PACKET_CAP = 3;
|
|
1205
|
+
// Normalize a file path to the repo-relative form used for packet citation matching:
|
|
1206
|
+
// absolute paths are made relative to the project root, backslashes/leading "./" and
|
|
1207
|
+
// trailing slashes are stripped. Returns "" for an empty/blank input.
|
|
1208
|
+
function normalizeRepoPath(projectDir, filePath) {
|
|
1209
|
+
let rel = (filePath ?? "").trim().replace(/\\/g, "/");
|
|
1210
|
+
if (!rel)
|
|
1211
|
+
return "";
|
|
1212
|
+
if ((0, node_path_1.isAbsolute)(rel))
|
|
1213
|
+
rel = (0, node_path_1.relative)((0, node_path_1.resolve)(projectDir), rel).replace(/\\/g, "/");
|
|
1214
|
+
return rel.replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1215
|
+
}
|
|
1206
1216
|
// PreToolUse(Read) injection: verified memory at the moment of relevance. Returns at
|
|
1207
1217
|
// most three currently-verified packets that cite the file an agent is about to read,
|
|
1208
1218
|
// as a compact context block. Reuses the same staleness machinery as recall — a packet
|
|
@@ -1216,12 +1226,7 @@ function kageFileContext(projectDir, filePath) {
|
|
|
1216
1226
|
packets: [],
|
|
1217
1227
|
context_block: "",
|
|
1218
1228
|
};
|
|
1219
|
-
|
|
1220
|
-
return result;
|
|
1221
|
-
let rel = filePath.trim().replace(/\\/g, "/");
|
|
1222
|
-
if ((0, node_path_1.isAbsolute)(rel))
|
|
1223
|
-
rel = (0, node_path_1.relative)((0, node_path_1.resolve)(projectDir), rel).replace(/\\/g, "/");
|
|
1224
|
-
rel = rel.replace(/^\.\//, "").replace(/^\/+/, "");
|
|
1229
|
+
const rel = normalizeRepoPath(projectDir, filePath);
|
|
1225
1230
|
result.path = rel;
|
|
1226
1231
|
// Files outside the project (or an uninitialized project) never produce context.
|
|
1227
1232
|
if (!rel || rel.startsWith("..") || !(0, node_fs_1.existsSync)(memoryRoot(projectDir)))
|
|
@@ -7965,51 +7970,6 @@ function isSerializedDumpBody(body) {
|
|
|
7965
7970
|
return isSerializedDumpTitle(t)
|
|
7966
7971
|
|| /<task-notification\b|<tool-use-id\b|"hookSpecificOutput"|"isImage"\s*:|"noOutputExpected"\s*:|"interrupted"\s*:\s*(true|false)/i.test(t.slice(0, 4000));
|
|
7967
7972
|
}
|
|
7968
|
-
// Surface watcher nudges: read the nudge inbox the kage-watcher writes, return the
|
|
7969
|
-
// unsurfaced ones as a prominent block, and mark them surfaced so each shows exactly once.
|
|
7970
|
-
// This is what makes recall/nudges FELT — the UserPromptSubmit hook injects this block, so
|
|
7971
|
-
// the agent sees "act on this" memory instead of it sitting silently in a file.
|
|
7972
|
-
function surfacePendingNudges(projectDir) {
|
|
7973
|
-
const path = (0, node_path_1.join)(projectDir, ".agent_memory", "nudges", "pending.jsonl");
|
|
7974
|
-
if (!(0, node_fs_1.existsSync)(path))
|
|
7975
|
-
return { block: "", items: [], count: 0 };
|
|
7976
|
-
let raw;
|
|
7977
|
-
try {
|
|
7978
|
-
raw = (0, node_fs_1.readFileSync)(path, "utf8");
|
|
7979
|
-
}
|
|
7980
|
-
catch {
|
|
7981
|
-
return { block: "", items: [], count: 0 };
|
|
7982
|
-
}
|
|
7983
|
-
const records = [];
|
|
7984
|
-
for (const line of raw.split("\n")) {
|
|
7985
|
-
const trimmed = line.trim();
|
|
7986
|
-
if (!trimmed)
|
|
7987
|
-
continue;
|
|
7988
|
-
try {
|
|
7989
|
-
const parsed = JSON.parse(trimmed);
|
|
7990
|
-
if (parsed && typeof parsed === "object")
|
|
7991
|
-
records.push(parsed);
|
|
7992
|
-
}
|
|
7993
|
-
catch { /* drop malformed lines */ }
|
|
7994
|
-
}
|
|
7995
|
-
const unsurfaced = records.filter((record) => record.surfaced !== true && typeof record.message === "string");
|
|
7996
|
-
if (!unsurfaced.length)
|
|
7997
|
-
return { block: "", items: [], count: 0 };
|
|
7998
|
-
const items = unsurfaced.map((record) => ({
|
|
7999
|
-
message: String(record.message),
|
|
8000
|
-
file: typeof record.file === "string" ? record.file : undefined,
|
|
8001
|
-
kind: typeof record.kind === "string" ? record.kind : undefined,
|
|
8002
|
-
}));
|
|
8003
|
-
for (const record of unsurfaced)
|
|
8004
|
-
record.surfaced = true;
|
|
8005
|
-
try {
|
|
8006
|
-
(0, node_fs_1.writeFileSync)(path, `${records.map((record) => JSON.stringify(record)).join("\n")}\n`, "utf8");
|
|
8007
|
-
}
|
|
8008
|
-
catch { /* best-effort; recall still works */ }
|
|
8009
|
-
const block = ["", "## ⚠ Kage nudges — act on these before proceeding",
|
|
8010
|
-
...items.map((item) => `- ${item.file ? `${item.file}: ` : ""}${item.message}`)].join("\n");
|
|
8011
|
-
return { block, items, count: items.length };
|
|
8012
|
-
}
|
|
8013
7973
|
// Collapse whitespace and hard-cap a value rendered inline in a context block, so one
|
|
8014
7974
|
// oversized field (e.g. a graph fact whose body is a raw transcript) can never blow up
|
|
8015
7975
|
// the assembled output — the 270k-char overflow that motivated this guard.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kage-core/kage-graph-mcp",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.4",
|
|
4
4
|
"description": "Team memory for coding agents: captures the decisions, runbooks, and bug fixes that get lost, verified against your code and shared via git. MCP server, zero deps, no account.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|