mindgraph-mcp 0.18.0 → 0.19.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 +5 -2
- package/dist/cli.js +755 -83
- package/dist/index.js +150 -11
- package/package.json +2 -2
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, path8, body) {
|
|
69
|
+
const url = `${this.baseUrl}${path8}`;
|
|
70
70
|
const requestId = createRequestId();
|
|
71
71
|
const init = {
|
|
72
72
|
method,
|
|
@@ -91,13 +91,14 @@ var init_dist = __esm({
|
|
|
91
91
|
parsed = text2;
|
|
92
92
|
}
|
|
93
93
|
const err5 = new MindGraphError(
|
|
94
|
-
`${method} ${
|
|
94
|
+
`${method} ${path8} failed: ${res.status}`,
|
|
95
95
|
res.status,
|
|
96
96
|
parsed
|
|
97
97
|
);
|
|
98
98
|
if (res.status === 503 && attempt < this.maxRetries) {
|
|
99
99
|
lastError = err5;
|
|
100
|
-
const
|
|
100
|
+
const retryAfterSecs = Number(res.headers.get("retry-after"));
|
|
101
|
+
const delay = Number.isFinite(retryAfterSecs) && retryAfterSecs > 0 ? Math.min(retryAfterSecs * 1e3, 1e4) : this.retryBackoffMs * 2 ** attempt;
|
|
101
102
|
await new Promise((r) => setTimeout(r, delay));
|
|
102
103
|
continue;
|
|
103
104
|
}
|
|
@@ -109,17 +110,17 @@ var init_dist = __esm({
|
|
|
109
110
|
}
|
|
110
111
|
throw lastError;
|
|
111
112
|
}
|
|
112
|
-
get(
|
|
113
|
-
return this.request("GET",
|
|
113
|
+
get(path8) {
|
|
114
|
+
return this.request("GET", path8);
|
|
114
115
|
}
|
|
115
|
-
post(
|
|
116
|
-
return this.request("POST",
|
|
116
|
+
post(path8, body) {
|
|
117
|
+
return this.request("POST", path8, body);
|
|
117
118
|
}
|
|
118
|
-
patch(
|
|
119
|
-
return this.request("PATCH",
|
|
119
|
+
patch(path8, body) {
|
|
120
|
+
return this.request("PATCH", path8, body);
|
|
120
121
|
}
|
|
121
|
-
del(
|
|
122
|
-
return this.request("DELETE",
|
|
122
|
+
del(path8) {
|
|
123
|
+
return this.request("DELETE", path8);
|
|
123
124
|
}
|
|
124
125
|
// ---- Health ----
|
|
125
126
|
async health() {
|
|
@@ -148,6 +149,52 @@ var init_dist = __esm({
|
|
|
148
149
|
async entity(req) {
|
|
149
150
|
return this.post("/reality/entity", req);
|
|
150
151
|
}
|
|
152
|
+
async series(req) {
|
|
153
|
+
return this.post("/reality/series", req);
|
|
154
|
+
}
|
|
155
|
+
async createSeries(req) {
|
|
156
|
+
return this.post("/reality/series", { action: "create", ...req });
|
|
157
|
+
}
|
|
158
|
+
async appendSeries(req) {
|
|
159
|
+
return this.post("/reality/series", { action: "append", ...req });
|
|
160
|
+
}
|
|
161
|
+
async seriesWindow(req) {
|
|
162
|
+
return this.post("/reality/series", { action: "window", ...req });
|
|
163
|
+
}
|
|
164
|
+
async aggregateSeries(req) {
|
|
165
|
+
return this.post("/reality/series", { action: "aggregate", ...req });
|
|
166
|
+
}
|
|
167
|
+
async latestSeries(seriesUid, options) {
|
|
168
|
+
return this.post("/reality/series", {
|
|
169
|
+
action: "latest",
|
|
170
|
+
series_uid: seriesUid,
|
|
171
|
+
...options
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async listSeriesForEntity(entityUid, options) {
|
|
175
|
+
return this.post("/reality/series", {
|
|
176
|
+
action: "list_for_entity",
|
|
177
|
+
entity_uid: entityUid,
|
|
178
|
+
...options
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async batchLatestSeries(entityUids, options) {
|
|
182
|
+
return this.post("/reality/series", {
|
|
183
|
+
action: "batch_latest",
|
|
184
|
+
entity_uids: entityUids,
|
|
185
|
+
...options
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async batchAggregateSeries(req) {
|
|
189
|
+
return this.post("/reality/series", { action: "batch_aggregate", ...req });
|
|
190
|
+
}
|
|
191
|
+
async deleteSeries(seriesUid, options) {
|
|
192
|
+
return this.post("/reality/series", {
|
|
193
|
+
action: "delete_series",
|
|
194
|
+
series_uid: seriesUid,
|
|
195
|
+
...options
|
|
196
|
+
});
|
|
197
|
+
}
|
|
151
198
|
/** Convenience: create or find an entity by label. */
|
|
152
199
|
async findOrCreateEntity(label, props, agentId) {
|
|
153
200
|
return this.post("/reality/entity", {
|
|
@@ -717,6 +764,20 @@ var init_dist = __esm({
|
|
|
717
764
|
async getOntologySchema(id) {
|
|
718
765
|
return this.get(`/v1/ontology/schemas/${seg(id)}`);
|
|
719
766
|
}
|
|
767
|
+
async createOntologySeriesBinding(schemaId, request) {
|
|
768
|
+
return this.post(`/v1/ontology/schemas/${seg(schemaId)}/series-bindings`, request);
|
|
769
|
+
}
|
|
770
|
+
async syncOntologySeriesBinding(bindingId, mode) {
|
|
771
|
+
return this.post(
|
|
772
|
+
`/v1/ontology/series-bindings/${seg(bindingId)}/sync`,
|
|
773
|
+
mode ? { mode } : {}
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
async archiveOntologySeriesBinding(schemaId, bindingId) {
|
|
777
|
+
return this.del(
|
|
778
|
+
`/v1/ontology/schemas/${seg(schemaId)}/series-bindings/${seg(bindingId)}`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
720
781
|
async createOntologySchema(req) {
|
|
721
782
|
return this.post("/v1/ontology/schemas", req);
|
|
722
783
|
}
|
|
@@ -988,14 +1049,14 @@ function unavailabilityCaveats(reason, message) {
|
|
|
988
1049
|
}
|
|
989
1050
|
return caveats;
|
|
990
1051
|
}
|
|
991
|
-
function normalizePath(
|
|
992
|
-
return
|
|
1052
|
+
function normalizePath(path8) {
|
|
1053
|
+
return path8.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
993
1054
|
}
|
|
994
1055
|
function normalizeSignature(signature) {
|
|
995
1056
|
return signature.replace(/\s+/g, " ").trim();
|
|
996
1057
|
}
|
|
997
|
-
function languageFromPath(
|
|
998
|
-
const extension = (0, import_node_path2.extname)(
|
|
1058
|
+
function languageFromPath(path8) {
|
|
1059
|
+
const extension = (0, import_node_path2.extname)(path8).toLowerCase();
|
|
999
1060
|
return {
|
|
1000
1061
|
".c": "c",
|
|
1001
1062
|
".cc": "cpp",
|
|
@@ -1158,7 +1219,7 @@ function codeRefIdentityKey(ref) {
|
|
|
1158
1219
|
}
|
|
1159
1220
|
};
|
|
1160
1221
|
}
|
|
1161
|
-
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2,
|
|
1222
|
+
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2, import_node_crypto3, UnknownRepositoryError, DEFAULT_TIMEOUT_MS, DEFAULT_STATUS_TTL_MS, MAX_OUTPUT_BYTES, CodegraphAdapter;
|
|
1162
1223
|
var init_codegraph = __esm({
|
|
1163
1224
|
"src/codegraph.ts"() {
|
|
1164
1225
|
"use strict";
|
|
@@ -1167,7 +1228,7 @@ var init_codegraph = __esm({
|
|
|
1167
1228
|
import_promises = require("fs/promises");
|
|
1168
1229
|
import_node_os2 = require("os");
|
|
1169
1230
|
import_node_path2 = require("path");
|
|
1170
|
-
|
|
1231
|
+
import_node_crypto3 = require("crypto");
|
|
1171
1232
|
UnknownRepositoryError = class extends Error {
|
|
1172
1233
|
constructor(repo) {
|
|
1173
1234
|
super(
|
|
@@ -1296,7 +1357,7 @@ var init_codegraph = __esm({
|
|
|
1296
1357
|
} catch {
|
|
1297
1358
|
}
|
|
1298
1359
|
(0, import_node_fs2.mkdirSync)(identityDir, { recursive: true, mode: 448 });
|
|
1299
|
-
const generated = (0,
|
|
1360
|
+
const generated = (0, import_node_crypto3.randomUUID)();
|
|
1300
1361
|
try {
|
|
1301
1362
|
(0, import_node_fs2.writeFileSync)(identityFile, `${generated}
|
|
1302
1363
|
`, {
|
|
@@ -2308,7 +2369,7 @@ function windowsCommand(harness) {
|
|
|
2308
2369
|
function installHookRunner(sourceFile, homeDir, version) {
|
|
2309
2370
|
const target = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
|
|
2310
2371
|
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(target), { recursive: true });
|
|
2311
|
-
const temporary = `${target}.${process.pid}.${
|
|
2372
|
+
const temporary = `${target}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
2312
2373
|
import_node_fs4.default.copyFileSync(sourceFile, temporary);
|
|
2313
2374
|
import_node_fs4.default.renameSync(temporary, target);
|
|
2314
2375
|
const versionPath = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE);
|
|
@@ -2478,7 +2539,7 @@ function entryHasOwnedHook(value) {
|
|
|
2478
2539
|
}
|
|
2479
2540
|
function writeSettings(file, settings) {
|
|
2480
2541
|
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
2481
|
-
const temporary = `${file}.${process.pid}.${
|
|
2542
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
2482
2543
|
import_node_fs4.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
2483
2544
|
`, {
|
|
2484
2545
|
mode: 384
|
|
@@ -2554,14 +2615,14 @@ function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
|
2554
2615
|
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2555
2616
|
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
2556
2617
|
}
|
|
2557
|
-
var import_node_fs4, import_node_os4, import_node_path4,
|
|
2618
|
+
var import_node_fs4, import_node_os4, import_node_path4, import_node_crypto5, OWNER_MARKER, RUNNER_RELATIVE, RUNNER_VERSION_RELATIVE;
|
|
2558
2619
|
var init_hook_installer = __esm({
|
|
2559
2620
|
"src/hook-installer.ts"() {
|
|
2560
2621
|
"use strict";
|
|
2561
2622
|
import_node_fs4 = __toESM(require("fs"));
|
|
2562
2623
|
import_node_os4 = __toESM(require("os"));
|
|
2563
2624
|
import_node_path4 = __toESM(require("path"));
|
|
2564
|
-
|
|
2625
|
+
import_node_crypto5 = __toESM(require("crypto"));
|
|
2565
2626
|
OWNER_MARKER = "--owner mindgraph";
|
|
2566
2627
|
RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
2567
2628
|
RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
|
|
@@ -2603,7 +2664,7 @@ function fileDescriptor(root, absolute) {
|
|
|
2603
2664
|
const content = import_node_fs5.default.readFileSync(absolute, "utf8");
|
|
2604
2665
|
return {
|
|
2605
2666
|
logical_path: import_node_path5.default.relative(root, absolute).split(import_node_path5.default.sep).join("/"),
|
|
2606
|
-
content_hash:
|
|
2667
|
+
content_hash: import_node_crypto6.default.createHash("sha256").update(content).digest("hex"),
|
|
2607
2668
|
bytes: Buffer.byteLength(content),
|
|
2608
2669
|
content
|
|
2609
2670
|
};
|
|
@@ -2716,11 +2777,11 @@ async function scanRepository(remote, repository, requested, provider, agentId)
|
|
|
2716
2777
|
files: results
|
|
2717
2778
|
};
|
|
2718
2779
|
}
|
|
2719
|
-
var
|
|
2780
|
+
var import_node_crypto6, import_node_fs5, import_node_path5, ok2, err2, SYNC_TOOL;
|
|
2720
2781
|
var init_sync_tool = __esm({
|
|
2721
2782
|
"src/sync-tool.ts"() {
|
|
2722
2783
|
"use strict";
|
|
2723
|
-
|
|
2784
|
+
import_node_crypto6 = __toESM(require("crypto"));
|
|
2724
2785
|
import_node_fs5 = __toESM(require("fs"));
|
|
2725
2786
|
import_node_path5 = __toESM(require("path"));
|
|
2726
2787
|
init_codegraph();
|
|
@@ -3121,15 +3182,15 @@ function renderTraversal(response) {
|
|
|
3121
3182
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
3122
3183
|
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
3123
3184
|
if (paths.length > 0) {
|
|
3124
|
-
const items = paths.map((raw) => obj(raw)).filter((
|
|
3125
|
-
const uids = Array.isArray(
|
|
3126
|
-
const labels = Array.isArray(
|
|
3185
|
+
const items = paths.map((raw) => obj(raw)).filter((path8) => Boolean(path8)).map((path8, index) => {
|
|
3186
|
+
const uids = Array.isArray(path8.node_uids) ? path8.node_uids.map(String) : [];
|
|
3187
|
+
const labels = Array.isArray(path8.labels) ? path8.labels.map(String) : Array.isArray(path8.nodes) ? path8.nodes.map((hop) => {
|
|
3127
3188
|
const hopNode = obj(hop);
|
|
3128
3189
|
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
3129
3190
|
}) : [];
|
|
3130
3191
|
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
3131
|
-
const cost = num(
|
|
3132
|
-
const confidence = num(
|
|
3192
|
+
const cost = num(path8.cost) ?? num(path8.path_cost);
|
|
3193
|
+
const confidence = num(path8.path_confidence);
|
|
3133
3194
|
const meta = [
|
|
3134
3195
|
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
3135
3196
|
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
@@ -3362,6 +3423,9 @@ async function handleTool(client2, name, args) {
|
|
|
3362
3423
|
case "mindgraph_retrieve":
|
|
3363
3424
|
result = await handleRetrieve(client2, args);
|
|
3364
3425
|
break;
|
|
3426
|
+
case "mindgraph_series_query":
|
|
3427
|
+
result = await handleSeriesQuery(client2, args);
|
|
3428
|
+
break;
|
|
3365
3429
|
case "mindgraph_ingest":
|
|
3366
3430
|
result = await handleIngest(client2, args);
|
|
3367
3431
|
break;
|
|
@@ -3406,12 +3470,36 @@ async function handleTool(client2, name, args) {
|
|
|
3406
3470
|
return err3(errorDetail(e));
|
|
3407
3471
|
}
|
|
3408
3472
|
}
|
|
3473
|
+
async function handleSeriesQuery(client2, args) {
|
|
3474
|
+
const action = typeof args.action === "string" ? args.action : "";
|
|
3475
|
+
if (!["list_for_entity", "latest", "window", "aggregate"].includes(action)) {
|
|
3476
|
+
return err3(`Unknown series query action: ${action}`);
|
|
3477
|
+
}
|
|
3478
|
+
if (action === "list_for_entity" && typeof args.entity_uid !== "string") {
|
|
3479
|
+
return err3("entity_uid is required for action='list_for_entity'");
|
|
3480
|
+
}
|
|
3481
|
+
if (action !== "list_for_entity" && typeof args.series_uid !== "string") {
|
|
3482
|
+
return err3(`series_uid is required for action='${action}'`);
|
|
3483
|
+
}
|
|
3484
|
+
if ((action === "window" || action === "aggregate") && (typeof args.from !== "number" || typeof args.to !== "number")) {
|
|
3485
|
+
return err3(`from and to are required for action='${action}'`);
|
|
3486
|
+
}
|
|
3487
|
+
if (action === "aggregate" && (typeof args.bucket !== "string" || typeof args.agg !== "string")) {
|
|
3488
|
+
return err3("bucket and agg are required for action='aggregate'");
|
|
3489
|
+
}
|
|
3490
|
+
return ok3(
|
|
3491
|
+
await client2.series(args)
|
|
3492
|
+
);
|
|
3493
|
+
}
|
|
3409
3494
|
async function handleCapture(client2, args) {
|
|
3410
3495
|
const {
|
|
3411
3496
|
action,
|
|
3412
3497
|
label,
|
|
3413
3498
|
summary,
|
|
3414
3499
|
content,
|
|
3500
|
+
name,
|
|
3501
|
+
description,
|
|
3502
|
+
license,
|
|
3415
3503
|
session_uid,
|
|
3416
3504
|
work_uid,
|
|
3417
3505
|
execution_uid,
|
|
@@ -3446,6 +3534,41 @@ async function handleCapture(client2, args) {
|
|
|
3446
3534
|
agent_id
|
|
3447
3535
|
})
|
|
3448
3536
|
);
|
|
3537
|
+
case "skill": {
|
|
3538
|
+
if (!name) return err3("name is required for action=skill");
|
|
3539
|
+
if (!description) return err3("description is required for action=skill");
|
|
3540
|
+
if (content === void 0) return err3("content is required for action=skill");
|
|
3541
|
+
const hasProvenance = [session_uid, work_uid, execution_uid].some(
|
|
3542
|
+
(uid) => typeof uid === "string" && uid.length > 0
|
|
3543
|
+
) || Array.isArray(summarizes_uids) && summarizes_uids.some((uid) => typeof uid === "string" && uid.length > 0);
|
|
3544
|
+
if (!hasProvenance) {
|
|
3545
|
+
return err3(
|
|
3546
|
+
"at least one of session_uid, work_uid, execution_uid, or summarizes_uids is required for action=skill"
|
|
3547
|
+
);
|
|
3548
|
+
}
|
|
3549
|
+
return ok3(
|
|
3550
|
+
await client2.distill({
|
|
3551
|
+
label,
|
|
3552
|
+
output_type: "skill",
|
|
3553
|
+
summary,
|
|
3554
|
+
confidence,
|
|
3555
|
+
salience,
|
|
3556
|
+
session_uid,
|
|
3557
|
+
work_uid,
|
|
3558
|
+
execution_uid,
|
|
3559
|
+
idempotency_key,
|
|
3560
|
+
supersedes_uid,
|
|
3561
|
+
summarizes_uids,
|
|
3562
|
+
props: {
|
|
3563
|
+
name,
|
|
3564
|
+
description,
|
|
3565
|
+
content,
|
|
3566
|
+
...license ? { license } : {}
|
|
3567
|
+
},
|
|
3568
|
+
agent_id
|
|
3569
|
+
})
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
3449
3572
|
case "journal": {
|
|
3450
3573
|
if (!content) return err3("content is required for action=journal");
|
|
3451
3574
|
const journalProps = { content };
|
|
@@ -4433,7 +4556,7 @@ var init_tools = __esm({
|
|
|
4433
4556
|
TOOLS = [
|
|
4434
4557
|
{
|
|
4435
4558
|
name: "mindgraph_capture",
|
|
4436
|
-
description: "Capture knowledge into the graph. Use 'entity' for people/orgs/places/events/nations/concepts (auto-deduplicates \u2014 safe to call even if already exists), 'observation' for factual statements, 'source' for documents/URLs, 'snippet' for quotes from a source, and 'concept' for abstract ideas. Use 'journal' for quick personal notes
|
|
4559
|
+
description: "Capture knowledge into the graph. Use 'entity' for people/orgs/places/events/nations/concepts (auto-deduplicates \u2014 safe to call even if already exists), 'observation' for factual statements, 'source' for documents/URLs, 'snippet' for quotes from a source, and 'concept' for abstract ideas. Use 'journal' for quick personal notes, 'lesson' for a durable learning, and 'skill' for a reusable procedure authored from completed work. Skill capture always creates a candidate requiring admin review; it never publishes. Skill names must be 1-64 lowercase a-z, digits, or hyphens, with no leading/trailing/consecutive hyphens and no 'anthropic' or 'claude'. Provide at least one provenance field and write instructions, not a transcript dump.",
|
|
4437
4560
|
inputSchema: {
|
|
4438
4561
|
type: "object",
|
|
4439
4562
|
properties: {
|
|
@@ -4446,7 +4569,8 @@ var init_tools = __esm({
|
|
|
4446
4569
|
"snippet",
|
|
4447
4570
|
"concept",
|
|
4448
4571
|
"journal",
|
|
4449
|
-
"lesson"
|
|
4572
|
+
"lesson",
|
|
4573
|
+
"skill"
|
|
4450
4574
|
],
|
|
4451
4575
|
description: "Type of knowledge to capture"
|
|
4452
4576
|
},
|
|
@@ -4460,11 +4584,30 @@ var init_tools = __esm({
|
|
|
4460
4584
|
},
|
|
4461
4585
|
content: {
|
|
4462
4586
|
type: "string",
|
|
4463
|
-
|
|
4587
|
+
maxLength: 65536,
|
|
4588
|
+
description: "Journal body (action=journal) or complete SKILL.md instructions, at most 64 KiB UTF-8 (action=skill)"
|
|
4589
|
+
},
|
|
4590
|
+
name: {
|
|
4591
|
+
type: "string",
|
|
4592
|
+
minLength: 1,
|
|
4593
|
+
maxLength: 64,
|
|
4594
|
+
pattern: "^(?!.*(?:anthropic|claude))(?!-)(?!.*--)[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$",
|
|
4595
|
+
description: "Stable skill identifier using lowercase a-z, digits, and hyphens (for action=skill)"
|
|
4596
|
+
},
|
|
4597
|
+
description: {
|
|
4598
|
+
type: "string",
|
|
4599
|
+
minLength: 1,
|
|
4600
|
+
maxLength: 1024,
|
|
4601
|
+
description: "When the skill should be used; XML tags are rejected (for action=skill)"
|
|
4602
|
+
},
|
|
4603
|
+
license: {
|
|
4604
|
+
type: "string",
|
|
4605
|
+
description: "Optional skill license (for action=skill)"
|
|
4464
4606
|
},
|
|
4465
4607
|
session_uid: {
|
|
4466
4608
|
type: "string",
|
|
4467
|
-
|
|
4609
|
+
minLength: 1,
|
|
4610
|
+
description: "Session UID that produced a lesson or skill"
|
|
4468
4611
|
},
|
|
4469
4612
|
work_uid: {
|
|
4470
4613
|
type: "string",
|
|
@@ -4485,7 +4628,8 @@ var init_tools = __esm({
|
|
|
4485
4628
|
summarizes_uids: {
|
|
4486
4629
|
type: "array",
|
|
4487
4630
|
items: { type: "string" },
|
|
4488
|
-
|
|
4631
|
+
minItems: 1,
|
|
4632
|
+
description: "Source node UIDs the lesson or skill was learned from"
|
|
4489
4633
|
},
|
|
4490
4634
|
entity_type: {
|
|
4491
4635
|
type: "string",
|
|
@@ -4552,7 +4696,24 @@ var init_tools = __esm({
|
|
|
4552
4696
|
description: "Agent identity"
|
|
4553
4697
|
}
|
|
4554
4698
|
},
|
|
4555
|
-
required: ["action", "label"]
|
|
4699
|
+
required: ["action", "label"],
|
|
4700
|
+
allOf: [
|
|
4701
|
+
{
|
|
4702
|
+
if: {
|
|
4703
|
+
properties: { action: { const: "skill" } },
|
|
4704
|
+
required: ["action"]
|
|
4705
|
+
},
|
|
4706
|
+
then: {
|
|
4707
|
+
required: ["name", "description", "content"],
|
|
4708
|
+
anyOf: [
|
|
4709
|
+
{ required: ["session_uid"] },
|
|
4710
|
+
{ required: ["work_uid"] },
|
|
4711
|
+
{ required: ["execution_uid"] },
|
|
4712
|
+
{ required: ["summarizes_uids"] }
|
|
4713
|
+
]
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
]
|
|
4556
4717
|
}
|
|
4557
4718
|
},
|
|
4558
4719
|
{
|
|
@@ -5138,6 +5299,42 @@ var init_tools = __esm({
|
|
|
5138
5299
|
required: ["action"]
|
|
5139
5300
|
}
|
|
5140
5301
|
},
|
|
5302
|
+
{
|
|
5303
|
+
name: "mindgraph_series_query",
|
|
5304
|
+
description: "Read dense numeric time series without placing raw points in general retrieval context. Use list_for_entity to discover series, latest for cached statistics, window for [from,to) keyset pages, and aggregate for bounded calendar buckets.",
|
|
5305
|
+
inputSchema: {
|
|
5306
|
+
type: "object",
|
|
5307
|
+
additionalProperties: false,
|
|
5308
|
+
properties: {
|
|
5309
|
+
action: {
|
|
5310
|
+
type: "string",
|
|
5311
|
+
enum: ["list_for_entity", "latest", "window", "aggregate"]
|
|
5312
|
+
},
|
|
5313
|
+
entity_uid: { type: "string" },
|
|
5314
|
+
series_uid: { type: "string" },
|
|
5315
|
+
from: { type: "number" },
|
|
5316
|
+
to: { type: "number" },
|
|
5317
|
+
cursor: { type: "number" },
|
|
5318
|
+
limit: { type: "number", minimum: 1, maximum: 1e4 },
|
|
5319
|
+
bucket: {
|
|
5320
|
+
type: "string",
|
|
5321
|
+
enum: ["year", "quarter", "month", "week", "day", "hour"]
|
|
5322
|
+
},
|
|
5323
|
+
agg: {
|
|
5324
|
+
type: "string",
|
|
5325
|
+
enum: ["sum", "mean", "min", "max", "count", "std_dev", "last"]
|
|
5326
|
+
},
|
|
5327
|
+
fill: { type: "string", enum: ["none", "null", "prev"] }
|
|
5328
|
+
},
|
|
5329
|
+
required: ["action"]
|
|
5330
|
+
},
|
|
5331
|
+
annotations: {
|
|
5332
|
+
readOnlyHint: true,
|
|
5333
|
+
destructiveHint: false,
|
|
5334
|
+
idempotentHint: true,
|
|
5335
|
+
openWorldHint: false
|
|
5336
|
+
}
|
|
5337
|
+
},
|
|
5141
5338
|
{
|
|
5142
5339
|
name: "mindgraph_ingest",
|
|
5143
5340
|
description: "Ingest long-form content (articles, transcripts, documents, meeting notes) into the knowledge graph. The pipeline automatically chunks the text, extracts entities/claims/relationships via LLM, and deduplicates against existing nodes. Use 'chunk' for a single passage (synchronous), 'document' for full documents (async \u2014 returns a job ID), 'session' for conversation transcripts. Use 'job_status' to check progress. For short individual facts, use the specific cognitive tools instead.",
|
|
@@ -5624,7 +5821,7 @@ function isTransientStatus(status) {
|
|
|
5624
5821
|
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
5625
5822
|
}
|
|
5626
5823
|
function sleep(ms) {
|
|
5627
|
-
return new Promise((
|
|
5824
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5628
5825
|
}
|
|
5629
5826
|
function headers(apiKey, orgId) {
|
|
5630
5827
|
const value = {
|
|
@@ -5642,6 +5839,7 @@ function unsupportedResponse(status, body) {
|
|
|
5642
5839
|
}
|
|
5643
5840
|
function toolMutability(toolName, action) {
|
|
5644
5841
|
if (toolName === "mindgraph_retrieve") return "read";
|
|
5842
|
+
if (toolName === "mindgraph_series_query") return "read";
|
|
5645
5843
|
if (toolName === "mindgraph_plan") {
|
|
5646
5844
|
return action && PLAN_READ_ACTIONS.has(action) ? "read" : "write";
|
|
5647
5845
|
}
|
|
@@ -5826,6 +6024,8 @@ var init_governance = __esm({
|
|
|
5826
6024
|
UID_FIELDS = [
|
|
5827
6025
|
"uid",
|
|
5828
6026
|
"source_uid",
|
|
6027
|
+
"entity_uid",
|
|
6028
|
+
"series_uid",
|
|
5829
6029
|
"session_uid",
|
|
5830
6030
|
"work_uid",
|
|
5831
6031
|
"goal_uid",
|
|
@@ -5978,7 +6178,7 @@ async function main() {
|
|
|
5978
6178
|
const transport = new import_stdio.StdioServerTransport();
|
|
5979
6179
|
await server.connect(transport);
|
|
5980
6180
|
console.error(
|
|
5981
|
-
`MindGraph MCP server v${"0.
|
|
6181
|
+
`MindGraph MCP server v${"0.19.0"} running on stdio (${BASE_URL})`
|
|
5982
6182
|
);
|
|
5983
6183
|
}
|
|
5984
6184
|
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;
|
|
@@ -6119,7 +6319,7 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
6119
6319
|
}
|
|
6120
6320
|
RUNNER_REFRESH = (() => {
|
|
6121
6321
|
try {
|
|
6122
|
-
return refreshHookRunner("0.
|
|
6322
|
+
return refreshHookRunner("0.19.0", import_node_path6.default.join(__dirname, "cli.js"));
|
|
6123
6323
|
} catch {
|
|
6124
6324
|
return void 0;
|
|
6125
6325
|
}
|
|
@@ -6133,12 +6333,12 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
6133
6333
|
}
|
|
6134
6334
|
})();
|
|
6135
6335
|
console.error(
|
|
6136
|
-
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.
|
|
6336
|
+
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.19.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
|
|
6137
6337
|
);
|
|
6138
6338
|
}
|
|
6139
6339
|
HOOK_SKEW_NOTE = (() => {
|
|
6140
6340
|
try {
|
|
6141
|
-
return versionSkewNote("0.
|
|
6341
|
+
return versionSkewNote("0.19.0");
|
|
6142
6342
|
} catch {
|
|
6143
6343
|
return void 0;
|
|
6144
6344
|
}
|
|
@@ -6152,7 +6352,7 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
6152
6352
|
server = new import_server.Server(
|
|
6153
6353
|
{
|
|
6154
6354
|
name: "mindgraph",
|
|
6155
|
-
version: "0.
|
|
6355
|
+
version: "0.19.0"
|
|
6156
6356
|
},
|
|
6157
6357
|
{
|
|
6158
6358
|
capabilities: {
|
|
@@ -6370,8 +6570,8 @@ ${conversation}`
|
|
|
6370
6570
|
});
|
|
6371
6571
|
|
|
6372
6572
|
// src/cli.ts
|
|
6373
|
-
var
|
|
6374
|
-
var
|
|
6573
|
+
var fs6 = __toESM(require("fs"));
|
|
6574
|
+
var path7 = __toESM(require("path"));
|
|
6375
6575
|
var os4 = __toESM(require("os"));
|
|
6376
6576
|
var crypto5 = __toESM(require("crypto"));
|
|
6377
6577
|
var import_child_process = require("child_process");
|
|
@@ -6389,6 +6589,8 @@ function parseArgs(argv) {
|
|
|
6389
6589
|
let harness;
|
|
6390
6590
|
let hooks = false;
|
|
6391
6591
|
let agentId = process.env.MINDGRAPH_AGENT_ID;
|
|
6592
|
+
const positionals = [];
|
|
6593
|
+
let dir;
|
|
6392
6594
|
for (let i = 0; i < args.length; i++) {
|
|
6393
6595
|
const arg = args[i];
|
|
6394
6596
|
switch (arg) {
|
|
@@ -6409,6 +6611,9 @@ function parseArgs(argv) {
|
|
|
6409
6611
|
case "--project-dir":
|
|
6410
6612
|
projectDir = args[++i];
|
|
6411
6613
|
break;
|
|
6614
|
+
case "--dir":
|
|
6615
|
+
dir = args[++i];
|
|
6616
|
+
break;
|
|
6412
6617
|
case "--harness":
|
|
6413
6618
|
harness = args[++i];
|
|
6414
6619
|
break;
|
|
@@ -6427,12 +6632,27 @@ function parseArgs(argv) {
|
|
|
6427
6632
|
break;
|
|
6428
6633
|
default:
|
|
6429
6634
|
if (!arg.startsWith("-")) {
|
|
6430
|
-
command2
|
|
6635
|
+
if (command2 === "serve") {
|
|
6636
|
+
command2 = arg;
|
|
6637
|
+
} else {
|
|
6638
|
+
positionals.push(arg);
|
|
6639
|
+
}
|
|
6431
6640
|
}
|
|
6432
6641
|
break;
|
|
6433
6642
|
}
|
|
6434
6643
|
}
|
|
6435
|
-
return {
|
|
6644
|
+
return {
|
|
6645
|
+
command: command2,
|
|
6646
|
+
apiKey,
|
|
6647
|
+
baseUrl,
|
|
6648
|
+
scope,
|
|
6649
|
+
projectDir,
|
|
6650
|
+
harness,
|
|
6651
|
+
hooks,
|
|
6652
|
+
agentId,
|
|
6653
|
+
positionals,
|
|
6654
|
+
dir
|
|
6655
|
+
};
|
|
6436
6656
|
}
|
|
6437
6657
|
function mcpAddFailureOutput(e) {
|
|
6438
6658
|
const err5 = e;
|
|
@@ -6446,8 +6666,375 @@ function classifyMcpAddFailure(e) {
|
|
|
6446
6666
|
return "other";
|
|
6447
6667
|
}
|
|
6448
6668
|
|
|
6669
|
+
// src/skills-cli.ts
|
|
6670
|
+
var import_node_crypto2 = require("crypto");
|
|
6671
|
+
var fs2 = __toESM(require("fs"));
|
|
6672
|
+
var path2 = __toESM(require("path"));
|
|
6673
|
+
function httpSkillsApi(baseUrl, apiKey, agentId) {
|
|
6674
|
+
const endpoint = `${baseUrl.replace(/\/$/, "")}/action/skill`;
|
|
6675
|
+
const call = async (body) => {
|
|
6676
|
+
const response = await fetch(endpoint, {
|
|
6677
|
+
method: "POST",
|
|
6678
|
+
headers: {
|
|
6679
|
+
"content-type": "application/json",
|
|
6680
|
+
authorization: `Bearer ${apiKey}`
|
|
6681
|
+
},
|
|
6682
|
+
body: JSON.stringify(
|
|
6683
|
+
agentId ? { agent_id: agentId, ...body } : body
|
|
6684
|
+
)
|
|
6685
|
+
});
|
|
6686
|
+
let parsed = {};
|
|
6687
|
+
try {
|
|
6688
|
+
parsed = await response.json();
|
|
6689
|
+
} catch {
|
|
6690
|
+
parsed = { error: `non-JSON response (HTTP ${response.status})` };
|
|
6691
|
+
}
|
|
6692
|
+
return { status: response.status, body: parsed };
|
|
6693
|
+
};
|
|
6694
|
+
return {
|
|
6695
|
+
async list(grantedTo) {
|
|
6696
|
+
const { status, body } = await call({
|
|
6697
|
+
action: "list",
|
|
6698
|
+
...grantedTo ? { granted_to: grantedTo } : {}
|
|
6699
|
+
});
|
|
6700
|
+
if (status !== 200) {
|
|
6701
|
+
throw new Error(
|
|
6702
|
+
`skill list failed (HTTP ${status}): ${String(body.error ?? "")}`
|
|
6703
|
+
);
|
|
6704
|
+
}
|
|
6705
|
+
return body.skills ?? [];
|
|
6706
|
+
},
|
|
6707
|
+
async get(uid) {
|
|
6708
|
+
const { status, body } = await call({ action: "get", uid });
|
|
6709
|
+
if (status === 404) return null;
|
|
6710
|
+
if (status !== 200) {
|
|
6711
|
+
throw new Error(
|
|
6712
|
+
`skill get failed (HTTP ${status}): ${String(body.error ?? "")}`
|
|
6713
|
+
);
|
|
6714
|
+
}
|
|
6715
|
+
const props = body.props ?? {};
|
|
6716
|
+
return {
|
|
6717
|
+
uid: String(body.uid ?? uid),
|
|
6718
|
+
name: String(props.name ?? ""),
|
|
6719
|
+
description: String(props.description ?? ""),
|
|
6720
|
+
content: String(props.content ?? ""),
|
|
6721
|
+
license: props.license ?? null,
|
|
6722
|
+
status: String(props.status ?? "candidate"),
|
|
6723
|
+
status_reason: props.status_reason ?? null,
|
|
6724
|
+
content_hash: String(props.content_hash ?? ""),
|
|
6725
|
+
version: Number(body.version ?? 0)
|
|
6726
|
+
};
|
|
6727
|
+
},
|
|
6728
|
+
send: call
|
|
6729
|
+
};
|
|
6730
|
+
}
|
|
6731
|
+
function isValidSkillName(name) {
|
|
6732
|
+
return name.length >= 1 && name.length <= 64 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
|
|
6733
|
+
}
|
|
6734
|
+
function renderSkillMd(skill) {
|
|
6735
|
+
const lines = [
|
|
6736
|
+
"---",
|
|
6737
|
+
`name: ${JSON.stringify(skill.name)}`,
|
|
6738
|
+
`description: ${JSON.stringify(skill.description)}`
|
|
6739
|
+
];
|
|
6740
|
+
if (skill.license) {
|
|
6741
|
+
lines.push(`license: ${JSON.stringify(skill.license)}`);
|
|
6742
|
+
}
|
|
6743
|
+
lines.push(
|
|
6744
|
+
"metadata:",
|
|
6745
|
+
` mindgraph-uid: ${JSON.stringify(skill.uid)}`,
|
|
6746
|
+
` mindgraph-version: ${JSON.stringify(String(skill.version))}`,
|
|
6747
|
+
` mindgraph-content-hash: ${JSON.stringify(skill.content_hash)}`,
|
|
6748
|
+
"---",
|
|
6749
|
+
"",
|
|
6750
|
+
skill.content
|
|
6751
|
+
);
|
|
6752
|
+
return `${lines.join("\n")}
|
|
6753
|
+
`;
|
|
6754
|
+
}
|
|
6755
|
+
function scalar(raw) {
|
|
6756
|
+
const trimmed = raw.trim();
|
|
6757
|
+
try {
|
|
6758
|
+
const parsed = JSON.parse(trimmed);
|
|
6759
|
+
if (typeof parsed === "string") return parsed;
|
|
6760
|
+
} catch {
|
|
6761
|
+
}
|
|
6762
|
+
return trimmed;
|
|
6763
|
+
}
|
|
6764
|
+
function parseSkillMd(text) {
|
|
6765
|
+
if (!text.startsWith("---\n")) return null;
|
|
6766
|
+
const end = text.indexOf("\n---\n", 4);
|
|
6767
|
+
if (end < 0) return null;
|
|
6768
|
+
const frontmatter = text.slice(4, end + 1);
|
|
6769
|
+
const body = text.slice(end + 5).replace(/^\n/, "").replace(/\n$/, "");
|
|
6770
|
+
const entries = [];
|
|
6771
|
+
for (const line of frontmatter.split("\n")) {
|
|
6772
|
+
const top = /^([A-Za-z0-9_-]+):(.*)$/.exec(line);
|
|
6773
|
+
if (top) {
|
|
6774
|
+
entries.push({ key: top[1], raw: top[2] });
|
|
6775
|
+
} else if (entries.length > 0) {
|
|
6776
|
+
entries[entries.length - 1].raw += `
|
|
6777
|
+
${line}`;
|
|
6778
|
+
}
|
|
6779
|
+
}
|
|
6780
|
+
const result = { unknown: {}, body };
|
|
6781
|
+
for (const { key, raw } of entries) {
|
|
6782
|
+
switch (key) {
|
|
6783
|
+
case "name":
|
|
6784
|
+
result.name = scalar(raw);
|
|
6785
|
+
break;
|
|
6786
|
+
case "description":
|
|
6787
|
+
result.description = scalar(raw);
|
|
6788
|
+
break;
|
|
6789
|
+
case "license":
|
|
6790
|
+
result.license = scalar(raw);
|
|
6791
|
+
break;
|
|
6792
|
+
case "metadata": {
|
|
6793
|
+
const uid = /mindgraph-uid:\s*(.+)/.exec(raw);
|
|
6794
|
+
const version = /mindgraph-version:\s*(.+)/.exec(raw);
|
|
6795
|
+
const hash = /mindgraph-content-hash:\s*(.+)/.exec(raw);
|
|
6796
|
+
if (uid) {
|
|
6797
|
+
result.pin = {
|
|
6798
|
+
uid: scalar(uid[1]),
|
|
6799
|
+
version: version ? scalar(version[1]) : void 0,
|
|
6800
|
+
contentHash: hash ? scalar(hash[1]) : void 0
|
|
6801
|
+
};
|
|
6802
|
+
} else if (raw.trim().length > 0) {
|
|
6803
|
+
result.unknown[key] = raw;
|
|
6804
|
+
}
|
|
6805
|
+
break;
|
|
6806
|
+
}
|
|
6807
|
+
default:
|
|
6808
|
+
result.unknown[key] = raw;
|
|
6809
|
+
break;
|
|
6810
|
+
}
|
|
6811
|
+
}
|
|
6812
|
+
return result;
|
|
6813
|
+
}
|
|
6814
|
+
var STATE_FILE = ".mindgraph-skills-state.json";
|
|
6815
|
+
function sha256(text) {
|
|
6816
|
+
return (0, import_node_crypto2.createHash)("sha256").update(text).digest("hex");
|
|
6817
|
+
}
|
|
6818
|
+
function loadState(dir) {
|
|
6819
|
+
try {
|
|
6820
|
+
return JSON.parse(
|
|
6821
|
+
fs2.readFileSync(path2.join(dir, STATE_FILE), "utf8")
|
|
6822
|
+
);
|
|
6823
|
+
} catch {
|
|
6824
|
+
return {};
|
|
6825
|
+
}
|
|
6826
|
+
}
|
|
6827
|
+
function saveState(dir, state) {
|
|
6828
|
+
fs2.writeFileSync(
|
|
6829
|
+
path2.join(dir, STATE_FILE),
|
|
6830
|
+
`${JSON.stringify(state, null, 2)}
|
|
6831
|
+
`
|
|
6832
|
+
);
|
|
6833
|
+
}
|
|
6834
|
+
function isSymlink(target) {
|
|
6835
|
+
try {
|
|
6836
|
+
return fs2.lstatSync(target).isSymbolicLink();
|
|
6837
|
+
} catch {
|
|
6838
|
+
return false;
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
function safeSkillPaths(dir, name) {
|
|
6842
|
+
if (!isValidSkillName(name)) {
|
|
6843
|
+
return `invalid skill name ${JSON.stringify(name)}`;
|
|
6844
|
+
}
|
|
6845
|
+
const root = path2.resolve(dir);
|
|
6846
|
+
const skillDir = path2.resolve(root, name);
|
|
6847
|
+
if (skillDir !== path2.join(root, name) || !skillDir.startsWith(root + path2.sep)) {
|
|
6848
|
+
return "resolved path escapes the target directory";
|
|
6849
|
+
}
|
|
6850
|
+
if (isSymlink(skillDir)) {
|
|
6851
|
+
return "target directory is a symlink";
|
|
6852
|
+
}
|
|
6853
|
+
const skillFile = path2.join(skillDir, "SKILL.md");
|
|
6854
|
+
if (isSymlink(skillFile)) {
|
|
6855
|
+
return "target SKILL.md is a symlink";
|
|
6856
|
+
}
|
|
6857
|
+
return { skillDir, skillFile };
|
|
6858
|
+
}
|
|
6859
|
+
async function skillsPull(api, dir, grantedTo) {
|
|
6860
|
+
const report = {
|
|
6861
|
+
written: [],
|
|
6862
|
+
unchanged: [],
|
|
6863
|
+
locallyEdited: [],
|
|
6864
|
+
conflicts: [],
|
|
6865
|
+
removed: [],
|
|
6866
|
+
refused: []
|
|
6867
|
+
};
|
|
6868
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
6869
|
+
const state = loadState(dir);
|
|
6870
|
+
const summaries = await api.list(grantedTo);
|
|
6871
|
+
const fetched = /* @__PURE__ */ new Set();
|
|
6872
|
+
for (const summary of summaries) {
|
|
6873
|
+
const detail = await api.get(summary.uid);
|
|
6874
|
+
if (!detail || detail.status !== "published") continue;
|
|
6875
|
+
fetched.add(detail.name);
|
|
6876
|
+
const paths = safeSkillPaths(dir, detail.name);
|
|
6877
|
+
if (typeof paths === "string") {
|
|
6878
|
+
report.refused.push({ name: detail.name, reason: paths });
|
|
6879
|
+
continue;
|
|
6880
|
+
}
|
|
6881
|
+
const { skillDir, skillFile } = paths;
|
|
6882
|
+
const rendered2 = renderSkillMd(detail);
|
|
6883
|
+
let existing = null;
|
|
6884
|
+
try {
|
|
6885
|
+
existing = fs2.readFileSync(skillFile, "utf8");
|
|
6886
|
+
} catch {
|
|
6887
|
+
existing = null;
|
|
6888
|
+
}
|
|
6889
|
+
if (existing === rendered2) {
|
|
6890
|
+
state[detail.name] = {
|
|
6891
|
+
uid: detail.uid,
|
|
6892
|
+
content_hash: detail.content_hash,
|
|
6893
|
+
file_sha256: sha256(rendered2)
|
|
6894
|
+
};
|
|
6895
|
+
report.unchanged.push(detail.name);
|
|
6896
|
+
continue;
|
|
6897
|
+
}
|
|
6898
|
+
const recorded = state[detail.name];
|
|
6899
|
+
const locallyModified = existing !== null && (!recorded || sha256(existing) !== recorded.file_sha256);
|
|
6900
|
+
if (locallyModified) {
|
|
6901
|
+
if (recorded && recorded.content_hash === detail.content_hash) {
|
|
6902
|
+
report.locallyEdited.push(detail.name);
|
|
6903
|
+
continue;
|
|
6904
|
+
}
|
|
6905
|
+
fs2.writeFileSync(`${skillFile}.conflict`, rendered2);
|
|
6906
|
+
report.conflicts.push(detail.name);
|
|
6907
|
+
continue;
|
|
6908
|
+
}
|
|
6909
|
+
fs2.mkdirSync(skillDir, { recursive: true });
|
|
6910
|
+
fs2.writeFileSync(skillFile, rendered2);
|
|
6911
|
+
state[detail.name] = {
|
|
6912
|
+
uid: detail.uid,
|
|
6913
|
+
content_hash: detail.content_hash,
|
|
6914
|
+
file_sha256: sha256(rendered2)
|
|
6915
|
+
};
|
|
6916
|
+
report.written.push(detail.name);
|
|
6917
|
+
}
|
|
6918
|
+
for (const [name, recorded] of Object.entries(state)) {
|
|
6919
|
+
if (fetched.has(name)) continue;
|
|
6920
|
+
const paths = safeSkillPaths(dir, name);
|
|
6921
|
+
if (typeof paths === "string") {
|
|
6922
|
+
delete state[name];
|
|
6923
|
+
continue;
|
|
6924
|
+
}
|
|
6925
|
+
const { skillDir, skillFile } = paths;
|
|
6926
|
+
let statusReason = null;
|
|
6927
|
+
try {
|
|
6928
|
+
const gone = await api.get(recorded.uid);
|
|
6929
|
+
statusReason = gone?.status_reason ?? (gone ? `status: ${gone.status}` : null);
|
|
6930
|
+
} catch {
|
|
6931
|
+
statusReason = null;
|
|
6932
|
+
}
|
|
6933
|
+
let existing = null;
|
|
6934
|
+
try {
|
|
6935
|
+
existing = fs2.readFileSync(skillFile, "utf8");
|
|
6936
|
+
} catch {
|
|
6937
|
+
existing = null;
|
|
6938
|
+
}
|
|
6939
|
+
if (existing === null) {
|
|
6940
|
+
delete state[name];
|
|
6941
|
+
continue;
|
|
6942
|
+
}
|
|
6943
|
+
const locallyModified = sha256(existing) !== recorded.file_sha256;
|
|
6944
|
+
if (locallyModified) {
|
|
6945
|
+
fs2.renameSync(skillFile, `${skillFile}.conflict`);
|
|
6946
|
+
report.removed.push({ name, statusReason, kept: true });
|
|
6947
|
+
} else {
|
|
6948
|
+
fs2.rmSync(skillDir, { recursive: true, force: true });
|
|
6949
|
+
report.removed.push({ name, statusReason, kept: false });
|
|
6950
|
+
}
|
|
6951
|
+
delete state[name];
|
|
6952
|
+
}
|
|
6953
|
+
saveState(dir, state);
|
|
6954
|
+
return report;
|
|
6955
|
+
}
|
|
6956
|
+
async function skillsPush(api, skillPath) {
|
|
6957
|
+
const resolved = path2.resolve(skillPath);
|
|
6958
|
+
const file = resolved.endsWith("SKILL.md") ? resolved : path2.join(resolved, "SKILL.md");
|
|
6959
|
+
if (isSymlink(file)) {
|
|
6960
|
+
return { outcome: "error", message: `${file} is a symlink; refusing` };
|
|
6961
|
+
}
|
|
6962
|
+
let text;
|
|
6963
|
+
try {
|
|
6964
|
+
text = fs2.readFileSync(file, "utf8");
|
|
6965
|
+
} catch {
|
|
6966
|
+
return { outcome: "error", message: `could not read ${file}` };
|
|
6967
|
+
}
|
|
6968
|
+
const parsed = parseSkillMd(text);
|
|
6969
|
+
if (!parsed) {
|
|
6970
|
+
return {
|
|
6971
|
+
outcome: "error",
|
|
6972
|
+
message: "SKILL.md must start with a --- frontmatter block"
|
|
6973
|
+
};
|
|
6974
|
+
}
|
|
6975
|
+
if (!parsed.name || !parsed.description) {
|
|
6976
|
+
return {
|
|
6977
|
+
outcome: "error",
|
|
6978
|
+
message: "SKILL.md frontmatter requires name and description"
|
|
6979
|
+
};
|
|
6980
|
+
}
|
|
6981
|
+
if (parsed.pin?.uid) {
|
|
6982
|
+
const { status: status2, body: body2 } = await api.send({
|
|
6983
|
+
action: "update",
|
|
6984
|
+
uid: parsed.pin.uid,
|
|
6985
|
+
name: parsed.name,
|
|
6986
|
+
description: parsed.description,
|
|
6987
|
+
content: parsed.body,
|
|
6988
|
+
...parsed.license ? { license: parsed.license } : {}
|
|
6989
|
+
});
|
|
6990
|
+
if (status2 === 200) {
|
|
6991
|
+
return {
|
|
6992
|
+
outcome: "updated",
|
|
6993
|
+
message: `updated candidate ${parsed.name} (${parsed.pin.uid})`,
|
|
6994
|
+
uid: parsed.pin.uid
|
|
6995
|
+
};
|
|
6996
|
+
}
|
|
6997
|
+
return {
|
|
6998
|
+
outcome: status2 === 409 ? "conflict" : "error",
|
|
6999
|
+
message: `update failed (HTTP ${status2}): ${String(body2.error ?? "")}`,
|
|
7000
|
+
existingUid: body2.existing_uid ?? void 0,
|
|
7001
|
+
existingStatus: body2.existing_status ?? void 0
|
|
7002
|
+
};
|
|
7003
|
+
}
|
|
7004
|
+
const { status, body } = await api.send({
|
|
7005
|
+
action: "create",
|
|
7006
|
+
skill_source: "imported",
|
|
7007
|
+
name: parsed.name,
|
|
7008
|
+
description: parsed.description,
|
|
7009
|
+
content: parsed.body,
|
|
7010
|
+
...parsed.license ? { license: parsed.license } : {},
|
|
7011
|
+
...Object.keys(parsed.unknown).length > 0 ? { imported_frontmatter: parsed.unknown } : {},
|
|
7012
|
+
import_origin: `skills-push:${path2.basename(resolved)}`
|
|
7013
|
+
});
|
|
7014
|
+
if (status === 200 && body.replayed === true) {
|
|
7015
|
+
return {
|
|
7016
|
+
outcome: "replayed",
|
|
7017
|
+
message: `unchanged: ${parsed.name} already imported (${String(body.uid)})`,
|
|
7018
|
+
uid: String(body.uid)
|
|
7019
|
+
};
|
|
7020
|
+
}
|
|
7021
|
+
if (status === 200) {
|
|
7022
|
+
return {
|
|
7023
|
+
outcome: "created",
|
|
7024
|
+
message: `imported ${parsed.name} as a candidate (${String(body.uid)}) \u2014 review and publish in the dashboard`,
|
|
7025
|
+
uid: String(body.uid)
|
|
7026
|
+
};
|
|
7027
|
+
}
|
|
7028
|
+
return {
|
|
7029
|
+
outcome: status === 409 ? "conflict" : "error",
|
|
7030
|
+
message: `import failed (HTTP ${status}): ${String(body.error ?? "")}`,
|
|
7031
|
+
existingUid: body.existing_uid ?? void 0,
|
|
7032
|
+
existingStatus: body.existing_status ?? void 0
|
|
7033
|
+
};
|
|
7034
|
+
}
|
|
7035
|
+
|
|
6449
7036
|
// src/hook-core.ts
|
|
6450
|
-
var
|
|
7037
|
+
var import_node_crypto4 = __toESM(require("crypto"));
|
|
6451
7038
|
var import_node_fs3 = __toESM(require("fs"));
|
|
6452
7039
|
var import_node_os3 = __toESM(require("os"));
|
|
6453
7040
|
var import_node_path3 = __toESM(require("path"));
|
|
@@ -6477,7 +7064,7 @@ function runtimeDir(options) {
|
|
|
6477
7064
|
}
|
|
6478
7065
|
function ledgerPath(sessionId, options) {
|
|
6479
7066
|
const harness = "harness" in options ? options.harness : "claude-code";
|
|
6480
|
-
const key =
|
|
7067
|
+
const key = import_node_crypto4.default.createHash("sha256").update(`${harness}\0${sessionId}`).digest("hex");
|
|
6481
7068
|
return import_node_path3.default.join(runtimeDir(options), `${key}.json`);
|
|
6482
7069
|
}
|
|
6483
7070
|
function waitBriefly(ms) {
|
|
@@ -6519,7 +7106,7 @@ function withLedger(sessionId, options, update) {
|
|
|
6519
7106
|
}
|
|
6520
7107
|
const result = update(ledger);
|
|
6521
7108
|
ledger.updatedAt = Date.now();
|
|
6522
|
-
const temporary = `${file}.${process.pid}.${
|
|
7109
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
6523
7110
|
import_node_fs3.default.writeFileSync(temporary, `${JSON.stringify(ledger)}
|
|
6524
7111
|
`, { mode: 384 });
|
|
6525
7112
|
import_node_fs3.default.renameSync(temporary, file);
|
|
@@ -6927,7 +7514,7 @@ async function sessionStart(input, client2, options) {
|
|
|
6927
7514
|
const contextText = registrationNote ? `${registrationNote}
|
|
6928
7515
|
|
|
6929
7516
|
${briefText}` : briefText;
|
|
6930
|
-
const briefHash =
|
|
7517
|
+
const briefHash = import_node_crypto4.default.createHash("sha256").update(contextText).digest("hex");
|
|
6931
7518
|
const shouldInject = withLedger(input.session_id, options, (ledger) => {
|
|
6932
7519
|
const changed = ledger.lastBriefHash !== briefHash;
|
|
6933
7520
|
const reinjectCompact = options.reinjectUnchangedCompact && input.source === "compact";
|
|
@@ -7026,7 +7613,7 @@ async function postTool(input, client2, options) {
|
|
|
7026
7613
|
ledger.mutatingOperations += 1;
|
|
7027
7614
|
}
|
|
7028
7615
|
if (!payload?.error) {
|
|
7029
|
-
if (name.includes("mindgraph_capture") && ["lesson", "journal"].includes(action || "") || name.includes("mindgraph_commit") && action === "resolve_decision" || name.includes("mindgraph_plan") && action === "assess_risk") {
|
|
7616
|
+
if (name.includes("mindgraph_capture") && ["lesson", "journal", "skill"].includes(action || "") || name.includes("mindgraph_commit") && action === "resolve_decision" || name.includes("mindgraph_plan") && action === "assess_risk") {
|
|
7030
7617
|
ledger.memoryWritten = true;
|
|
7031
7618
|
}
|
|
7032
7619
|
}
|
|
@@ -7309,15 +7896,15 @@ async function runCodexHook(input, client2, options = {}) {
|
|
|
7309
7896
|
init_hook_installer();
|
|
7310
7897
|
function recordHookHealth(harness, error) {
|
|
7311
7898
|
try {
|
|
7312
|
-
const dir = process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
7313
|
-
|
|
7314
|
-
const file =
|
|
7899
|
+
const dir = process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime");
|
|
7900
|
+
fs6.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
7901
|
+
const file = path7.join(dir, "hook-health.json");
|
|
7315
7902
|
let failures = 0;
|
|
7316
7903
|
try {
|
|
7317
|
-
failures = Number(JSON.parse(
|
|
7904
|
+
failures = Number(JSON.parse(fs6.readFileSync(file, "utf8")).consecutiveFailures) || 0;
|
|
7318
7905
|
} catch {
|
|
7319
7906
|
}
|
|
7320
|
-
|
|
7907
|
+
fs6.writeFileSync(
|
|
7321
7908
|
file,
|
|
7322
7909
|
`${JSON.stringify({
|
|
7323
7910
|
harness,
|
|
@@ -7333,14 +7920,14 @@ function recordHookHealth(harness, error) {
|
|
|
7333
7920
|
}
|
|
7334
7921
|
function clearHookHealth(harness) {
|
|
7335
7922
|
try {
|
|
7336
|
-
const file =
|
|
7337
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
7923
|
+
const file = path7.join(
|
|
7924
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
7338
7925
|
"hook-health.json"
|
|
7339
7926
|
);
|
|
7340
|
-
if (!
|
|
7341
|
-
const current = JSON.parse(
|
|
7927
|
+
if (!fs6.existsSync(file)) return;
|
|
7928
|
+
const current = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
7342
7929
|
if (!Number(current.consecutiveFailures)) return;
|
|
7343
|
-
|
|
7930
|
+
fs6.writeFileSync(
|
|
7344
7931
|
file,
|
|
7345
7932
|
`${JSON.stringify({
|
|
7346
7933
|
harness,
|
|
@@ -7356,7 +7943,7 @@ function clearHookHealth(harness) {
|
|
|
7356
7943
|
function getClaudeDesktopConfigPath() {
|
|
7357
7944
|
switch (process.platform) {
|
|
7358
7945
|
case "darwin":
|
|
7359
|
-
return
|
|
7946
|
+
return path7.join(
|
|
7360
7947
|
os4.homedir(),
|
|
7361
7948
|
"Library",
|
|
7362
7949
|
"Application Support",
|
|
@@ -7364,13 +7951,13 @@ function getClaudeDesktopConfigPath() {
|
|
|
7364
7951
|
"claude_desktop_config.json"
|
|
7365
7952
|
);
|
|
7366
7953
|
case "win32":
|
|
7367
|
-
return
|
|
7368
|
-
process.env.APPDATA ||
|
|
7954
|
+
return path7.join(
|
|
7955
|
+
process.env.APPDATA || path7.join(os4.homedir(), "AppData", "Roaming"),
|
|
7369
7956
|
"Claude",
|
|
7370
7957
|
"claude_desktop_config.json"
|
|
7371
7958
|
);
|
|
7372
7959
|
case "linux":
|
|
7373
|
-
return
|
|
7960
|
+
return path7.join(
|
|
7374
7961
|
os4.homedir(),
|
|
7375
7962
|
".config",
|
|
7376
7963
|
"Claude",
|
|
@@ -7381,16 +7968,16 @@ function getClaudeDesktopConfigPath() {
|
|
|
7381
7968
|
}
|
|
7382
7969
|
}
|
|
7383
7970
|
function readJsonFile(filePath) {
|
|
7384
|
-
if (!
|
|
7385
|
-
const content =
|
|
7971
|
+
if (!fs6.existsSync(filePath)) return {};
|
|
7972
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
7386
7973
|
return JSON.parse(content);
|
|
7387
7974
|
}
|
|
7388
7975
|
function writeJsonFile(filePath, data) {
|
|
7389
|
-
const dir =
|
|
7390
|
-
if (!
|
|
7391
|
-
|
|
7976
|
+
const dir = path7.dirname(filePath);
|
|
7977
|
+
if (!fs6.existsSync(dir)) {
|
|
7978
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
7392
7979
|
}
|
|
7393
|
-
|
|
7980
|
+
fs6.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
7394
7981
|
}
|
|
7395
7982
|
function getMcpServerEntry(apiKey, baseUrl) {
|
|
7396
7983
|
const env = { MINDGRAPH_API_KEY: apiKey };
|
|
@@ -7477,7 +8064,7 @@ function installClaudeCode(apiKey, baseUrl, agentId) {
|
|
|
7477
8064
|
}
|
|
7478
8065
|
function uninstallClaudeDesktop() {
|
|
7479
8066
|
const configPath = getClaudeDesktopConfigPath();
|
|
7480
|
-
if (!
|
|
8067
|
+
if (!fs6.existsSync(configPath)) {
|
|
7481
8068
|
console.log("Claude Desktop config not found. Nothing to remove.");
|
|
7482
8069
|
return;
|
|
7483
8070
|
}
|
|
@@ -7504,7 +8091,7 @@ function uninstallClaudeCode() {
|
|
|
7504
8091
|
}
|
|
7505
8092
|
function printStatus() {
|
|
7506
8093
|
const desktopPath = getClaudeDesktopConfigPath();
|
|
7507
|
-
if (
|
|
8094
|
+
if (fs6.existsSync(desktopPath)) {
|
|
7508
8095
|
const config = readJsonFile(desktopPath);
|
|
7509
8096
|
const servers = config.mcpServers;
|
|
7510
8097
|
if (servers && "mindgraph" in servers) {
|
|
@@ -7522,12 +8109,12 @@ function printStatus() {
|
|
|
7522
8109
|
`MINDGRAPH_BASE_URL: ${process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud (default)"}`
|
|
7523
8110
|
);
|
|
7524
8111
|
try {
|
|
7525
|
-
const healthFile =
|
|
7526
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
8112
|
+
const healthFile = path7.join(
|
|
8113
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
7527
8114
|
"hook-health.json"
|
|
7528
8115
|
);
|
|
7529
|
-
if (
|
|
7530
|
-
const health = JSON.parse(
|
|
8116
|
+
if (fs6.existsSync(healthFile)) {
|
|
8117
|
+
const health = JSON.parse(fs6.readFileSync(healthFile, "utf8"));
|
|
7531
8118
|
const failures = Number(health.consecutiveFailures) || 0;
|
|
7532
8119
|
console.log(
|
|
7533
8120
|
failures > 0 ? `Hooks: FAILING (${failures} consecutive; last: ${health.lastError} at ${health.lastFailureAt})` : `Hooks: healthy (last recovery ${health.lastOkAt || "unknown"})`
|
|
@@ -7544,7 +8131,7 @@ function withHooksFlag() {
|
|
|
7544
8131
|
}
|
|
7545
8132
|
function finishCodeInstall(apiKey, baseUrl, withHooks, agentId) {
|
|
7546
8133
|
if (withHooks) {
|
|
7547
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
8134
|
+
const runner = installHookRunner(__filename, void 0, "0.19.0");
|
|
7548
8135
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
7549
8136
|
const result = installClaudeHooks("user");
|
|
7550
8137
|
console.log(
|
|
@@ -7592,6 +8179,20 @@ USAGE:
|
|
|
7592
8179
|
mindgraph-mcp install-hooks --harness claude-code|codex [--scope user|project]
|
|
7593
8180
|
mindgraph-mcp uninstall-hooks --harness claude-code|codex [--scope user|project]
|
|
7594
8181
|
mindgraph-mcp status Show installation status
|
|
8182
|
+
mindgraph-mcp skills pull [--dir .claude/skills]
|
|
8183
|
+
Write your org's published+granted skills as
|
|
8184
|
+
SKILL.md directories (allowlisted frontmatter
|
|
8185
|
+
only). Revoked/archived skills are withdrawn;
|
|
8186
|
+
local edits are never overwritten (.conflict
|
|
8187
|
+
sidecars). NOTE: coding harnesses hot-reload
|
|
8188
|
+
skill files \u2014 running sessions adopt pulled
|
|
8189
|
+
changes immediately; session pinning exists
|
|
8190
|
+
on the managed-agent leg only.
|
|
8191
|
+
mindgraph-mcp skills push <path>
|
|
8192
|
+
Import a SKILL.md directory as a review
|
|
8193
|
+
candidate (nothing auto-publishes). A
|
|
8194
|
+
previously pulled directory routes to the
|
|
8195
|
+
candidate-only update path.
|
|
7595
8196
|
|
|
7596
8197
|
OPTIONS:
|
|
7597
8198
|
--api-key <key> MindGraph API key (or set MINDGRAPH_API_KEY env var)
|
|
@@ -7655,7 +8256,7 @@ async function pollForKey(code) {
|
|
|
7655
8256
|
} catch (e) {
|
|
7656
8257
|
if (e instanceof Error && e.message.includes("Session")) throw e;
|
|
7657
8258
|
}
|
|
7658
|
-
await new Promise((
|
|
8259
|
+
await new Promise((resolve3) => setTimeout(resolve3, POLL_INTERVAL));
|
|
7659
8260
|
}
|
|
7660
8261
|
throw new Error("Timed out waiting for authorization.");
|
|
7661
8262
|
}
|
|
@@ -7665,7 +8266,7 @@ async function interactiveInit(baseUrl) {
|
|
|
7665
8266
|
input: process.stdin,
|
|
7666
8267
|
output: process.stdout
|
|
7667
8268
|
});
|
|
7668
|
-
const ask = (q) => new Promise((
|
|
8269
|
+
const ask = (q) => new Promise((resolve3) => rl.question(q, resolve3));
|
|
7669
8270
|
console.log("\nMindGraph MCP Server Setup\n");
|
|
7670
8271
|
console.log(" 1. Sign in via browser (recommended)");
|
|
7671
8272
|
console.log(" 2. Enter API key manually");
|
|
@@ -7736,8 +8337,79 @@ async function interactiveInit(baseUrl) {
|
|
|
7736
8337
|
console.log("\nDone! Your knowledge graph is now connected.");
|
|
7737
8338
|
}
|
|
7738
8339
|
async function main2() {
|
|
7739
|
-
const {
|
|
8340
|
+
const {
|
|
8341
|
+
command: command2,
|
|
8342
|
+
apiKey,
|
|
8343
|
+
baseUrl,
|
|
8344
|
+
scope,
|
|
8345
|
+
projectDir,
|
|
8346
|
+
harness,
|
|
8347
|
+
hooks,
|
|
8348
|
+
agentId,
|
|
8349
|
+
positionals,
|
|
8350
|
+
dir
|
|
8351
|
+
} = parseArgs(process.argv);
|
|
7740
8352
|
switch (command2) {
|
|
8353
|
+
case "skills": {
|
|
8354
|
+
if (!apiKey) {
|
|
8355
|
+
console.error(
|
|
8356
|
+
"API key required. Use --api-key or set MINDGRAPH_API_KEY."
|
|
8357
|
+
);
|
|
8358
|
+
process.exit(1);
|
|
8359
|
+
}
|
|
8360
|
+
const sub = positionals[0];
|
|
8361
|
+
const api = httpSkillsApi(
|
|
8362
|
+
baseUrl ?? "https://api.mindgraph.cloud",
|
|
8363
|
+
apiKey,
|
|
8364
|
+
agentId
|
|
8365
|
+
);
|
|
8366
|
+
if (sub === "pull") {
|
|
8367
|
+
if (!agentId) {
|
|
8368
|
+
console.error(
|
|
8369
|
+
"note: no --agent-id/MINDGRAPH_AGENT_ID \u2014 pulling ALL published skills; bind an agent identity to scope the set by its grants."
|
|
8370
|
+
);
|
|
8371
|
+
}
|
|
8372
|
+
const report = await skillsPull(api, dir ?? ".claude/skills", agentId);
|
|
8373
|
+
for (const name of report.written) console.log(` written ${name}`);
|
|
8374
|
+
for (const name of report.unchanged)
|
|
8375
|
+
console.log(` unchanged ${name}`);
|
|
8376
|
+
for (const name of report.locallyEdited)
|
|
8377
|
+
console.log(` local-edit ${name} \u2014 upstream unchanged; left alone`);
|
|
8378
|
+
for (const name of report.conflicts)
|
|
8379
|
+
console.log(
|
|
8380
|
+
` CONFLICT ${name} \u2014 local edits kept; new upstream saved as SKILL.md.conflict`
|
|
8381
|
+
);
|
|
8382
|
+
for (const removal of report.removed)
|
|
8383
|
+
console.log(
|
|
8384
|
+
` withdrawn ${removal.name}${removal.kept ? " (local edits kept as SKILL.md.conflict)" : ""}${removal.statusReason ? ` \u2014 ${removal.statusReason}` : ""}`
|
|
8385
|
+
);
|
|
8386
|
+
for (const refusal of report.refused)
|
|
8387
|
+
console.log(` REFUSED ${refusal.name} \u2014 ${refusal.reason}`);
|
|
8388
|
+
const total = report.written.length + report.unchanged.length + report.locallyEdited.length + report.conflicts.length;
|
|
8389
|
+
console.log(`
|
|
8390
|
+
${total} skill(s) synced.`);
|
|
8391
|
+
} else if (sub === "push") {
|
|
8392
|
+
const target = positionals[1];
|
|
8393
|
+
if (!target) {
|
|
8394
|
+
console.error("Usage: mindgraph-mcp skills push <path>");
|
|
8395
|
+
process.exit(1);
|
|
8396
|
+
}
|
|
8397
|
+
const report = await skillsPush(api, target);
|
|
8398
|
+
console.log(report.message);
|
|
8399
|
+
if (report.existingUid) {
|
|
8400
|
+
console.log(
|
|
8401
|
+
` existing skill: ${report.existingUid}${report.existingStatus ? ` (${report.existingStatus})` : ""}`
|
|
8402
|
+
);
|
|
8403
|
+
}
|
|
8404
|
+
if (report.outcome === "error" || report.outcome === "conflict") {
|
|
8405
|
+
process.exit(1);
|
|
8406
|
+
}
|
|
8407
|
+
} else {
|
|
8408
|
+
console.error("Usage: mindgraph-mcp skills pull|push [...]");
|
|
8409
|
+
process.exit(1);
|
|
8410
|
+
}
|
|
8411
|
+
break;
|
|
8412
|
+
}
|
|
7741
8413
|
case "help":
|
|
7742
8414
|
printUsage();
|
|
7743
8415
|
break;
|
|
@@ -7774,7 +8446,7 @@ async function main2() {
|
|
|
7774
8446
|
if (hookHarness !== "claude-code" && hookHarness !== "codex") {
|
|
7775
8447
|
throw new Error("--harness must be claude-code or codex");
|
|
7776
8448
|
}
|
|
7777
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
8449
|
+
const runner = installHookRunner(__filename, void 0, "0.19.0");
|
|
7778
8450
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
7779
8451
|
const result = hookHarness === "codex" ? installCodexHooks(scope, projectDir) : installClaudeHooks(scope, projectDir);
|
|
7780
8452
|
console.log(
|