claudemesh-cli 0.6.6 → 0.6.7

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 +561 -152
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -47374,6 +47374,49 @@ var TOOLS = [
47374
47374
  }
47375
47375
  }
47376
47376
  },
47377
+ {
47378
+ name: "schedule_reminder",
47379
+ description: "Schedule a reminder message delivered back to yourself at a future time. The broker fires it as a push when the time arrives. Use to prompt yourself to check on something later.",
47380
+ inputSchema: {
47381
+ type: "object",
47382
+ properties: {
47383
+ message: { type: "string", description: "Reminder text" },
47384
+ deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver" },
47385
+ in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds" }
47386
+ },
47387
+ required: ["message"]
47388
+ }
47389
+ },
47390
+ {
47391
+ name: "send_later",
47392
+ description: "Send a message to a peer, @group, or broadcast (*) at a future time. The broker holds it and delivers when the time arrives.",
47393
+ inputSchema: {
47394
+ type: "object",
47395
+ properties: {
47396
+ to: { type: "string", description: "Recipient: display name, pubkey hex, @group, or *" },
47397
+ message: { type: "string", description: "Message text" },
47398
+ deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver" },
47399
+ in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds" }
47400
+ },
47401
+ required: ["to", "message"]
47402
+ }
47403
+ },
47404
+ {
47405
+ name: "list_scheduled",
47406
+ description: "List all your pending scheduled messages: id, recipient, preview, and delivery time.",
47407
+ inputSchema: { type: "object", properties: {} }
47408
+ },
47409
+ {
47410
+ name: "cancel_scheduled",
47411
+ description: "Cancel a pending scheduled message before it fires.",
47412
+ inputSchema: {
47413
+ type: "object",
47414
+ properties: {
47415
+ id: { type: "string", description: "Scheduled message ID" }
47416
+ },
47417
+ required: ["id"]
47418
+ }
47419
+ },
47377
47420
  {
47378
47421
  name: "mesh_info",
47379
47422
  description: "Get a complete overview of the mesh: peers, groups, state, memory, files, tasks, streams, tables. Call on session start for full situational awareness.",
@@ -47740,6 +47783,42 @@ class BrokerClient {
47740
47783
  return;
47741
47784
  this.ws.send(JSON.stringify({ type: "forget", memoryId }));
47742
47785
  }
47786
+ async scheduleMessage(to, message, deliverAt) {
47787
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47788
+ return null;
47789
+ return new Promise((resolve) => {
47790
+ const reqId = this.makeReqId();
47791
+ this.scheduledAckResolvers.set(reqId, { resolve, timer: setTimeout(() => {
47792
+ if (this.scheduledAckResolvers.delete(reqId))
47793
+ resolve(null);
47794
+ }, 8000) });
47795
+ this.ws.send(JSON.stringify({ type: "schedule", to, message, deliverAt, _reqId: reqId }));
47796
+ });
47797
+ }
47798
+ async listScheduled() {
47799
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47800
+ return [];
47801
+ return new Promise((resolve) => {
47802
+ const reqId = this.makeReqId();
47803
+ this.scheduledListResolvers.set(reqId, { resolve, timer: setTimeout(() => {
47804
+ if (this.scheduledListResolvers.delete(reqId))
47805
+ resolve([]);
47806
+ }, 5000) });
47807
+ this.ws.send(JSON.stringify({ type: "list_scheduled", _reqId: reqId }));
47808
+ });
47809
+ }
47810
+ async cancelScheduled(scheduledId) {
47811
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47812
+ return false;
47813
+ return new Promise((resolve) => {
47814
+ const reqId = this.makeReqId();
47815
+ this.cancelScheduledResolvers.set(reqId, { resolve, timer: setTimeout(() => {
47816
+ if (this.cancelScheduledResolvers.delete(reqId))
47817
+ resolve(false);
47818
+ }, 5000) });
47819
+ this.ws.send(JSON.stringify({ type: "cancel_scheduled", scheduledId, _reqId: reqId }));
47820
+ });
47821
+ }
47743
47822
  messageStatusResolvers = new Map;
47744
47823
  fileUrlResolvers = new Map;
47745
47824
  fileListResolvers = new Map;
@@ -47757,6 +47836,9 @@ class BrokerClient {
47757
47836
  streamCreatedResolvers = new Map;
47758
47837
  streamListResolvers = new Map;
47759
47838
  streamDataHandlers = new Set;
47839
+ scheduledAckResolvers = new Map;
47840
+ scheduledListResolvers = new Map;
47841
+ cancelScheduledResolvers = new Map;
47760
47842
  async messageStatus(messageId) {
47761
47843
  if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47762
47844
  return null;
@@ -48319,6 +48401,22 @@ class BrokerClient {
48319
48401
  this.resolveFromMap(this.meshInfoResolvers, msgReqId, msg);
48320
48402
  return;
48321
48403
  }
48404
+ if (msg.type === "scheduled_ack") {
48405
+ this.resolveFromMap(this.scheduledAckResolvers, msgReqId, {
48406
+ scheduledId: String(msg.scheduledId ?? ""),
48407
+ deliverAt: Number(msg.deliverAt ?? 0)
48408
+ });
48409
+ return;
48410
+ }
48411
+ if (msg.type === "scheduled_list") {
48412
+ const messages = msg.messages ?? [];
48413
+ this.resolveFromMap(this.scheduledListResolvers, msgReqId, messages);
48414
+ return;
48415
+ }
48416
+ if (msg.type === "cancel_scheduled_ack") {
48417
+ this.resolveFromMap(this.cancelScheduledResolvers, msgReqId, Boolean(msg.ok));
48418
+ return;
48419
+ }
48322
48420
  if (msg.type === "error") {
48323
48421
  this.debug(`broker error: ${msg.code} ${msg.message}`);
48324
48422
  const id = msg.id ? String(msg.id) : null;
@@ -48351,6 +48449,9 @@ class BrokerClient {
48351
48449
  [this.contextResultsResolvers, []],
48352
48450
  [this.contextListResolvers, []],
48353
48451
  [this.streamListResolvers, []],
48452
+ [this.scheduledAckResolvers, null],
48453
+ [this.scheduledListResolvers, []],
48454
+ [this.cancelScheduledResolvers, false],
48354
48455
  [this.messageStatusResolvers, null],
48355
48456
  [this.grantFileAccessResolvers, false],
48356
48457
  [this.collectionListResolvers, []],
@@ -48418,17 +48519,17 @@ async function ensureClient(mesh) {
48418
48519
  const existing = clients.get(mesh.meshId);
48419
48520
  if (existing)
48420
48521
  return existing;
48421
- const client = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG, displayName: configDisplayName });
48422
- clients.set(mesh.meshId, client);
48522
+ const client2 = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG, displayName: configDisplayName });
48523
+ clients.set(mesh.meshId, client2);
48423
48524
  try {
48424
- await client.connect();
48525
+ await client2.connect();
48425
48526
  for (const g of configGroups ?? []) {
48426
48527
  try {
48427
- await client.joinGroup(g.name, g.role);
48528
+ await client2.joinGroup(g.name, g.role);
48428
48529
  } catch {}
48429
48530
  }
48430
48531
  } catch {}
48431
- return client;
48532
+ return client2;
48432
48533
  }
48433
48534
  async function startClients(config2) {
48434
48535
  configDisplayName = config2.displayName;
@@ -48511,12 +48612,12 @@ async function resolveClient(to) {
48511
48612
  var peerNameCache = new Map;
48512
48613
  var peerNameCacheAge = 0;
48513
48614
  var CACHE_TTL_MS = 30000;
48514
- async function resolvePeerName(client, pubkey) {
48615
+ async function resolvePeerName(client2, pubkey) {
48515
48616
  const now = Date.now();
48516
48617
  if (now - peerNameCacheAge > CACHE_TTL_MS) {
48517
48618
  peerNameCache.clear();
48518
48619
  try {
48519
- const peers = await client.listPeers();
48620
+ const peers = await client2.listPeers();
48520
48621
  for (const p of peers)
48521
48622
  peerNameCache.set(p.pubkey, p.displayName);
48522
48623
  } catch {}
@@ -48666,15 +48767,15 @@ Your message mode is "${messageMode}".
48666
48767
  const results = [];
48667
48768
  const seen = new Set;
48668
48769
  for (const target of targets) {
48669
- const { client, targetSpec, error: error2 } = await resolveClient(target);
48670
- if (!client) {
48770
+ const { client: client2, targetSpec, error: error2 } = await resolveClient(target);
48771
+ if (!client2) {
48671
48772
  results.push(`✗ ${target}: ${error2 ?? "no client resolved"}`);
48672
48773
  continue;
48673
48774
  }
48674
48775
  if (seen.has(targetSpec))
48675
48776
  continue;
48676
48777
  seen.add(targetSpec);
48677
- const result = await client.send(targetSpec, message, priority ?? "next");
48778
+ const result = await client2.send(targetSpec, message, priority ?? "next");
48678
48779
  if (!result.ok) {
48679
48780
  results.push(`✗ ${target}: ${result.error}`);
48680
48781
  } else {
@@ -48795,19 +48896,19 @@ ${drained.join(`
48795
48896
  const { key } = args ?? {};
48796
48897
  if (!key)
48797
48898
  return text("get_state: `key` required", true);
48798
- const client = allClients()[0];
48799
- if (!client)
48899
+ const client2 = allClients()[0];
48900
+ if (!client2)
48800
48901
  return text("get_state: not connected", true);
48801
- const result = await client.getState(key);
48902
+ const result = await client2.getState(key);
48802
48903
  if (!result)
48803
48904
  return text(`State "${key}" not found.`);
48804
48905
  return text(`${key} = ${JSON.stringify(result.value)} (set by ${result.updatedBy} at ${result.updatedAt})`);
48805
48906
  }
48806
48907
  case "list_state": {
48807
- const client = allClients()[0];
48808
- if (!client)
48908
+ const client2 = allClients()[0];
48909
+ if (!client2)
48809
48910
  return text("list_state: not connected", true);
48810
- const entries = await client.listState();
48911
+ const entries = await client2.listState();
48811
48912
  if (entries.length === 0)
48812
48913
  return text("No shared state set.");
48813
48914
  const lines = entries.map((e) => `- **${e.key}** = ${JSON.stringify(e.value)} (by ${e.updatedBy})`);
@@ -48818,20 +48919,20 @@ ${drained.join(`
48818
48919
  const { content, tags } = args ?? {};
48819
48920
  if (!content)
48820
48921
  return text("remember: `content` required", true);
48821
- const client = allClients()[0];
48822
- if (!client)
48922
+ const client2 = allClients()[0];
48923
+ if (!client2)
48823
48924
  return text("remember: not connected", true);
48824
- const id = await client.remember(content, tags);
48925
+ const id = await client2.remember(content, tags);
48825
48926
  return text(`Remembered${id ? ` (${id})` : ""}: "${content.slice(0, 80)}${content.length > 80 ? "..." : ""}"`);
48826
48927
  }
48827
48928
  case "recall": {
48828
48929
  const { query } = args ?? {};
48829
48930
  if (!query)
48830
48931
  return text("recall: `query` required", true);
48831
- const client = allClients()[0];
48832
- if (!client)
48932
+ const client2 = allClients()[0];
48933
+ if (!client2)
48833
48934
  return text("recall: not connected", true);
48834
- const memories = await client.recall(query);
48935
+ const memories = await client2.recall(query);
48835
48936
  if (memories.length === 0)
48836
48937
  return text(`No memories found for "${query}".`);
48837
48938
  const lines = memories.map((m) => `- [${m.id.slice(0, 8)}] ${m.content} (by ${m.rememberedBy}, ${m.rememberedAt})`);
@@ -48843,12 +48944,63 @@ ${lines.join(`
48843
48944
  const { id } = args ?? {};
48844
48945
  if (!id)
48845
48946
  return text("forget: `id` required", true);
48846
- const client = allClients()[0];
48847
- if (!client)
48947
+ const client2 = allClients()[0];
48948
+ if (!client2)
48848
48949
  return text("forget: not connected", true);
48849
- await client.forget(id);
48950
+ await client2.forget(id);
48850
48951
  return text(`Forgotten: ${id}`);
48851
48952
  }
48953
+ case "schedule_reminder":
48954
+ case "send_later": {
48955
+ const sArgs = args ?? {};
48956
+ if (!sArgs.message)
48957
+ return text(`${name}: \`message\` required`, true);
48958
+ const to = name === "schedule_reminder" ? "self" : sArgs.to ?? "";
48959
+ if (name === "send_later" && !to)
48960
+ return text("send_later: `to` required", true);
48961
+ let deliverAt;
48962
+ if (sArgs.deliver_at) {
48963
+ deliverAt = Number(sArgs.deliver_at);
48964
+ } else if (sArgs.in_seconds) {
48965
+ deliverAt = Date.now() + Number(sArgs.in_seconds) * 1000;
48966
+ } else {
48967
+ return text(`${name}: provide \`deliver_at\` (ms timestamp) or \`in_seconds\``, true);
48968
+ }
48969
+ let targetSpec = to;
48970
+ if (name === "send_later" && !to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to) && to !== "self") {
48971
+ const peers = await client.listPeers();
48972
+ const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
48973
+ if (!match) {
48974
+ const names = peers.map((p) => p.displayName).join(", ");
48975
+ return text(`send_later: peer "${to}" not found. Online: ${names || "(none)"}`, true);
48976
+ }
48977
+ targetSpec = match.pubkey;
48978
+ }
48979
+ if (name === "schedule_reminder") {
48980
+ targetSpec = client.getSessionPubkey() ?? "*";
48981
+ }
48982
+ const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt);
48983
+ if (!result)
48984
+ return text(`${name}: broker did not acknowledge — check connection`, true);
48985
+ const when = new Date(result.deliverAt).toISOString();
48986
+ return text(name === "schedule_reminder" ? `Reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" at ${when}` : `Message to "${to}" scheduled (${result.scheduledId.slice(0, 8)}) for ${when}`);
48987
+ }
48988
+ case "list_scheduled": {
48989
+ const scheduled = await client.listScheduled();
48990
+ if (scheduled.length === 0)
48991
+ return text("No pending scheduled messages.");
48992
+ const lines = scheduled.map((m) => `- [${m.id.slice(0, 8)}] → ${m.to === client.getSessionPubkey() ? "self (reminder)" : m.to} at ${new Date(m.deliverAt).toISOString()}: "${m.message.slice(0, 60)}${m.message.length > 60 ? "…" : ""}"`);
48993
+ return text(`${scheduled.length} scheduled:
48994
+ ${lines.join(`
48995
+ `)}`);
48996
+ }
48997
+ case "cancel_scheduled": {
48998
+ const { id: schedId } = args ?? {};
48999
+ if (!schedId)
49000
+ return text("cancel_scheduled: `id` required", true);
49001
+ const ok = await client.cancelScheduled(schedId);
49002
+ return text(ok ? `Cancelled: ${schedId}` : `Not found or already fired: ${schedId}`, !ok);
49003
+ }
48852
49004
  case "share_file": {
48853
49005
  const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
48854
49006
  if (!filePath)
@@ -48856,15 +49008,15 @@ ${lines.join(`
48856
49008
  const { existsSync: existsSync2 } = await import("node:fs");
48857
49009
  if (!existsSync2(filePath))
48858
49010
  return text(`share_file: file not found: ${filePath}`, true);
48859
- const client = allClients()[0];
48860
- if (!client)
49011
+ const client2 = allClients()[0];
49012
+ if (!client2)
48861
49013
  return text("share_file: not connected", true);
48862
49014
  if (fileTo) {
48863
49015
  const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
48864
49016
  const { readFileSync: readFileSync2, writeFileSync: writeFileSync2, mkdtempSync, unlinkSync, rmdirSync } = await import("node:fs");
48865
49017
  const { tmpdir } = await import("node:os");
48866
49018
  const { join: join2, basename } = await import("node:path");
48867
- const peers = await client.listPeers();
49019
+ const peers = await client2.listPeers();
48868
49020
  const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
48869
49021
  if (!targetPeer) {
48870
49022
  return text(`share_file: peer not found: ${fileTo}`, true);
@@ -48872,7 +49024,7 @@ ${lines.join(`
48872
49024
  const plaintext = readFileSync2(filePath);
48873
49025
  const { ciphertext, nonce, key } = await encryptFile2(new Uint8Array(plaintext));
48874
49026
  const sealedForTarget = await sealKeyForPeer2(key, targetPeer.pubkey);
48875
- const myPubkey = client.getSessionPubkey();
49027
+ const myPubkey = client2.getSessionPubkey();
48876
49028
  const sealedForSelf = myPubkey ? await sealKeyForPeer2(key, myPubkey) : null;
48877
49029
  const fileKeys = [
48878
49030
  { peerPubkey: targetPeer.pubkey, sealedKey: sealedForTarget },
@@ -48889,7 +49041,7 @@ ${lines.join(`
48889
49041
  const tmpPath = join2(tmpDir, baseName);
48890
49042
  writeFileSync2(tmpPath, combined);
48891
49043
  try {
48892
- const fileId = await client.uploadFile(tmpPath, client.meshId, client.meshSlug, {
49044
+ const fileId = await client2.uploadFile(tmpPath, client2.meshId, client2.meshSlug, {
48893
49045
  name: baseName,
48894
49046
  tags,
48895
49047
  persistent: true,
@@ -48910,7 +49062,7 @@ ${lines.join(`
48910
49062
  }
48911
49063
  }
48912
49064
  try {
48913
- const fileId = await client.uploadFile(filePath, client.meshId, client.meshSlug, {
49065
+ const fileId = await client2.uploadFile(filePath, client2.meshId, client2.meshSlug, {
48914
49066
  name: fileName,
48915
49067
  tags,
48916
49068
  persistent: true
@@ -48924,10 +49076,10 @@ ${lines.join(`
48924
49076
  const { id, save_to } = args ?? {};
48925
49077
  if (!id || !save_to)
48926
49078
  return text("get_file: `id` and `save_to` required", true);
48927
- const client = allClients()[0];
48928
- if (!client)
49079
+ const client2 = allClients()[0];
49080
+ if (!client2)
48929
49081
  return text("get_file: not connected", true);
48930
- const result = await client.getFile(id);
49082
+ const result = await client2.getFile(id);
48931
49083
  if (!result)
48932
49084
  return text(`get_file: file ${id} not found`, true);
48933
49085
  if (result.encrypted) {
@@ -48935,8 +49087,8 @@ ${lines.join(`
48935
49087
  return text("get_file: encrypted file — no decryption key available for your session", true);
48936
49088
  const { openSealedKey: openSealedKey2, decryptFile: decryptFile2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
48937
49089
  const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
48938
- const myPubkey = client.getSessionPubkey();
48939
- const mySecret = client.getSessionSecretKey();
49090
+ const myPubkey = client2.getSessionPubkey();
49091
+ const mySecret = client2.getSessionSecretKey();
48940
49092
  if (!myPubkey || !mySecret) {
48941
49093
  return text("get_file: no session keypair — cannot decrypt", true);
48942
49094
  }
@@ -48971,10 +49123,10 @@ ${lines.join(`
48971
49123
  }
48972
49124
  case "list_files": {
48973
49125
  const { query, from } = args ?? {};
48974
- const client = allClients()[0];
48975
- if (!client)
49126
+ const client2 = allClients()[0];
49127
+ if (!client2)
48976
49128
  return text("list_files: not connected", true);
48977
- const files = await client.listFiles(query, from);
49129
+ const files = await client2.listFiles(query, from);
48978
49130
  if (files.length === 0)
48979
49131
  return text("No files found.");
48980
49132
  const lines = files.map((f) => `- **${f.name}** (${f.id.slice(0, 8)}…, ${f.size} bytes) by ${f.uploadedBy}${f.tags.length ? ` [${f.tags.join(", ")}]` : ""}`);
@@ -48985,10 +49137,10 @@ ${lines.join(`
48985
49137
  const { id } = args ?? {};
48986
49138
  if (!id)
48987
49139
  return text("file_status: `id` required", true);
48988
- const client = allClients()[0];
48989
- if (!client)
49140
+ const client2 = allClients()[0];
49141
+ if (!client2)
48990
49142
  return text("file_status: not connected", true);
48991
- const accesses = await client.fileStatus(id);
49143
+ const accesses = await client2.fileStatus(id);
48992
49144
  if (accesses.length === 0)
48993
49145
  return text("No one has accessed this file yet.");
48994
49146
  const lines = accesses.map((a) => `- ${a.peerName} at ${a.accessedAt}`);
@@ -49000,30 +49152,30 @@ ${lines.join(`
49000
49152
  const { id } = args ?? {};
49001
49153
  if (!id)
49002
49154
  return text("delete_file: `id` required", true);
49003
- const client = allClients()[0];
49004
- if (!client)
49155
+ const client2 = allClients()[0];
49156
+ if (!client2)
49005
49157
  return text("delete_file: not connected", true);
49006
- await client.deleteFile(id);
49158
+ await client2.deleteFile(id);
49007
49159
  return text(`Deleted: ${id}`);
49008
49160
  }
49009
49161
  case "vector_store": {
49010
49162
  const { collection, text: storeText, metadata } = args ?? {};
49011
49163
  if (!collection || !storeText)
49012
49164
  return text("vector_store: `collection` and `text` required", true);
49013
- const client = allClients()[0];
49014
- if (!client)
49165
+ const client2 = allClients()[0];
49166
+ if (!client2)
49015
49167
  return text("vector_store: not connected", true);
49016
- const id = await client.vectorStore(collection, storeText, metadata);
49168
+ const id = await client2.vectorStore(collection, storeText, metadata);
49017
49169
  return text(`Stored in ${collection}${id ? ` (${id})` : ""}`);
49018
49170
  }
49019
49171
  case "vector_search": {
49020
49172
  const { collection, query, limit } = args ?? {};
49021
49173
  if (!collection || !query)
49022
49174
  return text("vector_search: `collection` and `query` required", true);
49023
- const client = allClients()[0];
49024
- if (!client)
49175
+ const client2 = allClients()[0];
49176
+ if (!client2)
49025
49177
  return text("vector_search: not connected", true);
49026
- const results = await client.vectorSearch(collection, query, limit);
49178
+ const results = await client2.vectorSearch(collection, query, limit);
49027
49179
  if (results.length === 0)
49028
49180
  return text(`No results in ${collection} for "${query}".`);
49029
49181
  const lines = results.map((r) => `- [${r.id.slice(0, 8)}…] (score: ${r.score.toFixed(3)}) ${r.text.slice(0, 120)}${r.text.length > 120 ? "…" : ""}`);
@@ -49035,17 +49187,17 @@ ${lines.join(`
49035
49187
  const { collection, id } = args ?? {};
49036
49188
  if (!collection || !id)
49037
49189
  return text("vector_delete: `collection` and `id` required", true);
49038
- const client = allClients()[0];
49039
- if (!client)
49190
+ const client2 = allClients()[0];
49191
+ if (!client2)
49040
49192
  return text("vector_delete: not connected", true);
49041
- await client.vectorDelete(collection, id);
49193
+ await client2.vectorDelete(collection, id);
49042
49194
  return text(`Deleted ${id} from ${collection}`);
49043
49195
  }
49044
49196
  case "list_collections": {
49045
- const client = allClients()[0];
49046
- if (!client)
49197
+ const client2 = allClients()[0];
49198
+ if (!client2)
49047
49199
  return text("list_collections: not connected", true);
49048
- const collections = await client.listCollections();
49200
+ const collections = await client2.listCollections();
49049
49201
  if (collections.length === 0)
49050
49202
  return text("No vector collections.");
49051
49203
  return text(`Collections:
@@ -49056,10 +49208,10 @@ ${collections.map((c) => `- ${c}`).join(`
49056
49208
  const { cypher } = args ?? {};
49057
49209
  if (!cypher)
49058
49210
  return text("graph_query: `cypher` required", true);
49059
- const client = allClients()[0];
49060
- if (!client)
49211
+ const client2 = allClients()[0];
49212
+ if (!client2)
49061
49213
  return text("graph_query: not connected", true);
49062
- const rows = await client.graphQuery(cypher);
49214
+ const rows = await client2.graphQuery(cypher);
49063
49215
  if (rows.length === 0)
49064
49216
  return text("No results.");
49065
49217
  return text(JSON.stringify(rows, null, 2));
@@ -49068,30 +49220,30 @@ ${collections.map((c) => `- ${c}`).join(`
49068
49220
  const { cypher } = args ?? {};
49069
49221
  if (!cypher)
49070
49222
  return text("graph_execute: `cypher` required", true);
49071
- const client = allClients()[0];
49072
- if (!client)
49223
+ const client2 = allClients()[0];
49224
+ if (!client2)
49073
49225
  return text("graph_execute: not connected", true);
49074
- const rows = await client.graphExecute(cypher);
49226
+ const rows = await client2.graphExecute(cypher);
49075
49227
  return text(rows.length > 0 ? JSON.stringify(rows, null, 2) : "Executed successfully.");
49076
49228
  }
49077
49229
  case "share_context": {
49078
49230
  const { summary, files_read, key_findings, tags } = args ?? {};
49079
49231
  if (!summary)
49080
49232
  return text("share_context: `summary` required", true);
49081
- const client = allClients()[0];
49082
- if (!client)
49233
+ const client2 = allClients()[0];
49234
+ if (!client2)
49083
49235
  return text("share_context: not connected", true);
49084
- await client.shareContext(summary, files_read, key_findings, tags);
49236
+ await client2.shareContext(summary, files_read, key_findings, tags);
49085
49237
  return text(`Context shared: "${summary.slice(0, 80)}${summary.length > 80 ? "…" : ""}"`);
49086
49238
  }
49087
49239
  case "get_context": {
49088
49240
  const { query } = args ?? {};
49089
49241
  if (!query)
49090
49242
  return text("get_context: `query` required", true);
49091
- const client = allClients()[0];
49092
- if (!client)
49243
+ const client2 = allClients()[0];
49244
+ if (!client2)
49093
49245
  return text("get_context: not connected", true);
49094
- const contexts = await client.getContext(query);
49246
+ const contexts = await client2.getContext(query);
49095
49247
  if (contexts.length === 0)
49096
49248
  return text(`No context found for "${query}".`);
49097
49249
  const lines = contexts.map((c) => {
@@ -49106,10 +49258,10 @@ ${lines.join(`
49106
49258
  `)}`);
49107
49259
  }
49108
49260
  case "list_contexts": {
49109
- const client = allClients()[0];
49110
- if (!client)
49261
+ const client2 = allClients()[0];
49262
+ if (!client2)
49111
49263
  return text("list_contexts: not connected", true);
49112
- const contexts = await client.listContexts();
49264
+ const contexts = await client2.listContexts();
49113
49265
  if (contexts.length === 0)
49114
49266
  return text("No peer contexts shared yet.");
49115
49267
  const lines = contexts.map((c) => `- **${c.peerName}**: ${c.summary}${c.tags.length ? ` [${c.tags.join(", ")}]` : ""}`);
@@ -49121,38 +49273,38 @@ ${lines.join(`
49121
49273
  const { title, assignee, priority, tags } = args ?? {};
49122
49274
  if (!title)
49123
49275
  return text("create_task: `title` required", true);
49124
- const client = allClients()[0];
49125
- if (!client)
49276
+ const client2 = allClients()[0];
49277
+ if (!client2)
49126
49278
  return text("create_task: not connected", true);
49127
- const id = await client.createTask(title, assignee, priority, tags);
49279
+ const id = await client2.createTask(title, assignee, priority, tags);
49128
49280
  return text(`Task created${id ? ` (${id})` : ""}: "${title}"${assignee ? ` → ${assignee}` : ""}`);
49129
49281
  }
49130
49282
  case "claim_task": {
49131
49283
  const { id } = args ?? {};
49132
49284
  if (!id)
49133
49285
  return text("claim_task: `id` required", true);
49134
- const client = allClients()[0];
49135
- if (!client)
49286
+ const client2 = allClients()[0];
49287
+ if (!client2)
49136
49288
  return text("claim_task: not connected", true);
49137
- await client.claimTask(id);
49289
+ await client2.claimTask(id);
49138
49290
  return text(`Claimed task: ${id}`);
49139
49291
  }
49140
49292
  case "complete_task": {
49141
49293
  const { id, result } = args ?? {};
49142
49294
  if (!id)
49143
49295
  return text("complete_task: `id` required", true);
49144
- const client = allClients()[0];
49145
- if (!client)
49296
+ const client2 = allClients()[0];
49297
+ if (!client2)
49146
49298
  return text("complete_task: not connected", true);
49147
- await client.completeTask(id, result);
49299
+ await client2.completeTask(id, result);
49148
49300
  return text(`Completed task: ${id}${result ? ` — ${result}` : ""}`);
49149
49301
  }
49150
49302
  case "list_tasks": {
49151
49303
  const { status, assignee } = args ?? {};
49152
- const client = allClients()[0];
49153
- if (!client)
49304
+ const client2 = allClients()[0];
49305
+ if (!client2)
49154
49306
  return text("list_tasks: not connected", true);
49155
- const tasks = await client.listTasks(status, assignee);
49307
+ const tasks = await client2.listTasks(status, assignee);
49156
49308
  if (tasks.length === 0)
49157
49309
  return text("No tasks found.");
49158
49310
  const lines = tasks.map((t) => `- [${t.id.slice(0, 8)}…] **${t.title}** (${t.status}, ${t.priority}) ${t.assignee ? `→ ${t.assignee}` : "unassigned"} (by ${t.createdBy})`);
@@ -49164,10 +49316,10 @@ ${lines.join(`
49164
49316
  const { sql: querySql } = args ?? {};
49165
49317
  if (!querySql)
49166
49318
  return text("mesh_query: `sql` required", true);
49167
- const client = allClients()[0];
49168
- if (!client)
49319
+ const client2 = allClients()[0];
49320
+ if (!client2)
49169
49321
  return text("mesh_query: not connected", true);
49170
- const result = await client.meshQuery(querySql);
49322
+ const result = await client2.meshQuery(querySql);
49171
49323
  if (!result)
49172
49324
  return text("mesh_query: query failed or timed out", true);
49173
49325
  if (result.rows.length === 0)
@@ -49185,17 +49337,17 @@ ${rows.join(`
49185
49337
  const { sql: execSql } = args ?? {};
49186
49338
  if (!execSql)
49187
49339
  return text("mesh_execute: `sql` required", true);
49188
- const client = allClients()[0];
49189
- if (!client)
49340
+ const client2 = allClients()[0];
49341
+ if (!client2)
49190
49342
  return text("mesh_execute: not connected", true);
49191
- await client.meshExecute(execSql);
49343
+ await client2.meshExecute(execSql);
49192
49344
  return text(`Executed.`);
49193
49345
  }
49194
49346
  case "mesh_schema": {
49195
- const client = allClients()[0];
49196
- if (!client)
49347
+ const client2 = allClients()[0];
49348
+ if (!client2)
49197
49349
  return text("mesh_schema: not connected", true);
49198
- const tables = await client.meshSchema();
49350
+ const tables = await client2.meshSchema();
49199
49351
  if (!tables || tables.length === 0)
49200
49352
  return text("No tables in mesh database.");
49201
49353
  const lines = tables.map((t) => `**${t.name}**: ${t.columns.map((c) => `${c.name} (${c.type}${c.nullable ? ", nullable" : ""})`).join(", ")}`);
@@ -49206,37 +49358,37 @@ ${rows.join(`
49206
49358
  const { name: streamName } = args ?? {};
49207
49359
  if (!streamName)
49208
49360
  return text("create_stream: `name` required", true);
49209
- const client = allClients()[0];
49210
- if (!client)
49361
+ const client2 = allClients()[0];
49362
+ if (!client2)
49211
49363
  return text("create_stream: not connected", true);
49212
- const streamId = await client.createStream(streamName);
49364
+ const streamId = await client2.createStream(streamName);
49213
49365
  return text(`Stream created: ${streamName}${streamId ? ` (${streamId})` : ""}`);
49214
49366
  }
49215
49367
  case "publish": {
49216
49368
  const { stream: pubStream, data: pubData } = args ?? {};
49217
49369
  if (!pubStream)
49218
49370
  return text("publish: `stream` required", true);
49219
- const client = allClients()[0];
49220
- if (!client)
49371
+ const client2 = allClients()[0];
49372
+ if (!client2)
49221
49373
  return text("publish: not connected", true);
49222
- await client.publish(pubStream, pubData);
49374
+ await client2.publish(pubStream, pubData);
49223
49375
  return text(`Published to ${pubStream}.`);
49224
49376
  }
49225
49377
  case "subscribe": {
49226
49378
  const { stream: subStream } = args ?? {};
49227
49379
  if (!subStream)
49228
49380
  return text("subscribe: `stream` required", true);
49229
- const client = allClients()[0];
49230
- if (!client)
49381
+ const client2 = allClients()[0];
49382
+ if (!client2)
49231
49383
  return text("subscribe: not connected", true);
49232
- await client.subscribe(subStream);
49384
+ await client2.subscribe(subStream);
49233
49385
  return text(`Subscribed to ${subStream}. Data pushes will arrive as channel notifications.`);
49234
49386
  }
49235
49387
  case "list_streams": {
49236
- const client = allClients()[0];
49237
- if (!client)
49388
+ const client2 = allClients()[0];
49389
+ if (!client2)
49238
49390
  return text("list_streams: not connected", true);
49239
- const streams = await client.listStreams();
49391
+ const streams = await client2.listStreams();
49240
49392
  if (streams.length === 0)
49241
49393
  return text("No active streams.");
49242
49394
  const lines = streams.map((s) => `- **${s.name}** (${s.id.slice(0, 8)}…) by ${s.createdBy}, ${s.subscriberCount} subscriber(s)`);
@@ -49244,10 +49396,10 @@ ${rows.join(`
49244
49396
  `));
49245
49397
  }
49246
49398
  case "mesh_info": {
49247
- const client = allClients()[0];
49248
- if (!client)
49399
+ const client2 = allClients()[0];
49400
+ if (!client2)
49249
49401
  return text("mesh_info: not connected", true);
49250
- const info = await client.meshInfo();
49402
+ const info = await client2.meshInfo();
49251
49403
  if (!info)
49252
49404
  return text("mesh_info: timed out", true);
49253
49405
  const lines = [
@@ -49269,21 +49421,21 @@ ${rows.join(`
49269
49421
  case "ping_mesh": {
49270
49422
  const { priorities: pingPriorities } = args ?? {};
49271
49423
  const toTest = pingPriorities ?? ["now", "next"];
49272
- const client = allClients()[0];
49273
- if (!client)
49424
+ const client2 = allClients()[0];
49425
+ if (!client2)
49274
49426
  return text("ping_mesh: not connected", true);
49275
49427
  const results = [];
49276
- results.push(`WS status: ${client.status}`);
49277
- results.push(`Mesh: ${client.meshSlug}`);
49278
- const peers = await client.listPeers();
49428
+ results.push(`WS status: ${client2.status}`);
49429
+ results.push(`Mesh: ${client2.meshSlug}`);
49430
+ const peers = await client2.listPeers();
49279
49431
  const selfPeer = peers.find((p) => p.displayName === myName);
49280
49432
  results.push(`Your status: ${selfPeer?.status ?? "not found in peer list"}`);
49281
49433
  results.push(`Peers online: ${peers.length}`);
49282
- results.push(`Push buffer: ${client.pushHistory.length} buffered`);
49434
+ results.push(`Push buffer: ${client2.pushHistory.length} buffered`);
49283
49435
  for (const prio of toTest) {
49284
49436
  const sendTime = Date.now();
49285
49437
  const target = peers.find((p) => p.displayName !== myName);
49286
- const sendResult = await client.send(target?.pubkey ?? "*", `__ping__ ${prio} from ${myName} at ${new Date().toISOString()}`, prio);
49438
+ const sendResult = await client2.send(target?.pubkey ?? "*", `__ping__ ${prio} from ${myName} at ${new Date().toISOString()}`, prio);
49287
49439
  const ackTime = Date.now();
49288
49440
  if (!sendResult.ok) {
49289
49441
  results.push(`[${prio}] SEND FAILED: ${sendResult.error}`);
@@ -49306,14 +49458,14 @@ ${rows.join(`
49306
49458
  const { fileId, to: grantTo } = args ?? {};
49307
49459
  if (!fileId || !grantTo)
49308
49460
  return text("grant_file_access: `fileId` and `to` required", true);
49309
- const client = allClients()[0];
49310
- if (!client)
49461
+ const client2 = allClients()[0];
49462
+ if (!client2)
49311
49463
  return text("grant_file_access: not connected", true);
49312
- const peers = await client.listPeers();
49464
+ const peers = await client2.listPeers();
49313
49465
  const targetPeer = peers.find((p) => p.pubkey === grantTo || p.displayName === grantTo);
49314
49466
  if (!targetPeer)
49315
49467
  return text(`grant_file_access: peer not found: ${grantTo}`, true);
49316
- const result = await client.getFile(fileId);
49468
+ const result = await client2.getFile(fileId);
49317
49469
  if (!result)
49318
49470
  return text("grant_file_access: file not found", true);
49319
49471
  if (!result.encrypted)
@@ -49321,15 +49473,15 @@ ${rows.join(`
49321
49473
  if (!result.sealedKey)
49322
49474
  return text("grant_file_access: no key available (are you the owner?)", true);
49323
49475
  const { openSealedKey: openSealedKey2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
49324
- const myPubkey = client.getSessionPubkey();
49325
- const mySecret = client.getSessionSecretKey();
49476
+ const myPubkey = client2.getSessionPubkey();
49477
+ const mySecret = client2.getSessionSecretKey();
49326
49478
  if (!myPubkey || !mySecret)
49327
49479
  return text("grant_file_access: no session keypair", true);
49328
49480
  const kf = await openSealedKey2(result.sealedKey, myPubkey, mySecret);
49329
49481
  if (!kf)
49330
49482
  return text("grant_file_access: cannot decrypt your own key", true);
49331
49483
  const sealedForPeer = await sealKeyForPeer2(kf, targetPeer.pubkey);
49332
- const ok = await client.grantFileAccess(fileId, targetPeer.pubkey, sealedForPeer);
49484
+ const ok = await client2.grantFileAccess(fileId, targetPeer.pubkey, sealedForPeer);
49333
49485
  if (!ok)
49334
49486
  return text("grant_file_access: broker did not confirm", true);
49335
49487
  return text(`Access granted: ${targetPeer.displayName} can now download file ${fileId}`);
@@ -49341,12 +49493,12 @@ ${rows.join(`
49341
49493
  await startClients(config2);
49342
49494
  const transport = new StdioServerTransport;
49343
49495
  await server.connect(transport);
49344
- for (const client of allClients()) {
49345
- client.onPush(async (msg) => {
49496
+ for (const client2 of allClients()) {
49497
+ client2.onPush(async (msg) => {
49346
49498
  if (messageMode === "off")
49347
49499
  return;
49348
49500
  const fromPubkey = msg.senderPubkey || "";
49349
- const fromName = fromPubkey ? await resolvePeerName(client, fromPubkey) : "unknown";
49501
+ const fromName = fromPubkey ? await resolvePeerName(client2, fromPubkey) : "unknown";
49350
49502
  if (messageMode === "inbox") {
49351
49503
  try {
49352
49504
  await server.notification({
@@ -49368,8 +49520,8 @@ ${rows.join(`
49368
49520
  meta: {
49369
49521
  from_id: fromPubkey,
49370
49522
  from_name: fromName,
49371
- mesh_slug: client.meshSlug,
49372
- mesh_id: client.meshId,
49523
+ mesh_slug: client2.meshSlug,
49524
+ mesh_id: client2.meshId,
49373
49525
  priority: msg.priority,
49374
49526
  sent_at: msg.createdAt,
49375
49527
  delivered_at: msg.receivedAt,
@@ -49384,7 +49536,7 @@ ${rows.join(`
49384
49536
  `);
49385
49537
  }
49386
49538
  });
49387
- client.onStreamData(async (evt) => {
49539
+ client2.onStreamData(async (evt) => {
49388
49540
  try {
49389
49541
  await server.notification({
49390
49542
  method: "notifications/claude/channel",
@@ -49399,7 +49551,7 @@ ${rows.join(`
49399
49551
  });
49400
49552
  } catch {}
49401
49553
  });
49402
- client.onStateChange(async (change) => {
49554
+ client2.onStateChange(async (change) => {
49403
49555
  try {
49404
49556
  await server.notification({
49405
49557
  method: "notifications/claude/channel",
@@ -49416,17 +49568,17 @@ ${rows.join(`
49416
49568
  });
49417
49569
  }
49418
49570
  setTimeout(async () => {
49419
- const client = allClients()[0];
49420
- if (!client || client.status !== "open")
49571
+ const client2 = allClients()[0];
49572
+ if (!client2 || client2.status !== "open")
49421
49573
  return;
49422
49574
  try {
49423
- const peers = await client.listPeers();
49575
+ const peers = await client2.listPeers();
49424
49576
  const peerNames = peers.filter((p) => p.displayName !== myName).map((p) => p.displayName).join(", ") || "none";
49425
49577
  await server.notification({
49426
49578
  method: "notifications/claude/channel",
49427
49579
  params: {
49428
- content: `[system] Connected as ${myName} to mesh ${client.meshSlug}. ${peers.length} peer(s) online: ${peerNames}. Call mesh_info for full details or set_summary to announce yourself.`,
49429
- meta: { kind: "welcome", mesh_slug: client.meshSlug }
49580
+ content: `[system] Connected as ${myName} to mesh ${client2.meshSlug}. ${peers.length} peer(s) online: ${peerNames}. Call mesh_info for full details or set_summary to announce yourself.`,
49581
+ meta: { kind: "welcome", mesh_slug: client2.meshSlug }
49430
49582
  }
49431
49583
  });
49432
49584
  } catch {}
@@ -50401,7 +50553,7 @@ init_config();
50401
50553
  // package.json
50402
50554
  var package_default = {
50403
50555
  name: "claudemesh-cli",
50404
- version: "0.6.6",
50556
+ version: "0.6.7",
50405
50557
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
50406
50558
  keywords: [
50407
50559
  "claude-code",
@@ -50834,13 +50986,13 @@ Joined: ${config2.meshes.map((m) => m.slug).join(", ")}`);
50834
50986
  process.exit(1);
50835
50987
  }
50836
50988
  const displayName = opts.displayName ?? config2.displayName ?? `${hostname3()}-${process.pid}`;
50837
- const client = new BrokerClient(mesh, { displayName });
50989
+ const client2 = new BrokerClient(mesh, { displayName });
50838
50990
  try {
50839
- await client.connect();
50840
- const result = await fn(client, mesh);
50991
+ await client2.connect();
50992
+ const result = await fn(client2, mesh);
50841
50993
  return result;
50842
50994
  } finally {
50843
- client.close();
50995
+ client2.close();
50844
50996
  }
50845
50997
  }
50846
50998
 
@@ -50851,8 +51003,8 @@ async function runPeers(flags) {
50851
51003
  const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
50852
51004
  const green = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
50853
51005
  const yellow = (s) => useColor ? `\x1B[33m${s}\x1B[39m` : s;
50854
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
50855
- const peers = await client.listPeers();
51006
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
51007
+ const peers = await client2.listPeers();
50856
51008
  if (flags.json) {
50857
51009
  console.log(JSON.stringify(peers, null, 2));
50858
51010
  return;
@@ -50877,10 +51029,10 @@ async function runPeers(flags) {
50877
51029
  // src/commands/send.ts
50878
51030
  async function runSend(flags, to, message) {
50879
51031
  const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
50880
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
51032
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
50881
51033
  let targetSpec = to;
50882
51034
  if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
50883
- const peers = await client.listPeers();
51035
+ const peers = await client2.listPeers();
50884
51036
  const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
50885
51037
  if (!match) {
50886
51038
  const names = peers.map((p) => p.displayName).join(", ");
@@ -50889,7 +51041,7 @@ async function runSend(flags, to, message) {
50889
51041
  }
50890
51042
  targetSpec = match.pubkey;
50891
51043
  }
50892
- const result = await client.send(targetSpec, message, priority);
51044
+ const result = await client2.send(targetSpec, message, priority);
50893
51045
  if (result.ok) {
50894
51046
  console.log(`✓ Sent to ${to}${result.messageId ? ` (${result.messageId.slice(0, 8)})` : ""}`);
50895
51047
  } else {
@@ -50915,9 +51067,9 @@ async function runInbox(flags) {
50915
51067
  const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
50916
51068
  const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
50917
51069
  const waitMs = (flags.wait ?? 1) * 1000;
50918
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
51070
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
50919
51071
  await new Promise((resolve2) => setTimeout(resolve2, waitMs));
50920
- const messages = client.drainPushBuffer();
51072
+ const messages = client2.drainPushBuffer();
50921
51073
  if (flags.json) {
50922
51074
  console.log(JSON.stringify(messages, null, 2));
50923
51075
  return;
@@ -50939,8 +51091,8 @@ async function runInbox(flags) {
50939
51091
  async function runStateGet(flags, key) {
50940
51092
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
50941
51093
  const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
50942
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
50943
- const entry = await client.getState(key);
51094
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51095
+ const entry = await client2.getState(key);
50944
51096
  if (!entry) {
50945
51097
  console.log(dim(`(not set)`));
50946
51098
  return;
@@ -50961,8 +51113,8 @@ async function runStateSet(flags, key, value) {
50961
51113
  } catch {
50962
51114
  parsed = value;
50963
51115
  }
50964
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
50965
- await client.setState(key, parsed);
51116
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51117
+ await client2.setState(key, parsed);
50966
51118
  console.log(`✓ ${key} = ${JSON.stringify(parsed)}`);
50967
51119
  });
50968
51120
  }
@@ -50970,8 +51122,8 @@ async function runStateList(flags) {
50970
51122
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
50971
51123
  const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
50972
51124
  const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
50973
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
50974
- const entries = await client.listState();
51125
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
51126
+ const entries = await client2.listState();
50975
51127
  if (flags.json) {
50976
51128
  console.log(JSON.stringify(entries, null, 2));
50977
51129
  return;
@@ -50988,6 +51140,214 @@ async function runStateList(flags) {
50988
51140
  });
50989
51141
  }
50990
51142
 
51143
+ // src/commands/memory.ts
51144
+ async function runRemember(flags, content) {
51145
+ const tags = flags.tags ? flags.tags.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
51146
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51147
+ const id = await client2.remember(content, tags);
51148
+ if (flags.json) {
51149
+ console.log(JSON.stringify({ id, content, tags }));
51150
+ return;
51151
+ }
51152
+ if (id) {
51153
+ console.log(`✓ Remembered (${id.slice(0, 8)})`);
51154
+ } else {
51155
+ console.error("✗ Failed to store memory");
51156
+ process.exit(1);
51157
+ }
51158
+ });
51159
+ }
51160
+ async function runRecall(flags, query) {
51161
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
51162
+ const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
51163
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
51164
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51165
+ const memories = await client2.recall(query);
51166
+ if (flags.json) {
51167
+ console.log(JSON.stringify(memories, null, 2));
51168
+ return;
51169
+ }
51170
+ if (memories.length === 0) {
51171
+ console.log(dim("No memories found."));
51172
+ return;
51173
+ }
51174
+ for (const m of memories) {
51175
+ const tags = m.tags.length ? dim(` [${m.tags.join(", ")}]`) : "";
51176
+ console.log(`${bold2(m.id.slice(0, 8))}${tags}`);
51177
+ console.log(` ${m.content}`);
51178
+ console.log(dim(` ${m.rememberedBy} · ${new Date(m.rememberedAt).toLocaleString()}`));
51179
+ console.log("");
51180
+ }
51181
+ });
51182
+ }
51183
+
51184
+ // src/commands/info.ts
51185
+ init_config();
51186
+ async function runInfo(flags) {
51187
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
51188
+ const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
51189
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
51190
+ const config2 = loadConfig();
51191
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
51192
+ const [brokerInfo, peers, state] = await Promise.all([
51193
+ client2.meshInfo(),
51194
+ client2.listPeers(),
51195
+ client2.listState()
51196
+ ]);
51197
+ const output = {
51198
+ slug: mesh.slug,
51199
+ meshId: mesh.meshId,
51200
+ memberId: mesh.memberId,
51201
+ brokerUrl: mesh.brokerUrl,
51202
+ displayName: config2.displayName ?? null,
51203
+ peerCount: peers.length,
51204
+ stateCount: state.length,
51205
+ ...brokerInfo ?? {}
51206
+ };
51207
+ if (flags.json) {
51208
+ console.log(JSON.stringify(output, null, 2));
51209
+ return;
51210
+ }
51211
+ console.log(bold2(mesh.slug) + dim(` · ${mesh.brokerUrl}`));
51212
+ console.log(dim(` mesh: ${mesh.meshId}`));
51213
+ console.log(dim(` member: ${mesh.memberId}`));
51214
+ console.log(` peers: ${peers.length} connected`);
51215
+ console.log(` state: ${state.length} keys`);
51216
+ if (brokerInfo && typeof brokerInfo === "object") {
51217
+ for (const [k, v] of Object.entries(brokerInfo)) {
51218
+ if (["slug", "meshId", "brokerUrl"].includes(k))
51219
+ continue;
51220
+ console.log(dim(` ${k}: ${JSON.stringify(v)}`));
51221
+ }
51222
+ }
51223
+ });
51224
+ }
51225
+
51226
+ // src/commands/remind.ts
51227
+ function parseDuration(raw) {
51228
+ const m = raw.trim().match(/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day)?$/i);
51229
+ if (!m)
51230
+ return null;
51231
+ const n = parseFloat(m[1]);
51232
+ const unit = (m[2] ?? "s").toLowerCase();
51233
+ if (unit.startsWith("d"))
51234
+ return n * 86400000;
51235
+ if (unit.startsWith("h"))
51236
+ return n * 3600000;
51237
+ if (unit.startsWith("m"))
51238
+ return n * 60000;
51239
+ return n * 1000;
51240
+ }
51241
+ function parseDeliverAt(flags) {
51242
+ if (flags.in) {
51243
+ const ms = parseDuration(flags.in);
51244
+ if (ms === null)
51245
+ return null;
51246
+ return Date.now() + ms;
51247
+ }
51248
+ if (flags.at) {
51249
+ const hm = flags.at.match(/^(\d{1,2}):(\d{2})$/);
51250
+ if (hm) {
51251
+ const now = new Date;
51252
+ const target = new Date(now);
51253
+ target.setHours(parseInt(hm[1], 10), parseInt(hm[2], 10), 0, 0);
51254
+ if (target <= now)
51255
+ target.setDate(target.getDate() + 1);
51256
+ return target.getTime();
51257
+ }
51258
+ const ts = Date.parse(flags.at);
51259
+ return isNaN(ts) ? null : ts;
51260
+ }
51261
+ return null;
51262
+ }
51263
+ async function runRemind(flags, positional) {
51264
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
51265
+ const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
51266
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
51267
+ const action = positional[0];
51268
+ if (action === "list") {
51269
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51270
+ const scheduled = await client2.listScheduled();
51271
+ if (flags.json) {
51272
+ console.log(JSON.stringify(scheduled, null, 2));
51273
+ return;
51274
+ }
51275
+ if (scheduled.length === 0) {
51276
+ console.log(dim("No pending reminders."));
51277
+ return;
51278
+ }
51279
+ for (const m of scheduled) {
51280
+ const when = new Date(m.deliverAt).toLocaleString();
51281
+ const to = m.to === client2.getSessionPubkey() ? dim("(self)") : m.to;
51282
+ console.log(` ${bold2(m.id.slice(0, 8))} → ${to} at ${when}`);
51283
+ console.log(` ${dim(m.message.slice(0, 80))}`);
51284
+ console.log("");
51285
+ }
51286
+ });
51287
+ return;
51288
+ }
51289
+ if (action === "cancel") {
51290
+ const id = positional[1];
51291
+ if (!id) {
51292
+ console.error("Usage: claudemesh remind cancel <id>");
51293
+ process.exit(1);
51294
+ }
51295
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51296
+ const ok = await client2.cancelScheduled(id);
51297
+ if (ok)
51298
+ console.log(`✓ Cancelled ${id}`);
51299
+ else {
51300
+ console.error(`✗ Not found or already fired: ${id}`);
51301
+ process.exit(1);
51302
+ }
51303
+ });
51304
+ return;
51305
+ }
51306
+ const message = action ?? positional.join(" ");
51307
+ if (!message) {
51308
+ console.error("Usage: claudemesh remind <message> --in <duration>");
51309
+ console.error(" claudemesh remind <message> --at <time>");
51310
+ console.error(" claudemesh remind list");
51311
+ console.error(" claudemesh remind cancel <id>");
51312
+ process.exit(1);
51313
+ }
51314
+ const deliverAt = parseDeliverAt(flags);
51315
+ if (deliverAt === null) {
51316
+ console.error('Specify when: --in <duration> (e.g. "2h", "30m") or --at <time> (e.g. "15:00")');
51317
+ process.exit(1);
51318
+ }
51319
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51320
+ let targetSpec;
51321
+ if (flags.to && flags.to !== "self") {
51322
+ if (flags.to.startsWith("@") || flags.to === "*" || /^[0-9a-f]{64}$/i.test(flags.to)) {
51323
+ targetSpec = flags.to;
51324
+ } else {
51325
+ const peers = await client2.listPeers();
51326
+ const match = peers.find((p) => p.displayName.toLowerCase() === flags.to.toLowerCase());
51327
+ if (!match) {
51328
+ console.error(`Peer "${flags.to}" not found. Online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`);
51329
+ process.exit(1);
51330
+ }
51331
+ targetSpec = match.pubkey;
51332
+ }
51333
+ } else {
51334
+ targetSpec = client2.getSessionPubkey() ?? "*";
51335
+ }
51336
+ const result = await client2.scheduleMessage(targetSpec, message, deliverAt);
51337
+ if (!result) {
51338
+ console.error("✗ Broker did not acknowledge — check connection");
51339
+ process.exit(1);
51340
+ }
51341
+ if (flags.json) {
51342
+ console.log(JSON.stringify(result));
51343
+ return;
51344
+ }
51345
+ const when = new Date(result.deliverAt).toLocaleString();
51346
+ const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
51347
+ console.log(`✓ Reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} at ${when}`);
51348
+ });
51349
+ }
51350
+
50991
51351
  // src/index.ts
50992
51352
  var launch = defineCommand({
50993
51353
  meta: {
@@ -51173,6 +51533,55 @@ var main = defineCommand({
51173
51533
  }
51174
51534
  }
51175
51535
  }),
51536
+ info: defineCommand({
51537
+ meta: { name: "info", description: "Show mesh overview: slug, broker, peer count, state keys" },
51538
+ args: {
51539
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51540
+ json: { type: "boolean", description: "Output as JSON", default: false }
51541
+ },
51542
+ async run({ args }) {
51543
+ await runInfo(args);
51544
+ }
51545
+ }),
51546
+ remember: defineCommand({
51547
+ meta: { name: "remember", description: "Store a memory in the mesh (accessible to all peers)" },
51548
+ args: {
51549
+ content: { type: "positional", description: "Text to remember", required: true },
51550
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51551
+ tags: { type: "string", description: "Comma-separated tags (e.g. task,context)" },
51552
+ json: { type: "boolean", description: "Output as JSON", default: false }
51553
+ },
51554
+ async run({ args }) {
51555
+ await runRemember(args, args.content);
51556
+ }
51557
+ }),
51558
+ recall: defineCommand({
51559
+ meta: { name: "recall", description: "Search mesh memory by keyword or phrase" },
51560
+ args: {
51561
+ query: { type: "positional", description: "Search query", required: true },
51562
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51563
+ json: { type: "boolean", description: "Output as JSON", default: false }
51564
+ },
51565
+ async run({ args }) {
51566
+ await runRecall(args, args.query);
51567
+ }
51568
+ }),
51569
+ remind: defineCommand({
51570
+ meta: { name: "remind", description: "Schedule a reminder or delayed message via the broker" },
51571
+ args: {
51572
+ message: { type: "positional", description: "Message text, or: list | cancel <id>", required: false },
51573
+ extra: { type: "positional", description: "Additional positional args", required: false },
51574
+ in: { type: "string", description: 'Deliver after duration: "2h", "30m", "90s"' },
51575
+ at: { type: "string", description: 'Deliver at time: "15:00" or ISO timestamp' },
51576
+ to: { type: "string", description: "Recipient (default: self). Name, @group, pubkey, or *" },
51577
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51578
+ json: { type: "boolean", description: "Output as JSON", default: false }
51579
+ },
51580
+ async run({ args, rawArgs }) {
51581
+ const positionals = rawArgs.filter((a) => !a.startsWith("-"));
51582
+ await runRemind(args, positionals);
51583
+ }
51584
+ }),
51176
51585
  status: defineCommand({
51177
51586
  meta: { name: "status", description: "Check broker reachability for each joined mesh" },
51178
51587
  async run() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",