claudemesh-cli 1.1.0 → 1.2.1

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.
@@ -88,7 +88,7 @@ __export(exports_urls, {
88
88
  VERSION: () => VERSION,
89
89
  URLS: () => URLS
90
90
  });
91
- var URLS, VERSION = "1.1.0", env;
91
+ var URLS, VERSION = "1.2.1", env;
92
92
  var init_urls = __esm(() => {
93
93
  URLS = {
94
94
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -6392,11 +6392,136 @@ var init_ban = __esm(() => {
6392
6392
  init_exit_codes();
6393
6393
  });
6394
6394
 
6395
+ // src/services/bridge/protocol.ts
6396
+ import { homedir as homedir4 } from "node:os";
6397
+ import { join as join4 } from "node:path";
6398
+ function socketPath(meshSlug) {
6399
+ return join4(homedir4(), ".claudemesh", "sockets", `${meshSlug}.sock`);
6400
+ }
6401
+ function socketDir() {
6402
+ return join4(homedir4(), ".claudemesh", "sockets");
6403
+ }
6404
+ function frame(obj) {
6405
+ return JSON.stringify(obj) + `
6406
+ `;
6407
+ }
6408
+
6409
+ class LineParser {
6410
+ buf = "";
6411
+ feed(chunk) {
6412
+ this.buf += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
6413
+ const lines = [];
6414
+ let nl = this.buf.indexOf(`
6415
+ `);
6416
+ while (nl !== -1) {
6417
+ lines.push(this.buf.slice(0, nl));
6418
+ this.buf = this.buf.slice(nl + 1);
6419
+ nl = this.buf.indexOf(`
6420
+ `);
6421
+ }
6422
+ return lines;
6423
+ }
6424
+ }
6425
+ var init_protocol = () => {};
6426
+
6427
+ // src/services/bridge/client.ts
6428
+ import { createConnection } from "node:net";
6429
+ import { existsSync as existsSync5 } from "node:fs";
6430
+ import { randomUUID as randomUUID2 } from "node:crypto";
6431
+ async function tryBridge(meshSlug, verb, args = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
6432
+ const path = socketPath(meshSlug);
6433
+ if (!existsSync5(path))
6434
+ return null;
6435
+ return new Promise((resolve) => {
6436
+ const id = randomUUID2();
6437
+ const req = { id, verb, args };
6438
+ const parser = new LineParser;
6439
+ let settled = false;
6440
+ const finish = (value) => {
6441
+ if (settled)
6442
+ return;
6443
+ settled = true;
6444
+ try {
6445
+ socket.destroy();
6446
+ } catch {}
6447
+ clearTimeout(timer);
6448
+ resolve(value);
6449
+ };
6450
+ const socket = createConnection({ path });
6451
+ const timer = setTimeout(() => {
6452
+ finish(null);
6453
+ }, timeoutMs);
6454
+ socket.on("connect", () => {
6455
+ try {
6456
+ socket.write(frame(req));
6457
+ } catch {
6458
+ finish(null);
6459
+ }
6460
+ });
6461
+ socket.on("data", (chunk) => {
6462
+ const lines = parser.feed(chunk);
6463
+ for (const line of lines) {
6464
+ if (!line.trim())
6465
+ continue;
6466
+ let res;
6467
+ try {
6468
+ res = JSON.parse(line);
6469
+ } catch {
6470
+ continue;
6471
+ }
6472
+ if (res.id !== id)
6473
+ continue;
6474
+ if (res.ok)
6475
+ finish({ ok: true, result: res.result });
6476
+ else
6477
+ finish({ ok: false, error: res.error });
6478
+ return;
6479
+ }
6480
+ });
6481
+ socket.on("error", (err) => {
6482
+ const code = err.code;
6483
+ if (code === "ECONNREFUSED" || code === "ENOENT" || code === "EPERM") {
6484
+ finish(null);
6485
+ } else {
6486
+ finish(null);
6487
+ }
6488
+ });
6489
+ socket.on("close", () => {
6490
+ finish(null);
6491
+ });
6492
+ });
6493
+ }
6494
+ var DEFAULT_TIMEOUT_MS = 5000;
6495
+ var init_client3 = __esm(() => {
6496
+ init_protocol();
6497
+ });
6498
+
6395
6499
  // src/commands/peers.ts
6396
6500
  var exports_peers = {};
6397
6501
  __export(exports_peers, {
6398
6502
  runPeers: () => runPeers
6399
6503
  });
6504
+ function projectFields(record, fields) {
6505
+ const out = {};
6506
+ for (const f of fields) {
6507
+ const sourceKey = FIELD_ALIAS[f] ?? f;
6508
+ out[f] = record[sourceKey];
6509
+ }
6510
+ return out;
6511
+ }
6512
+ async function listPeersForMesh(slug) {
6513
+ const bridged = await tryBridge(slug, "peers");
6514
+ if (bridged && bridged.ok) {
6515
+ return bridged.result;
6516
+ }
6517
+ let result = [];
6518
+ await withMesh({ meshSlug: slug }, async (client) => {
6519
+ const all = await client.listPeers();
6520
+ const selfPubkey = client.getSessionPubkey();
6521
+ result = selfPubkey ? all.filter((p) => p.pubkey !== selfPubkey) : all;
6522
+ });
6523
+ return result;
6524
+ }
6400
6525
  async function runPeers(flags) {
6401
6526
  const config = readConfig();
6402
6527
  const slugs = flags.mesh ? [flags.mesh] : config.meshes.map((m) => m.slug);
@@ -6405,55 +6530,59 @@ async function runPeers(flags) {
6405
6530
  render.hint("claudemesh <invite-url> # join + launch");
6406
6531
  process.exit(1);
6407
6532
  }
6533
+ const fieldList = typeof flags.json === "string" && flags.json.length > 0 ? flags.json.split(",").map((s) => s.trim()).filter(Boolean) : null;
6534
+ const wantsJson = flags.json !== undefined && flags.json !== false;
6408
6535
  const allJson = [];
6409
6536
  for (const slug of slugs) {
6410
6537
  try {
6411
- await withMesh({ meshSlug: slug }, async (client, mesh) => {
6412
- const allPeers = await client.listPeers();
6413
- const selfPubkey = client.getSessionPubkey();
6414
- const peers = selfPubkey ? allPeers.filter((p) => p.pubkey !== selfPubkey) : allPeers;
6415
- if (flags.json) {
6416
- allJson.push({ mesh: mesh.slug, peers });
6417
- return;
6418
- }
6419
- render.section(`peers on ${mesh.slug} (${peers.length})`);
6420
- if (peers.length === 0) {
6421
- render.info(dim(" (no peers connected)"));
6422
- return;
6423
- }
6424
- for (const p of peers) {
6425
- const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
6426
- const statusDot = p.status === "working" ? yellow("●") : green("●");
6427
- const name = bold(p.displayName);
6428
- const meta = [];
6429
- if (p.peerType)
6430
- meta.push(p.peerType);
6431
- if (p.channel)
6432
- meta.push(p.channel);
6433
- if (p.model)
6434
- meta.push(p.model);
6435
- const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
6436
- const summary = p.summary ? dim(` — ${p.summary}`) : "";
6437
- const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
6438
- render.info(`${statusDot} ${name}${groups}${metaStr}${pubkeyTag}${summary}`);
6439
- if (p.cwd)
6440
- render.info(dim(` cwd: ${p.cwd}`));
6441
- }
6442
- });
6538
+ const peers = await listPeersForMesh(slug);
6539
+ if (wantsJson) {
6540
+ const projected = fieldList ? peers.map((p) => projectFields(p, fieldList)) : peers;
6541
+ allJson.push({ mesh: slug, peers: projected });
6542
+ continue;
6543
+ }
6544
+ render.section(`peers on ${slug} (${peers.length})`);
6545
+ if (peers.length === 0) {
6546
+ render.info(dim(" (no peers connected)"));
6547
+ continue;
6548
+ }
6549
+ for (const p of peers) {
6550
+ const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
6551
+ const statusDot = p.status === "working" ? yellow("●") : green("●");
6552
+ const name = bold(p.displayName);
6553
+ const meta = [];
6554
+ if (p.peerType)
6555
+ meta.push(p.peerType);
6556
+ if (p.channel)
6557
+ meta.push(p.channel);
6558
+ if (p.model)
6559
+ meta.push(p.model);
6560
+ const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
6561
+ const summary = p.summary ? dim(` — ${p.summary}`) : "";
6562
+ const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
6563
+ render.info(`${statusDot} ${name}${groups}${metaStr}${pubkeyTag}${summary}`);
6564
+ if (p.cwd)
6565
+ render.info(dim(` cwd: ${p.cwd}`));
6566
+ }
6443
6567
  } catch (e) {
6444
6568
  render.err(`${slug}: ${e instanceof Error ? e.message : String(e)}`);
6445
6569
  }
6446
6570
  }
6447
- if (flags.json) {
6571
+ if (wantsJson) {
6448
6572
  process.stdout.write(JSON.stringify(slugs.length === 1 ? allJson[0]?.peers : allJson, null, 2) + `
6449
6573
  `);
6450
6574
  }
6451
6575
  }
6576
+ var FIELD_ALIAS;
6452
6577
  var init_peers = __esm(() => {
6453
6578
  init_connect();
6454
6579
  init_facade();
6580
+ init_client3();
6455
6581
  init_render();
6456
6582
  init_styles();
6583
+ FIELD_ALIAS = {
6584
+ name: "displayName"
6585
+ };
6457
6586
  });
6458
6587
 
6459
6588
  // src/commands/send.ts
@@ -6462,7 +6591,33 @@ __export(exports_send, {
6462
6591
  runSend: () => runSend
6463
6592
  });
6464
6593
  async function runSend(flags, to, message) {
6594
+ if (!to || !message) {
6595
+ render.err("Usage: claudemesh send <to> <message>");
6596
+ process.exit(1);
6597
+ }
6465
6598
  const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
6599
+ const config = readConfig();
6600
+ const meshSlug = flags.mesh ?? (config.meshes.length === 1 ? config.meshes[0].slug : null);
6601
+ if (meshSlug) {
6602
+ const bridged = await tryBridge(meshSlug, "send", { to, message, priority });
6603
+ if (bridged !== null) {
6604
+ if (bridged.ok) {
6605
+ const r = bridged.result;
6606
+ if (flags.json) {
6607
+ console.log(JSON.stringify({ ok: true, messageId: r.messageId, target: to }));
6608
+ } else {
6609
+ render.ok(`sent to ${to}`, r.messageId ? dim(r.messageId.slice(0, 8)) : undefined);
6610
+ }
6611
+ return;
6612
+ }
6613
+ if (flags.json) {
6614
+ console.log(JSON.stringify({ ok: false, error: bridged.error }));
6615
+ } else {
6616
+ render.err(`send failed: ${bridged.error}`);
6617
+ }
6618
+ process.exit(1);
6619
+ }
6620
+ }
6466
6621
  await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
6467
6622
  let targetSpec = to;
6468
6623
  if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
@@ -6470,22 +6625,34 @@ async function runSend(flags, to, message) {
6470
6625
  const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
6471
6626
  if (!match) {
6472
6627
  const names = peers.map((p) => p.displayName).join(", ");
6473
- console.error(`Peer "${to}" not found. Online: ${names || "(none)"}`);
6628
+ render.err(`Peer "${to}" not found.`, `online: ${names || "(none)"}`);
6474
6629
  process.exit(1);
6475
6630
  }
6476
6631
  targetSpec = match.pubkey;
6477
6632
  }
6478
6633
  const result = await client.send(targetSpec, message, priority);
6479
6634
  if (result.ok) {
6480
- console.log(`✓ Sent to ${to}${result.messageId ? ` (${result.messageId.slice(0, 8)})` : ""}`);
6635
+ if (flags.json) {
6636
+ console.log(JSON.stringify({ ok: true, messageId: result.messageId, target: to }));
6637
+ } else {
6638
+ render.ok(`sent to ${to}`, result.messageId ? dim(result.messageId.slice(0, 8)) : undefined);
6639
+ }
6481
6640
  } else {
6482
- console.error(`✗ Send failed: ${result.error ?? "unknown error"}`);
6641
+ if (flags.json) {
6642
+ console.log(JSON.stringify({ ok: false, error: result.error ?? "unknown" }));
6643
+ } else {
6644
+ render.err(`send failed: ${result.error ?? "unknown error"}`);
6645
+ }
6483
6646
  process.exit(1);
6484
6647
  }
6485
6648
  });
6486
6649
  }
6487
6650
  var init_send = __esm(() => {
6488
6651
  init_connect();
6652
+ init_facade();
6653
+ init_client3();
6654
+ init_render();
6655
+ init_styles();
6489
6656
  });
6490
6657
 
6491
6658
  // src/commands/inbox.ts
@@ -6731,20 +6898,40 @@ __export(exports_broker_actions, {
6731
6898
  runForget: () => runForget,
6732
6899
  runClock: () => runClock
6733
6900
  });
6901
+ function unambiguousMesh(opts) {
6902
+ if (opts.mesh)
6903
+ return opts.mesh;
6904
+ const config = readConfig();
6905
+ return config.meshes.length === 1 ? config.meshes[0].slug : null;
6906
+ }
6734
6907
  async function runStatusSet(state, opts) {
6735
6908
  const valid = ["idle", "working", "dnd"];
6736
6909
  if (!valid.includes(state)) {
6737
6910
  render.err(`Invalid status: ${state}`, `must be one of: ${valid.join(", ")}`);
6738
6911
  return EXIT.INVALID_ARGS;
6739
6912
  }
6913
+ const meshSlug = unambiguousMesh(opts);
6914
+ if (meshSlug) {
6915
+ const bridged = await tryBridge(meshSlug, "status_set", { status: state });
6916
+ if (bridged !== null) {
6917
+ if (bridged.ok) {
6918
+ if (opts.json)
6919
+ console.log(JSON.stringify({ status: state }));
6920
+ else
6921
+ render.ok(`status set to ${bold(state)}`);
6922
+ return EXIT.SUCCESS;
6923
+ }
6924
+ render.err(bridged.error);
6925
+ return EXIT.INTERNAL_ERROR;
6926
+ }
6927
+ }
6740
6928
  await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
6741
6929
  await client.setStatus(state);
6742
6930
  });
6743
- if (opts.json) {
6931
+ if (opts.json)
6744
6932
  console.log(JSON.stringify({ status: state }));
6745
- return EXIT.SUCCESS;
6746
- }
6747
- render.ok(`status set to ${bold(state)}`);
6933
+ else
6934
+ render.ok(`status set to ${bold(state)}`);
6748
6935
  return EXIT.SUCCESS;
6749
6936
  }
