claudemesh-cli 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/dist/index.js +139 -50
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -54773,7 +54773,7 @@ class StdioServerTransport {
54773
54773
  var TOOLS = [
54774
54774
  {
54775
54775
  name: "send_message",
54776
- description: "Send a message to a peer in one of your joined meshes. `to` is a peer display name, hex pubkey, or `#channel`. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.",
54776
+ 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.",
54777
54777
  inputSchema: {
54778
54778
  type: "object",
54779
54779
  properties: {
@@ -54990,6 +54990,7 @@ class BrokerClient {
54990
54990
  outbound = [];
54991
54991
  pushHandlers = new Set;
54992
54992
  pushBuffer = [];
54993
+ listPeersResolvers = [];
54993
54994
  closed = false;
54994
54995
  reconnectAttempt = 0;
54995
54996
  helloTimer = null;
@@ -55013,7 +55014,7 @@ class BrokerClient {
55013
55014
  async connect() {
55014
55015
  if (this.closed)
55015
55016
  throw new Error("client is closed");
55016
- this.setStatus("connecting");
55017
+ this.setConnStatus("connecting");
55017
55018
  const ws = new wrapper_default(this.mesh.brokerUrl);
55018
55019
  this.ws = ws;
55019
55020
  return new Promise((resolve, reject) => {
@@ -55053,7 +55054,7 @@ class BrokerClient {
55053
55054
  if (this.helloTimer)
55054
55055
  clearTimeout(this.helloTimer);
55055
55056
  this.helloTimer = null;
55056
- this.setStatus("open");
55057
+ this.setConnStatus("open");
55057
55058
  this.reconnectAttempt = 0;
55058
55059
  this.flushOutbound();
55059
55060
  resolve();
@@ -55072,7 +55073,7 @@ class BrokerClient {
55072
55073
  if (!this.closed)
55073
55074
  this.scheduleReconnect();
55074
55075
  else
55075
- this.setStatus("closed");
55076
+ this.setConnStatus("closed");
55076
55077
  };
55077
55078
  const onError = (err) => {
55078
55079
  this.debug(`ws error: ${err.message}`);
@@ -55152,6 +55153,26 @@ class BrokerClient {
55152
55153
  return;
55153
55154
  this.ws.send(JSON.stringify({ type: "set_status", status }));
55154
55155
  }
55156
+ async listPeers() {
55157
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
55158
+ return [];
55159
+ return new Promise((resolve) => {
55160
+ this.listPeersResolvers.push(resolve);
55161
+ this.ws.send(JSON.stringify({ type: "list_peers" }));
55162
+ setTimeout(() => {
55163
+ const idx = this.listPeersResolvers.indexOf(resolve);
55164
+ if (idx !== -1) {
55165
+ this.listPeersResolvers.splice(idx, 1);
55166
+ resolve([]);
55167
+ }
55168
+ }, 5000);
55169
+ });
55170
+ }
55171
+ async setSummary(summary) {
55172
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
55173
+ return;
55174
+ this.ws.send(JSON.stringify({ type: "set_summary", summary }));
55175
+ }
55155
55176
  close() {
55156
55177
  this.closed = true;
55157
55178
  if (this.helloTimer)
@@ -55163,7 +55184,7 @@ class BrokerClient {
55163
55184
  this.ws.close();
55164
55185
  } catch {}
55165
55186
  }
55166
- this.setStatus("closed");
55187
+ this.setConnStatus("closed");
55167
55188
  }
55168
55189
  handleServerMessage(msg) {
55169
55190
  if (msg.type === "ack") {
@@ -55177,6 +55198,13 @@ class BrokerClient {
55177
55198
  }
55178
55199
  return;
55179
55200
  }
55201
+ if (msg.type === "peers_list") {
55202
+ const peers = msg.peers ?? [];
55203
+ const resolver = this.listPeersResolvers.shift();
55204
+ if (resolver)
55205
+ resolver(peers);
55206
+ return;
55207
+ }
55180
55208
  if (msg.type === "push") {
55181
55209
  const nonce = String(msg.nonce ?? "");
55182
55210
  const ciphertext = String(msg.ciphertext ?? "");
@@ -55239,7 +55267,7 @@ class BrokerClient {
55239
55267
  send();
55240
55268
  }
55241
55269
  scheduleReconnect() {
55242
- this.setStatus("reconnecting");
55270
+ this.setConnStatus("reconnecting");
55243
55271
  const delay = BACKOFF_CAPS[Math.min(this.reconnectAttempt, BACKOFF_CAPS.length - 1)];
55244
55272
  this.reconnectAttempt += 1;
55245
55273
  this.debug(`reconnect in ${delay}ms (attempt ${this.reconnectAttempt})`);
@@ -55251,7 +55279,7 @@ class BrokerClient {
55251
55279
  });
55252
55280
  }, delay);
55253
55281
  }
55254
- setStatus(s) {
55282
+ setConnStatus(s) {
55255
55283
  if (this._status === s)
55256
55284
  return;
55257
55285
  this._status = s;
@@ -55311,26 +55339,51 @@ function text(msg, isError = false) {
55311
55339
  ...isError ? { isError: true } : {}
55312
55340
  };
55313
55341
  }
55314
- function resolveClient(to) {
55342
+ async function resolveClient(to) {
55315
55343
  const clients2 = allClients();
55316
55344
  if (clients2.length === 0) {
55317
55345
  return { client: null, targetSpec: to, error: "no meshes joined" };
55318
55346
  }
55347
+ let targetClients = clients2;
55348
+ let target = to;
55319
55349
  const colonIdx = to.indexOf(":");
55320
55350
  if (colonIdx > 0 && colonIdx < to.length - 1) {
55321
55351
  const slug = to.slice(0, colonIdx);
55322
55352
  const rest = to.slice(colonIdx + 1);
55323
55353
  const match = findClient(slug);
55354
+ if (match) {
55355
+ targetClients = [match];
55356
+ target = rest;
55357
+ }
55358
+ }
55359
+ if (/^[0-9a-f]{64}$/.test(target) || target.startsWith("#") || target === "*") {
55360
+ if (targetClients.length === 1) {
55361
+ return { client: targetClients[0], targetSpec: target };
55362
+ }
55363
+ return {
55364
+ client: null,
55365
+ targetSpec: target,
55366
+ error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients2.map((c) => c.meshSlug).join(", ")})`
55367
+ };
55368
+ }
55369
+ const nameLower = target.toLowerCase();
55370
+ for (const c of targetClients) {
55371
+ const peers = await c.listPeers();
55372
+ const match = peers.find((p) => p.displayName.toLowerCase() === nameLower);
55324
55373
  if (match)
55325
- return { client: match, targetSpec: rest };
55374
+ return { client: c, targetSpec: match.pubkey };
55375
+ const partials = peers.filter((p) => p.displayName.toLowerCase().includes(nameLower));
55376
+ if (partials.length === 1) {
55377
+ return { client: c, targetSpec: partials[0].pubkey };
55378
+ }
55326
55379
  }
55327
- if (clients2.length === 1) {
55328
- return { client: clients2[0], targetSpec: to };
55380
+ if (targetClients.length === 1) {
55381
+ return { client: targetClients[0], targetSpec: target };
55329
55382
  }
55330
55383
  return {
55331
55384
  client: null,
55332
- targetSpec: to,
55333
- error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients2.map((c) => c.meshSlug).join(", ")})`
55385
+ targetSpec: target,
55386
+ error: `peer "${target}" not found in any mesh (joined: ${clients2.map((c) => c.meshSlug).join(", ")})`
55334
55387
  };
55335
55388
  }
55336
55389
  function decryptFailedWarning(senderPubkey) {
@@ -55357,7 +55410,7 @@ Read the from_id, from_name, mesh_slug, and priority attributes to understand co
55357
55410
 
55358
55411
  Available tools:
55359
55412
  - list_peers: see joined meshes + their connection status
55360
- - send_message: send to a peer pubkey, channel, or broadcast (priority: now/next/low)
55413
+ - send_message: send to a peer by display name, pubkey, #channel, or * broadcast (priority: now/next/low)
55361
55414
  - check_messages: drain buffered inbound messages (usually auto-pushed)
55362
55415
  - set_summary: 1-2 sentence summary of what you're working on
55363
55416
  - set_status: manually override your status (idle/working/dnd)
@@ -55382,7 +55435,7 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
55382
55435
  const { to, message, priority } = args ?? {};
55383
55436
  if (!to || !message)
55384
55437
  return text("send_message: `to` and `message` required", true);
55385
- const { client, targetSpec, error: error46 } = resolveClient(to);
55438
+ const { client, targetSpec, error: error46 } = await resolveClient(to);
55386
55439
  if (!client)
55387
55440
  return text(`send_message: ${error46 ?? "no client resolved"}`, true);
55388
55441
  const result = await client.send(targetSpec, message, priority ?? "next");
@@ -55395,12 +55448,26 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
55395
55448
  const clients2 = mesh_slug ? [findClient(mesh_slug)].filter(Boolean) : allClients();
55396
55449
  if (clients2.length === 0)
55397
55450
  return text(mesh_slug ? `list_peers: no joined mesh "${mesh_slug}"` : "list_peers: no joined meshes", true);
55398
- const lines = clients2.map((c) => `- ${c.meshSlug} (${c.status}, mesh ${c.meshId.slice(0, 8)}…)`);
55399
- return text(`Connected meshes:
55400
- ${lines.join(`
55401
- `)}
55451
+ const sections = [];
55452
+ for (const c of clients2) {
55453
+ const peers = await c.listPeers();
55454
+ const header = `## ${c.meshSlug} (${c.status}, mesh ${c.meshId.slice(0, 8)}…)`;
55455
+ if (peers.length === 0) {
55456
+ sections.push(`${header}
55457
+ No peers connected.`);
55458
+ } else {
55459
+ const peerLines = peers.map((p) => {
55460
+ const summary = p.summary ? ` — "${p.summary}"` : "";
55461
+ return `- **${p.displayName}** [${p.status}] (${p.pubkey.slice(0, 12)}…)${summary}`;
55462
+ });
55463
+ sections.push(`${header}
55464
+ ${peerLines.join(`
55465
+ `)}`);
55466
+ }
55467
+ }
55468
+ return text(sections.join(`
55402
55469
 
55403
- (list_peers WS protocol lands in Step 16; only mesh status is shown for now.)`);
55470
+ `));
55404
55471
  }
55405
55472
  case "check_messages": {
55406
55473
  const drained = [];
@@ -55423,7 +55490,9 @@ ${drained.join(`
55423
55490
  const { summary } = args ?? {};
55424
55491
  if (!summary)
55425
55492
  return text("set_summary: `summary` required", true);
55426
- return text(`set_summary: summary recorded locally ("${summary}"). (Broker WS protocol for summaries lands in Step 16.)`);
55493
+ for (const c of allClients())
55494
+ await c.setSummary(summary);
55495
+ return text(`Summary set: "${summary}" (visible to ${allClients().length} mesh(es)).`);
55427
55496
  }
55428
55497
  case "set_status": {
55429
55498
  const { status } = args ?? {};
@@ -55477,6 +55546,7 @@ ${drained.join(`
55477
55546
  // src/commands/install.ts
55478
55547
  import {
55479
55548
  chmodSync as chmodSync2,
55549
+ copyFileSync,
55480
55550
  existsSync as existsSync2,
55481
55551
  mkdirSync as mkdirSync2,
55482
55552
  readFileSync as readFileSync2,
@@ -55504,7 +55574,49 @@ function readClaudeConfig() {
55504
55574
  throw new Error(`failed to parse ${CLAUDE_CONFIG}: ${e instanceof Error ? e.message : String(e)}`);
55505
55575
  }
55506
55576
  }
55507
- function writeClaudeConfig(obj) {
55577
+ function backupClaudeConfig() {
55578
+ if (!existsSync2(CLAUDE_CONFIG))
55579
+ return;
55580
+ const backupDir = join2(dirname2(CLAUDE_CONFIG), ".claude", "backups");
55581
+ mkdirSync2(backupDir, { recursive: true });
55582
+ const ts = Date.now();
55583
+ const dest = join2(backupDir, `.claude.json.pre-claudemesh.${ts}`);
55584
+ copyFileSync(CLAUDE_CONFIG, dest);
55585
+ }
55586
+ function patchMcpServer(entry) {
55587
+ backupClaudeConfig();
55588
+ const cfg = readClaudeConfig();
55589
+ const servers = cfg.mcpServers ?? {};
55590
+ if (!cfg.mcpServers)
55591
+ cfg.mcpServers = servers;
55592
+ const existing = servers[MCP_NAME];
55593
+ let action;
55594
+ if (!existing) {
55595
+ servers[MCP_NAME] = entry;
55596
+ action = "added";
55597
+ } else if (entriesEqual(existing, entry)) {
55598
+ return "unchanged";
55599
+ } else {
55600
+ servers[MCP_NAME] = entry;
55601
+ action = "updated";
55602
+ }
55603
+ flushClaudeConfig(cfg);
55604
+ return action;
55605
+ }
55606
+ function removeMcpServer() {
55607
+ if (!existsSync2(CLAUDE_CONFIG))
55608
+ return false;
55609
+ backupClaudeConfig();
55610
+ const cfg = readClaudeConfig();
55611
+ const servers = cfg.mcpServers;
55612
+ if (!servers || !(MCP_NAME in servers))
55613
+ return false;
55614
+ delete servers[MCP_NAME];
55615
+ cfg.mcpServers = servers;
55616
+ flushClaudeConfig(cfg);
55617
+ return true;
55618
+ }
55619
+ function flushClaudeConfig(obj) {
55508
55620
  mkdirSync2(dirname2(CLAUDE_CONFIG), { recursive: true });
55509
55621
  writeFileSync2(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
55510
55622
  `, "utf-8");
@@ -55616,22 +55728,8 @@ function runInstall(args = []) {
55616
55728
  console.error(`✗ MCP entry not found at ${entry}`);
55617
55729
  process.exit(1);
55618
55730
  }
55619
- const cfg = readClaudeConfig();
55620
- const servers = (cfg.mcpServers ??= {}) ?? {};
55621
55731
  const desired = buildMcpEntry(entry);
55622
- const existing = servers[MCP_NAME];
55623
- let action;
55624
- if (!existing) {
55625
- servers[MCP_NAME] = desired;
55626
- action = "added";
55627
- } else if (entriesEqual(existing, desired)) {
55628
- action = "unchanged";
55629
- } else {
55630
- servers[MCP_NAME] = desired;
55631
- action = "updated";
55632
- }
55633
- cfg.mcpServers = servers;
55634
- writeClaudeConfig(cfg);
55732
+ const action = patchMcpServer(desired);
55635
55733
  const verify = readClaudeConfig();
55636
55734
  const verifyServers = verify.mcpServers ?? {};
55637
55735
  const stored = verifyServers[MCP_NAME];
@@ -55674,19 +55772,10 @@ function runInstall(args = []) {
55674
55772
  function runUninstall() {
55675
55773
  console.log("claudemesh uninstall");
55676
55774
  console.log("--------------------");
55677
- if (existsSync2(CLAUDE_CONFIG)) {
55678
- const cfg = readClaudeConfig();
55679
- const servers = cfg.mcpServers;
55680
- if (servers && MCP_NAME in servers) {
55681
- delete servers[MCP_NAME];
55682
- cfg.mcpServers = servers;
55683
- writeClaudeConfig(cfg);
55684
- console.log(`✓ MCP server "${MCP_NAME}" removed`);
55685
- } else {
55686
- console.log(`· MCP server "${MCP_NAME}" not present`);
55687
- }
55775
+ if (removeMcpServer()) {
55776
+ console.log(`✓ MCP server "${MCP_NAME}" removed`);
55688
55777
  } else {
55689
- console.log(`· no ${CLAUDE_CONFIG} MCP entry skipped`);
55778
+ console.log(`· MCP server "${MCP_NAME}" not present`);
55690
55779
  }
55691
55780
  try {
55692
55781
  const removed = uninstallHooks();
@@ -56086,7 +56175,7 @@ import { statSync, existsSync as existsSync3 } from "node:fs";
56086
56175
  // package.json
56087
56176
  var package_default = {
56088
56177
  name: "claudemesh-cli",
56089
- version: "0.1.4",
56178
+ version: "0.1.6",
56090
56179
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
56091
56180
  keywords: [
56092
56181
  "claude-code",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",