@sym-bot/mesh-channel 0.3.13 → 0.3.15

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sym-mesh-channel",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "description": "Real-time Claude-to-Claude mesh. Agent-to-agent cognitive signals over Bonjour LAN or WebSocket relay.",
5
5
  "author": {
6
6
  "name": "Hongwei Xu",
package/.mcp.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "@sym-bot/mesh-channel@0.3.10"
7
+ "@sym-bot/mesh-channel@0.3.14"
8
8
  ],
9
9
  "env": {
10
10
  "SYM_RELAY_URL": "${user_config.relay_url}",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.15
4
+
5
+ ### Fixed
6
+
7
+ - **`start` now finds `claude` on Windows.** The launch used `spawnSync('claude', …)` with no shell, which does an exact-filename lookup that ignores Windows `PATHEXT` — so `start` failed with `ENOENT` even when `claude` ran fine in the shell (there it resolves a `.cmd`/`.ps1` shim or `.exe`, never bare `claude`). The launch now routes through a shell on Windows so `PATHEXT` resolution applies; whitespace args are quoted since `shell: true` forwards them unquoted. The POSIX launch path is unchanged.
8
+ - **`start --name` no longer silently reverts identity on a stale entry.** `start` auto-injected `--force` only on a *live* entry mismatch, but `npx` rotates its cached `server.js` path on every version resolve, so the persisted entry is routinely stale yet still holds the node's name/group. On a re-run, `start` saw no live entry, pushed no `--force`, and `init`'s preserve-over-request precedence dropped the requested `--name` — reverting the node's identity to the stale name. `start` now reconciles against the persisted entry whether or not it's stale and forces the rewrite when an explicit `--name`/`--group` differs. The group is preserved when no `--group` is passed.
9
+
10
+ ## 0.3.14
11
+
12
+ ### Added
13
+
14
+ - **`sym-mesh-channel start` — one command to a live mesh session.** Configures the MCP server if needed, then launches Claude Code with the real-time Channels flag already on, so users never type `--dangerously-load-development-channels …` or have to choose between the `plugin:` and `server:` handle. `start --project --name <node> --group <team>` stands up a named mesh agent; `start --print` is a dry run; everything after `--` is forwarded to `claude`. Co-resident sessions don't collide (server.js auto-suffixes a live-identity clash since 0.3.10), so `start` in several terminals just works.
15
+
16
+ ### Fixed
17
+
18
+ - **CLI subcommand dispatch via the published bin.** The `bin` entrypoint (`server.js`) only routed `init` to the installer, so `npx @sym-bot/mesh-channel doctor` silently fell through and started the MCP server instead. Now `init`, `doctor`, and `start` all route to the installer/launcher.
19
+ - **`init --force` with an explicit `SYM_NODE_NAME` now relabels the entry** instead of always preserving the prior name (symmetric with how `--group` already behaves). A routine reinstall with no explicit name still preserves identity.
20
+
3
21
  ## 0.3.13
4
22
 
5
23
  ### Changed
package/README.md CHANGED
@@ -66,37 +66,39 @@ They're **not alternatives** — the channel is built *on* sym and speaks the sa
66
66
 
67
67
  ## Quick start
68
68
 
69
- Install the published plugin in Claude Code:
69
+ **One command — it configures the mesh and launches Claude Code with real-time push already on:**
70
70
 
71
71
  ```
72
- /plugin marketplace add anthropics/claude-plugins-community
73
- /plugin install sym-mesh-channel@claude-community
72
+ npx @sym-bot/mesh-channel@latest start
74
73
  ```
75
74
 
76
- That gives you all **11 MCP tools no flag, no npm, nothing else to add** and **one install covers every Claude Code session on the machine**: open as many as you like (one per repo, or one planning while another codes); each gets its own mesh identity and picks up the mesh on resume. The first command is a one-time marketplace registration.
75
+ Run it in any repo, in as many terminals as you like the sessions discover each other over loopback (or the same wifi) and **think together in real time**. No flag to remember, no `plugin:` vs `server:` to choose `start` wires the channel for you. (First run configures the MCP server; after that it just launches. Co-resident sessions don't collide each becomes its own peer.)
77
76
 
78
- Discoverable in Claude Code too `/plugin` **Discover** search "mesh".
79
-
80
- Want the always-latest build straight from the source repo? Add it as its own marketplace instead:
77
+ Stand up a persistent **named** agent, or join a team room:
81
78
 
82
79
  ```
83
- /plugin marketplace add sym-bot/sym-mesh-channel
84
- /plugin install sym-mesh-channel@sym-mesh-channel
80
+ npx @sym-bot/mesh-channel@latest start --name cto --group my-team
85
81
  ```
86
82
 
87
- This tracks `main`, so it can be a few versions ahead of the community listing (whose pinned build lags until the next directory sync). **Whichever marketplace you install from, the channel flag below must use that same `@<marketplace>` handle** — `@claude-community` for the directory install above, `@sym-mesh-channel` for this source-repo install. Mismatching them is the #1 cause of `plugin not installed` when launching with the channel flag.
83
+ (`start --print` shows the exact `claude …` command without launching; anything after `--` is passed straight to `claude`, e.g. `… start -- --resume`.)
84
+
85
+ ### Prefer the Claude Code plugin UI?
88
86
 
89
- ### Real-time push (the `<channel>` experience)
87
+ Install the plugin and you get the 11 MCP tools immediately:
88
+
89
+ ```
90
+ /plugin install sym-mesh-channel@claude-community
91
+ ```
90
92
 
91
- The tools above are pull-based. For a peer's message to **land in Claude's context mid-turn, with no tool call** — the "Claude thinks with the mesh" experience the screenshots show Claude Code has to load this plugin's *channel*, which is currently gated behind a flag while it awaits Anthropic's approved-channels allowlist:
93
+ For real-time push on this path, launch with the channel flag (the handle must match where you installed from):
92
94
 
93
95
  ```
94
96
  claude --dangerously-load-development-channels plugin:sym-mesh-channel@claude-community
95
97
  ```
96
98
 
97
- (If you installed from the source-repo marketplace instead, use `plugin:sym-mesh-channel@sym-mesh-channel` the `@<marketplace>` must match wherever you installed from.)
99
+ Want the latest build before the community directory syncs? Add SYM.BOT's own catalog instead `/plugin marketplace add sym-bot/marketplace`, then `/plugin install sym-mesh-channel@sym-bot` (and launch with `plugin:sym-mesh-channel@sym-bot`).
98
100
 
99
- The flag becomes unnecessary once the channel is allowlisted tracked in [anthropics/claude-plugins-official#1512](https://github.com/anthropics/claude-plugins-official/issues/1512).
101
+ > **The dev flag is temporary** an Anthropic-side gate while the channel awaits the approved-channels allowlist ([anthropics/claude-plugins-official#1512](https://github.com/anthropics/claude-plugins-official/issues/1512)). `start` passes it under the hood today; once the channel is allowlisted it disappears. Curious when to use the npm/server install directly (named agents, repo-committed team config, the `sym` CLI)? See [Advanced](#advanced-named-agents-teams--the-cli).
100
102
 
101
103
  ## What you get
102
104
 
@@ -234,7 +236,9 @@ The plugin composes two open specs:
234
236
 
235
237
  ## Advanced: per-project node identity
236
238
 
237
- By default every Claude Code session on a machine shares one mesh identity (set globally in `~/.claude.json`). If you run several Claude Code sessions in parallel from distinct project directories and want each to appear as its own peer on the mesh — e.g. a "research" session and a "strategy" session on the same laptop — install per-project instead:
239
+ *(npm / MCP-server install only plugin sessions already get a distinct peer identity each.)*
240
+
241
+ With the global npm install, every Claude Code session on the machine shares one mesh identity (set in `~/.claude.json`). To give each project directory its own stable peer name instead — e.g. a "research" session and a "strategy" session on the same laptop — install per-project:
238
242
 
239
243
  ```bash
240
244
  cd path/to/your/project
@@ -345,7 +349,7 @@ Some corporate networks block mDNS multicast entirely — try a hotspot or home
345
349
 
346
350
  The 11 tools work without any flag. The real-time `<channel>` **push** is separate: Claude Code only delivers channel notifications for channels on its **approved-channels allowlist**, and sym-mesh-channel isn't on it yet — so push requires the development-channels flag matching your install path:
347
351
 
348
- - plugin install: `--dangerously-load-development-channels plugin:sym-mesh-channel@claude-community` (or `@sym-mesh-channel` if you installed from the source-repo marketplace — the handle must match your install source)
352
+ - plugin install: `--dangerously-load-development-channels plugin:sym-mesh-channel@claude-community` (or `@sym-bot` if you installed from the source-repo marketplace — the handle must match your install source)
349
353
  - npm install: `--dangerously-load-development-channels server:claude-sym-mesh`
350
354
 
351
355
  This is an Anthropic-side gate, not a bug here — once the channel is allowlisted the flag is no longer needed. Tracked in [anthropics/claude-plugins-official#1512](https://github.com/anthropics/claude-plugins-official/issues/1512).
@@ -358,15 +362,25 @@ Your shell profile (`~/.zshrc`, `~/.bashrc`) exports `SYM_RELAY_URL`. Claude Cod
358
362
 
359
363
  Don't. Each session should have a distinct `SYM_NODE_NAME`. The SymNode acquires an exclusive lockfile on its identity (`~/.sym/nodes/<name>/lock.pid`) and refuses to start a second process with the same name. If you see `EIDENTITYLOCK`, kill the other process or pick a different name. For multiple parallel sessions with their own identities, use the per-project install above.
360
364
 
361
- ## Other install paths
365
+ ## Advanced: named agents, teams & the CLI
362
366
 
363
- ### Via npm (server install)
367
+ Everything above uses the plugin — all most users need. Install the engine as an npm package / MCP server instead **only** when you want one of these:
368
+
369
+ - **A persistent, *named* agent.** The plugin auto-names each session (`claude-myrepo-3f9a`); the server install lets you pin `SYM_NODE_NAME=cto` so the node always appears under the same name in `sym_peers`, mesh memory, and CMB lineage — a durable agent identity, not an anonymous session.
370
+ - **Team config committed to a repo.** Config lives in a project `.mcp.json`, so you can commit `SYM_GROUP=backend-team` (plus relay URL/token) into the repo — anyone who opens it in Claude Code auto-joins the team room with zero per-person setup.
371
+ - **The `sym` CLI and non-Claude agents.** This also installs [`@sym-bot/sym`](https://github.com/sym-bot/sym), giving you `sym ask` / `sym groups` in your terminal and an engine that other agents, scripts, and languages can share.
364
372
 
365
373
  ```bash
366
374
  npm install -g @sym-bot/mesh-channel
367
375
  ```
368
376
 
369
- Installs the engine globally and exposes it as an MCP server (`server:claude-sym-mesh`). Use this if you prefer npm for install/update management, or want `@sym-bot/sym` available outside the plugin surface. (For real-time push on this path, see [Troubleshooting](#channel-notifications-never-arrive-even-though-peers-are-connected).)
377
+ This registers a raw MCP server keyed `claude-sym-mesh`. Because it's a server (not a plugin), its real-time channel handle is **`server:claude-sym-mesh`** so launch with:
378
+
379
+ ```bash
380
+ claude --dangerously-load-development-channels server:claude-sym-mesh
381
+ ```
382
+
383
+ Pin a fixed per-session identity with [per-project node identity](#advanced-per-project-node-identity); set relay credentials in the env block per [cross-network setup](#cross-network-setup-own-hosted-relay).
370
384
 
371
385
  ## References
372
386
 
package/bin/install.js CHANGED
@@ -55,8 +55,8 @@ const cmd = args.find((a) => !a.startsWith('--')) || 'init';
55
55
  const groupArgIdx = args.indexOf('--group');
56
56
  const groupArg = groupArgIdx !== -1 ? args[groupArgIdx + 1] : null;
57
57
 
58
- if (cmd !== 'init' && cmd !== 'doctor') {
59
- process.stderr.write(`Unknown command: ${cmd}\nUsage: sym-mesh-channel init [--project] [--force] [--group <name>]\n sym-mesh-channel doctor\n`);
58
+ if (cmd !== 'init' && cmd !== 'doctor' && cmd !== 'start') {
59
+ process.stderr.write(`Unknown command: ${cmd}\nUsage: sym-mesh-channel start [--project] [--name <node>] [--group <name>] [-- <claude args>]\n sym-mesh-channel init [--project] [--force] [--group <name>]\n sym-mesh-channel doctor\n`);
60
60
  process.exit(1);
61
61
  }
62
62
 
@@ -111,6 +111,115 @@ function preserveGroup(entry) {
111
111
  return g || null;
112
112
  }
113
113
 
114
+ // ── start: one command to a live mesh session ─────────────────────
115
+ // `sym-mesh-channel start` configures the MCP server (only if needed)
116
+ // and then launches Claude Code with the real-time Channels flag
117
+ // already on — so the user never types `--dangerously-load-development-
118
+ // channels …` or has to choose between the plugin: and server: handle.
119
+ // The npx / MCP-server install path always exposes the channel as a raw
120
+ // server, so the handle is deterministically `server:claude-sym-mesh`.
121
+ //
122
+ // sym-mesh-channel start # this dir, real-time push on
123
+ // sym-mesh-channel start --project --name cto --group my-team
124
+ // sym-mesh-channel start --print # show the command, don't launch
125
+ // sym-mesh-channel start -- --resume # pass args through to claude
126
+ //
127
+ // Co-resident sessions sharing one config don't collide: server.js
128
+ // auto-suffixes a live-identity clash (since 0.3.10), so each session
129
+ // becomes its own peer.
130
+ if (cmd === 'start') {
131
+ const { spawnSync } = require('child_process');
132
+
133
+ const nameArgIdx = args.indexOf('--name');
134
+ const nameArg = nameArgIdx !== -1 ? args[nameArgIdx + 1] : null;
135
+ const printOnly = args.includes('--print') || args.includes('--dry-run');
136
+
137
+ // Everything after `--` is forwarded verbatim to `claude`.
138
+ const dashDash = args.indexOf('--');
139
+ const passthrough = dashDash !== -1 ? args.slice(dashDash + 1) : [];
140
+
141
+ const launchDir = process.cwd();
142
+
143
+ const handle = 'server:claude-sym-mesh';
144
+ const claudeArgs = ['--dangerously-load-development-channels', handle, ...passthrough];
145
+
146
+ // --print is a pure dry-run: show the launch command, change nothing.
147
+ if (printOnly) {
148
+ console.log(`claude ${claudeArgs.join(' ')}`);
149
+ process.exit(0);
150
+ }
151
+
152
+ // Is the scope Claude Code will actually read already configured with a
153
+ // LIVE entry? --project → <cwd>/.mcp.json ; otherwise → ~/.claude.json
154
+ // Reconcile identity against the persisted entry whether or not its
155
+ // server.js path is stale. npx rotates the cached server.js path on every
156
+ // version resolve (…/_npx/<hash>/…), so the entry is routinely stale yet
157
+ // still carries the node's name/group. Comparing only against a *live*
158
+ // entry let an explicit --name lose to the stale name on the heal path:
159
+ // start saw no live entry, pushed no --force, and init's non-force
160
+ // precedence (preserve-over-request) kept the old identity.
161
+ function rawEntryInScope() {
162
+ try {
163
+ const p = isProject
164
+ ? path.join(launchDir, '.mcp.json')
165
+ : path.join(os.homedir(), '.claude.json');
166
+ if (!fs.existsSync(p)) return null;
167
+ const j = JSON.parse(fs.readFileSync(p, 'utf8'));
168
+ return (j && j.mcpServers && j.mcpServers['claude-sym-mesh']) || null;
169
+ } catch { return null; }
170
+ }
171
+
172
+ const existing = rawEntryInScope();
173
+ const stale = existing ? isStaleEntry(existing) : false;
174
+ const wantName = nameArg || null;
175
+ const wantGroup = groupArg || null;
176
+ const mismatch = !!existing && (
177
+ (wantName && preserveNodeName(existing) !== wantName) ||
178
+ (wantGroup && (preserveGroup(existing) || 'default') !== wantGroup)
179
+ );
180
+
181
+ // Configure when there's nothing persisted yet, the persisted server.js
182
+ // path is stale (heal), an explicit --name/--group differs, or --force.
183
+ // Otherwise launch straight away — `start` stays cheap to run every
184
+ // session.
185
+ if (!existing || stale || mismatch || force) {
186
+ const initArgs = ['init'];
187
+ if (isProject) initArgs.push('--project');
188
+ if (groupArg) initArgs.push('--group', groupArg);
189
+ // --force makes init honor the explicit --name/--group over the
190
+ // persisted value. Required on the rename path AND on stale-heal with
191
+ // an explicit --name, where init would otherwise preserve the old name
192
+ // and silently drop the request. Force alone never clobbers an
193
+ // unspecified group: resolveGroup() still preserves when no --group.
194
+ if (existing && (mismatch || force)) initArgs.push('--force');
195
+ const childEnv = Object.assign({}, process.env);
196
+ if (nameArg) childEnv.SYM_NODE_NAME = nameArg;
197
+ const r = spawnSync(process.execPath, [__filename, ...initArgs], {
198
+ stdio: 'inherit', env: childEnv, cwd: launchDir,
199
+ });
200
+ if (r.status !== 0) process.exit(r.status == null ? 1 : r.status);
201
+ }
202
+
203
+ console.log(`\n▶ Launching Claude Code on the SYM mesh — real-time push on.\n (channel: ${handle}; the dev flag is temporary until Anthropic allowlists it)\n`);
204
+ // On Windows the `claude` CLI is a `.cmd`/`.ps1` shim (npm) or `.exe`
205
+ // (native installer). Node's spawn does an exact-filename lookup that
206
+ // ignores PATHEXT, so bare `spawnSync('claude', …)` returns ENOENT even
207
+ // when `claude` runs fine in the user's shell. Route through a shell on
208
+ // Windows so PATHEXT resolution kicks in; quote args that contain spaces
209
+ // since shell:true forwards the args unquoted.
210
+ const isWindows = process.platform === 'win32';
211
+ const spawnArgs = isWindows
212
+ ? claudeArgs.map((a) => (/\s/.test(a) ? `"${a}"` : a))
213
+ : claudeArgs;
214
+ const run = spawnSync('claude', spawnArgs, { stdio: 'inherit', cwd: launchDir, shell: isWindows });
215
+ if (run.error && run.error.code === 'ENOENT') {
216
+ process.stderr.write('ERROR: `claude` was not found on your PATH.\n');
217
+ process.stderr.write('Install Claude Code (https://claude.com/code), make sure `claude` runs in your terminal, then re-run `sym-mesh-channel start`.\n');
218
+ process.exit(127);
219
+ }
220
+ process.exit(run.status == null ? 0 : run.status);
221
+ }
222
+
114
223
  // --postinstall always runs global install (npm postinstall runs from
115
224
  // npm's staging directory, not the user's project dir). If both flags
116
225
  // are passed, the --project flag is ignored during postinstall.
@@ -219,9 +328,12 @@ if (useProjectMode) {
219
328
  process.exit(2);
220
329
  }
221
330
 
222
- // Preserve the prior node name on rewrite so mesh identity doesn't drift
223
- // back to the hostname default on every reinstall.
224
- const projectNodeName = preserveNodeName(existingProjectEntry) || nodeName;
331
+ // --force + an explicit SYM_NODE_NAME deliberately relabels this entry
332
+ // (symmetric with resolveGroup). Otherwise preserve the prior name so the
333
+ // mesh identity doesn't drift back to the hostname default on a reinstall.
334
+ const projectNodeName = (force && process.env.SYM_NODE_NAME)
335
+ ? process.env.SYM_NODE_NAME
336
+ : (preserveNodeName(existingProjectEntry) || nodeName);
225
337
 
226
338
  // Group resolution priority — see resolveGroup() at top of file.
227
339
  // Summary: --force + explicit flag/env wins; otherwise preserve, then
@@ -469,7 +581,9 @@ if (existingTopEntry && !force && !topEntryIsStale) {
469
581
  }
470
582
 
471
583
  // Preserve the prior node name on rewrite so mesh identity doesn't drift.
472
- const topNodeName = preserveNodeName(existingTopEntry) || nodeName;
584
+ const topNodeName = (force && process.env.SYM_NODE_NAME)
585
+ ? process.env.SYM_NODE_NAME
586
+ : (preserveNodeName(existingTopEntry) || nodeName);
473
587
 
474
588
  // Resolve SYM_GROUP for the global entry — see resolveGroup() at top.
475
589
  // Heal-path default preserves; --force lets the user explicitly switch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sym-bot/mesh-channel",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "MCP server — real-time agent-to-agent cognition for Claude Code remote teams via the SYM mesh.",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- // Subcommand dispatch: `sym-mesh-channel init` runs the installer.
5
- if (process.argv[2] === 'init') {
4
+ // Subcommand dispatch: CLI subcommands run the installer/launcher, not the
5
+ // MCP server. (When Claude Code spawns the server there is no subcommand, so
6
+ // argv[2] is undefined and this falls through to the MCP server below.)
7
+ if (['init', 'doctor', 'start'].includes(process.argv[2])) {
6
8
  require('./bin/install.js');
7
9
  return;
8
10
  }
@@ -1,46 +0,0 @@
1
- {
2
- "name": "sym-mesh-channel",
3
- "owner": {
4
- "name": "SYM.BOT",
5
- "email": "info@sym.bot"
6
- },
7
- "metadata": {
8
- "description": "Real-time communication and collaboration among Claude Code sessions — agent-to-agent cognitive signals over Bonjour LAN or WebSocket relay, on the Mesh Memory Protocol (MMP).",
9
- "version": "0.3.11"
10
- },
11
- "plugins": [
12
- {
13
- "name": "sym-mesh-channel",
14
- "source": "./",
15
- "description": "Real-time communication and collaboration among Claude Code sessions. Turns Claude Code into a peer node on the SYM mesh — two or more sessions discover each other via Bonjour (LAN) or a WebSocket relay (cross-network) and exchange structured cognitive signals as channel notifications. Each peer has its own Ed25519 identity, SVAF content gating, and local memory. Built on the Mesh Memory Protocol (MMP), an open peer-to-peer protocol for multi-agent collective intelligence.",
16
- "version": "0.3.11",
17
- "author": {
18
- "name": "Hongwei Xu",
19
- "email": "hongwei@sym.bot"
20
- },
21
- "homepage": "https://meshcognition.org/spec/mmp",
22
- "repository": "https://github.com/sym-bot/sym-mesh-channel",
23
- "license": "Apache-2.0",
24
- "keywords": [
25
- "mesh",
26
- "p2p",
27
- "mcp",
28
- "channel",
29
- "agents",
30
- "multi-agent",
31
- "bonjour",
32
- "cognitive",
33
- "svaf",
34
- "mmp"
35
- ],
36
- "category": "agents",
37
- "tags": [
38
- "mesh",
39
- "cognitive",
40
- "channel",
41
- "mcp",
42
- "multi-agent"
43
- ]
44
- }
45
- ]
46
- }