6750
6937
  async function runSummary(text, opts) {
@@ -6752,14 +6939,28 @@ async function runSummary(text, opts) {
6752
6939
  render.err("Usage: claudemesh summary <text>");
6753
6940
  return EXIT.INVALID_ARGS;
6754
6941
  }
6942
+ const meshSlug = unambiguousMesh(opts);
6943
+ if (meshSlug) {
6944
+ const bridged = await tryBridge(meshSlug, "summary", { summary: text });
6945
+ if (bridged !== null) {
6946
+ if (bridged.ok) {
6947
+ if (opts.json)
6948
+ console.log(JSON.stringify({ summary: text }));
6949
+ else
6950
+ render.ok("summary set", dim(text));
6951
+ return EXIT.SUCCESS;
6952
+ }
6953
+ render.err(bridged.error);
6954
+ return EXIT.INTERNAL_ERROR;
6955
+ }
6956
+ }
6755
6957
  await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
6756
6958
  await client.setSummary(text);
6757
6959
  });
6758
- if (opts.json) {
6960
+ if (opts.json)
6759
6961
  console.log(JSON.stringify({ summary: text }));
6760
- return EXIT.SUCCESS;
6761
- }
6762
- render.ok("summary set", dim(text));
6962
+ else
6963
+ render.ok("summary set", dim(text));
6763
6964
  return EXIT.SUCCESS;
6764
6965
  }
6765
6966
  async function runVisible(value, opts) {
@@ -6772,14 +6973,28 @@ async function runVisible(value, opts) {
6772
6973
  render.err("Usage: claudemesh visible <true|false>");
6773
6974
  return EXIT.INVALID_ARGS;
6774
6975
  }
6976
+ const meshSlug = unambiguousMesh(opts);
6977
+ if (meshSlug) {
6978
+ const bridged = await tryBridge(meshSlug, "visible", { visible });
6979
+ if (bridged !== null) {
6980
+ if (bridged.ok) {
6981
+ if (opts.json)
6982
+ console.log(JSON.stringify({ visible }));
6983
+ else
6984
+ render.ok(visible ? "you are now visible to peers" : "you are now hidden", visible ? undefined : "direct messages still reach you");
6985
+ return EXIT.SUCCESS;
6986
+ }
6987
+ render.err(bridged.error);
6988
+ return EXIT.INTERNAL_ERROR;
6989
+ }
6990
+ }
6775
6991
  await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
6776
6992
  await client.setVisible(visible);
6777
6993
  });
