feat(cli): scaffold @claudemesh/cli MCP client package (stubs)

The user-facing tool. Two invocation modes:
  - `claudemesh mcp`           → MCP server (stdio), consumed by Claude Code
  - `claudemesh <subcommand>`  → human CLI

Layout:
  apps/cli/
  ├── package.json       bin: { claudemesh: ./src/index.ts }
  ├── README.md          install + usage
  └── src/
      ├── index.ts       dispatcher (mcp | install | join | list | leave | --help)
      ├── env.ts         CLAUDEMESH_BROKER_URL, CONFIG_DIR, DEBUG
      ├── mcp/
      │   ├── server.ts  MCP stdio server with 5 tools
      │   ├── tools.ts   tool schemas (send_message, list_peers,
      │   │              check_messages, set_summary, set_status)
      │   └── types.ts
      ├── ws/client.ts   broker connection (stub for 15b)
      ├── state/config.ts ~/.claudemesh/config.json (joined meshes + keys)
      └── commands/
          ├── install.ts print `claude mcp add ...` instruction
          ├── join.ts    parse ic://join/... (stub, Step 17)
          ├── list.ts    show joined meshes
          └── leave.ts   remove mesh from local config

Tool stubs return "not connected, run `claudemesh join <invite-link>`"
errors until 15b wires the WS client.

Verified:
- `bun src/index.ts --help` → prints usage
- `bun src/index.ts install` → prints MCP add command with resolved path
- `bun src/index.ts list` → "No meshes joined yet"
- `bun src/index.ts mcp` (via stdin) → returns tools/list with all 5 tools

Deps: @modelcontextprotocol/sdk, ws, libsodium-wrappers, zod.
Lockfile regenerated in the same commit per claudemesh-3's flag —
avoids breaking Coolify's --frozen-lockfile deploys.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 22:23:12 +01:00
parent c6674e971a
commit 8931296e82
16 changed files with 1048 additions and 2 deletions

73
apps/cli/src/index.ts Normal file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bun
/**
* @claudemesh/cli entry point.
*
* Dispatches between two modes:
* - `claudemesh mcp` → MCP server (stdio transport)
* - `claudemesh <subcommand>` → CLI subcommand
*
* Claude Code invokes the `mcp` mode via stdio. Humans use all others.
*/
import { startMcpServer } from "./mcp/server";
import { runInstall } from "./commands/install";
import { runJoin } from "./commands/join";
import { runList } from "./commands/list";
import { runLeave } from "./commands/leave";
const HELP = `claudemesh — peer mesh for Claude Code sessions
Usage:
claudemesh <command> [args]
Commands:
install Print Claude Code MCP registration instructions
join <link> Join a mesh via invite link (ic://join/...)
list Show all joined meshes
leave <slug> Leave a joined mesh
mcp Start MCP server (stdio) — invoked by Claude Code
--help, -h Show this help
Environment:
CLAUDEMESH_BROKER_URL Override broker URL (default: wss://ic.claudemesh.com/ws)
CLAUDEMESH_CONFIG_DIR Override config directory (default: ~/.claudemesh/)
CLAUDEMESH_DEBUG=1 Verbose logging
`;
const cmd = process.argv[2];
const args = process.argv.slice(3);
async function main(): Promise<void> {
switch (cmd) {
case "mcp":
await startMcpServer();
return;
case "install":
runInstall();
return;
case "join":
runJoin(args);
return;
case "list":
runList();
return;
case "leave":
runLeave(args);
return;
case "--help":
case "-h":
case "help":
case undefined:
console.log(HELP);
return;
default:
console.error(`Unknown command: ${cmd}`);
console.error("Run `claudemesh --help` for usage.");
process.exit(1);
}
}
main().catch((e) => {
console.error(`claudemesh: ${e instanceof Error ? e.message : String(e)}`);
process.exit(1);
});