diff --git a/apps/cli/package.json b/apps/cli/package.json index 2e16993..04087a8 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,26 +1,55 @@ { "name": "@claudemesh/cli", "version": "0.1.0", - "private": true, + "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.", + "keywords": [ + "claude-code", + "mcp", + "model-context-protocol", + "claudemesh", + "peer-messaging", + "multi-agent" + ], + "author": "Alejandro Gutiérrez", + "license": "MIT", + "homepage": "https://claudemesh.com", + "repository": { + "type": "git", + "url": "https://github.com/alezmad/claudemesh.git", + "directory": "apps/cli" + }, "type": "module", "bin": { - "claudemesh": "./src/index.ts" + "claudemesh": "./dist/index.js" + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" }, "scripts": { + "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js", "clean": "git clean -xdf .cache .turbo dist node_modules", "dev": "bun --hot src/index.ts", "start": "bun src/index.ts", "format": "prettier --check . --ignore-path ../../.gitignore", "lint": "eslint", + "prepublishOnly": "bun run build", "test": "vitest run", "typecheck": "tsc --noEmit" }, "prettier": "@turbostarter/prettier-config", + "engines": { + "node": ">=20" + }, "dependencies": { "@modelcontextprotocol/sdk": "1.27.1", "libsodium-wrappers": "0.7.15", "ws": "8.20.0", - "zod": "catalog:" + "zod": "4.1.13" }, "devDependencies": { "@turbostarter/eslint-config": "workspace:*", diff --git a/apps/cli/src/commands/install.ts b/apps/cli/src/commands/install.ts index 3c77eb3..c2740bc 100644 --- a/apps/cli/src/commands/install.ts +++ b/apps/cli/src/commands/install.ts @@ -27,6 +27,7 @@ import { import { homedir, platform } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; +import { spawnSync } from "node:child_process"; const MCP_NAME = "claudemesh"; const CLAUDE_CONFIG = join(homedir(), ".claude.json"); @@ -64,22 +65,44 @@ function writeClaudeConfig(obj: Record): void { } } -/** Check `bun` is on PATH — OS-agnostic. */ +/** Check `bun` is on PATH — OS-agnostic, node:child_process. */ function bunAvailable(): boolean { - const which = + const res = platform() === "win32" - ? Bun.spawnSync(["where", "bun"]) - : Bun.spawnSync(["sh", "-c", "command -v bun"]); - return which.exitCode === 0; + ? spawnSync("where", ["bun"]) + : spawnSync("sh", ["-c", "command -v bun"]); + return res.status === 0; } /** Absolute path to this CLI's entry file. */ function resolveEntry(): string { const here = fileURLToPath(import.meta.url); + // When bundled (dist/index.js), this file IS the entry → return self. + // When running from source (src/index.ts via bun), walk up to the + // dir + resolve index.ts. + if (here.endsWith("/dist/index.js") || here.endsWith("\\dist\\index.js")) { + return here; + } return resolve(dirname(here), "..", "index.ts"); } +/** + * Build the MCP server entry for Claude Code's config. + * + * Two modes: + * - Installed globally (npm i -g @claudemesh/cli): use `claudemesh` + * as the command, relies on it being on PATH. + * - Local dev (bun apps/cli/src/index.ts): use `bun `. + */ function buildMcpEntry(entryPath: string): McpEntry { + const isBundled = entryPath.endsWith("/dist/index.js") || + entryPath.endsWith("\\dist\\index.js"); + if (isBundled) { + return { + command: "claudemesh", + args: ["mcp"], + }; + } return { command: "bun", args: [entryPath, "mcp"], @@ -97,14 +120,18 @@ export function runInstall(): void { console.log("claudemesh install"); console.log("------------------"); - if (!bunAvailable()) { + const entry = resolveEntry(); + const isBundled = entry.endsWith("/dist/index.js") || + entry.endsWith("\\dist\\index.js"); + + // Dev mode (running from src/) requires bun on PATH; bundled mode + // (npm install -g) just uses node + the claudemesh bin shim. + if (!isBundled && !bunAvailable()) { console.error( "✗ `bun` is not on PATH. Install Bun first: https://bun.com", ); process.exit(1); } - - const entry = resolveEntry(); if (!existsSync(entry)) { console.error(`✗ MCP entry not found at ${entry}`); process.exit(1); @@ -142,7 +169,9 @@ export function runInstall(): void { console.log(`✓ MCP server "${MCP_NAME}" ${action}`); console.log(` config: ${CLAUDE_CONFIG}`); - console.log(` command: bun ${entry} mcp`); + console.log( + ` command: ${desired.command}${desired.args?.length ? " " + desired.args.join(" ") : ""}`, + ); console.log(""); console.log("Restart Claude Code to load the MCP server."); console.log("Then join a mesh:"); diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index f9d79f5..e6255b7 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,4 +1,3 @@ -#!/usr/bin/env bun /** * @claudemesh/cli entry point. *