@studiomopoke/crosschat 1.8.2 → 2.0.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.
Files changed (82) hide show
  1. package/README.md +48 -118
  2. package/bin/cli.cjs +37 -57
  3. package/crosschat.md +73 -104
  4. package/dashboard/dist/assets/index-BY-IQhma.js +49 -0
  5. package/dashboard/dist/assets/index-CI8v9PKQ.css +1 -0
  6. package/dashboard/dist/index.html +2 -2
  7. package/dist/hub/_hub-section-1-infra.d.ts +8 -0
  8. package/dist/hub/_hub-section-1-infra.d.ts.map +1 -0
  9. package/dist/hub/_hub-section-1-infra.js +152 -0
  10. package/dist/hub/_hub-section-1-infra.js.map +1 -0
  11. package/dist/hub/_hub-section-2-handlers.d.ts +2 -0
  12. package/dist/hub/_hub-section-2-handlers.d.ts.map +1 -0
  13. package/dist/hub/_hub-section-2-handlers.js +514 -0
  14. package/dist/hub/_hub-section-2-handlers.js.map +1 -0
  15. package/dist/hub/_hub-section-3-rest.d.ts +2 -0
  16. package/dist/hub/_hub-section-3-rest.d.ts.map +1 -0
  17. package/dist/hub/_hub-section-3-rest.js +418 -0
  18. package/dist/hub/_hub-section-3-rest.js.map +1 -0
  19. package/dist/hub/_hub-section-4-ws.d.ts +2 -0
  20. package/dist/hub/_hub-section-4-ws.d.ts.map +1 -0
  21. package/dist/hub/_hub-section-4-ws.js +367 -0
  22. package/dist/hub/_hub-section-4-ws.js.map +1 -0
  23. package/dist/hub/agent-connection.d.ts +38 -62
  24. package/dist/hub/agent-connection.d.ts.map +1 -1
  25. package/dist/hub/agent-connection.js +146 -229
  26. package/dist/hub/agent-connection.js.map +1 -1
  27. package/dist/hub/hub-server.d.ts +1 -1
  28. package/dist/hub/hub-server.d.ts.map +1 -1
  29. package/dist/hub/hub-server.js +389 -685
  30. package/dist/hub/hub-server.js.map +1 -1
  31. package/dist/hub/message-manager.d.ts +158 -0
  32. package/dist/hub/message-manager.d.ts.map +1 -0
  33. package/dist/hub/message-manager.js +443 -0
  34. package/dist/hub/message-manager.js.map +1 -0
  35. package/dist/hub/protocol.d.ts +73 -131
  36. package/dist/hub/protocol.d.ts.map +1 -1
  37. package/dist/hub/protocol.js +3 -0
  38. package/dist/hub/protocol.js.map +1 -1
  39. package/dist/lifecycle.d.ts.map +1 -1
  40. package/dist/lifecycle.js +15 -89
  41. package/dist/lifecycle.js.map +1 -1
  42. package/dist/server.d.ts.map +1 -1
  43. package/dist/server.js +22 -33
  44. package/dist/server.js.map +1 -1
  45. package/dist/tools/add-badge.d.ts +4 -0
  46. package/dist/tools/add-badge.d.ts.map +1 -0
  47. package/dist/tools/add-badge.js +29 -0
  48. package/dist/tools/add-badge.js.map +1 -0
  49. package/dist/tools/claim-task.js +5 -5
  50. package/dist/tools/claim-task.js.map +1 -1
  51. package/dist/tools/clear-session.d.ts +1 -2
  52. package/dist/tools/clear-session.d.ts.map +1 -1
  53. package/dist/tools/clear-session.js +7 -22
  54. package/dist/tools/clear-session.js.map +1 -1
  55. package/dist/tools/flag-as-task.d.ts +4 -0
  56. package/dist/tools/flag-as-task.d.ts.map +1 -0
  57. package/dist/tools/flag-as-task.js +31 -0
  58. package/dist/tools/flag-as-task.js.map +1 -0
  59. package/dist/tools/get-messages.d.ts +2 -1
  60. package/dist/tools/get-messages.d.ts.map +1 -1
  61. package/dist/tools/get-messages.js +19 -6
  62. package/dist/tools/get-messages.js.map +1 -1
  63. package/dist/tools/list-peers.d.ts.map +1 -1
  64. package/dist/tools/list-peers.js +1 -4
  65. package/dist/tools/list-peers.js.map +1 -1
  66. package/dist/tools/resolve-task.d.ts +4 -0
  67. package/dist/tools/resolve-task.d.ts.map +1 -0
  68. package/dist/tools/resolve-task.js +29 -0
  69. package/dist/tools/resolve-task.js.map +1 -0
  70. package/dist/tools/send-message.d.ts.map +1 -1
  71. package/dist/tools/send-message.js +6 -5
  72. package/dist/tools/send-message.js.map +1 -1
  73. package/dist/tools/set-status.js +3 -3
  74. package/dist/tools/set-status.js.map +1 -1
  75. package/dist/tools/wait-for-messages.js +1 -1
  76. package/dist/tools/wait-for-messages.js.map +1 -1
  77. package/dist/types.d.ts +4 -3
  78. package/dist/types.d.ts.map +1 -1
  79. package/hooks/permission-hook.sh +19 -18
  80. package/package.json +1 -1
  81. package/dashboard/dist/assets/index-BR-2rRm6.css +0 -1
  82. package/dashboard/dist/assets/index-Ci2ihChN.js +0 -49
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CrossChat
2
2
 
