claudemesh-cli 0.6.6 → 0.6.8

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 (3) hide show
  1. package/LICENSE.md +37 -0
  2. package/dist/index.js +570 -155
  3. package/package.json +20 -21
package/LICENSE.md ADDED
@@ -0,0 +1,37 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alezmad (claudemesh)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ ---
24
+
25
+ ## Attribution
26
+
27
+ This project was originally scaffolded using TurboStarter (https://turbostarter.dev),
28
+ a proprietary SaaS starter kit. The TurboStarter scaffold code is covered by
29
+ your separate purchase agreement with TurboStarter and is NOT re-licensed by
30
+ this MIT license. The MIT license above covers claudemesh-specific additions,
31
+ modifications, and original code written on top of that scaffold — including
32
+ but not limited to: apps/broker, apps/cli, apps/web/src/modules/marketing/home,
33
+ packages/db/src/schema/mesh.ts, the protocol, and the documentation.
34
+
35
+ If you are redistributing this repository, you are responsible for compliance
36
+ with BOTH the TurboStarter EULA (for scaffold components) and this MIT license
37
+ (for claudemesh code).
package/dist/index.js CHANGED
@@ -47374,6 +47374,39 @@ var TOOLS = [
47374
47374
  }
47375
47375
  }
47376
47376
  },
