@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 +91 -17
- package/dist/cli.js.map +1 -1
- package/dist/mcp-main.js +91 -17
- package/dist/mcp-main.js.map +1 -1
- package/package.json +1 -1
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) =>
|
|
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
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
|
824
|
-
2.
|
|
825
|
-
3.
|
|
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
|
-
-
|
|
830
|
-
-
|
|
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
|
|
835
|
-
- You just want to share information without needing a response
|
|
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
|
-
|
|
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) {
|