3
- CrossChat is an MCP server that lets multiple Claude Code instances discover each other, communicate, and collaborate — all on a single machine. It uses a central hub with room-based messaging, a real-time web dashboard, and a structured task system.
3
+ CrossChat is an MCP server that lets multiple Claude Code instances discover each other, communicate, and collaborate — all on a single machine. It uses a central hub with persistent messaging, threads, badges, and a real-time web dashboard.
4
4
 
5
5
  ## How it works
6
6
 
@@ -23,15 +23,15 @@ Each Claude Code instance runs CrossChat as an MCP server (via stdio). On startu
23
23
  └─────────────┘ └──────────────┘
24
24
  ```
25
25
 
26
- **Hub server** — a single Node.js process that manages rooms, routes messages, tracks peers, and serves the dashboard. Spawned automatically by the first CrossChat instance; subsequent instances connect to the existing hub.
26
+ **Hub server** — a single Node.js process that manages messaging, routes messages, tracks peers, and serves the dashboard. Spawned automatically by the first CrossChat instance.
27
27
 
28
- **Peer discovery** — agents register with the hub on connect. `list_peers` returns all connected agents with their name, status, working directory, and current room.
28
+ **Peer discovery** — agents register with the hub on connect. `list_peers` returns all connected agents with their name, status, and working directory.
29
29
 
30
- **Room-based messaging** — all messages go to a room (default: "general"). Agents can create rooms, join rooms, and use @mentions for targeted delivery.
30
+ **Unified messaging** — messages are the atomic unit. Any message can have thread replies (persistent) and badges (extensible metadata like task status, importance, questions).
31
31
 
32
- **Task system** — structured task delegation with a full lifecycle: delegate claim accept update complete. Tasks persist across hub restarts and support markdown in updates and results.
32
+ **Tasks as badges** — any message can be flagged as a task. Tasks are a workflow layer on messages, not a separate system. Lifecycle: flag claim resolve.
33
33
 
34
- **Dashboard** — a real-time web UI for monitoring agent activity, sending messages, managing projects, and approving permission requests.
34
+ **Dashboard** — a real-time web UI for monitoring agent activity, sending messages, viewing badges, and approving permission requests.
35
35
 
36
36
  ---
37
37
 
@@ -65,40 +65,6 @@ npm install
65
65
  npm run build
66
66
  ```
67
67
 
