claudemesh-cli 0.9.4 → 0.9.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.
Files changed (2) hide show
  1. package/dist/index.js +247 -144
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -40069,7 +40069,7 @@ function moveTo(row, col) {
40069
40069
  function visibleLength(s) {
40070
40070
  return s.replace(/\x1b\[[^m]*m/g, "").length;
40071
40071
  }
40072
- var isTTY, esc2 = (code) => (s) => isTTY ? `${code}${s}\x1B[0m` : s, orange, clay, amber, bold2, dim, green, yellow, red, cyan2, boldOrange, HIDE_CURSOR, SHOW_CURSOR, CLEAR_SCREEN, CLEAR_LINE;
40072
+ var isTTY, esc2 = (code) => (s) => isTTY ? `${code}${s}\x1B[0m` : s, orange, clay, amber, bold2, dim, green2, yellow, red, cyan2, boldOrange, HIDE_CURSOR, SHOW_CURSOR, CLEAR_SCREEN, CLEAR_LINE;
40073
40073
  var init_colors = __esm(() => {
40074
40074
  isTTY = process.stdout.isTTY && !process.env.NO_COLOR && process.env.TERM !== "dumb";
40075
40075
  orange = esc2("\x1B[38;5;208m");
@@ -40077,7 +40077,7 @@ var init_colors = __esm(() => {
40077
40077
  amber = esc2("\x1B[38;5;214m");
40078
40078
  bold2 = esc2("\x1B[1m");
40079
40079
  dim = esc2("\x1B[2m");
40080
- green = esc2("\x1B[32m");
40080
+ green2 = esc2("\x1B[32m");
40081
40081
  yellow = esc2("\x1B[33m");
40082
40082
  red = esc2("\x1B[31m");
40083
40083
  cyan2 = esc2("\x1B[36m");
@@ -40093,7 +40093,7 @@ var package_default;
40093
40093
  var init_package = __esm(() => {
40094
40094
  package_default = {
40095
40095
  name: "claudemesh-cli",
40096
- version: "0.9.4",
40096
+ version: "0.9.5",
40097
40097
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
40098
40098
  keywords: [
40099
40099
  "claude-code",
@@ -53943,14 +53943,24 @@ function center(s) {
53943
53943
  function writeCentered(row, s) {
53944
53944
  process.stdout.write(moveTo(row, 1) + center(s) + CLEAR_LINE);
53945
53945
  }
53946
- function drawTopBar() {
53946
+ function drawTopBar(extra) {
53947
53947
  const { cols } = termSize();
53948
- const left = orange(`claudemesh v${VERSION}`);
53949
- const right = dim("claudemesh.com");
53950
- const leftVis = visibleLength(left);
53951
- const rightVis = visibleLength(right);
53952
- const gap = Math.max(1, cols - leftVis - rightVis);
53953
- process.stdout.write(moveTo(1, 1) + left + " ".repeat(gap) + right + CLEAR_LINE);
53948
+ const bg = "\x1B[48;5;208m\x1B[30m";
53949
+ const reset = "\x1B[0m";
53950
+ const left = ` claudemesh v${VERSION}`;
53951
+ const right = `claudemesh.com `;
53952
+ const mid = extra ? ` ${extra}` : "";
53953
+ const gap = Math.max(1, cols - left.length - right.length - mid.length);
53954
+ process.stdout.write(moveTo(1, 1) + bg + left + mid + " ".repeat(gap) + right + reset);
53955
+ }
53956
+ function drawBottomBar(left, right) {
53957
+ const { cols, rows } = termSize();
53958
+ const bg = "\x1B[48;5;208m\x1B[30m";
53959
+ const reset = "\x1B[0m";
53960
+ const l = ` ${left}`;
53961
+ const r = right ? `${right} ` : "";
53962
+ const gap = Math.max(1, cols - l.length - r.length);
53963
+ process.stdout.write(moveTo(rows, 1) + bg + l + " ".repeat(gap) + r + reset);
53954
53964
  }
53955
53965
  function enterFullScreen() {
53956
53966
  process.stdout.write(HIDE_CURSOR + CLEAR_SCREEN);
@@ -54000,6 +54010,87 @@ async function menuSelect(opts) {
54000
54010
  stdin.on("data", onData);
54001
54011
  });
54002
54012
  }
54013
+ async function textInput(opts) {
54014
+ const { label, row, placeholder } = opts;
54015
+ let value = "";
54016
+ const render = () => {
54017
+ const display = value || dim(placeholder ?? "");
54018
+ const cursor = SHOW_CURSOR;
54019
+ writeCentered(row, `${bold2(label)} ${display}${CLEAR_LINE}`);
54020
+ const fullText = `${label.replace(/\x1b\[[^m]*m/g, "")} ${value}`;
54021
+ const { cols } = termSize();
54022
+ const leftPad = Math.max(0, Math.floor((cols - fullText.length) / 2));
54023
+ process.stdout.write(moveTo(row, leftPad + fullText.length + 1) + cursor);
54024
+ };
54025
+ process.stdout.write(SHOW_CURSOR);
54026
+ render();
54027
+ return new Promise((resolve2) => {
54028
+ const stdin = process.stdin;
54029
+ const wasRaw = stdin.isRaw;
54030
+ stdin.setRawMode(true);
54031
+ stdin.resume();
54032
+ stdin.setEncoding("utf8");
54033
+ const onData = (key) => {
54034
+ if (key === "\r" || key === `
54035
+ `) {
54036
+ stdin.removeListener("data", onData);
54037
+ stdin.setRawMode(wasRaw ?? false);
54038
+ stdin.pause();
54039
+ process.stdout.write(HIDE_CURSOR);
54040
+ const final = value || dim("skipped");
54041
+ writeCentered(row, `${label} ${green("✓")} ${final}${CLEAR_LINE}`);
54042
+ resolve2(value);
54043
+ } else if (key === "" || key === "\b") {
54044
+ value = value.slice(0, -1);
54045
+ render();
54046
+ } else if (key === "\x03") {
54047
+ stdin.removeListener("data", onData);
54048
+ stdin.setRawMode(wasRaw ?? false);
54049
+ exitFullScreen();
54050
+ process.exit(0);
54051
+ } else if (key === "\x1B") {
54052
+ stdin.removeListener("data", onData);
54053
+ stdin.setRawMode(wasRaw ?? false);
54054
+ stdin.pause();
54055
+ process.stdout.write(HIDE_CURSOR);
54056
+ writeCentered(row, `${label} ${dim("skipped")}${CLEAR_LINE}`);
54057
+ resolve2("");
54058
+ } else if (key >= " " && key <= "~") {
54059
+ value += key;
54060
+ render();
54061
+ }
54062
+ };
54063
+ stdin.on("data", onData);
54064
+ });
54065
+ }
54066
+ async function confirmPrompt(opts) {
54067
+ const { message, row } = opts;
54068
+ const defaultYes = opts.defaultYes ?? true;
54069
+ const hint = defaultYes ? `${bold2("Y")}/${dim("n")}` : `${dim("y")}/${bold2("N")}`;
54070
+ writeCentered(row, `${message} [${hint}]`);
54071
+ process.stdout.write(SHOW_CURSOR);
54072
+ return new Promise((resolve2) => {
54073
+ const stdin = process.stdin;
54074
+ const wasRaw = stdin.isRaw;
54075
+ stdin.setRawMode(true);
54076
+ stdin.resume();
54077
+ stdin.setEncoding("utf8");
54078
+ const onData = (key) => {
54079
+ stdin.removeListener("data", onData);
54080
+ stdin.setRawMode(wasRaw ?? false);
54081
+ stdin.pause();
54082
+ process.stdout.write(HIDE_CURSOR);
54083
+ if (key === "\x03") {
54084
+ exitFullScreen();
54085
+ process.exit(0);
54086
+ }
54087
+ const yes = key === "y" || key === "Y" || key === "\r" && defaultYes;
54088
+ writeCentered(row, `${message} ${yes ? green("✓ yes") : dim("✗ no")}${CLEAR_LINE}`);
54089
+ resolve2(yes);
54090
+ };
54091
+ stdin.on("data", onData);
54092
+ });
54093
+ }
54003
54094
 
54004
54095
  // src/commands/launch.ts
54005
54096
  init_spinner();
@@ -54034,45 +54125,115 @@ function parseGroupsString(raw) {
54034
54125
  return { name: token.slice(0, idx), role: token.slice(idx + 1) };
54035
54126
  });
54036
54127
  }
54037
- function askLine(prompt) {
54038
- const rl = createInterface({ input: process.stdin, output: process.stdout });
54039
- return new Promise((resolve2) => {
54040
- rl.question(prompt, (answer) => {
54041
- rl.close();
54042
- resolve2(answer.trim());
54043
- });
54044
- });
54045
- }
54046
- async function confirmPermissions() {
54047
- const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
54048
- const bold3 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
54049
- const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
54050
- const yellow2 = (s) => useColor ? `\x1B[33m${s}\x1B[39m` : s;
54051
- console.log(yellow2(bold3(" Autonomous mode")));
54052
- console.log("");
54053
- console.log(" Claude will run with --dangerously-skip-permissions, bypassing");
54054
- console.log(" ALL permission prompts — not just claudemesh tools.");
54055
- console.log(" Peers exchange text only — no file access, no tool calls.");
54056
- console.log("");
54057
- console.log(dim2(" Without -y: only claudemesh tools are pre-approved (via allowedTools)."));
54058
- console.log(dim2(" Use -y for autonomous agents. Omit it for shared/multi-person meshes."));
54059
- console.log("");
54060
- const rl = createInterface({ input: process.stdin, output: process.stdout });
54061
- return new Promise((resolve2, reject) => {
54062
- rl.question(` ${bold3("Continue?")} [Y/n] `, (answer) => {
54063
- rl.close();
54064
- const a = answer.trim().toLowerCase();
54065
- if (a === "" || a === "y" || a === "yes") {
54066
- resolve2();
54067
- } else {
54068
- console.log(`
54069
- Aborted. Run without autonomous mode:`);
54070
- console.log(` claude --dangerously-load-development-channels server:claudemesh
54071
- `);
54072
- process.exit(0);
54128
+ async function runLaunchWizard(opts) {
54129
+ if (!process.stdout.isTTY) {
54130
+ return {
54131
+ role: opts.existingRole,
54132
+ groups: opts.existingGroups,
54133
+ messageMode: opts.existingMessageMode ?? "push",
54134
+ skipPermissions: opts.skipPermConfirm
54135
+ };
54136
+ }
54137
+ const { rows } = termSize();
54138
+ enterFullScreen();
54139
+ drawTopBar();
54140
+ drawBottomBar("↑↓ navigate ⏎ select esc skip ctrl-c quit", `mesh: ${opts.meshSlug}`);
54141
+ const logoTop = Math.floor((rows - FRAME_HEIGHT - 16) / 2);
54142
+ const brandRow = logoTop + FRAME_HEIGHT + 1;
54143
+ const subtitleRow = brandRow + 1;
54144
+ const formRow = subtitleRow + 2;
54145
+ writeCentered(brandRow, boldOrange("claudemesh"));
54146
+ writeCentered(subtitleRow, dim("peer mesh for Claude Code"));
54147
+ const spinner = createSpinner({
54148
+ render(lines) {
54149
+ for (let i = 0;i < lines.length; i++) {
54150
+ writeCentered(logoTop + i, lines[i]);
54073
54151
  }
54074
- });
54152
+ },
54153
+ interval: 70
54075
54154
  });
54155
+ spinner.start();
54156
+ let row = formRow;
54157
+ writeCentered(row, `Directory ${green2("✓")} ${process.cwd()}`);
54158
+ row++;
54159
+ writeCentered(row, `Name ${green2("✓")} ${opts.displayName}`);
54160
+ row++;
54161
+ writeCentered(row, `Mesh ${green2("✓")} ${opts.meshSlug}`);
54162
+ row += 2;
54163
+ let role = opts.existingRole;
54164
+ let groups = opts.existingGroups;
54165
+ let messageMode = opts.existingMessageMode ?? "push";
54166
+ if (role === null) {
54167
+ spinner.stop();
54168
+ const answer = await textInput({ label: "Role", row, placeholder: "optional — press Enter to skip" });
54169
+ if (answer)
54170
+ role = answer;
54171
+ spinner.start();
54172
+ row++;
54173
+ } else {
54174
+ writeCentered(row, `Role ${green2("✓")} ${role}`);
54175
+ row++;
54176
+ }
54177
+ if (groups.length === 0) {
54178
+ spinner.stop();
54179
+ const answer = await textInput({ label: "Groups", row, placeholder: "comma-separated, optional" });
54180
+ if (answer)
54181
+ groups = parseGroupsString(answer);
54182
+ spinner.start();
54183
+ row++;
54184
+ } else {
54185
+ const tags = groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ");
54186
+ writeCentered(row, `Groups ${green2("✓")} ${tags}`);
54187
+ row++;
54188
+ }
54189
+ if (opts.existingMessageMode === null) {
54190
+ row++;
54191
+ spinner.stop();
54192
+ const choice = await menuSelect({
54193
+ title: "Message mode",
54194
+ items: [
54195
+ "Push (real-time, peers can interrupt)",
54196
+ "Inbox (held until you check)",
54197
+ "Off (tools only, no messages)"
54198
+ ],
54199
+ row
54200
+ });
54201
+ messageMode = ["push", "inbox", "off"][choice];
54202
+ spinner.start();
54203
+ row += 5;
54204
+ } else {
54205
+ writeCentered(row, `Messages ${green2("✓")} ${messageMode}`);
54206
+ row++;
54207
+ }
54208
+ let skipPermissions = opts.skipPermConfirm;
54209
+ if (!skipPermissions) {
54210
+ row++;
54211
+ spinner.stop();
54212
+ writeCentered(row, dim("Claude will run with --dangerously-skip-permissions,"));
54213
+ writeCentered(row + 1, dim("bypassing ALL permission prompts — not just claudemesh."));
54214
+ row += 3;
54215
+ const confirmed = await confirmPrompt({
54216
+ message: boldOrange("Autonomous mode?"),
54217
+ row,
54218
+ defaultYes: true
54219
+ });
54220
+ if (!confirmed) {
54221
+ exitFullScreen();
54222
+ console.log(" Run without autonomous mode:");
54223
+ console.log(` claude --dangerously-load-development-channels server:claudemesh
54224
+ `);
54225
+ process.exit(0);
54226
+ }
54227
+ skipPermissions = true;
54228
+ spinner.start();
54229
+ }
54230
+ row += 2;
54231
+ writeCentered(row, dim("Launching Claude Code..."));
54232
+ drawBottomBar(`${opts.displayName} on ${opts.meshSlug}`, `mode: ${messageMode}`);
54233
+ await new Promise((r) => setTimeout(r, 800));
54234
+ spinner.stop();
54235
+ exitFullScreen();
54236
+ return { role, groups, messageMode, skipPermissions };
54076
54237
  }
54077
54238
  function printBanner(name, meshSlug, role, groups, messageMode) {
54078
54239
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
@@ -54095,44 +54256,6 @@ function printBanner(name, meshSlug, role, groups, messageMode) {
54095
54256
  console.log(rule);
54096
54257
  console.log("");
54097
54258
  }
54098
- async function showLaunchSplash(name, meshSlug, role, groups, messageMode) {
54099
- if (!process.stdout.isTTY)
54100
- return;
54101
- const { rows } = termSize();
54102
- enterFullScreen();
54103
- drawTopBar();
54104
- const logoTop = Math.floor((rows - FRAME_HEIGHT - 10) / 2);
54105
- const brandRow = logoTop + FRAME_HEIGHT + 1;
54106
- const subtitleRow = brandRow + 1;
54107
- const infoRow = subtitleRow + 2;
54108
- writeCentered(brandRow, boldOrange("claudemesh"));
54109
- writeCentered(subtitleRow, dim("peer mesh for Claude Code"));
54110
- const spinner = createSpinner({
54111
- render(lines) {
54112
- for (let i = 0;i < lines.length; i++) {
54113
- writeCentered(logoTop + i, lines[i]);
54114
- }
54115
- },
54116
- interval: 70
54117
- });
54118
- spinner.start();
54119
- const roleSuffix = role ? ` (${role})` : "";
54120
- const groupStr = groups.length ? " " + groups.map((g) => dim(`@${g.name}`)).join(", ") : "";
54121
- const steps = [
54122
- { label: `Identity`, value: `${name}${roleSuffix}` },
54123
- { label: `Mesh`, value: meshSlug },
54124
- { label: `Messages`, value: messageMode }
54125
- ];
54126
- for (let i = 0;i < steps.length; i++) {
54127
- await new Promise((r) => setTimeout(r, 300));
54128
- writeCentered(infoRow + i, `${steps[i].label} ${green("✓")} ${steps[i].value}${i === 0 ? groupStr : ""}`);
54129
- }
54130
- await new Promise((r) => setTimeout(r, 400));
54131
- writeCentered(infoRow + steps.length + 1, dim("Launching Claude Code..."));
54132
- await new Promise((r) => setTimeout(r, 600));
54133
- spinner.stop();
54134
- exitFullScreen();
54135
- }
54136
54259
  async function runLaunch(flags, rawArgs) {
54137
54260
  const dashIdx = rawArgs.indexOf("--");
54138
54261
  const claudePassthrough = dashIdx >= 0 ? rawArgs.slice(dashIdx + 1) : [];
@@ -54184,7 +54307,7 @@ async function runLaunch(flags, rawArgs) {
54184
54307
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
54185
54308
  const bold3 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
54186
54309
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
54187
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54310
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54188
54311
  const code = generatePairingCode();
54189
54312
  const listener = await startCallbackListener();
54190
54313
  const url = `https://claudemesh.com/cli-auth?port=${listener.port}&code=${code}&action=sync`;
@@ -54243,7 +54366,7 @@ async function runLaunch(flags, rawArgs) {
54243
54366
  saveConfig2(config2);
54244
54367
  justSynced = true;
54245
54368
  console.log(`
54246
- ${green2("✓")} Synced ${result.meshes.length} mesh(es): ${result.meshes.map((m) => m.slug).join(", ")}
54369
+ ${green3("✓")} Synced ${result.meshes.length} mesh(es): ${result.meshes.map((m) => m.slug).join(", ")}
54247
54370
  `);
54248
54371
  }
54249
54372
  if (config2.meshes.length === 0) {
@@ -54266,34 +54389,18 @@ async function runLaunch(flags, rawArgs) {
54266
54389
  let parsedGroups = args.groups ? parseGroupsString(args.groups) : [];
54267
54390
  let messageMode = args.messageMode ?? "push";
54268
54391
  if (!args.quiet && !justSynced) {
54269
- if (role === null) {
54270
- const answer = await askLine(" Role (optional): ");
54271
- if (answer)
54272
- role = answer;
54273
- }
54274
- if (parsedGroups.length === 0 && args.groups === null) {
54275
- const answer = await askLine(" Groups (comma-separated, optional): ");
54276
- if (answer)
54277
- parsedGroups = parseGroupsString(answer);
54278
- }
54279
- if (args.messageMode === null) {
54280
- console.log(`
54281
- Message mode:`);
54282
- console.log(" 1) Push (real-time, peers can interrupt your work)");
54283
- console.log(" 2) Inbox (held until you check, notification only)");
54284
- console.log(" 3) Off (tools only, no messages)");
54285
- console.log("");
54286
- const answer = await askLine(" Choice [1]: ");
54287
- const choice = parseInt(answer || "1", 10);
54288
- if (choice === 2)
54289
- messageMode = "inbox";
54290
- else if (choice === 3)
54291
- messageMode = "off";
54292
- else
54293
- messageMode = "push";
54294
- }
54295
- if (role || parsedGroups.length)
54296
- console.log("");
54392
+ const wizardResult = await runLaunchWizard({
54393
+ displayName,
54394
+ meshSlug: mesh.slug,
54395
+ existingRole: args.role,
54396
+ existingGroups: parsedGroups,
54397
+ existingMessageMode: args.messageMode ?? null,
54398
+ skipPermConfirm: args.skipPermConfirm
54399
+ });
54400
+ role = wizardResult.role;
54401
+ parsedGroups = wizardResult.groups;
54402
+ messageMode = wizardResult.messageMode;
54403
+ args.skipPermConfirm = wizardResult.skipPermissions;
54297
54404
  }
54298
54405
  const tmpBase = tmpdir();
54299
54406
  try {
@@ -54356,12 +54463,7 @@ async function runLaunch(flags, rawArgs) {
54356
54463
  writeFileSync4(join4(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
54357
54464
  `, "utf-8");
54358
54465
  if (!args.quiet) {
54359
- await showLaunchSplash(displayName, mesh.slug, role, parsedGroups, messageMode);
54360
54466
  printBanner(displayName, mesh.slug, role, parsedGroups, messageMode);
54361
- if (!args.skipPermConfirm) {
54362
- await confirmPermissions();
54363
- args.skipPermConfirm = true;
54364
- }
54365
54467
  }
54366
54468
  const meshMcpEntries = [];
54367
54469
  if (serviceCatalog.length > 0) {
@@ -54530,7 +54632,7 @@ async function probeBroker(url, timeoutMs = 4000) {
54530
54632
  async function runStatus() {
54531
54633
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
54532
54634
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
54533
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54635
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54534
54636
  const red2 = (s) => useColor ? `\x1B[31m${s}\x1B[39m` : s;
54535
54637
  console.log(`claudemesh status (v${VERSION})`);
54536
54638
  console.log("─".repeat(60));
@@ -54562,7 +54664,7 @@ async function runStatus() {
54562
54664
  error: probe.error
54563
54665
  });
54564
54666
  if (probe.ok) {
54565
- console.log(green2("reachable"));
54667
+ console.log(green3("reachable"));
54566
54668
  } else {
54567
54669
  console.log(red2(`unreachable (${probe.error})`));
54568
54670
  }
@@ -54574,7 +54676,7 @@ async function runStatus() {
54574
54676
  const allOk = results.every((r) => r.reachable);
54575
54677
  console.log("");
54576
54678
  if (allOk) {
54577
- console.log(green2("All meshes reachable."));
54679
+ console.log(green3("All meshes reachable."));
54578
54680
  process.exit(0);
54579
54681
  } else {
54580
54682
  const broken = results.filter((r) => !r.reachable).length;
@@ -54735,7 +54837,7 @@ function checkKeypairs() {
54735
54837
  async function runDoctor() {
54736
54838
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
54737
54839
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
54738
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54840
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54739
54841
  const red2 = (s) => useColor ? `\x1B[31m${s}\x1B[39m` : s;
54740
54842
  console.log(`claudemesh doctor (v${VERSION})`);
54741
54843
  console.log("─".repeat(60));
@@ -54748,7 +54850,7 @@ async function runDoctor() {
54748
54850
  checkKeypairs()
54749
54851
  ];
54750
54852
  for (const c of checks3) {
54751
- const mark = c.pass ? green2("✓") : red2("✗");
54853
+ const mark = c.pass ? green3("✓") : red2("✗");
54752
54854
  const detail = c.detail ? dim2(` (${c.detail})`) : "";
54753
54855
  console.log(`${mark} ${c.name}${detail}`);
54754
54856
  if (!c.pass && c.fix) {
@@ -54758,7 +54860,7 @@ async function runDoctor() {
54758
54860
  const failing = checks3.filter((c) => !c.pass);
54759
54861
  console.log("");
54760
54862
  if (failing.length === 0) {
54761
- console.log(green2("All checks passed."));
54863
+ console.log(green3("All checks passed."));
54762
54864
  process.exit(0);
54763
54865
  } else {
54764
54866
  console.log(red2(`${failing.length} check(s) failed.`));
@@ -54799,6 +54901,7 @@ async function runWelcome() {
54799
54901
  const { rows } = termSize();
54800
54902
  enterFullScreen();
54801
54903
  drawTopBar();
54904
+ drawBottomBar("↑↓ navigate ⏎ select ctrl-c quit", "claudemesh.com");
54802
54905
  const logoTop = Math.floor((rows - FRAME_HEIGHT - 10) / 2);
54803
54906
  const brandRow = logoTop + FRAME_HEIGHT + 1;
54804
54907
  const subtitleRow = brandRow + 1;
@@ -54830,14 +54933,14 @@ async function runWelcome() {
54830
54933
  });
54831
54934
  exitFullScreen();
54832
54935
  if (choice === 0) {
54833
- console.log(green("$") + ` claudemesh install
54936
+ console.log(green2("$") + ` claudemesh install
54834
54937
  `);
54835
54938
  }
54836
54939
  break;
54837
54940
  }
54838
54941
  case "no-meshes": {
54839
- writeCentered(contentRow, green("✓") + " MCP registered. Now join a mesh.");
54840
- writeCentered(contentRow + 2, dim("MCP server ") + green("✓") + dim(" registered"));
54942
+ writeCentered(contentRow, green2("✓") + " MCP registered. Now join a mesh.");
54943
+ writeCentered(contentRow + 2, dim("MCP server ") + green2("✓") + dim(" registered"));
54841
54944
  writeCentered(contentRow + 3, dim("Mesh ") + yellow("○") + dim(" none joined"));
54842
54945
  menuRow = contentRow + 5;
54843
54946
  spinner.stop();
@@ -54852,11 +54955,11 @@ async function runWelcome() {
54852
54955
  });
54853
54956
  exitFullScreen();
54854
54957
  if (choice === 0) {
54855
- console.log(green("$") + ` claudemesh join https://claudemesh.com/join/<token>
54958
+ console.log(green2("$") + ` claudemesh join https://claudemesh.com/join/<token>
54856
54959
  `);
54857
54960
  console.log(dim(" Don't have an invite? Create one at ") + bold2("https://claudemesh.com") + dim(" or ask a mesh owner."));
54858
54961
  } else if (choice === 1) {
54859
- console.log(green("$") + ` claudemesh create
54962
+ console.log(green2("$") + ` claudemesh create
54860
54963
  `);
54861
54964
  }
54862
54965
  break;
@@ -54864,8 +54967,8 @@ async function runWelcome() {
54864
54967
  case "ready": {
54865
54968
  const cfg = loadConfig();
54866
54969
  const meshNames = cfg.meshes.map((m) => m.slug).join(", ");
54867
- writeCentered(contentRow, green("✓") + " MCP registered");
54868
- writeCentered(contentRow + 1, green("✓") + ` ${cfg.meshes.length} mesh(es): ${meshNames}`);
54970
+ writeCentered(contentRow, green2("✓") + " MCP registered");
54971
+ writeCentered(contentRow + 1, green2("✓") + ` ${cfg.meshes.length} mesh(es): ${meshNames}`);
54869
54972
  writeCentered(contentRow + 2, "");
54870
54973
  writeCentered(contentRow + 3, bold2("Ready to launch."));
54871
54974
  menuRow = contentRow + 5;
@@ -54884,19 +54987,19 @@ async function runWelcome() {
54884
54987
  exitFullScreen();
54885
54988
  switch (choice) {
54886
54989
  case 0:
54887
- console.log(green("$") + ` claudemesh launch
54990
+ console.log(green2("$") + ` claudemesh launch
54888
54991
  `);
54889
54992
  break;
54890
54993
  case 1:
54891
- console.log(green("$") + ` claudemesh peers
54994
+ console.log(green2("$") + ` claudemesh peers
54892
54995
  `);
54893
54996
  break;
54894
54997
  case 2:
54895
- console.log(green("$") + ` claudemesh status
54998
+ console.log(green2("$") + ` claudemesh status
54896
54999
  `);
54897
55000
  break;
54898
55001
  case 3:
54899
- console.log(green("$") + ` claudemesh doctor
55002
+ console.log(green2("$") + ` claudemesh doctor
54900
55003
  `);
54901
55004
  break;
54902
55005
  }
@@ -54913,7 +55016,7 @@ async function runWelcome() {
54913
55016
  });
54914
55017
  exitFullScreen();
54915
55018
  if (choice === 0) {
54916
- console.log(green("$") + ` claudemesh doctor
55019
+ console.log(green2("$") + ` claudemesh doctor
54917
55020
  `);
54918
55021
  }
54919
55022
  break;
@@ -54984,7 +55087,7 @@ async function runPeers(flags) {
54984
55087
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
54985
55088
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
54986
55089
  const bold3 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
54987
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55090
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
54988
55091
  const yellow2 = (s) => useColor ? `\x1B[33m${s}\x1B[39m` : s;
54989
55092
  await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
54990
55093
  const peers = await client2.listPeers();
@@ -55000,7 +55103,7 @@ async function runPeers(flags) {
55000
55103
  console.log("");
55001
55104
  for (const p of peers) {
55002
55105
  const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
55003
- const statusIcon = p.status === "working" ? yellow2("●") : green2("●");
55106
+ const statusIcon = p.status === "working" ? yellow2("●") : green3("●");
55004
55107
  const name = bold3(p.displayName);
55005
55108
  const meta2 = [];
55006
55109
  if (p.peerType)
@@ -55488,7 +55591,7 @@ init_keypair();
55488
55591
  async function runSync(args) {
55489
55592
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
55490
55593
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
55491
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55594
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55492
55595
  const config2 = loadConfig();
55493
55596
  const code = generatePairingCode();
55494
55597
  const listener = await startCallbackListener();
@@ -55539,7 +55642,7 @@ async function runSync(args) {
55539
55642
  config2.accountId = result.account_id;
55540
55643
  saveConfig(config2);
55541
55644
  if (added > 0) {
55542
- console.log(green2(`✓ Added ${added} new mesh(es)`));
55645
+ console.log(green3(`✓ Added ${added} new mesh(es)`));
55543
55646
  } else {
55544
55647
  console.log(`Already up to date (${config2.meshes.length} meshes)`);
55545
55648
  }
@@ -55550,7 +55653,7 @@ init_config();
55550
55653
  async function runProfile(flags) {
55551
55654
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
55552
55655
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
55553
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55656
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55554
55657
  const config2 = loadConfig();
55555
55658
  if (config2.meshes.length === 0) {
55556
55659
  console.error("No meshes joined. Run `claudemesh join <url>` first.");
@@ -55590,7 +55693,7 @@ async function runProfile(flags) {
55590
55693
  if (flags.json) {
55591
55694
  console.log(JSON.stringify(result, null, 2));
55592
55695
  } else if (result.ok) {
55593
- console.log(green2("✓ Profile updated"));
55696
+ console.log(green3("✓ Profile updated"));
55594
55697
  const member = result.member;
55595
55698
  printProfile(member, dim2);
55596
55699
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -48,10 +48,10 @@
48
48
  "prettier": "3.6.2",
49
49
  "typescript": "5.9.3",
50
50
  "vitest": "4.0.14",
51
- "@turbostarter/prettier-config": "0.1.0",
52
- "@turbostarter/eslint-config": "0.1.0",
51
+ "@turbostarter/tsconfig": "0.1.0",
53
52
  "@turbostarter/vitest-config": "0.1.0",
54
- "@turbostarter/tsconfig": "0.1.0"
53
+ "@turbostarter/prettier-config": "0.1.0",
54
+ "@turbostarter/eslint-config": "0.1.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",