47377
+ {
47378
+ name: "schedule_reminder",
47379
+ description: "Schedule a message for future delivery. Without `to`, it fires back to yourself (a self-reminder). With `to`, it delivers to a peer, @group, or * broadcast. The broker holds it and delivers when the time arrives. Receivers see `subtype: reminder` in the push envelope.",
47380
+ inputSchema: {
47381
+ type: "object",
47382
+ properties: {
47383
+ message: { type: "string", description: "Message or 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
+ to: {
47387
+ type: "string",
47388
+ description: "Recipient: display name, pubkey hex, @group, or * (omit for self-reminder)"
47389
+ }
47390
+ },
47391
+ required: ["message"]
47392
+ }
47393
+ },
47394
+ {
47395
+ name: "list_scheduled",
47396
+ description: "List all your pending scheduled messages: id, recipient, preview, and delivery time.",
47397
+ inputSchema: { type: "object", properties: {} }
47398
+ },
47399
+ {
47400
+ name: "cancel_scheduled",
47401
+ description: "Cancel a pending scheduled message before it fires.",
47402
+ inputSchema: {
47403
+ type: "object",
47404
+ properties: {
47405
+ id: { type: "string", description: "Scheduled message ID" }
47406
+ },
47407
+ required: ["id"]
47408
+ }
47409
+ },
47377
47410
  {
47378
47411
  name: "mesh_info",
47379
47412
  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 +47773,49 @@ class BrokerClient {
47740
47773
  return;
47741
47774
  this.ws.send(JSON.stringify({ type: "forget", memoryId }));
47742
47775
  }
47776
+ async scheduleMessage(to, message, deliverAt, isReminder = false) {
47777
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47778
+ return null;
47779
+ return new Promise((resolve) => {
47780
+ const reqId = this.makeReqId();
47781
+ this.scheduledAckResolvers.set(reqId, { resolve, timer: setTimeout(() => {
47782
+ if (this.scheduledAckResolvers.delete(reqId))
47783
+ resolve(null);
47784
+ }, 8000) });
47785
+ this.ws.send(JSON.stringify({
47786
+ type: "schedule",
47787
+ to,
47788
+ message,
47789
+ deliverAt,
47790
+ ...isReminder ? { subtype: "reminder" } : {},
47791
+ _reqId: reqId
47792
+ }));
47793
+ });
47794
+ }
47795
+ async listScheduled() {
47796
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47797
+ return [];
47798
+ return new Promise((resolve) => {
47799
+ const reqId = this.makeReqId();
47800
+ this.scheduledListResolvers.set(reqId, { resolve, timer: setTimeout(() => {
47801
+ if (this.scheduledListResolvers.delete(reqId))
47802
+ resolve([]);
47803
+ }, 5000) });
47804
+ this.ws.send(JSON.stringify({ type: "list_scheduled", _reqId: reqId }));
47805
+ });
47806
+ }
47807
+ async cancelScheduled(scheduledId) {
47808
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47809
+ return false;
47810
+ return new Promise((resolve) => {
47811
+ const reqId = this.makeReqId();
47812
+ this.cancelScheduledResolvers.set(reqId, { resolve, timer: setTimeout(() => {
47813
+ if (this.cancelScheduledResolvers.delete(reqId))
47814
+ resolve(false);
47815
+ }, 5000) });
47816
+ this.ws.send(JSON.stringify({ type: "cancel_scheduled", scheduledId, _reqId: reqId }));
47817
+ });
47818
+ }
47743
47819
  messageStatusResolvers = new Map;
47744
47820
  fileUrlResolvers = new Map;
47745
47821
  fileListResolvers = new Map;
@@ -47757,6 +47833,9 @@ class BrokerClient {
47757
47833
  streamCreatedResolvers = new Map;
47758
47834
  streamListResolvers = new Map;
47759
47835
  streamDataHandlers = new Set;
47836
+ scheduledAckResolvers = new Map;
47837
+ scheduledListResolvers = new Map;
47838
+ cancelScheduledResolvers = new Map;
47760
47839
  async messageStatus(messageId) {
47761
47840
  if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47762
47841
  return null;
@@ -48154,7 +48233,8 @@ class BrokerClient {
48154
48233
  createdAt: String(msg.createdAt ?? ""),
48155
48234
  receivedAt: new Date().toISOString(),
48156
48235
  plaintext,
48157
- kind
48236
+ kind,
48237
+ ...msg.subtype ? { subtype: msg.subtype } : {}
48158
48238
  };
48159
48239
  this.pushBuffer.push(push);
48160
48240
  if (this.pushBuffer.length > 500)
@@ -48319,6 +48399,22 @@ class BrokerClient {
48319
48399
  this.resolveFromMap(this.meshInfoResolvers, msgReqId, msg);
48320
48400
  return;
48321
48401
  }
48402
+ if (msg.type === "scheduled_ack") {
48403
+ this.resolveFromMap(this.scheduledAckResolvers, msgReqId, {
48404
+ scheduledId: String(msg.scheduledId ?? ""),
48405
+ deliverAt: Number(msg.deliverAt ?? 0)
48406
+ });
48407
+ return;
48408
+ }
48409
+ if (msg.type === "scheduled_list") {
48410
+ const messages = msg.messages ?? [];
48411
+ this.resolveFromMap(this.scheduledListResolvers, msgReqId, messages);
48412
+ return;
48413
+ }
48414
+ if (msg.type === "cancel_scheduled_ack") {
48415
+ this.resolveFromMap(this.cancelScheduledResolvers, msgReqId, Boolean(msg.ok));
48416
+ return;
48417
+ }
48322
48418
  if (msg.type === "error") {
48323
48419
  this.debug(`broker error: ${msg.code} ${msg.message}`);
48324
48420
  const id = msg.id ? String(msg.id) : null;
@@ -48351,6 +48447,9 @@ class BrokerClient {
48351
48447
  [this.contextResultsResolvers, []],
48352
48448
  [this.contextListResolvers, []],
48353
48449
  [this.streamListResolvers, []],
48450
+ [this.scheduledAckResolvers, null],
48451
+ [this.scheduledListResolvers, []],
48452
+ [this.cancelScheduledResolvers, false],
48354
48453
  [this.messageStatusResolvers, null],
48355
48454
  [this.grantFileAccessResolvers, false],
48356
48455
  [this.collectionListResolvers, []],
@@ -48418,17 +48517,17 @@ async function ensureClient(mesh) {
48418
48517
  const existing = clients.get(mesh.meshId);
48419
48518
  if (existing)
48420
48519
  return existing;
48421
- const client = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG, displayName: configDisplayName });
48422
- clients.set(mesh.meshId, client);
48520
+ const client2 = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG, displayName: configDisplayName });
48521
+ clients.set(mesh.meshId, client2);
48423
48522
  try {
48424
- await client.connect();
48523
+ await client2.connect();
48425
48524
  for (const g of configGroups ?? []) {
48426
48525
  try {
48427
- await client.joinGroup(g.name, g.role);
48526
+ await client2.joinGroup(g.name, g.role);
48428
48527
  } catch {}
48429
48528
  }
48430
48529
  } catch {}
48431
- return client;
48530
+ return client2;
48432
48531
  }
48433
48532
  async function startClients(config2) {
48434
48533
  configDisplayName = config2.displayName;
@@ -48511,12 +48610,12 @@ async function resolveClient(to) {
48511
48610
  var peerNameCache = new Map;
48512
48611
  var peerNameCacheAge = 0;
48513
48612
  var CACHE_TTL_MS = 30000;
48514
- async function resolvePeerName(client, pubkey) {
48613
+ async function resolvePeerName(client2, pubkey) {
48515
48614
  const now = Date.now();
48516
48615
  if (now - peerNameCacheAge > CACHE_TTL_MS) {
48517
48616
  peerNameCache.clear();
48518
48617
  try {
48519
- const peers = await client.listPeers();
48618
+ const peers = await client2.listPeers();
48520
48619
  for (const p of peers)
48521
48620
  peerNameCache.set(p.pubkey, p.displayName);
48522
48621
  } catch {}
@@ -48530,7 +48629,8 @@ function decryptFailedWarning(senderPubkey) {
48530
48629
  }
48531
48630
  function formatPush(p, meshSlug) {
48532
48631
  const body = p.plaintext ?? decryptFailedWarning(p.senderPubkey);
48533
- return `[${meshSlug}] from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
48632
+ const tag = p.subtype === "reminder" ? " [REMINDER]" : "";
48633
+ return `[${meshSlug}]${tag} from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
48534
48634
  ${body}`;
48535
48635
  }
48536
48636
  async function startMcpServer() {
@@ -48550,6 +48650,8 @@ You are "${myName}"${myRole ? ` (${myRole})` : ""} — a peer in the claudemesh
48550
48650
  ## Responding to messages
48551
48651
  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.
48552
48652
 
48653
+ If the channel meta contains \`subtype: reminder\`, this is a scheduled reminder you set for yourself — act on it immediately (no reply needed).
48654
+
48553
48655
  ## Tools
48554
48656
  | Tool | Description |
48555
48657
  |------|-------------|
@@ -48591,6 +48693,9 @@ When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATEL
48591
48693
  | claim_task(id) | Claim an unclaimed task. |
48592
48694
  | complete_task(id, result?) | Mark task done with optional result. |
48593
48695
  | list_tasks(status?, assignee?) | List tasks filtered by status/assignee. |
48696
+ | schedule_reminder(message, in_seconds?, deliver_at?, to?) | Schedule a reminder to yourself (no \`to\`) or a delayed message to a peer/group. Delivered as a push with \`subtype: reminder\` in the channel meta. |
48697
+ | list_scheduled() | List pending scheduled reminders and messages. |
48698
+ | cancel_scheduled(id) | Cancel a pending scheduled item. |
48594
48699
 
48595
48700
  If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
48596
48701
 
@@ -48666,15 +48771,15 @@ Your message mode is "${messageMode}".
48666
48771
  const results = [];
48667
48772
  const seen = new Set;
48668
48773
  for (const target of targets) {
48669
- const { client, targetSpec, error: error2 } = await resolveClient(target);
48670
- if (!client) {
48774
+ const { client: client2, targetSpec, error: error2 } = await resolveClient(target);
48775
+ if (!client2) {
48671
48776
  results.push(`✗ ${target}: ${error2 ?? "no client resolved"}`);
48672
48777
  continue;
48673
48778
  }
48674
48779
  if (seen.has(targetSpec))
48675
48780
  continue;
48676
48781
  seen.add(targetSpec);
48677
- const result = await client.send(targetSpec, message, priority ?? "next");
48782
+ const result = await client2.send(targetSpec, message, priority ?? "next");
48678
48783
  if (!result.ok) {
48679
48784
  results.push(`✗ ${target}: ${result.error}`);
48680
48785
  } else {
@@ -48795,19 +48900,19 @@ ${drained.join(`
48795
48900
  const { key } = args ?? {};
48796
48901
  if (!key)
48797
48902
  return text("get_state: `key` required", true);
48798
- const client = allClients()[0];
48799
- if (!client)
48903
+ const client2 = allClients()[0];
48904
+ if (!client2)
48800
48905
  return text("get_state: not connected", true);
48801
- const result = await client.getState(key);
48906
+ const result = await client2.getState(key);
48802
48907
  if (!result)
48803
48908
  return text(`State "${key}" not found.`);
48804
48909
  return text(`${key} = ${JSON.stringify(result.value)} (set by ${result.updatedBy} at ${result.updatedAt})`);
48805
48910
  }
48806
48911
  case "list_state": {
48807
- const client = allClients()[0];
48808
- if (!client)
48912
+ const client2 = allClients()[0];
48913
+ if (!client2)
48809
48914
  return text("list_state: not connected", true);
48810
- const entries = await client.listState();
48915
+ const entries = await client2.listState();
48811
48916
  if (entries.length === 0)
48812
48917
  return text("No shared state set.");
48813
48918
  const lines = entries.map((e) => `- **${e.key}** = ${JSON.stringify(e.value)} (by ${e.updatedBy})`);
@@ -48818,20 +48923,20 @@ ${drained.join(`
48818
48923
  const { content, tags } = args ?? {};
48819
48924
  if (!content)
48820
48925
  return text("remember: `content` required", true);
48821
- const client = allClients()[0];
48822
- if (!client)
48926
+ const client2 = allClients()[0];
48927
+ if (!client2)
48823
48928
  return text("remember: not connected", true);
48824
- const id = await client.remember(content, tags);
48929
+ const id = await client2.remember(content, tags);
48825
48930
  return text(`Remembered${id ? ` (${id})` : ""}: "${content.slice(0, 80)}${content.length > 80 ? "..." : ""}"`);
48826
48931
  }
48827
48932
  case "recall": {
48828
48933
  const { query } = args ?? {};
48829
48934
  if (!query)
48830
48935
  return text("recall: `query` required", true);
48831
- const client = allClients()[0];
48832
- if (!client)
48936
+ const client2 = allClients()[0];
48937
+ if (!client2)
48833
48938
  return text("recall: not connected", true);
48834
- const memories = await client.recall(query);
48939
+ const memories = await client2.recall(query);
48835
48940
  if (memories.length === 0)
48836
48941
  return text(`No memories found for "${query}".`);
48837
48942
  const lines = memories.map((m) => `- [${m.id.slice(0, 8)}] ${m.content} (by ${m.rememberedBy}, ${m.rememberedAt})`);
@@ -48843,12 +48948,64 @@ ${lines.join(`
48843
48948
  const { id } = args ?? {};
48844
48949
  if (!id)
48845
48950
  return text("forget: `id` required", true);
48846
- const client = allClients()[0];
48847
- if (!client)
48951
+ const client2 = allClients()[0];
48952
+ if (!client2)
48848
48953
  return text("forget: not connected", true);
48849
- await client.forget(id);
48954
+ await client2.forget(id);
48850
48955
  return text(`Forgotten: ${id}`);
48851
48956
  }
48957
+ case "schedule_reminder": {
48958
+ const sArgs = args ?? {};
48959
+ if (!sArgs.message)
48960
+ return text("schedule_reminder: `message` 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("schedule_reminder: provide `deliver_at` (ms timestamp) or `in_seconds`", true);
48968
+ }
48969
+ const isSelf = !sArgs.to;
48970
+ let targetSpec;
48971
+ if (isSelf) {
48972
+ targetSpec = client.getSessionPubkey() ?? "*";
48973
+ } else {
48974
+ const to = sArgs.to;
48975
+ if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
48976
+ const peers = await client.listPeers();
48977
+ const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
48978
+ if (!match) {
48979
+ const names = peers.map((p) => p.displayName).join(", ");
48980
+ return text(`schedule_reminder: peer "${to}" not found. Online: ${names || "(none)"}`, true);
48981
+ }
48982
+ targetSpec = match.pubkey;
48983
+ } else {
48984
+ targetSpec = to;
48985
+ }
48986
+ }
48987
+ const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt, true);
48988
+ if (!result)
48989
+ return text("schedule_reminder: broker did not acknowledge — check connection", true);
48990
+ const when = new Date(result.deliverAt).toISOString();
48991
+ return text(isSelf ? `Self-reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" at ${when}` : `Reminder to "${sArgs.to}" scheduled (${result.scheduledId.slice(0, 8)}) for ${when}`);
48992
+ }
48993
+ case "list_scheduled": {
48994
+ const scheduled = await client.listScheduled();
48995
+ if (scheduled.length === 0)
48996
+ return text("No pending scheduled messages.");
48997
+ 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 ? "…" : ""}"`);
48998
+ return text(`${scheduled.length} scheduled:
48999
+ ${lines.join(`
49000
+ `)}`);
49001
+ }
49002
+ case "cancel_scheduled": {
49003
+ const { id: schedId } = args ?? {};
49004
+ if (!schedId)
49005
+ return text("cancel_scheduled: `id` required", true);
49006
+ const ok = await client.cancelScheduled(schedId);
49007
+ return text(ok ? `Cancelled: ${schedId}` : `Not found or already fired: ${schedId}`, !ok);
49008
+ }
48852
49009
  case "share_file": {
48853
49010
  const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
48854
49011
  if (!filePath)
@@ -48856,15 +49013,15 @@ ${lines.join(`
48856
49013
  const { existsSync: existsSync2 } = await import("node:fs");
48857
49014
  if (!existsSync2(filePath))
48858
49015
  return text(`share_file: file not found: ${filePath}`, true);
48859
- const client = allClients()[0];
48860
- if (!client)
49016
+ const client2 = allClients()[0];
49017
+ if (!client2)
48861
49018
  return text("share_file: not connected", true);
48862
49019
  if (fileTo) {
48863
49020
  const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
48864
49021
  const { readFileSync: readFileSync2, writeFileSync: writeFileSync2, mkdtempSync, unlinkSync, rmdirSync } = await import("node:fs");
48865
49022
  const { tmpdir } = await import("node:os");
48866
49023
  const { join: join2, basename } = await import("node:path");
48867
- const peers = await client.listPeers();
49024
+ const peers = await client2.listPeers();
48868
49025
  const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
48869
49026
  if (!targetPeer) {
48870
49027
  return text(`share_file: peer not found: ${fileTo}`, true);
@@ -48872,7 +49029,7 @@ ${lines.join(`
48872
49029
  const plaintext = readFileSync2(filePath);
48873
49030
  const { ciphertext, nonce, key } = await encryptFile2(new Uint8Array(plaintext));
48874
49031
  const sealedForTarget = await sealKeyForPeer2(key, targetPeer.pubkey);
48875
- const myPubkey = client.getSessionPubkey();
49032
+ const myPubkey = client2.getSessionPubkey();
48876
49033
  const sealedForSelf = myPubkey ? await sealKeyForPeer2(key, myPubkey) : null;
48877
49034
  const fileKeys = [
48878
49035
  { peerPubkey: targetPeer.pubkey, sealedKey: sealedForTarget },
@@ -48889,7 +49046,7 @@ ${lines.join(`
48889
49046
  const tmpPath = join2(tmpDir, baseName);
48890
49047
  writeFileSync2(tmpPath, combined);
48891
49048
  try {
48892
- const fileId = await client.uploadFile(tmpPath, client.meshId, client.meshSlug, {
49049
+ const fileId = await client2.uploadFile(tmpPath, client2.meshId, client2.meshSlug, {
48893
49050
  name: baseName,
48894
49051
  tags,
48895
49052
  persistent: true,
@@ -48910,7 +49067,7 @@ ${lines.join(`
48910
49067
  }
48911
49068
  }
48912
49069
  try {
48913
- const fileId = await client.uploadFile(filePath, client.meshId, client.meshSlug, {
49070
+ const fileId = await client2.uploadFile(filePath, client2.meshId, client2.meshSlug, {
48914
49071
  name: fileName,
48915
49072
  tags,
48916
49073
  persistent: true
@@ -48924,10 +49081,10 @@ ${lines.join(`
48924
49081
  const { id, save_to } = args ?? {};
48925
49082
  if (!id || !save_to)
48926
49083
  return text("get_file: `id` and `save_to` required", true);
48927
- const client = allClients()[0];
48928
- if (!client)
49084
+ const client2 = allClients()[0];
49085
+ if (!client2)
48929
49086
  return text("get_file: not connected", true);
48930
- const result = await client.getFile(id);
49087
+ const result = await client2.getFile(id);
48931
49088
  if (!result)
48932
49089
  return text(`get_file: file ${id} not found`, true);
48933
49090
  if (result.encrypted) {
@@ -48935,8 +49092,8 @@ ${lines.join(`
48935
49092
  return text("get_file: encrypted file — no decryption key available for your session", true);
48936
49093
  const { openSealedKey: openSealedKey2, decryptFile: decryptFile2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
48937
49094
  const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
48938
- const myPubkey = client.getSessionPubkey();
48939
- const mySecret = client.getSessionSecretKey();
49095
+ const myPubkey = client2.getSessionPubkey();
49096
+ const mySecret = client2.getSessionSecretKey();
48940
49097
  if (!myPubkey || !mySecret) {
48941
49098
  return text("get_file: no session keypair — cannot decrypt", true);
48942
49099
  }
@@ -48971,10 +49128,10 @@ ${lines.join(`
48971
49128
  }
48972
49129
  case "list_files": {
48973
49130
  const { query, from } = args ?? {};
48974
- const client = allClients()[0];
48975
- if (!client)
49131
+ const client2 = allClients()[0];
49132
+ if (!client2)
48976
49133
  return text("list_files: not connected", true);
48977
- const files = await client.listFiles(query, from);
49134
+ const files = await client2.listFiles(query, from);
48978
49135
  if (files.length === 0)
48979
49136
  return text("No files found.");
48980
49137
  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 +49142,10 @@ ${lines.join(`
48985
49142
  const { id } = args ?? {};
48986
49143
  if (!id)
48987
49144
  return text("file_status: `id` required", true);
48988
- const client = allClients()[0];
48989
- if (!client)
49145
+ const client2 = allClients()[0];
49146
+ if (!client2)
48990
49147
  return text("file_status: not connected", true);
48991
- const accesses = await client.fileStatus(id);
49148
+ const accesses = await client2.fileStatus(id);
48992
49149
  if (accesses.length === 0)
48993
49150
  return text("No one has accessed this file yet.");
48994
49151
  const lines = accesses.map((a) => `- ${a.peerName} at ${a.accessedAt}`);
@@ -49000,30 +49157,30 @@ ${lines.join(`
49000
49157
  const { id } = args ?? {};
49001
49158
  if (!id)
49002
49159
  return text("delete_file: `id` required", true);
49003
- const client = allClients()[0];
49004
- if (!client)
49160
+ const client2 = allClients()[0];
49161
+ if (!client2)
49005
49162
  return text("delete_file: not connected", true);
49006
- await client.deleteFile(id);
49163
+ await client2.deleteFile(id);
49007
49164
  return text(`Deleted: ${id}`);
49008
49165
  }
49009
49166
  case "vector_store": {
49010
49167
  const { collection, text: storeText, metadata } = args ?? {};
49011
49168
  if (!collection || !storeText)
49012
49169
  return text("vector_store: `collection` and `text` required", true);
49013
- const client = allClients()[0];
49014
- if (!client)
49170
+ const client2 = allClients()[0];
49171
+ if (!client2)
49015
49172
  return text("vector_store: not connected", true);
49016
- const id = await client.vectorStore(collection, storeText, metadata);
49173
+ const id = await client2.vectorStore(collection, storeText, metadata);
49017
49174
  return text(`Stored in ${collection}${id ? ` (${id})` : ""}`);
49018
49175
  }
49019
49176
  case "vector_search": {
49020
49177
  const { collection, query, limit } = args ?? {};
49021
49178
  if (!collection || !query)
49022
49179
  return text("vector_search: `collection` and `query` required", true);
49023
- const client = allClients()[0];
49024
- if (!client)
49180
+ const client2 = allClients()[0];
49181
+ if (!client2)
49025
49182
  return text("vector_search: not connected", true);
49026
- const results = await client.vectorSearch(collection, query, limit);
49183
+ const results = await client2.vectorSearch(collection, query, limit);
49027
49184
  if (results.length === 0)
49028
49185
  return text(`No results in ${collection} for "${query}".`);
49029
49186
  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 +49192,17 @@ ${lines.join(`
49035
49192
  const { collection, id } = args ?? {};
49036
49193
  if (!collection || !id)
49037
49194
  return text("vector_delete: `collection` and `id` required", true);
49038
- const client = allClients()[0];
49039
- if (!client)
49195
+ const client2 = allClients()[0];
49196
+ if (!client2)
49040
49197
  return text("vector_delete: not connected", true);
49041
- await client.vectorDelete(collection, id);
49198
+ await client2.vectorDelete(collection, id);
49042
49199
  return text(`Deleted ${id} from ${collection}`);
49043
49200
  }
49044
49201
  case "list_collections": {
49045
- const client = allClients()[0];
49046
- if (!client)
49202
+ const client2 = allClients()[0];
49203
+ if (!client2)
49047
49204
  return text("list_collections: not connected", true);
49048
- const collections = await client.listCollections();
49205
+ const collections = await client2.listCollections();
49049
49206
  if (collections.length === 0)
49050
49207
  return text("No vector collections.");
49051
49208
  return text(`Collections:
@@ -49056,10 +49213,10 @@ ${collections.map((c) => `- ${c}`).join(`
49056
49213
  const { cypher } = args ?? {};
49057
49214
  if (!cypher)
49058
49215
  return text("graph_query: `cypher` required", true);
49059
- const client = allClients()[0];
49060
- if (!client)
49216
+ const client2 = allClients()[0];
49217
+ if (!client2)
49061
49218
  return text("graph_query: not connected", true);
49062
- const rows = await client.graphQuery(cypher);
49219
+ const rows = await client2.graphQuery(cypher);
49063
49220
  if (rows.length === 0)
49064
49221
  return text("No results.");
49065
49222
  return text(JSON.stringify(rows, null, 2));
@@ -49068,30 +49225,30 @@ ${collections.map((c) => `- ${c}`).join(`
49068
49225
  const { cypher } = args ?? {};
49069
49226
  if (!cypher)
49070
49227
  return text("graph_execute: `cypher` required", true);
49071
- const client = allClients()[0];
49072
- if (!client)
49228
+ const client2 = allClients()[0];
49229
+ if (!client2)
49073
49230
  return text("graph_execute: not connected", true);
49074
- const rows = await client.graphExecute(cypher);
49231
+ const rows = await client2.graphExecute(cypher);
49075
49232
  return text(rows.length > 0 ? JSON.stringify(rows, null, 2) : "Executed successfully.");
49076
49233
  }
49077
49234
  case "share_context": {
49078
49235
  const { summary, files_read, key_findings, tags } = args ?? {};
49079
49236
  if (!summary)
49080
49237
  return text("share_context: `summary` required", true);
49081
- const client = allClients()[0];
49082
- if (!client)
49238
+ const client2 = allClients()[0];
49239
+ if (!client2)
49083
49240
  return text("share_context: not connected", true);
49084
- await client.shareContext(summary, files_read, key_findings, tags);
49241
+ await client2.shareContext(summary, files_read, key_findings, tags);
49085
49242
  return text(`Context shared: "${summary.slice(0, 80)}${summary.length > 80 ? "…" : ""}"`);
49086
49243
  }
49087
49244
  case "get_context": {
49088
49245
  const { query } = args ?? {};
49089
49246
  if (!query)
49090
49247
  return text("get_context: `query` required", true);
49091
- const client = allClients()[0];
49092
- if (!client)
49248
+ const client2 = allClients()[0];
49249
+ if (!client2)
49093
49250
  return text("get_context: not connected", true);
49094
- const contexts = await client.getContext(query);
49251
+ const contexts = await client2.getContext(query);
49095
49252
  if (contexts.length === 0)
49096
49253
  return text(`No context found for "${query}".`);
49097
49254
  const lines = contexts.map((c) => {
@@ -49106,10 +49263,10 @@ ${lines.join(`
49106
49263
  `)}`);
49107
49264
  }
49108
49265
  case "list_contexts": {
49109
- const client = allClients()[0];
49110
- if (!client)
49266
+ const client2 = allClients()[0];
49267
+ if (!client2)
49111
49268
  return text("list_contexts: not connected", true);
49112
- const contexts = await client.listContexts();
49269
+ const contexts = await client2.listContexts();
49113
49270
  if (contexts.length === 0)
49114
49271
  return text("No peer contexts shared yet.");
49115
49272
  const lines = contexts.map((c) => `- **${c.peerName}**: ${c.summary}${c.tags.length ? ` [${c.tags.join(", ")}]` : ""}`);
@@ -49121,38 +49278,38 @@ ${lines.join(`
49121
49278
  const { title, assignee, priority, tags } = args ?? {};
49122
49279
  if (!title)
49123
49280
  return text("create_task: `title` required", true);
49124
- const client = allClients()[0];
49125
- if (!client)
49281
+ const client2 = allClients()[0];
49282
+ if (!client2)
49126
49283
  return text("create_task: not connected", true);
49127
- const id = await client.createTask(title, assignee, priority, tags);
49284
+ const id = await client2.createTask(title, assignee, priority, tags);
49128
49285
  return text(`Task created${id ? ` (${id})` : ""}: "${title}"${assignee ? ` → ${assignee}` : ""}`);
49129
49286
  }
49130
49287
  case "claim_task": {
49131
49288
  const { id } = args ?? {};
49132
49289
  if (!id)
49133
49290
  return text("claim_task: `id` required", true);
49134
- const client = allClients()[0];
49135
- if (!client)
49291
+ const client2 = allClients()[0];
49292
+ if (!client2)
49136
49293
  return text("claim_task: not connected", true);
49137
- await client.claimTask(id);
49294
+ await client2.claimTask(id);
49138
49295
  return text(`Claimed task: ${id}`);
49139
49296
  }
49140
49297
  case "complete_task": {
49141
49298
  const { id, result } = args ?? {};
49142
49299
  if (!id)
49143
49300
  return text("complete_task: `id` required", true);
49144
- const client = allClients()[0];
49145
- if (!client)
49301
+ const client2 = allClients()[0];
49302
+ if (!client2)
49146
49303
  return text("complete_task: not connected", true);
49147
- await client.completeTask(id, result);
49304
+ await client2.completeTask(id, result);
49148
49305
  return text(`Completed task: ${id}${result ? ` — ${result}` : ""}`);
49149
49306
  }
49150
49307
  case "list_tasks": {
49151
49308
  const { status, assignee } = args ?? {};
49152
- const client = allClients()[0];
49153
- if (!client)
49309
+ const client2 = allClients()[0];
49310
+ if (!client2)
49154
49311
  return text("list_tasks: not connected", true);
49155
- const tasks = await client.listTasks(status, assignee);
49312
+ const tasks = await client2.listTasks(status, assignee);
49156
49313
  if (tasks.length === 0)
49157
49314
  return text("No tasks found.");
49158
49315
  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 +49321,10 @@ ${lines.join(`
49164
49321
  const { sql: querySql } = args ?? {};
49165
49322
  if (!querySql)
49166
49323
  return text("mesh_query: `sql` required", true);
49167
- const client = allClients()[0];
49168
- if (!client)
49324
+ const client2 = allClients()[0];
49325
+ if (!client2)
49169
49326
  return text("mesh_query: not connected", true);
49170
- const result = await client.meshQuery(querySql);
49327
+ const result = await client2.meshQuery(querySql);
49171
49328
  if (!result)
49172
49329
  return text("mesh_query: query failed or timed out", true);
49173
49330
  if (result.rows.length === 0)
@@ -49185,17 +49342,17 @@ ${rows.join(`
49185
49342
  const { sql: execSql } = args ?? {};
49186
49343
  if (!execSql)
49187
49344
  return text("mesh_execute: `sql` required", true);
49188
- const client = allClients()[0];
49189
- if (!client)
49345
+ const client2 = allClients()[0];
49346
+ if (!client2)
49190
49347
  return text("mesh_execute: not connected", true);
49191
- await client.meshExecute(execSql);
49348
+ await client2.meshExecute(execSql);
49192
49349
  return text(`Executed.`);
49193
49350
  }
49194
49351
  case "mesh_schema": {
49195
- const client = allClients()[0];
49196
- if (!client)
49352
+ const client2 = allClients()[0];
49353
+ if (!client2)
49197
49354
  return text("mesh_schema: not connected", true);
49198
- const tables = await client.meshSchema();
49355
+ const tables = await client2.meshSchema();
49199
49356
  if (!tables || tables.length === 0)
49200
49357
  return text("No tables in mesh database.");
49201
49358
  const lines = tables.map((t) => `**${t.name}**: ${t.columns.map((c) => `${c.name} (${c.type}${c.nullable ? ", nullable" : ""})`).join(", ")}`);
@@ -49206,37 +49363,37 @@ ${rows.join(`
49206
49363
  const { name: streamName } = args ?? {};
49207
49364
  if (!streamName)
49208
49365
  return text("create_stream: `name` required", true);
49209
- const client = allClients()[0];
49210
- if (!client)
49366
+ const client2 = allClients()[0];
49367
+ if (!client2)
49211
49368
  return text("create_stream: not connected", true);
49212
- const streamId = await client.createStream(streamName);
49369
+ const streamId = await client2.createStream(streamName);
49213
49370
  return text(`Stream created: ${streamName}${streamId ? ` (${streamId})` : ""}`);
49214
49371
  }
49215
49372
  case "publish": {
49216
49373
  const { stream: pubStream, data: pubData } = args ?? {};
49217
49374
  if (!pubStream)
49218
49375
  return text("publish: `stream` required", true);
49219
- const client = allClients()[0];
49220
- if (!client)
49376
+ const client2 = allClients()[0];
49377
+ if (!client2)
49221
49378
  return text("publish: not connected", true);
49222
- await client.publish(pubStream, pubData);
49379
+ await client2.publish(pubStream, pubData);
49223
49380
  return text(`Published to ${pubStream}.`);
49224
49381
  }
49225
49382
  case "subscribe": {
49226
49383
  const { stream: subStream } = args ?? {};
49227
49384
  if (!subStream)
49228
49385
  return text("subscribe: `stream` required", true);
49229
- const client = allClients()[0];
49230
- if (!client)
49386
+ const client2 = allClients()[0];
49387
+ if (!client2)
49231
49388
  return text("subscribe: not connected", true);
49232
- await client.subscribe(subStream);
49389
+ await client2.subscribe(subStream);
49233
49390
  return text(`Subscribed to ${subStream}. Data pushes will arrive as channel notifications.`);
49234
49391
  }
49235
49392
  case "list_streams": {
49236
- const client = allClients()[0];
49237
- if (!client)
49393
+ const client2 = allClients()[0];
49394
+ if (!client2)
49238
49395
  return text("list_streams: not connected", true);
49239
- const streams = await client.listStreams();
49396
+ const streams = await client2.listStreams();
49240
49397
  if (streams.length === 0)
49241
49398
  return text("No active streams.");
49242
49399
  const lines = streams.map((s) => `- **${s.name}** (${s.id.slice(0, 8)}…) by ${s.createdBy}, ${s.subscriberCount} subscriber(s)`);
@@ -49244,10 +49401,10 @@ ${rows.join(`
49244
49401
  `));
49245
49402
  }
49246
49403
  case "mesh_info": {
49247
- const client = allClients()[0];
49248
- if (!client)
49404
+ const client2 = allClients()[0];
49405
+ if (!client2)
49249
49406
  return text("mesh_info: not connected", true);
49250
- const info = await client.meshInfo();
49407
+ const info = await client2.meshInfo();
49251
49408
  if (!info)
49252
49409
  return text("mesh_info: timed out", true);
49253
49410
  const lines = [
@@ -49269,21 +49426,21 @@ ${rows.join(`
49269
49426
  case "ping_mesh": {
49270
49427
  const { priorities: pingPriorities } = args ?? {};
49271
49428
  const toTest = pingPriorities ?? ["now", "next"];
49272
- const client = allClients()[0];
49273
- if (!client)
49429
+ const client2 = allClients()[0];
49430
+ if (!client2)
49274
49431
  return text("ping_mesh: not connected", true);
49275
49432
  const results = [];
49276
- results.push(`WS status: ${client.status}`);
49277
- results.push(`Mesh: ${client.meshSlug}`);
49278
- const peers = await client.listPeers();
49433
+ results.push(`WS status: ${client2.status}`);
49434
+ results.push(`Mesh: ${client2.meshSlug}`);
49435
+ const peers = await client2.listPeers();
49279
49436
  const selfPeer = peers.find((p) => p.displayName === myName);
49280
49437
  results.push(`Your status: ${selfPeer?.status ?? "not found in peer list"}`);
49281
49438
  results.push(`Peers online: ${peers.length}`);
49282
- results.push(`Push buffer: ${client.pushHistory.length} buffered`);
49439
+ results.push(`Push buffer: ${client2.pushHistory.length} buffered`);
49283
49440
  for (const prio of toTest) {
49284
49441
  const sendTime = Date.now();
49285
49442
  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);
49443
+ const sendResult = await client2.send(target?.pubkey ?? "*", `__ping__ ${prio} from ${myName} at ${new Date().toISOString()}`, prio);
49287
49444
  const ackTime = Date.now();
49288
49445
  if (!sendResult.ok) {
49289
49446
  results.push(`[${prio}] SEND FAILED: ${sendResult.error}`);
@@ -49306,14 +49463,14 @@ ${rows.join(`
49306
49463
  const { fileId, to: grantTo } = args ?? {};
49307
49464
  if (!fileId || !grantTo)
49308
49465
  return text("grant_file_access: `fileId` and `to` required", true);
49309
- const client = allClients()[0];
49310
- if (!client)
49466
+ const client2 = allClients()[0];
49467
+ if (!client2)
49311
49468
  return text("grant_file_access: not connected", true);
49312
- const peers = await client.listPeers();
49469
+ const peers = await client2.listPeers();
49313
49470
  const targetPeer = peers.find((p) => p.pubkey === grantTo || p.displayName === grantTo);
49314
49471
  if (!targetPeer)
49315
49472
  return text(`grant_file_access: peer not found: ${grantTo}`, true);
49316
- const result = await client.getFile(fileId);
49473
+ const result = await client2.getFile(fileId);
49317
49474
  if (!result)
49318
49475
  return text("grant_file_access: file not found", true);
49319
49476
  if (!result.encrypted)
@@ -49321,15 +49478,15 @@ ${rows.join(`
49321
49478
  if (!result.sealedKey)
49322
49479
  return text("grant_file_access: no key available (are you the owner?)", true);
49323
49480
  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();
49481
+ const myPubkey = client2.getSessionPubkey();
49482
+ const mySecret = client2.getSessionSecretKey();
49326
49483
  if (!myPubkey || !mySecret)
49327
49484
  return text("grant_file_access: no session keypair", true);
49328
49485
  const kf = await openSealedKey2(result.sealedKey, myPubkey, mySecret);
49329
49486
  if (!kf)
49330
49487
  return text("grant_file_access: cannot decrypt your own key", true);
49331
49488
  const sealedForPeer = await sealKeyForPeer2(kf, targetPeer.pubkey);
49332
- const ok = await client.grantFileAccess(fileId, targetPeer.pubkey, sealedForPeer);
49489
+ const ok = await client2.grantFileAccess(fileId, targetPeer.pubkey, sealedForPeer);
49333
49490
  if (!ok)
49334
49491
  return text("grant_file_access: broker did not confirm", true);
49335
49492
  return text(`Access granted: ${targetPeer.displayName} can now download file ${fileId}`);
@@ -49341,12 +49498,12 @@ ${rows.join(`
49341
49498
  await startClients(config2);
49342
49499
  const transport = new StdioServerTransport;
49343
49500
  await server.connect(transport);
49344
- for (const client of allClients()) {
49345
- client.onPush(async (msg) => {
49501
+ for (const client2 of allClients()) {
49502
+ client2.onPush(async (msg) => {
49346
49503
  if (messageMode === "off")
49347
49504
  return;
49348
49505
  const fromPubkey = msg.senderPubkey || "";
49349
- const fromName = fromPubkey ? await resolvePeerName(client, fromPubkey) : "unknown";
49506
+ const fromName = fromPubkey ? await resolvePeerName(client2, fromPubkey) : "unknown";
49350
49507
  if (messageMode === "inbox") {
49351
49508
  try {
49352
49509
  await server.notification({
@@ -49368,12 +49525,13 @@ ${rows.join(`
49368
49525
  meta: {
49369
49526
  from_id: fromPubkey,
49370
49527
  from_name: fromName,
49371
- mesh_slug: client.meshSlug,
49372
- mesh_id: client.meshId,
49528
+ mesh_slug: client2.meshSlug,
49529
+ mesh_id: client2.meshId,
49373
49530
  priority: msg.priority,
49374
49531
  sent_at: msg.createdAt,
49375
49532
  delivered_at: msg.receivedAt,
49376
- kind: msg.kind
49533
+ kind: msg.kind,
49534
+ ...msg.subtype ? { subtype: msg.subtype } : {}
49377
49535
  }
49378
49536
  }
49379
49537
  });
@@ -49384,7 +49542,7 @@ ${rows.join(`
49384
49542
  `);
49385
49543
  }
49386
49544
  });
49387
- client.onStreamData(async (evt) => {
49545
+ client2.onStreamData(async (evt) => {
49388
49546
  try {
49389
49547
  await server.notification({
49390
49548
  method: "notifications/claude/channel",
@@ -49399,7 +49557,7 @@ ${rows.join(`
49399
49557
  });
49400
49558
  } catch {}
49401
49559
  });
49402
- client.onStateChange(async (change) => {
49560
+ client2.onStateChange(async (change) => {
49403
49561
  try {
49404
49562
  await server.notification({
49405
49563
  method: "notifications/claude/channel",
@@ -49416,17 +49574,17 @@ ${rows.join(`
49416
49574
  });
49417
49575
  }
49418
49576
  setTimeout(async () => {
49419
- const client = allClients()[0];
49420
- if (!client || client.status !== "open")
49577
+ const client2 = allClients()[0];
49578
+ if (!client2 || client2.status !== "open")
49421
49579
  return;
49422
49580
  try {
49423
- const peers = await client.listPeers();
49581
+ const peers = await client2.listPeers();
49424
49582
  const peerNames = peers.filter((p) => p.displayName !== myName).map((p) => p.displayName).join(", ") || "none";
49425
49583
  await server.notification({
49426
49584
  method: "notifications/claude/channel",
49427
49585
  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 }
49586
+ 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.`,
49587
+ meta: { kind: "welcome", mesh_slug: client2.meshSlug }
49430
49588
  }
49431
49589
  });
49432
49590
  } catch {}
@@ -50401,7 +50559,7 @@ init_config();
50401
50559
  // package.json
50402
50560
  var package_default = {
50403
50561
  name: "claudemesh-cli",
50404
- version: "0.6.6",
50562
+ version: "0.6.8",
50405
50563
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
50406
50564
  keywords: [
50407
50565
  "claude-code",
@@ -50834,13 +50992,13 @@ Joined: ${config2.meshes.map((m) => m.slug).join(", ")}`);
50834
50992
  process.exit(1);
50835
50993
  }
50836
50994
  const displayName = opts.displayName ?? config2.displayName ?? `${hostname3()}-${process.pid}`;
50837
- const client = new BrokerClient(mesh, { displayName });
50995
+ const client2 = new BrokerClient(mesh, { displayName });
50838
50996
  try {
50839
- await client.connect();
50840
- const result = await fn(client, mesh);
50997
+ await client2.connect();
50998
+ const result = await fn(client2, mesh);
50841
50999
  return result;
50842
51000
  } finally {
50843
- client.close();
51001
+ client2.close();
50844
51002
  }
50845
51003
  }
50846
51004
 
@@ -50851,8 +51009,8 @@ async function runPeers(flags) {
50851
51009
  const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
50852
51010
  const green = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
50853
51011
  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();
51012
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
51013
+ const peers = await client2.listPeers();
50856
51014
  if (flags.json) {
50857
51015
  console.log(JSON.stringify(peers, null, 2));
50858
51016
  return;
@@ -50877,10 +51035,10 @@ async function runPeers(flags) {
50877
51035
  // src/commands/send.ts
50878
51036
  async function runSend(flags, to, message) {
50879
51037
  const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
50880
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
51038
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
50881
51039
  let targetSpec = to;
50882
51040
  if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
50883
- const peers = await client.listPeers();
51041
+ const peers = await client2.listPeers();
50884
51042
  const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
50885
51043
  if (!match) {
50886
51044
  const names = peers.map((p) => p.displayName).join(", ");
@@ -50889,7 +51047,7 @@ async function runSend(flags, to, message) {
50889
51047
  }
50890
51048
  targetSpec = match.pubkey;
50891
51049
  }
50892
- const result = await client.send(targetSpec, message, priority);
51050
+ const result = await client2.send(targetSpec, message, priority);
50893
51051
  if (result.ok) {
50894
51052
  console.log(`✓ Sent to ${to}${result.messageId ? ` (${result.messageId.slice(0, 8)})` : ""}`);
50895
51053
  } else {
@@ -50915,9 +51073,9 @@ async function runInbox(flags) {
50915
51073
  const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
50916
51074
  const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
50917
51075
  const waitMs = (flags.wait ?? 1) * 1000;
50918
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
51076
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
50919
51077
  await new Promise((resolve2) => setTimeout(resolve2, waitMs));
50920
- const messages = client.drainPushBuffer();
51078
+ const messages = client2.drainPushBuffer();
50921
51079
  if (flags.json) {
50922
51080
  console.log(JSON.stringify(messages, null, 2));
50923
51081
  return;
@@ -50939,8 +51097,8 @@ async function runInbox(flags) {
50939
51097
  async function runStateGet(flags, key) {
50940
51098
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
50941
51099
  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);
51100
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51101
+ const entry = await client2.getState(key);
50944
51102
  if (!entry) {
50945
51103
  console.log(dim(`(not set)`));
50946
51104
  return;
@@ -50961,8 +51119,8 @@ async function runStateSet(flags, key, value) {
50961
51119
  } catch {
50962
51120
  parsed = value;
50963
51121
  }
50964
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
50965
- await client.setState(key, parsed);
51122
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51123
+ await client2.setState(key, parsed);
50966
51124
  console.log(`✓ ${key} = ${JSON.stringify(parsed)}`);
50967
51125
  });
50968
51126
  }
@@ -50970,8 +51128,8 @@ async function runStateList(flags) {
50970
51128
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
50971
51129
  const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
50972
51130
  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();
51131
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
51132
+ const entries = await client2.listState();
50975
51133
  if (flags.json) {
50976
51134
  console.log(JSON.stringify(entries, null, 2));
50977
51135
  return;
@@ -50988,6 +51146,214 @@ async function runStateList(flags) {
50988
51146
  });
50989
51147
  }
50990
51148
 
51149
+ // src/commands/memory.ts
51150
+ async function runRemember(flags, content) {
51151
+ const tags = flags.tags ? flags.tags.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
51152
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51153
+ const id = await client2.remember(content, tags);
51154
+ if (flags.json) {
51155
+ console.log(JSON.stringify({ id, content, tags }));
51156
+ return;
51157
+ }
51158
+ if (id) {
51159
+ console.log(`✓ Remembered (${id.slice(0, 8)})`);
51160
+ } else {
51161
+ console.error("✗ Failed to store memory");
51162
+ process.exit(1);
51163
+ }
51164
+ });
51165
+ }
51166
+ async function runRecall(flags, query) {
51167
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
51168
+ const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
51169
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
51170
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51171
+ const memories = await client2.recall(query);
51172
+ if (flags.json) {
51173
+ console.log(JSON.stringify(memories, null, 2));
51174
+ return;
51175
+ }
51176
+ if (memories.length === 0) {
51177
+ console.log(dim("No memories found."));
51178
+ return;
51179
+ }
51180
+ for (const m of memories) {
51181
+ const tags = m.tags.length ? dim(` [${m.tags.join(", ")}]`) : "";
51182
+ console.log(`${bold2(m.id.slice(0, 8))}${tags}`);
51183
+ console.log(` ${m.content}`);
51184
+ console.log(dim(` ${m.rememberedBy} · ${new Date(m.rememberedAt).toLocaleString()}`));
51185
+ console.log("");
51186
+ }
51187
+ });
51188
+ }
51189
+
51190
+ // src/commands/info.ts
51191
+ init_config();
51192
+ async function runInfo(flags) {
51193
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
51194
+ const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
51195
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
51196
+ const config2 = loadConfig();
51197
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
51198
+ const [brokerInfo, peers, state] = await Promise.all([
51199
+ client2.meshInfo(),
51200
+ client2.listPeers(),
51201
+ client2.listState()
51202
+ ]);
51203
+ const output = {
51204
+ slug: mesh.slug,
51205
+ meshId: mesh.meshId,
51206
+ memberId: mesh.memberId,
51207
+ brokerUrl: mesh.brokerUrl,
51208
+ displayName: config2.displayName ?? null,
51209
+ peerCount: peers.length,
51210
+ stateCount: state.length,
51211
+ ...brokerInfo ?? {}
51212
+ };
51213
+ if (flags.json) {
51214
+ console.log(JSON.stringify(output, null, 2));
51215
+ return;
51216
+ }
51217
+ console.log(bold2(mesh.slug) + dim(` · ${mesh.brokerUrl}`));
51218
+ console.log(dim(` mesh: ${mesh.meshId}`));
51219
+ console.log(dim(` member: ${mesh.memberId}`));
51220
+ console.log(` peers: ${peers.length} connected`);
51221
+ console.log(` state: ${state.length} keys`);
51222
+ if (brokerInfo && typeof brokerInfo === "object") {
51223
+ for (const [k, v] of Object.entries(brokerInfo)) {
51224
+ if (["slug", "meshId", "brokerUrl"].includes(k))
51225
+ continue;
51226
+ console.log(dim(` ${k}: ${JSON.stringify(v)}`));
51227
+ }
51228
+ }
51229
+ });
51230
+ }
51231
+
51232
+ // src/commands/remind.ts
51233
+ function parseDuration(raw) {
51234
+ const m = raw.trim().match(/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day)?$/i);
51235
+ if (!m)
51236
+ return null;
51237
+ const n = parseFloat(m[1]);
51238
+ const unit = (m[2] ?? "s").toLowerCase();
51239
+ if (unit.startsWith("d"))
51240
+ return n * 86400000;
51241
+ if (unit.startsWith("h"))
51242
+ return n * 3600000;
51243
+ if (unit.startsWith("m"))
51244
+ return n * 60000;
51245
+ return n * 1000;
51246
+ }
51247
+ function parseDeliverAt(flags) {
51248
+ if (flags.in) {
51249
+ const ms = parseDuration(flags.in);
51250
+ if (ms === null)
51251
+ return null;
51252
+ return Date.now() + ms;
51253
+ }
51254
+ if (flags.at) {
51255
+ const hm = flags.at.match(/^(\d{1,2}):(\d{2})$/);
51256
+ if (hm) {
51257
+ const now = new Date;
51258
+ const target = new Date(now);
51259
+ target.setHours(parseInt(hm[1], 10), parseInt(hm[2], 10), 0, 0);
51260
+ if (target <= now)
51261
+ target.setDate(target.getDate() + 1);
51262
+ return target.getTime();
51263
+ }
51264
+ const ts = Date.parse(flags.at);
51265
+ return isNaN(ts) ? null : ts;
51266
+ }
51267
+ return null;
51268
+ }
51269
+ async function runRemind(flags, positional) {
51270
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
51271
+ const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
51272
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
51273
+ const action = positional[0];
51274
+ if (action === "list") {
51275
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51276
+ const scheduled = await client2.listScheduled();
51277
+ if (flags.json) {
51278
+ console.log(JSON.stringify(scheduled, null, 2));
51279
+ return;
51280
+ }
51281
+ if (scheduled.length === 0) {
51282
+ console.log(dim("No pending reminders."));
51283
+ return;
51284
+ }
51285
+ for (const m of scheduled) {
51286
+ const when = new Date(m.deliverAt).toLocaleString();
51287
+ const to = m.to === client2.getSessionPubkey() ? dim("(self)") : m.to;
51288
+ console.log(` ${bold2(m.id.slice(0, 8))} → ${to} at ${when}`);
51289
+ console.log(` ${dim(m.message.slice(0, 80))}`);
51290
+ console.log("");
51291
+ }
51292
+ });
51293
+ return;
51294
+ }
51295
+ if (action === "cancel") {
51296
+ const id = positional[1];
51297
+ if (!id) {
51298
+ console.error("Usage: claudemesh remind cancel <id>");
51299
+ process.exit(1);
51300
+ }
51301
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51302
+ const ok = await client2.cancelScheduled(id);
51303
+ if (ok)
51304
+ console.log(`✓ Cancelled ${id}`);
51305
+ else {
51306
+ console.error(`✗ Not found or already fired: ${id}`);
51307
+ process.exit(1);
51308
+ }
51309
+ });
51310
+ return;
51311
+ }
51312
+ const message = action ?? positional.join(" ");
51313
+ if (!message) {
51314
+ console.error("Usage: claudemesh remind <message> --in <duration>");
51315
+ console.error(" claudemesh remind <message> --at <time>");
51316
+ console.error(" claudemesh remind list");
51317
+ console.error(" claudemesh remind cancel <id>");
51318
+ process.exit(1);
51319
+ }
51320
+ const deliverAt = parseDeliverAt(flags);
51321
+ if (deliverAt === null) {
51322
+ console.error('Specify when: --in <duration> (e.g. "2h", "30m") or --at <time> (e.g. "15:00")');
51323
+ process.exit(1);
51324
+ }
51325
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
51326
+ let targetSpec;
51327
+ if (flags.to && flags.to !== "self") {
51328
+ if (flags.to.startsWith("@") || flags.to === "*" || /^[0-9a-f]{64}$/i.test(flags.to)) {
51329
+ targetSpec = flags.to;
51330
+ } else {
51331
+ const peers = await client2.listPeers();
51332
+ const match = peers.find((p) => p.displayName.toLowerCase() === flags.to.toLowerCase());
51333
+ if (!match) {
51334
+ console.error(`Peer "${flags.to}" not found. Online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`);
51335
+ process.exit(1);
51336
+ }
51337
+ targetSpec = match.pubkey;
51338
+ }
51339
+ } else {
51340
+ targetSpec = client2.getSessionPubkey() ?? "*";
51341
+ }
51342
+ const result = await client2.scheduleMessage(targetSpec, message, deliverAt);
51343
+ if (!result) {
51344
+ console.error("✗ Broker did not acknowledge — check connection");
51345
+ process.exit(1);
51346
+ }
51347
+ if (flags.json) {
51348
+ console.log(JSON.stringify(result));
51349
+ return;
51350
+ }
51351
+ const when = new Date(result.deliverAt).toLocaleString();
51352
+ const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
51353
+ console.log(`✓ Reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} at ${when}`);
51354
+ });
51355
+ }
51356
+
50991
51357
  // src/index.ts
50992
51358
  var launch = defineCommand({
50993
51359
  meta: {
@@ -51173,6 +51539,55 @@ var main = defineCommand({
51173
51539
  }
51174
51540
  }
51175
51541
  }),
51542
+ info: defineCommand({
51543
+ meta: { name: "info", description: "Show mesh overview: slug, broker, peer count, state keys" },
51544
+ args: {
51545
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51546
+ json: { type: "boolean", description: "Output as JSON", default: false }
51547
+ },
51548
+ async run({ args }) {
51549
+ await runInfo(args);
51550
+ }
51551
+ }),
51552
+ remember: defineCommand({
51553
+ meta: { name: "remember", description: "Store a memory in the mesh (accessible to all peers)" },
51554
+ args: {
51555
+ content: { type: "positional", description: "Text to remember", required: true },
51556
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51557
+ tags: { type: "string", description: "Comma-separated tags (e.g. task,context)" },
51558
+ json: { type: "boolean", description: "Output as JSON", default: false }
51559
+ },
51560
+ async run({ args }) {
51561
+ await runRemember(args, args.content);
51562
+ }
51563
+ }),
51564
+ recall: defineCommand({
51565
+ meta: { name: "recall", description: "Search mesh memory by keyword or phrase" },
51566
+ args: {
51567
+ query: { type: "positional", description: "Search query", required: true },
51568
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51569
+ json: { type: "boolean", description: "Output as JSON", default: false }
51570
+ },
51571
+ async run({ args }) {
51572
+ await runRecall(args, args.query);
51573
+ }
51574
+ }),
51575
+ remind: defineCommand({
51576
+ meta: { name: "remind", description: "Schedule a reminder or delayed message via the broker" },
51577
+ args: {
51578
+ message: { type: "positional", description: "Message text, or: list | cancel <id>", required: false },
51579
+ extra: { type: "positional", description: "Additional positional args", required: false },
51580
+ in: { type: "string", description: 'Deliver after duration: "2h", "30m", "90s"' },
51581
+ at: { type: "string", description: 'Deliver at time: "15:00" or ISO timestamp' },
51582
+ to: { type: "string", description: "Recipient (default: self). Name, @group, pubkey, or *" },
51583
+ mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51584
+ json: { type: "boolean", description: "Output as JSON", default: false }
51585
+ },
51586
+ async run({ args, rawArgs }) {
51587
+ const positionals = rawArgs.filter((a) => !a.startsWith("-"));
51588
+ await runRemind(args, positionals);
51589
+ }
51590
+ }),
51176
51591
  status: defineCommand({
51177
51592
  meta: { name: "status", description: "Check broker reachability for each joined mesh" },
51178
51593
  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.8",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -30,17 +30,6 @@
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
33
- "scripts": {
34
- "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
35
- "clean": "git clean -xdf .cache .turbo dist node_modules",
36
- "dev": "bun --hot src/index.ts",
37
- "start": "bun src/index.ts",
38
- "format": "prettier --check . --ignore-path ../../.gitignore",
39
- "lint": "eslint",
40
- "prepublishOnly": "bun run build",
41
- "test": "vitest run",
42
- "typecheck": "tsc --noEmit"
43
- },
44
33
  "prettier": "@turbostarter/prettier-config",
45
34
  "engines": {
46
35
  "node": ">=20"
@@ -53,15 +42,25 @@
53
42
  "zod": "4.1.13"
54
43
  },
55
44
  "devDependencies": {
56
- "@turbostarter/eslint-config": "workspace:*",
57
- "@turbostarter/prettier-config": "workspace:*",
58
- "@turbostarter/tsconfig": "workspace:*",
59
- "@turbostarter/vitest-config": "workspace:*",
60
45
  "@types/libsodium-wrappers": "0.7.14",
61
46
  "@types/ws": "8.5.13",
62
- "eslint": "catalog:",
63
- "prettier": "catalog:",
64
- "typescript": "catalog:",
65
- "vitest": "catalog:"
47
+ "eslint": "9.39.0",
48
+ "prettier": "3.6.2",
49
+ "typescript": "5.9.3",
50
+ "vitest": "4.0.14",
51
+ "@turbostarter/prettier-config": "0.1.0",
52
+ "@turbostarter/eslint-config": "0.1.0",
53
+ "@turbostarter/vitest-config": "0.1.0",
54
+ "@turbostarter/tsconfig": "0.1.0"
55
+ },
56
+ "scripts": {
57
+ "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
58
+ "clean": "git clean -xdf .cache .turbo dist node_modules",
59
+ "dev": "bun --hot src/index.ts",
60
+ "start": "bun src/index.ts",
61
+ "format": "prettier --check . --ignore-path ../../.gitignore",
62
+ "lint": "eslint",
63
+ "test": "vitest run",
64
+ "typecheck": "tsc --noEmit"
66
65
  }
67
- }
66
+ }