68
- ### Configure Claude Code
69
-
70
- Add CrossChat to your MCP configuration (`~/.claude/settings.json`):
71
-
72
- ```json
73
- {
74
- "mcpServers": {
75
- "crosschat": {
76
- "command": "node",
77
- "args": ["/path/to/crosschat/dist/index.js"]
78
- }
79
- }
80
- }
81
- ```
82
-
83
- Optionally set a custom name or working directory:
84
-
85
- ```json
86
- {
87
- "mcpServers": {
88
- "crosschat": {
89
- "command": "node",
90
- "args": ["/path/to/crosschat/dist/index.js"],
91
- "env": {
92
- "CROSSCHAT_NAME": "frontend-worker",
93
- "CROSSCHAT_CWD": "/path/to/project"
94
- }
95
- }
96
- }
97
- }
98
- ```
99
-
100
- If `CROSSCHAT_NAME` is omitted, a name is auto-generated from the working directory (e.g., `myproject-a1b2`).
101
-
102
68
  ---
103
69
 
104
70
  ## Using CrossChat
@@ -107,10 +73,10 @@ Once configured, CrossChat starts automatically with each Claude Code session. T
107
73
 
108
74
  1. Discovers connected peers
109
75
  2. Sets up a background message listener
110
- 3. Announces the instance to the room
76
+ 3. Announces the instance to the channel
111
77
  4. Shows the dashboard URL
112
78
 
113
- From there, you can ask Claude to message peers, delegate tasks, switch rooms, or check status — all through natural language.
79
+ From there, you can ask Claude to message peers, flag tasks, add badges, or check status — all through natural language.
114
80
 
115
81
  ### Example
116
82
 
@@ -118,142 +84,106 @@ From there, you can ask Claude to message peers, delegate tasks, switch rooms, o
118
84
  ```
119
85
  You: /crosschat
120
86
 
121
- Claude: CrossChat is live. You are frontend-a1b2 in room "general".
87
+ Claude: CrossChat is live. You are frontend-a1b2.
122
88
  Peers: api-worker-c3d4 (available), backend-e5f6 (available)
123
89
  Dashboard: http://localhost:49322
124
90
 
125
91
  You: Ask the other instances to run their test suites
126
92
 
127
- Claude: Delegated tasks to api-worker and backend. Listening for results...
93
+ Claude: Sent messages and flagged as tasks. Listening for results...
128
94
 
129
- Claude: api-worker completed: 18 tests passed, 0 failed.
95
+ Claude: api-worker claimed the task and completed: 18 tests passed, 0 failed.
130
96
  Claude: backend completed: 42 tests passed, 2 failed (auth_middleware, rate_limiter).
131
97
  ```
132
98
 
133
- **Terminal 2:**
134
- ```
135
- You: /crosschat
136
-
137
- Claude: Connected as api-worker-c3d4. Listening for messages.
138
-
139
- Claude: Received task from frontend-a1b2: "Run the test suite and report results"
140
- Working on it now...
141
- All 18 tests passed. Sent results back.
142
- ```
143
-
144
99
  ### @mentions
145
100
 
146
101
  - **`@agent-name`** — delivers only to that agent
147
- - **`@here`** — delivers to everyone in the room
102
+ - **`@here`** — delivers to everyone
148
103
  - **No mention** — broadcast to all (default)
149
104
 
150
- The hub parses @mentions automatically. The dashboard always shows all messages regardless of mentions.
151
-
152
105
  ---
153
106
 
154
107
  ## Dashboard
155
108
 
156
- The hub serves a real-time web dashboard. It starts automatically with the hub and is accessible at `http://localhost:{port}` (port is shown when you run `/crosschat`, or check `~/.crosschat/dashboard.lock`).
109
+ The hub serves a real-time web dashboard at `http://localhost:{port}`.
157
110
 
158
111
  The dashboard provides:
159
112
 
