mindgraph-mcp 0.22.0 → 0.24.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/README.md +37 -1
- package/dist/cli.js +915 -258
- package/dist/index.js +263 -136
- package/dist/server.d.mts +4 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +32 -2
- package/dist/server.mjs +32 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -22,36 +22,93 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
mod
|
|
23
23
|
));
|
|
24
24
|
|
|
25
|
+
// src/call-ledger.ts
|
|
26
|
+
var import_node_crypto = __toESM(require("crypto"));
|
|
27
|
+
var import_node_fs = __toESM(require("fs"));
|
|
28
|
+
var import_node_os = __toESM(require("os"));
|
|
29
|
+
var import_node_path = __toESM(require("path"));
|
|
30
|
+
var TTL_MS = 10 * 6e4;
|
|
31
|
+
function callsDir(options) {
|
|
32
|
+
return import_node_path.default.join(
|
|
33
|
+
options.runtimeDir || process.env.MINDGRAPH_RUNTIME_DIR || import_node_path.default.join(import_node_os.default.homedir(), ".mindgraph", "runtime"),
|
|
34
|
+
"calls"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function stableJson(value) {
|
|
38
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
39
|
+
if (value && typeof value === "object") {
|
|
40
|
+
return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
41
|
+
}
|
|
42
|
+
return JSON.stringify(value) ?? "null";
|
|
43
|
+
}
|
|
44
|
+
function bareToolName(name) {
|
|
45
|
+
const cut = name.lastIndexOf("__");
|
|
46
|
+
return cut === -1 ? name : name.slice(cut + 2);
|
|
47
|
+
}
|
|
48
|
+
function callKey(toolName, toolInput) {
|
|
49
|
+
return import_node_crypto.default.createHash("sha256").update(`${bareToolName(toolName)}\0${stableJson(toolInput)}`).digest("hex");
|
|
50
|
+
}
|
|
51
|
+
function resolvePendingCall(toolName, toolInput, options = {}) {
|
|
52
|
+
const now = (options.now ?? Date.now)();
|
|
53
|
+
const dir = callsDir(options);
|
|
54
|
+
const prefix = `${callKey(toolName, toolInput)}.`;
|
|
55
|
+
let names;
|
|
56
|
+
try {
|
|
57
|
+
names = import_node_fs.default.readdirSync(dir).filter((name) => name.startsWith(prefix) && name.endsWith(".json"));
|
|
58
|
+
} catch {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
let matches = [];
|
|
62
|
+
for (const name of names) {
|
|
63
|
+
try {
|
|
64
|
+
const record = JSON.parse(
|
|
65
|
+
import_node_fs.default.readFileSync(import_node_path.default.join(dir, name), "utf8")
|
|
66
|
+
);
|
|
67
|
+
if (typeof record.createdAt === "number" && now - record.createdAt <= TTL_MS && record.fills && typeof record.fills === "object") {
|
|
68
|
+
matches.push(record);
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (matches.length > 1) {
|
|
74
|
+
const projectDir = options.projectDir ?? process.env.CLAUDE_PROJECT_DIR;
|
|
75
|
+
matches = projectDir ? matches.filter((record) => record.projectDir === projectDir) : [];
|
|
76
|
+
}
|
|
77
|
+
return matches.length === 1 ? matches[0].fills : void 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
25
80
|
// src/index.ts
|
|
26
81
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
27
82
|
var import_mindgraph3 = require("mindgraph");
|
|
28
83
|
|
|
29
84
|
// src/hook-env.ts
|
|
30
|
-
var
|
|
31
|
-
var
|
|
32
|
-
var
|
|
33
|
-
var
|
|
85
|
+
var import_node_crypto2 = __toESM(require("crypto"));
|
|
86
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
87
|
+
var import_node_os2 = __toESM(require("os"));
|
|
88
|
+
var import_node_path2 = __toESM(require("path"));
|
|
34
89
|
function stableAgentId() {
|
|
35
90
|
let user = "user";
|
|
36
91
|
try {
|
|
37
|
-
user =
|
|
92
|
+
user = import_node_os2.default.userInfo().username || "user";
|
|
38
93
|
} catch {
|
|
39
94
|
}
|
|
40
|
-
const digest =
|
|
95
|
+
const digest = import_node_crypto2.default.createHash("sha256").update(`${user}@${import_node_os2.default.hostname()}`).digest("hex").slice(0, 12);
|
|
41
96
|
return `u-${digest}`;
|
|
42
97
|
}
|
|
43
98
|
function hookEnvPath(dir) {
|
|
44
|
-
return
|
|
99
|
+
return import_node_path2.default.join(dir || import_node_path2.default.join(import_node_os2.default.homedir(), ".mindgraph"), "hooks.json");
|
|
45
100
|
}
|
|
46
101
|
function loadHookEnv(dir) {
|
|
47
102
|
try {
|
|
48
|
-
const raw =
|
|
103
|
+
const raw = import_node_fs2.default.readFileSync(hookEnvPath(dir), "utf8");
|
|
49
104
|
const parsed = JSON.parse(raw);
|
|
50
105
|
return {
|
|
51
106
|
baseUrl: typeof parsed.baseUrl === "string" ? parsed.baseUrl : void 0,
|
|
52
107
|
apiKey: typeof parsed.apiKey === "string" ? parsed.apiKey : void 0,
|
|
53
108
|
agentId: typeof parsed.agentId === "string" ? parsed.agentId : void 0,
|
|
54
|
-
orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0
|
|
109
|
+
orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0,
|
|
110
|
+
promptRecall: typeof parsed.promptRecall === "boolean" ? parsed.promptRecall : void 0,
|
|
111
|
+
capture: typeof parsed.capture === "boolean" ? parsed.capture : void 0
|
|
55
112
|
};
|
|
56
113
|
} catch {
|
|
57
114
|
return {};
|
|
@@ -355,6 +412,14 @@ function hiddenSections(payload) {
|
|
|
355
412
|
function renderContext(response, maxChars) {
|
|
356
413
|
const payload = obj(response);
|
|
357
414
|
if (!payload) return void 0;
|
|
415
|
+
if (typeof payload.context_text === "string" && payload.context_text.length > 0) {
|
|
416
|
+
const text2 = payload.context_text;
|
|
417
|
+
if (maxChars !== void 0 && text2.length > maxChars) {
|
|
418
|
+
return `${text2.slice(0, maxChars)}
|
|
419
|
+
[truncated to ${maxChars} characters]`;
|
|
420
|
+
}
|
|
421
|
+
return text2;
|
|
422
|
+
}
|
|
358
423
|
const graph = obj(payload.graph);
|
|
359
424
|
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
360
425
|
const shaped = rawNodes.filter(isNodeShaped);
|
|
@@ -577,15 +642,15 @@ function renderTraversal(response, maxChars) {
|
|
|
577
642
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
578
643
|
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
579
644
|
if (paths.length > 0) {
|
|
580
|
-
const items = paths.map((raw) => obj(raw)).filter((
|
|
581
|
-
const uids = Array.isArray(
|
|
582
|
-
const labels = Array.isArray(
|
|
645
|
+
const items = paths.map((raw) => obj(raw)).filter((path6) => Boolean(path6)).map((path6, index) => {
|
|
646
|
+
const uids = Array.isArray(path6.node_uids) ? path6.node_uids.map(String) : [];
|
|
647
|
+
const labels = Array.isArray(path6.labels) ? path6.labels.map(String) : Array.isArray(path6.nodes) ? path6.nodes.map((hop) => {
|
|
583
648
|
const hopNode = obj(hop);
|
|
584
649
|
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
585
650
|
}) : [];
|
|
586
651
|
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
587
|
-
const cost = num(
|
|
588
|
-
const confidence = num(
|
|
652
|
+
const cost = num(path6.cost) ?? num(path6.path_cost);
|
|
653
|
+
const confidence = num(path6.path_confidence);
|
|
589
654
|
const meta = [
|
|
590
655
|
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
591
656
|
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
@@ -1587,7 +1652,7 @@ ${conversation}`
|
|
|
1587
1652
|
}
|
|
1588
1653
|
|
|
1589
1654
|
// src/memory-tool.ts
|
|
1590
|
-
var
|
|
1655
|
+
var import_node_crypto3 = require("crypto");
|
|
1591
1656
|
var RESULT_SCHEMA_VERSION = "mindgraph.memory.result.v1";
|
|
1592
1657
|
var DEFAULT_OUTPUT_CHARS = 8e3;
|
|
1593
1658
|
var MIN_OUTPUT_CHARS = 512;
|
|
@@ -1735,15 +1800,15 @@ function obj2(value) {
|
|
|
1735
1800
|
function text(value) {
|
|
1736
1801
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1737
1802
|
}
|
|
1738
|
-
function
|
|
1739
|
-
if (Array.isArray(value)) return `[${value.map(
|
|
1803
|
+
function stableJson2(value) {
|
|
1804
|
+
if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
|
|
1740
1805
|
if (value && typeof value === "object") {
|
|
1741
|
-
return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${
|
|
1806
|
+
return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
|
|
1742
1807
|
}
|
|
1743
1808
|
return JSON.stringify(value);
|
|
1744
1809
|
}
|
|
1745
1810
|
function sha256(value) {
|
|
1746
|
-
return (0,
|
|
1811
|
+
return (0, import_node_crypto3.createHash)("sha256").update(value).digest("hex");
|
|
1747
1812
|
}
|
|
1748
1813
|
function outputBudget(value) {
|
|
1749
1814
|
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OUTPUT_CHARS;
|
|
@@ -2049,7 +2114,7 @@ async function handleMemoryTool(client2, args, config = {}) {
|
|
|
2049
2114
|
scope_uids: Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : void 0
|
|
2050
2115
|
});
|
|
2051
2116
|
const contextId = `ctx_${sha256(
|
|
2052
|
-
|
|
2117
|
+
stableJson2({ mode, scope, strategy, query: text(args.query), rendered_hash: renderedHash })
|
|
2053
2118
|
).slice(0, 24)}`;
|
|
2054
2119
|
const bounded = candidates.length > items.length || rendered2.includes("item(s) omitted");
|
|
2055
2120
|
const result = {
|
|
@@ -2210,15 +2275,15 @@ async function readGovernedResource(client2, uri, governanceConfig, memoryConfig
|
|
|
2210
2275
|
}
|
|
2211
2276
|
|
|
2212
2277
|
// src/tools.ts
|
|
2213
|
-
var
|
|
2278
|
+
var import_node_crypto6 = require("crypto");
|
|
2214
2279
|
|
|
2215
2280
|
// src/codegraph.ts
|
|
2216
2281
|
var import_node_child_process = require("child_process");
|
|
2217
|
-
var
|
|
2282
|
+
var import_node_fs3 = require("fs");
|
|
2218
2283
|
var import_promises = require("fs/promises");
|
|
2219
|
-
var
|
|
2220
|
-
var
|
|
2221
|
-
var
|
|
2284
|
+
var import_node_os3 = require("os");
|
|
2285
|
+
var import_node_path3 = require("path");
|
|
2286
|
+
var import_node_crypto4 = require("crypto");
|
|
2222
2287
|
var UnknownRepositoryError = class extends Error {
|
|
2223
2288
|
constructor(repo) {
|
|
2224
2289
|
super(
|
|
@@ -2249,14 +2314,14 @@ function unavailabilityCaveats(reason, message) {
|
|
|
2249
2314
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
2250
2315
|
var DEFAULT_STATUS_TTL_MS = 6e4;
|
|
2251
2316
|
var MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
2252
|
-
function normalizePath(
|
|
2253
|
-
return
|
|
2317
|
+
function normalizePath(path6) {
|
|
2318
|
+
return path6.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
2254
2319
|
}
|
|
2255
2320
|
function normalizeSignature(signature) {
|
|
2256
2321
|
return signature.replace(/\s+/g, " ").trim();
|
|
2257
2322
|
}
|
|
2258
|
-
function languageFromPath(
|
|
2259
|
-
const extension = (0,
|
|
2323
|
+
function languageFromPath(path6) {
|
|
2324
|
+
const extension = (0, import_node_path3.extname)(path6).toLowerCase();
|
|
2260
2325
|
return {
|
|
2261
2326
|
".c": "c",
|
|
2262
2327
|
".cc": "cpp",
|
|
@@ -2305,30 +2370,30 @@ function normalizeRemote(remote) {
|
|
|
2305
2370
|
}
|
|
2306
2371
|
}
|
|
2307
2372
|
function pathContains(root, candidate) {
|
|
2308
|
-
const child = (0,
|
|
2309
|
-
return child === "" || child !== ".." && !child.startsWith(`..${
|
|
2373
|
+
const child = (0, import_node_path3.relative)(root, candidate);
|
|
2374
|
+
return child === "" || child !== ".." && !child.startsWith(`..${import_node_path3.sep}`) && !(0, import_node_path3.isAbsolute)(child);
|
|
2310
2375
|
}
|
|
2311
2376
|
function nearestExistingDirectory(candidate) {
|
|
2312
|
-
let current = (0,
|
|
2377
|
+
let current = (0, import_node_path3.resolve)(candidate);
|
|
2313
2378
|
for (; ; ) {
|
|
2314
|
-
if ((0,
|
|
2379
|
+
if ((0, import_node_fs3.existsSync)(current)) {
|
|
2315
2380
|
try {
|
|
2316
|
-
return (0,
|
|
2381
|
+
return (0, import_node_fs3.statSync)(current).isDirectory() ? current : (0, import_node_path3.dirname)(current);
|
|
2317
2382
|
} catch {
|
|
2318
2383
|
}
|
|
2319
2384
|
}
|
|
2320
|
-
const parent = (0,
|
|
2385
|
+
const parent = (0, import_node_path3.dirname)(current);
|
|
2321
2386
|
if (parent === current) return current;
|
|
2322
2387
|
current = parent;
|
|
2323
2388
|
}
|
|
2324
2389
|
}
|
|
2325
2390
|
function findWorkspaceFile(start, explicit) {
|
|
2326
|
-
if (explicit) return (0,
|
|
2327
|
-
let current = (0,
|
|
2391
|
+
if (explicit) return (0, import_node_path3.resolve)(start, explicit);
|
|
2392
|
+
let current = (0, import_node_path3.resolve)(start);
|
|
2328
2393
|
for (; ; ) {
|
|
2329
|
-
const candidate = (0,
|
|
2330
|
-
if ((0,
|
|
2331
|
-
const parent = (0,
|
|
2394
|
+
const candidate = (0, import_node_path3.join)(current, ".mindgraph", "workspace.json");
|
|
2395
|
+
if ((0, import_node_fs3.existsSync)(candidate)) return candidate;
|
|
2396
|
+
const parent = (0, import_node_path3.dirname)(current);
|
|
2332
2397
|
if (parent === current) return void 0;
|
|
2333
2398
|
current = parent;
|
|
2334
2399
|
}
|
|
@@ -2415,7 +2480,7 @@ var CodegraphAdapter = class {
|
|
|
2415
2480
|
async resolveRepository(repo, fallbackSpaceUid) {
|
|
2416
2481
|
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
2417
2482
|
const selected = repo && configured.find((candidate) => candidate.repoId === repo) || repo && configured.find(
|
|
2418
|
-
(candidate) => candidate.repoRoot === repo || (0,
|
|
2483
|
+
(candidate) => candidate.repoRoot === repo || (0, import_node_path3.basename)(candidate.repoRoot) === repo
|
|
2419
2484
|
) || (!repo ? [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, this.cwd)) : void 0) || (!repo && configured.length === 1 ? configured[0] : void 0);
|
|
2420
2485
|
if (selected) {
|
|
2421
2486
|
return !repo && this.env.MINDGRAPH_REPO_ID ? this.resolveRepositoryIdentity(
|
|
@@ -2425,10 +2490,10 @@ var CodegraphAdapter = class {
|
|
|
2425
2490
|
) : selected;
|
|
2426
2491
|
}
|
|
2427
2492
|
if (repo) {
|
|
2428
|
-
const requestedPath = await (0, import_promises.realpath)((0,
|
|
2429
|
-
() => (0,
|
|
2493
|
+
const requestedPath = await (0, import_promises.realpath)((0, import_node_path3.resolve)(this.cwd, repo)).catch(
|
|
2494
|
+
() => (0, import_node_path3.resolve)(this.cwd, repo)
|
|
2430
2495
|
);
|
|
2431
|
-
if (!(0,
|
|
2496
|
+
if (!(0, import_node_fs3.existsSync)(requestedPath)) {
|
|
2432
2497
|
const current = await this.currentRepository(fallbackSpaceUid);
|
|
2433
2498
|
if (current.repoId === repo) return current;
|
|
2434
2499
|
throw new UnknownRepositoryError(repo);
|
|
@@ -2444,8 +2509,8 @@ var CodegraphAdapter = class {
|
|
|
2444
2509
|
}
|
|
2445
2510
|
async currentRepository(fallbackSpaceUid) {
|
|
2446
2511
|
const requestedRoot = this.env.MINDGRAPH_REPO_ROOT || this.cwd;
|
|
2447
|
-
const requestedPath = await (0, import_promises.realpath)((0,
|
|
2448
|
-
() => (0,
|
|
2512
|
+
const requestedPath = await (0, import_promises.realpath)((0, import_node_path3.resolve)(this.cwd, requestedRoot)).catch(
|
|
2513
|
+
() => (0, import_node_path3.resolve)(this.cwd, requestedRoot)
|
|
2449
2514
|
);
|
|
2450
2515
|
const repoRoot = await this.gitRoot(requestedPath) ?? requestedPath;
|
|
2451
2516
|
return this.resolveRepositoryIdentity(
|
|
@@ -2466,7 +2531,7 @@ var CodegraphAdapter = class {
|
|
|
2466
2531
|
);
|
|
2467
2532
|
}
|
|
2468
2533
|
async resolveRepositoryIdentity(root, explicitRepoId, spaceUid) {
|
|
2469
|
-
const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0,
|
|
2534
|
+
const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0, import_node_path3.resolve)(root));
|
|
2470
2535
|
const cacheKey = `${repoRoot}\0${explicitRepoId ?? ""}\0${spaceUid ?? ""}`;
|
|
2471
2536
|
const cached = this.identityCache.get(cacheKey);
|
|
2472
2537
|
if (cached) return cached;
|
|
@@ -2496,20 +2561,20 @@ var CodegraphAdapter = class {
|
|
|
2496
2561
|
this.env
|
|
2497
2562
|
).catch(() => "");
|
|
2498
2563
|
if (!rawCommonDir.trim()) {
|
|
2499
|
-
return `local:${(0,
|
|
2564
|
+
return `local:${(0, import_node_path3.basename)(repoRoot)}`;
|
|
2500
2565
|
}
|
|
2501
|
-
const commonDir = (0,
|
|
2502
|
-
const identityDir = (0,
|
|
2503
|
-
const identityFile = (0,
|
|
2566
|
+
const commonDir = (0, import_node_path3.resolve)(repoRoot, rawCommonDir.trim());
|
|
2567
|
+
const identityDir = (0, import_node_path3.join)(commonDir, "mindgraph");
|
|
2568
|
+
const identityFile = (0, import_node_path3.join)(identityDir, "repository-id");
|
|
2504
2569
|
try {
|
|
2505
|
-
const existing = (0,
|
|
2570
|
+
const existing = (0, import_node_fs3.readFileSync)(identityFile, "utf8").trim();
|
|
2506
2571
|
if (existing) return existing;
|
|
2507
2572
|
} catch {
|
|
2508
2573
|
}
|
|
2509
|
-
(0,
|
|
2510
|
-
const generated = (0,
|
|
2574
|
+
(0, import_node_fs3.mkdirSync)(identityDir, { recursive: true, mode: 448 });
|
|
2575
|
+
const generated = (0, import_node_crypto4.randomUUID)();
|
|
2511
2576
|
try {
|
|
2512
|
-
(0,
|
|
2577
|
+
(0, import_node_fs3.writeFileSync)(identityFile, `${generated}
|
|
2513
2578
|
`, {
|
|
2514
2579
|
encoding: "utf8",
|
|
2515
2580
|
flag: "wx",
|
|
@@ -2518,7 +2583,7 @@ var CodegraphAdapter = class {
|
|
|
2518
2583
|
return generated;
|
|
2519
2584
|
} catch (cause) {
|
|
2520
2585
|
if (cause.code !== "EEXIST") throw cause;
|
|
2521
|
-
const winner = (0,
|
|
2586
|
+
const winner = (0, import_node_fs3.readFileSync)(identityFile, "utf8").trim();
|
|
2522
2587
|
if (!winner) throw new Error(`empty repository identity file: ${identityFile}`);
|
|
2523
2588
|
return winner;
|
|
2524
2589
|
}
|
|
@@ -2531,12 +2596,12 @@ var CodegraphAdapter = class {
|
|
|
2531
2596
|
Math.min(this.timeoutMs, 2e3),
|
|
2532
2597
|
this.env
|
|
2533
2598
|
).catch(() => "");
|
|
2534
|
-
return raw.trim() ? (0,
|
|
2599
|
+
return raw.trim() ? (0, import_node_path3.resolve)(start, raw.trim()) : void 0;
|
|
2535
2600
|
}
|
|
2536
2601
|
/** Why the workspace map yielded no repositories — for callers that were
|
|
2537
2602
|
* explicitly asked to fan out and would otherwise return silently empty. */
|
|
2538
2603
|
workspaceDiagnostic() {
|
|
2539
|
-
if (!this.workspaceFile || !(0,
|
|
2604
|
+
if (!this.workspaceFile || !(0, import_node_fs3.existsSync)(this.workspaceFile)) {
|
|
2540
2605
|
return "no .mindgraph/workspace.json was found between the working directory and the filesystem root";
|
|
2541
2606
|
}
|
|
2542
2607
|
return `${this.workspaceFile} is malformed or declares no repositories`;
|
|
@@ -2656,7 +2721,7 @@ var CodegraphAdapter = class {
|
|
|
2656
2721
|
return this.resolveRepository(ref.repo || defaultRepo, fallbackSpaceUid);
|
|
2657
2722
|
}
|
|
2658
2723
|
if (ref.path) {
|
|
2659
|
-
const requested = (0,
|
|
2724
|
+
const requested = (0, import_node_path3.isAbsolute)(ref.path) ? (0, import_node_path3.resolve)(ref.path) : (0, import_node_path3.resolve)(this.cwd, ref.path);
|
|
2660
2725
|
const absolute = await (0, import_promises.realpath)(requested).catch(() => requested);
|
|
2661
2726
|
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
2662
2727
|
const defining = [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, absolute));
|
|
@@ -2733,19 +2798,19 @@ var CodegraphAdapter = class {
|
|
|
2733
2798
|
}
|
|
2734
2799
|
configuredRepositories() {
|
|
2735
2800
|
const configured = [];
|
|
2736
|
-
if (this.workspaceFile && (0,
|
|
2801
|
+
if (this.workspaceFile && (0, import_node_fs3.existsSync)(this.workspaceFile)) {
|
|
2737
2802
|
try {
|
|
2738
2803
|
const parsed = JSON.parse(
|
|
2739
|
-
(0,
|
|
2804
|
+
(0, import_node_fs3.readFileSync)(this.workspaceFile, "utf8")
|
|
2740
2805
|
);
|
|
2741
2806
|
if (parsed.v === 1 && Array.isArray(parsed.repositories)) {
|
|
2742
|
-
const configDirectory = (0,
|
|
2743
|
-
const base = (0,
|
|
2807
|
+
const configDirectory = (0, import_node_path3.dirname)(this.workspaceFile);
|
|
2808
|
+
const base = (0, import_node_path3.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path3.dirname)(configDirectory) : configDirectory;
|
|
2744
2809
|
for (const repository of parsed.repositories) {
|
|
2745
2810
|
if (!repository || typeof repository.root !== "string") continue;
|
|
2746
2811
|
configured.push({
|
|
2747
2812
|
repoId: typeof repository.repo_id === "string" ? repository.repo_id : void 0,
|
|
2748
|
-
repoRoot: (0,
|
|
2813
|
+
repoRoot: (0, import_node_path3.resolve)(base, repository.root),
|
|
2749
2814
|
spaceUid: typeof repository.space_uid === "string" ? repository.space_uid : void 0
|
|
2750
2815
|
});
|
|
2751
2816
|
}
|
|
@@ -2760,7 +2825,7 @@ var CodegraphAdapter = class {
|
|
|
2760
2825
|
for (const [repoId, config] of Object.entries(parsed)) {
|
|
2761
2826
|
configured.push({
|
|
2762
2827
|
repoId,
|
|
2763
|
-
repoRoot: (0,
|
|
2828
|
+
repoRoot: (0, import_node_path3.resolve)(
|
|
2764
2829
|
this.cwd,
|
|
2765
2830
|
typeof config === "string" ? config : config.root
|
|
2766
2831
|
),
|
|
@@ -2788,9 +2853,9 @@ var CodegraphAdapter = class {
|
|
|
2788
2853
|
const hits = await this.runJson(args);
|
|
2789
2854
|
let candidates = hits.map(asNode).map(toCandidate).filter((item) => item !== null);
|
|
2790
2855
|
if (ref.path) {
|
|
2791
|
-
const absolutePath = (0,
|
|
2856
|
+
const absolutePath = (0, import_node_path3.isAbsolute)(ref.path) ? await (0, import_promises.realpath)(ref.path).catch(() => ref.path) : void 0;
|
|
2792
2857
|
const wanted = normalizePath(
|
|
2793
|
-
absolutePath ? (0,
|
|
2858
|
+
absolutePath ? (0, import_node_path3.relative)(repository.repoRoot, absolutePath) : ref.path
|
|
2794
2859
|
);
|
|
2795
2860
|
candidates = candidates.filter((candidate) => candidate.path === wanted);
|
|
2796
2861
|
}
|
|
@@ -2891,7 +2956,7 @@ var CodegraphAdapter = class {
|
|
|
2891
2956
|
const candidates = [
|
|
2892
2957
|
this.env.MINDGRAPH_CODEGRAPH_BIN,
|
|
2893
2958
|
"codegraph",
|
|
2894
|
-
(0,
|
|
2959
|
+
(0, import_node_path3.resolve)((0, import_node_os3.homedir)(), ".local", "bin", "codegraph")
|
|
2895
2960
|
].filter((candidate, index, all) => {
|
|
2896
2961
|
return Boolean(candidate) && all.indexOf(candidate) === index;
|
|
2897
2962
|
});
|
|
@@ -3492,9 +3557,9 @@ function primaryUids(value) {
|
|
|
3492
3557
|
}
|
|
3493
3558
|
|
|
3494
3559
|
// src/sync-tool.ts
|
|
3495
|
-
var
|
|
3496
|
-
var
|
|
3497
|
-
var
|
|
3560
|
+
var import_node_crypto5 = __toESM(require("crypto"));
|
|
3561
|
+
var import_node_fs4 = __toESM(require("fs"));
|
|
3562
|
+
var import_node_path4 = __toESM(require("path"));
|
|
3498
3563
|
var ok3 = (data) => ({
|
|
3499
3564
|
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
3500
3565
|
});
|
|
@@ -3571,12 +3636,12 @@ var SYNC_TOOL = {
|
|
|
3571
3636
|
}
|
|
3572
3637
|
};
|
|
3573
3638
|
function safePath(root, candidate) {
|
|
3574
|
-
const absolute =
|
|
3575
|
-
const relative2 =
|
|
3576
|
-
if (relative2.startsWith("..") ||
|
|
3639
|
+
const absolute = import_node_path4.default.resolve(root, candidate);
|
|
3640
|
+
const relative2 = import_node_path4.default.relative(root, absolute);
|
|
3641
|
+
if (relative2.startsWith("..") || import_node_path4.default.isAbsolute(relative2)) {
|
|
3577
3642
|
throw new Error(`path escapes repository root: ${candidate}`);
|
|
3578
3643
|
}
|
|
3579
|
-
if (
|
|
3644
|
+
if (import_node_fs4.default.existsSync(absolute)) {
|
|
3580
3645
|
if (!pathContains(realPathOf(root), realPathOf(absolute))) {
|
|
3581
3646
|
throw new Error(`path escapes repository root via symlink: ${candidate}`);
|
|
3582
3647
|
}
|
|
@@ -3585,26 +3650,26 @@ function safePath(root, candidate) {
|
|
|
3585
3650
|
}
|
|
3586
3651
|
function realPathOf(target) {
|
|
3587
3652
|
try {
|
|
3588
|
-
return
|
|
3653
|
+
return import_node_fs4.default.realpathSync(target);
|
|
3589
3654
|
} catch {
|
|
3590
|
-
return
|
|
3655
|
+
return import_node_path4.default.resolve(target);
|
|
3591
3656
|
}
|
|
3592
3657
|
}
|
|
3593
3658
|
function markdownFiles(target, realRoot, visited = /* @__PURE__ */ new Set()) {
|
|
3594
|
-
if (!
|
|
3659
|
+
if (!import_node_fs4.default.existsSync(target)) return [];
|
|
3595
3660
|
const real = realPathOf(target);
|
|
3596
3661
|
if (!pathContains(realRoot, real)) return [];
|
|
3597
|
-
const stat =
|
|
3662
|
+
const stat = import_node_fs4.default.statSync(target);
|
|
3598
3663
|
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
3599
3664
|
if (!stat.isDirectory() || visited.has(real)) return [];
|
|
3600
3665
|
visited.add(real);
|
|
3601
|
-
return
|
|
3666
|
+
return import_node_fs4.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path4.default.join(target, entry.name), realRoot, visited));
|
|
3602
3667
|
}
|
|
3603
3668
|
function fileDescriptor(root, absolute) {
|
|
3604
|
-
const content =
|
|
3669
|
+
const content = import_node_fs4.default.readFileSync(absolute, "utf8");
|
|
3605
3670
|
return {
|
|
3606
|
-
logical_path:
|
|
3607
|
-
content_hash:
|
|
3671
|
+
logical_path: import_node_path4.default.relative(root, absolute).split(import_node_path4.default.sep).join("/"),
|
|
3672
|
+
content_hash: import_node_crypto5.default.createHash("sha256").update(content).digest("hex"),
|
|
3608
3673
|
bytes: Buffer.byteLength(content),
|
|
3609
3674
|
content
|
|
3610
3675
|
};
|
|
@@ -3673,7 +3738,7 @@ async function handleSyncTool(client2, args) {
|
|
|
3673
3738
|
}
|
|
3674
3739
|
if (action === "status" && request.logical_path && request.file_present !== false) {
|
|
3675
3740
|
const absolute = safePath(root, String(request.logical_path));
|
|
3676
|
-
request.file_present =
|
|
3741
|
+
request.file_present = import_node_fs4.default.existsSync(absolute);
|
|
3677
3742
|
if (request.file_present && !request.content_hash) {
|
|
3678
3743
|
request.content_hash = fileDescriptor(root, absolute).content_hash;
|
|
3679
3744
|
}
|
|
@@ -3746,10 +3811,10 @@ var RESOLUTION_PROPERTIES = {
|
|
|
3746
3811
|
description: "Alias for confirm_new on a near-duplicate response"
|
|
3747
3812
|
}
|
|
3748
3813
|
};
|
|
3749
|
-
function
|
|
3750
|
-
if (Array.isArray(value)) return `[${value.map(
|
|
3814
|
+
function stableJson3(value) {
|
|
3815
|
+
if (Array.isArray(value)) return `[${value.map(stableJson3).join(",")}]`;
|
|
3751
3816
|
if (value && typeof value === "object") {
|
|
3752
|
-
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${
|
|
3817
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson3(item)}`).join(",")}}`;
|
|
3753
3818
|
}
|
|
3754
3819
|
return JSON.stringify(value);
|
|
3755
3820
|
}
|
|
@@ -3763,8 +3828,8 @@ function defaultWriteIdempotencyKey(tool, args) {
|
|
|
3763
3828
|
([key]) => key !== "invocation_context" && key !== "idempotency_key"
|
|
3764
3829
|
)
|
|
3765
3830
|
);
|
|
3766
|
-
return (0,
|
|
3767
|
-
|
|
3831
|
+
return (0, import_node_crypto6.createHash)("sha256").update(
|
|
3832
|
+
stableJson3({
|
|
3768
3833
|
harness_session_id: sessionId,
|
|
3769
3834
|
harness_turn_id: context?.harnessTurnId ?? null,
|
|
3770
3835
|
tool,
|
|
@@ -4476,6 +4541,15 @@ var TOOLS = [
|
|
|
4476
4541
|
type: "string",
|
|
4477
4542
|
description: "ISO-8601 date (e.g. '2021-06') for context: nodes with validity windows are annotated as of this date instead of today"
|
|
4478
4543
|
},
|
|
4544
|
+
as_of: {
|
|
4545
|
+
type: "string",
|
|
4546
|
+
description: "ISO-8601 date for context: answer with what had been STATED by this date (a node is admitted when one of its sources was stated on or before it; fact dates never exclude it). Use for 'what did we know / what was the value as of <date>' questions."
|
|
4547
|
+
},
|
|
4548
|
+
retrieval_mode: {
|
|
4549
|
+
type: "string",
|
|
4550
|
+
enum: ["normal", "exhaustive"],
|
|
4551
|
+
description: "context: 'exhaustive' widens retrieval for list/count questions ('how many different \u2026 across', 'what two \u2026'); omitted lets the server's cue decide."
|
|
4552
|
+
},
|
|
4479
4553
|
created_after: {
|
|
4480
4554
|
type: "number",
|
|
4481
4555
|
description: "Unix seconds \u2014 'recent' action: only nodes ingested after this time"
|
|
@@ -5704,6 +5778,8 @@ async function handleRetrieve(client2, args, profile) {
|
|
|
5704
5778
|
salience_min,
|
|
5705
5779
|
article_limit,
|
|
5706
5780
|
valid_at,
|
|
5781
|
+
as_of,
|
|
5782
|
+
retrieval_mode,
|
|
5707
5783
|
created_after,
|
|
5708
5784
|
created_before,
|
|
5709
5785
|
explain,
|
|
@@ -5732,9 +5808,15 @@ async function handleRetrieve(client2, args, profile) {
|
|
|
5732
5808
|
// the advertised knob must not get a silent no-op).
|
|
5733
5809
|
...article_limit !== void 0 ? { article_limit } : include_documents === false || profile === "coding" && !include_documents ? { article_limit: 0 } : {},
|
|
5734
5810
|
valid_at,
|
|
5811
|
+
as_of,
|
|
5812
|
+
retrieval_mode,
|
|
5735
5813
|
include_graph,
|
|
5736
5814
|
graph_expansion_limit: graph_expansion_limit ?? 3,
|
|
5737
|
-
graph_max_depth: graph_max_depth ?? 2
|
|
5815
|
+
graph_max_depth: graph_max_depth ?? 2,
|
|
5816
|
+
// Evidence plan R2: the server's canonical rendering under a token
|
|
5817
|
+
// budget derived from the character budget; the local renderer is
|
|
5818
|
+
// the fallback for servers that predate it.
|
|
5819
|
+
...format === "json" ? {} : { format: "text", max_tokens: Math.max(256, Math.floor((max_output_chars ?? 24e3) / 4)) }
|
|
5738
5820
|
}),
|
|
5739
5821
|
(raw) => renderContext(raw, max_output_chars)
|
|
5740
5822
|
);
|
|
@@ -6208,7 +6290,12 @@ function buildMindGraphServer(wiring) {
|
|
|
6208
6290
|
});
|
|
6209
6291
|
server2.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
6210
6292
|
const { name, arguments: args } = request.params;
|
|
6211
|
-
const rawArgs = {
|
|
6293
|
+
const rawArgs = {
|
|
6294
|
+
...args || {},
|
|
6295
|
+
// H1: session, lease and invocation fields the local hook recorded for
|
|
6296
|
+
// this exact call (stdio only; hosted mode has no local hook).
|
|
6297
|
+
...hosted ? {} : wiring.resolveHookFields?.(name, args || {})
|
|
6298
|
+
};
|
|
6212
6299
|
const toolArgs = { ...rawArgs };
|
|
6213
6300
|
let governanceConfig = wiring.governance;
|
|
6214
6301
|
if (hosted) {
|
|
@@ -6273,13 +6360,19 @@ function buildMindGraphServer(wiring) {
|
|
|
6273
6360
|
}
|
|
6274
6361
|
|
|
6275
6362
|
// src/index.ts
|
|
6276
|
-
var
|
|
6363
|
+
var import_node_path6 = __toESM(require("path"));
|
|
6277
6364
|
|
|
6278
6365
|
// src/hook-installer.ts
|
|
6279
|
-
var
|
|
6280
|
-
var
|
|
6281
|
-
var
|
|
6282
|
-
var
|
|
6366
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
6367
|
+
var import_node_os4 = __toESM(require("os"));
|
|
6368
|
+
var import_node_path5 = __toESM(require("path"));
|
|
6369
|
+
var import_node_crypto7 = __toESM(require("crypto"));
|
|
6370
|
+
function storedFeatures(homeDir) {
|
|
6371
|
+
const { promptRecall, capture } = loadHookEnv(
|
|
6372
|
+
import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), ".mindgraph")
|
|
6373
|
+
);
|
|
6374
|
+
return { promptRecall, capture };
|
|
6375
|
+
}
|
|
6283
6376
|
var OWNER_MARKER = "--owner mindgraph";
|
|
6284
6377
|
var RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
6285
6378
|
function command(harness) {
|
|
@@ -6290,24 +6383,24 @@ function windowsCommand(harness) {
|
|
|
6290
6383
|
}
|
|
6291
6384
|
var RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
|
|
6292
6385
|
function installHookRunner(sourceFile, homeDir, version) {
|
|
6293
|
-
const target =
|
|
6294
|
-
|
|
6295
|
-
const temporary = `${target}.${process.pid}.${
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
const versionPath =
|
|
6386
|
+
const target = import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
|
|
6387
|
+
import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(target), { recursive: true });
|
|
6388
|
+
const temporary = `${target}.${process.pid}.${import_node_crypto7.default.randomUUID()}.tmp`;
|
|
6389
|
+
import_node_fs5.default.copyFileSync(sourceFile, temporary);
|
|
6390
|
+
import_node_fs5.default.renameSync(temporary, target);
|
|
6391
|
+
const versionPath = import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE);
|
|
6299
6392
|
if (version) {
|
|
6300
|
-
|
|
6393
|
+
import_node_fs5.default.writeFileSync(versionPath, `${version}
|
|
6301
6394
|
`);
|
|
6302
6395
|
} else {
|
|
6303
|
-
|
|
6396
|
+
import_node_fs5.default.rmSync(versionPath, { force: true });
|
|
6304
6397
|
}
|
|
6305
6398
|
return target;
|
|
6306
6399
|
}
|
|
6307
6400
|
function installedHookRunnerVersion(homeDir) {
|
|
6308
6401
|
try {
|
|
6309
|
-
const raw =
|
|
6310
|
-
|
|
6402
|
+
const raw = import_node_fs5.default.readFileSync(
|
|
6403
|
+
import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE),
|
|
6311
6404
|
"utf8"
|
|
6312
6405
|
).trim();
|
|
6313
6406
|
return raw || void 0;
|
|
@@ -6332,11 +6425,11 @@ function refreshHookRunner(serverVersion, sourceBundle, homeDir) {
|
|
|
6332
6425
|
if (process.env.MINDGRAPH_HOOK_AUTOUPDATE === "off") {
|
|
6333
6426
|
return { refreshed: false, reason: "disabled" };
|
|
6334
6427
|
}
|
|
6335
|
-
const home = homeDir ||
|
|
6336
|
-
if (!
|
|
6428
|
+
const home = homeDir || import_node_os4.default.homedir();
|
|
6429
|
+
if (!import_node_fs5.default.existsSync(import_node_path5.default.join(home, RUNNER_RELATIVE))) {
|
|
6337
6430
|
return { refreshed: false, reason: "no_runner" };
|
|
6338
6431
|
}
|
|
6339
|
-
if (!
|
|
6432
|
+
if (!import_node_fs5.default.existsSync(sourceBundle)) {
|
|
6340
6433
|
return { refreshed: false, reason: "no_source" };
|
|
6341
6434
|
}
|
|
6342
6435
|
const server2 = parseSemver(serverVersion);
|
|
@@ -6367,9 +6460,16 @@ function refreshOwnedClaudeHooks(projectDir = process.cwd(), homeDir) {
|
|
|
6367
6460
|
for (const scope of scopes) {
|
|
6368
6461
|
try {
|
|
6369
6462
|
const file = settingsPath("claude-code", scope, projectDir, void 0, homeDir);
|
|
6370
|
-
if (!
|
|
6371
|
-
if (!
|
|
6372
|
-
installHooks(
|
|
6463
|
+
if (!import_node_fs5.default.existsSync(file)) continue;
|
|
6464
|
+
if (!import_node_fs5.default.readFileSync(file, "utf8").includes(OWNER_MARKER)) continue;
|
|
6465
|
+
installHooks(
|
|
6466
|
+
"claude-code",
|
|
6467
|
+
scope,
|
|
6468
|
+
projectDir,
|
|
6469
|
+
void 0,
|
|
6470
|
+
homeDir,
|
|
6471
|
+
storedFeatures(homeDir)
|
|
6472
|
+
);
|
|
6373
6473
|
refreshed.push(file);
|
|
6374
6474
|
} catch {
|
|
6375
6475
|
}
|
|
@@ -6381,15 +6481,15 @@ function versionSkewNote(serverVersion, homeDir) {
|
|
|
6381
6481
|
if (!runnerVersion || runnerVersion === serverVersion) return void 0;
|
|
6382
6482
|
return `NOTE: the installed MindGraph hook runner is v${runnerVersion} but this MCP server is v${serverVersion} \u2014 hook behavior and tool contracts may be out of sync. Tell the user to run: npx -y mindgraph-mcp@latest install-hooks (refreshes the pinned runner).`;
|
|
6383
6483
|
}
|
|
6384
|
-
function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME ||
|
|
6484
|
+
function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME || import_node_path5.default.join(import_node_os4.default.homedir(), ".codex"), homeDir) {
|
|
6385
6485
|
if (harness === "codex") {
|
|
6386
|
-
return scope === "user" ?
|
|
6486
|
+
return scope === "user" ? import_node_path5.default.join(codexHomeDir, "hooks.json") : import_node_path5.default.join(projectDir, ".codex", "hooks.json");
|
|
6387
6487
|
}
|
|
6388
|
-
return scope === "user" ?
|
|
6488
|
+
return scope === "user" ? import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), ".claude", "settings.json") : import_node_path5.default.join(projectDir, ".claude", "settings.json");
|
|
6389
6489
|
}
|
|
6390
6490
|
function readSettings(file) {
|
|
6391
|
-
if (!
|
|
6392
|
-
return JSON.parse(
|
|
6491
|
+
if (!import_node_fs5.default.existsSync(file)) return {};
|
|
6492
|
+
return JSON.parse(import_node_fs5.default.readFileSync(file, "utf8"));
|
|
6393
6493
|
}
|
|
6394
6494
|
function ownedHook(harness, timeout, additionalContextLimit) {
|
|
6395
6495
|
return {
|
|
@@ -6401,7 +6501,8 @@ function ownedHook(harness, timeout, additionalContextLimit) {
|
|
|
6401
6501
|
...additionalContextLimit === void 0 ? {} : { additionalContextLimit }
|
|
6402
6502
|
};
|
|
6403
6503
|
}
|
|
6404
|
-
|
|
6504
|
+
var CLAUDE_POST_TOOL_MATCHER = "mcp__mindgraph__.*|Edit|MultiEdit|Write|NotebookEdit|Bash|Task|Agent";
|
|
6505
|
+
function desiredEntries(harness, features = {}) {
|
|
6405
6506
|
const common = {
|
|
6406
6507
|
SessionStart: [
|
|
6407
6508
|
{
|
|
@@ -6433,7 +6534,17 @@ function desiredEntries(harness) {
|
|
|
6433
6534
|
],
|
|
6434
6535
|
// PostToolUse awaits the lease renewal round-trip, which fires exactly
|
|
6435
6536
|
// when the tenant is cold (post-idle reclaim) — 5s killed it mid-claim.
|
|
6436
|
-
|
|
6537
|
+
// Claude Code: only the tools the hook acts on (MindGraph calls, edits,
|
|
6538
|
+
// and the shell/agent calls that keep a lease alive). Read, Grep and Glob
|
|
6539
|
+
// are most of a session's tool calls and the hook did nothing useful for
|
|
6540
|
+
// them but start a process (roadmap H8). Codex names its tools
|
|
6541
|
+
// differently, so it keeps the catch-all.
|
|
6542
|
+
PostToolUse: [
|
|
6543
|
+
{
|
|
6544
|
+
matcher: harness === "claude-code" ? CLAUDE_POST_TOOL_MATCHER : ".*",
|
|
6545
|
+
hooks: [ownedHook(harness, 10)]
|
|
6546
|
+
}
|
|
6547
|
+
],
|
|
6437
6548
|
Stop: [{ hooks: [ownedHook(harness, 5)] }],
|
|
6438
6549
|
// SessionEnd makes up to two sequential cloud calls (abandon_iteration +
|
|
6439
6550
|
// session close) — the SAME cold-tenant latency SessionStart needs 30s
|
|
@@ -6444,11 +6555,17 @@ function desiredEntries(harness) {
|
|
|
6444
6555
|
{ hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
|
|
6445
6556
|
]
|
|
6446
6557
|
};
|
|
6447
|
-
|
|
6558
|
+
if (harness !== "claude-code") return common;
|
|
6559
|
+
return {
|
|
6448
6560
|
...common,
|
|
6449
6561
|
TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
|
|
6450
|
-
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }]
|
|
6451
|
-
|
|
6562
|
+
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }],
|
|
6563
|
+
// Bounded recall per prompt: the core gives up at 4s and fails open, so
|
|
6564
|
+
// a 5s budget never holds the prompt longer than that.
|
|
6565
|
+
...features.promptRecall !== false ? { UserPromptSubmit: [{ hooks: [ownedHook(harness, 5)] }] } : {},
|
|
6566
|
+
// Opt-in transcript capture before compaction (cold-tenant ingest).
|
|
6567
|
+
...features.capture === true ? { PreCompact: [{ hooks: [ownedHook(harness, 30)] }] } : {}
|
|
6568
|
+
};
|
|
6452
6569
|
}
|
|
6453
6570
|
function hookCommand(value) {
|
|
6454
6571
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -6461,21 +6578,30 @@ function entryHasOwnedHook(value) {
|
|
|
6461
6578
|
return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
|
|
6462
6579
|
}
|
|
6463
6580
|
function writeSettings(file, settings) {
|
|
6464
|
-
|
|
6465
|
-
const temporary = `${file}.${process.pid}.${
|
|
6466
|
-
|
|
6581
|
+
import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true });
|
|
6582
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto7.default.randomUUID()}.tmp`;
|
|
6583
|
+
import_node_fs5.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
6467
6584
|
`, {
|
|
6468
6585
|
mode: 384
|
|
6469
6586
|
});
|
|
6470
|
-
|
|
6587
|
+
import_node_fs5.default.renameSync(temporary, file);
|
|
6471
6588
|
}
|
|
6472
|
-
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir) {
|
|
6589
|
+
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir, features = {}) {
|
|
6473
6590
|
const file = settingsPath(harness, scope, projectDir, codexHomeDir, homeDir);
|
|
6474
6591
|
const settings = readSettings(file);
|
|
6475
6592
|
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
6476
6593
|
let added = 0;
|
|
6477
6594
|
let updated = 0;
|
|
6478
|
-
|
|
6595
|
+
const desired = desiredEntries(harness, features);
|
|
6596
|
+
for (const optional of ["UserPromptSubmit", "PreCompact"]) {
|
|
6597
|
+
if (optional in desired || !Array.isArray(hooks[optional])) continue;
|
|
6598
|
+
const kept = hooks[optional].filter(
|
|
6599
|
+
(entry) => !entryHasOwnedHook(entry)
|
|
6600
|
+
);
|
|
6601
|
+
if (kept.length > 0) hooks[optional] = kept;
|
|
6602
|
+
else delete hooks[optional];
|
|
6603
|
+
}
|
|
6604
|
+
for (const [event, wanted] of Object.entries(desired)) {
|
|
6479
6605
|
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
6480
6606
|
const kept = current.filter((entry) => !entryHasOwnedHook(entry));
|
|
6481
6607
|
const hadOwned = kept.length !== current.length;
|
|
@@ -6583,7 +6709,7 @@ if (PROFILE === "coding") {
|
|
|
6583
6709
|
- Capture corrections, decisions with rationale, fixes that survived meaningful failed attempts, durable invariants/preferences, and blockers with evidence.
|
|
6584
6710
|
- If a write returns \`near_duplicate\`, choose \`reuse_uid\` or \`supersedes_uid\`; use \`confirm_new\` only for genuinely distinct facts.
|
|
6585
6711
|
- Do not capture routine mechanics, transient command output, facts cheaply derivable from code, unsupported speculation as high-confidence lessons, or completion without evidence.
|
|
6586
|
-
- Adapter context is owned by ${HARNESS}; omit invocation_context because
|
|
6712
|
+
- Adapter context is owned by ${HARNESS}; omit invocation_context because the adapter supplies it.`;
|
|
6587
6713
|
} else {
|
|
6588
6714
|
INSTRUCTIONS = INSTRUCTIONS.split("\n").filter(
|
|
6589
6715
|
(line) => !line.includes("mindgraph_code") && !line.includes("`code`") && !line.includes("Typed code anchors")
|
|
@@ -6591,7 +6717,7 @@ if (PROFILE === "coding") {
|
|
|
6591
6717
|
}
|
|
6592
6718
|
var RUNNER_REFRESH = (() => {
|
|
6593
6719
|
try {
|
|
6594
|
-
return refreshHookRunner("0.
|
|
6720
|
+
return refreshHookRunner("0.24.0", import_node_path6.default.join(__dirname, "cli.js"));
|
|
6595
6721
|
} catch {
|
|
6596
6722
|
return void 0;
|
|
6597
6723
|
}
|
|
@@ -6605,12 +6731,12 @@ if (RUNNER_REFRESH?.refreshed) {
|
|
|
6605
6731
|
}
|
|
6606
6732
|
})();
|
|
6607
6733
|
console.error(
|
|
6608
|
-
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.
|
|
6734
|
+
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.24.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
|
|
6609
6735
|
);
|
|
6610
6736
|
}
|
|
6611
6737
|
var HOOK_SKEW_NOTE = (() => {
|
|
6612
6738
|
try {
|
|
6613
|
-
return versionSkewNote("0.
|
|
6739
|
+
return versionSkewNote("0.24.0");
|
|
6614
6740
|
} catch {
|
|
6615
6741
|
return void 0;
|
|
6616
6742
|
}
|
|
@@ -6631,7 +6757,7 @@ var GOVERNANCE_CONFIG = {
|
|
|
6631
6757
|
}
|
|
6632
6758
|
};
|
|
6633
6759
|
var MEMORY_RUNTIME_CONFIG = {
|
|
6634
|
-
serverVersion: "0.
|
|
6760
|
+
serverVersion: "0.24.0",
|
|
6635
6761
|
baseUrl: BASE_URL,
|
|
6636
6762
|
profile: PROFILE,
|
|
6637
6763
|
harness: HARNESS,
|
|
@@ -6640,18 +6766,19 @@ var MEMORY_RUNTIME_CONFIG = {
|
|
|
6640
6766
|
};
|
|
6641
6767
|
var server = buildMindGraphServer({
|
|
6642
6768
|
client,
|
|
6643
|
-
serverVersion: "0.
|
|
6769
|
+
serverVersion: "0.24.0",
|
|
6644
6770
|
instructions: INSTRUCTIONS,
|
|
6645
6771
|
profile: PROFILE,
|
|
6646
6772
|
agentId: AGENT_ID,
|
|
6647
6773
|
governance: GOVERNANCE_CONFIG,
|
|
6648
|
-
memory: MEMORY_RUNTIME_CONFIG
|
|
6774
|
+
memory: MEMORY_RUNTIME_CONFIG,
|
|
6775
|
+
resolveHookFields: (name, args) => resolvePendingCall(name, args)
|
|
6649
6776
|
});
|
|
6650
6777
|
async function main() {
|
|
6651
6778
|
const transport = new import_stdio.StdioServerTransport();
|
|
6652
6779
|
await server.connect(transport);
|
|
6653
6780
|
console.error(
|
|
6654
|
-
`MindGraph MCP server v${"0.
|
|
6781
|
+
`MindGraph MCP server v${"0.24.0"} running on stdio (${BASE_URL})`
|
|
6655
6782
|
);
|
|
6656
6783
|
}
|
|
6657
6784
|
main().catch((error2) => {
|