claudemesh-cli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +660 -27
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46250,8 +46250,11 @@ var TOOLS = [
|
|
|
46250
46250
|
type: "object",
|
|
46251
46251
|
properties: {
|
|
46252
46252
|
to: {
|
|
46253
|
-
|
|
46254
|
-
|
|
46253
|
+
oneOf: [
|
|
46254
|
+
{ type: "string", description: "Peer name, pubkey, @group" },
|
|
46255
|
+
{ type: "array", items: { type: "string" }, description: "Multiple targets" }
|
|
46256
|
+
],
|
|
46257
|
+
description: "Single target or array of targets"
|
|
46255
46258
|
},
|
|
46256
46259
|
message: { type: "string", description: "Message text" },
|
|
46257
46260
|
priority: {
|
|
@@ -46276,6 +46279,20 @@ var TOOLS = [
|
|
|
46276
46279
|
}
|
|
46277
46280
|
}
|
|
46278
46281
|
},
|
|
46282
|
+
{
|
|
46283
|
+
name: "message_status",
|
|
46284
|
+
description: "Check the delivery status of a sent message. Shows whether each recipient received it.",
|
|
46285
|
+
inputSchema: {
|
|
46286
|
+
type: "object",
|
|
46287
|
+
properties: {
|
|
46288
|
+
id: {
|
|
46289
|
+
type: "string",
|
|
46290
|
+
description: "Message ID (returned by send_message)"
|
|
46291
|
+
}
|
|
46292
|
+
},
|
|
46293
|
+
required: ["id"]
|
|
46294
|
+
}
|
|
46295
|
+
},
|
|
46279
46296
|
{
|
|
46280
46297
|
name: "check_messages",
|
|
46281
46298
|
description: "Pull any undelivered messages from the broker. Normally messages arrive via push; use this to drain the queue after being offline.",
|
|
@@ -46332,6 +46349,143 @@ var TOOLS = [
|
|
|
46332
46349
|
},
|
|
46333
46350
|
required: ["name"]
|
|
46334
46351
|
}
|
|
46352
|
+
},
|
|
46353
|
+
{
|
|
46354
|
+
name: "set_state",
|
|
46355
|
+
description: "Set a shared state value visible to all peers in the mesh. Pushes a change notification.",
|
|
46356
|
+
inputSchema: {
|
|
46357
|
+
type: "object",
|
|
46358
|
+
properties: {
|
|
46359
|
+
key: { type: "string" },
|
|
46360
|
+
value: { description: "Any JSON value" }
|
|
46361
|
+
},
|
|
46362
|
+
required: ["key", "value"]
|
|
46363
|
+
}
|
|
46364
|
+
},
|
|
46365
|
+
{
|
|
46366
|
+
name: "get_state",
|
|
46367
|
+
description: "Read a shared state value.",
|
|
46368
|
+
inputSchema: {
|
|
46369
|
+
type: "object",
|
|
46370
|
+
properties: {
|
|
46371
|
+
key: { type: "string" }
|
|
46372
|
+
},
|
|
46373
|
+
required: ["key"]
|
|
46374
|
+
}
|
|
46375
|
+
},
|
|
46376
|
+
{
|
|
46377
|
+
name: "list_state",
|
|
46378
|
+
description: "List all shared state keys and values in the mesh.",
|
|
46379
|
+
inputSchema: { type: "object", properties: {} }
|
|
46380
|
+
},
|
|
46381
|
+
{
|
|
46382
|
+
name: "remember",
|
|
46383
|
+
description: "Store persistent knowledge in the mesh's shared memory. Survives across sessions.",
|
|
46384
|
+
inputSchema: {
|
|
46385
|
+
type: "object",
|
|
46386
|
+
properties: {
|
|
46387
|
+
content: {
|
|
46388
|
+
type: "string",
|
|
46389
|
+
description: "The knowledge to remember"
|
|
46390
|
+
},
|
|
46391
|
+
tags: {
|
|
46392
|
+
type: "array",
|
|
46393
|
+
items: { type: "string" },
|
|
46394
|
+
description: "Optional categorization tags"
|
|
46395
|
+
}
|
|
46396
|
+
},
|
|
46397
|
+
required: ["content"]
|
|
46398
|
+
}
|
|
46399
|
+
},
|
|
46400
|
+
{
|
|
46401
|
+
name: "recall",
|
|
46402
|
+
description: "Search the mesh's shared memory by relevance.",
|
|
46403
|
+
inputSchema: {
|
|
46404
|
+
type: "object",
|
|
46405
|
+
properties: {
|
|
46406
|
+
query: { type: "string", description: "Search query" }
|
|
46407
|
+
},
|
|
46408
|
+
required: ["query"]
|
|
46409
|
+
}
|
|
46410
|
+
},
|
|
46411
|
+
{
|
|
46412
|
+
name: "forget",
|
|
46413
|
+
description: "Remove a memory from the mesh's shared knowledge.",
|
|
46414
|
+
inputSchema: {
|
|
46415
|
+
type: "object",
|
|
46416
|
+
properties: {
|
|
46417
|
+
id: { type: "string", description: "Memory ID to forget" }
|
|
46418
|
+
},
|
|
46419
|
+
required: ["id"]
|
|
46420
|
+
}
|
|
46421
|
+
},
|
|
46422
|
+
{
|
|
46423
|
+
name: "share_file",
|
|
46424
|
+
description: "Share a persistent file with the mesh. All current and future peers can access it.",
|
|
46425
|
+
inputSchema: {
|
|
46426
|
+
type: "object",
|
|
46427
|
+
properties: {
|
|
46428
|
+
path: { type: "string", description: "Local file path to share" },
|
|
46429
|
+
name: {
|
|
46430
|
+
type: "string",
|
|
46431
|
+
description: "Display name (defaults to filename)"
|
|
46432
|
+
},
|
|
46433
|
+
tags: {
|
|
46434
|
+
type: "array",
|
|
46435
|
+
items: { type: "string" },
|
|
46436
|
+
description: "Tags for categorization"
|
|
46437
|
+
}
|
|
46438
|
+
},
|
|
46439
|
+
required: ["path"]
|
|
46440
|
+
}
|
|
46441
|
+
},
|
|
46442
|
+
{
|
|
46443
|
+
name: "get_file",
|
|
46444
|
+
description: "Download a shared file to a local path.",
|
|
46445
|
+
inputSchema: {
|
|
46446
|
+
type: "object",
|
|
46447
|
+
properties: {
|
|
46448
|
+
id: { type: "string", description: "File ID" },
|
|
46449
|
+
save_to: {
|
|
46450
|
+
type: "string",
|
|
46451
|
+
description: "Local path to save the file"
|
|
46452
|
+
}
|
|
46453
|
+
},
|
|
46454
|
+
required: ["id", "save_to"]
|
|
46455
|
+
}
|
|
46456
|
+
},
|
|
46457
|
+
{
|
|
46458
|
+
name: "list_files",
|
|
46459
|
+
description: "List files shared in the mesh.",
|
|
46460
|
+
inputSchema: {
|
|
46461
|
+
type: "object",
|
|
46462
|
+
properties: {
|
|
46463
|
+
query: { type: "string", description: "Search by name or tags" },
|
|
46464
|
+
from: { type: "string", description: "Filter by uploader name" }
|
|
46465
|
+
}
|
|
46466
|
+
}
|
|
46467
|
+
},
|
|
46468
|
+
{
|
|
46469
|
+
name: "file_status",
|
|
46470
|
+
description: "Check who has accessed a shared file.",
|
|
46471
|
+
inputSchema: {
|
|
46472
|
+
type: "object",
|
|
46473
|
+
properties: {
|
|
46474
|
+
id: { type: "string", description: "File ID" }
|
|
46475
|
+
},
|
|
46476
|
+
required: ["id"]
|
|
46477
|
+
}
|
|
46478
|
+
},
|
|
46479
|
+
{
|
|
46480
|
+
name: "delete_file",
|
|
46481
|
+
description: "Remove a shared file from the mesh.",
|
|
46482
|
+
inputSchema: {
|
|
46483
|
+
type: "object",
|
|
46484
|
+
properties: {
|
|
46485
|
+
id: { type: "string", description: "File ID" }
|
|
46486
|
+
},
|
|
46487
|
+
required: ["id"]
|
|
46488
|
+
}
|
|
46335
46489
|
}
|
|
46336
46490
|
];
|
|
46337
46491
|
|
|
@@ -46425,6 +46579,11 @@ class BrokerClient {
|
|
|
46425
46579
|
pushHandlers = new Set;
|
|
46426
46580
|
pushBuffer = [];
|
|
46427
46581
|
listPeersResolvers = [];
|
|
46582
|
+
stateResolvers = [];
|
|
46583
|
+
stateListResolvers = [];
|
|
46584
|
+
memoryStoreResolvers = [];
|
|
46585
|
+
memoryRecallResolvers = [];
|
|
46586
|
+
stateChangeHandlers = new Set;
|
|
46428
46587
|
sessionPubkey = null;
|
|
46429
46588
|
sessionSecretKey = null;
|
|
46430
46589
|
closed = false;
|
|
@@ -46626,6 +46785,172 @@ class BrokerClient {
|
|
|
46626
46785
|
return;
|
|
46627
46786
|
this.ws.send(JSON.stringify({ type: "leave_group", name }));
|
|
46628
46787
|
}
|
|
46788
|
+
async setState(key, value) {
|
|
46789
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46790
|
+
return;
|
|
46791
|
+
this.ws.send(JSON.stringify({ type: "set_state", key, value }));
|
|
46792
|
+
}
|
|
46793
|
+
async getState(key) {
|
|
46794
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46795
|
+
return null;
|
|
46796
|
+
return new Promise((resolve) => {
|
|
46797
|
+
this.stateResolvers.push(resolve);
|
|
46798
|
+
this.ws.send(JSON.stringify({ type: "get_state", key }));
|
|
46799
|
+
setTimeout(() => {
|
|
46800
|
+
const idx = this.stateResolvers.indexOf(resolve);
|
|
46801
|
+
if (idx !== -1) {
|
|
46802
|
+
this.stateResolvers.splice(idx, 1);
|
|
46803
|
+
resolve(null);
|
|
46804
|
+
}
|
|
46805
|
+
}, 5000);
|
|
46806
|
+
});
|
|
46807
|
+
}
|
|
46808
|
+
async listState() {
|
|
46809
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46810
|
+
return [];
|
|
46811
|
+
return new Promise((resolve) => {
|
|
46812
|
+
this.stateListResolvers.push(resolve);
|
|
46813
|
+
this.ws.send(JSON.stringify({ type: "list_state" }));
|
|
46814
|
+
setTimeout(() => {
|
|
46815
|
+
const idx = this.stateListResolvers.indexOf(resolve);
|
|
46816
|
+
if (idx !== -1) {
|
|
46817
|
+
this.stateListResolvers.splice(idx, 1);
|
|
46818
|
+
resolve([]);
|
|
46819
|
+
}
|
|
46820
|
+
}, 5000);
|
|
46821
|
+
});
|
|
46822
|
+
}
|
|
46823
|
+
async remember(content, tags) {
|
|
46824
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46825
|
+
return null;
|
|
46826
|
+
return new Promise((resolve) => {
|
|
46827
|
+
this.memoryStoreResolvers.push(resolve);
|
|
46828
|
+
this.ws.send(JSON.stringify({ type: "remember", content, tags }));
|
|
46829
|
+
setTimeout(() => {
|
|
46830
|
+
const idx = this.memoryStoreResolvers.indexOf(resolve);
|
|
46831
|
+
if (idx !== -1) {
|
|
46832
|
+
this.memoryStoreResolvers.splice(idx, 1);
|
|
46833
|
+
resolve(null);
|
|
46834
|
+
}
|
|
46835
|
+
}, 5000);
|
|
46836
|
+
});
|
|
46837
|
+
}
|
|
46838
|
+
async recall(query) {
|
|
46839
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46840
|
+
return [];
|
|
46841
|
+
return new Promise((resolve) => {
|
|
46842
|
+
this.memoryRecallResolvers.push(resolve);
|
|
46843
|
+
this.ws.send(JSON.stringify({ type: "recall", query }));
|
|
46844
|
+
setTimeout(() => {
|
|
46845
|
+
const idx = this.memoryRecallResolvers.indexOf(resolve);
|
|
46846
|
+
if (idx !== -1) {
|
|
46847
|
+
this.memoryRecallResolvers.splice(idx, 1);
|
|
46848
|
+
resolve([]);
|
|
46849
|
+
}
|
|
46850
|
+
}, 5000);
|
|
46851
|
+
});
|
|
46852
|
+
}
|
|
46853
|
+
async forget(memoryId) {
|
|
46854
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46855
|
+
return;
|
|
46856
|
+
this.ws.send(JSON.stringify({ type: "forget", memoryId }));
|
|
46857
|
+
}
|
|
46858
|
+
messageStatusResolvers = [];
|
|
46859
|
+
fileUrlResolvers = [];
|
|
46860
|
+
fileListResolvers = [];
|
|
46861
|
+
fileStatusResolvers = [];
|
|
46862
|
+
async messageStatus(messageId) {
|
|
46863
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46864
|
+
return null;
|
|
46865
|
+
return new Promise((resolve) => {
|
|
46866
|
+
this.messageStatusResolvers.push(resolve);
|
|
46867
|
+
this.ws.send(JSON.stringify({ type: "message_status", messageId }));
|
|
46868
|
+
setTimeout(() => {
|
|
46869
|
+
const idx = this.messageStatusResolvers.indexOf(resolve);
|
|
46870
|
+
if (idx !== -1) {
|
|
46871
|
+
this.messageStatusResolvers.splice(idx, 1);
|
|
46872
|
+
resolve(null);
|
|
46873
|
+
}
|
|
46874
|
+
}, 5000);
|
|
46875
|
+
});
|
|
46876
|
+
}
|
|
46877
|
+
async getFile(fileId) {
|
|
46878
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46879
|
+
return null;
|
|
46880
|
+
return new Promise((resolve) => {
|
|
46881
|
+
this.fileUrlResolvers.push(resolve);
|
|
46882
|
+
this.ws.send(JSON.stringify({ type: "get_file", fileId }));
|
|
46883
|
+
setTimeout(() => {
|
|
46884
|
+
const idx = this.fileUrlResolvers.indexOf(resolve);
|
|
46885
|
+
if (idx !== -1) {
|
|
46886
|
+
this.fileUrlResolvers.splice(idx, 1);
|
|
46887
|
+
resolve(null);
|
|
46888
|
+
}
|
|
46889
|
+
}, 5000);
|
|
46890
|
+
});
|
|
46891
|
+
}
|
|
46892
|
+
async listFiles(query, from) {
|
|
46893
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46894
|
+
return [];
|
|
46895
|
+
return new Promise((resolve) => {
|
|
46896
|
+
this.fileListResolvers.push(resolve);
|
|
46897
|
+
this.ws.send(JSON.stringify({ type: "list_files", query, from }));
|
|
46898
|
+
setTimeout(() => {
|
|
46899
|
+
const idx = this.fileListResolvers.indexOf(resolve);
|
|
46900
|
+
if (idx !== -1) {
|
|
46901
|
+
this.fileListResolvers.splice(idx, 1);
|
|
46902
|
+
resolve([]);
|
|
46903
|
+
}
|
|
46904
|
+
}, 5000);
|
|
46905
|
+
});
|
|
46906
|
+
}
|
|
46907
|
+
async fileStatus(fileId) {
|
|
46908
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46909
|
+
return [];
|
|
46910
|
+
return new Promise((resolve) => {
|
|
46911
|
+
this.fileStatusResolvers.push(resolve);
|
|
46912
|
+
this.ws.send(JSON.stringify({ type: "file_status", fileId }));
|
|
46913
|
+
setTimeout(() => {
|
|
46914
|
+
const idx = this.fileStatusResolvers.indexOf(resolve);
|
|
46915
|
+
if (idx !== -1) {
|
|
46916
|
+
this.fileStatusResolvers.splice(idx, 1);
|
|
46917
|
+
resolve([]);
|
|
46918
|
+
}
|
|
46919
|
+
}, 5000);
|
|
46920
|
+
});
|
|
46921
|
+
}
|
|
46922
|
+
async deleteFile(fileId) {
|
|
46923
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46924
|
+
return;
|
|
46925
|
+
this.ws.send(JSON.stringify({ type: "delete_file", fileId }));
|
|
46926
|
+
}
|
|
46927
|
+
async uploadFile(filePath, meshId, memberId, opts) {
|
|
46928
|
+
const { readFileSync: readFileSync2 } = await import("node:fs");
|
|
46929
|
+
const { basename } = await import("node:path");
|
|
46930
|
+
const data = readFileSync2(filePath);
|
|
46931
|
+
const fileName = opts.name ?? basename(filePath);
|
|
46932
|
+
const brokerHttp = this.mesh.brokerUrl.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
|
|
46933
|
+
const res = await fetch(`${brokerHttp}/upload`, {
|
|
46934
|
+
method: "POST",
|
|
46935
|
+
headers: {
|
|
46936
|
+
"Content-Type": "application/octet-stream",
|
|
46937
|
+
"X-Mesh-Id": meshId,
|
|
46938
|
+
"X-Member-Id": memberId,
|
|
46939
|
+
"X-File-Name": fileName,
|
|
46940
|
+
"X-Tags": JSON.stringify(opts.tags ?? []),
|
|
46941
|
+
"X-Persistent": String(opts.persistent ?? true),
|
|
46942
|
+
"X-Target-Spec": opts.targetSpec ?? ""
|
|
46943
|
+
},
|
|
46944
|
+
body: data,
|
|
46945
|
+
signal: AbortSignal.timeout(30000)
|
|
46946
|
+
});
|
|
46947
|
+
const body = await res.json();
|
|
46948
|
+
return body.fileId ?? null;
|
|
46949
|
+
}
|
|
46950
|
+
onStateChange(handler) {
|
|
46951
|
+
this.stateChangeHandlers.add(handler);
|
|
46952
|
+
return () => this.stateChangeHandlers.delete(handler);
|
|
46953
|
+
}
|
|
46629
46954
|
close() {
|
|
46630
46955
|
this.closed = true;
|
|
46631
46956
|
if (this.helloTimer)
|
|
@@ -46708,6 +47033,86 @@ class BrokerClient {
|
|
|
46708
47033
|
})();
|
|
46709
47034
|
return;
|
|
46710
47035
|
}
|
|
47036
|
+
if (msg.type === "state_result") {
|
|
47037
|
+
const resolver = this.stateResolvers.shift();
|
|
47038
|
+
if (resolver) {
|
|
47039
|
+
if (msg.key) {
|
|
47040
|
+
resolver({
|
|
47041
|
+
key: String(msg.key),
|
|
47042
|
+
value: msg.value,
|
|
47043
|
+
updatedBy: String(msg.updatedBy ?? ""),
|
|
47044
|
+
updatedAt: String(msg.updatedAt ?? "")
|
|
47045
|
+
});
|
|
47046
|
+
} else {
|
|
47047
|
+
resolver(null);
|
|
47048
|
+
}
|
|
47049
|
+
}
|
|
47050
|
+
return;
|
|
47051
|
+
}
|
|
47052
|
+
if (msg.type === "state_list") {
|
|
47053
|
+
const entries = msg.entries ?? [];
|
|
47054
|
+
const resolver = this.stateListResolvers.shift();
|
|
47055
|
+
if (resolver)
|
|
47056
|
+
resolver(entries);
|
|
47057
|
+
return;
|
|
47058
|
+
}
|
|
47059
|
+
if (msg.type === "state_change") {
|
|
47060
|
+
const change = {
|
|
47061
|
+
key: String(msg.key ?? ""),
|
|
47062
|
+
value: msg.value,
|
|
47063
|
+
updatedBy: String(msg.updatedBy ?? "")
|
|
47064
|
+
};
|
|
47065
|
+
for (const h of this.stateChangeHandlers) {
|
|
47066
|
+
try {
|
|
47067
|
+
h(change);
|
|
47068
|
+
} catch {}
|
|
47069
|
+
}
|
|
47070
|
+
return;
|
|
47071
|
+
}
|
|
47072
|
+
if (msg.type === "memory_stored") {
|
|
47073
|
+
const resolver = this.memoryStoreResolvers.shift();
|
|
47074
|
+
if (resolver)
|
|
47075
|
+
resolver(msg.id ? String(msg.id) : null);
|
|
47076
|
+
return;
|
|
47077
|
+
}
|
|
47078
|
+
if (msg.type === "memory_results") {
|
|
47079
|
+
const memories = msg.memories ?? [];
|
|
47080
|
+
const resolver = this.memoryRecallResolvers.shift();
|
|
47081
|
+
if (resolver)
|
|
47082
|
+
resolver(memories);
|
|
47083
|
+
return;
|
|
47084
|
+
}
|
|
47085
|
+
if (msg.type === "message_status_result") {
|
|
47086
|
+
const resolver = this.messageStatusResolvers.shift();
|
|
47087
|
+
if (resolver)
|
|
47088
|
+
resolver(msg);
|
|
47089
|
+
return;
|
|
47090
|
+
}
|
|
47091
|
+
if (msg.type === "file_url") {
|
|
47092
|
+
const resolver = this.fileUrlResolvers.shift();
|
|
47093
|
+
if (resolver) {
|
|
47094
|
+
if (msg.url) {
|
|
47095
|
+
resolver({ url: String(msg.url), name: String(msg.name ?? "") });
|
|
47096
|
+
} else {
|
|
47097
|
+
resolver(null);
|
|
47098
|
+
}
|
|
47099
|
+
}
|
|
47100
|
+
return;
|
|
47101
|
+
}
|
|
47102
|
+
if (msg.type === "file_list") {
|
|
47103
|
+
const files = msg.files ?? [];
|
|
47104
|
+
const resolver = this.fileListResolvers.shift();
|
|
47105
|
+
if (resolver)
|
|
47106
|
+
resolver(files);
|
|
47107
|
+
return;
|
|
47108
|
+
}
|
|
47109
|
+
if (msg.type === "file_status_result") {
|
|
47110
|
+
const accesses = msg.accesses ?? [];
|
|
47111
|
+
const resolver = this.fileStatusResolvers.shift();
|
|
47112
|
+
if (resolver)
|
|
47113
|
+
resolver(accesses);
|
|
47114
|
+
return;
|
|
47115
|
+
}
|
|
46711
47116
|
if (msg.type === "error") {
|
|
46712
47117
|
this.debug(`broker error: ${msg.code} ${msg.message}`);
|
|
46713
47118
|
const id = msg.id ? String(msg.id) : null;
|
|
@@ -46879,32 +47284,73 @@ ${body}`;
|
|
|
46879
47284
|
}
|
|
46880
47285
|
async function startMcpServer() {
|
|
46881
47286
|
const config2 = loadConfig();
|
|
46882
|
-
const
|
|
47287
|
+
const myName = config2.displayName ?? "unnamed";
|
|
47288
|
+
const myGroups = (config2.groups ?? []).map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ") || "none";
|
|
47289
|
+
const server = new Server({ name: "claudemesh", version: "0.3.0" }, {
|
|
46883
47290
|
capabilities: {
|
|
46884
47291
|
experimental: { "claude/channel": {} },
|
|
46885
47292
|
tools: {}
|
|
46886
47293
|
},
|
|
46887
|
-
instructions:
|
|
47294
|
+
instructions: `## Identity
|
|
47295
|
+
You are "${myName}" — a peer in the claudemesh network. Your groups: ${myGroups}. You are one of several Claude Code sessions connected to the same mesh. No orchestrator exists — peers are equals. Your identity comes from your name and group roles, not from a central authority.
|
|
46888
47296
|
|
|
46889
|
-
|
|
47297
|
+
## Responding to messages
|
|
47298
|
+
When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATELY. Pause your current task, reply via send_message, then resume. Read from_name, mesh_slug, and priority from the channel attributes. Reply by setting \`to\` to the sender's from_name (display name). Stay in character per your system prompt. Do not ignore low-priority messages — acknowledge them briefly even if you defer action.
|
|
46890
47299
|
|
|
46891
|
-
|
|
47300
|
+
## Tools
|
|
47301
|
+
| Tool | Description |
|
|
47302
|
+
|------|-------------|
|
|
47303
|
+
| send_message(to, message, priority?) | Send to peer name, @group, or * broadcast. \`to\` accepts display name, pubkey hex, @groupname, or *. |
|
|
47304
|
+
| list_peers(mesh_slug?) | List connected peers with status, summary, groups, and roles. |
|
|
47305
|
+
| check_messages() | Drain buffered inbound messages (auto-pushed in most cases, use as fallback). |
|
|
47306
|
+
| set_summary(summary) | Set 1-2 sentence description of your current work, visible to all peers. |
|
|
47307
|
+
| set_status(status) | Override status: idle, working, or dnd. |
|
|
47308
|
+
| join_group(name, role?) | Join a @group with optional role (lead, member, observer, or any string). |
|
|
47309
|
+
| leave_group(name) | Leave a @group. |
|
|
47310
|
+
| set_state(key, value) | Write shared state; pushes change to all peers. |
|
|
47311
|
+
| get_state(key) | Read a shared state value. |
|
|
47312
|
+
| list_state() | List all state keys with values, authors, and timestamps. |
|
|
47313
|
+
| remember(content, tags?) | Store persistent knowledge with optional tags. |
|
|
47314
|
+
| recall(query) | Full-text search over mesh memory. |
|
|
47315
|
+
| forget(id) | Soft-delete a memory entry. |
|
|
47316
|
+
| share_file(path, name?, tags?) | Share a persistent file with the mesh. |
|
|
47317
|
+
| get_file(id, save_to) | Download a shared file to a local path. |
|
|
47318
|
+
| list_files(query?, from?) | Find files shared in the mesh. |
|
|
47319
|
+
| file_status(id) | Check who has accessed a file. |
|
|
47320
|
+
| delete_file(id) | Remove a shared file from the mesh. |
|
|
46892
47321
|
|
|
46893
|
-
|
|
46894
|
-
- list_peers: see joined meshes + their connection status
|
|
46895
|
-
- send_message: send to a peer by display name, pubkey, @group, #channel, or * broadcast (priority: now/next/low)
|
|
46896
|
-
- check_messages: drain buffered inbound messages (usually auto-pushed)
|
|
46897
|
-
- set_summary: 1-2 sentence summary of what you're working on
|
|
46898
|
-
- set_status: manually override your status (idle/working/dnd)
|
|
46899
|
-
- join_group: join a @group with optional role
|
|
46900
|
-
- leave_group: leave a @group
|
|
47322
|
+
If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
|
|
46901
47323
|
|
|
46902
|
-
|
|
46903
|
-
|
|
46904
|
-
|
|
46905
|
-
- "low": pull-only (check_messages)
|
|
47324
|
+
Multi-target: send_message accepts an array of targets for the 'to' field.
|
|
47325
|
+
send_message(to: ["Alice", "@backend"], message: "sprint starts")
|
|
47326
|
+
Targets are deduplicated — each peer receives the message once.
|
|
46906
47327
|
|
|
46907
|
-
|
|
47328
|
+
Targeted views: when different audiences need different details about the same event,
|
|
47329
|
+
send tailored messages instead of one generic broadcast:
|
|
47330
|
+
send_message(to: "@frontend", message: "Auth v2: useAuth hook changed, see src/auth/")
|
|
47331
|
+
send_message(to: "@backend", message: "Auth v2: new /api/auth/v2 endpoints, v1 deprecated")
|
|
47332
|
+
send_message(to: "@pm", message: "Auth v2 done. 3 points, no blockers.")
|
|
47333
|
+
|
|
47334
|
+
## Groups
|
|
47335
|
+
Groups are routing labels. Send to @groupname to multicast to all members. Roles are metadata that peers interpret: a "lead" gathers input before synthesizing a response, a "member" contributes when asked, an "observer" watches silently. Join and leave groups dynamically with join_group/leave_group. Check list_peers to see who belongs to which groups and their roles.
|
|
47336
|
+
|
|
47337
|
+
## State
|
|
47338
|
+
Shared key-value store scoped to the mesh. Use get_state/set_state for live coordination facts (deploy frozen? current sprint? PR queue). set_state pushes the change to all connected peers. Read state before asking peers questions — the answer may already be there. State is operational, not archival.
|
|
47339
|
+
|
|
47340
|
+
## Memory
|
|
47341
|
+
Persistent knowledge that survives across sessions. Use remember(content, tags?) to store lessons, decisions, and incidents. Use recall(query) to search before asking peers. New peers should recall at session start to load institutional knowledge.
|
|
47342
|
+
|
|
47343
|
+
## Files
|
|
47344
|
+
share_file for persistent references, send_message(file:) for ephemeral attachments.
|
|
47345
|
+
Tags on shared files make them searchable. Use list_files to find what peers shared.
|
|
47346
|
+
|
|
47347
|
+
## Priority
|
|
47348
|
+
- "now": interrupt immediately, even if recipient is in DND (use for urgent: broken deploy, blocking issue)
|
|
47349
|
+
- "next" (default): deliver when recipient goes idle (normal coordination)
|
|
47350
|
+
- "low": pull-only via check_messages (FYI, non-blocking context)
|
|
47351
|
+
|
|
47352
|
+
## Coordination
|
|
47353
|
+
Call list_peers at session start to understand who is online, their roles, and what they are working on. If you are a group lead, gather input from members before responding to external requests — do not answer alone. If you are a member, contribute to your lead when asked. Use @group messages for team-wide questions, direct messages for 1:1 coordination. Set a meaningful summary so peers know your current focus.`
|
|
46908
47354
|
});
|
|
46909
47355
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
46910
47356
|
tools: TOOLS
|
|
@@ -46919,13 +47365,27 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
|
|
|
46919
47365
|
const { to, message, priority } = args ?? {};
|
|
46920
47366
|
if (!to || !message)
|
|
46921
47367
|
return text("send_message: `to` and `message` required", true);
|
|
46922
|
-
const
|
|
46923
|
-
|
|
46924
|
-
|
|
46925
|
-
|
|
46926
|
-
|
|
46927
|
-
|
|
46928
|
-
|
|
47368
|
+
const targets = Array.isArray(to) ? to : [to];
|
|
47369
|
+
const results = [];
|
|
47370
|
+
const seen = new Set;
|
|
47371
|
+
for (const target of targets) {
|
|
47372
|
+
const { client, targetSpec, error: error2 } = await resolveClient(target);
|
|
47373
|
+
if (!client) {
|
|
47374
|
+
results.push(`✗ ${target}: ${error2 ?? "no client resolved"}`);
|
|
47375
|
+
continue;
|
|
47376
|
+
}
|
|
47377
|
+
if (seen.has(targetSpec))
|
|
47378
|
+
continue;
|
|
47379
|
+
seen.add(targetSpec);
|
|
47380
|
+
const result = await client.send(targetSpec, message, priority ?? "next");
|
|
47381
|
+
if (!result.ok) {
|
|
47382
|
+
results.push(`✗ ${target}: ${result.error}`);
|
|
47383
|
+
} else {
|
|
47384
|
+
results.push(`✓ ${target} → ${result.messageId}`);
|
|
47385
|
+
}
|
|
47386
|
+
}
|
|
47387
|
+
return text(results.join(`
|
|
47388
|
+
`));
|
|
46929
47389
|
}
|
|
46930
47390
|
case "list_peers": {
|
|
46931
47391
|
const { mesh_slug } = args ?? {};
|
|
@@ -46953,6 +47413,23 @@ ${peerLines.join(`
|
|
|
46953
47413
|
return text(sections.join(`
|
|
46954
47414
|
|
|
46955
47415
|
`));
|
|
47416
|
+
}
|
|
47417
|
+
case "message_status": {
|
|
47418
|
+
const { id } = args ?? {};
|
|
47419
|
+
if (!id)
|
|
47420
|
+
return text("message_status: `id` required", true);
|
|
47421
|
+
const client = allClients()[0];
|
|
47422
|
+
if (!client)
|
|
47423
|
+
return text("message_status: not connected", true);
|
|
47424
|
+
const result = await client.messageStatus(id);
|
|
47425
|
+
if (!result)
|
|
47426
|
+
return text(`Message ${id} not found or timed out.`);
|
|
47427
|
+
const recipientLines = result.recipients.map((r) => ` - ${r.name} (${r.pubkey.slice(0, 12)}…): ${r.status}`);
|
|
47428
|
+
return text(`Message ${id.slice(0, 12)}… → ${result.targetSpec}
|
|
47429
|
+
` + `Delivered: ${result.delivered}${result.deliveredAt ? ` at ${result.deliveredAt}` : ""}
|
|
47430
|
+
` + `Recipients:
|
|
47431
|
+
${recipientLines.join(`
|
|
47432
|
+
`)}`);
|
|
46956
47433
|
}
|
|
46957
47434
|
case "check_messages": {
|
|
46958
47435
|
const drained = [];
|
|
@@ -47004,6 +47481,147 @@ ${drained.join(`
|
|
|
47004
47481
|
await c.leaveGroup(groupName);
|
|
47005
47482
|
return text(`Left @${groupName}`);
|
|
47006
47483
|
}
|
|
47484
|
+
case "set_state": {
|
|
47485
|
+
const { key, value } = args ?? {};
|
|
47486
|
+
if (!key)
|
|
47487
|
+
return text("set_state: `key` required", true);
|
|
47488
|
+
for (const c of allClients())
|
|
47489
|
+
await c.setState(key, value);
|
|
47490
|
+
return text(`State set: ${key} = ${JSON.stringify(value)}`);
|
|
47491
|
+
}
|
|
47492
|
+
case "get_state": {
|
|
47493
|
+
const { key } = args ?? {};
|
|
47494
|
+
if (!key)
|
|
47495
|
+
return text("get_state: `key` required", true);
|
|
47496
|
+
const client = allClients()[0];
|
|
47497
|
+
if (!client)
|
|
47498
|
+
return text("get_state: not connected", true);
|
|
47499
|
+
const result = await client.getState(key);
|
|
47500
|
+
if (!result)
|
|
47501
|
+
return text(`State "${key}" not found.`);
|
|
47502
|
+
return text(`${key} = ${JSON.stringify(result.value)} (set by ${result.updatedBy} at ${result.updatedAt})`);
|
|
47503
|
+
}
|
|
47504
|
+
case "list_state": {
|
|
47505
|
+
const client = allClients()[0];
|
|
47506
|
+
if (!client)
|
|
47507
|
+
return text("list_state: not connected", true);
|
|
47508
|
+
const entries = await client.listState();
|
|
47509
|
+
if (entries.length === 0)
|
|
47510
|
+
return text("No shared state set.");
|
|
47511
|
+
const lines = entries.map((e) => `- **${e.key}** = ${JSON.stringify(e.value)} (by ${e.updatedBy})`);
|
|
47512
|
+
return text(lines.join(`
|
|
47513
|
+
`));
|
|
47514
|
+
}
|
|
47515
|
+
case "remember": {
|
|
47516
|
+
const { content, tags } = args ?? {};
|
|
47517
|
+
if (!content)
|
|
47518
|
+
return text("remember: `content` required", true);
|
|
47519
|
+
const client = allClients()[0];
|
|
47520
|
+
if (!client)
|
|
47521
|
+
return text("remember: not connected", true);
|
|
47522
|
+
const id = await client.remember(content, tags);
|
|
47523
|
+
return text(`Remembered${id ? ` (${id})` : ""}: "${content.slice(0, 80)}${content.length > 80 ? "..." : ""}"`);
|
|
47524
|
+
}
|
|
47525
|
+
case "recall": {
|
|
47526
|
+
const { query } = args ?? {};
|
|
47527
|
+
if (!query)
|
|
47528
|
+
return text("recall: `query` required", true);
|
|
47529
|
+
const client = allClients()[0];
|
|
47530
|
+
if (!client)
|
|
47531
|
+
return text("recall: not connected", true);
|
|
47532
|
+
const memories = await client.recall(query);
|
|
47533
|
+
if (memories.length === 0)
|
|
47534
|
+
return text(`No memories found for "${query}".`);
|
|
47535
|
+
const lines = memories.map((m) => `- [${m.id.slice(0, 8)}] ${m.content} (by ${m.rememberedBy}, ${m.rememberedAt})`);
|
|
47536
|
+
return text(`${memories.length} memor${memories.length === 1 ? "y" : "ies"}:
|
|
47537
|
+
${lines.join(`
|
|
47538
|
+
`)}`);
|
|
47539
|
+
}
|
|
47540
|
+
case "forget": {
|
|
47541
|
+
const { id } = args ?? {};
|
|
47542
|
+
if (!id)
|
|
47543
|
+
return text("forget: `id` required", true);
|
|
47544
|
+
const client = allClients()[0];
|
|
47545
|
+
if (!client)
|
|
47546
|
+
return text("forget: not connected", true);
|
|
47547
|
+
await client.forget(id);
|
|
47548
|
+
return text(`Forgotten: ${id}`);
|
|
47549
|
+
}
|
|
47550
|
+
case "share_file": {
|
|
47551
|
+
const { path: filePath, name: fileName, tags } = args ?? {};
|
|
47552
|
+
if (!filePath)
|
|
47553
|
+
return text("share_file: `path` required", true);
|
|
47554
|
+
const { existsSync: existsSync2 } = await import("node:fs");
|
|
47555
|
+
if (!existsSync2(filePath))
|
|
47556
|
+
return text(`share_file: file not found: ${filePath}`, true);
|
|
47557
|
+
const client = allClients()[0];
|
|
47558
|
+
if (!client)
|
|
47559
|
+
return text("share_file: not connected", true);
|
|
47560
|
+
const fileId = await client.uploadFile(filePath, client.meshId, client.meshSlug, {
|
|
47561
|
+
name: fileName,
|
|
47562
|
+
tags,
|
|
47563
|
+
persistent: true
|
|
47564
|
+
});
|
|
47565
|
+
if (!fileId)
|
|
47566
|
+
return text("share_file: upload failed", true);
|
|
47567
|
+
return text(`Shared: ${fileName ?? filePath} (${fileId})`);
|
|
47568
|
+
}
|
|
47569
|
+
case "get_file": {
|
|
47570
|
+
const { id, save_to } = args ?? {};
|
|
47571
|
+
if (!id || !save_to)
|
|
47572
|
+
return text("get_file: `id` and `save_to` required", true);
|
|
47573
|
+
const client = allClients()[0];
|
|
47574
|
+
if (!client)
|
|
47575
|
+
return text("get_file: not connected", true);
|
|
47576
|
+
const result = await client.getFile(id);
|
|
47577
|
+
if (!result)
|
|
47578
|
+
return text(`get_file: file ${id} not found`, true);
|
|
47579
|
+
const res = await fetch(result.url, { signal: AbortSignal.timeout(30000) });
|
|
47580
|
+
if (!res.ok)
|
|
47581
|
+
return text(`get_file: download failed (${res.status})`, true);
|
|
47582
|
+
const { writeFileSync: writeFileSync2, mkdirSync: mkdirSync2 } = await import("node:fs");
|
|
47583
|
+
const { dirname: dirname2 } = await import("node:path");
|
|
47584
|
+
mkdirSync2(dirname2(save_to), { recursive: true });
|
|
47585
|
+
writeFileSync2(save_to, Buffer.from(await res.arrayBuffer()));
|
|
47586
|
+
return text(`Downloaded: ${result.name} → ${save_to}`);
|
|
47587
|
+
}
|
|
47588
|
+
case "list_files": {
|
|
47589
|
+
const { query, from } = args ?? {};
|
|
47590
|
+
const client = allClients()[0];
|
|
47591
|
+
if (!client)
|
|
47592
|
+
return text("list_files: not connected", true);
|
|
47593
|
+
const files = await client.listFiles(query, from);
|
|
47594
|
+
if (files.length === 0)
|
|
47595
|
+
return text("No files found.");
|
|
47596
|
+
const lines = files.map((f) => `- **${f.name}** (${f.id.slice(0, 8)}…, ${f.size} bytes) by ${f.uploadedBy}${f.tags.length ? ` [${f.tags.join(", ")}]` : ""}`);
|
|
47597
|
+
return text(lines.join(`
|
|
47598
|
+
`));
|
|
47599
|
+
}
|
|
47600
|
+
case "file_status": {
|
|
47601
|
+
const { id } = args ?? {};
|
|
47602
|
+
if (!id)
|
|
47603
|
+
return text("file_status: `id` required", true);
|
|
47604
|
+
const client = allClients()[0];
|
|
47605
|
+
if (!client)
|
|
47606
|
+
return text("file_status: not connected", true);
|
|
47607
|
+
const accesses = await client.fileStatus(id);
|
|
47608
|
+
if (accesses.length === 0)
|
|
47609
|
+
return text("No one has accessed this file yet.");
|
|
47610
|
+
const lines = accesses.map((a) => `- ${a.peerName} at ${a.accessedAt}`);
|
|
47611
|
+
return text(`Accessed by:
|
|
47612
|
+
${lines.join(`
|
|
47613
|
+
`)}`);
|
|
47614
|
+
}
|
|
47615
|
+
case "delete_file": {
|
|
47616
|
+
const { id } = args ?? {};
|
|
47617
|
+
if (!id)
|
|
47618
|
+
return text("delete_file: `id` required", true);
|
|
47619
|
+
const client = allClients()[0];
|
|
47620
|
+
if (!client)
|
|
47621
|
+
return text("delete_file: not connected", true);
|
|
47622
|
+
await client.deleteFile(id);
|
|
47623
|
+
return text(`Deleted: ${id}`);
|
|
47624
|
+
}
|
|
47007
47625
|
default:
|
|
47008
47626
|
return text(`Unknown tool: ${name}`, true);
|
|
47009
47627
|
}
|
|
@@ -47035,6 +47653,21 @@ ${drained.join(`
|
|
|
47035
47653
|
});
|
|
47036
47654
|
} catch {}
|
|
47037
47655
|
});
|
|
47656
|
+
client.onStateChange(async (change) => {
|
|
47657
|
+
try {
|
|
47658
|
+
await server.notification({
|
|
47659
|
+
method: "notifications/claude/channel",
|
|
47660
|
+
params: {
|
|
47661
|
+
content: `[state] ${change.key} = ${JSON.stringify(change.value)} (set by ${change.updatedBy})`,
|
|
47662
|
+
meta: {
|
|
47663
|
+
kind: "state_change",
|
|
47664
|
+
key: change.key,
|
|
47665
|
+
updated_by: change.updatedBy
|
|
47666
|
+
}
|
|
47667
|
+
}
|
|
47668
|
+
});
|
|
47669
|
+
} catch {}
|
|
47670
|
+
});
|
|
47038
47671
|
}
|
|
47039
47672
|
const shutdown = () => {
|
|
47040
47673
|
stopAll();
|
|
@@ -47920,7 +48553,7 @@ init_config();
|
|
|
47920
48553
|
// package.json
|
|
47921
48554
|
var package_default = {
|
|
47922
48555
|
name: "claudemesh-cli",
|
|
47923
|
-
version: "0.
|
|
48556
|
+
version: "0.4.0",
|
|
47924
48557
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
47925
48558
|
keywords: [
|
|
47926
48559
|
"claude-code",
|