mindgraph-mcp 0.16.0 → 0.18.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 +57 -4
- package/dist/cli.js +879 -335
- package/dist/index.js +718 -119
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -65,8 +65,8 @@ var init_dist = __esm({
|
|
|
65
65
|
this.retryBackoffMs = config.retryBackoffMs ?? 1e3;
|
|
66
66
|
}
|
|
67
67
|
// ---- HTTP helpers ----
|
|
68
|
-
async request(method,
|
|
69
|
-
const url = `${this.baseUrl}${
|
|
68
|
+
async request(method, path7, body) {
|
|
69
|
+
const url = `${this.baseUrl}${path7}`;
|
|
70
70
|
const requestId = createRequestId();
|
|
71
71
|
const init = {
|
|
72
72
|
method,
|
|
@@ -91,7 +91,7 @@ var init_dist = __esm({
|
|
|
91
91
|
parsed = text2;
|
|
92
92
|
}
|
|
93
93
|
const err5 = new MindGraphError(
|
|
94
|
-
`${method} ${
|
|
94
|
+
`${method} ${path7} failed: ${res.status}`,
|
|
95
95
|
res.status,
|
|
96
96
|
parsed
|
|
97
97
|
);
|
|
@@ -109,17 +109,17 @@ var init_dist = __esm({
|
|
|
109
109
|
}
|
|
110
110
|
throw lastError;
|
|
111
111
|
}
|
|
112
|
-
get(
|
|
113
|
-
return this.request("GET",
|
|
112
|
+
get(path7) {
|
|
113
|
+
return this.request("GET", path7);
|
|
114
114
|
}
|
|
115
|
-
post(
|
|
116
|
-
return this.request("POST",
|
|
115
|
+
post(path7, body) {
|
|
116
|
+
return this.request("POST", path7, body);
|
|
117
117
|
}
|
|
118
|
-
patch(
|
|
119
|
-
return this.request("PATCH",
|
|
118
|
+
patch(path7, body) {
|
|
119
|
+
return this.request("PATCH", path7, body);
|
|
120
120
|
}
|
|
121
|
-
del(
|
|
122
|
-
return this.request("DELETE",
|
|
121
|
+
del(path7) {
|
|
122
|
+
return this.request("DELETE", path7);
|
|
123
123
|
}
|
|
124
124
|
// ---- Health ----
|
|
125
125
|
async health() {
|
|
@@ -971,6 +971,14 @@ var init_hook_env = __esm({
|
|
|
971
971
|
});
|
|
972
972
|
|
|
973
973
|
// src/codegraph.ts
|
|
974
|
+
function invocationCwd(args) {
|
|
975
|
+
const context = args.invocation_context;
|
|
976
|
+
if (context && typeof context === "object" && !Array.isArray(context)) {
|
|
977
|
+
const cwd = context.cwd;
|
|
978
|
+
if (typeof cwd === "string" && cwd) return cwd;
|
|
979
|
+
}
|
|
980
|
+
return process.cwd();
|
|
981
|
+
}
|
|
974
982
|
function unavailabilityCaveats(reason, message) {
|
|
975
983
|
const caveats = [message || "codegraph status failed"];
|
|
976
984
|
if (reason === "absent") {
|
|
@@ -980,14 +988,14 @@ function unavailabilityCaveats(reason, message) {
|
|
|
980
988
|
}
|
|
981
989
|
return caveats;
|
|
982
990
|
}
|
|
983
|
-
function normalizePath(
|
|
984
|
-
return
|
|
991
|
+
function normalizePath(path7) {
|
|
992
|
+
return path7.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
985
993
|
}
|
|
986
994
|
function normalizeSignature(signature) {
|
|
987
995
|
return signature.replace(/\s+/g, " ").trim();
|
|
988
996
|
}
|
|
989
|
-
function languageFromPath(
|
|
990
|
-
const extension = (0, import_node_path2.extname)(
|
|
997
|
+
function languageFromPath(path7) {
|
|
998
|
+
const extension = (0, import_node_path2.extname)(path7).toLowerCase();
|
|
991
999
|
return {
|
|
992
1000
|
".c": "c",
|
|
993
1001
|
".cc": "cpp",
|
|
@@ -1150,7 +1158,7 @@ function codeRefIdentityKey(ref) {
|
|
|
1150
1158
|
}
|
|
1151
1159
|
};
|
|
1152
1160
|
}
|
|
1153
|
-
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2, import_node_crypto2, DEFAULT_TIMEOUT_MS, DEFAULT_STATUS_TTL_MS, MAX_OUTPUT_BYTES, CodegraphAdapter;
|
|
1161
|
+
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2, import_node_crypto2, UnknownRepositoryError, DEFAULT_TIMEOUT_MS, DEFAULT_STATUS_TTL_MS, MAX_OUTPUT_BYTES, CodegraphAdapter;
|
|
1154
1162
|
var init_codegraph = __esm({
|
|
1155
1163
|
"src/codegraph.ts"() {
|
|
1156
1164
|
"use strict";
|
|
@@ -1160,6 +1168,16 @@ var init_codegraph = __esm({
|
|
|
1160
1168
|
import_node_os2 = require("os");
|
|
1161
1169
|
import_node_path2 = require("path");
|
|
1162
1170
|
import_node_crypto2 = require("crypto");
|
|
1171
|
+
UnknownRepositoryError = class extends Error {
|
|
1172
|
+
constructor(repo) {
|
|
1173
|
+
super(
|
|
1174
|
+
`unknown repository "${repo}": not declared in .mindgraph/workspace.json, not the current repository, and no such path exists`
|
|
1175
|
+
);
|
|
1176
|
+
this.repo = repo;
|
|
1177
|
+
this.name = "UnknownRepositoryError";
|
|
1178
|
+
}
|
|
1179
|
+
repo;
|
|
1180
|
+
};
|
|
1163
1181
|
DEFAULT_TIMEOUT_MS = 5e3;
|
|
1164
1182
|
DEFAULT_STATUS_TTL_MS = 6e4;
|
|
1165
1183
|
MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
@@ -1195,7 +1213,26 @@ var init_codegraph = __esm({
|
|
|
1195
1213
|
selected.spaceUid
|
|
1196
1214
|
) : selected;
|
|
1197
1215
|
}
|
|
1198
|
-
|
|
1216
|
+
if (repo) {
|
|
1217
|
+
const requestedPath = await (0, import_promises.realpath)((0, import_node_path2.resolve)(this.cwd, repo)).catch(
|
|
1218
|
+
() => (0, import_node_path2.resolve)(this.cwd, repo)
|
|
1219
|
+
);
|
|
1220
|
+
if (!(0, import_node_fs2.existsSync)(requestedPath)) {
|
|
1221
|
+
const current = await this.currentRepository(fallbackSpaceUid);
|
|
1222
|
+
if (current.repoId === repo) return current;
|
|
1223
|
+
throw new UnknownRepositoryError(repo);
|
|
1224
|
+
}
|
|
1225
|
+
const repoRoot = await this.gitRoot(requestedPath) ?? requestedPath;
|
|
1226
|
+
return this.resolveRepositoryIdentity(
|
|
1227
|
+
repoRoot,
|
|
1228
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
1229
|
+
this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
return this.currentRepository(fallbackSpaceUid);
|
|
1233
|
+
}
|
|
1234
|
+
async currentRepository(fallbackSpaceUid) {
|
|
1235
|
+
const requestedRoot = this.env.MINDGRAPH_REPO_ROOT || this.cwd;
|
|
1199
1236
|
const requestedPath = await (0, import_promises.realpath)((0, import_node_path2.resolve)(this.cwd, requestedRoot)).catch(
|
|
1200
1237
|
() => (0, import_node_path2.resolve)(this.cwd, requestedRoot)
|
|
1201
1238
|
);
|
|
@@ -1285,6 +1322,14 @@ var init_codegraph = __esm({
|
|
|
1285
1322
|
).catch(() => "");
|
|
1286
1323
|
return raw.trim() ? (0, import_node_path2.resolve)(start, raw.trim()) : void 0;
|
|
1287
1324
|
}
|
|
1325
|
+
/** Why the workspace map yielded no repositories — for callers that were
|
|
1326
|
+
* explicitly asked to fan out and would otherwise return silently empty. */
|
|
1327
|
+
workspaceDiagnostic() {
|
|
1328
|
+
if (!this.workspaceFile || !(0, import_node_fs2.existsSync)(this.workspaceFile)) {
|
|
1329
|
+
return "no .mindgraph/workspace.json was found between the working directory and the filesystem root";
|
|
1330
|
+
}
|
|
1331
|
+
return `${this.workspaceFile} is malformed or declares no repositories`;
|
|
1332
|
+
}
|
|
1288
1333
|
async availability(repository) {
|
|
1289
1334
|
const cached = this.statusCache.get(repository.repoRoot);
|
|
1290
1335
|
if (cached && cached.expiresAt > this.now()) return cached.availability;
|
|
@@ -1698,13 +1743,13 @@ var init_error_detail = __esm({
|
|
|
1698
1743
|
});
|
|
1699
1744
|
|
|
1700
1745
|
// src/code-tool.ts
|
|
1701
|
-
async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
1746
|
+
async function handleCodeTool(client2, args, adapter = new CodegraphAdapter({ cwd: invocationCwd(args) })) {
|
|
1702
1747
|
const codeClient = client2;
|
|
1703
1748
|
const action = args.action;
|
|
1704
1749
|
const refs = Array.isArray(args.code_refs) ? args.code_refs : [];
|
|
1705
1750
|
const anchorUids = Array.isArray(args.anchor_uids) ? args.anchor_uids : [];
|
|
1706
1751
|
const agentId = typeof args.agent_id === "string" ? args.agent_id : "mcp";
|
|
1707
|
-
const fallbackSpaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid :
|
|
1752
|
+
const fallbackSpaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : void 0;
|
|
1708
1753
|
const options = {
|
|
1709
1754
|
repo: typeof args.repo === "string" ? args.repo : void 0,
|
|
1710
1755
|
fallbackSpaceUid
|
|
@@ -1828,6 +1873,7 @@ async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
|
1828
1873
|
const targetUid = typeof args.task_uid === "string" ? args.task_uid : typeof args.execution_uid === "string" ? args.execution_uid : void 0;
|
|
1829
1874
|
let graphContext;
|
|
1830
1875
|
const graphRefs = [];
|
|
1876
|
+
let unavailableRepositories = [];
|
|
1831
1877
|
if (targetUid) {
|
|
1832
1878
|
graphContext = await codeClient.traverse({
|
|
1833
1879
|
action: "neighborhood",
|
|
@@ -1835,9 +1881,13 @@ async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
|
1835
1881
|
depth: 2,
|
|
1836
1882
|
max_nodes: 50
|
|
1837
1883
|
});
|
|
1838
|
-
|
|
1839
|
-
|
|
1884
|
+
const fromGraph = await codeRefsFromGraphContext(
|
|
1885
|
+
codeClient,
|
|
1886
|
+
adapter,
|
|
1887
|
+
graphContext
|
|
1840
1888
|
);
|
|
1889
|
+
graphRefs.push(...fromGraph.refs);
|
|
1890
|
+
unavailableRepositories = fromGraph.unavailable;
|
|
1841
1891
|
}
|
|
1842
1892
|
if (refs.length > 0 || anchorUids.length > 0) {
|
|
1843
1893
|
const target = await resolveSingleTarget(
|
|
@@ -1856,7 +1906,8 @@ async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
|
1856
1906
|
target_uid: targetUid,
|
|
1857
1907
|
graph_context: graphContext,
|
|
1858
1908
|
affected: [],
|
|
1859
|
-
degradation: "no materialized code anchors were attached"
|
|
1909
|
+
degradation: unavailableRepositories.length > 0 ? `attached code anchors reference repositories unavailable here: ${unavailableRepositories.join(", ")}` : "no materialized code anchors were attached",
|
|
1910
|
+
...unavailableRepositories.length > 0 ? { unavailable_repositories: unavailableRepositories } : {}
|
|
1860
1911
|
});
|
|
1861
1912
|
}
|
|
1862
1913
|
const impacts = await Promise.all(
|
|
@@ -1873,13 +1924,17 @@ async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
|
1873
1924
|
target_uid: targetUid,
|
|
1874
1925
|
graph_context: graphContext,
|
|
1875
1926
|
impacts,
|
|
1876
|
-
materialized: joined
|
|
1927
|
+
materialized: joined,
|
|
1928
|
+
...unavailableRepositories.length > 0 ? { unavailable_repositories: unavailableRepositories } : {}
|
|
1877
1929
|
});
|
|
1878
1930
|
}
|
|
1879
1931
|
default:
|
|
1880
1932
|
return err("unknown_action", `unknown mindgraph_code action: ${action}`);
|
|
1881
1933
|
}
|
|
1882
1934
|
} catch (cause) {
|
|
1935
|
+
if (cause instanceof UnknownRepositoryError) {
|
|
1936
|
+
return err("unknown_repository", cause.message);
|
|
1937
|
+
}
|
|
1883
1938
|
return err("code_tool_failed", errorDetail(cause));
|
|
1884
1939
|
}
|
|
1885
1940
|
}
|
|
@@ -1905,7 +1960,10 @@ async function attachCodeRefsToToolResult(client2, result, args, adapter) {
|
|
|
1905
1960
|
code_refs: args.code_refs,
|
|
1906
1961
|
repo: args.repo,
|
|
1907
1962
|
repo_space_uid: args.repo_space_uid,
|
|
1908
|
-
agent_id: args.agent_id
|
|
1963
|
+
agent_id: args.agent_id,
|
|
1964
|
+
// Keeps the default adapter anchored to the live session directory
|
|
1965
|
+
// rather than wherever the MCP server process was launched.
|
|
1966
|
+
invocation_context: args.invocation_context
|
|
1909
1967
|
},
|
|
1910
1968
|
adapter
|
|
1911
1969
|
);
|
|
@@ -1915,17 +1973,33 @@ async function attachCodeRefsToToolResult(client2, result, args, adapter) {
|
|
|
1915
1973
|
const anchorUids = (anchors.anchors ?? []).flatMap(
|
|
1916
1974
|
(anchor) => anchor.uid ? [anchor.uid] : []
|
|
1917
1975
|
);
|
|
1976
|
+
const relateFailures = [];
|
|
1918
1977
|
for (const targetUid of targetUids) {
|
|
1919
1978
|
for (const anchorUid of anchorUids) {
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1979
|
+
try {
|
|
1980
|
+
await codeClient.entity({
|
|
1981
|
+
action: "relate",
|
|
1982
|
+
source_uid: targetUid,
|
|
1983
|
+
target_uid: anchorUid,
|
|
1984
|
+
edge_type: args.action === "lesson" ? "ABOUT" : "RELEVANT_TO",
|
|
1985
|
+
agent_id: args.agent_id
|
|
1986
|
+
});
|
|
1987
|
+
} catch (cause) {
|
|
1988
|
+
relateFailures.push(
|
|
1989
|
+
`${targetUid} -> ${anchorUid}: ${errorDetail(cause)}`
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1927
1992
|
}
|
|
1928
1993
|
}
|
|
1994
|
+
if (relateFailures.length > 0) {
|
|
1995
|
+
return ok({
|
|
1996
|
+
result: original,
|
|
1997
|
+
code_anchors: {
|
|
1998
|
+
...anchors,
|
|
1999
|
+
caveat: `anchors written, but ${relateFailures.length} relate edge(s) failed: ${relateFailures.join("; ")}`
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
1929
2003
|
}
|
|
1930
2004
|
return ok({ result: original, code_anchors: anchors });
|
|
1931
2005
|
}
|
|
@@ -1998,16 +2072,34 @@ async function codeRefFromAnchor(client2, adapter, uid) {
|
|
|
1998
2072
|
if (!attributes || typeof attributes !== "object") return void 0;
|
|
1999
2073
|
const codeRef = attributes.code_ref;
|
|
2000
2074
|
if (!isStoredCodeRef(codeRef)) return void 0;
|
|
2001
|
-
|
|
2002
|
-
|
|
2075
|
+
try {
|
|
2076
|
+
const repository = await adapter.resolveRepository(codeRef.repoId);
|
|
2077
|
+
return { ref: { ...codeRef, repoRoot: repository.repoRoot } };
|
|
2078
|
+
} catch (cause) {
|
|
2079
|
+
if (cause instanceof UnknownRepositoryError) {
|
|
2080
|
+
return { missingRepo: codeRef.repoId };
|
|
2081
|
+
}
|
|
2082
|
+
throw cause;
|
|
2083
|
+
}
|
|
2003
2084
|
}
|
|
2004
2085
|
async function resolveSingleTarget(client2, adapter, refs, anchorUids, options) {
|
|
2005
2086
|
if (refs.length > 1 || anchorUids.length > 1) {
|
|
2006
2087
|
return { error: err("ambiguous_target", "expand/affected accepts exactly one code target") };
|
|
2007
2088
|
}
|
|
2008
2089
|
if (anchorUids.length === 1) {
|
|
2009
|
-
const
|
|
2010
|
-
|
|
2090
|
+
const outcome = await codeRefFromAnchor(client2, adapter, anchorUids[0]);
|
|
2091
|
+
if (!outcome) {
|
|
2092
|
+
return { error: err("invalid_anchor", "anchor UID does not contain a typed code_ref") };
|
|
2093
|
+
}
|
|
2094
|
+
if ("missingRepo" in outcome) {
|
|
2095
|
+
return {
|
|
2096
|
+
error: err(
|
|
2097
|
+
"repository_unavailable",
|
|
2098
|
+
`repository ${outcome.missingRepo} is not checked out here or declared in .mindgraph/workspace.json; expand/affected need its local index`
|
|
2099
|
+
)
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
return { ref: outcome.ref };
|
|
2011
2103
|
}
|
|
2012
2104
|
if (refs.length !== 1) {
|
|
2013
2105
|
return { error: err("missing_code_ref", "one code_ref or anchor_uid is required") };
|
|
@@ -2046,7 +2138,7 @@ async function joinMaterialized(client2, refs, agentId, limit) {
|
|
|
2046
2138
|
async function codeRefsFromGraphContext(client2, adapter, context) {
|
|
2047
2139
|
const uids = /* @__PURE__ */ new Set();
|
|
2048
2140
|
collectUids(context, uids);
|
|
2049
|
-
if (uids.size === 0) return [];
|
|
2141
|
+
if (uids.size === 0) return { refs: [], unavailable: [] };
|
|
2050
2142
|
const nodes = client2.getNodesBatch ? await client2.getNodesBatch([...uids]) : await Promise.all([...uids].map((uid) => client2.getNode(uid).catch(() => void 0)));
|
|
2051
2143
|
const stored = nodes.flatMap((node) => {
|
|
2052
2144
|
if (!node) return [];
|
|
@@ -2055,12 +2147,21 @@ async function codeRefsFromGraphContext(client2, adapter, context) {
|
|
|
2055
2147
|
const codeRef = attributes.code_ref;
|
|
2056
2148
|
return isStoredCodeRef(codeRef) ? [codeRef] : [];
|
|
2057
2149
|
});
|
|
2058
|
-
|
|
2059
|
-
|
|
2150
|
+
const refs = [];
|
|
2151
|
+
const unavailable = /* @__PURE__ */ new Set();
|
|
2152
|
+
for (const ref of stored) {
|
|
2153
|
+
try {
|
|
2060
2154
|
const repository = await adapter.resolveRepository(ref.repoId);
|
|
2061
|
-
|
|
2062
|
-
})
|
|
2063
|
-
|
|
2155
|
+
refs.push({ ...ref, repoRoot: repository.repoRoot });
|
|
2156
|
+
} catch (cause) {
|
|
2157
|
+
if (cause instanceof UnknownRepositoryError) {
|
|
2158
|
+
unavailable.add(ref.repoId);
|
|
2159
|
+
} else {
|
|
2160
|
+
throw cause;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
return { refs, unavailable: [...unavailable].sort() };
|
|
2064
2165
|
}
|
|
2065
2166
|
function collectUids(value, uids) {
|
|
2066
2167
|
if (Array.isArray(value)) {
|
|
@@ -2123,20 +2224,20 @@ var init_code_tool = __esm({
|
|
|
2123
2224
|
init_error_detail();
|
|
2124
2225
|
init_codegraph();
|
|
2125
2226
|
ok = (data) => ({
|
|
2126
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
2227
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2127
2228
|
});
|
|
2128
2229
|
err = (code, message, details) => ({
|
|
2129
2230
|
content: [
|
|
2130
2231
|
{
|
|
2131
2232
|
type: "text",
|
|
2132
|
-
text: JSON.stringify({ error: message, code, details }
|
|
2233
|
+
text: JSON.stringify({ error: message, code, details })
|
|
2133
2234
|
}
|
|
2134
2235
|
],
|
|
2135
2236
|
isError: true
|
|
2136
2237
|
});
|
|
2137
2238
|
CODE_TOOL = {
|
|
2138
2239
|
name: "mindgraph_code",
|
|
2139
|
-
description: "Resolve typed code references through the local codegraph index and join them to persistent MindGraph memory/work. anchor creates exact repository/code anchors; recall reads attached graph context; expand reads callers/callees; affected reads impact for a code ref or Task/Execution. Ambiguous refs return candidates without writing.",
|
|
2240
|
+
description: "Resolve typed code references through the local codegraph index and join them to persistent MindGraph memory/work. anchor creates exact repository/code anchors; recall reads attached graph context; expand reads callers/callees; affected reads impact for a code ref or Task/Execution. Ambiguous refs return candidates without writing. Refs with a path route to the repository defining that file (nearest declared root in .mindgraph/workspace.json); bare symbols resolve against the current repository only \u2014 pass path or repo to target a sibling.",
|
|
2140
2241
|
inputSchema: {
|
|
2141
2242
|
type: "object",
|
|
2142
2243
|
properties: {
|
|
@@ -2178,7 +2279,7 @@ var init_code_tool = __esm({
|
|
|
2178
2279
|
},
|
|
2179
2280
|
repo_space_uid: {
|
|
2180
2281
|
type: "string",
|
|
2181
|
-
description: "Writable shared Project/repository Space for new anchors"
|
|
2282
|
+
description: "Writable shared Project/repository Space for new anchors (anchor requires this, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID)"
|
|
2182
2283
|
},
|
|
2183
2284
|
task_uid: { type: "string" },
|
|
2184
2285
|
execution_uid: { type: "string" },
|
|
@@ -2197,6 +2298,276 @@ var init_code_tool = __esm({
|
|
|
2197
2298
|
}
|
|
2198
2299
|
});
|
|
2199
2300
|
|
|
2301
|
+
// src/hook-installer.ts
|
|
2302
|
+
function command(harness) {
|
|
2303
|
+
return `node "$HOME/${RUNNER_RELATIVE}" hook --harness ${harness} --owner mindgraph`;
|
|
2304
|
+
}
|
|
2305
|
+
function windowsCommand(harness) {
|
|
2306
|
+
return `node "%USERPROFILE%\\.mindgraph\\bin\\mindgraph-hook.cjs" hook --harness ${harness} --owner mindgraph`;
|
|
2307
|
+
}
|
|
2308
|
+
function installHookRunner(sourceFile, homeDir, version) {
|
|
2309
|
+
const target = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
|
|
2310
|
+
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(target), { recursive: true });
|
|
2311
|
+
const temporary = `${target}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
2312
|
+
import_node_fs4.default.copyFileSync(sourceFile, temporary);
|
|
2313
|
+
import_node_fs4.default.renameSync(temporary, target);
|
|
2314
|
+
const versionPath = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE);
|
|
2315
|
+
if (version) {
|
|
2316
|
+
import_node_fs4.default.writeFileSync(versionPath, `${version}
|
|
2317
|
+
`);
|
|
2318
|
+
} else {
|
|
2319
|
+
import_node_fs4.default.rmSync(versionPath, { force: true });
|
|
2320
|
+
}
|
|
2321
|
+
return target;
|
|
2322
|
+
}
|
|
2323
|
+
function installedHookRunnerVersion(homeDir) {
|
|
2324
|
+
try {
|
|
2325
|
+
const raw = import_node_fs4.default.readFileSync(
|
|
2326
|
+
import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE),
|
|
2327
|
+
"utf8"
|
|
2328
|
+
).trim();
|
|
2329
|
+
return raw || void 0;
|
|
2330
|
+
} catch {
|
|
2331
|
+
return void 0;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
function parseSemver(value) {
|
|
2335
|
+
if (!value) return void 0;
|
|
2336
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value.trim());
|
|
2337
|
+
if (!match) return void 0;
|
|
2338
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
2339
|
+
}
|
|
2340
|
+
function semverNewer(a, b) {
|
|
2341
|
+
for (let index = 0; index < 3; index += 1) {
|
|
2342
|
+
if (a[index] !== b[index]) return a[index] > b[index];
|
|
2343
|
+
}
|
|
2344
|
+
return false;
|
|
2345
|
+
}
|
|
2346
|
+
function refreshHookRunner(serverVersion, sourceBundle, homeDir) {
|
|
2347
|
+
try {
|
|
2348
|
+
if (process.env.MINDGRAPH_HOOK_AUTOUPDATE === "off") {
|
|
2349
|
+
return { refreshed: false, reason: "disabled" };
|
|
2350
|
+
}
|
|
2351
|
+
const home = homeDir || import_node_os4.default.homedir();
|
|
2352
|
+
if (!import_node_fs4.default.existsSync(import_node_path4.default.join(home, RUNNER_RELATIVE))) {
|
|
2353
|
+
return { refreshed: false, reason: "no_runner" };
|
|
2354
|
+
}
|
|
2355
|
+
if (!import_node_fs4.default.existsSync(sourceBundle)) {
|
|
2356
|
+
return { refreshed: false, reason: "no_source" };
|
|
2357
|
+
}
|
|
2358
|
+
const server2 = parseSemver(serverVersion);
|
|
2359
|
+
if (!server2) return { refreshed: false, reason: "unparseable" };
|
|
2360
|
+
const runnerVersion = installedHookRunnerVersion(home);
|
|
2361
|
+
if (runnerVersion !== void 0) {
|
|
2362
|
+
const pinned = parseSemver(runnerVersion);
|
|
2363
|
+
if (!pinned) {
|
|
2364
|
+
return { refreshed: false, reason: "unparseable", runnerVersion };
|
|
2365
|
+
}
|
|
2366
|
+
if (!semverNewer(server2, pinned)) {
|
|
2367
|
+
return {
|
|
2368
|
+
refreshed: false,
|
|
2369
|
+
reason: runnerVersion === serverVersion ? "current" : "downgrade",
|
|
2370
|
+
runnerVersion
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
installHookRunner(sourceBundle, home, serverVersion);
|
|
2375
|
+
return { refreshed: true, reason: "updated", runnerVersion };
|
|
2376
|
+
} catch {
|
|
2377
|
+
return { refreshed: false, reason: "error" };
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
function refreshOwnedClaudeHooks(projectDir = process.cwd(), homeDir) {
|
|
2381
|
+
const refreshed = [];
|
|
2382
|
+
const scopes = ["user", "project"];
|
|
2383
|
+
for (const scope of scopes) {
|
|
2384
|
+
try {
|
|
2385
|
+
const file = settingsPath("claude-code", scope, projectDir, void 0, homeDir);
|
|
2386
|
+
if (!import_node_fs4.default.existsSync(file)) continue;
|
|
2387
|
+
if (!import_node_fs4.default.readFileSync(file, "utf8").includes(OWNER_MARKER)) continue;
|
|
2388
|
+
installHooks("claude-code", scope, projectDir, void 0, homeDir);
|
|
2389
|
+
refreshed.push(file);
|
|
2390
|
+
} catch {
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
return refreshed;
|
|
2394
|
+
}
|
|
2395
|
+
function versionSkewNote(serverVersion, homeDir) {
|
|
2396
|
+
const runnerVersion = installedHookRunnerVersion(homeDir);
|
|
2397
|
+
if (!runnerVersion || runnerVersion === serverVersion) return void 0;
|
|
2398
|
+
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).`;
|
|
2399
|
+
}
|
|
2400
|
+
function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME || import_node_path4.default.join(import_node_os4.default.homedir(), ".codex"), homeDir) {
|
|
2401
|
+
if (harness === "codex") {
|
|
2402
|
+
return scope === "user" ? import_node_path4.default.join(codexHomeDir, "hooks.json") : import_node_path4.default.join(projectDir, ".codex", "hooks.json");
|
|
2403
|
+
}
|
|
2404
|
+
return scope === "user" ? import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), ".claude", "settings.json") : import_node_path4.default.join(projectDir, ".claude", "settings.json");
|
|
2405
|
+
}
|
|
2406
|
+
function readSettings(file) {
|
|
2407
|
+
if (!import_node_fs4.default.existsSync(file)) return {};
|
|
2408
|
+
return JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
|
|
2409
|
+
}
|
|
2410
|
+
function ownedHook(harness, timeout, additionalContextLimit) {
|
|
2411
|
+
return {
|
|
2412
|
+
type: "command",
|
|
2413
|
+
command: command(harness),
|
|
2414
|
+
...harness === "codex" ? { commandWindows: windowsCommand(harness) } : {},
|
|
2415
|
+
timeout,
|
|
2416
|
+
statusMessage: "Syncing MindGraph session context",
|
|
2417
|
+
...additionalContextLimit === void 0 ? {} : { additionalContextLimit }
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
function desiredEntries(harness) {
|
|
2421
|
+
const common = {
|
|
2422
|
+
SessionStart: [
|
|
2423
|
+
{
|
|
2424
|
+
matcher: harness === "claude-code" ? "startup|resume|clear|compact|fork" : "startup|resume|clear|compact",
|
|
2425
|
+
// B7 live acceptance caught cold SessionStart calls crossing the
|
|
2426
|
+
// inherited 20s edge in both harnesses: the hooks correctly failed
|
|
2427
|
+
// open, but no brief reached the model. The same installed runner
|
|
2428
|
+
// completed in 16.84s once warm. Keep a 30s cold-tenant margin.
|
|
2429
|
+
// The declared Codex context limit must not undercut the brief's own
|
|
2430
|
+
// 9,000-char budget: at 3,000 the harness applied a SECOND, tighter
|
|
2431
|
+
// truncation the renderer knew nothing about.
|
|
2432
|
+
hooks: [
|
|
2433
|
+
ownedHook(
|
|
2434
|
+
harness,
|
|
2435
|
+
30,
|
|
2436
|
+
harness === "codex" ? 1e4 : void 0
|
|
2437
|
+
)
|
|
2438
|
+
]
|
|
2439
|
+
}
|
|
2440
|
+
],
|
|
2441
|
+
PreToolUse: [
|
|
2442
|
+
{
|
|
2443
|
+
matcher: "mcp__mindgraph__.*",
|
|
2444
|
+
// Repository resolution over a large multi-repo workspace plus three
|
|
2445
|
+
// git probes can exceed 3s on the first (uncached) call; a killed
|
|
2446
|
+
// PreToolUse means the call goes through untagged.
|
|
2447
|
+
hooks: [ownedHook(harness, 5)]
|
|
2448
|
+
}
|
|
2449
|
+
],
|
|
2450
|
+
// PostToolUse awaits the lease renewal round-trip, which fires exactly
|
|
2451
|
+
// when the tenant is cold (post-idle reclaim) — 5s killed it mid-claim.
|
|
2452
|
+
PostToolUse: [{ matcher: ".*", hooks: [ownedHook(harness, 10)] }],
|
|
2453
|
+
Stop: [{ hooks: [ownedHook(harness, 5)] }],
|
|
2454
|
+
// SessionEnd makes up to two sequential cloud calls (abandon_iteration +
|
|
2455
|
+
// session close) — the SAME cold-tenant latency SessionStart needs 30s
|
|
2456
|
+
// for. Claude honors the configured budget; Codex clamps SessionEnd to a
|
|
2457
|
+
// 3s platform cap regardless of this value (see codex-hooks.ts wire
|
|
2458
|
+
// map), so the codex adapter instead makes at most ONE call there.
|
|
2459
|
+
SessionEnd: [
|
|
2460
|
+
{ hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
|
|
2461
|
+
]
|
|
2462
|
+
};
|
|
2463
|
+
return harness === "claude-code" ? {
|
|
2464
|
+
...common,
|
|
2465
|
+
TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
|
|
2466
|
+
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }]
|
|
2467
|
+
} : common;
|
|
2468
|
+
}
|
|
2469
|
+
function hookCommand(value) {
|
|
2470
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2471
|
+
const command2 = value.command;
|
|
2472
|
+
return typeof command2 === "string" ? command2 : void 0;
|
|
2473
|
+
}
|
|
2474
|
+
function entryHasOwnedHook(value) {
|
|
2475
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2476
|
+
const hooks = value.hooks;
|
|
2477
|
+
return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
|
|
2478
|
+
}
|
|
2479
|
+
function writeSettings(file, settings) {
|
|
2480
|
+
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
2481
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
2482
|
+
import_node_fs4.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
2483
|
+
`, {
|
|
2484
|
+
mode: 384
|
|
2485
|
+
});
|
|
2486
|
+
import_node_fs4.default.renameSync(temporary, file);
|
|
2487
|
+
}
|
|
2488
|
+
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir) {
|
|
2489
|
+
const file = settingsPath(harness, scope, projectDir, codexHomeDir, homeDir);
|
|
2490
|
+
const settings = readSettings(file);
|
|
2491
|
+
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
2492
|
+
let added = 0;
|
|
2493
|
+
let updated = 0;
|
|
2494
|
+
for (const [event, wanted] of Object.entries(desiredEntries(harness))) {
|
|
2495
|
+
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
2496
|
+
const kept = current.filter((entry) => !entryHasOwnedHook(entry));
|
|
2497
|
+
const hadOwned = kept.length !== current.length;
|
|
2498
|
+
for (const entry of wanted) {
|
|
2499
|
+
kept.push(entry);
|
|
2500
|
+
if (hadOwned) updated += 1;
|
|
2501
|
+
else added += 1;
|
|
2502
|
+
}
|
|
2503
|
+
hooks[event] = kept;
|
|
2504
|
+
}
|
|
2505
|
+
settings.hooks = hooks;
|
|
2506
|
+
writeSettings(file, settings);
|
|
2507
|
+
return { path: file, added, updated };
|
|
2508
|
+
}
|
|
2509
|
+
function uninstallHooks(harness, scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2510
|
+
const file = settingsPath(harness, scope, projectDir, codexHomeDir);
|
|
2511
|
+
if (!import_node_fs4.default.existsSync(file)) return { path: file, removed: 0 };
|
|
2512
|
+
const settings = readSettings(file);
|
|
2513
|
+
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
2514
|
+
let removed = 0;
|
|
2515
|
+
for (const [event, value] of Object.entries(hooks)) {
|
|
2516
|
+
if (!Array.isArray(value)) continue;
|
|
2517
|
+
const retainedEntries = [];
|
|
2518
|
+
for (const rawEntry of value) {
|
|
2519
|
+
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) {
|
|
2520
|
+
retainedEntries.push(rawEntry);
|
|
2521
|
+
continue;
|
|
2522
|
+
}
|
|
2523
|
+
const entry = { ...rawEntry };
|
|
2524
|
+
if (!Array.isArray(entry.hooks)) {
|
|
2525
|
+
retainedEntries.push(entry);
|
|
2526
|
+
continue;
|
|
2527
|
+
}
|
|
2528
|
+
const retainedHooks = entry.hooks.filter((hook) => {
|
|
2529
|
+
const owned = hookCommand(hook)?.includes(OWNER_MARKER) || false;
|
|
2530
|
+
if (owned) removed += 1;
|
|
2531
|
+
return !owned;
|
|
2532
|
+
});
|
|
2533
|
+
if (retainedHooks.length > 0) {
|
|
2534
|
+
entry.hooks = retainedHooks;
|
|
2535
|
+
retainedEntries.push(entry);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
if (retainedEntries.length > 0) hooks[event] = retainedEntries;
|
|
2539
|
+
else delete hooks[event];
|
|
2540
|
+
}
|
|
2541
|
+
settings.hooks = hooks;
|
|
2542
|
+
writeSettings(file, settings);
|
|
2543
|
+
return { path: file, removed };
|
|
2544
|
+
}
|
|
2545
|
+
function installClaudeHooks(scope, projectDir = process.cwd()) {
|
|
2546
|
+
return installHooks("claude-code", scope, projectDir);
|
|
2547
|
+
}
|
|
2548
|
+
function uninstallClaudeHooks(scope, projectDir = process.cwd()) {
|
|
2549
|
+
return uninstallHooks("claude-code", scope, projectDir);
|
|
2550
|
+
}
|
|
2551
|
+
function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2552
|
+
return installHooks("codex", scope, projectDir, codexHomeDir);
|
|
2553
|
+
}
|
|
2554
|
+
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2555
|
+
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
2556
|
+
}
|
|
2557
|
+
var import_node_fs4, import_node_os4, import_node_path4, import_node_crypto4, OWNER_MARKER, RUNNER_RELATIVE, RUNNER_VERSION_RELATIVE;
|
|
2558
|
+
var init_hook_installer = __esm({
|
|
2559
|
+
"src/hook-installer.ts"() {
|
|
2560
|
+
"use strict";
|
|
2561
|
+
import_node_fs4 = __toESM(require("fs"));
|
|
2562
|
+
import_node_os4 = __toESM(require("os"));
|
|
2563
|
+
import_node_path4 = __toESM(require("path"));
|
|
2564
|
+
import_node_crypto4 = __toESM(require("crypto"));
|
|
2565
|
+
OWNER_MARKER = "--owner mindgraph";
|
|
2566
|
+
RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
2567
|
+
RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
|
|
2568
|
+
}
|
|
2569
|
+
});
|
|
2570
|
+
|
|
2200
2571
|
// src/sync-tool.ts
|
|
2201
2572
|
function safePath(root, candidate) {
|
|
2202
2573
|
const absolute = import_node_path5.default.resolve(root, candidate);
|
|
@@ -2204,14 +2575,29 @@ function safePath(root, candidate) {
|
|
|
2204
2575
|
if (relative2.startsWith("..") || import_node_path5.default.isAbsolute(relative2)) {
|
|
2205
2576
|
throw new Error(`path escapes repository root: ${candidate}`);
|
|
2206
2577
|
}
|
|
2578
|
+
if (import_node_fs5.default.existsSync(absolute)) {
|
|
2579
|
+
if (!pathContains(realPathOf(root), realPathOf(absolute))) {
|
|
2580
|
+
throw new Error(`path escapes repository root via symlink: ${candidate}`);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2207
2583
|
return absolute;
|
|
2208
2584
|
}
|
|
2209
|
-
function
|
|
2585
|
+
function realPathOf(target) {
|
|
2586
|
+
try {
|
|
2587
|
+
return import_node_fs5.default.realpathSync(target);
|
|
2588
|
+
} catch {
|
|
2589
|
+
return import_node_path5.default.resolve(target);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
function markdownFiles(target, realRoot, visited = /* @__PURE__ */ new Set()) {
|
|
2210
2593
|
if (!import_node_fs5.default.existsSync(target)) return [];
|
|
2594
|
+
const real = realPathOf(target);
|
|
2595
|
+
if (!pathContains(realRoot, real)) return [];
|
|
2211
2596
|
const stat = import_node_fs5.default.statSync(target);
|
|
2212
2597
|
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
2213
|
-
if (!stat.isDirectory()) return [];
|
|
2214
|
-
|
|
2598
|
+
if (!stat.isDirectory() || visited.has(real)) return [];
|
|
2599
|
+
visited.add(real);
|
|
2600
|
+
return import_node_fs5.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path5.default.join(target, entry.name), realRoot, visited));
|
|
2215
2601
|
}
|
|
2216
2602
|
function fileDescriptor(root, absolute) {
|
|
2217
2603
|
const content = import_node_fs5.default.readFileSync(absolute, "utf8");
|
|
@@ -2226,9 +2612,7 @@ async function handleSyncTool(client2, args) {
|
|
|
2226
2612
|
try {
|
|
2227
2613
|
const action = String(args.action || "");
|
|
2228
2614
|
const context = args.invocation_context || {};
|
|
2229
|
-
const adapter = new CodegraphAdapter({
|
|
2230
|
-
cwd: context.cwd || process.cwd()
|
|
2231
|
-
});
|
|
2615
|
+
const adapter = new CodegraphAdapter({ cwd: invocationCwd(args) });
|
|
2232
2616
|
const requestedRepo = typeof args.repo === "string" ? args.repo : typeof args.repo_id === "string" ? args.repo_id : context.repoId;
|
|
2233
2617
|
const repository = await adapter.resolveRepository(requestedRepo);
|
|
2234
2618
|
const root = repository.repoRoot;
|
|
@@ -2237,6 +2621,12 @@ async function handleSyncTool(client2, args) {
|
|
|
2237
2621
|
const remote = client2;
|
|
2238
2622
|
if (action === "scan") {
|
|
2239
2623
|
const repositories = args.workspace === true ? await adapter.resolveRepositories() : [repository];
|
|
2624
|
+
if (args.workspace === true && repositories.length === 0) {
|
|
2625
|
+
return err2(
|
|
2626
|
+
"workspace_not_configured",
|
|
2627
|
+
`workspace scan found no declared repositories: ${adapter.workspaceDiagnostic()}`
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2240
2630
|
const requested = Array.isArray(args.paths) && args.paths.length > 0 ? args.paths.map(String) : [".claude/agent-memory", "memory"];
|
|
2241
2631
|
const scans = [];
|
|
2242
2632
|
for (const selected of repositories) {
|
|
@@ -2293,10 +2683,11 @@ async function handleSyncTool(client2, args) {
|
|
|
2293
2683
|
}
|
|
2294
2684
|
}
|
|
2295
2685
|
async function scanRepository(remote, repository, requested, provider, agentId) {
|
|
2686
|
+
const realRoot = realPathOf(repository.repoRoot);
|
|
2296
2687
|
const files = [
|
|
2297
2688
|
...new Set(
|
|
2298
2689
|
requested.flatMap(
|
|
2299
|
-
(candidate) => markdownFiles(safePath(repository.repoRoot, candidate))
|
|
2690
|
+
(candidate) => markdownFiles(safePath(repository.repoRoot, candidate), realRoot)
|
|
2300
2691
|
)
|
|
2301
2692
|
)
|
|
2302
2693
|
].sort();
|
|
@@ -2335,7 +2726,7 @@ var init_sync_tool = __esm({
|
|
|
2335
2726
|
init_codegraph();
|
|
2336
2727
|
init_error_detail();
|
|
2337
2728
|
ok2 = (data) => ({
|
|
2338
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
2729
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2339
2730
|
});
|
|
2340
2731
|
err2 = (code, message) => ({
|
|
2341
2732
|
content: [{ type: "text", text: JSON.stringify({ code, error: message }) }],
|
|
@@ -2431,12 +2822,23 @@ function clip(value, max, state) {
|
|
|
2431
2822
|
}
|
|
2432
2823
|
function isNodeShaped(value) {
|
|
2433
2824
|
const node = obj(value);
|
|
2434
|
-
return Boolean(
|
|
2825
|
+
return Boolean(
|
|
2826
|
+
node && str(node.uid) && (str(node.label) || str(node.node_type) || obj(node.node_type))
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
function nodeDisplayType(node) {
|
|
2830
|
+
const props = obj(node.props);
|
|
2831
|
+
const plain = str(node.node_type);
|
|
2832
|
+
return (plain && plain !== "Custom" ? plain : void 0) ?? str(obj(node.node_type)?.Custom) ?? str(props?.object_type) ?? str(props?.type_name) ?? str(obj(props?.data)?.domain_type) ?? plain ?? "Node";
|
|
2833
|
+
}
|
|
2834
|
+
function nodeFields(node) {
|
|
2835
|
+
const props = obj(node.props);
|
|
2836
|
+
return obj(props?.fields) ?? obj(obj(props?.data)?.fields);
|
|
2435
2837
|
}
|
|
2436
2838
|
function unwrapSearchResult(value) {
|
|
2437
2839
|
if (isNodeShaped(value)) return value;
|
|
2438
2840
|
const wrapper = obj(value);
|
|
2439
|
-
const inner = obj(wrapper?.node);
|
|
2841
|
+
const inner = obj(wrapper?.node) ?? obj(wrapper?.object);
|
|
2440
2842
|
if (inner && isNodeShaped(inner)) {
|
|
2441
2843
|
const score = num(wrapper.score);
|
|
2442
2844
|
return score !== void 0 ? { ...inner, retrieval_score: score } : inner;
|
|
@@ -2474,7 +2876,7 @@ function sourceTag(node) {
|
|
|
2474
2876
|
return `from: ${title}${extra}`;
|
|
2475
2877
|
}
|
|
2476
2878
|
function layer7Fields(node, state) {
|
|
2477
|
-
const fields =
|
|
2879
|
+
const fields = nodeFields(node);
|
|
2478
2880
|
if (!fields) return void 0;
|
|
2479
2881
|
const pairs = Object.entries(fields).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
2480
2882
|
if (pairs.length === 0) return void 0;
|
|
@@ -2486,7 +2888,7 @@ function nodeSummaryText(node, state) {
|
|
|
2486
2888
|
}
|
|
2487
2889
|
function nodeLines(node, state) {
|
|
2488
2890
|
const label = clip(node.label, LABEL_MAX, state) || "(unlabeled)";
|
|
2489
|
-
const type =
|
|
2891
|
+
const type = nodeDisplayType(node);
|
|
2490
2892
|
const confidence = num(node.confidence);
|
|
2491
2893
|
const score = num(node.retrieval_score);
|
|
2492
2894
|
const parenthetical = [
|
|
@@ -2505,19 +2907,19 @@ function nodeLines(node, state) {
|
|
|
2505
2907
|
if (source) lines.push(` - ${source}`);
|
|
2506
2908
|
const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
|
|
2507
2909
|
if (believers.length > 0) {
|
|
2508
|
-
const
|
|
2910
|
+
const rendered2 = believers.slice(0, 3).map((raw) => {
|
|
2509
2911
|
const believer = obj(raw);
|
|
2510
2912
|
const conf = num(believer?.confidence);
|
|
2511
2913
|
return `${str(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
2512
2914
|
}).join(", ");
|
|
2513
|
-
lines.push(` - believed by: ${
|
|
2915
|
+
lines.push(` - believed by: ${rendered2}`);
|
|
2514
2916
|
}
|
|
2515
2917
|
return lines.join("\n");
|
|
2516
2918
|
}
|
|
2517
2919
|
function groupedNodeSections(nodes, state, headingPrefix = "") {
|
|
2518
2920
|
const groups = /* @__PURE__ */ new Map();
|
|
2519
2921
|
for (const node of nodes) {
|
|
2520
|
-
const type =
|
|
2922
|
+
const type = nodeDisplayType(node);
|
|
2521
2923
|
const group = groups.get(type) ?? [];
|
|
2522
2924
|
group.push(node);
|
|
2523
2925
|
groups.set(type, group);
|
|
@@ -2533,7 +2935,7 @@ function groupedNodeSections(nodes, state, headingPrefix = "") {
|
|
|
2533
2935
|
}
|
|
2534
2936
|
return sections;
|
|
2535
2937
|
}
|
|
2536
|
-
function relationshipItems(edges, labelByUid, state) {
|
|
2938
|
+
function relationshipItems(edges, labelByUid, state, options) {
|
|
2537
2939
|
const lines = [];
|
|
2538
2940
|
for (const raw of edges) {
|
|
2539
2941
|
const edge = obj(raw);
|
|
@@ -2543,8 +2945,8 @@ function relationshipItems(edges, labelByUid, state) {
|
|
|
2543
2945
|
const to = str(edge.to_uid);
|
|
2544
2946
|
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
2545
2947
|
if (!from || !to || from === to) continue;
|
|
2546
|
-
const fromLabel = labelByUid.get(from);
|
|
2547
|
-
const toLabel = labelByUid.get(to);
|
|
2948
|
+
const fromLabel = labelByUid.get(from) ?? (options?.fallbackToUid ? `[${from}]` : void 0);
|
|
2949
|
+
const toLabel = labelByUid.get(to) ?? (options?.fallbackToUid ? `[${to}]` : void 0);
|
|
2548
2950
|
if (!fromLabel || !toLabel) continue;
|
|
2549
2951
|
lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
|
|
2550
2952
|
}
|
|
@@ -2587,8 +2989,8 @@ function assemble(header, sections, state) {
|
|
|
2587
2989
|
sectionUsed += marker.length + 1;
|
|
2588
2990
|
}
|
|
2589
2991
|
lines.push(
|
|
2590
|
-
section.items.length === 0 ? section.heading : `${section.heading}
|
|
2591
|
-
${kept.join("\n")}`
|
|
2992
|
+
section.items.length === 0 ? section.heading : section.heading ? `${section.heading}
|
|
2993
|
+
${kept.join("\n")}` : kept.join("\n")
|
|
2592
2994
|
);
|
|
2593
2995
|
used += headingCost + sectionUsed;
|
|
2594
2996
|
}
|
|
@@ -2719,15 +3121,15 @@ function renderTraversal(response) {
|
|
|
2719
3121
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
2720
3122
|
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2721
3123
|
if (paths.length > 0) {
|
|
2722
|
-
const items = paths.map((raw) => obj(raw)).filter((
|
|
2723
|
-
const uids = Array.isArray(
|
|
2724
|
-
const labels = Array.isArray(
|
|
3124
|
+
const items = paths.map((raw) => obj(raw)).filter((path7) => Boolean(path7)).map((path7, index) => {
|
|
3125
|
+
const uids = Array.isArray(path7.node_uids) ? path7.node_uids.map(String) : [];
|
|
3126
|
+
const labels = Array.isArray(path7.labels) ? path7.labels.map(String) : Array.isArray(path7.nodes) ? path7.nodes.map((hop) => {
|
|
2725
3127
|
const hopNode = obj(hop);
|
|
2726
3128
|
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
2727
3129
|
}) : [];
|
|
2728
3130
|
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
2729
|
-
const cost = num(
|
|
2730
|
-
const confidence = num(
|
|
3131
|
+
const cost = num(path7.cost) ?? num(path7.path_cost);
|
|
3132
|
+
const confidence = num(path7.path_confidence);
|
|
2731
3133
|
const meta = [
|
|
2732
3134
|
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
2733
3135
|
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
@@ -2749,9 +3151,159 @@ function renderTraversal(response) {
|
|
|
2749
3151
|
sections.push({ heading: "## Relationships", items: relationships });
|
|
2750
3152
|
}
|
|
2751
3153
|
}
|
|
2752
|
-
return assemble("# Graph traversal", sections, state);
|
|
3154
|
+
return assemble("# Graph traversal", sections, state);
|
|
3155
|
+
}
|
|
3156
|
+
function neighborLine(value) {
|
|
3157
|
+
const node = obj(value);
|
|
3158
|
+
if (!node) return void 0;
|
|
3159
|
+
const label = str(node.label) || "(unlabeled)";
|
|
3160
|
+
const uid = str(node.uid);
|
|
3161
|
+
const type = str(node.node_type);
|
|
3162
|
+
return `- ${label}${uid ? ` [${uid}]` : ""}${type ? ` (${type})` : ""}`;
|
|
3163
|
+
}
|
|
3164
|
+
function renderOntologyAnswer(response) {
|
|
3165
|
+
const payload = obj(response);
|
|
3166
|
+
if (!payload) return void 0;
|
|
3167
|
+
const objects = Array.isArray(payload.objects) ? payload.objects.filter(isNodeShaped) : isNodeShaped(payload.object) ? [payload.object] : [];
|
|
3168
|
+
const cognitive = obj(payload.cognitive_context);
|
|
3169
|
+
const provenance = Array.isArray(payload.provenance) ? payload.provenance : [];
|
|
3170
|
+
const hasQuerySignature = Array.isArray(payload.objects) || obj(payload.confidence) !== void 0;
|
|
3171
|
+
const cognitiveEmpty = !cognitive || Object.values(cognitive).every((v) => !Array.isArray(v) || v.length === 0);
|
|
3172
|
+
if (objects.length === 0 && cognitiveEmpty && provenance.length === 0) {
|
|
3173
|
+
return hasQuerySignature ? "No matching domain objects." : void 0;
|
|
3174
|
+
}
|
|
3175
|
+
const state = { bounded: false };
|
|
3176
|
+
const sections = [];
|
|
3177
|
+
if (objects.length > 0) {
|
|
3178
|
+
sections.push(...groupedNodeSections(objects, state));
|
|
3179
|
+
}
|
|
3180
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
3181
|
+
for (const node of objects) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
3182
|
+
const graph = obj(payload.graph);
|
|
3183
|
+
for (const raw of Array.isArray(graph?.nodes) ? graph.nodes : []) {
|
|
3184
|
+
const node = obj(raw);
|
|
3185
|
+
const uid = str(node?.uid);
|
|
3186
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
|
|
3187
|
+
}
|
|
3188
|
+
if (cognitive) {
|
|
3189
|
+
for (const rawNodes of Object.values(cognitive)) {
|
|
3190
|
+
if (!Array.isArray(rawNodes)) continue;
|
|
3191
|
+
for (const raw of rawNodes) {
|
|
3192
|
+
const node = obj(raw);
|
|
3193
|
+
const uid = str(node?.uid);
|
|
3194
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
const relations = Array.isArray(payload.relations) ? payload.relations : [];
|
|
3199
|
+
const relationLines = relationshipItems(relations, labelByUid, state, { fallbackToUid: true });
|
|
3200
|
+
if (relationLines.length > 0) {
|
|
3201
|
+
sections.push({ heading: "## Relations", items: relationLines });
|
|
3202
|
+
}
|
|
3203
|
+
if (cognitive) {
|
|
3204
|
+
for (const [category, rawNodes] of Object.entries(cognitive)) {
|
|
3205
|
+
if (!Array.isArray(rawNodes) || rawNodes.length === 0) continue;
|
|
3206
|
+
const items = rawNodes.map(neighborLine).filter((line) => Boolean(line));
|
|
3207
|
+
if (items.length > 0) {
|
|
3208
|
+
sections.push({ heading: `### Cognitive context \u2014 ${category}`, items });
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
if (provenance.length > 0) {
|
|
3213
|
+
const items = provenance.map((raw) => obj(raw)).filter((entry) => Boolean(entry)).map((entry) => {
|
|
3214
|
+
const span = clip(entry.text_span, QUOTE_MAX, state) || "";
|
|
3215
|
+
const by = str(entry.ingested_by_name);
|
|
3216
|
+
const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${str(entry.source_uid) || "?"}]`;
|
|
3217
|
+
return str(entry.span_kind) === "anchor" ? `- "${span}"${suffix}` : `- context: ${span}${suffix}`;
|
|
3218
|
+
});
|
|
3219
|
+
sections.push({ heading: "## Sources", items });
|
|
3220
|
+
}
|
|
3221
|
+
const confidence = num(obj(payload.confidence)?.overall);
|
|
3222
|
+
const notes = [];
|
|
3223
|
+
if (confidence !== void 0) notes.push(`overall confidence ${confidence}`);
|
|
3224
|
+
if (payload.has_more === true) notes.push("more results exist (has_more)");
|
|
3225
|
+
if (payload.seed_cap_hit === true) notes.push("seed cap hit \u2014 narrow the query for a complete set");
|
|
3226
|
+
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
3227
|
+
return assemble(
|
|
3228
|
+
`# Ontology results (${objects.length} object${objects.length === 1 ? "" : "s"})`,
|
|
3229
|
+
sections,
|
|
3230
|
+
state
|
|
3231
|
+
);
|
|
3232
|
+
}
|
|
3233
|
+
function renderObjectList(response, title) {
|
|
3234
|
+
const payload = obj(response);
|
|
3235
|
+
if (!payload) return void 0;
|
|
3236
|
+
const rawItems = Array.isArray(payload.items) ? payload.items : Array.isArray(payload.rows) && payload.rows.some((row) => unwrapSearchResult(row)) ? payload.rows : void 0;
|
|
3237
|
+
const items = (rawItems ?? []).map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
3238
|
+
if (items.length === 0) {
|
|
3239
|
+
return rawItems ? `# ${title} (0 results)` : void 0;
|
|
3240
|
+
}
|
|
3241
|
+
const state = { bounded: false };
|
|
3242
|
+
const sections = groupedNodeSections(items, state);
|
|
3243
|
+
const total = num(payload.total_count);
|
|
3244
|
+
const truncations = Array.isArray(payload.truncation_reasons) ? payload.truncation_reasons.filter((reason) => typeof reason === "string") : [];
|
|
3245
|
+
const notes = [];
|
|
3246
|
+
if (payload.has_more === true) notes.push("more results exist");
|
|
3247
|
+
if (truncations.length > 0) notes.push(`truncated: ${truncations.join(", ")}`);
|
|
3248
|
+
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
3249
|
+
const count = total !== void 0 && total !== items.length ? `${items.length} of ${total}` : `${items.length}`;
|
|
3250
|
+
return assemble(`# ${title} (${count})`, sections, state);
|
|
3251
|
+
}
|
|
3252
|
+
function renderJobs(response) {
|
|
3253
|
+
const jobs = Array.isArray(response) ? response : obj(response)?.jobs;
|
|
3254
|
+
if (!Array.isArray(jobs)) return void 0;
|
|
3255
|
+
if (jobs.length === 0) return "No ingestion jobs.";
|
|
3256
|
+
const state = { bounded: false };
|
|
3257
|
+
const sorted = jobs.map((raw) => obj(raw)).filter((job) => Boolean(job)).sort((a, b) => (num(b.created_at) ?? 0) - (num(a.created_at) ?? 0));
|
|
3258
|
+
const shown = sorted.slice(0, JOBS_RENDER_MAX);
|
|
3259
|
+
if (shown.length < sorted.length) state.bounded = true;
|
|
3260
|
+
const items = shown.map((job) => {
|
|
3261
|
+
const progress = obj(job.progress);
|
|
3262
|
+
const done = num(progress?.processed_chunks);
|
|
3263
|
+
const totalChunks = num(progress?.total_chunks);
|
|
3264
|
+
const bits = [
|
|
3265
|
+
str(job.status) || "?",
|
|
3266
|
+
done !== void 0 && totalChunks !== void 0 ? `${done}/${totalChunks} chunks` : void 0,
|
|
3267
|
+
num(progress?.nodes_created) !== void 0 ? `${num(progress?.nodes_created)} nodes` : void 0,
|
|
3268
|
+
num(job.queue_position) !== void 0 ? `queue #${num(job.queue_position)}` : void 0,
|
|
3269
|
+
str(job.error) ? `error: ${clip(job.error, 160, state)}` : void 0
|
|
3270
|
+
].filter(Boolean);
|
|
3271
|
+
return `- [${str(job.id) || "?"}] ${clip(job.title, 120, state) || "(untitled)"} \u2014 ${bits.join(", ")}`;
|
|
3272
|
+
});
|
|
3273
|
+
return assemble(
|
|
3274
|
+
`# Ingestion jobs (${shown.length}${shown.length < sorted.length ? ` of ${sorted.length}` : ""})`,
|
|
3275
|
+
[{ heading: "", items }],
|
|
3276
|
+
state
|
|
3277
|
+
);
|
|
3278
|
+
}
|
|
3279
|
+
function renderSignals(response) {
|
|
3280
|
+
const payload = obj(response);
|
|
3281
|
+
if (!payload) return void 0;
|
|
3282
|
+
const state = { bounded: false };
|
|
3283
|
+
const sections = [];
|
|
3284
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
3285
|
+
if (!Array.isArray(value) || value.length === 0) continue;
|
|
3286
|
+
const items = value.map((raw) => obj(raw)).filter((item) => Boolean(item)).map((item) => {
|
|
3287
|
+
const fromLabel = str(item.from_label);
|
|
3288
|
+
const toLabel = str(item.to_label);
|
|
3289
|
+
const anyLabelKey = Object.keys(item).find(
|
|
3290
|
+
(k) => k.endsWith("_label") && str(item[k])
|
|
3291
|
+
);
|
|
3292
|
+
const label = str(item.label) ?? str(item.title) ?? str(item.name) ?? (fromLabel && toLabel ? `${fromLabel} \u2194 ${toLabel}` : void 0) ?? (anyLabelKey ? str(item[anyLabelKey]) : void 0) ?? str(item.uid) ?? str(item.target_uid) ?? "(item)";
|
|
3293
|
+
const uid = str(item.uid) ?? str(item.target_uid);
|
|
3294
|
+
const numbers = Object.entries(item).filter(([, v]) => typeof v === "number").slice(0, 4).map(([k, v]) => `${k} ${Math.round(v * 100) / 100}`);
|
|
3295
|
+
const lists = Object.entries(item).filter(([, v]) => Array.isArray(v) && v.length > 0).slice(0, 2).map(([k, v]) => `${k}: ${v.length}`);
|
|
3296
|
+
const meta = [...numbers, ...lists].join(", ");
|
|
3297
|
+
return `- ${clip(label, 160, state)}${uid && uid !== label ? ` [${uid}]` : ""}${meta ? ` (${meta})` : ""}`;
|
|
3298
|
+
});
|
|
3299
|
+
if (items.length > 0) {
|
|
3300
|
+
sections.push({ heading: `## ${key.replace(/_/g, " ")}`, items });
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
if (sections.length === 0) return void 0;
|
|
3304
|
+
return assemble("# Synthesis signals", sections, state);
|
|
2753
3305
|
}
|
|
2754
|
-
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, CURATION_EDGE_TYPES, INFRA_NODE_TYPES;
|
|
3306
|
+
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, CURATION_EDGE_TYPES, INFRA_NODE_TYPES, JOBS_RENDER_MAX;
|
|
2755
3307
|
var init_render = __esm({
|
|
2756
3308
|
"src/render.ts"() {
|
|
2757
3309
|
"use strict";
|
|
@@ -2764,6 +3316,7 @@ var init_render = __esm({
|
|
|
2764
3316
|
RENDER_CHAR_BUDGET = 24e3;
|
|
2765
3317
|
CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
2766
3318
|
INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
3319
|
+
JOBS_RENDER_MAX = 20;
|
|
2767
3320
|
}
|
|
2768
3321
|
});
|
|
2769
3322
|
|
|
@@ -2776,9 +3329,14 @@ function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
|
2776
3329
|
}
|
|
2777
3330
|
function ok3(data) {
|
|
2778
3331
|
return {
|
|
2779
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
3332
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2780
3333
|
};
|
|
2781
3334
|
}
|
|
3335
|
+
function renderedResult(format, raw, render) {
|
|
3336
|
+
if (format === "json") return ok3(raw);
|
|
3337
|
+
const text = render(raw);
|
|
3338
|
+
return text !== void 0 ? { content: [{ type: "text", text }] } : ok3(raw);
|
|
3339
|
+
}
|
|
2782
3340
|
function err3(message) {
|
|
2783
3341
|
return {
|
|
2784
3342
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
@@ -3390,15 +3948,11 @@ async function handleRetrieve(client2, args) {
|
|
|
3390
3948
|
max_cost,
|
|
3391
3949
|
agent_id
|
|
3392
3950
|
} = args;
|
|
3393
|
-
const
|
|
3394
|
-
if (format === "json") return ok3(raw);
|
|
3395
|
-
const text = render(raw);
|
|
3396
|
-
return text !== void 0 ? { content: [{ type: "text", text }] } : ok3(raw);
|
|
3397
|
-
};
|
|
3951
|
+
const rendered2 = (raw, render) => renderedResult(format, raw, render);
|
|
3398
3952
|
switch (action) {
|
|
3399
3953
|
case "context":
|
|
3400
3954
|
if (!query) return err3("query is required for context retrieval");
|
|
3401
|
-
return
|
|
3955
|
+
return rendered2(
|
|
3402
3956
|
await client2.retrieveContext({
|
|
3403
3957
|
query,
|
|
3404
3958
|
node_limit: limit,
|
|
@@ -3423,7 +3977,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3423
3977
|
);
|
|
3424
3978
|
case "text":
|
|
3425
3979
|
if (!query) return err3("query is required for text search");
|
|
3426
|
-
return
|
|
3980
|
+
return rendered2(
|
|
3427
3981
|
await client2.retrieve({
|
|
3428
3982
|
action: "text",
|
|
3429
3983
|
query,
|
|
@@ -3437,7 +3991,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3437
3991
|
);
|
|
3438
3992
|
case "semantic":
|
|
3439
3993
|
if (!query) return err3("query is required for semantic search");
|
|
3440
|
-
return
|
|
3994
|
+
return rendered2(
|
|
3441
3995
|
await client2.retrieve({
|
|
3442
3996
|
action: "semantic",
|
|
3443
3997
|
query,
|
|
@@ -3451,7 +4005,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3451
4005
|
);
|
|
3452
4006
|
case "hybrid":
|
|
3453
4007
|
if (!query) return err3("query is required for hybrid search");
|
|
3454
|
-
return
|
|
4008
|
+
return rendered2(
|
|
3455
4009
|
await client2.retrieve({
|
|
3456
4010
|
action: "hybrid",
|
|
3457
4011
|
query,
|
|
@@ -3473,7 +4027,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3473
4027
|
case "weak_claims":
|
|
3474
4028
|
case "pending_approvals":
|
|
3475
4029
|
case "unresolved_contradictions":
|
|
3476
|
-
return
|
|
4030
|
+
return rendered2(
|
|
3477
4031
|
await client2.retrieve({
|
|
3478
4032
|
action,
|
|
3479
4033
|
limit,
|
|
@@ -3483,7 +4037,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3483
4037
|
(raw) => renderNodeList(raw, action.replace(/_/g, " "), limit)
|
|
3484
4038
|
);
|
|
3485
4039
|
case "stale_derivations":
|
|
3486
|
-
return
|
|
4040
|
+
return rendered2(
|
|
3487
4041
|
await client2.retrieve({
|
|
3488
4042
|
action: "stale_derivations",
|
|
3489
4043
|
limit,
|
|
@@ -3492,7 +4046,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3492
4046
|
(raw) => renderNodeList(raw, "stale derivations", limit)
|
|
3493
4047
|
);
|
|
3494
4048
|
case "preferences":
|
|
3495
|
-
return
|
|
4049
|
+
return rendered2(
|
|
3496
4050
|
await client2.retrieve({
|
|
3497
4051
|
action: "preferences",
|
|
3498
4052
|
...query ? { query, k: limit } : {},
|
|
@@ -3503,7 +4057,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3503
4057
|
);
|
|
3504
4058
|
case "layer":
|
|
3505
4059
|
if (!layer) return err3("layer is required for layer retrieval");
|
|
3506
|
-
return
|
|
4060
|
+
return rendered2(
|
|
3507
4061
|
await client2.retrieve({
|
|
3508
4062
|
action: "layer",
|
|
3509
4063
|
layer,
|
|
@@ -3514,7 +4068,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3514
4068
|
(raw) => renderNodeList(raw, `Layer: ${layer}`, limit)
|
|
3515
4069
|
);
|
|
3516
4070
|
case "recent":
|
|
3517
|
-
return
|
|
4071
|
+
return rendered2(
|
|
3518
4072
|
await client2.retrieve({
|
|
3519
4073
|
action: "recent",
|
|
3520
4074
|
limit,
|
|
@@ -3529,7 +4083,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3529
4083
|
);
|
|
3530
4084
|
// Document inventory
|
|
3531
4085
|
case "document_index":
|
|
3532
|
-
return
|
|
4086
|
+
return rendered2(
|
|
3533
4087
|
await client2.getNodes({
|
|
3534
4088
|
node_type: "Document",
|
|
3535
4089
|
limit: limit ?? 100
|
|
@@ -3539,7 +4093,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3539
4093
|
// Graph traversal
|
|
3540
4094
|
case "chain":
|
|
3541
4095
|
if (!start_uid) return err3("start_uid is required for chain traversal");
|
|
3542
|
-
return
|
|
4096
|
+
return rendered2(
|
|
3543
4097
|
await client2.traverse({
|
|
3544
4098
|
action: "chain",
|
|
3545
4099
|
start_uid,
|
|
@@ -3552,7 +4106,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3552
4106
|
);
|
|
3553
4107
|
case "neighborhood":
|
|
3554
4108
|
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
3555
|
-
return
|
|
4109
|
+
return rendered2(
|
|
3556
4110
|
await client2.traverse({
|
|
3557
4111
|
action: "neighborhood",
|
|
3558
4112
|
start_uid,
|
|
@@ -3568,7 +4122,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3568
4122
|
case "path":
|
|
3569
4123
|
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
3570
4124
|
if (!end_uid) return err3("end_uid is required for path traversal");
|
|
3571
|
-
return
|
|
4125
|
+
return rendered2(
|
|
3572
4126
|
await client2.traverse({
|
|
3573
4127
|
action: "path",
|
|
3574
4128
|
start_uid,
|
|
@@ -3584,7 +4138,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3584
4138
|
);
|
|
3585
4139
|
case "subgraph":
|
|
3586
4140
|
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
3587
|
-
return
|
|
4141
|
+
return rendered2(
|
|
3588
4142
|
await client2.traverse({
|
|
3589
4143
|
action: "subgraph",
|
|
3590
4144
|
start_uid,
|
|
@@ -3600,7 +4154,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3600
4154
|
case "top_k_paths":
|
|
3601
4155
|
if (!start_uid) return err3("start_uid is required for top_k_paths");
|
|
3602
4156
|
if (!end_uid) return err3("end_uid is required for top_k_paths");
|
|
3603
|
-
return
|
|
4157
|
+
return rendered2(
|
|
3604
4158
|
await client2.traverse({
|
|
3605
4159
|
action: "top_k_paths",
|
|
3606
4160
|
start_uid,
|
|
@@ -3637,6 +4191,7 @@ async function handleIngest(client2, args) {
|
|
|
3637
4191
|
chunk_size,
|
|
3638
4192
|
chunk_overlap,
|
|
3639
4193
|
job_id,
|
|
4194
|
+
format,
|
|
3640
4195
|
agent_id
|
|
3641
4196
|
} = args;
|
|
3642
4197
|
const src = source_uri ?? source;
|
|
@@ -3706,19 +4261,21 @@ async function handleIngest(client2, args) {
|
|
|
3706
4261
|
if (job_id) {
|
|
3707
4262
|
return ok3(await client2.getJob(job_id));
|
|
3708
4263
|
}
|
|
3709
|
-
return
|
|
4264
|
+
return renderedResult(format, await client2.listJobs(), renderJobs);
|
|
3710
4265
|
default:
|
|
3711
4266
|
return err3(`Unknown ingest action: ${action}`);
|
|
3712
4267
|
}
|
|
3713
4268
|
}
|
|
3714
4269
|
async function handleSynthesize(client2, args) {
|
|
3715
|
-
const { action, project_uid, signals, target_types, job_id } = args;
|
|
4270
|
+
const { action, project_uid, signals, target_types, job_id, format } = args;
|
|
3716
4271
|
switch (action) {
|
|
3717
4272
|
case "signals":
|
|
3718
4273
|
if (!project_uid)
|
|
3719
4274
|
return err3("project_uid is required for action='signals'");
|
|
3720
|
-
return
|
|
3721
|
-
|
|
4275
|
+
return renderedResult(
|
|
4276
|
+
format,
|
|
4277
|
+
await client2.signals(project_uid, { signals, target_types }),
|
|
4278
|
+
renderSignals
|
|
3722
4279
|
);
|
|
3723
4280
|
case "run":
|
|
3724
4281
|
if (!project_uid)
|
|
@@ -3752,6 +4309,7 @@ async function handleOntology(client2, args) {
|
|
|
3752
4309
|
relation_type,
|
|
3753
4310
|
fields,
|
|
3754
4311
|
source_uids,
|
|
4312
|
+
format,
|
|
3755
4313
|
agent_id
|
|
3756
4314
|
} = args;
|
|
3757
4315
|
switch (action) {
|
|
@@ -3767,7 +4325,8 @@ async function handleOntology(client2, args) {
|
|
|
3767
4325
|
"schema_id is required for action='query' (use action='schemas' to find the active schema)"
|
|
3768
4326
|
);
|
|
3769
4327
|
}
|
|
3770
|
-
return
|
|
4328
|
+
return renderedResult(
|
|
4329
|
+
format,
|
|
3771
4330
|
await client2.queryOntology({
|
|
3772
4331
|
query,
|
|
3773
4332
|
schema_id,
|
|
@@ -3776,29 +4335,42 @@ async function handleOntology(client2, args) {
|
|
|
3776
4335
|
include_sources: include_sources ?? true,
|
|
3777
4336
|
depth,
|
|
3778
4337
|
limit
|
|
3779
|
-
})
|
|
4338
|
+
}),
|
|
4339
|
+
renderOntologyAnswer
|
|
3780
4340
|
);
|
|
3781
4341
|
}
|
|
3782
4342
|
case "search":
|
|
3783
4343
|
if (!query) return err3("query is required for action='search'");
|
|
3784
|
-
return
|
|
4344
|
+
return renderedResult(
|
|
4345
|
+
format,
|
|
3785
4346
|
await client2.searchDomainObjects(query, {
|
|
3786
4347
|
schema_id,
|
|
3787
4348
|
object_types,
|
|
3788
4349
|
limit
|
|
3789
|
-
})
|
|
4350
|
+
}),
|
|
4351
|
+
(raw) => renderObjectList(raw, `Domain search: ${query}`)
|
|
3790
4352
|
);
|
|
3791
4353
|
case "objects":
|
|
3792
4354
|
if (!schema_id) return err3("schema_id is required for action='objects'");
|
|
3793
|
-
return
|
|
3794
|
-
|
|
4355
|
+
return renderedResult(
|
|
4356
|
+
format,
|
|
4357
|
+
await client2.listDomainObjects({ schema_id, object_type, limit }),
|
|
4358
|
+
(raw) => renderObjectList(raw, `Domain objects${object_type ? `: ${object_type}` : ""}`)
|
|
3795
4359
|
);
|
|
3796
4360
|
case "object":
|
|
3797
4361
|
if (!uid) return err3("uid is required for action='object'");
|
|
3798
|
-
return
|
|
4362
|
+
return renderedResult(
|
|
4363
|
+
format,
|
|
4364
|
+
await client2.getDomainObject(uid),
|
|
4365
|
+
(raw) => renderNodeList([raw], "Domain object")
|
|
4366
|
+
);
|
|
3799
4367
|
case "object_context":
|
|
3800
4368
|
if (!uid) return err3("uid is required for action='object_context'");
|
|
3801
|
-
return
|
|
4369
|
+
return renderedResult(
|
|
4370
|
+
format,
|
|
4371
|
+
await client2.getDomainObjectContext(uid, depth),
|
|
4372
|
+
renderOntologyAnswer
|
|
4373
|
+
);
|
|
3802
4374
|
case "proposals":
|
|
3803
4375
|
return ok3(
|
|
3804
4376
|
await client2.listOntologyProposals({
|
|
@@ -4665,6 +5237,11 @@ var init_tools = __esm({
|
|
|
4665
5237
|
type: "string",
|
|
4666
5238
|
description: "Job ID to check status (for job_status action)"
|
|
4667
5239
|
},
|
|
5240
|
+
format: {
|
|
5241
|
+
type: "string",
|
|
5242
|
+
enum: ["text", "json"],
|
|
5243
|
+
description: "job_status without job_id: 'text' (default) renders the 20 most recent jobs; 'json' returns the full raw list."
|
|
5244
|
+
},
|
|
4668
5245
|
agent_id: {
|
|
4669
5246
|
type: "string",
|
|
4670
5247
|
description: "Agent identity"
|
|
@@ -4700,6 +5277,11 @@ var init_tools = __esm({
|
|
|
4700
5277
|
type: "string",
|
|
4701
5278
|
description: "Job ID to poll (for action='job_status')"
|
|
4702
5279
|
},
|
|
5280
|
+
format: {
|
|
5281
|
+
type: "string",
|
|
5282
|
+
enum: ["text", "json"],
|
|
5283
|
+
description: "signals action: 'text' (default) renders signal sections; 'json' returns the raw payload."
|
|
5284
|
+
},
|
|
4703
5285
|
agent_id: {
|
|
4704
5286
|
type: "string",
|
|
4705
5287
|
description: "Agent identity"
|
|
@@ -4808,6 +5390,11 @@ var init_tools = __esm({
|
|
|
4808
5390
|
items: { type: "string" },
|
|
4809
5391
|
description: "Chunk/document UIDs to extract from (for action=extract)"
|
|
4810
5392
|
},
|
|
5393
|
+
format: {
|
|
5394
|
+
type: "string",
|
|
5395
|
+
enum: ["text", "json"],
|
|
5396
|
+
description: "Output format for the read actions query/search/objects/object/object_context (default 'text'): a compact rendered block. Pass 'json' for the raw server response."
|
|
5397
|
+
},
|
|
4811
5398
|
agent_id: {
|
|
4812
5399
|
type: "string",
|
|
4813
5400
|
description: "Agent identity"
|
|
@@ -4870,10 +5457,21 @@ async function getGeneratedTools(client2) {
|
|
|
4870
5457
|
);
|
|
4871
5458
|
}
|
|
4872
5459
|
byName.set(d.name, d);
|
|
5460
|
+
const inputSchema = d.maps_to === "structured_query" ? d.input_schema : {
|
|
5461
|
+
...d.input_schema,
|
|
5462
|
+
properties: {
|
|
5463
|
+
...d.input_schema.properties,
|
|
5464
|
+
format: {
|
|
5465
|
+
type: "string",
|
|
5466
|
+
enum: ["text", "json"],
|
|
5467
|
+
description: "Output format (default 'text': compact rendered block). Pass 'json' for the raw server response."
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
};
|
|
4873
5471
|
tools.push({
|
|
4874
5472
|
name: d.name,
|
|
4875
5473
|
description: d.description,
|
|
4876
|
-
inputSchema
|
|
5474
|
+
inputSchema
|
|
4877
5475
|
});
|
|
4878
5476
|
}
|
|
4879
5477
|
cache = { tools, byName, fetchedAt: Date.now() };
|
|
@@ -4894,31 +5492,41 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
4894
5492
|
if (!query) return err4(`query is required for ${desc.name}`);
|
|
4895
5493
|
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
4896
5494
|
const filters = Array.isArray(args.filters) ? args.filters : void 0;
|
|
4897
|
-
return
|
|
5495
|
+
return rendered(
|
|
5496
|
+
args.format,
|
|
4898
5497
|
await ontologyClient.searchDomainObjects(query, {
|
|
4899
5498
|
schema_id: desc.schema_id,
|
|
4900
5499
|
object_types: [desc.object_type],
|
|
4901
5500
|
filters,
|
|
4902
5501
|
limit
|
|
4903
|
-
})
|
|
5502
|
+
}),
|
|
5503
|
+
(raw) => renderObjectList(raw, `${desc.object_type} search: ${query}`)
|
|
4904
5504
|
);
|
|
4905
5505
|
}
|
|
4906
5506
|
case "object": {
|
|
4907
5507
|
const uid = args.uid;
|
|
4908
5508
|
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
4909
|
-
return
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
5509
|
+
return rendered(
|
|
5510
|
+
args.format,
|
|
5511
|
+
await ontologyClient.getDomainObject(uid, {
|
|
5512
|
+
schema_id: desc.schema_id,
|
|
5513
|
+
object_type: desc.object_type
|
|
5514
|
+
}),
|
|
5515
|
+
(raw) => renderNodeList([raw], desc.object_type)
|
|
5516
|
+
);
|
|
4913
5517
|
}
|
|
4914
5518
|
case "object_context": {
|
|
4915
5519
|
const uid = args.uid;
|
|
4916
5520
|
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
4917
5521
|
const depth = typeof args.depth === "number" ? args.depth : void 0;
|
|
4918
|
-
return
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
5522
|
+
return rendered(
|
|
5523
|
+
args.format,
|
|
5524
|
+
await ontologyClient.getDomainObjectContext(uid, depth, {
|
|
5525
|
+
schema_id: desc.schema_id,
|
|
5526
|
+
object_type: desc.object_type
|
|
5527
|
+
}),
|
|
5528
|
+
renderOntologyAnswer
|
|
5529
|
+
);
|
|
4922
5530
|
}
|
|
4923
5531
|
case "related": {
|
|
4924
5532
|
const uid = args.uid;
|
|
@@ -4931,18 +5539,22 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
4931
5539
|
"related ontology tools require a graph-aware version of the mindgraph SDK"
|
|
4932
5540
|
);
|
|
4933
5541
|
}
|
|
4934
|
-
return
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
5542
|
+
return rendered(
|
|
5543
|
+
args.format,
|
|
5544
|
+
await ontologyClient.queryRelatedDomainObjects({
|
|
5545
|
+
schema_id: desc.schema_id,
|
|
5546
|
+
entry_type: desc.object_type,
|
|
5547
|
+
uid,
|
|
5548
|
+
relation: desc.relation,
|
|
5549
|
+
entry_role: desc.entry_role,
|
|
5550
|
+
far_type: desc.far_type,
|
|
5551
|
+
where: Array.isArray(args.where) ? args.where : void 0,
|
|
5552
|
+
include_provenance: args.include_provenance === true,
|
|
5553
|
+
limit: typeof args.limit === "number" ? args.limit : void 0,
|
|
5554
|
+
offset: typeof args.offset === "number" ? args.offset : void 0
|
|
5555
|
+
}),
|
|
5556
|
+
(raw) => renderObjectList(raw, `${desc.far_type} related via ${desc.relation}`)
|
|
5557
|
+
);
|
|
4946
5558
|
}
|
|
4947
5559
|
case "structured_query": {
|
|
4948
5560
|
const select = args.select;
|
|
@@ -4968,13 +5580,20 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
4968
5580
|
return err4(e instanceof Error ? e.message : String(e));
|
|
4969
5581
|
}
|
|
4970
5582
|
}
|
|
4971
|
-
var ok4, err4, CACHE_TTL_MS, FAILURE_COOLDOWN_MS, cache, retryNotBefore, EMPTY, DuplicateGeneratedToolError, STRUCTURED_QUERY_PASSTHROUGH;
|
|
5583
|
+
var ok4, rendered, err4, CACHE_TTL_MS, FAILURE_COOLDOWN_MS, cache, retryNotBefore, EMPTY, DuplicateGeneratedToolError, STRUCTURED_QUERY_PASSTHROUGH;
|
|
4972
5584
|
var init_generated_tools = __esm({
|
|
4973
5585
|
"src/generated-tools.ts"() {
|
|
4974
5586
|
"use strict";
|
|
5587
|
+
init_render();
|
|
4975
5588
|
ok4 = (data) => ({
|
|
4976
|
-
|
|
5589
|
+
// Compact on purpose — pretty-printing doubled the token cost.
|
|
5590
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
4977
5591
|
});
|
|
5592
|
+
rendered = (format, raw, render) => {
|
|
5593
|
+
if (format === "json") return ok4(raw);
|
|
5594
|
+
const text = render(raw);
|
|
5595
|
+
return text !== void 0 ? { content: [{ type: "text", text }] } : ok4(raw);
|
|
5596
|
+
};
|
|
4978
5597
|
err4 = (message) => ({
|
|
4979
5598
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
4980
5599
|
isError: true
|
|
@@ -5115,7 +5734,7 @@ async function checkMcpGovernance(toolName, args, config) {
|
|
|
5115
5734
|
if (!response) {
|
|
5116
5735
|
return {
|
|
5117
5736
|
allowed: false,
|
|
5118
|
-
message: `MindGraph
|
|
5737
|
+
message: `MindGraph at ${config.baseUrl} is unreachable (${lastFailure}; ${retryDelays.length + 1} attempts). The tool call was NOT evaluated against policy and NOT executed \u2014 this is a connectivity failure, not a governance denial. Check the network and MINDGRAPH_BASE_URL, then retry.`
|
|
5119
5738
|
};
|
|
5120
5739
|
}
|
|
5121
5740
|
if (!response.ok) {
|
|
@@ -5245,7 +5864,7 @@ var init_governance = __esm({
|
|
|
5245
5864
|
// src/resources.ts
|
|
5246
5865
|
function json(uri, data) {
|
|
5247
5866
|
return {
|
|
5248
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data
|
|
5867
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data) }]
|
|
5249
5868
|
};
|
|
5250
5869
|
}
|
|
5251
5870
|
async function readResource(client2, uri) {
|
|
@@ -5359,10 +5978,10 @@ async function main() {
|
|
|
5359
5978
|
const transport = new import_stdio.StdioServerTransport();
|
|
5360
5979
|
await server.connect(transport);
|
|
5361
5980
|
console.error(
|
|
5362
|
-
`MindGraph MCP server v${"0.
|
|
5981
|
+
`MindGraph MCP server v${"0.18.0"} running on stdio (${BASE_URL})`
|
|
5363
5982
|
);
|
|
5364
5983
|
}
|
|
5365
|
-
var import_server, import_stdio, import_types, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, server, GOVERNANCE_CONFIG, PROMPTS;
|
|
5984
|
+
var import_server, import_stdio, import_types, import_node_path6, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, RUNNER_REFRESH, HOOK_SKEW_NOTE, server, GOVERNANCE_CONFIG, PROMPTS;
|
|
5366
5985
|
var init_src = __esm({
|
|
5367
5986
|
"src/index.ts"() {
|
|
5368
5987
|
"use strict";
|
|
@@ -5375,6 +5994,8 @@ var init_src = __esm({
|
|
|
5375
5994
|
init_generated_tools();
|
|
5376
5995
|
init_governance();
|
|
5377
5996
|
init_resources();
|
|
5997
|
+
import_node_path6 = __toESM(require("path"));
|
|
5998
|
+
init_hook_installer();
|
|
5378
5999
|
API_KEY = process.env.MINDGRAPH_API_KEY;
|
|
5379
6000
|
BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
|
|
5380
6001
|
AGENT_ID = process.env.MINDGRAPH_AGENT_ID || loadHookEnv().agentId || stableAgentId();
|
|
@@ -5496,10 +6117,42 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
5496
6117
|
(line) => !line.includes("mindgraph_code") && !line.includes("`code`") && !line.includes("Typed code anchors")
|
|
5497
6118
|
).join("\n");
|
|
5498
6119
|
}
|
|
6120
|
+
RUNNER_REFRESH = (() => {
|
|
6121
|
+
try {
|
|
6122
|
+
return refreshHookRunner("0.18.0", import_node_path6.default.join(__dirname, "cli.js"));
|
|
6123
|
+
} catch {
|
|
6124
|
+
return void 0;
|
|
6125
|
+
}
|
|
6126
|
+
})();
|
|
6127
|
+
if (RUNNER_REFRESH?.refreshed) {
|
|
6128
|
+
const refreshedSettings = (() => {
|
|
6129
|
+
try {
|
|
6130
|
+
return refreshOwnedClaudeHooks();
|
|
6131
|
+
} catch {
|
|
6132
|
+
return [];
|
|
6133
|
+
}
|
|
6134
|
+
})();
|
|
6135
|
+
console.error(
|
|
6136
|
+
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.18.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
|
|
6137
|
+
);
|
|
6138
|
+
}
|
|
6139
|
+
HOOK_SKEW_NOTE = (() => {
|
|
6140
|
+
try {
|
|
6141
|
+
return versionSkewNote("0.18.0");
|
|
6142
|
+
} catch {
|
|
6143
|
+
return void 0;
|
|
6144
|
+
}
|
|
6145
|
+
})();
|
|
6146
|
+
if (HOOK_SKEW_NOTE) {
|
|
6147
|
+
INSTRUCTIONS += `
|
|
6148
|
+
|
|
6149
|
+
${HOOK_SKEW_NOTE}`;
|
|
6150
|
+
console.error(`[mindgraph-mcp] ${HOOK_SKEW_NOTE}`);
|
|
6151
|
+
}
|
|
5499
6152
|
server = new import_server.Server(
|
|
5500
6153
|
{
|
|
5501
6154
|
name: "mindgraph",
|
|
5502
|
-
version: "0.
|
|
6155
|
+
version: "0.18.0"
|
|
5503
6156
|
},
|
|
5504
6157
|
{
|
|
5505
6158
|
capabilities: {
|
|
@@ -5718,7 +6371,7 @@ ${conversation}`
|
|
|
5718
6371
|
|
|
5719
6372
|
// src/cli.ts
|
|
5720
6373
|
var fs5 = __toESM(require("fs"));
|
|
5721
|
-
var
|
|
6374
|
+
var path6 = __toESM(require("path"));
|
|
5722
6375
|
var os4 = __toESM(require("os"));
|
|
5723
6376
|
var crypto5 = __toESM(require("crypto"));
|
|
5724
6377
|
var import_child_process = require("child_process");
|
|
@@ -5801,6 +6454,7 @@ var import_node_path3 = __toESM(require("path"));
|
|
|
5801
6454
|
var import_node_child_process2 = require("child_process");
|
|
5802
6455
|
init_codegraph();
|
|
5803
6456
|
init_code_tool();
|
|
6457
|
+
init_error_detail();
|
|
5804
6458
|
init_hook_env();
|
|
5805
6459
|
var NOOP = { kind: "noop" };
|
|
5806
6460
|
function defaultLedger(sessionId, options) {
|
|
@@ -6064,8 +6718,8 @@ ${knowledgeLines.join("\n")}`);
|
|
|
6064
6718
|
);
|
|
6065
6719
|
}
|
|
6066
6720
|
if (footer) lines.push(footer);
|
|
6067
|
-
const
|
|
6068
|
-
return
|
|
6721
|
+
const rendered2 = lines.join("\n");
|
|
6722
|
+
return rendered2.length <= BRIEF_CHAR_BUDGET ? rendered2 : `${rendered2.slice(0, BRIEF_CHAR_BUDGET - 40)}
|
|
6069
6723
|
\u2026[bounded work brief truncated]`;
|
|
6070
6724
|
}
|
|
6071
6725
|
async function repositoryScopeUids(input, client2, options, agentId) {
|
|
@@ -6132,6 +6786,51 @@ function isOwnPriorWork(brief, agentId, harness) {
|
|
|
6132
6786
|
const owner = string(object(brief?.lease)?.lease_owner_agent_id);
|
|
6133
6787
|
return owner === agentId || owner === harness;
|
|
6134
6788
|
}
|
|
6789
|
+
function hasMindgraphServer(servers) {
|
|
6790
|
+
if (!servers || typeof servers !== "object") return false;
|
|
6791
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
6792
|
+
if (name === "mindgraph") return true;
|
|
6793
|
+
const cfg = entry && typeof entry === "object" ? entry : void 0;
|
|
6794
|
+
const text = [
|
|
6795
|
+
String(cfg?.command ?? ""),
|
|
6796
|
+
Array.isArray(cfg?.args) ? cfg.args.join(" ") : "",
|
|
6797
|
+
String(cfg?.url ?? "")
|
|
6798
|
+
].join(" ");
|
|
6799
|
+
if (text.includes("mindgraph-mcp")) return true;
|
|
6800
|
+
}
|
|
6801
|
+
return false;
|
|
6802
|
+
}
|
|
6803
|
+
function pathIsWithin(ancestor, descendant) {
|
|
6804
|
+
const base = import_node_path3.default.resolve(ancestor);
|
|
6805
|
+
const target = import_node_path3.default.resolve(descendant);
|
|
6806
|
+
return target === base || target.startsWith(base + import_node_path3.default.sep);
|
|
6807
|
+
}
|
|
6808
|
+
function claudeMcpRegistrationNote(input, options) {
|
|
6809
|
+
if (options.harness !== "claude-code") return void 0;
|
|
6810
|
+
if (process.env.MINDGRAPH_SKIP_MCP_REGISTRATION_CHECK === "1") return void 0;
|
|
6811
|
+
try {
|
|
6812
|
+
const configPath = options.claudeConfigPath || import_node_path3.default.join(import_node_os3.default.homedir(), ".claude.json");
|
|
6813
|
+
if (!import_node_fs3.default.existsSync(configPath)) return void 0;
|
|
6814
|
+
const config = JSON.parse(import_node_fs3.default.readFileSync(configPath, "utf8"));
|
|
6815
|
+
if (hasMindgraphServer(config.mcpServers)) return void 0;
|
|
6816
|
+
const cwd = import_node_path3.default.resolve(input.cwd || ".");
|
|
6817
|
+
for (const [projectDir, project] of Object.entries(config.projects || {})) {
|
|
6818
|
+
if (!pathIsWithin(projectDir, cwd)) continue;
|
|
6819
|
+
if (hasMindgraphServer(project?.mcpServers)) return void 0;
|
|
6820
|
+
}
|
|
6821
|
+
for (let dir = cwd; ; dir = import_node_path3.default.dirname(dir)) {
|
|
6822
|
+
const candidate = import_node_path3.default.join(dir, ".mcp.json");
|
|
6823
|
+
if (import_node_fs3.default.existsSync(candidate)) {
|
|
6824
|
+
const parsed = JSON.parse(import_node_fs3.default.readFileSync(candidate, "utf8"));
|
|
6825
|
+
if (hasMindgraphServer(parsed.mcpServers)) return void 0;
|
|
6826
|
+
}
|
|
6827
|
+
if (dir === import_node_path3.default.dirname(dir)) break;
|
|
6828
|
+
}
|
|
6829
|
+
} catch {
|
|
6830
|
+
return void 0;
|
|
6831
|
+
}
|
|
6832
|
+
return "NOTE: no MindGraph MCP server appears to be registered for Claude Code, so this brief's continuity actions (claim, checkpoint, capture) have no mcp__mindgraph__* tools to run through and durable memory cannot be written this session. Tell the user to run: npx -y mindgraph-mcp@latest install-code \u2014 then start a new session.";
|
|
6833
|
+
}
|
|
6135
6834
|
async function sessionStart(input, client2, options) {
|
|
6136
6835
|
const agentId = resolveAgentId(options);
|
|
6137
6836
|
const harnessLabel = options.harness === "claude-code" ? "Claude Code" : "Codex";
|
|
@@ -6221,7 +6920,14 @@ async function sessionStart(input, client2, options) {
|
|
|
6221
6920
|
claimFailed: claimFailed && !stillHeld,
|
|
6222
6921
|
scopeNote
|
|
6223
6922
|
});
|
|
6224
|
-
const
|
|
6923
|
+
const registrationNote = claudeMcpRegistrationNote(input, options);
|
|
6924
|
+
if (registrationNote) {
|
|
6925
|
+
console.error(`mindgraph-hook: ${registrationNote}`);
|
|
6926
|
+
}
|
|
6927
|
+
const contextText = registrationNote ? `${registrationNote}
|
|
6928
|
+
|
|
6929
|
+
${briefText}` : briefText;
|
|
6930
|
+
const briefHash = import_node_crypto3.default.createHash("sha256").update(contextText).digest("hex");
|
|
6225
6931
|
const shouldInject = withLedger(input.session_id, options, (ledger) => {
|
|
6226
6932
|
const changed = ledger.lastBriefHash !== briefHash;
|
|
6227
6933
|
const reinjectCompact = options.reinjectUnchangedCompact && input.source === "compact";
|
|
@@ -6254,7 +6960,7 @@ async function sessionStart(input, client2, options) {
|
|
|
6254
6960
|
ledger.lastBriefHash = briefHash;
|
|
6255
6961
|
return changed || reinjectCompact;
|
|
6256
6962
|
});
|
|
6257
|
-
return shouldInject ? { kind: "context", context:
|
|
6963
|
+
return shouldInject ? { kind: "context", context: contextText } : NOOP;
|
|
6258
6964
|
}
|
|
6259
6965
|
async function preTool(input, options) {
|
|
6260
6966
|
const original = { ...input.tool_input || {} };
|
|
@@ -6361,9 +7067,7 @@ async function postTool(input, client2, options) {
|
|
|
6361
7067
|
}
|
|
6362
7068
|
}
|
|
6363
7069
|
} else if (aboutLedgerTask) {
|
|
6364
|
-
ledger
|
|
6365
|
-
ledger.leaseEpoch = number(payload?.current_epoch) ?? ledger.leaseEpoch;
|
|
6366
|
-
ledger.leaseExpiresAt = number(payload?.lease_expires_at) ?? ledger.leaseExpiresAt;
|
|
7070
|
+
adoptConflictFence(ledger, payload);
|
|
6367
7071
|
}
|
|
6368
7072
|
}
|
|
6369
7073
|
if (ledger.taskUid && ledger.sessionUid && ledger.taskVersion !== void 0 && ledger.leaseEpoch !== void 0 && ledger.leaseExpiresAt !== void 0 && // Only ledgers whose lease state was written by a claim THIS code made
|
|
@@ -6420,19 +7124,35 @@ async function postTool(input, client2, options) {
|
|
|
6420
7124
|
})
|
|
6421
7125
|
);
|
|
6422
7126
|
withLedger(input.session_id, options, (ledger) => {
|
|
6423
|
-
if (payloadIsError(response)) return void 0;
|
|
6424
7127
|
if (ledger.taskUid !== active.taskUid) return void 0;
|
|
7128
|
+
if (payloadIsError(response)) {
|
|
7129
|
+
adoptConflictFence(ledger, response);
|
|
7130
|
+
return void 0;
|
|
7131
|
+
}
|
|
6425
7132
|
ledger.taskVersion = number(response?.task_version) ?? ledger.taskVersion;
|
|
6426
7133
|
ledger.leaseEpoch = number(response?.lease_epoch) ?? ledger.leaseEpoch;
|
|
6427
7134
|
ledger.leaseExpiresAt = number(response?.lease_expires_at) ?? ledger.leaseExpiresAt;
|
|
6428
7135
|
ledger.lastLeaseRenewalAt = nowSeconds;
|
|
6429
7136
|
return void 0;
|
|
6430
7137
|
});
|
|
6431
|
-
} catch {
|
|
7138
|
+
} catch (cause) {
|
|
7139
|
+
const conflict = conflictState(cause);
|
|
7140
|
+
if (Object.keys(conflict).length > 0) {
|
|
7141
|
+
withLedger(input.session_id, options, (ledger) => {
|
|
7142
|
+
if (ledger.taskUid !== active.taskUid) return void 0;
|
|
7143
|
+
adoptConflictFence(ledger, conflict);
|
|
7144
|
+
return void 0;
|
|
7145
|
+
});
|
|
7146
|
+
}
|
|
6432
7147
|
}
|
|
6433
7148
|
}
|
|
6434
7149
|
return NOOP;
|
|
6435
7150
|
}
|
|
7151
|
+
function adoptConflictFence(ledger, payload) {
|
|
7152
|
+
ledger.taskVersion = number(payload?.current_version) ?? ledger.taskVersion;
|
|
7153
|
+
ledger.leaseEpoch = number(payload?.current_epoch) ?? ledger.leaseEpoch;
|
|
7154
|
+
ledger.leaseExpiresAt = number(payload?.lease_expires_at) ?? ledger.leaseExpiresAt;
|
|
7155
|
+
}
|
|
6436
7156
|
function payloadIsError(payload) {
|
|
6437
7157
|
return Boolean(payload && payload.error);
|
|
6438
7158
|
}
|
|
@@ -6585,189 +7305,13 @@ async function runCodexHook(input, client2, options = {}) {
|
|
|
6585
7305
|
}
|
|
6586
7306
|
}
|
|
6587
7307
|
|
|
6588
|
-
// src/hook-installer.ts
|
|
6589
|
-
var import_node_fs4 = __toESM(require("fs"));
|
|
6590
|
-
var import_node_os4 = __toESM(require("os"));
|
|
6591
|
-
var import_node_path4 = __toESM(require("path"));
|
|
6592
|
-
var import_node_crypto4 = __toESM(require("crypto"));
|
|
6593
|
-
var OWNER_MARKER = "--owner mindgraph";
|
|
6594
|
-
var RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
6595
|
-
function command(harness) {
|
|
6596
|
-
return `node "$HOME/${RUNNER_RELATIVE}" hook --harness ${harness} --owner mindgraph`;
|
|
6597
|
-
}
|
|
6598
|
-
function windowsCommand(harness) {
|
|
6599
|
-
return `node "%USERPROFILE%\\.mindgraph\\bin\\mindgraph-hook.cjs" hook --harness ${harness} --owner mindgraph`;
|
|
6600
|
-
}
|
|
6601
|
-
function installHookRunner(sourceFile, homeDir) {
|
|
6602
|
-
const target = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
|
|
6603
|
-
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(target), { recursive: true });
|
|
6604
|
-
import_node_fs4.default.copyFileSync(sourceFile, target);
|
|
6605
|
-
return target;
|
|
6606
|
-
}
|
|
6607
|
-
function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME || import_node_path4.default.join(import_node_os4.default.homedir(), ".codex")) {
|
|
6608
|
-
if (harness === "codex") {
|
|
6609
|
-
return scope === "user" ? import_node_path4.default.join(codexHomeDir, "hooks.json") : import_node_path4.default.join(projectDir, ".codex", "hooks.json");
|
|
6610
|
-
}
|
|
6611
|
-
return scope === "user" ? import_node_path4.default.join(import_node_os4.default.homedir(), ".claude", "settings.json") : import_node_path4.default.join(projectDir, ".claude", "settings.json");
|
|
6612
|
-
}
|
|
6613
|
-
function readSettings(file) {
|
|
6614
|
-
if (!import_node_fs4.default.existsSync(file)) return {};
|
|
6615
|
-
return JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
|
|
6616
|
-
}
|
|
6617
|
-
function ownedHook(harness, timeout, additionalContextLimit) {
|
|
6618
|
-
return {
|
|
6619
|
-
type: "command",
|
|
6620
|
-
command: command(harness),
|
|
6621
|
-
...harness === "codex" ? { commandWindows: windowsCommand(harness) } : {},
|
|
6622
|
-
timeout,
|
|
6623
|
-
statusMessage: "Syncing MindGraph session context",
|
|
6624
|
-
...additionalContextLimit === void 0 ? {} : { additionalContextLimit }
|
|
6625
|
-
};
|
|
6626
|
-
}
|
|
6627
|
-
function desiredEntries(harness) {
|
|
6628
|
-
const common = {
|
|
6629
|
-
SessionStart: [
|
|
6630
|
-
{
|
|
6631
|
-
matcher: harness === "claude-code" ? "startup|resume|clear|compact|fork" : "startup|resume|clear|compact",
|
|
6632
|
-
// B7 live acceptance caught cold SessionStart calls crossing the
|
|
6633
|
-
// inherited 20s edge in both harnesses: the hooks correctly failed
|
|
6634
|
-
// open, but no brief reached the model. The same installed runner
|
|
6635
|
-
// completed in 16.84s once warm. Keep a 30s cold-tenant margin.
|
|
6636
|
-
// The declared Codex context limit must not undercut the brief's own
|
|
6637
|
-
// 9,000-char budget: at 3,000 the harness applied a SECOND, tighter
|
|
6638
|
-
// truncation the renderer knew nothing about.
|
|
6639
|
-
hooks: [
|
|
6640
|
-
ownedHook(
|
|
6641
|
-
harness,
|
|
6642
|
-
30,
|
|
6643
|
-
harness === "codex" ? 1e4 : void 0
|
|
6644
|
-
)
|
|
6645
|
-
]
|
|
6646
|
-
}
|
|
6647
|
-
],
|
|
6648
|
-
PreToolUse: [
|
|
6649
|
-
{
|
|
6650
|
-
matcher: "mcp__mindgraph__.*",
|
|
6651
|
-
// Repository resolution over a large multi-repo workspace plus three
|
|
6652
|
-
// git probes can exceed 3s on the first (uncached) call; a killed
|
|
6653
|
-
// PreToolUse means the call goes through untagged.
|
|
6654
|
-
hooks: [ownedHook(harness, 5)]
|
|
6655
|
-
}
|
|
6656
|
-
],
|
|
6657
|
-
// PostToolUse awaits the lease renewal round-trip, which fires exactly
|
|
6658
|
-
// when the tenant is cold (post-idle reclaim) — 5s killed it mid-claim.
|
|
6659
|
-
PostToolUse: [{ matcher: ".*", hooks: [ownedHook(harness, 10)] }],
|
|
6660
|
-
Stop: [{ hooks: [ownedHook(harness, 5)] }],
|
|
6661
|
-
// SessionEnd makes up to two sequential cloud calls (abandon_iteration +
|
|
6662
|
-
// session close) — the SAME cold-tenant latency SessionStart needs 30s
|
|
6663
|
-
// for. Claude honors the configured budget; Codex clamps SessionEnd to a
|
|
6664
|
-
// 3s platform cap regardless of this value (see codex-hooks.ts wire
|
|
6665
|
-
// map), so the codex adapter instead makes at most ONE call there.
|
|
6666
|
-
SessionEnd: [
|
|
6667
|
-
{ hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
|
|
6668
|
-
]
|
|
6669
|
-
};
|
|
6670
|
-
return harness === "claude-code" ? {
|
|
6671
|
-
...common,
|
|
6672
|
-
TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
|
|
6673
|
-
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }]
|
|
6674
|
-
} : common;
|
|
6675
|
-
}
|
|
6676
|
-
function hookCommand(value) {
|
|
6677
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
6678
|
-
const command2 = value.command;
|
|
6679
|
-
return typeof command2 === "string" ? command2 : void 0;
|
|
6680
|
-
}
|
|
6681
|
-
function entryHasOwnedHook(value) {
|
|
6682
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
6683
|
-
const hooks = value.hooks;
|
|
6684
|
-
return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
|
|
6685
|
-
}
|
|
6686
|
-
function writeSettings(file, settings) {
|
|
6687
|
-
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
6688
|
-
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
6689
|
-
import_node_fs4.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
6690
|
-
`, {
|
|
6691
|
-
mode: 384
|
|
6692
|
-
});
|
|
6693
|
-
import_node_fs4.default.renameSync(temporary, file);
|
|
6694
|
-
}
|
|
6695
|
-
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir) {
|
|
6696
|
-
const file = settingsPath(harness, scope, projectDir, codexHomeDir);
|
|
6697
|
-
const settings = readSettings(file);
|
|
6698
|
-
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
6699
|
-
let added = 0;
|
|
6700
|
-
let updated = 0;
|
|
6701
|
-
for (const [event, wanted] of Object.entries(desiredEntries(harness))) {
|
|
6702
|
-
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
6703
|
-
const kept = current.filter((entry) => !entryHasOwnedHook(entry));
|
|
6704
|
-
const hadOwned = kept.length !== current.length;
|
|
6705
|
-
for (const entry of wanted) {
|
|
6706
|
-
kept.push(entry);
|
|
6707
|
-
if (hadOwned) updated += 1;
|
|
6708
|
-
else added += 1;
|
|
6709
|
-
}
|
|
6710
|
-
hooks[event] = kept;
|
|
6711
|
-
}
|
|
6712
|
-
settings.hooks = hooks;
|
|
6713
|
-
writeSettings(file, settings);
|
|
6714
|
-
return { path: file, added, updated };
|
|
6715
|
-
}
|
|
6716
|
-
function uninstallHooks(harness, scope, projectDir = process.cwd(), codexHomeDir) {
|
|
6717
|
-
const file = settingsPath(harness, scope, projectDir, codexHomeDir);
|
|
6718
|
-
if (!import_node_fs4.default.existsSync(file)) return { path: file, removed: 0 };
|
|
6719
|
-
const settings = readSettings(file);
|
|
6720
|
-
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
6721
|
-
let removed = 0;
|
|
6722
|
-
for (const [event, value] of Object.entries(hooks)) {
|
|
6723
|
-
if (!Array.isArray(value)) continue;
|
|
6724
|
-
const retainedEntries = [];
|
|
6725
|
-
for (const rawEntry of value) {
|
|
6726
|
-
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) {
|
|
6727
|
-
retainedEntries.push(rawEntry);
|
|
6728
|
-
continue;
|
|
6729
|
-
}
|
|
6730
|
-
const entry = { ...rawEntry };
|
|
6731
|
-
if (!Array.isArray(entry.hooks)) {
|
|
6732
|
-
retainedEntries.push(entry);
|
|
6733
|
-
continue;
|
|
6734
|
-
}
|
|
6735
|
-
const retainedHooks = entry.hooks.filter((hook) => {
|
|
6736
|
-
const owned = hookCommand(hook)?.includes(OWNER_MARKER) || false;
|
|
6737
|
-
if (owned) removed += 1;
|
|
6738
|
-
return !owned;
|
|
6739
|
-
});
|
|
6740
|
-
if (retainedHooks.length > 0) {
|
|
6741
|
-
entry.hooks = retainedHooks;
|
|
6742
|
-
retainedEntries.push(entry);
|
|
6743
|
-
}
|
|
6744
|
-
}
|
|
6745
|
-
if (retainedEntries.length > 0) hooks[event] = retainedEntries;
|
|
6746
|
-
else delete hooks[event];
|
|
6747
|
-
}
|
|
6748
|
-
settings.hooks = hooks;
|
|
6749
|
-
writeSettings(file, settings);
|
|
6750
|
-
return { path: file, removed };
|
|
6751
|
-
}
|
|
6752
|
-
function installClaudeHooks(scope, projectDir = process.cwd()) {
|
|
6753
|
-
return installHooks("claude-code", scope, projectDir);
|
|
6754
|
-
}
|
|
6755
|
-
function uninstallClaudeHooks(scope, projectDir = process.cwd()) {
|
|
6756
|
-
return uninstallHooks("claude-code", scope, projectDir);
|
|
6757
|
-
}
|
|
6758
|
-
function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
6759
|
-
return installHooks("codex", scope, projectDir, codexHomeDir);
|
|
6760
|
-
}
|
|
6761
|
-
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
6762
|
-
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
6763
|
-
}
|
|
6764
|
-
|
|
6765
7308
|
// src/cli.ts
|
|
7309
|
+
init_hook_installer();
|
|
6766
7310
|
function recordHookHealth(harness, error) {
|
|
6767
7311
|
try {
|
|
6768
|
-
const dir = process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
7312
|
+
const dir = process.env.MINDGRAPH_RUNTIME_DIR || path6.join(os4.homedir(), ".mindgraph", "runtime");
|
|
6769
7313
|
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
6770
|
-
const file =
|
|
7314
|
+
const file = path6.join(dir, "hook-health.json");
|
|
6771
7315
|
let failures = 0;
|
|
6772
7316
|
try {
|
|
6773
7317
|
failures = Number(JSON.parse(fs5.readFileSync(file, "utf8")).consecutiveFailures) || 0;
|
|
@@ -6789,8 +7333,8 @@ function recordHookHealth(harness, error) {
|
|
|
6789
7333
|
}
|
|
6790
7334
|
function clearHookHealth(harness) {
|
|
6791
7335
|
try {
|
|
6792
|
-
const file =
|
|
6793
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
7336
|
+
const file = path6.join(
|
|
7337
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path6.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
6794
7338
|
"hook-health.json"
|
|
6795
7339
|
);
|
|
6796
7340
|
if (!fs5.existsSync(file)) return;
|
|
@@ -6812,7 +7356,7 @@ function clearHookHealth(harness) {
|
|
|
6812
7356
|
function getClaudeDesktopConfigPath() {
|
|
6813
7357
|
switch (process.platform) {
|
|
6814
7358
|
case "darwin":
|
|
6815
|
-
return
|
|
7359
|
+
return path6.join(
|
|
6816
7360
|
os4.homedir(),
|
|
6817
7361
|
"Library",
|
|
6818
7362
|
"Application Support",
|
|
@@ -6820,13 +7364,13 @@ function getClaudeDesktopConfigPath() {
|
|
|
6820
7364
|
"claude_desktop_config.json"
|
|
6821
7365
|
);
|
|
6822
7366
|
case "win32":
|
|
6823
|
-
return
|
|
6824
|
-
process.env.APPDATA ||
|
|
7367
|
+
return path6.join(
|
|
7368
|
+
process.env.APPDATA || path6.join(os4.homedir(), "AppData", "Roaming"),
|
|
6825
7369
|
"Claude",
|
|
6826
7370
|
"claude_desktop_config.json"
|
|
6827
7371
|
);
|
|
6828
7372
|
case "linux":
|
|
6829
|
-
return
|
|
7373
|
+
return path6.join(
|
|
6830
7374
|
os4.homedir(),
|
|
6831
7375
|
".config",
|
|
6832
7376
|
"Claude",
|
|
@@ -6842,7 +7386,7 @@ function readJsonFile(filePath) {
|
|
|
6842
7386
|
return JSON.parse(content);
|
|
6843
7387
|
}
|
|
6844
7388
|
function writeJsonFile(filePath, data) {
|
|
6845
|
-
const dir =
|
|
7389
|
+
const dir = path6.dirname(filePath);
|
|
6846
7390
|
if (!fs5.existsSync(dir)) {
|
|
6847
7391
|
fs5.mkdirSync(dir, { recursive: true });
|
|
6848
7392
|
}
|
|
@@ -6978,8 +7522,8 @@ function printStatus() {
|
|
|
6978
7522
|
`MINDGRAPH_BASE_URL: ${process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud (default)"}`
|
|
6979
7523
|
);
|
|
6980
7524
|
try {
|
|
6981
|
-
const healthFile =
|
|
6982
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
7525
|
+
const healthFile = path6.join(
|
|
7526
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path6.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
6983
7527
|
"hook-health.json"
|
|
6984
7528
|
);
|
|
6985
7529
|
if (fs5.existsSync(healthFile)) {
|
|
@@ -7000,7 +7544,7 @@ function withHooksFlag() {
|
|
|
7000
7544
|
}
|
|
7001
7545
|
function finishCodeInstall(apiKey, baseUrl, withHooks, agentId) {
|
|
7002
7546
|
if (withHooks) {
|
|
7003
|
-
const runner = installHookRunner(__filename);
|
|
7547
|
+
const runner = installHookRunner(__filename, void 0, "0.18.0");
|
|
7004
7548
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
7005
7549
|
const result = installClaudeHooks("user");
|
|
7006
7550
|
console.log(
|
|
@@ -7230,7 +7774,7 @@ async function main2() {
|
|
|
7230
7774
|
if (hookHarness !== "claude-code" && hookHarness !== "codex") {
|
|
7231
7775
|
throw new Error("--harness must be claude-code or codex");
|
|
7232
7776
|
}
|
|
7233
|
-
const runner = installHookRunner(__filename);
|
|
7777
|
+
const runner = installHookRunner(__filename, void 0, "0.18.0");
|
|
7234
7778
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
7235
7779
|
const result = hookHarness === "codex" ? installCodexHooks(scope, projectDir) : installClaudeHooks(scope, projectDir);
|
|
7236
7780
|
console.log(
|