6778
- if (opts.json) {
6994
+ if (opts.json)
6779
6995
  console.log(JSON.stringify({ visible }));
6780
- return EXIT.SUCCESS;
6781
- }
6782
- render.ok(visible ? "you are now visible to peers" : "you are now hidden", visible ? undefined : "direct messages still reach you");
6996
+ else
6997
+ render.ok(visible ? "you are now visible to peers" : "you are now hidden", visible ? undefined : "direct messages still reach you");
6783
6998
  return EXIT.SUCCESS;
6784
6999
  }
6785
7000
  async function runGroupJoin(name, opts) {
@@ -6967,6 +7182,8 @@ async function runTaskComplete(id, result, opts) {
6967
7182
  }
6968
7183
  var init_broker_actions = __esm(() => {
6969
7184
  init_connect();
7185
+ init_facade();
7186
+ init_client3();
6970
7187
  init_render();
6971
7188
  init_styles();
6972
7189
  init_exit_codes();
@@ -7278,17 +7495,17 @@ __export(exports_install, {
7278
7495
  import {
7279
7496
  chmodSync as chmodSync3,
7280
7497
  copyFileSync,
7281
- existsSync as existsSync5,
7498
+ existsSync as existsSync6,
7282
7499
  mkdirSync as mkdirSync3,
7283
7500
  readFileSync as readFileSync4,
7284
7501
  writeFileSync as writeFileSync5
7285
7502
  } from "node:fs";
7286
- import { homedir as homedir4, platform as platform5 } from "node:os";
7287
- import { dirname as dirname2, join as join4, resolve } from "node:path";
7503
+ import { homedir as homedir5, platform as platform5 } from "node:os";
7504
+ import { dirname as dirname2, join as join5, resolve } from "node:path";
7288
7505
  import { fileURLToPath } from "node:url";
7289
7506
  import { spawnSync as spawnSync3 } from "node:child_process";
7290
7507
  function readClaudeConfig() {
7291
- if (!existsSync5(CLAUDE_CONFIG))
7508
+ if (!existsSync6(CLAUDE_CONFIG))
7292
7509
  return {};
7293
7510
  const text = readFileSync4(CLAUDE_CONFIG, "utf-8").trim();
7294
7511
  if (!text)
@@ -7300,12 +7517,12 @@ function readClaudeConfig() {
7300
7517
  }
7301
7518
  }
7302
7519
  function backupClaudeConfig() {
7303
- if (!existsSync5(CLAUDE_CONFIG))
7520
+ if (!existsSync6(CLAUDE_CONFIG))
7304
7521
  return;
7305
- const backupDir = join4(dirname2(CLAUDE_CONFIG), ".claude", "backups");
7522
+ const backupDir = join5(dirname2(CLAUDE_CONFIG), ".claude", "backups");
7306
7523
  mkdirSync3(backupDir, { recursive: true });
7307
7524
  const ts = Date.now();
7308
- const dest = join4(backupDir, `.claude.json.pre-claudemesh.${ts}`);
7525
+ const dest = join5(backupDir, `.claude.json.pre-claudemesh.${ts}`);
7309
7526
  copyFileSync(CLAUDE_CONFIG, dest);
7310
7527
  }
7311
7528
  function patchMcpServer(entry) {
@@ -7329,7 +7546,7 @@ function patchMcpServer(entry) {
7329
7546
  return action;
7330
7547
  }
7331
7548
  function removeMcpServer() {
7332
- if (!existsSync5(CLAUDE_CONFIG))
7549
+ if (!existsSync6(CLAUDE_CONFIG))
7333
7550
  return false;
7334
7551
  backupClaudeConfig();
7335
7552
  const cfg = readClaudeConfig();
@@ -7353,16 +7570,65 @@ function bunAvailable() {
7353
7570
  const res = platform5() === "win32" ? spawnSync3("where", ["bun"]) : spawnSync3("sh", ["-c", "command -v bun"]);
7354
7571
  return res.status === 0;
7355
7572
  }
7573
+ function isBundledFile(p) {
7574
+ return /[/\\]dist[/\\]/.test(p);
7575
+ }
7356
7576
  function resolveEntry() {
7357
7577
  const here = fileURLToPath(import.meta.url);
7358
- if (here.endsWith("/dist/index.js") || here.endsWith("\\dist\\index.js")) {
7578
+ if (isBundledFile(here))
7359
7579
  return here;
7360
- }
7361
7580
  return resolve(dirname2(here), "..", "index.ts");
7362
7581
  }
7582
+ function resolveBundledSkillsDir() {
7583
+ const here = fileURLToPath(import.meta.url);
7584
+ const pkgRoot = resolve(dirname2(here), "..", "..");
7585
+ const skillsDir = join5(pkgRoot, "skills");
7586
+ if (existsSync6(skillsDir))
7587
+ return skillsDir;
7588
+ return null;
7589
+ }
7590
+ function installSkills() {
7591
+ const src = resolveBundledSkillsDir();
7592
+ if (!src)
7593
+ return [];
7594
+ const fs = __require("node:fs");
7595
+ const installed = [];
7596
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
7597
+ if (!entry.isDirectory())
7598
+ continue;
7599
+ const srcDir = join5(src, entry.name);
7600
+ const dstDir = join5(CLAUDE_SKILLS_ROOT, entry.name);
7601
+ mkdirSync3(dstDir, { recursive: true });
7602
+ for (const file of fs.readdirSync(srcDir, { withFileTypes: true })) {
7603
+ if (!file.isFile())
7604
+ continue;
7605
+ copyFileSync(join5(srcDir, file.name), join5(dstDir, file.name));
7606
+ }
7607
+ installed.push(entry.name);
7608
+ }
7609
+ return installed;
7610
+ }
7611
+ function uninstallSkills() {
7612
+ const src = resolveBundledSkillsDir();
7613
+ if (!src)
7614
+ return [];
7615
+ const fs = __require("node:fs");
7616
+ const removed = [];
7617
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
7618
+ if (!entry.isDirectory())
7619
+ continue;
7620
+ const dstDir = join5(CLAUDE_SKILLS_ROOT, entry.name);
7621
+ if (existsSync6(dstDir)) {
7622
+ try {
7623
+ fs.rmSync(dstDir, { recursive: true, force: true });
7624
+ removed.push(entry.name);
7625
+ } catch {}
7626
+ }
7627
+ }
7628
+ return removed;
7629
+ }
7363
7630
  function buildMcpEntry(entryPath) {
7364
- const isBundled = entryPath.endsWith("/dist/index.js") || entryPath.endsWith("\\dist\\index.js");
7365
- if (isBundled) {
7631
+ if (isBundledFile(entryPath)) {
7366
7632
  return {
7367
7633
  command: "claudemesh",
7368
7634
  args: ["mcp"]
@@ -7377,7 +7643,7 @@ function entriesEqual(a, b) {
7377
7643
  return a.command === b.command && JSON.stringify(a.args ?? []) === JSON.stringify(b.args ?? []);
7378
7644
  }
7379
7645
  function readClaudeSettings() {
7380
- if (!existsSync5(CLAUDE_SETTINGS))
7646
+ if (!existsSync6(CLAUDE_SETTINGS))
7381
7647
  return {};
7382
7648
  const text = readFileSync4(CLAUDE_SETTINGS, "utf-8").trim();
7383
7649
  if (!text)
@@ -7404,7 +7670,7 @@ function installAllowedTools() {
7404
7670
  return { added: toAdd, unchanged: CLAUDEMESH_TOOLS.length - toAdd.length };
7405
7671
  }
7406
7672
  function uninstallAllowedTools() {
7407
- if (!existsSync5(CLAUDE_SETTINGS))
7673
+ if (!existsSync6(CLAUDE_SETTINGS))
7408
7674
  return 0;
7409
7675
  const settings = readClaudeSettings();
7410
7676
  const existing = settings.allowedTools ?? [];
@@ -7439,7 +7705,7 @@ function installHooks() {
7439
7705
  return { added, unchanged };
7440
7706
  }
7441
7707
  function uninstallHooks() {
7442
- if (!existsSync5(CLAUDE_SETTINGS))
7708
+ if (!existsSync6(CLAUDE_SETTINGS))
7443
7709
  return 0;
7444
7710
  const settings = readClaudeSettings();
7445
7711
  const hooks = settings.hooks;
@@ -7479,15 +7745,16 @@ function installStatusLine() {
7479
7745
  }
7480
7746
  function runInstall(args = []) {
7481
7747
  const skipHooks = args.includes("--no-hooks");
7748
+ const skipSkill = args.includes("--no-skill");
7482
7749
  const wantStatusLine = args.includes("--status-line");
7483
7750
  render.section("claudemesh install");
7484
7751
  const entry = resolveEntry();
7485
- const isBundled = entry.endsWith("/dist/index.js") || entry.endsWith("\\dist\\index.js");
7486
- if (!isBundled && !bunAvailable()) {
7752
+ const bundled = isBundledFile(entry);
7753
+ if (!bundled && !bunAvailable()) {
7487
7754
  render.err("`bun` is not on PATH.", "Install Bun first: https://bun.com");
7488
7755
  process.exit(1);
7489
7756
  }
7490
- if (!existsSync5(entry)) {
7757
+ if (!existsSync6(entry)) {
7491
7758
  render.err(`MCP entry not found at ${entry}`);
7492
7759
  process.exit(1);
7493
7760
  }
@@ -7533,6 +7800,19 @@ function runInstall(args = []) {
7533
7800
  } else {
7534
7801
  render.info(dim("· Hooks skipped (--no-hooks)"));
7535
7802
  }
7803
+ if (!skipSkill) {
7804
+ try {
7805
+ const installed = installSkills();
7806
+ if (installed.length > 0) {
7807
+ render.ok(`Claude skill${installed.length === 1 ? "" : "s"} installed`, installed.join(", "));
7808
+ render.info(dim(` ${join5(CLAUDE_SKILLS_ROOT, installed[0])}/SKILL.md`));
7809
+ }
7810
+ } catch (e) {
7811
+ render.warn(`skill install failed: ${e instanceof Error ? e.message : String(e)}`);
7812
+ }
7813
+ } else {
7814
+ render.info(dim("· Skill install skipped (--no-skill)"));
7815
+ }
7536
7816
  if (wantStatusLine) {
7537
7817
  try {
7538
7818
  const { installed } = installStatusLine();
@@ -7595,16 +7875,27 @@ function runUninstall() {
7595
7875
  } catch (e) {
7596
7876
  render.warn(`hook removal failed: ${e instanceof Error ? e.message : String(e)}`);
7597
7877
  }
7878
+ try {
7879
+ const removed = uninstallSkills();
7880
+ if (removed.length > 0) {
7881
+ render.ok(`Skill${removed.length === 1 ? "" : "s"} removed`, removed.join(", "));
7882
+ } else {
7883
+ render.info(dim("· No claudemesh skills to remove"));
7884
+ }
7885
+ } catch (e) {
7886
+ render.warn(`skill removal failed: ${e instanceof Error ? e.message : String(e)}`);
7887
+ }
7598
7888
  render.blank();
7599
7889
  render.info("Restart Claude Code to drop the MCP connection + hooks.");
7600
7890
  }
7601
- var MCP_NAME = "claudemesh", CLAUDE_CONFIG, CLAUDE_SETTINGS, HOOK_COMMAND_STOP = "claudemesh hook idle", HOOK_COMMAND_USER_PROMPT = "claudemesh hook working", HOOK_MARKER = "claudemesh hook ", CLAUDEMESH_TOOLS;
7891
+ var MCP_NAME = "claudemesh", CLAUDE_CONFIG, CLAUDE_SETTINGS, HOOK_COMMAND_STOP = "claudemesh hook idle", HOOK_COMMAND_USER_PROMPT = "claudemesh hook working", HOOK_MARKER = "claudemesh hook ", CLAUDE_SKILLS_ROOT, CLAUDEMESH_TOOLS;
7602
7892
  var init_install = __esm(() => {
7603
7893
  init_facade();
7604
7894
  init_render();
7605
7895
  init_styles();
7606
- CLAUDE_CONFIG = join4(homedir4(), ".claude.json");
7607
- CLAUDE_SETTINGS = join4(homedir4(), ".claude", "settings.json");
7896
+ CLAUDE_CONFIG = join5(homedir5(), ".claude.json");
7897
+ CLAUDE_SETTINGS = join5(homedir5(), ".claude", "settings.json");
7898
+ CLAUDE_SKILLS_ROOT = join5(homedir5(), ".claude", "skills");
7608
7899
  CLAUDEMESH_TOOLS = [
7609
7900
  "mcp__claudemesh__cancel_scheduled",
7610
7901
  "mcp__claudemesh__check_messages",
@@ -7659,10 +7950,19 @@ var exports_uninstall = {};
7659
7950
  __export(exports_uninstall, {
7660
7951
  uninstall: () => uninstall
7661
7952
  });
7662
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync6 } from "node:fs";
7953
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync7, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
7954
+ import { join as join6, dirname as dirname3 } from "node:path";
7955
+ import { homedir as homedir6 } from "node:os";
7956
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7957
+ function bundledSkillsDir() {
7958
+ const here = fileURLToPath2(import.meta.url);
7959
+ const pkgRoot = join6(dirname3(here), "..", "..");
7960
+ const skillsDir = join6(pkgRoot, "skills");
7961
+ return existsSync7(skillsDir) ? skillsDir : null;
7962
+ }
7663
7963
  async function uninstall() {
7664
7964
  let removed = 0;
7665
- if (existsSync6(PATHS.CLAUDE_JSON)) {
7965
+ if (existsSync7(PATHS.CLAUDE_JSON)) {
7666
7966
  try {
7667
7967
  const raw = readFileSync5(PATHS.CLAUDE_JSON, "utf-8");
7668
7968
  const config = JSON.parse(raw);
@@ -7676,7 +7976,7 @@ async function uninstall() {
7676
7976
  }
7677
7977
  } catch {}
7678
7978
  }
7679
- if (existsSync6(PATHS.CLAUDE_SETTINGS)) {
7979
+ if (existsSync7(PATHS.CLAUDE_SETTINGS)) {
7680
7980
  try {
7681
7981
  const raw = readFileSync5(PATHS.CLAUDE_SETTINGS, "utf-8");
7682
7982
  const config = JSON.parse(raw);
@@ -7707,16 +8007,39 @@ async function uninstall() {
7707
8007
  }
7708
8008
  } catch {}
7709
8009
  }
8010
+ const src = bundledSkillsDir();
8011
+ if (src) {
8012
+ const removedSkills = [];
8013
+ try {
8014
+ for (const entry of readdirSync2(src, { withFileTypes: true })) {
8015
+ if (!entry.isDirectory())
8016
+ continue;
8017
+ const dst = join6(CLAUDE_SKILLS_ROOT2, entry.name);
8018
+ if (existsSync7(dst)) {
8019
+ try {
8020
+ rmSync2(dst, { recursive: true, force: true });
8021
+ removedSkills.push(entry.name);
8022
+ } catch {}
8023
+ }
8024
+ }
8025
+ if (removedSkills.length > 0) {
8026
+ render.ok(`removed Claude skill${removedSkills.length === 1 ? "" : "s"}`, removedSkills.join(", "));
8027
+ removed++;
8028
+ }
8029
+ } catch {}
8030
+ }
7710
8031
  if (removed === 0) {
7711
8032
  render.info(dim("Nothing to remove — claudemesh was not installed."));
7712
8033
  }
7713
8034
  return EXIT.SUCCESS;
7714
8035
  }
8036
+ var CLAUDE_SKILLS_ROOT2;
7715
8037
  var init_uninstall = __esm(() => {
7716
8038
  init_paths();
7717
8039
  init_render();
7718
8040
  init_styles();
7719
8041
  init_exit_codes();
8042
+ CLAUDE_SKILLS_ROOT2 = join6(homedir6(), ".claude", "skills");
7720
8043
  });
7721
8044
 
7722
8045
  // src/commands/doctor.ts
@@ -7724,9 +8047,9 @@ var exports_doctor = {};
7724
8047
  __export(exports_doctor, {
7725
8048
  runDoctor: () => runDoctor
7726
8049
  });
7727
- import { existsSync as existsSync7, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7728
- import { homedir as homedir5, platform as platform6 } from "node:os";
7729
- import { join as join5 } from "node:path";
8050
+ import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
8051
+ import { homedir as homedir7, platform as platform6 } from "node:os";
8052
+ import { join as join7 } from "node:path";
7730
8053
  import { spawnSync as spawnSync4 } from "node:child_process";
7731
8054
  function checkNode() {
7732
8055
  const major = Number(process.versions.node.split(".")[0]);
@@ -7750,8 +8073,8 @@ function checkClaudeOnPath() {
7750
8073
  };
7751
8074
  }
7752
8075
  function checkMcpRegistered() {
7753
- const claudeConfig = join5(homedir5(), ".claude.json");
7754
- if (!existsSync7(claudeConfig)) {
8076
+ const claudeConfig = join7(homedir7(), ".claude.json");
8077
+ if (!existsSync8(claudeConfig)) {
7755
8078
  return {
7756
8079
  name: "claudemesh MCP registered in ~/.claude.json",
7757
8080
  pass: false,
@@ -7776,8 +8099,8 @@ function checkMcpRegistered() {
7776
8099
  }
7777
8100
  }
7778
8101
  function checkHooksRegistered() {
7779
- const settings = join5(homedir5(), ".claude", "settings.json");
7780
- if (!existsSync7(settings)) {
8102
+ const settings = join7(homedir7(), ".claude", "settings.json");
8103
+ if (!existsSync8(settings)) {
7781
8104
  return {
7782
8105
  name: "Status hooks registered in ~/.claude/settings.json",
7783
8106
  pass: false,
@@ -7802,7 +8125,7 @@ function checkHooksRegistered() {
7802
8125
  }
7803
8126
  function checkConfigFile() {
7804
8127
  const path = getConfigPath();
7805
- if (!existsSync7(path)) {
8128
+ if (!existsSync8(path)) {
7806
8129
  return {
7807
8130
  name: "~/.claudemesh/config.json exists and parses",
7808
8131
  pass: true,
@@ -7985,7 +8308,7 @@ var exports_status = {};
7985
8308
  __export(exports_status, {
7986
8309
  runStatus: () => runStatus
7987
8310
  });
7988
- import { statSync as statSync3, existsSync as existsSync8 } from "node:fs";
8311
+ import { statSync as statSync3, existsSync as existsSync9 } from "node:fs";
7989
8312
  import WebSocket2 from "ws";
7990
8313
  async function probeBroker(url, timeoutMs = 4000) {
7991
8314
  return new Promise((resolve2) => {
@@ -8015,7 +8338,7 @@ async function runStatus() {
8015
8338
  render.section(`status (v${VERSION})`);
8016
8339
  const configPath = getConfigPath();
8017
8340
  let configPermsNote = "missing";
8018
- if (existsSync8(configPath)) {
8341
+ if (existsSync9(configPath)) {
8019
8342
  const mode = (statSync3(configPath).mode & 511).toString(8).padStart(4, "0");
8020
8343
  configPermsNote = mode === "0600" ? `${mode}` : `${mode} — expected 0600`;
8021
8344
  }
@@ -8161,10 +8484,10 @@ var init_check_claude_binary = __esm(() => {
8161
8484
  });
8162
8485
 
8163
8486
  // src/services/health/check-mcp-registered.ts
8164
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
8487
+ import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
8165
8488
  function checkMcpRegistered2() {
8166
8489
  try {
8167
- if (!existsSync9(PATHS.CLAUDE_JSON)) {
8490
+ if (!existsSync10(PATHS.CLAUDE_JSON)) {
8168
8491
  return { name: "mcp-registered", ok: false, message: "~/.claude.json not found" };
8169
8492
  }
8170
8493
  const raw = readFileSync7(PATHS.CLAUDE_JSON, "utf-8");
@@ -8182,10 +8505,10 @@ var init_check_mcp_registered = __esm(() => {
8182
8505
  });
8183
8506
 
8184
8507
  // src/services/health/check-hooks-registered.ts
8185
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
8508
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
8186
8509
  function checkHooksRegistered2() {
8187
8510
  try {
8188
- if (!existsSync10(PATHS.CLAUDE_SETTINGS)) {
8511
+ if (!existsSync11(PATHS.CLAUDE_SETTINGS)) {
8189
8512
  return { name: "hooks-registered", ok: false, message: "~/.claude/settings.json not found" };
8190
8513
  }
8191
8514
  const raw = readFileSync8(PATHS.CLAUDE_SETTINGS, "utf-8");
@@ -8203,10 +8526,10 @@ var init_check_hooks_registered = __esm(() => {
8203
8526
  });
8204
8527
 
8205
8528
  // src/services/health/check-config-perms.ts
8206
- import { existsSync as existsSync11, statSync as statSync4 } from "node:fs";
8529
+ import { existsSync as existsSync12, statSync as statSync4 } from "node:fs";
8207
8530
  function checkConfigPerms() {
8208
8531
  const configFile = PATHS.CONFIG_FILE;
8209
- if (!existsSync11(configFile)) {
8532
+ if (!existsSync12(configFile)) {
8210
8533
  return { name: "config-perms", ok: true, message: "No config file yet (first run)" };
8211
8534
  }
8212
8535
  try {
@@ -8224,9 +8547,9 @@ var init_check_config_perms = __esm(() => {
8224
8547
  });
8225
8548
 
8226
8549
  // src/services/health/check-keypairs-valid.ts
8227
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
8550
+ import { existsSync as existsSync13, readFileSync as readFileSync9 } from "node:fs";
8228
8551
  function checkKeypairsValid() {
8229
- if (!existsSync12(PATHS.CONFIG_FILE)) {
8552
+ if (!existsSync13(PATHS.CONFIG_FILE)) {
8230
8553
  return { name: "keypairs-valid", ok: true, message: "No config (first run)" };
8231
8554
  }
8232
8555
  try {
@@ -8710,18 +9033,18 @@ var exports_url_handler = {};
8710
9033
  __export(exports_url_handler, {
8711
9034
  runUrlHandler: () => runUrlHandler
8712
9035
  });
8713
- import { platform as platform7, homedir as homedir6 } from "node:os";
8714
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, rmSync as rmSync2, chmodSync as chmodSync4 } from "node:fs";
8715
- import { join as join6 } from "node:path";
9036
+ import { platform as platform7, homedir as homedir8 } from "node:os";
9037
+ import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, rmSync as rmSync3, chmodSync as chmodSync4 } from "node:fs";
9038
+ import { join as join8 } from "node:path";
8716
9039
  import { spawnSync as spawnSync5 } from "node:child_process";
8717
9040
  function resolveClaudemeshBin() {
8718
9041
  return process.argv[1] ?? "claudemesh";
8719
9042
  }
8720
9043
  function installDarwin() {
8721
9044
  const binPath = resolveClaudemeshBin();
8722
- const appDir = join6(homedir6(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
8723
- const contents = join6(appDir, "Contents");
8724
- const macOS = join6(contents, "MacOS");
9045
+ const appDir = join8(homedir8(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
9046
+ const contents = join8(appDir, "Contents");
9047
+ const macOS = join8(contents, "MacOS");
8725
9048
  mkdirSync4(macOS, { recursive: true });
8726
9049
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
8727
9050
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -8745,7 +9068,7 @@ function installDarwin() {
8745
9068
  </array>
8746
9069
  </dict>
8747
9070
  </plist>`;
8748
- writeFileSync7(join6(contents, "Info.plist"), plist);
9071
+ writeFileSync7(join8(contents, "Info.plist"), plist);
8749
9072
  const shim = `#!/bin/sh
8750
9073
  URL="$1"
8751
9074
  CODE=\${URL#claudemesh://}
@@ -8759,7 +9082,7 @@ tell application "Terminal"
8759
9082
  end tell
8760
9083
  EOF
8761
9084
  `;
8762
- const shimPath = join6(macOS, "open-url");
9085
+ const shimPath = join8(macOS, "open-url");
8763
9086
  writeFileSync7(shimPath, shim);
8764
9087
  chmodSync4(shimPath, 493);
8765
9088
  const lsreg = spawnSync5("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", ["-f", appDir], { encoding: "utf-8" });
@@ -8771,7 +9094,7 @@ EOF
8771
9094
  }
8772
9095
  function installLinux() {
8773
9096
  const binPath = resolveClaudemeshBin();
8774
- const appsDir = join6(homedir6(), ".local", "share", "applications");
9097
+ const appsDir = join8(homedir8(), ".local", "share", "applications");
8775
9098
  mkdirSync4(appsDir, { recursive: true });
8776
9099
  const desktop = `[Desktop Entry]
8777
9100
  Type=Application
@@ -8783,7 +9106,7 @@ Terminal=true
8783
9106
  MimeType=x-scheme-handler/claudemesh;
8784
9107
  NoDisplay=true
8785
9108
  `;
8786
- const desktopPath = join6(appsDir, "claudemesh.desktop");
9109
+ const desktopPath = join8(appsDir, "claudemesh.desktop");
8787
9110
  writeFileSync7(desktopPath, desktop);
8788
9111
  const xdg1 = spawnSync5("xdg-mime", ["default", "claudemesh.desktop", "x-scheme-handler/claudemesh"], { encoding: "utf-8" });
8789
9112
  if (xdg1.status !== 0) {
@@ -8806,7 +9129,7 @@ function installWindows() {
8806
9129
  `[HKEY_CURRENT_USER\\Software\\Classes\\claudemesh\\shell\\open\\command]`,
8807
9130
  `@="\\"${binPath.replace(/\\/g, "\\\\")}\\" \\"%1\\""`
8808
9131
  ];
8809
- const regPath = join6(homedir6(), "claudemesh-handler.reg");
9132
+ const regPath = join8(homedir8(), "claudemesh-handler.reg");
8810
9133
  writeFileSync7(regPath, lines.join(`\r
8811
9134
  `));
8812
9135
  const res = spawnSync5("reg.exe", ["import", regPath], { encoding: "utf-8" });
@@ -8818,16 +9141,16 @@ function installWindows() {
8818
9141
  return EXIT.SUCCESS;
8819
9142
  }
8820
9143
  function uninstallDarwin() {
8821
- const appDir = join6(homedir6(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
8822
- if (existsSync13(appDir))
8823
- rmSync2(appDir, { recursive: true, force: true });
9144
+ const appDir = join8(homedir8(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
9145
+ if (existsSync14(appDir))
9146
+ rmSync3(appDir, { recursive: true, force: true });
8824
9147
  render.ok("removed claudemesh:// handler on macOS");
8825
9148
  return EXIT.SUCCESS;
8826
9149
  }
8827
9150
  function uninstallLinux() {
8828
- const desktopPath = join6(homedir6(), ".local", "share", "applications", "claudemesh.desktop");
8829
- if (existsSync13(desktopPath))
8830
- rmSync2(desktopPath, { force: true });
9151
+ const desktopPath = join8(homedir8(), ".local", "share", "applications", "claudemesh.desktop");
9152
+ if (existsSync14(desktopPath))
9153
+ rmSync3(desktopPath, { force: true });
8831
9154
  render.ok("removed claudemesh:// handler on Linux");
8832
9155
  return EXIT.SUCCESS;
8833
9156
  }
@@ -8871,9 +9194,9 @@ var exports_status_line = {};
8871
9194
  __export(exports_status_line, {
8872
9195
  runStatusLine: () => runStatusLine
8873
9196
  });
8874
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
8875
- import { join as join7 } from "node:path";
8876
- import { homedir as homedir7 } from "node:os";
9197
+ import { existsSync as existsSync15, readFileSync as readFileSync10 } from "node:fs";
9198
+ import { join as join9 } from "node:path";
9199
+ import { homedir as homedir9 } from "node:os";
8877
9200
  async function runStatusLine() {
8878
9201
  try {
8879
9202
  const config = readConfig();
@@ -8881,9 +9204,9 @@ async function runStatusLine() {
8881
9204
  process.stdout.write("◇ claudemesh (not joined)");
8882
9205
  return EXIT.SUCCESS;
8883
9206
  }
8884
- const cachePath = join7(homedir7(), ".claudemesh", "peer-cache.json");
9207
+ const cachePath = join9(homedir9(), ".claudemesh", "peer-cache.json");
8885
9208
  let cache = {};
8886
- if (existsSync14(cachePath)) {
9209
+ if (existsSync15(cachePath)) {
8887
9210
  try {
8888
9211
  cache = JSON.parse(readFileSync10(cachePath, "utf-8"));
8889
9212
  } catch {}
@@ -8916,7 +9239,7 @@ __export(exports_backup, {
8916
9239
  runRestore: () => runRestore,
8917
9240
  runBackup: () => runBackup
8918
9241
  });
8919
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync15 } from "node:fs";
9242
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync16 } from "node:fs";
8920
9243
  import { createInterface as createInterface10 } from "node:readline";
8921
9244
  function readHidden(prompt5) {
8922
9245
  return new Promise((resolve2) => {
@@ -8958,7 +9281,7 @@ async function deriveKey(pass, salt, s) {
8958
9281
  }
8959
9282
  async function runBackup(outPath) {
8960
9283
  const configPath = getConfigPath();
8961
- if (!existsSync15(configPath)) {
9284
+ if (!existsSync16(configPath)) {
8962
9285
  console.error(" No config found — nothing to back up. Join a mesh first.");
8963
9286
  return EXIT.NOT_FOUND;
8964
9287
  }
@@ -8992,7 +9315,7 @@ async function runRestore(inPath) {
8992
9315
  console.error(" Usage: claudemesh restore <backup-file>");
8993
9316
  return EXIT.INVALID_ARGS;
8994
9317
  }
8995
- if (!existsSync15(inPath)) {
9318
+ if (!existsSync16(inPath)) {
8996
9319
  console.error(` ✗ File not found: ${inPath}`);
8997
9320
  return EXIT.NOT_FOUND;
8998
9321
  }
@@ -9015,7 +9338,7 @@ async function runRestore(inPath) {
9015
9338
  return EXIT.INTERNAL_ERROR;
9016
9339
  }
9017
9340
  const configPath = getConfigPath();
9018
- if (existsSync15(configPath)) {
9341
+ if (existsSync16(configPath)) {
9019
9342
  const backupOld = `${configPath}.before-restore.${Date.now()}`;
9020
9343
  writeFileSync8(backupOld, readFileSync11(configPath), { mode: 384 });
9021
9344
  console.log(` ↻ Existing config saved to ${backupOld}`);
@@ -9040,8 +9363,8 @@ __export(exports_upgrade, {
9040
9363
  runUpgrade: () => runUpgrade
9041
9364
  });
9042
9365
  import { spawnSync as spawnSync6 } from "node:child_process";
9043
- import { existsSync as existsSync16 } from "node:fs";
9044
- import { dirname as dirname3, join as join8, resolve as resolve2 } from "node:path";
9366
+ import { existsSync as existsSync17 } from "node:fs";
9367
+ import { dirname as dirname4, join as join10, resolve as resolve2 } from "node:path";
9045
9368
  async function latestVersion() {
9046
9369
  try {
9047
9370
  const res = await fetch(URLS.NPM_REGISTRY, { signal: AbortSignal.timeout(8000) });
@@ -9054,15 +9377,15 @@ async function latestVersion() {
9054
9377
  }
9055
9378
  }
9056
9379
  function findNpm() {
9057
- const portable = join8(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
9058
- if (existsSync16(portable)) {
9059
- return { npm: portable, prefix: join8(process.env.HOME ?? "", ".claudemesh") };
9380
+ const portable = join10(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
9381
+ if (existsSync17(portable)) {
9382
+ return { npm: portable, prefix: join10(process.env.HOME ?? "", ".claudemesh") };
9060
9383
  }
9061
9384
  let cur = resolve2(process.argv[1] ?? ".");
9062
9385
  for (let i = 0;i < 6; i++) {
9063
- cur = dirname3(cur);
9064
- const candidate = join8(cur, "bin", "npm");
9065
- if (existsSync16(candidate))
9386
+ cur = dirname4(cur);
9387
+ const candidate = join10(cur, "bin", "npm");
9388
+ if (existsSync17(candidate))
9066
9389
  return { npm: candidate };
9067
9390
  }
9068
9391
  return { npm: "npm" };
@@ -9124,9 +9447,9 @@ __export(exports_grants, {
9124
9447
  runBlock: () => runBlock,
9125
9448
  isAllowed: () => isAllowed
9126
9449
  });
9127
- import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
9128
- import { homedir as homedir8 } from "node:os";
9129
- import { join as join9 } from "node:path";
9450
+ import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
9451
+ import { homedir as homedir10 } from "node:os";
9452
+ import { join as join11 } from "node:path";
9130
9453
  async function syncToBroker(meshSlug, grants) {
9131
9454
  const auth = getStoredToken();
9132
9455
  if (!auth)
@@ -9144,7 +9467,7 @@ async function syncToBroker(meshSlug, grants) {
9144
9467
  }
9145
9468
  }
9146
9469
  function readGrants() {
9147
- if (!existsSync17(GRANT_FILE))
9470
+ if (!existsSync18(GRANT_FILE))
9148
9471
  return {};
9149
9472
  try {
9150
9473
  return JSON.parse(readFileSync12(GRANT_FILE, "utf-8"));
@@ -9153,8 +9476,8 @@ function readGrants() {
9153
9476
  }
9154
9477
  }
9155
9478
  function writeGrants(g) {
9156
- const dir = join9(homedir8(), ".claudemesh");
9157
- if (!existsSync17(dir))
9479
+ const dir = join11(homedir10(), ".claudemesh");
9480
+ if (!existsSync18(dir))
9158
9481
  mkdirSync5(dir, { recursive: true });
9159
9482
  writeFileSync9(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
9160
9483
  }
@@ -9312,7 +9635,7 @@ var init_grants = __esm(() => {
9312
9635
  BROKER_HTTP7 = URLS.BROKER.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
9313
9636
  ALL_CAPS = ["read", "dm", "broadcast", "state-read", "state-write", "file-read"];
9314
9637
  DEFAULT_CAPS = ["read", "dm", "broadcast", "state-read"];
9315
- GRANT_FILE = join9(homedir8(), ".claudemesh", "grants.json");
9638
+ GRANT_FILE = join11(homedir10(), ".claudemesh", "grants.json");
9316
9639
  });
9317
9640
 
9318
9641
  // src/mcp/tools/definitions.ts
@@ -10238,6 +10561,165 @@ var init_definitions = __esm(() => {
10238
10561
  ];
10239
10562
  });
10240
10563
 
10564
+ // src/services/bridge/server.ts
10565
+ import { createServer as createServer2 } from "node:net";
10566
+ import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, existsSync as existsSync19, chmodSync as chmodSync5 } from "node:fs";
10567
+ async function resolveTarget2(client, to) {
10568
+ if (to.startsWith("@") || to === "*" || /^[0-9a-f]{64}$/i.test(to)) {
10569
+ return { ok: true, spec: to };
10570
+ }
10571
+ const peers = await client.listPeers();
10572
+ const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
10573
+ if (!match) {
10574
+ return {
10575
+ ok: false,
10576
+ error: `peer "${to}" not found. online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`
10577
+ };
10578
+ }
10579
+ return { ok: true, spec: match.pubkey };
10580
+ }
10581
+ async function dispatch(client, req) {
10582
+ const args = req.args ?? {};
10583
+ try {
10584
+ switch (req.verb) {
10585
+ case "ping": {
10586
+ const peers = await client.listPeers();
10587
+ return {
10588
+ id: req.id,
10589
+ ok: true,
10590
+ result: {
10591
+ mesh: client.meshSlug,
10592
+ ws_status: client.status,
10593
+ peers_online: peers.length,
10594
+ push_buffer: client.pushHistory.length
10595
+ }
10596
+ };
10597
+ }
10598
+ case "peers": {
10599
+ const peers = await client.listPeers();
10600
+ return { id: req.id, ok: true, result: peers };
10601
+ }
10602
+ case "send": {
10603
+ const to = String(args.to ?? "");
10604
+ const message = String(args.message ?? "");
10605
+ const priority = args.priority ?? "next";
10606
+ if (!to || !message) {
10607
+ return { id: req.id, ok: false, error: "send: `to` and `message` required" };
10608
+ }
10609
+ const resolved = await resolveTarget2(client, to);
10610
+ if (!resolved.ok)
10611
+ return { id: req.id, ok: false, error: resolved.error };
10612
+ const result = await client.send(resolved.spec, message, priority);
10613
+ if (!result.ok) {
10614
+ return { id: req.id, ok: false, error: result.error ?? "send failed" };
10615
+ }
10616
+ return {
10617
+ id: req.id,
10618
+ ok: true,
10619
+ result: { messageId: result.messageId, target: resolved.spec }
10620
+ };
10621
+ }
10622
+ case "summary": {
10623
+ const text = String(args.summary ?? "");
10624
+ if (!text)
10625
+ return { id: req.id, ok: false, error: "summary: `summary` required" };
10626
+ await client.setSummary(text);
10627
+ return { id: req.id, ok: true, result: { summary: text } };
10628
+ }
10629
+ case "status_set": {
10630
+ const state = String(args.status ?? "");
10631
+ if (!["idle", "working", "dnd"].includes(state)) {
10632
+ return { id: req.id, ok: false, error: "status_set: must be idle | working | dnd" };
10633
+ }
10634
+ await client.setStatus(state);
10635
+ return { id: req.id, ok: true, result: { status: state } };
10636
+ }
10637
+ case "visible": {
10638
+ const visible = Boolean(args.visible);
10639
+ await client.setVisible(visible);
10640
+ return { id: req.id, ok: true, result: { visible } };
10641
+ }
10642
+ default:
10643
+ return { id: req.id, ok: false, error: `unknown verb: ${req.verb}` };
10644
+ }
10645
+ } catch (err) {
10646
+ return {
10647
+ id: req.id,
10648
+ ok: false,
10649
+ error: err instanceof Error ? err.message : String(err)
10650
+ };
10651
+ }
10652
+ }
10653
+ function handleConnection(socket, client) {
10654
+ const parser = new LineParser;
10655
+ socket.on("data", (chunk) => {
10656
+ const lines = parser.feed(chunk);
10657
+ for (const line of lines) {
10658
+ if (!line.trim())
10659
+ continue;
10660
+ let req;
10661
+ try {
10662
+ req = JSON.parse(line);
10663
+ } catch {
10664
+ continue;
10665
+ }
10666
+ if (!req || typeof req !== "object" || !req.id || !req.verb)
10667
+ continue;
10668
+ dispatch(client, req).then((res) => {
10669
+ try {
10670
+ socket.write(frame(res));
10671
+ } catch {}
10672
+ });
10673
+ }
10674
+ });
10675
+ socket.on("error", () => {});
10676
+ }
10677
+ function startBridgeServer(client) {
10678
+ const path = socketPath(client.meshSlug);
10679
+ const dir = socketDir();
10680
+ if (!existsSync19(dir)) {
10681
+ mkdirSync6(dir, { recursive: true, mode: 448 });
10682
+ }
10683
+ if (existsSync19(path)) {
10684
+ try {
10685
+ unlinkSync2(path);
10686
+ } catch {}
10687
+ }
10688
+ const server = createServer2((socket) => handleConnection(socket, client));
10689
+ try {
10690
+ server.listen(path);
10691
+ } catch (err) {
10692
+ process.stderr.write(`[claudemesh] bridge: failed to bind ${path}: ${String(err)}
10693
+ `);
10694
+ return null;
10695
+ }
10696
+ server.on("error", (err) => {
10697
+ process.stderr.write(`[claudemesh] bridge: ${String(err)}
10698
+ `);
10699
+ });
10700
+ try {
10701
+ chmodSync5(path, 384);
10702
+ } catch {}
10703
+ let stopped = false;
10704
+ return {
10705
+ path,
10706
+ stop() {
10707
+ if (stopped)
10708
+ return;
10709
+ stopped = true;
10710
+ try {
10711
+ server.close();
10712
+ } catch {}
10713
+ try {
10714
+ unlinkSync2(path);
10715
+ } catch {}
10716
+ }
10717
+ };
10718
+ }
10719
+ var init_server = __esm(() => {
10720
+ init_protocol();
10721
+ });
10722
+
10241
10723
  // src/mcp/server.ts
10242
10724
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
10243
10725
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -10803,12 +11285,12 @@ ${peerLines.join(`
10803
11285
  }
10804
11286
  }
10805
11287
  try {
10806
- const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync6, existsSync: existsSync18 } = await import("node:fs");
11288
+ const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync7, existsSync: existsSync20 } = await import("node:fs");
10807
11289
  const { join: joinPath } = await import("node:path");
10808
- const { homedir: homedir9 } = await import("node:os");
10809
- const dir = joinPath(homedir9(), ".claudemesh");
10810
- if (!existsSync18(dir))
10811
- mkdirSync6(dir, { recursive: true });
11290
+ const { homedir: homedir11 } = await import("node:os");
11291
+ const dir = joinPath(homedir11(), ".claudemesh");
11292
+ if (!existsSync20(dir))
11293
+ mkdirSync7(dir, { recursive: true });
10812
11294
  writeFileSync10(joinPath(dir, "peer-cache.json"), JSON.stringify(statusCache));
10813
11295
  } catch {}
10814
11296
  return text(sections.join(`
@@ -11051,17 +11533,17 @@ ${lines.join(`
11051
11533
  const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
11052
11534
  if (!filePath)
11053
11535
  return text("share_file: `path` required", true);
11054
- const { existsSync: existsSync18 } = await import("node:fs");
11055
- if (!existsSync18(filePath))
11536
+ const { existsSync: existsSync20 } = await import("node:fs");
11537
+ if (!existsSync20(filePath))
11056
11538
  return text(`share_file: file not found: ${filePath}`, true);
11057
11539
  const client = allClients()[0];
11058
11540
  if (!client)
11059
11541
  return text("share_file: not connected", true);
11060
11542
  if (fileTo) {
11061
11543
  const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
11062
- const { readFileSync: readFileSync13, writeFileSync: writeFileSync10, mkdtempSync: mkdtempSync2, unlinkSync: unlinkSync2, rmdirSync } = await import("node:fs");
11544
+ const { readFileSync: readFileSync13, writeFileSync: writeFileSync10, mkdtempSync: mkdtempSync2, unlinkSync: unlinkSync3, rmdirSync } = await import("node:fs");
11063
11545
  const { tmpdir: tmpdir2 } = await import("node:os");
11064
- const { join: join10, basename } = await import("node:path");
11546
+ const { join: join12, basename } = await import("node:path");
11065
11547
  const peers = await client.listPeers();
11066
11548
  const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
11067
11549
  if (!targetPeer) {
@@ -11084,8 +11566,8 @@ ${lines.join(`
11084
11566
  combined.set(ciphertext, nonceBytes.length);
11085
11567
  const rawName = fileName ?? basename(filePath);
11086
11568
  const baseName = basename(rawName).replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 255);
11087
- const tmpDir = mkdtempSync2(join10(tmpdir2(), "cm-"));
11088
- const tmpPath = join10(tmpDir, baseName);
11569
+ const tmpDir = mkdtempSync2(join12(tmpdir2(), "cm-"));
11570
+ const tmpPath = join12(tmpDir, baseName);
11089
11571
  writeFileSync10(tmpPath, combined);
11090
11572
  try {
11091
11573
  const fileId = await client.uploadFile(tmpPath, client.meshId, client.meshSlug, {
@@ -11101,7 +11583,7 @@ ${lines.join(`
11101
11583
  return text(`share_file: upload failed — ${e instanceof Error ? e.message : String(e)}`, true);
11102
11584
  } finally {
11103
11585
  try {
11104
- unlinkSync2(tmpPath);
11586
+ unlinkSync3(tmpPath);
11105
11587
  } catch {}
11106
11588
  try {
11107
11589
  rmdirSync(tmpDir);
@@ -11161,9 +11643,9 @@ ${lines.join(`
11161
11643
  const plaintext = await decryptFile2(ciphertext, nonce, kf);
11162
11644
  if (!plaintext)
11163
11645
  return text(genericErr, true);
11164
- const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync7 } = await import("node:fs");
11165
- const { dirname: dirname5 } = await import("node:path");
11166
- mkdirSync7(dirname5(save_to), { recursive: true });
11646
+ const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync8 } = await import("node:fs");
11647
+ const { dirname: dirname6 } = await import("node:path");
11648
+ mkdirSync8(dirname6(save_to), { recursive: true });
11167
11649
  writeFileSync11(save_to, plaintext);
11168
11650
  return text(`Downloaded and decrypted: ${result.name} → ${save_to}`);
11169
11651
  }
@@ -11174,9 +11656,9 @@ ${lines.join(`
11174
11656
  }
11175
11657
  if (!res.ok)
11176
11658
  return text(`get_file: download failed (${res.status})`, true);
11177
- const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync6 } = await import("node:fs");
11178
- const { dirname: dirname4 } = await import("node:path");
11179
- mkdirSync6(dirname4(save_to), { recursive: true });
11659
+ const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync7 } = await import("node:fs");
11660
+ const { dirname: dirname5 } = await import("node:path");
11661
+ mkdirSync7(dirname5(save_to), { recursive: true });
11180
11662
  writeFileSync10(save_to, Buffer.from(await res.arrayBuffer()));
11181
11663
  return text(`Downloaded: ${result.name} → ${save_to}`);
11182
11664
  }
@@ -11935,8 +12417,8 @@ ${lines.join(`
11935
12417
  const entryType = vType ?? "env";
11936
12418
  let plaintextBytes;
11937
12419
  if (entryType === "file") {
11938
- const { existsSync: existsSync18, readFileSync: readFileSync13 } = await import("node:fs");
11939
- if (!existsSync18(value))
12420
+ const { existsSync: existsSync20, readFileSync: readFileSync13 } = await import("node:fs");
12421
+ if (!existsSync20(value))
11940
12422
  return text(`vault_set: file not found: ${value}`, true);
11941
12423
  plaintextBytes = new Uint8Array(readFileSync13(value));
11942
12424
  } else {
@@ -12195,8 +12677,14 @@ ${lines.join(`
12195
12677
  });
12196
12678
  const transport = new StdioServerTransport;
12197
12679
  await server.connect(transport);
12680
+ const bridges = [];
12198
12681
  startClients(config).then(() => {
12199
12682
  wirePushHandlers().catch(() => {});
12683
+ for (const client of allClients()) {
12684
+ const bridge = startBridgeServer(client);
12685
+ if (bridge)
12686
+ bridges.push(bridge);
12687
+ }
12200
12688
  }).catch(() => {
12201
12689
  wirePushHandlers().catch(() => {});
12202
12690
  });
@@ -12369,6 +12857,11 @@ ${lines.join(`
12369
12857
  const keepalive = setInterval(() => {}, 1000);
12370
12858
  const shutdown = () => {
12371
12859
  clearInterval(keepalive);
12860
+ for (const b of bridges) {
12861
+ try {
12862
+ b.stop();
12863
+ } catch {}
12864
+ }
12372
12865
  stopAll();
12373
12866
  process.exit(0);
12374
12867
  };
@@ -12490,10 +12983,11 @@ async function startServiceProxy(serviceName) {
12490
12983
  process.on("SIGINT", shutdown);
12491
12984
  }
12492
12985
  var peerNameCache, peerNameCacheAge = 0, CACHE_TTL_MS = 30000, DEPRECATED_TOOL_HINTS, warnedTools;
12493
- var init_server = __esm(() => {
12986
+ var init_server2 = __esm(() => {
12494
12987
  init_definitions();
12495
12988
  init_facade();
12496
12989
  init_facade8();
12990
+ init_server();
12497
12991
  peerNameCache = new Map;
12498
12992
  DEPRECATED_TOOL_HINTS = {
12499
12993
  send_message: "claudemesh send <to> <msg> [--mesh X] [--priority Y]",
@@ -12537,7 +13031,7 @@ async function runMcp() {
12537
13031
  process.exit(0);
12538
13032
  }
12539
13033
  var init_mcp = __esm(() => {
12540
- init_server();
13034
+ init_server2();
12541
13035
  });
12542
13036
 
12543
13037
  // src/commands/hook.ts
@@ -12993,12 +13487,12 @@ async function main() {
12993
13487
  }
12994
13488
  case "peers": {
12995
13489
  const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
12996
- await runPeers2({ mesh: flags.mesh, json: !!flags.json });
13490
+ await runPeers2({ mesh: flags.mesh, json: flags.json });
12997
13491
  break;
12998
13492
  }
12999
13493
  case "send": {
13000
13494
  const { runSend: runSend2 } = await Promise.resolve().then(() => (init_send(), exports_send));
13001
- await runSend2({}, positionals[0] ?? "", positionals.slice(1).join(" "));
13495
+ await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json }, positionals[0] ?? "", positionals.slice(1).join(" "));
13002
13496
  break;
13003
13497
  }
13004
13498
  case "inbox": {
@@ -13246,4 +13740,4 @@ main().catch((err) => {
13246
13740
  process.exit(EXIT.INTERNAL_ERROR);
13247
13741
  });
13248
13742
 
13249
- //# debugId=0A4A6F72C9E014B864756E2164756E21
13743
+ //# debugId=E126BD4968D2FCDF64756E2164756E21