claudemesh-cli 0.1.0 → 0.1.2
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/README.md +25 -1
- package/dist/index.js +321 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,28 @@ Run the printed command, then restart Claude Code.
|
|
|
28
28
|
claudemesh join https://claudemesh.com/join/<token>
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## Launch Claude Code
|
|
32
|
+
|
|
33
|
+
For real-time **push messages** from peers (messages injected mid-turn
|
|
34
|
+
as `<channel source="claudemesh">` system reminders), launch with:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
claudemesh launch
|
|
38
|
+
# or pass through any claude flags:
|
|
39
|
+
claudemesh launch --model opus
|
|
40
|
+
claudemesh launch --resume
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Under the hood this runs:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
claude --dangerously-load-development-channels server:claudemesh
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Plain `claude` still works — the MCP tools are available — but incoming
|
|
50
|
+
messages are **pull-only** via the `check_messages` tool instead of
|
|
51
|
+
being pushed to Claude immediately.
|
|
52
|
+
|
|
31
53
|
The invite link is generated by whoever runs the mesh. It bundles the
|
|
32
54
|
mesh id, expiry, signing key, and role. Your CLI verifies it,
|
|
33
55
|
generates a fresh keypair, enrolls you with the broker, and persists
|
|
@@ -36,7 +58,9 @@ the result to `~/.claudemesh/config.json`.
|
|
|
36
58
|
## Commands
|
|
37
59
|
|
|
38
60
|
```sh
|
|
39
|
-
claudemesh install #
|
|
61
|
+
claudemesh install # register MCP + status hooks
|
|
62
|
+
claudemesh uninstall # remove MCP + status hooks
|
|
63
|
+
claudemesh launch [args] # launch Claude Code with push messages enabled
|
|
40
64
|
claudemesh join <url> # join a mesh via invite URL
|
|
41
65
|
claudemesh list # show joined meshes + identities
|
|
42
66
|
claudemesh leave <slug> # leave a mesh
|
package/dist/index.js
CHANGED
|
@@ -55187,7 +55187,7 @@ class BrokerClient {
|
|
|
55187
55187
|
if (senderPubkey && nonce && ciphertext) {
|
|
55188
55188
|
plaintext = await decryptDirect({ nonce, ciphertext }, senderPubkey, this.mesh.secretKey);
|
|
55189
55189
|
}
|
|
55190
|
-
if (plaintext === null && ciphertext) {
|
|
55190
|
+
if (plaintext === null && ciphertext && !senderPubkey) {
|
|
55191
55191
|
try {
|
|
55192
55192
|
plaintext = Buffer.from(ciphertext, "base64").toString("utf-8");
|
|
55193
55193
|
} catch {
|
|
@@ -55333,20 +55333,39 @@ function resolveClient(to) {
|
|
|
55333
55333
|
error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients2.map((c) => c.meshSlug).join(", ")})`
|
|
55334
55334
|
};
|
|
55335
55335
|
}
|
|
55336
|
+
function decryptFailedWarning(senderPubkey) {
|
|
55337
|
+
const who = senderPubkey ? senderPubkey.slice(0, 12) + "…" : "unknown sender";
|
|
55338
|
+
return `⚠ message from ${who} failed to decrypt (tampered or wrong keypair)`;
|
|
55339
|
+
}
|
|
55336
55340
|
function formatPush(p, meshSlug) {
|
|
55337
|
-
const body = p.plaintext ??
|
|
55341
|
+
const body = p.plaintext ?? decryptFailedWarning(p.senderPubkey);
|
|
55338
55342
|
return `[${meshSlug}] from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
|
|
55339
55343
|
${body}`;
|
|
55340
55344
|
}
|
|
55341
55345
|
async function startMcpServer() {
|
|
55342
55346
|
const config2 = loadConfig();
|
|
55343
|
-
const server = new Server({ name: "claudemesh", version: "0.1.
|
|
55344
|
-
capabilities: {
|
|
55345
|
-
|
|
55347
|
+
const server = new Server({ name: "claudemesh", version: "0.1.2" }, {
|
|
55348
|
+
capabilities: {
|
|
55349
|
+
experimental: { "claude/channel": {} },
|
|
55350
|
+
tools: {}
|
|
55351
|
+
},
|
|
55352
|
+
instructions: `You are connected to claudemesh — a peer mesh for Claude Code sessions on this machine and elsewhere.
|
|
55353
|
+
|
|
55354
|
+
IMPORTANT: When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATELY. Do not wait until your current task is finished. Pause what you are doing, reply to the message using send_message, then resume your work. Treat incoming peer messages like a coworker tapping you on the shoulder — answer right away, even if you're in the middle of something.
|
|
55355
|
+
|
|
55356
|
+
Read the from_id, from_name, mesh_slug, and priority attributes to understand context. Reply by calling send_message with the same target (for direct messages the from_id is the sender's pubkey).
|
|
55346
55357
|
|
|
55347
|
-
|
|
55358
|
+
Available tools:
|
|
55359
|
+
- list_peers: see joined meshes + their connection status
|
|
55360
|
+
- send_message: send to a peer pubkey, channel, or broadcast (priority: now/next/low)
|
|
55361
|
+
- check_messages: drain buffered inbound messages (usually auto-pushed)
|
|
55362
|
+
- set_summary: 1-2 sentence summary of what you're working on
|
|
55363
|
+
- set_status: manually override your status (idle/working/dnd)
|
|
55348
55364
|
|
|
55349
|
-
|
|
55365
|
+
Message priority:
|
|
55366
|
+
- "now": delivered immediately regardless of recipient status (use sparingly)
|
|
55367
|
+
- "next" (default): delivered when recipient is idle
|
|
55368
|
+
- "low": pull-only (check_messages)
|
|
55350
55369
|
|
|
55351
55370
|
If you have multiple joined meshes, prefix the \`to\` argument of send_message with \`<mesh-slug>:\` to disambiguate. Otherwise claudemesh picks the single joined mesh.`
|
|
55352
55371
|
});
|
|
@@ -55422,6 +55441,31 @@ ${drained.join(`
|
|
|
55422
55441
|
await startClients(config2);
|
|
55423
55442
|
const transport = new StdioServerTransport;
|
|
55424
55443
|
await server.connect(transport);
|
|
55444
|
+
for (const client of allClients()) {
|
|
55445
|
+
client.onPush(async (msg) => {
|
|
55446
|
+
const fromPubkey = msg.senderPubkey || "";
|
|
55447
|
+
const fromName = fromPubkey ? `peer-${fromPubkey.slice(0, 8)}` : "unknown";
|
|
55448
|
+
const content = msg.plaintext ?? decryptFailedWarning(fromPubkey);
|
|
55449
|
+
try {
|
|
55450
|
+
await server.notification({
|
|
55451
|
+
method: "notifications/claude/channel",
|
|
55452
|
+
params: {
|
|
55453
|
+
content,
|
|
55454
|
+
meta: {
|
|
55455
|
+
from_id: fromPubkey,
|
|
55456
|
+
from_name: fromName,
|
|
55457
|
+
mesh_slug: client.meshSlug,
|
|
55458
|
+
mesh_id: client.meshId,
|
|
55459
|
+
priority: msg.priority,
|
|
55460
|
+
sent_at: msg.createdAt,
|
|
55461
|
+
delivered_at: msg.receivedAt,
|
|
55462
|
+
kind: msg.kind
|
|
55463
|
+
}
|
|
55464
|
+
}
|
|
55465
|
+
});
|
|
55466
|
+
} catch {}
|
|
55467
|
+
});
|
|
55468
|
+
}
|
|
55425
55469
|
const shutdown = () => {
|
|
55426
55470
|
stopAll();
|
|
55427
55471
|
process.exit(0);
|
|
@@ -55444,6 +55488,10 @@ import { fileURLToPath } from "node:url";
|
|
|
55444
55488
|
import { spawnSync } from "node:child_process";
|
|
55445
55489
|
var MCP_NAME = "claudemesh";
|
|
55446
55490
|
var CLAUDE_CONFIG = join2(homedir2(), ".claude.json");
|
|
55491
|
+
var CLAUDE_SETTINGS = join2(homedir2(), ".claude", "settings.json");
|
|
55492
|
+
var HOOK_COMMAND_STOP = "claudemesh hook idle";
|
|
55493
|
+
var HOOK_COMMAND_USER_PROMPT = "claudemesh hook working";
|
|
55494
|
+
var HOOK_MARKER = "claudemesh hook ";
|
|
55447
55495
|
function readClaudeConfig() {
|
|
55448
55496
|
if (!existsSync2(CLAUDE_CONFIG))
|
|
55449
55497
|
return {};
|
|
@@ -55491,7 +55539,71 @@ function buildMcpEntry(entryPath) {
|
|
|
55491
55539
|
function entriesEqual(a, b) {
|
|
55492
55540
|
return a.command === b.command && JSON.stringify(a.args ?? []) === JSON.stringify(b.args ?? []);
|
|
55493
55541
|
}
|
|
55494
|
-
function
|
|
55542
|
+
function readClaudeSettings() {
|
|
55543
|
+
if (!existsSync2(CLAUDE_SETTINGS))
|
|
55544
|
+
return {};
|
|
55545
|
+
const text2 = readFileSync2(CLAUDE_SETTINGS, "utf-8").trim();
|
|
55546
|
+
if (!text2)
|
|
55547
|
+
return {};
|
|
55548
|
+
try {
|
|
55549
|
+
return JSON.parse(text2);
|
|
55550
|
+
} catch (e) {
|
|
55551
|
+
throw new Error(`failed to parse ${CLAUDE_SETTINGS}: ${e instanceof Error ? e.message : String(e)}`);
|
|
55552
|
+
}
|
|
55553
|
+
}
|
|
55554
|
+
function writeClaudeSettings(obj) {
|
|
55555
|
+
mkdirSync2(dirname2(CLAUDE_SETTINGS), { recursive: true });
|
|
55556
|
+
writeFileSync2(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
|
|
55557
|
+
`, "utf-8");
|
|
55558
|
+
}
|
|
55559
|
+
function installHooks() {
|
|
55560
|
+
const settings = readClaudeSettings();
|
|
55561
|
+
const hooks = (settings.hooks ??= {}) ?? {};
|
|
55562
|
+
let added = 0;
|
|
55563
|
+
let unchanged = 0;
|
|
55564
|
+
const ensure = (event, command) => {
|
|
55565
|
+
const list = hooks[event] ??= [];
|
|
55566
|
+
const alreadyPresent = list.some((entry) => (entry.hooks ?? []).some((h) => h.command === command));
|
|
55567
|
+
if (alreadyPresent) {
|
|
55568
|
+
unchanged += 1;
|
|
55569
|
+
return;
|
|
55570
|
+
}
|
|
55571
|
+
list.push({ hooks: [{ type: "command", command }] });
|
|
55572
|
+
added += 1;
|
|
55573
|
+
};
|
|
55574
|
+
ensure("Stop", HOOK_COMMAND_STOP);
|
|
55575
|
+
ensure("UserPromptSubmit", HOOK_COMMAND_USER_PROMPT);
|
|
55576
|
+
settings.hooks = hooks;
|
|
55577
|
+
writeClaudeSettings(settings);
|
|
55578
|
+
return { added, unchanged };
|
|
55579
|
+
}
|
|
55580
|
+
function uninstallHooks() {
|
|
55581
|
+
if (!existsSync2(CLAUDE_SETTINGS))
|
|
55582
|
+
return 0;
|
|
55583
|
+
const settings = readClaudeSettings();
|
|
55584
|
+
const hooks = settings.hooks;
|
|
55585
|
+
if (!hooks)
|
|
55586
|
+
return 0;
|
|
55587
|
+
let removed = 0;
|
|
55588
|
+
for (const event of Object.keys(hooks)) {
|
|
55589
|
+
const kept = [];
|
|
55590
|
+
for (const entry of hooks[event] ?? []) {
|
|
55591
|
+
const filtered = (entry.hooks ?? []).filter((h) => !(h.command ?? "").includes(HOOK_MARKER));
|
|
55592
|
+
removed += (entry.hooks ?? []).length - filtered.length;
|
|
55593
|
+
if (filtered.length > 0)
|
|
55594
|
+
kept.push({ ...entry, hooks: filtered });
|
|
55595
|
+
}
|
|
55596
|
+
if (kept.length === 0)
|
|
55597
|
+
delete hooks[event];
|
|
55598
|
+
else
|
|
55599
|
+
hooks[event] = kept;
|
|
55600
|
+
}
|
|
55601
|
+
settings.hooks = hooks;
|
|
55602
|
+
writeClaudeSettings(settings);
|
|
55603
|
+
return removed;
|
|
55604
|
+
}
|
|
55605
|
+
function runInstall(args = []) {
|
|
55606
|
+
const skipHooks = args.includes("--no-hooks");
|
|
55495
55607
|
console.log("claudemesh install");
|
|
55496
55608
|
console.log("------------------");
|
|
55497
55609
|
const entry = resolveEntry();
|
|
@@ -55534,29 +55646,60 @@ function runInstall() {
|
|
|
55534
55646
|
console.log(`✓ MCP server "${MCP_NAME}" ${action}`);
|
|
55535
55647
|
console.log(dim(` config: ${CLAUDE_CONFIG}`));
|
|
55536
55648
|
console.log(dim(` command: ${desired.command}${desired.args?.length ? " " + desired.args.join(" ") : ""}`));
|
|
55649
|
+
if (!skipHooks) {
|
|
55650
|
+
try {
|
|
55651
|
+
const { added, unchanged } = installHooks();
|
|
55652
|
+
if (added > 0) {
|
|
55653
|
+
console.log(`✓ Hooks registered (Stop + UserPromptSubmit) → ${added} added, ${unchanged} already present`);
|
|
55654
|
+
} else {
|
|
55655
|
+
console.log(`✓ Hooks already registered (${unchanged} present)`);
|
|
55656
|
+
}
|
|
55657
|
+
console.log(dim(` config: ${CLAUDE_SETTINGS}`));
|
|
55658
|
+
} catch (e) {
|
|
55659
|
+
console.error(`⚠ hook registration failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
55660
|
+
console.error(" (MCP is still installed — hooks just skip. Retry with --no-hooks to suppress.)");
|
|
55661
|
+
}
|
|
55662
|
+
} else {
|
|
55663
|
+
console.log(dim("· Hooks skipped (--no-hooks)"));
|
|
55664
|
+
}
|
|
55537
55665
|
console.log("");
|
|
55538
55666
|
console.log(yellow(bold("⚠ RESTART CLAUDE CODE")) + yellow(" for MCP tools to appear."));
|
|
55539
55667
|
console.log("");
|
|
55540
55668
|
console.log(`Next: ${bold("claudemesh join https://claudemesh.com/join/<token>")}`);
|
|
55669
|
+
console.log("");
|
|
55670
|
+
console.log(yellow("⚠ For real-time push messages from peers, launch with:"));
|
|
55671
|
+
console.log(` ${bold("claudemesh launch")}` + dim(" (or: claude --dangerously-load-development-channels server:claudemesh)"));
|
|
55672
|
+
console.log(dim(" Plain `claude` still works — messages are then pull-only via check_messages."));
|
|
55541
55673
|
}
|
|
55542
55674
|
function runUninstall() {
|
|
55543
55675
|
console.log("claudemesh uninstall");
|
|
55544
55676
|
console.log("--------------------");
|
|
55545
|
-
if (
|
|
55546
|
-
|
|
55547
|
-
|
|
55677
|
+
if (existsSync2(CLAUDE_CONFIG)) {
|
|
55678
|
+
const cfg = readClaudeConfig();
|
|
55679
|
+
const servers = cfg.mcpServers;
|
|
55680
|
+
if (servers && MCP_NAME in servers) {
|
|
55681
|
+
delete servers[MCP_NAME];
|
|
55682
|
+
cfg.mcpServers = servers;
|
|
55683
|
+
writeClaudeConfig(cfg);
|
|
55684
|
+
console.log(`✓ MCP server "${MCP_NAME}" removed`);
|
|
55685
|
+
} else {
|
|
55686
|
+
console.log(`· MCP server "${MCP_NAME}" not present`);
|
|
55687
|
+
}
|
|
55688
|
+
} else {
|
|
55689
|
+
console.log(`· no ${CLAUDE_CONFIG} — MCP entry skipped`);
|
|
55548
55690
|
}
|
|
55549
|
-
|
|
55550
|
-
|
|
55551
|
-
|
|
55552
|
-
|
|
55553
|
-
|
|
55691
|
+
try {
|
|
55692
|
+
const removed = uninstallHooks();
|
|
55693
|
+
if (removed > 0) {
|
|
55694
|
+
console.log(`✓ Hooks removed (${removed} entries)`);
|
|
55695
|
+
} else {
|
|
55696
|
+
console.log("· No claudemesh hooks to remove");
|
|
55697
|
+
}
|
|
55698
|
+
} catch (e) {
|
|
55699
|
+
console.error(`⚠ hook removal failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
55554
55700
|
}
|
|
55555
|
-
|
|
55556
|
-
|
|
55557
|
-
writeClaudeConfig(cfg);
|
|
55558
|
-
console.log(`✓ MCP server "${MCP_NAME}" removed`);
|
|
55559
|
-
console.log("Restart Claude Code to drop the MCP connection.");
|
|
55701
|
+
console.log("");
|
|
55702
|
+
console.log("Restart Claude Code to drop the MCP connection + hooks.");
|
|
55560
55703
|
}
|
|
55561
55704
|
|
|
55562
55705
|
// src/invite/parse.ts
|
|
@@ -55794,6 +55937,150 @@ function runSeedTestMesh(args) {
|
|
|
55794
55937
|
console.log(`Run \`claudemesh mcp\` to connect, or register with Claude Code via \`claudemesh install\`.`);
|
|
55795
55938
|
}
|
|
55796
55939
|
|
|
55940
|
+
// src/commands/hook.ts
|
|
55941
|
+
var DEBUG = process.env.CLAUDEMESH_HOOK_DEBUG === "1";
|
|
55942
|
+
function debug(msg) {
|
|
55943
|
+
if (DEBUG)
|
|
55944
|
+
console.error(`[claudemesh-hook] ${msg}`);
|
|
55945
|
+
}
|
|
55946
|
+
function wsToHttp2(wsUrl) {
|
|
55947
|
+
try {
|
|
55948
|
+
const u = new URL(wsUrl);
|
|
55949
|
+
const httpScheme = u.protocol === "wss:" ? "https:" : "http:";
|
|
55950
|
+
return `${httpScheme}//${u.host}`;
|
|
55951
|
+
} catch {
|
|
55952
|
+
return wsUrl;
|
|
55953
|
+
}
|
|
55954
|
+
}
|
|
55955
|
+
async function readStdinJson() {
|
|
55956
|
+
if (process.stdin.isTTY)
|
|
55957
|
+
return {};
|
|
55958
|
+
const chunks = [];
|
|
55959
|
+
const reader = process.stdin;
|
|
55960
|
+
try {
|
|
55961
|
+
for await (const chunk of reader) {
|
|
55962
|
+
chunks.push(chunk);
|
|
55963
|
+
if (chunks.reduce((n, c) => n + c.length, 0) > 256 * 1024)
|
|
55964
|
+
break;
|
|
55965
|
+
}
|
|
55966
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
55967
|
+
if (!raw)
|
|
55968
|
+
return {};
|
|
55969
|
+
return JSON.parse(raw);
|
|
55970
|
+
} catch {
|
|
55971
|
+
return {};
|
|
55972
|
+
}
|
|
55973
|
+
}
|
|
55974
|
+
async function postHook(brokerWsUrl, body) {
|
|
55975
|
+
const base = wsToHttp2(brokerWsUrl);
|
|
55976
|
+
try {
|
|
55977
|
+
const controller = new AbortController;
|
|
55978
|
+
const t = setTimeout(() => controller.abort(), 1000);
|
|
55979
|
+
await fetch(`${base}/hook/set-status`, {
|
|
55980
|
+
method: "POST",
|
|
55981
|
+
headers: { "Content-Type": "application/json" },
|
|
55982
|
+
body: JSON.stringify(body),
|
|
55983
|
+
signal: controller.signal
|
|
55984
|
+
}).finally(() => clearTimeout(t));
|
|
55985
|
+
} catch (e) {
|
|
55986
|
+
debug(`post failed ${base}: ${e instanceof Error ? e.message : e}`);
|
|
55987
|
+
}
|
|
55988
|
+
}
|
|
55989
|
+
async function runHook(args) {
|
|
55990
|
+
const status = args[0];
|
|
55991
|
+
if (!status || !["idle", "working", "dnd"].includes(status)) {
|
|
55992
|
+
process.exit(0);
|
|
55993
|
+
}
|
|
55994
|
+
const stdinTimeout = new Promise((r) => setTimeout(() => r({}), 500));
|
|
55995
|
+
const payload = await Promise.race([readStdinJson(), stdinTimeout]);
|
|
55996
|
+
const cwd = typeof payload.cwd === "string" && payload.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
55997
|
+
const sessionId = typeof payload.session_id === "string" && payload.session_id || "";
|
|
55998
|
+
let config2;
|
|
55999
|
+
try {
|
|
56000
|
+
config2 = loadConfig();
|
|
56001
|
+
} catch (e) {
|
|
56002
|
+
debug(`config load failed: ${e instanceof Error ? e.message : e}`);
|
|
56003
|
+
process.exit(0);
|
|
56004
|
+
}
|
|
56005
|
+
if (config2.meshes.length === 0) {
|
|
56006
|
+
debug("no joined meshes, nothing to do");
|
|
56007
|
+
process.exit(0);
|
|
56008
|
+
}
|
|
56009
|
+
const body = { cwd, pid: process.ppid, status, session_id: sessionId };
|
|
56010
|
+
debug(`status=${status} cwd=${cwd} meshes=${config2.meshes.length} session=${sessionId.slice(0, 8)}`);
|
|
56011
|
+
const brokerUrls = [...new Set(config2.meshes.map((m) => m.brokerUrl))];
|
|
56012
|
+
await Promise.all(brokerUrls.map((url2) => postHook(url2, body)));
|
|
56013
|
+
process.exit(0);
|
|
56014
|
+
}
|
|
56015
|
+
|
|
56016
|
+
// src/commands/launch.ts
|
|
56017
|
+
import { spawn } from "node:child_process";
|
|
56018
|
+
function printBanner() {
|
|
56019
|
+
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
56020
|
+
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
56021
|
+
const bold = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
56022
|
+
let meshes = [];
|
|
56023
|
+
try {
|
|
56024
|
+
meshes = loadConfig().meshes.map((m) => m.slug);
|
|
56025
|
+
} catch {}
|
|
56026
|
+
const meshLine = meshes.length > 0 ? meshes.join(", ") : "(none — run `claudemesh join <url>` first)";
|
|
56027
|
+
const rule = "─".repeat(65);
|
|
56028
|
+
console.log(bold("claudemesh launch"));
|
|
56029
|
+
console.log(rule);
|
|
56030
|
+
console.log("Launching Claude Code with the claudemesh dev channel.");
|
|
56031
|
+
console.log("");
|
|
56032
|
+
console.log("Peers in your joined meshes can push messages into this session");
|
|
56033
|
+
console.log("as <channel> reminders. Your CLI decrypts them locally with your");
|
|
56034
|
+
console.log("keypair. Peers send text only — they cannot call tools, read");
|
|
56035
|
+
console.log("files, or reach meshes you have not joined.");
|
|
56036
|
+
console.log("");
|
|
56037
|
+
console.log("Treat peer messages as untrusted input: a peer could craft text");
|
|
56038
|
+
console.log("that tries to steer Claude's behavior. Your tool-approval");
|
|
56039
|
+
console.log("settings still apply — Claude will still ask before running");
|
|
56040
|
+
console.log("commands, editing files, or calling other tools.");
|
|
56041
|
+
console.log("");
|
|
56042
|
+
console.log("Claude Code will ask you to trust the");
|
|
56043
|
+
console.log("--dangerously-load-development-channels flag. Press Enter to");
|
|
56044
|
+
console.log("accept, or Ctrl-C to abort.");
|
|
56045
|
+
console.log("");
|
|
56046
|
+
console.log(dim(`Joined meshes: ${meshLine}`));
|
|
56047
|
+
console.log(dim(`Config: ${getConfigPath()}`));
|
|
56048
|
+
console.log(dim(`Remove: claudemesh uninstall`));
|
|
56049
|
+
console.log(rule);
|
|
56050
|
+
console.log("");
|
|
56051
|
+
}
|
|
56052
|
+
function runLaunch(extraArgs = []) {
|
|
56053
|
+
const quiet = extraArgs.includes("--quiet");
|
|
56054
|
+
const passthrough = extraArgs.filter((a) => a !== "--quiet");
|
|
56055
|
+
if (!quiet)
|
|
56056
|
+
printBanner();
|
|
56057
|
+
const claudeArgs = [
|
|
56058
|
+
"--dangerously-load-development-channels",
|
|
56059
|
+
"server:claudemesh",
|
|
56060
|
+
...passthrough
|
|
56061
|
+
];
|
|
56062
|
+
const isWindows = process.platform === "win32";
|
|
56063
|
+
const child = spawn("claude", claudeArgs, {
|
|
56064
|
+
stdio: "inherit",
|
|
56065
|
+
shell: isWindows
|
|
56066
|
+
});
|
|
56067
|
+
child.on("error", (err) => {
|
|
56068
|
+
if (err.code === "ENOENT") {
|
|
56069
|
+
console.error("✗ `claude` not found on PATH. Install Claude Code first: https://claude.com/claude-code");
|
|
56070
|
+
} else {
|
|
56071
|
+
console.error(`✗ failed to launch claude: ${err.message}`);
|
|
56072
|
+
}
|
|
56073
|
+
process.exit(1);
|
|
56074
|
+
});
|
|
56075
|
+
child.on("exit", (code, signal) => {
|
|
56076
|
+
if (signal) {
|
|
56077
|
+
process.kill(process.pid, signal);
|
|
56078
|
+
return;
|
|
56079
|
+
}
|
|
56080
|
+
process.exit(code ?? 0);
|
|
56081
|
+
});
|
|
56082
|
+
}
|
|
56083
|
+
|
|
55797
56084
|
// src/index.ts
|
|
55798
56085
|
var HELP = `claudemesh — peer mesh for Claude Code sessions
|
|
55799
56086
|
|
|
@@ -55801,8 +56088,12 @@ Usage:
|
|
|
55801
56088
|
claudemesh <command> [args]
|
|
55802
56089
|
|
|
55803
56090
|
Commands:
|
|
55804
|
-
install Register
|
|
55805
|
-
|
|
56091
|
+
install Register MCP + Stop/UserPromptSubmit status hooks
|
|
56092
|
+
(add --no-hooks for bare MCP registration)
|
|
56093
|
+
uninstall Remove MCP server + hooks
|
|
56094
|
+
launch [args] Launch Claude Code with real-time push messages enabled
|
|
56095
|
+
(add --quiet to skip the info banner; passes through
|
|
56096
|
+
extra flags, e.g. --model, --resume)
|
|
55806
56097
|
join <url> Join a mesh via https://claudemesh.com/join/... URL
|
|
55807
56098
|
list Show all joined meshes
|
|
55808
56099
|
leave <slug> Leave a joined mesh
|
|
@@ -55823,11 +56114,17 @@ async function main() {
|
|
|
55823
56114
|
await startMcpServer();
|
|
55824
56115
|
return;
|
|
55825
56116
|
case "install":
|
|
55826
|
-
runInstall();
|
|
56117
|
+
runInstall(args);
|
|
55827
56118
|
return;
|
|
55828
56119
|
case "uninstall":
|
|
55829
56120
|
runUninstall();
|
|
55830
56121
|
return;
|
|
56122
|
+
case "hook":
|
|
56123
|
+
await runHook(args);
|
|
56124
|
+
return;
|
|
56125
|
+
case "launch":
|
|
56126
|
+
runLaunch(args);
|
|
56127
|
+
return;
|
|
55831
56128
|
case "join":
|
|
55832
56129
|
await runJoin(args);
|
|
55833
56130
|
return;
|