160
- - **Chat view** — real-time message stream across rooms, with the ability to send messages as a dashboard user
161
- - **Agent sidebar** — all connected agents with status, working directory, and connection time
162
- - **Task panel** — task lifecycle tracking with status, progress notes, and results
163
- - **Projects panel** — registered projects with active agent counts and one-click agent launching
113
+ - **Chat view** — real-time message stream with badge rendering and thread indicators
114
+ - **Agent sidebar** — all connected agents with status and working directory
115
+ - **Badge system** — visual metadata on messages (task status, importance, questions, git commits)
116
+ - **Instances panel** — registered projects with one-click agent launching
164
117
  - **Permission popups** — approve or deny tool-use requests from agents in real time
165
118
 
166
119
  ---
167
120
 
168
121
  ## Tools
169
122
 
170
- CrossChat provides 15 MCP tools:
123
+ CrossChat provides 10 MCP tools:
171
124
 
172
125
  ### Messaging
173
126
  | Tool | Purpose |
174
127
  |------|---------|
175
- | `send_message` | Post a message to your current room |
176
- | `get_messages` | Read messages from your current room (filter by unread, sender, etc.) |
177
- | `wait_for_messages` | Block until a message arrives or timeout — used for background listeners |
178
- | `join_room` | Switch to a different room |
179
- | `create_room` | Create a new room and join it |
180
- | `get_room_digest` | Get an AI-generated summary of room history |
128
+ | `send_message` | Send to channel or thread (use `threadId` to reply) |
129
+ | `get_messages` | Read messages with badge data for at-a-glance context |
130
+ | `wait_for_messages` | Block until a message arrives — used for background listeners |
181
131
 
182
132
  ### Peers
183
133
  | Tool | Purpose |
184
134
  |------|---------|
185
- | `list_peers` | Discover connected agents — names, status, working directories, rooms |
186
- | `set_status` | Set your availability (`available` or `busy`) with optional detail |
135
+ | `list_peers` | Discover connected agents — names, status, working directories |
136
+ | `set_status` | Set your availability (`available` or `busy`) |
187
137
 
188
- ### Tasks
138
+ ### Tasks & Badges
189
139
  | Tool | Purpose |
190
140
  |------|---------|
191
- | `delegate_task` | Create a task in the current room, optionally targeting a specific agent |
192
- | `claim_task` | Bid on an open task |
193
- | `accept_claim` | Accept an agent's bid on your task |
194
- | `update_task` | Append markdown progress notes to a task |
195
- | `complete_task` | Mark a task done or failed with a markdown result |
196
- | `list_tasks` | List tasks with optional filters (status, room, assignee) |
197
- | `get_task_status` | Get full task details including notes history |
141
+ | `flag_as_task` | Promote any message to a tracked task |
142
+ | `claim_task` | Claim a flagged task (first-come-first-served) |
143
+ | `resolve_task` | Complete or fail a task with a markdown result |
144
+ | `add_badge` | Add metadata badge to any message |
198
145
 
199
146
  ### Session
200
147
  | Tool | Purpose |
201
148
  |------|---------|
202
- | `clear_session` | Reset your session state |
149
+ | `clear_session` | Clear channel messages |
203
150
 
204
151
  ---
205
152
 
206
153
  ## Architecture
207
154
 
208
- ### Hub server
155
+ ### Unified messaging
209
156
 
210
- The hub is a single Node.js process that runs on localhost. It provides:
157
+ Messages are persistent (JSONL storage) and support:
158
+ - **Threads** — reply to any message to start a persistent thread
159
+ - **Badges** — extensible metadata: task status, importance, questions, git commits, projects
160
+ - **Task workflow** — flag any message as a task, claim it, resolve it
211
161
 
212
- - **WebSocket server** for agent connections (registration, messaging, task events)
213
- - **WebSocket server** for browser dashboard connections
214
- - **HTTP API** for REST access to peers, tasks, projects, rooms, and permissions
215
- - **Heartbeat** — pings agents every 30 seconds; terminates unresponsive connections after 10 seconds
162
+ ### Task lifecycle
163
+
164
+ ```
165
+ send message → flag_as_task → claim_task → resolve_task (completed/failed)
166
+ ```
216
167
 
