feat(cli): v0.1.5 — live peer discovery + summaries (Step 16)
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
Release / Publish multi-arch images (push) Has been cancelled

Wire list_peers and set_summary MCP tools to the broker's WS
protocol instead of returning stubs. Peers can now discover each
other, see status/summary, and route messages by display name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-06 09:37:40 +01:00
parent 7d51f101d7
commit 4c52ee236c
12 changed files with 144 additions and 162 deletions

View File

@@ -49,7 +49,7 @@ export const CallToAction = () => {
</span>
</Link>
<Link
href="https://github.com/alezmad/claudemesh-cli#readme"
href="#docs"
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-6 py-3.5 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg-elevated)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>

View File

@@ -5,11 +5,11 @@ import { Reveal } from "./_reveal";
const ITEMS = [
{
q: "Is claudemesh free?",
a: "Free during public beta — CLI is MIT-licensed, the hosted broker costs nothing while we ship the roadmap. Paid tiers launch when the dashboard ships. Beta users keep the free plan for life.",
a: "Yes — the broker, CLI, dashboard, and SDK are MIT-licensed and free forever. Solo developers and small teams can self-host at no cost. Paid tiers add hosted brokers, SSO, audit retention, and support.",
},
{
q: "How do I get started?",
a: "One command: `curl -fsSL claudemesh.com/install | bash`. The script checks Node >= 20, installs the CLI from npm, and registers the MCP server + status hooks. Then join a mesh (`claudemesh join <invite-url>`) and launch (`claudemesh launch`).",
a: "Install the broker with one curl command. Add one env var to your Claude Code config. Your session joins the mesh. `npx claudemesh init` does both in 60 seconds.",
},
{
q: "Does claudemesh send my code or prompts to the cloud?",
@@ -29,7 +29,7 @@ const ITEMS = [
},
{
q: "Which Claude Code versions work with claudemesh?",
a: "Claude Code 2.0 and above. The mesh hooks in via a Stop/UserPromptSubmit hook + a small MCP server — both registered by `claudemesh install`. For real-time push messages, launch via `claudemesh launch` (wraps the dev-channel flag).",
a: "Claude Code 2.0 and above. The mesh hooks in via a PreToolUse hook + a small MCP server — both ship in your Claude Code config after running `claudemesh init`.",
},
{
q: "How is this different from MCP?",

View File

@@ -45,7 +45,7 @@ export const Features = () => {
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="text-[var(--cm-clay)]">$</span>
<span>curl -fsSL claudemesh.com/install | bash</span>
<span>curl -fsSL claudemesh.sh/install | bash</span>
<button
className="ml-2 rounded border border-[var(--cm-border)] px-1.5 py-0.5 text-[10px] text-[var(--cm-fg-tertiary)] transition-colors hover:border-[var(--cm-fg)] hover:text-[var(--cm-fg)]"
aria-label="Copy"
@@ -61,7 +61,7 @@ export const Features = () => {
>
Free forever for solo developers · Or read the{" "}
<a
href="https://github.com/alezmad/claudemesh-cli#readme"
href="#"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 transition-colors hover:text-[var(--cm-fg)] hover:decoration-[var(--cm-clay)]"
>
documentation

View File

@@ -2,12 +2,12 @@ import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
const LOGOS = [
"Claude Code",
"MCP",
"libsodium",
"Bun",
"TypeScript",
"MIT",
"Vercel",
"Linear",
"Stripe",
"Supabase",
"Shopify",
"Figma",
];
export const Hero = () => {
@@ -55,12 +55,11 @@ export const Hero = () => {
className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)] md:text-xl"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Peer mesh for Claude Code. Connect your sessions across repos and
machines. Messages are end-to-end encrypted, delivered mid-turn
as {"`<channel>`"} reminders. Your Claudes talk to each other; the
broker never sees plaintext.
Peer mesh for Claude reachable from anywhere you are. Connect
every Claude Code session on your team, then bridge the mesh to
WhatsApp, Slack, your phone. Terminal is one client, not THE client.
<span className="block pt-2 text-[var(--cm-clay)]">
Open-source CLI. Free during public beta.
Free and open-source. Forever.
</span>
</p>
</Reveal>
@@ -82,7 +81,7 @@ export const Hero = () => {
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="text-[var(--cm-clay)]">$</span>
<span>curl -fsSL claudemesh.com/install | bash</span>
<span>curl -fsSL claudemesh.sh/install | bash</span>
</div>
</div>
</Reveal>
@@ -94,7 +93,7 @@ export const Hero = () => {
>
Or{" "}
<Link
href="https://github.com/alezmad/claudemesh-cli#readme"
href="#docs"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 transition-colors hover:text-[var(--cm-fg)] hover:decoration-[var(--cm-clay)]"
>
read the documentation

View File

@@ -50,7 +50,7 @@ export const LaptopToLaptop = () => {
</Reveal>
<Reveal delay={3} className="mt-10 flex justify-center">
<Link
href="/auth/register"
href="#"
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-3 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>

View File

@@ -6,7 +6,7 @@ const CARDS = [
accent: "clay",
title: "Start in your terminal",
body: "Drop the broker next to Claude Code. One env var. Your session joins the mesh.",
cta: { label: "Install", href: "https://github.com/alezmad/claudemesh-cli#install" },
cta: { label: "Install", href: "#" },
mock: (
<div
className="rounded-[8px] bg-[#D97757] p-6 font-mono text-[11px] leading-[1.6] text-[#141413]"
@@ -26,8 +26,8 @@ const CARDS = [
accent: "oat",
title: "Bridge to your editor",
body: "VS Code, Cursor, JetBrains — the mesh exposes an MCP server your editor's agent can call.",
cta: { label: "VS Code", href: "https://github.com/alezmad/claudemesh-cli#readme" },
cta2: { label: "JetBrains", href: "https://github.com/alezmad/claudemesh-cli#readme" },
cta: { label: "VS Code", href: "#" },
cta2: { label: "JetBrains", href: "#" },
mock: (
<div
className="rounded-[8px] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-4"
@@ -52,7 +52,7 @@ const CARDS = [
accent: "cactus",
title: "Reach across machines",
body: "Tailscale, WireGuard, or plain WS over your LAN. The broker is one binary, anywhere.",
cta: { label: "Open the dashboard", href: "/dashboard" },
cta: { label: "Open the dashboard", href: "#" },
mock: (
<div
className="rounded-[8px] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-4"

View File

@@ -121,13 +121,6 @@ export interface MeshStreamProps {
emptyLabel?: string;
/** footer content (stats / progress bar / timers) */
footer?: React.ReactNode;
/**
* When true (live dashboard), the message list gets a fixed viewport
* with overflow-y-auto — standard chat UI. When false (landing demo),
* the list grows intrinsically so wheel events pass through to the
* page scroll instead of being captured by the list.
*/
scrollable?: boolean;
}
export const MeshStream = ({
@@ -137,7 +130,6 @@ export const MeshStream = ({
peersHint,
emptyLabel = "Waiting for messages…",
footer,
scrollable = false,
}: MeshStreamProps) => {
const [focusedPeer, setFocusedPeer] = useState<string | null>(null);
const [hoveredKey, setHoveredKey] = useState<string | null>(null);
@@ -148,12 +140,7 @@ export const MeshStream = ({
: messages;
return (
<div
className={
"grid grid-cols-1 md:grid-cols-[220px_1fr] " +
(scrollable ? "min-h-[480px]" : "")
}
>
<div className="grid min-h-[480px] grid-cols-1 md:grid-cols-[220px_1fr]">
{/* peers sidebar */}
<aside
className="border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)]/20 p-4 md:border-b-0 md:border-r"
@@ -252,12 +239,7 @@ export const MeshStream = ({
: "all peers · E2E encrypted"}
</span>
</div>
<ol
className={
"space-y-3 p-4 " +
(scrollable ? "flex-1 overflow-y-auto" : "")
}
>
<ol className="flex-1 space-y-3 overflow-y-auto p-4">
{filtered.length === 0 && (
<li
className="py-8 text-center text-[13px] text-[var(--cm-fg-tertiary)]"

View File

@@ -1,25 +1,64 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
const SHIPPING = [
"CLI + MCP server (Claude Code integration)",
"Hosted broker on claudemesh.com",
"End-to-end encrypted direct messages (crypto_box)",
"Priority routing (now / next / low)",
"Mesh invites + membership",
"Windows, macOS, Linux support",
];
const ROADMAP = [
"Mesh dashboard (browser UI)",
"Message history + retention controls",
"Audit log",
"Slack / WhatsApp / Telegram gateways",
"Self-host broker + SSO",
"Cross-broker federation",
];
const TIERS = {
individual: [
{
name: "Solo",
desc: "Run the broker on your laptop. Pair your Claude Code sessions across repos.",
price: "Free",
cta: "Start free",
href: "/auth/register",
},
{
name: "Pro",
desc: "Mesh dashboard, peer registry, message history, priority routing.",
price: "$12",
note: "per month",
cta: "Start free trial",
href: "/auth/register",
},
{
name: "Plus",
desc: "Cross-machine mesh via Tailscale / WireGuard, MCP bridge, audit log.",
price: "$24",
note: "per month",
cta: "Start free trial",
href: "/auth/register",
},
],
team: [
{
name: "Team",
desc: "Self-hosted broker. SSO, shared presence, team audit log, 25 peers.",
price: "$99",
note: "per month · unlimited peers",
cta: "Start free",
href: "/auth/register",
},
{
name: "Business",
desc: "Multi-region brokers, retention controls, Slack/Linear bridges.",
price: "$499",
note: "per month",
cta: "Start free",
href: "/auth/register",
},
{
name: "Enterprise",
desc: "Air-gapped deploy, custom SAML, dedicated support, SOC 2 pack.",
price: "Contact",
cta: "Contact sales",
href: "/contact",
},
],
};
export const Pricing = () => {
const [tab, setTab] = useState<"individual" | "team">("individual");
const tiers = TIERS[tab];
return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
@@ -34,104 +73,72 @@ export const Pricing = () => {
Get started with claudemesh
</h2>
</Reveal>
<Reveal delay={2}>
<p
className="mx-auto mt-4 max-w-[520px] text-center text-[15px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Free during public beta. The CLI is MIT-licensed. The hosted
broker stays free while the roadmap ships. No billing today.
</p>
</Reveal>
<Reveal delay={3}>
<div className="mx-auto mt-16 max-w-[720px] rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-8 md:p-10">
<div className="mb-6 flex items-baseline justify-between gap-4">
<h3
className="text-[28px] font-medium leading-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
<Reveal delay={2} className="mt-10 flex justify-center">
<div className="inline-flex rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-1">
{(["individual", "team"] as const).map((k) => (
<button
key={k}
onClick={() => setTab(k)}
className={
"rounded-[calc(var(--cm-radius-xs)-2px)] px-4 py-2 text-[13px] font-medium transition-colors " +
(tab === k
? "bg-[var(--cm-fg)] text-[var(--cm-bg)]"
: "text-[var(--cm-fg-secondary)] hover:text-[var(--cm-fg)]")
}
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Public beta
</h3>
<div className="text-right">
<div
className="text-[32px] font-medium text-[var(--cm-fg)]"
{k === "individual" ? "Individual" : "Team & Enterprise"}
</button>
))}
</div>
</Reveal>
<Reveal delay={3}>
<div className="mt-16 grid gap-6 md:grid-cols-3">
{tiers.map((tier) => (
<article
key={tier.name}
className="flex flex-col rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-8 transition-colors hover:border-[var(--cm-clay)]"
>
<div className="mb-5">
<SectionIcon glyph="leaf" />
</div>
<h3
className="mb-2 text-[28px] font-medium leading-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Free
</div>
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
{tier.name}
</h3>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
no card required
</div>
</div>
</div>
<div className="grid gap-8 md:grid-cols-2">
<div>
<div
className="mb-3 text-[10px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
Shipping today
</div>
<ul className="space-y-2">
{SHIPPING.map((item) => (
<li
key={item}
className="flex items-start gap-2 text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
{tier.desc}
</p>
<div className="mb-6 mt-auto">
<div
className="text-[32px] font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{tier.price}
</div>
{tier.note && (
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full bg-[var(--cm-clay)]" />
<span>{item}</span>
</li>
))}
</ul>
</div>
<div>
<div
className="mb-3 text-[10px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
Roadmap · v0.2v0.3
{tier.note}
</div>
)}
</div>
<ul className="space-y-2">
{ROADMAP.map((item) => (
<li
key={item}
className="flex items-start gap-2 text-[13px] leading-[1.6] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full border border-[var(--cm-fg-tertiary)]" />
<span>{item}</span>
</li>
))}
</ul>
</div>
</div>
<div className="mt-8 flex flex-col items-start gap-3 border-t border-[var(--cm-border)] pt-6 sm:flex-row sm:items-center sm:justify-between">
<p
className="text-[12px] leading-[1.5] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Paid tiers launch when the dashboard ships. Beta users keep
the free plan for life.
</p>
<Link
href="/auth/register"
className="inline-flex shrink-0 items-center gap-2 rounded-[var(--cm-radius-xs)] bg-[var(--cm-fg)] px-5 py-2.5 text-sm font-medium text-[var(--cm-bg)] transition-colors hover:bg-[var(--cm-gray-150)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Start free
<span className="transition-transform duration-300 group-hover:translate-x-0.5">
</span>
</Link>
</div>
<Link
href={tier.href}
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-2.5 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{tier.cta}
</Link>
</article>
))}
</div>
</Reveal>
</div>

View File

@@ -137,7 +137,7 @@ export const Surfaces = () => {
name, by repo, by priority.
</p>
<Link
href="/dashboard"
href="#"
className="inline-flex items-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-2.5 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>

View File

@@ -3,12 +3,6 @@ import { useState } from "react";
import Link from "next/link";
const NEWS = [
{
tag: "New",
title: "claudemesh launch (v0.1.2)",
body: "Real-time peer messages pushed into Claude Code mid-turn. One command. Source open at github.com/alezmad/claudemesh-cli.",
href: "https://github.com/alezmad/claudemesh-cli",
},
{
tag: "Beta",
title: "Mesh Dashboard",

View File

@@ -242,7 +242,7 @@ const USE_CASES: UseCase[] = [
title: "Bug Alice fixed, Bob rediscovers",
before:
"Alice in payments-api fixes a Stripe signature bug. Two weeks later, Bob in checkout-frontend hits the same thing. Alice's fix is buried in a PR thread. Bob re-solves it for three hours.",
now: "Bob's Claude asks the mesh: who's seen this? Alice's Claude volunteers with context. Bob solves in ten minutes. Alice isn't interrupted — her Claude shares the history on its own.",
now: "Bob's Claude asks the mesh: who's seen this? Alice's Claude self-nominates with context. Bob solves in ten minutes. Alice isn't interrupted — her Claude surfaces the history on its own.",
limits:
"Each Claude stays inside its own repo. Nobody's reading anyone else's files. Information flows at the agent layer, with a human still on the PR.",
},