claudemesh-cli 0.1.16 → 0.3.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 +479 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6514,7 +6514,7 @@ function loadConfig() {
|
|
|
6514
6514
|
if (!parsed || !Array.isArray(parsed.meshes)) {
|
|
6515
6515
|
return { version: 1, meshes: [] };
|
|
6516
6516
|
}
|
|
6517
|
-
return { version: 1, meshes: parsed.meshes, displayName: parsed.displayName };
|
|
6517
|
+
return { version: 1, meshes: parsed.meshes, displayName: parsed.displayName, groups: parsed.groups };
|
|
6518
6518
|
} catch (e) {
|
|
6519
6519
|
throw new Error(`Failed to load ${CONFIG_PATH}: ${e instanceof Error ? e.message : String(e)}`);
|
|
6520
6520
|
}
|
|
@@ -46245,13 +46245,13 @@ class StdioServerTransport {
|
|
|
46245
46245
|
var TOOLS = [
|
|
46246
46246
|
{
|
|
46247
46247
|
name: "send_message",
|
|
46248
|
-
description: "Send a message to a peer in one of your joined meshes. `to` can be a peer display name (resolved via list_peers), hex pubkey, `#channel`, or `*` for broadcast. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.",
|
|
46248
|
+
description: "Send a message to a peer in one of your joined meshes. `to` can be a peer display name (resolved via list_peers), hex pubkey, @group, `#channel`, or `*` for broadcast. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.",
|
|
46249
46249
|
inputSchema: {
|
|
46250
46250
|
type: "object",
|
|
46251
46251
|
properties: {
|
|
46252
46252
|
to: {
|
|
46253
46253
|
type: "string",
|
|
46254
|
-
description: "Peer name, pubkey, or #channel"
|
|
46254
|
+
description: "Peer name, pubkey, @group, or #channel"
|
|
46255
46255
|
},
|
|
46256
46256
|
message: { type: "string", description: "Message text" },
|
|
46257
46257
|
priority: {
|
|
@@ -46276,6 +46276,20 @@ var TOOLS = [
|
|
|
46276
46276
|
}
|
|
46277
46277
|
}
|
|
46278
46278
|
},
|
|
46279
|
+
{
|
|
46280
|
+
name: "message_status",
|
|
46281
|
+
description: "Check the delivery status of a sent message. Shows whether each recipient received it.",
|
|
46282
|
+
inputSchema: {
|
|
46283
|
+
type: "object",
|
|
46284
|
+
properties: {
|
|
46285
|
+
id: {
|
|
46286
|
+
type: "string",
|
|
46287
|
+
description: "Message ID (returned by send_message)"
|
|
46288
|
+
}
|
|
46289
|
+
},
|
|
46290
|
+
required: ["id"]
|
|
46291
|
+
}
|
|
46292
|
+
},
|
|
46279
46293
|
{
|
|
46280
46294
|
name: "check_messages",
|
|
46281
46295
|
description: "Pull any undelivered messages from the broker. Normally messages arrive via push; use this to drain the queue after being offline.",
|
|
@@ -46306,6 +46320,101 @@ var TOOLS = [
|
|
|
46306
46320
|
},
|
|
46307
46321
|
required: ["status"]
|
|
46308
46322
|
}
|
|
46323
|
+
},
|
|
46324
|
+
{
|
|
46325
|
+
name: "join_group",
|
|
46326
|
+
description: "Join a group with an optional role. Other peers see your group membership in list_peers.",
|
|
46327
|
+
inputSchema: {
|
|
46328
|
+
type: "object",
|
|
46329
|
+
properties: {
|
|
46330
|
+
name: { type: "string", description: "Group name (without @)" },
|
|
46331
|
+
role: {
|
|
46332
|
+
type: "string",
|
|
46333
|
+
description: "Your role in the group (e.g. lead, member, observer)"
|
|
46334
|
+
}
|
|
46335
|
+
},
|
|
46336
|
+
required: ["name"]
|
|
46337
|
+
}
|
|
46338
|
+
},
|
|
46339
|
+
{
|
|
46340
|
+
name: "leave_group",
|
|
46341
|
+
description: "Leave a group.",
|
|
46342
|
+
inputSchema: {
|
|
46343
|
+
type: "object",
|
|
46344
|
+
properties: {
|
|
46345
|
+
name: { type: "string", description: "Group name (without @)" }
|
|
46346
|
+
},
|
|
46347
|
+
required: ["name"]
|
|
46348
|
+
}
|
|
46349
|
+
},
|
|
46350
|
+
{
|
|
46351
|
+
name: "set_state",
|
|
46352
|
+
description: "Set a shared state value visible to all peers in the mesh. Pushes a change notification.",
|
|
46353
|
+
inputSchema: {
|
|
46354
|
+
type: "object",
|
|
46355
|
+
properties: {
|
|
46356
|
+
key: { type: "string" },
|
|
46357
|
+
value: { description: "Any JSON value" }
|
|
46358
|
+
},
|
|
46359
|
+
required: ["key", "value"]
|
|
46360
|
+
}
|
|
46361
|
+
},
|
|
46362
|
+
{
|
|
46363
|
+
name: "get_state",
|
|
46364
|
+
description: "Read a shared state value.",
|
|
46365
|
+
inputSchema: {
|
|
46366
|
+
type: "object",
|
|
46367
|
+
properties: {
|
|
46368
|
+
key: { type: "string" }
|
|
46369
|
+
},
|
|
46370
|
+
required: ["key"]
|
|
46371
|
+
}
|
|
46372
|
+
},
|
|
46373
|
+
{
|
|
46374
|
+
name: "list_state",
|
|
46375
|
+
description: "List all shared state keys and values in the mesh.",
|
|
46376
|
+
inputSchema: { type: "object", properties: {} }
|
|
46377
|
+
},
|
|
46378
|
+
{
|
|
46379
|
+
name: "remember",
|
|
46380
|
+
description: "Store persistent knowledge in the mesh's shared memory. Survives across sessions.",
|
|
46381
|
+
inputSchema: {
|
|
46382
|
+
type: "object",
|
|
46383
|
+
properties: {
|
|
46384
|
+
content: {
|
|
46385
|
+
type: "string",
|
|
46386
|
+
description: "The knowledge to remember"
|
|
46387
|
+
},
|
|
46388
|
+
tags: {
|
|
46389
|
+
type: "array",
|
|
46390
|
+
items: { type: "string" },
|
|
46391
|
+
description: "Optional categorization tags"
|
|
46392
|
+
}
|
|
46393
|
+
},
|
|
46394
|
+
required: ["content"]
|
|
46395
|
+
}
|
|
46396
|
+
},
|
|
46397
|
+
{
|
|
46398
|
+
name: "recall",
|
|
46399
|
+
description: "Search the mesh's shared memory by relevance.",
|
|
46400
|
+
inputSchema: {
|
|
46401
|
+
type: "object",
|
|
46402
|
+
properties: {
|
|
46403
|
+
query: { type: "string", description: "Search query" }
|
|
46404
|
+
},
|
|
46405
|
+
required: ["query"]
|
|
46406
|
+
}
|
|
46407
|
+
},
|
|
46408
|
+
{
|
|
46409
|
+
name: "forget",
|
|
46410
|
+
description: "Remove a memory from the mesh's shared knowledge.",
|
|
46411
|
+
inputSchema: {
|
|
46412
|
+
type: "object",
|
|
46413
|
+
properties: {
|
|
46414
|
+
id: { type: "string", description: "Memory ID to forget" }
|
|
46415
|
+
},
|
|
46416
|
+
required: ["id"]
|
|
46417
|
+
}
|
|
46309
46418
|
}
|
|
46310
46419
|
];
|
|
46311
46420
|
|
|
@@ -46399,6 +46508,11 @@ class BrokerClient {
|
|
|
46399
46508
|
pushHandlers = new Set;
|
|
46400
46509
|
pushBuffer = [];
|
|
46401
46510
|
listPeersResolvers = [];
|
|
46511
|
+
stateResolvers = [];
|
|
46512
|
+
stateListResolvers = [];
|
|
46513
|
+
memoryStoreResolvers = [];
|
|
46514
|
+
memoryRecallResolvers = [];
|
|
46515
|
+
stateChangeHandlers = new Set;
|
|
46402
46516
|
sessionPubkey = null;
|
|
46403
46517
|
sessionSecretKey = null;
|
|
46404
46518
|
closed = false;
|
|
@@ -46590,6 +46704,106 @@ class BrokerClient {
|
|
|
46590
46704
|
return;
|
|
46591
46705
|
this.ws.send(JSON.stringify({ type: "set_summary", summary }));
|
|
46592
46706
|
}
|
|
46707
|
+
async joinGroup(name, role) {
|
|
46708
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46709
|
+
return;
|
|
46710
|
+
this.ws.send(JSON.stringify({ type: "join_group", name, role }));
|
|
46711
|
+
}
|
|
46712
|
+
async leaveGroup(name) {
|
|
46713
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46714
|
+
return;
|
|
46715
|
+
this.ws.send(JSON.stringify({ type: "leave_group", name }));
|
|
46716
|
+
}
|
|
46717
|
+
async setState(key, value) {
|
|
46718
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46719
|
+
return;
|
|
46720
|
+
this.ws.send(JSON.stringify({ type: "set_state", key, value }));
|
|
46721
|
+
}
|
|
46722
|
+
async getState(key) {
|
|
46723
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46724
|
+
return null;
|
|
46725
|
+
return new Promise((resolve) => {
|
|
46726
|
+
this.stateResolvers.push(resolve);
|
|
46727
|
+
this.ws.send(JSON.stringify({ type: "get_state", key }));
|
|
46728
|
+
setTimeout(() => {
|
|
46729
|
+
const idx = this.stateResolvers.indexOf(resolve);
|
|
46730
|
+
if (idx !== -1) {
|
|
46731
|
+
this.stateResolvers.splice(idx, 1);
|
|
46732
|
+
resolve(null);
|
|
46733
|
+
}
|
|
46734
|
+
}, 5000);
|
|
46735
|
+
});
|
|
46736
|
+
}
|
|
46737
|
+
async listState() {
|
|
46738
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46739
|
+
return [];
|
|
46740
|
+
return new Promise((resolve) => {
|
|
46741
|
+
this.stateListResolvers.push(resolve);
|
|
46742
|
+
this.ws.send(JSON.stringify({ type: "list_state" }));
|
|
46743
|
+
setTimeout(() => {
|
|
46744
|
+
const idx = this.stateListResolvers.indexOf(resolve);
|
|
46745
|
+
if (idx !== -1) {
|
|
46746
|
+
this.stateListResolvers.splice(idx, 1);
|
|
46747
|
+
resolve([]);
|
|
46748
|
+
}
|
|
46749
|
+
}, 5000);
|
|
46750
|
+
});
|
|
46751
|
+
}
|
|
46752
|
+
async remember(content, tags) {
|
|
46753
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46754
|
+
return null;
|
|
46755
|
+
return new Promise((resolve) => {
|
|
46756
|
+
this.memoryStoreResolvers.push(resolve);
|
|
46757
|
+
this.ws.send(JSON.stringify({ type: "remember", content, tags }));
|
|
46758
|
+
setTimeout(() => {
|
|
46759
|
+
const idx = this.memoryStoreResolvers.indexOf(resolve);
|
|
46760
|
+
if (idx !== -1) {
|
|
46761
|
+
this.memoryStoreResolvers.splice(idx, 1);
|
|
46762
|
+
resolve(null);
|
|
46763
|
+
}
|
|
46764
|
+
}, 5000);
|
|
46765
|
+
});
|
|
46766
|
+
}
|
|
46767
|
+
async recall(query) {
|
|
46768
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46769
|
+
return [];
|
|
46770
|
+
return new Promise((resolve) => {
|
|
46771
|
+
this.memoryRecallResolvers.push(resolve);
|
|
46772
|
+
this.ws.send(JSON.stringify({ type: "recall", query }));
|
|
46773
|
+
setTimeout(() => {
|
|
46774
|
+
const idx = this.memoryRecallResolvers.indexOf(resolve);
|
|
46775
|
+
if (idx !== -1) {
|
|
46776
|
+
this.memoryRecallResolvers.splice(idx, 1);
|
|
46777
|
+
resolve([]);
|
|
46778
|
+
}
|
|
46779
|
+
}, 5000);
|
|
46780
|
+
});
|
|
46781
|
+
}
|
|
46782
|
+
async forget(memoryId) {
|
|
46783
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46784
|
+
return;
|
|
46785
|
+
this.ws.send(JSON.stringify({ type: "forget", memoryId }));
|
|
46786
|
+
}
|
|
46787
|
+
messageStatusResolvers = [];
|
|
46788
|
+
async messageStatus(messageId) {
|
|
46789
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46790
|
+
return null;
|
|
46791
|
+
return new Promise((resolve) => {
|
|
46792
|
+
this.messageStatusResolvers.push(resolve);
|
|
46793
|
+
this.ws.send(JSON.stringify({ type: "message_status", messageId }));
|
|
46794
|
+
setTimeout(() => {
|
|
46795
|
+
const idx = this.messageStatusResolvers.indexOf(resolve);
|
|
46796
|
+
if (idx !== -1) {
|
|
46797
|
+
this.messageStatusResolvers.splice(idx, 1);
|
|
46798
|
+
resolve(null);
|
|
46799
|
+
}
|
|
46800
|
+
}, 5000);
|
|
46801
|
+
});
|
|
46802
|
+
}
|
|
46803
|
+
onStateChange(handler) {
|
|
46804
|
+
this.stateChangeHandlers.add(handler);
|
|
46805
|
+
return () => this.stateChangeHandlers.delete(handler);
|
|
46806
|
+
}
|
|
46593
46807
|
close() {
|
|
46594
46808
|
this.closed = true;
|
|
46595
46809
|
if (this.helloTimer)
|
|
@@ -46672,6 +46886,61 @@ class BrokerClient {
|
|
|
46672
46886
|
})();
|
|
46673
46887
|
return;
|
|
46674
46888
|
}
|
|
46889
|
+
if (msg.type === "state_result") {
|
|
46890
|
+
const resolver = this.stateResolvers.shift();
|
|
46891
|
+
if (resolver) {
|
|
46892
|
+
if (msg.key) {
|
|
46893
|
+
resolver({
|
|
46894
|
+
key: String(msg.key),
|
|
46895
|
+
value: msg.value,
|
|
46896
|
+
updatedBy: String(msg.updatedBy ?? ""),
|
|
46897
|
+
updatedAt: String(msg.updatedAt ?? "")
|
|
46898
|
+
});
|
|
46899
|
+
} else {
|
|
46900
|
+
resolver(null);
|
|
46901
|
+
}
|
|
46902
|
+
}
|
|
46903
|
+
return;
|
|
46904
|
+
}
|
|
46905
|
+
if (msg.type === "state_list") {
|
|
46906
|
+
const entries = msg.entries ?? [];
|
|
46907
|
+
const resolver = this.stateListResolvers.shift();
|
|
46908
|
+
if (resolver)
|
|
46909
|
+
resolver(entries);
|
|
46910
|
+
return;
|
|
46911
|
+
}
|
|
46912
|
+
if (msg.type === "state_change") {
|
|
46913
|
+
const change = {
|
|
46914
|
+
key: String(msg.key ?? ""),
|
|
46915
|
+
value: msg.value,
|
|
46916
|
+
updatedBy: String(msg.updatedBy ?? "")
|
|
46917
|
+
};
|
|
46918
|
+
for (const h of this.stateChangeHandlers) {
|
|
46919
|
+
try {
|
|
46920
|
+
h(change);
|
|
46921
|
+
} catch {}
|
|
46922
|
+
}
|
|
46923
|
+
return;
|
|
46924
|
+
}
|
|
46925
|
+
if (msg.type === "memory_stored") {
|
|
46926
|
+
const resolver = this.memoryStoreResolvers.shift();
|
|
46927
|
+
if (resolver)
|
|
46928
|
+
resolver(msg.id ? String(msg.id) : null);
|
|
46929
|
+
return;
|
|
46930
|
+
}
|
|
46931
|
+
if (msg.type === "memory_results") {
|
|
46932
|
+
const memories = msg.memories ?? [];
|
|
46933
|
+
const resolver = this.memoryRecallResolvers.shift();
|
|
46934
|
+
if (resolver)
|
|
46935
|
+
resolver(memories);
|
|
46936
|
+
return;
|
|
46937
|
+
}
|
|
46938
|
+
if (msg.type === "message_status_result") {
|
|
46939
|
+
const resolver = this.messageStatusResolvers.shift();
|
|
46940
|
+
if (resolver)
|
|
46941
|
+
resolver(msg);
|
|
46942
|
+
return;
|
|
46943
|
+
}
|
|
46675
46944
|
if (msg.type === "error") {
|
|
46676
46945
|
this.debug(`broker error: ${msg.code} ${msg.message}`);
|
|
46677
46946
|
const id = msg.id ? String(msg.id) : null;
|
|
@@ -46786,7 +47055,7 @@ async function resolveClient(to) {
|
|
|
46786
47055
|
target = rest;
|
|
46787
47056
|
}
|
|
46788
47057
|
}
|
|
46789
|
-
if (/^[0-9a-f]{64}$/.test(target) || target.startsWith("#") || target === "*") {
|
|
47058
|
+
if (/^[0-9a-f]{64}$/.test(target) || target.startsWith("#") || target.startsWith("@") || target === "*") {
|
|
46790
47059
|
if (targetClients.length === 1) {
|
|
46791
47060
|
return { client: targetClients[0], targetSpec: target };
|
|
46792
47061
|
}
|
|
@@ -46843,30 +47112,54 @@ ${body}`;
|
|
|
46843
47112
|
}
|
|
46844
47113
|
async function startMcpServer() {
|
|
46845
47114
|
const config2 = loadConfig();
|
|
46846
|
-
const
|
|
47115
|
+
const myName = config2.displayName ?? "unnamed";
|
|
47116
|
+
const myGroups = (config2.groups ?? []).map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ") || "none";
|
|
47117
|
+
const server = new Server({ name: "claudemesh", version: "0.3.0" }, {
|
|
46847
47118
|
capabilities: {
|
|
46848
47119
|
experimental: { "claude/channel": {} },
|
|
46849
47120
|
tools: {}
|
|
46850
47121
|
},
|
|
46851
|
-
instructions:
|
|
47122
|
+
instructions: `## Identity
|
|
47123
|
+
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.
|
|
47124
|
+
|
|
47125
|
+
## Responding to messages
|
|
47126
|
+
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.
|
|
47127
|
+
|
|
47128
|
+
## Tools
|
|
47129
|
+
| Tool | Description |
|
|
47130
|
+
|------|-------------|
|
|
47131
|
+
| send_message(to, message, priority?) | Send to peer name, @group, or * broadcast. \`to\` accepts display name, pubkey hex, @groupname, or *. |
|
|
47132
|
+
| list_peers(mesh_slug?) | List connected peers with status, summary, groups, and roles. |
|
|
47133
|
+
| check_messages() | Drain buffered inbound messages (auto-pushed in most cases, use as fallback). |
|
|
47134
|
+
| set_summary(summary) | Set 1-2 sentence description of your current work, visible to all peers. |
|
|
47135
|
+
| set_status(status) | Override status: idle, working, or dnd. |
|
|
47136
|
+
| join_group(name, role?) | Join a @group with optional role (lead, member, observer, or any string). |
|
|
47137
|
+
| leave_group(name) | Leave a @group. |
|
|
47138
|
+
| set_state(key, value) | Write shared state; pushes change to all peers. |
|
|
47139
|
+
| get_state(key) | Read a shared state value. |
|
|
47140
|
+
| list_state() | List all state keys with values, authors, and timestamps. |
|
|
47141
|
+
| remember(content, tags?) | Store persistent knowledge with optional tags. |
|
|
47142
|
+
| recall(query) | Full-text search over mesh memory. |
|
|
47143
|
+
| forget(id) | Soft-delete a memory entry. |
|
|
46852
47144
|
|
|
46853
|
-
|
|
47145
|
+
If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
|
|
46854
47146
|
|
|
46855
|
-
|
|
47147
|
+
## Groups
|
|
47148
|
+
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.
|
|
46856
47149
|
|
|
46857
|
-
|
|
46858
|
-
-
|
|
46859
|
-
- send_message: send to a peer by display name, pubkey, #channel, or * broadcast (priority: now/next/low)
|
|
46860
|
-
- check_messages: drain buffered inbound messages (usually auto-pushed)
|
|
46861
|
-
- set_summary: 1-2 sentence summary of what you're working on
|
|
46862
|
-
- set_status: manually override your status (idle/working/dnd)
|
|
47150
|
+
## State
|
|
47151
|
+
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.
|
|
46863
47152
|
|
|
46864
|
-
|
|
46865
|
-
|
|
46866
|
-
- "next" (default): delivered when recipient is idle
|
|
46867
|
-
- "low": pull-only (check_messages)
|
|
47153
|
+
## Memory
|
|
47154
|
+
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.
|
|
46868
47155
|
|
|
46869
|
-
|
|
47156
|
+
## Priority
|
|
47157
|
+
- "now": interrupt immediately, even if recipient is in DND (use for urgent: broken deploy, blocking issue)
|
|
47158
|
+
- "next" (default): deliver when recipient goes idle (normal coordination)
|
|
47159
|
+
- "low": pull-only via check_messages (FYI, non-blocking context)
|
|
47160
|
+
|
|
47161
|
+
## Coordination
|
|
47162
|
+
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.`
|
|
46870
47163
|
});
|
|
46871
47164
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
46872
47165
|
tools: TOOLS
|
|
@@ -46904,7 +47197,8 @@ No peers connected.`);
|
|
|
46904
47197
|
} else {
|
|
46905
47198
|
const peerLines = peers.map((p) => {
|
|
46906
47199
|
const summary = p.summary ? ` — "${p.summary}"` : "";
|
|
46907
|
-
|
|
47200
|
+
const groupsStr = p.groups?.length ? ` [${p.groups.map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ")}]` : "";
|
|
47201
|
+
return `- **${p.displayName}** [${p.status}]${groupsStr} (${p.pubkey.slice(0, 12)}…)${summary}`;
|
|
46908
47202
|
});
|
|
46909
47203
|
sections.push(`${header}
|
|
46910
47204
|
${peerLines.join(`
|
|
@@ -46914,6 +47208,23 @@ ${peerLines.join(`
|
|
|
46914
47208
|
return text(sections.join(`
|
|
46915
47209
|
|
|
46916
47210
|
`));
|
|
47211
|
+
}
|
|
47212
|
+
case "message_status": {
|
|
47213
|
+
const { id } = args ?? {};
|
|
47214
|
+
if (!id)
|
|
47215
|
+
return text("message_status: `id` required", true);
|
|
47216
|
+
const client = allClients()[0];
|
|
47217
|
+
if (!client)
|
|
47218
|
+
return text("message_status: not connected", true);
|
|
47219
|
+
const result = await client.messageStatus(id);
|
|
47220
|
+
if (!result)
|
|
47221
|
+
return text(`Message ${id} not found or timed out.`);
|
|
47222
|
+
const recipientLines = result.recipients.map((r) => ` - ${r.name} (${r.pubkey.slice(0, 12)}…): ${r.status}`);
|
|
47223
|
+
return text(`Message ${id.slice(0, 12)}… → ${result.targetSpec}
|
|
47224
|
+
` + `Delivered: ${result.delivered}${result.deliveredAt ? ` at ${result.deliveredAt}` : ""}
|
|
47225
|
+
` + `Recipients:
|
|
47226
|
+
${recipientLines.join(`
|
|
47227
|
+
`)}`);
|
|
46917
47228
|
}
|
|
46918
47229
|
case "check_messages": {
|
|
46919
47230
|
const drained = [];
|
|
@@ -46949,6 +47260,88 @@ ${drained.join(`
|
|
|
46949
47260
|
await c.setStatus(s);
|
|
46950
47261
|
return text(`Status set to ${s} across ${allClients().length} mesh(es).`);
|
|
46951
47262
|
}
|
|
47263
|
+
case "join_group": {
|
|
47264
|
+
const { name: groupName, role } = args ?? {};
|
|
47265
|
+
if (!groupName)
|
|
47266
|
+
return text("join_group: `name` required", true);
|
|
47267
|
+
for (const c of allClients())
|
|
47268
|
+
await c.joinGroup(groupName, role);
|
|
47269
|
+
return text(`Joined @${groupName}${role ? ` as ${role}` : ""}`);
|
|
47270
|
+
}
|
|
47271
|
+
case "leave_group": {
|
|
47272
|
+
const { name: groupName } = args ?? {};
|
|
47273
|
+
if (!groupName)
|
|
47274
|
+
return text("leave_group: `name` required", true);
|
|
47275
|
+
for (const c of allClients())
|
|
47276
|
+
await c.leaveGroup(groupName);
|
|
47277
|
+
return text(`Left @${groupName}`);
|
|
47278
|
+
}
|
|
47279
|
+
case "set_state": {
|
|
47280
|
+
const { key, value } = args ?? {};
|
|
47281
|
+
if (!key)
|
|
47282
|
+
return text("set_state: `key` required", true);
|
|
47283
|
+
for (const c of allClients())
|
|
47284
|
+
await c.setState(key, value);
|
|
47285
|
+
return text(`State set: ${key} = ${JSON.stringify(value)}`);
|
|
47286
|
+
}
|
|
47287
|
+
case "get_state": {
|
|
47288
|
+
const { key } = args ?? {};
|
|
47289
|
+
if (!key)
|
|
47290
|
+
return text("get_state: `key` required", true);
|
|
47291
|
+
const client = allClients()[0];
|
|
47292
|
+
if (!client)
|
|
47293
|
+
return text("get_state: not connected", true);
|
|
47294
|
+
const result = await client.getState(key);
|
|
47295
|
+
if (!result)
|
|
47296
|
+
return text(`State "${key}" not found.`);
|
|
47297
|
+
return text(`${key} = ${JSON.stringify(result.value)} (set by ${result.updatedBy} at ${result.updatedAt})`);
|
|
47298
|
+
}
|
|
47299
|
+
case "list_state": {
|
|
47300
|
+
const client = allClients()[0];
|
|
47301
|
+
if (!client)
|
|
47302
|
+
return text("list_state: not connected", true);
|
|
47303
|
+
const entries = await client.listState();
|
|
47304
|
+
if (entries.length === 0)
|
|
47305
|
+
return text("No shared state set.");
|
|
47306
|
+
const lines = entries.map((e) => `- **${e.key}** = ${JSON.stringify(e.value)} (by ${e.updatedBy})`);
|
|
47307
|
+
return text(lines.join(`
|
|
47308
|
+
`));
|
|
47309
|
+
}
|
|
47310
|
+
case "remember": {
|
|
47311
|
+
const { content, tags } = args ?? {};
|
|
47312
|
+
if (!content)
|
|
47313
|
+
return text("remember: `content` required", true);
|
|
47314
|
+
const client = allClients()[0];
|
|
47315
|
+
if (!client)
|
|
47316
|
+
return text("remember: not connected", true);
|
|
47317
|
+
const id = await client.remember(content, tags);
|
|
47318
|
+
return text(`Remembered${id ? ` (${id})` : ""}: "${content.slice(0, 80)}${content.length > 80 ? "..." : ""}"`);
|
|
47319
|
+
}
|
|
47320
|
+
case "recall": {
|
|
47321
|
+
const { query } = args ?? {};
|
|
47322
|
+
if (!query)
|
|
47323
|
+
return text("recall: `query` required", true);
|
|
47324
|
+
const client = allClients()[0];
|
|
47325
|
+
if (!client)
|
|
47326
|
+
return text("recall: not connected", true);
|
|
47327
|
+
const memories = await client.recall(query);
|
|
47328
|
+
if (memories.length === 0)
|
|
47329
|
+
return text(`No memories found for "${query}".`);
|
|
47330
|
+
const lines = memories.map((m) => `- [${m.id.slice(0, 8)}] ${m.content} (by ${m.rememberedBy}, ${m.rememberedAt})`);
|
|
47331
|
+
return text(`${memories.length} memor${memories.length === 1 ? "y" : "ies"}:
|
|
47332
|
+
${lines.join(`
|
|
47333
|
+
`)}`);
|
|
47334
|
+
}
|
|
47335
|
+
case "forget": {
|
|
47336
|
+
const { id } = args ?? {};
|
|
47337
|
+
if (!id)
|
|
47338
|
+
return text("forget: `id` required", true);
|
|
47339
|
+
const client = allClients()[0];
|
|
47340
|
+
if (!client)
|
|
47341
|
+
return text("forget: not connected", true);
|
|
47342
|
+
await client.forget(id);
|
|
47343
|
+
return text(`Forgotten: ${id}`);
|
|
47344
|
+
}
|
|
46952
47345
|
default:
|
|
46953
47346
|
return text(`Unknown tool: ${name}`, true);
|
|
46954
47347
|
}
|
|
@@ -46980,6 +47373,21 @@ ${drained.join(`
|
|
|
46980
47373
|
});
|
|
46981
47374
|
} catch {}
|
|
46982
47375
|
});
|
|
47376
|
+
client.onStateChange(async (change) => {
|
|
47377
|
+
try {
|
|
47378
|
+
await server.notification({
|
|
47379
|
+
method: "notifications/claude/channel",
|
|
47380
|
+
params: {
|
|
47381
|
+
content: `[state] ${change.key} = ${JSON.stringify(change.value)} (set by ${change.updatedBy})`,
|
|
47382
|
+
meta: {
|
|
47383
|
+
kind: "state_change",
|
|
47384
|
+
key: change.key,
|
|
47385
|
+
updated_by: change.updatedBy
|
|
47386
|
+
}
|
|
47387
|
+
}
|
|
47388
|
+
});
|
|
47389
|
+
} catch {}
|
|
47390
|
+
});
|
|
46983
47391
|
}
|
|
46984
47392
|
const shutdown = () => {
|
|
46985
47393
|
stopAll();
|
|
@@ -47582,6 +47990,8 @@ import { createInterface } from "node:readline";
|
|
|
47582
47990
|
function parseArgs(argv) {
|
|
47583
47991
|
const result = {
|
|
47584
47992
|
name: null,
|
|
47993
|
+
role: null,
|
|
47994
|
+
groups: null,
|
|
47585
47995
|
joinLink: null,
|
|
47586
47996
|
meshSlug: null,
|
|
47587
47997
|
quiet: false,
|
|
@@ -47595,6 +48005,14 @@ function parseArgs(argv) {
|
|
|
47595
48005
|
result.name = argv[++i];
|
|
47596
48006
|
} else if (arg.startsWith("--name=")) {
|
|
47597
48007
|
result.name = arg.slice("--name=".length);
|
|
48008
|
+
} else if (arg === "--role" && i + 1 < argv.length) {
|
|
48009
|
+
result.role = argv[++i];
|
|
48010
|
+
} else if (arg.startsWith("--role=")) {
|
|
48011
|
+
result.role = arg.slice("--role=".length);
|
|
48012
|
+
} else if (arg === "--groups" && i + 1 < argv.length) {
|
|
48013
|
+
result.groups = argv[++i];
|
|
48014
|
+
} else if (arg.startsWith("--groups=")) {
|
|
48015
|
+
result.groups = arg.slice("--groups=".length);
|
|
47598
48016
|
} else if (arg === "--join" && i + 1 < argv.length) {
|
|
47599
48017
|
result.joinLink = argv[++i];
|
|
47600
48018
|
} else if (arg.startsWith("--join=")) {
|
|
@@ -47640,6 +48058,23 @@ async function pickMesh(meshes) {
|
|
|
47640
48058
|
});
|
|
47641
48059
|
});
|
|
47642
48060
|
}
|
|
48061
|
+
function parseGroupsString(raw) {
|
|
48062
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean).map((token) => {
|
|
48063
|
+
const idx = token.indexOf(":");
|
|
48064
|
+
if (idx === -1)
|
|
48065
|
+
return { name: token };
|
|
48066
|
+
return { name: token.slice(0, idx), role: token.slice(idx + 1) };
|
|
48067
|
+
});
|
|
48068
|
+
}
|
|
48069
|
+
function askLine(prompt) {
|
|
48070
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
48071
|
+
return new Promise((resolve2) => {
|
|
48072
|
+
rl.question(prompt, (answer) => {
|
|
48073
|
+
rl.close();
|
|
48074
|
+
resolve2(answer.trim());
|
|
48075
|
+
});
|
|
48076
|
+
});
|
|
48077
|
+
}
|
|
47643
48078
|
async function confirmPermissions() {
|
|
47644
48079
|
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
47645
48080
|
const bold = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
@@ -47671,12 +48106,14 @@ async function confirmPermissions() {
|
|
|
47671
48106
|
});
|
|
47672
48107
|
});
|
|
47673
48108
|
}
|
|
47674
|
-
function printBanner(name, meshSlug) {
|
|
48109
|
+
function printBanner(name, meshSlug, role, groups) {
|
|
47675
48110
|
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
47676
48111
|
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
47677
48112
|
const bold = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
48113
|
+
const roleSuffix = role ? ` (${role})` : "";
|
|
48114
|
+
const groupTags = groups.length ? " [" + groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
|
|
47678
48115
|
const rule = "─".repeat(60);
|
|
47679
|
-
console.log(bold(`claudemesh launch`) + dim(` — as ${name} on ${meshSlug}`));
|
|
48116
|
+
console.log(bold(`claudemesh launch`) + dim(` — as ${name}${roleSuffix} on ${meshSlug}${groupTags}`));
|
|
47680
48117
|
console.log(rule);
|
|
47681
48118
|
console.log("Peer messages arrive as <channel> reminders in real-time.");
|
|
47682
48119
|
console.log("Peers send text only — they cannot call tools or read files.");
|
|
@@ -47731,6 +48168,22 @@ async function runLaunch(extraArgs) {
|
|
|
47731
48168
|
mesh = await pickMesh(config2.meshes);
|
|
47732
48169
|
}
|
|
47733
48170
|
const displayName = args.name ?? `${hostname2()}-${process.pid}`;
|
|
48171
|
+
let role = args.role;
|
|
48172
|
+
let parsedGroups = args.groups ? parseGroupsString(args.groups) : [];
|
|
48173
|
+
if (!args.quiet) {
|
|
48174
|
+
if (role === null) {
|
|
48175
|
+
const answer = await askLine(" Role (optional): ");
|
|
48176
|
+
if (answer)
|
|
48177
|
+
role = answer;
|
|
48178
|
+
}
|
|
48179
|
+
if (parsedGroups.length === 0 && args.groups === null) {
|
|
48180
|
+
const answer = await askLine(" Groups (comma-separated, optional): ");
|
|
48181
|
+
if (answer)
|
|
48182
|
+
parsedGroups = parseGroupsString(answer);
|
|
48183
|
+
}
|
|
48184
|
+
if (role || parsedGroups.length)
|
|
48185
|
+
console.log("");
|
|
48186
|
+
}
|
|
47734
48187
|
const tmpBase = tmpdir();
|
|
47735
48188
|
try {
|
|
47736
48189
|
for (const entry of readdirSync(tmpBase)) {
|
|
@@ -47746,12 +48199,13 @@ async function runLaunch(extraArgs) {
|
|
|
47746
48199
|
const sessionConfig = {
|
|
47747
48200
|
version: 1,
|
|
47748
48201
|
meshes: [mesh],
|
|
47749
|
-
displayName
|
|
48202
|
+
displayName,
|
|
48203
|
+
...parsedGroups.length > 0 ? { groups: parsedGroups } : {}
|
|
47750
48204
|
};
|
|
47751
48205
|
writeFileSync4(join4(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
|
|
47752
48206
|
`, "utf-8");
|
|
47753
48207
|
if (!args.quiet) {
|
|
47754
|
-
printBanner(displayName, mesh.slug);
|
|
48208
|
+
printBanner(displayName, mesh.slug, role, parsedGroups);
|
|
47755
48209
|
if (!args.skipPermConfirm) {
|
|
47756
48210
|
await confirmPermissions();
|
|
47757
48211
|
}
|
|
@@ -47819,7 +48273,7 @@ init_config();
|
|
|
47819
48273
|
// package.json
|
|
47820
48274
|
var package_default = {
|
|
47821
48275
|
name: "claudemesh-cli",
|
|
47822
|
-
version: "0.
|
|
48276
|
+
version: "0.3.0",
|
|
47823
48277
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
47824
48278
|
keywords: [
|
|
47825
48279
|
"claude-code",
|