claudemesh-cli 0.9.4 → 0.9.6

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 +219 -116
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -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.6",
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 ${green("✓")} ${process.cwd()}`);
54158
+ row++;
54159
+ writeCentered(row, `Name ${green("✓")} ${opts.displayName}`);
54160
+ row++;
54161
+ writeCentered(row, `Mesh ${green("✓")} ${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 ${green("✓")} ${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 ${green("✓")} ${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 ${green("✓")} ${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) : [];
@@ -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) {
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
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",
53
51
  "@turbostarter/vitest-config": "0.1.0",
54
- "@turbostarter/tsconfig": "0.1.0"
52
+ "@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",