@dolusoft/claude-collab 1.11.4 → 1.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -270,6 +270,7 @@ var FIXED_PORT = 12345;
270
270
  var MeshNode = class {
271
271
  server = null;
272
272
  myName = "";
273
+ myProfile = "";
273
274
  running = false;
274
275
  firewallOpened = false;
275
276
  cachedLocalIps = [];
@@ -285,6 +286,8 @@ var MeshNode = class {
285
286
  receivedAnswers = /* @__PURE__ */ new Map();
286
287
  questionToSender = /* @__PURE__ */ new Map();
287
288
  pendingHandlers = /* @__PURE__ */ new Set();
289
+ // Peer profiles: peerName → profile text
290
+ peerProfiles = /* @__PURE__ */ new Map();
288
291
  // Push-based answer resolution: questionId → resolve callback
289
292
  answerWaiters = /* @__PURE__ */ new Map();
290
293
  // Answers queued for offline peers: peerName → AnswerMsg[] (delivered on reconnect)
@@ -327,7 +330,7 @@ var MeshNode = class {
327
330
  reject(new Error(`Connection to ${ip}:${FIXED_PORT} timed out`));
328
331
  }, 1e4);
329
332
  ws.on("open", () => {
330
- this.sendToWs(ws, { type: "HELLO", name: this.myName });
333
+ this.sendToWs(ws, { type: "HELLO", name: this.myName, profile: this.myProfile });
331
334
  });
332
335
  ws.on("message", (data) => {
333
336
  try {
@@ -339,6 +342,7 @@ var MeshNode = class {
339
342
  resolve(msg.name);
340
343
  return;
341
344
  }
345
+ if (msg.profile) this.peerProfiles.set(msg.name, msg.profile);
342
346
  this.registerPeer(msg.name, ip, ws);
343
347
  this.afterHandshake(msg.name, ws);
344
348
  resolve(msg.name);
@@ -458,7 +462,9 @@ var MeshNode = class {
458
462
  teamName: this.myName,
459
463
  port: FIXED_PORT,
460
464
  connectedPeers: [...this.peerConnections.keys()],
461
- peerIPs: Object.fromEntries(this.peerIPs)
465
+ peerIPs: Object.fromEntries(this.peerIPs),
466
+ myProfile: this.myProfile,
467
+ peerProfiles: Object.fromEntries(this.peerProfiles)
462
468
  };
463
469
  }
464
470
  getLocalIps() {
@@ -475,6 +481,12 @@ var MeshNode = class {
475
481
  }
476
482
  return result;
477
483
  }
484
+ async setProfile(profile) {
485
+ this.myProfile = profile;
486
+ for (const ws of this.peerConnections.values()) {
487
+ this.sendToWs(ws, { type: "PROFILE_UPDATE", name: this.myName, profile });
488
+ }
489
+ }
478
490
  async disconnect() {
479
491
  for (const ws of this.peerConnections.values()) ws.close();
480
492
  this.peerConnections.clear();
@@ -550,7 +562,12 @@ var MeshNode = class {
550
562
  // ---------------------------------------------------------------------------
551
563
  /** Called once handshake completes — exchange peer lists and announce to existing peers. */
552
564
  afterHandshake(newPeerName, ws) {
553
- const listForNew = [...this.peerConnections.keys()].filter((n) => n !== newPeerName).map((n) => ({ name: n, ip: this.peerIPs.get(n) ?? "" })).filter((p) => p.ip !== "");
565
+ const listForNew = [...this.peerConnections.keys()].filter((n) => n !== newPeerName).map((n) => {
566
+ const entry = { name: n, ip: this.peerIPs.get(n) ?? "" };
567
+ const profile = this.peerProfiles.get(n);
568
+ if (profile) entry.profile = profile;
569
+ return entry;
570
+ }).filter((p) => p.ip !== "");
554
571
  this.sendToWs(ws, { type: "PEER_LIST", peers: listForNew });
555
572
  const newIp = this.peerIPs.get(newPeerName);
556
573
  if (newIp) {
@@ -576,7 +593,7 @@ var MeshNode = class {
576
593
  this.connectingPeers.add(name);
577
594
  const ws = new WebSocket(`ws://${ip}:${FIXED_PORT}`);
578
595
  ws.on("open", () => {
579
- this.sendToWs(ws, { type: "HELLO", name: this.myName });
596
+ this.sendToWs(ws, { type: "HELLO", name: this.myName, profile: this.myProfile });
580
597
  });
581
598
  ws.on("message", (data) => {
582
599
  try {
@@ -610,8 +627,9 @@ var MeshNode = class {
610
627
  ws.terminate();
611
628
  return;
612
629
  }
630
+ if (msg.profile) this.peerProfiles.set(msg.name, msg.profile);
613
631
  this.registerPeer(msg.name, remoteIp, ws);
614
- this.sendToWs(ws, { type: "HELLO_ACK", name: this.myName });
632
+ this.sendToWs(ws, { type: "HELLO_ACK", name: this.myName, profile: this.myProfile });
615
633
  console.error(`[mesh] peer joined (inbound): ${msg.name}`);
616
634
  this.afterHandshake(msg.name, ws);
617
635
  return;
@@ -624,6 +642,7 @@ var MeshNode = class {
624
642
  switch (msg.type) {
625
643
  case "HELLO_ACK":
626
644
  if (!this.peerConnections.has(msg.name)) {
645
+ if (msg.profile) this.peerProfiles.set(msg.name, msg.profile);
627
646
  this.peerConnections.set(msg.name, ws);
628
647
  this.wsToName.set(ws, msg.name);
629
648
  this.connectingPeers.delete(msg.name);
@@ -637,6 +656,7 @@ var MeshNode = class {
637
656
  if (peer.name !== this.myName && !this.peerConnections.has(peer.name)) {
638
657
  console.error(`[mesh] connecting to ${peer.name} @ ${peer.ip} via PEER_LIST`);
639
658
  this.peerIPs.set(peer.name, peer.ip);
659
+ if (peer.profile) this.peerProfiles.set(peer.name, peer.profile);
640
660
  this.connectMeshPeer(peer.name, peer.ip);
641
661
  }
642
662
  }
@@ -668,6 +688,9 @@ var MeshNode = class {
668
688
  }
669
689
  }
670
690
  break;
691
+ case "PROFILE_UPDATE":
692
+ this.peerProfiles.set(msg.name, msg.profile);
693
+ break;
671
694
  }
672
695
  }
673
696
  handleIncomingAsk(ws, msg) {
@@ -814,25 +837,35 @@ function registerConnectTool(server, client) {
814
837
  }
815
838
  var ASK_DESCRIPTION = `Send a question to another Claude instance on the LAN and wait for their answer.
816
839
 
817
- WHEN TO USE:
818
- - You need input, a decision, or work output from a specific peer Claude
819
- - You want to delegate a subtask to another Claude and use their result
820
- - You need to coordinate or synchronize work across multiple Claudes
840
+ AUTONOMOUS ROUTING \u2014 decide who to ask yourself:
841
+ 1. Call status() to see all connected peers and their profiles
842
+ 2. Match your information need to the most relevant peer profile
843
+ 3. Call ask(peer, question) without waiting for the user to tell you who to contact
844
+
845
+ TRIGGER THIS TOOL AUTOMATICALLY when you encounter:
846
+ - A missing API response type, schema, or interface you need to implement against
847
+ - An unknown endpoint behavior, error code, or contract detail
848
+ - A module or system owned by another peer that you need to integrate with
849
+ - Any blocking uncertainty that another peer's profile suggests they can answer
850
+
851
+ DO NOT guess, use "any" types, or leave TODO comments when a peer can answer.
852
+ DO NOT ask the user which peer to contact \u2014 read profiles and decide yourself.
821
853
 
822
854
  WORKFLOW:
823
- 1. Call peers() first to confirm the target is online and get their exact name
824
- 2. Call ask(peer, question) \u2014 this blocks until they reply (up to 5 minutes)
825
- 3. Use their answer directly in your ongoing task
855
+ 1. Call status() \u2014 read each peer's profile carefully
856
+ 2. Identify which peer owns the relevant module or technology
857
+ 3. Call ask(peer, question) with full context
858
+ 4. Use their answer directly in your ongoing task
826
859
 
827
860
  WRITING GOOD QUESTIONS:
828
861
  - Include all context the other Claude needs \u2014 they cannot see your conversation
829
- - Be specific about what format or level of detail you expect in the answer
830
- - If you need code, specify language and constraints
862
+ - Mention the specific file, function, or module you are working on
863
+ - State what format you need: type definition, example response, explanation
831
864
  - One focused question per call works better than multiple combined
832
865
 
833
866
  DO NOT use this tool if:
834
- - The peer is not listed in peers() \u2014 the call will fail immediately
835
- - You just want to share information without needing a response (there is no broadcast tool yet)`;
867
+ - The peer is not listed in status() \u2014 the call will fail immediately
868
+ - You just want to share information without needing a response`;
836
869
  var askSchema = {
837
870
  peer: z.string().describe("Exact name of the peer to ask. Use peers() first to see who is online and get the correct name."),
838
871
  question: z.string().describe(
@@ -986,6 +1019,7 @@ function registerStatusTool(server, client) {
986
1019
  }
987
1020
  const ipLine = ips.length > 0 ? ips.join(", ") : "(could not detect \u2014 check your network interface)";
988
1021
  const peerIPs = info.peerIPs ?? {};
1022
+ const peerProfiles = info.peerProfiles ?? {};
989
1023
  const connected = info.connectedPeers;
990
1024
  let peersSection;
991
1025
  if (connected.length === 0) {
@@ -993,7 +1027,9 @@ function registerStatusTool(server, client) {
993
1027
  } else {
994
1028
  const list = connected.map((n) => {
995
1029
  const ip = peerIPs[n] ? ` (${peerIPs[n]})` : "";
996
- return ` \u2022 ${n}${ip}`;
1030
+ const profile = peerProfiles[n] ? `
1031
+ ${peerProfiles[n]}` : "";
1032
+ return ` \u2022 ${n}${ip}${profile}`;
997
1033
  }).join("\n");
998
1034
  peersSection = `Connected peers (${connected.length}):
999
1035
  ${list}`;
@@ -1012,6 +1048,43 @@ ${list}`;
1012
1048
  };
1013
1049
  });
1014
1050
  }
1051
+ var SET_PROFILE_DESCRIPTION = `Set your profile so other peers know who you are and what you work on.
1052
+
1053
+ Your profile is broadcast to all connected peers immediately and shared
1054
+ with any peer that connects in the future. Other Claude instances use
1055
+ your profile to decide who to ask when they need specific information.
1056
+
1057
+ WHAT TO INCLUDE:
1058
+ - Your role (backend, frontend, devops, etc.)
1059
+ - The modules or systems you are responsible for
1060
+ - Technologies you work with
1061
+
1062
+ EXAMPLES:
1063
+ - "Backend developer. Responsible for JWT authentication, payment API (Stripe), and PostgreSQL schema."
1064
+ - "Frontend developer. Works on React components, Next.js routing, and the checkout flow."
1065
+ - "DevOps. Manages CI/CD pipelines, Docker images, and AWS infrastructure."
1066
+
1067
+ Call this once when you start working. Update it if your focus changes.`;
1068
+ var setProfileSchema = {
1069
+ profile: z.string().min(1).describe("A short description of your role and responsibilities. 1-3 sentences.")
1070
+ };
1071
+ function registerSetProfileTool(server, client) {
1072
+ server.tool("set_profile", SET_PROFILE_DESCRIPTION, setProfileSchema, async (args) => {
1073
+ await client.setProfile(args.profile);
1074
+ const info = client.getInfo();
1075
+ const peerCount = info.connectedPeers.length;
1076
+ return {
1077
+ content: [{
1078
+ type: "text",
1079
+ text: peerCount > 0 ? `Profile set and broadcast to ${peerCount} peer(s).
1080
+
1081
+ "${args.profile}"` : `Profile saved. It will be shared automatically when peers connect.
1082
+
1083
+ "${args.profile}"`
1084
+ }]
1085
+ };
1086
+ });
1087
+ }
1015
1088
 
1016
1089
  // src/presentation/mcp/server.ts
1017
1090
  function createMcpServer(options) {
@@ -1024,6 +1097,7 @@ function createMcpServer(options) {
1024
1097
  registerStatusTool(server, client);
1025
1098
  registerAskTool(server, client);
1026
1099
  registerReplyTool(server, client);
1100
+ registerSetProfileTool(server, client);
1027
1101
  return server;
1028
1102
  }
1029
1103
  async function startMcpServer(options) {