217
- The hub is spawned automatically by the first CrossChat MCP server to start. Subsequent instances detect the existing hub via `~/.crosschat/dashboard.lock` and connect to it.
168
+ Tasks are badges on messages, not a separate system. Thread replies on the flagged message serve as progress discussion.
218
169
 
219
170
  ### Data directory (`~/.crosschat/`)
220
171
 
221
172
  ```
222
173
  ~/.crosschat/
223
174
  dashboard.lock # Hub PID, port, and start time
224
- projects.json # Registered projects (persisted)
225
- sessions/ # Session markers for permission hook (keyed by PID)
226
- digests/ # AI-generated room history digests
227
- tasks/ # Persisted task data
228
- ```
229
-
230
- ### Task lifecycle
231
-
232
- ```
233
- delegate → open → claimed → in_progress → completed
234
- → failed
175
+ instances.json # Registered instances (persisted)
176
+ hooks/ # Permission hook (stable location)
177
+ messages/ # Persistent message storage
178
+ general.jsonl # Channel messages
179
+ threads/ # Thread messages (one file per thread)
180
+ badges/ # Badge sidecar files
181
+ tasks/ # Task metadata for flagged messages
235
182
  ```
236
183
 
237
- 1. **Delegator** creates a task with `delegate_task`
238
- 2. **Worker** bids on it with `claim_task`
239
- 3. **Delegator** accepts the bid with `accept_claim`
240
- 4. **Worker** sends progress updates with `update_task`
241
- 5. **Worker** finishes with `complete_task` (status: `completed` or `failed`)
242
-
243
- Tasks persist across hub restarts. Messages are ephemeral.
244
-
245
184
  ### Permission elevation
246
185
 
247
- When the permission hook is installed (`crosschat install`), tool-use requests from Claude Code instances are elevated to the dashboard. Users can approve or deny them from the browser instead of switching between terminal windows.
248
-
249
- The hook works by:
250
- 1. Detecting CrossChat-connected instances via session markers (`~/.crosschat/sessions/`)
251
- 2. POSTing the permission request to the hub API
252
- 3. Polling until the dashboard user approves or denies (up to 5 minutes)
253
-
254
- ### Project registry
255
-
256
- Projects are auto-registered when agents connect — the hub records each agent's working directory. Projects can also be manually added via the dashboard. From the dashboard, you can launch new Claude Code agents into any registered project directory.
186
+ When the permission hook is installed (`crosschat install`), tool-use requests are elevated to the dashboard. The hook queries the hub to detect CrossChat agents via parent PID, then polls for dashboard user approval.
257
187
 
258
188
  ---
259
189
 
package/bin/cli.cjs CHANGED
@@ -12,26 +12,20 @@ const COMMANDS_DIR = path.join(os.homedir(), ".claude", "commands");
12
12
  const COMMAND_SOURCE = path.join(__dirname, "..", "crosschat.md");
13
13
  const COMMAND_TARGET = path.join(COMMANDS_DIR, "crosschat.md");
14
14
  const HOOK_SOURCE = path.join(__dirname, "..", "hooks", "permission-hook.sh");
15
+ const STABLE_HOOK = path.join(os.homedir(), ".crosschat", "hooks", "permission-hook.sh");
15
16
  const MCP_KEY = "crosschat";
16
17
 
17
18
  const CROSSCHAT_PERMISSIONS = [
18
- "mcp__crosschat__wait_for_messages",
19
+ "mcp__crosschat__send_message",
19
20
  "mcp__crosschat__get_messages",
21
+ "mcp__crosschat__wait_for_messages",
20
22
  "mcp__crosschat__list_peers",
21
- "mcp__crosschat__send_message",
22
23
  "mcp__crosschat__set_status",
23
- "mcp__crosschat__complete_task",
24
- "mcp__crosschat__delegate_task",
25
- "mcp__crosschat__get_task_status",
26
- "mcp__crosschat__join_room",
27
- "mcp__crosschat__create_room",
24
+ "mcp__crosschat__flag_as_task",
28
25
  "mcp__crosschat__claim_task",
29
- "mcp__crosschat__accept_claim",
30
- "mcp__crosschat__update_task",
31
- "mcp__crosschat__list_tasks",
26
+ "mcp__crosschat__resolve_task",
27
+ "mcp__crosschat__add_badge",
32
28
  "mcp__crosschat__clear_session",
33
- "mcp__crosschat__get_room_digest",
34
- "mcp__crosschat__request_digest",
35
29
  ];
36
30
 
37
31
  const { spawn } = require("child_process");
@@ -141,12 +135,18 @@ function install() {
141
135
  addedPerms++;
142
136
  }
143
137
  }
144
- // 2b. Add or update permission hook in settings (PreToolUse)
138
+ // 2b. Copy hook to stable location and register in settings (PreToolUse)
145
139
  if (fs.existsSync(HOOK_SOURCE)) {
140
+ // Copy to ~/.crosschat/hooks/ so the path survives package updates
141
+ const stableDir = path.dirname(STABLE_HOOK);
142
+ fs.mkdirSync(stableDir, { recursive: true });
143
+ fs.copyFileSync(HOOK_SOURCE, STABLE_HOOK);
144
+ fs.chmodSync(STABLE_HOOK, 0o755);
145
+
146
146
  if (!settings.hooks) settings.hooks = {};
147
147
  if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
148
148
 
149
- const hookCommand = HOOK_SOURCE;
149
+ const hookCommand = STABLE_HOOK;
150
150
  const existingIdx = settings.hooks.PreToolUse.findIndex((entry) =>
151
151
  entry.hooks &&
152
152
  entry.hooks.some((h) => h.command && h.command.includes("permission-hook.sh"))
@@ -303,15 +303,13 @@ async function status() {
303
303
  console.log("Dashboard: http://localhost:" + hubPort);
304
304
 
305
305
  try {
306
- const [peerList, taskList, roomList] = await Promise.all([
306
+ const [peerList, channelList] = await Promise.all([
307
307
  hubGet(hubPort, "/api/peers"),
308
- hubGet(hubPort, "/api/tasks"),
309
- hubGet(hubPort, "/api/rooms"),
308
+ hubGet(hubPort, "/api/channels"),
310
309
  ]);
311
310
 
312
311
  const peers = Array.isArray(peerList) ? peerList : [];
313
- const tasks = Array.isArray(taskList) ? taskList : [];
314
- const roomArr = Array.isArray(roomList) ? roomList : [];
312
+ const channels = Array.isArray(channelList) ? channelList : [];
315
313
 
316
314
  console.log("");
317
315
  console.log("Agents: " + peers.length + " connected");
@@ -320,16 +318,7 @@ async function status() {
320
318
  console.log(" " + badge + " " + p.name + " — " + (p.cwd || "?"));
321
319
  }
322
320
 
323
- if (tasks.length > 0) {
324
- const byStatus = {};
325
- for (const t of tasks) {
326
- byStatus[t.status] = (byStatus[t.status] || 0) + 1;
327
- }
328
- const summary = Object.entries(byStatus).map(([k, v]) => v + " " + k).join(", ");
329
- console.log("Tasks: " + tasks.length + " (" + summary + ")");
330
- }
331
-
332
- console.log("Rooms: " + roomArr.length);
321
+ console.log("Channels: " + channels.length);
333
322
  } catch {
334
323
  console.log("");
335
324
  console.log("Hub not responding (port " + hubPort + ").");
@@ -456,7 +445,7 @@ async function peers() {
456
445
  const detail = p.statusDetail ? " " + p.statusDetail : "";
457
446
  console.log(" " + p.name + " " + badge + detail);
458
447
  console.log(" ID: " + p.peerId);
459
- console.log(" Room: " + (p.currentRoom || ""));
448
+ console.log(" Channel: " + (p.currentChannel || "general"));
460
449
  console.log(" Directory: " + (p.cwd || "—"));
461
450
  console.log(" Connected: " + (p.connectedAt ? timeAgo(p.connectedAt) : "—"));
462
451
  console.log("");
@@ -485,7 +474,7 @@ async function tasks() {
485
474
  }[t.status] || t.status.toUpperCase();
486
475
  console.log(" [" + statusTag + "] " + t.description);
487
476
  console.log(" ID: " + t.taskId);
488
- console.log(" Room: " + (t.roomId || "—"));
477
+ console.log(" Channel: " + (t.channelId || "—"));
489
478
  console.log(" Creator: " + (t.creatorName || "—"));
490
479
  if (t.claimantName) {
491
480
  console.log(" Assignee: " + t.claimantName);
@@ -498,37 +487,27 @@ async function tasks() {
498
487
  }
499
488
  }
500
489
 
501
- async function rooms() {
490
+ async function channels() {
502
491
  const port = requireHub();
503
- const [roomData, peerData] = await Promise.all([
504
- hubGet(port, "/api/rooms"),
492
+ const [channelData, peerData] = await Promise.all([
493
+ hubGet(port, "/api/channels"),
505
494
  hubGet(port, "/api/peers"),
506
495
  ]);
507
- const roomList = Array.isArray(roomData) ? roomData : [];
496
+ const channelList = Array.isArray(channelData) ? channelData : [];
508
497
  const peerList = Array.isArray(peerData) ? peerData : [];
509
498
 
510
- if (roomList.length === 0) {
511
- console.log("No active rooms.");
499
+ if (channelList.length === 0) {
500
+ console.log("No active channels.");
512
501
  return;
513
502
  }
514
503
 
515
- // Count peers per room
516
- const peersPerRoom = {};
517
- for (const p of peerList) {
518
- const room = p.currentRoom || "general";
519
- if (!peersPerRoom[room]) peersPerRoom[room] = [];
520
- peersPerRoom[room].push(p.name);
521
- }
522
-
523
- console.log("Rooms (" + roomList.length + "):\n");
524
- for (const r of roomList) {
525
- const name = r.name || r.id;
526
- const msgCount = r.messageCount != null ? r.messageCount : "?";
527
- const roomPeers = peersPerRoom[name] || peersPerRoom[r.id] || [];
528
- console.log(" " + name);
529
- console.log(" Messages: " + msgCount + " Agents: " + roomPeers.length);
530
- if (roomPeers.length > 0) {
531
- console.log(" " + roomPeers.join(", "));
504
+ console.log("Channels (" + channelList.length + "):\n");
505
+ for (const ch of channelList) {
506
+ const agentsInChannel = peerList.filter((p) => (p.currentChannel || "general") === ch);
507
+ console.log(" # " + ch);
508
+ console.log(" Agents: " + agentsInChannel.length);
509
+ if (agentsInChannel.length > 0) {
510
+ console.log(" " + agentsInChannel.map((a) => a.name).join(", "));
532
511
  }
533
512
  console.log("");
534
513
  }
@@ -589,10 +568,10 @@ function showHelp() {
589
568
  console.log(" crosschat restart Restart the hub server");
590
569
  console.log("");
591
570
  console.log("Info:");
592
- console.log(" crosschat status Overview of hub, agents, tasks, rooms");
571
+ console.log(" crosschat status Overview of hub, agents, and channels");
593
572
  console.log(" crosschat peers List connected agents with details");
594
573
  console.log(" crosschat tasks [status] List tasks (optional: open, claimed, completed, failed)");
595
- console.log(" crosschat rooms List active rooms");
574
+ console.log(" crosschat channels List active channels");
596
575
  console.log("");
597
576
  console.log(" crosschat --version Show version");
598
577
  console.log(" crosschat --help Show this help");
@@ -633,8 +612,9 @@ switch (command) {
633
612
  case "tasks":
634
613
  tasks();
635
614
  break;
615
+ case "channels":
636
616
  case "rooms":
637
- rooms();
617
+ channels();
638
618
  break;
639
619
  case "--version":
640
620
  case "-v":