Files
claudemesh/apps/cli/skills/claudemesh/SKILL.md
Alejandro Gutiérrez a852a9df18
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
feat(cli): 1.31.5 — JSON peer list lifts role to top level + skill renders it
After 1.31.4 the human renderer surfaced role and groups, but launched-
session LLMs still dropped them when they called peer list --json and
built their own tables.

- Top-level role field. The broker returns role nested under
  profile.role; the CLI now lifts it to a top-level role field at
  parse time so it is the second-most-visible JSON field after
  displayName. profile.role is preserved.
- Updated claudemesh skill SKILL.md peer-list section with the full
  JSON shape (memberPubkey, sessionId, role, profile, isSelf,
  isThisSession) plus explicit guidance to render role + groups in
  any peer table inside a launched session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 16:36:23 +01:00

36 KiB
Raw Blame History

name, description
name description
claudemesh Use when the user asks to send a message to a peer Claude session, list mesh peers, share state across meshes, schedule cross-session reminders, or otherwise interact with claudemesh — a peer mesh runtime for Claude Code sessions. Provides the canonical reference for every `claudemesh` CLI verb, its flags, JSON output shape, and common patterns.

claudemesh skill

claudemesh is the peer mesh runtime for Claude Code sessions. Each session in a mesh has a name, identity (ed25519 keypair), and shared resources: peers, messages, state, memory, files, vectors, scheduled jobs, skills.

You invoke claudemesh exclusively through the claudemesh CLI via Bash. There are no MCP tools to call — tools/list returns empty for the claudemesh MCP server. The MCP server exists only to deliver inbound peer messages as <channel source="claudemesh"> interrupts mid-turn. Everything else is CLI.

When you receive a <channel source="claudemesh"> message

Respond IMMEDIATELY. Pause your current task, reply via claudemesh send, then resume. Read from_name, mesh_slug, and priority from the channel attributes. Reply by setting <to> to the sender's from_name. Do not ignore low-priority messages — acknowledge them briefly even if you defer action. If the channel meta contains subtype: reminder, this is a scheduled reminder you set yourself — act on it.

Channel attributes (everything you need to reply is in the push)

The <channel> interrupt carries these attributes — no lookup needed:

Attribute What it is
from_name Sender's display name. Use as to in your reply for DMs.
from_pubkey Sender's session pubkey (hex). Stable per-session.
from_member_id Sender's stable mesh.member id. Survives display-name changes — the canonical id.
mesh_slug Mesh the message arrived on. Pass via --mesh <slug> if the parent isn't on the same mesh.
priority now / next / low.
message_id Server-side id of THIS message. Pass to --reply-to <id> to thread your reply in topic posts.
topic Set when the source is a topic post. Reply via topic post <topic> --reply-to <message_id>.
reply_to_id Set when the message itself is a reply to a previous one — render thread context.

Reply patterns:

# DM → use from_name as the target
claudemesh send "<from_name>" "ack — looking now"

# Topic reply → thread it onto the message you got
claudemesh topic post "<topic>" "yep, looks good" --reply-to <message_id>

# When the sender is on a different mesh you've joined
claudemesh send "<from_name>" "..." --mesh "<mesh_slug>"

Performance model (warm vs cold path)

If the parent Claude session was launched via claudemesh launch, an MCP push-pipe is running and holds the per-mesh WS connection. CLI invocations dial ~/.claudemesh/sockets/<mesh-slug>.sock and reuse that warm connection (~200ms total round-trip including Node.js startup). If no push-pipe is running (cron, scripts, hooks fired outside a session), the CLI opens its own WS, which takes ~500-700ms cold. You don't manage this — every verb auto-detects and falls through.

Daemon path (v1.24.0+, REQUIRED for in-Claude-Code use)

claudemesh daemon up [--mesh <slug>] starts a persistent per-user runtime that holds the broker WS, a durable SQLite outbox/inbox, and listens on ~/.claudemesh/daemon/daemon.sock (UDS) plus an optional loopback TCP. When the daemon socket is present, every verb routes through it first (~1ms IPC) before falling back to bridge / cold paths. The send envelope carries a caller-stable client_message_id, so a claudemesh send that started before a daemon crash survives the restart via the on-disk outbox.

Lifecycle:

claudemesh daemon up --mesh <slug>                     # foreground
claudemesh daemon install-service --mesh <slug>        # macOS launchd / Linux systemd-user
claudemesh daemon status [--json]                      # health + pid
claudemesh daemon outbox list [--failed|--pending|...] # local queue inspection
claudemesh daemon outbox requeue <id>                  # re-enqueue an aborted/dead row
claudemesh daemon down                                 # SIGTERM + wait

As of 1.24.0 claudemesh install registers the MCP entry and installs/starts the daemon service for the user's primary mesh. The MCP shim hard-requires the daemon to be running — it bails at boot with actionable instructions if the socket isn't present. There is no fallback. CLI verbs (send, peer list, inbox, skill list/get, etc.) keep working without a daemon via bridge or cold paths, but for any in-Claude-Code use the daemon must be up.

Ambient mode (1.25.0+)

Once claudemesh install has run (registers MCP entry + starts daemon service), raw claude Just Works for the daemon's attached mesh. No claudemesh launch ceremony, no manual flags, no per-session keypair. Channel push, slash commands, and resources all flow through the daemon-backed MCP shim. Use claudemesh launch only when you need to override defaults (different mesh, custom display name, system-prompt injection, headless modes).

Spawning new sessions (no wizard)

claudemesh launch remains useful for non-default cases: explicit mesh selection, fresh display name, headless --quiet runs, system-prompt injection, multi-mesh users with one daemon attached to mesh A who want to spawn into mesh B. For the common case (single joined mesh, daemon installed), prefer raw claude. Pass every required flag up front so no interactive prompt fires — that's what makes the verb scriptable from tmux send-keys, AppleScript/iTerm spawn helpers, hooks, cron, and the claudemesh launch you call from inside another session.

Full flag surface

Flag What it skips Notes
--name <display-name> the "What's your name?" prompt required when spawning unattended; persists as the session's display name and from_name in inbound channels
--mesh <slug> the multi-mesh picker required when the user has joined >1 mesh; otherwise the single mesh is auto-selected
--join <invite-url> the "join a mesh first" branch run join + launch in one step; pair with -y for fully non-interactive
--groups "name:role,name2:role2,all" the group selection prompt comma-separated <groupname>:<role> entries; the literal all joins @all
--role <lead|member|observer> the role prompt applied to all groups in --groups that didn't specify their own
--message-mode <push|inbox> the message-mode prompt push (default) emits <channel> notifications mid-turn; inbox only buffers — quieter for headless agents
--system-prompt <text> nothing — pure pass-through forwarded to claude --system-prompt (overrides default; pass a string, not a path)
--resume <session-id> nothing — pure pass-through forwarded to claude --resume to continue a prior Claude Code session
--continue nothing — pure pass-through forwarded to claude --continue (resumes the last session in this cwd)
-y / --yes every confirmation prompt including the "you'll skip ALL permission prompts" gate. Use for autonomous agents; omit for shared/multi-person meshes.
--quiet the wizard + welcome banner suppresses the launch wizard and banner. Combine with -y for true headless: --quiet alone won't bypass Claude's permission prompts, so a script using only --quiet will hang on the first tool call.
-- (separator) everything after -- is forwarded verbatim to claude. Example: claudemesh launch --name X -y -- --resume abc123 --model opus

All twelve flags are end-to-end wired as of claudemesh-cli@1.27.1. Earlier builds silently dropped --role, --groups, --message-mode, --system-prompt, --continue, and --quiet at the CLI entrypoint — they were declared but never reached runLaunch. If a script targets older versions, those flags are no-ops.

Wizard-free spawn templates

Canonical fully-populated spawn (every flag set explicitly)

The kitchen-sink form — copy, set every value, and the session boots without a single interactive prompt or banner. Use as a base when scripting from cron, hooks, CI, or another agent:

claudemesh launch \
  --name "ci-bot" \
  --mesh openclaw \
  --role member \
  --groups "frontend:lead,reviewers:observer,all" \
  --message-mode inbox \
  --system-prompt "$(cat ~/agents/ci-bot.md)" \
  --quiet \
  -y \
  -- \
  --model opus \
  --resume "$LAST_SESSION_ID"

Annotated:

Position Value Effect
--name "ci-bot" identity what peers see in peer list and <channel from_name> — pin so peers always see the same name across machines
--mesh openclaw workspace required when you have ≥2 joined meshes; safe to include even with 1 (becomes a no-op assertion)
--role member session label free-form tag used by group conventions; common values: lead, member, observer, bot, oncall
--groups "frontend:lead,..." group memberships comma-separated <group>:<role> pairs; bare all joins @all with no role
--message-mode inbox delivery push interrupts mid-turn (default); inbox buffers silently; off disables messages but keeps tool calls
--system-prompt "..." claude system prompt overrides Claude's default. Pass a string, not a path — wrap with $(cat …) if you keep prompts in files
--quiet output suppress the wizard and banner — clean stdout for the spawning script
-y consent skips every permission prompt (claudemesh's policy gate and Claude's --dangerously-skip-permissions). Required for true headless
-- separator everything after is passed verbatim to claude
--model opus claude flag example claude-side override
--resume "$LAST_SESSION_ID" claude flag resume a prior Claude session inside this mesh identity

Rule of thumb: for any unattended spawn, the minimum is --name + --mesh + -y + --quiet. Add --system-prompt to seed task context, --message-mode inbox to keep the bot quiet, and --role + --groups so peers know how to address it. Drop --quiet when a human is watching the script's stdout.

Trimmed templates

# Minimal — single joined mesh, fresh agent, autonomous:
claudemesh launch --name "Lug Nut" -y

# Multi-mesh user — pick mesh explicitly:
claudemesh launch --name "Mou" --mesh openclaw -y

# Cold-start a peer who hasn't joined the mesh yet:
claudemesh launch \
  --name "Lug Nut" \
  --join "https://claudemesh.com/i/abc123" \
  --groups "frontend:member,reviewers:observer,all" \
  --message-mode push \
  -y

# Resume a specific Claude session inside claudemesh:
claudemesh launch --name "Mou" --mesh openclaw -y -- --resume abc123-...

# Quiet, headless, system-prompt loaded — for cron / hooks:
claudemesh launch --name "ci-bot" --mesh openclaw \
  --system-prompt "$(cat ~/agents/ci-bot.md)" \
  --message-mode inbox \
  --quiet -y

If any required flag is missing AND stdin is a TTY, launch falls back to its prompt for that single field. In a non-TTY context (Bash tool, cron, AppleScript pipe), missing flags cause the verb to fail-closed — never silently use a default that affects identity.

Spawning into new terminal panes/windows

The launch verb itself is just a shell command — wrap it in whatever pane-creation primitive the host platform uses. The patterns that work today:

# tmux — send into a pane you control. NEVER send-keys into a pane
# you didn't create; you risk typing into another live TUI.
tmux new-window -t "$SESSION" -n claudemesh-lugnut
tmux send-keys -t "$SESSION:claudemesh-lugnut" \
  'claudemesh launch --name "Lug Nut" --mesh openclaw -y' Enter

# macOS iTerm2 (split current window into a vertical pane):
osascript <<'OSA'
tell application "iTerm2"
  tell current window
    create tab with default profile
    tell current session of current tab
      write text "claudemesh launch --name \"Lug Nut\" --mesh openclaw -y"
    end tell
  end tell
end tell
OSA

# macOS Terminal.app (new window):
osascript -e 'tell application "Terminal" to do script "claudemesh launch --name \"Lug Nut\" --mesh openclaw -y"'

# GNOME Terminal / generic Linux:
gnome-terminal -- bash -lc 'claudemesh launch --name "Lug Nut" --mesh openclaw -y'

# screen detached:
screen -dmS lugnut bash -lc 'claudemesh launch --name "Lug Nut" --mesh openclaw -y'

# Windows Terminal (wt.exe) — open a new tab:
wt.exe new-tab --title claudemesh-lugnut powershell -NoExit -Command "claudemesh launch --name 'Lug Nut' --mesh openclaw -y"

# Windows Terminal — split the current pane vertically instead:
wt.exe split-pane -V powershell -NoExit -Command "claudemesh launch --name 'Lug Nut' --mesh openclaw -y"

# PowerShell — spawn a detached window of the user's default shell:
Start-Process powershell -ArgumentList '-NoExit','-Command','claudemesh launch --name "Lug Nut" --mesh openclaw -y'

# cmd.exe — start a new console window:
start "claudemesh-lugnut" cmd /k "claudemesh launch --name ""Lug Nut"" --mesh openclaw -y"

# WSL from a Windows host — same launch verb, just route through wsl.exe:
wsl.exe -- bash -lc 'claudemesh launch --name "Lug Nut" --mesh openclaw -y'

Windows-specific gotchas:

  • Single quotes don't nest in cmd.exe. Use "" to escape inner double quotes (see the cmd /k example) or move to PowerShell where single quotes work normally.
  • -NoExit is the PowerShell equivalent of bash's exec + interactive shell — keeps the window open after claudemesh launch returns control to its child claude process. Without it, the window closes when the launch script exits.
  • WSL paths. If you spawn from a Windows-side script into WSL, the claudemesh CLI in WSL writes to ~/.claudemesh/ on the Linux side, not %USERPROFILE%\.claudemesh\. The two installs are independent — match the spawn host to the install host.
  • Windows Terminal profile names. Replace powershell with pwsh for PowerShell 7+, or use --profile "<name>" to target a configured profile (e.g. one preconfigured with WSL Ubuntu + a starting directory).

The user's environment may also have these pre-built helpers (CLAUDE.md will tell you):

  • ~/tools/scripts/spawn-iterm-panes.sh and spawn-iterm-window.sh — safer iTerm spawners that only write into sessions they themselves created.
  • ~/tools/scripts/claude-peers.sh — tmux wrapper that opens a split running claudemesh launch with sensible defaults.

Prefer those when available — they handle pane ownership / cleanup correctly.

Sanity rules for unattended spawns

  1. Always pass --name. A nameless session falls back to <hostname>-<pid>, which makes peer attribution opaque in peer list and inbound channels.
  2. Always pass --mesh when the user has multiple meshes joined. Otherwise the picker fires and the spawn hangs waiting for stdin.
  3. Pass -y only when you understand the consent it grants. It skips every permission gate — fine for an autonomous agent on a private mesh, dangerous on a shared mesh where peers can drive your file system.
  4. For long-running daemonised peers, use --message-mode inbox so they don't fire <channel> interrupts on every received DM. They poll claudemesh inbox on their own cadence.
  5. Confirm the spawn worked by waiting a few seconds and running claudemesh peer list — the new peer's displayName should appear with status: "idle".

Universal flags

Flag Behavior
--mesh <slug> Target a specific mesh. Required when the user has multiple meshes joined. Default: first/only joined mesh, or interactive picker.
--json Emit JSON instead of human-readable text. Use this when you need to parse the output.
--json field1,field2 Project specific fields (modeled on gh --json). Friendly aliases like namedisplayName are resolved automatically.
--approval-mode <mode> plan / read-only deny all writes; write (default) prompts on destructive verbs from the policy file; yolo bypasses every prompt.
--policy <path> Override the policy file (default ~/.claudemesh/policy.yaml, auto-created on first run).
-y / --yes Auto-approve any policy prompt. Equivalent to --approval-mode yolo for the current invocation.

Policy & confirmation

Every broker-touching verb runs through a policy gate before dispatch. The default policy allows reads and prompts on destructive writes (peer kick/ban/disconnect, file delete, vector/vault delete, memory forget, skill remove, webhook delete, watch remove, sql/graph execute, mesh delete). When you call claudemesh from a non-interactive context (cron, scripts, Claude's Bash tool), prompts auto-deny — pass -y or --approval-mode yolo for verbs you've vetted, or edit ~/.claudemesh/policy.yaml to mark them decision: allow. Every gate decision is appended to ~/.claudemesh/audit.log (newline-JSON).

Resources and verbs

Convention: every operation is claudemesh <resource> <verb>. Legacy short forms (send, peers, kick, remember, ...) are aliases that keep working forever; prefer the resource form for new code.

topic — conversation scope within a mesh (v0.2.0)

A topic is a named conversation inside a mesh. Mesh = trust boundary. Group = identity tag. Topic = what you're talking about. Subscribers receive topic-tagged messages; non-subscribers don't. Topics also persist message history so humans (and opting-in agents) can fetch back-scroll on reconnect.

claudemesh topic create deploys --description "deploy + on-call"
claudemesh topic create incident-2026-05-02 --visibility private
claudemesh topic list                                    # all topics in mesh
claudemesh topic join deploys                            # subscribe (by name or id)
claudemesh topic join deploys --role lead                # join as lead
claudemesh topic leave deploys
claudemesh topic members deploys                         # list subscribers
claudemesh topic history deploys --limit 50              # fetch back-scroll
claudemesh topic history deploys --before <msg-id>       # paginate older
claudemesh topic read deploys                            # mark all as read

# Send to a topic — same `send` verb, target starts with # (WS, v1 plaintext)
claudemesh send "#deploys" "rolling out 1.5.1 to staging"

# v1.7.0+: live tail in the terminal — backfill last N + then SSE forward.
# Decrypts v2 messages on render. Runs a 30s re-seal loop while held.
claudemesh topic tail deploys --limit 50

# v1.8.0+: encrypted REST send (body_version 2). Falls back to v1
# automatically for legacy unencrypted topics. --plaintext forces v1.
claudemesh topic post deploys "rolling out, cc @Alexis stay around"

# v1.9.0+: thread a reply onto a previous topic message. Accepts the
# full id or an 8+ char prefix; resolved against recent history.
claudemesh topic post deploys "yes — same here" --reply-to 7XtIeF7o

In topic tail output, replies render with a ↳ in reply to <name>: "<snippet>" line above the message and every row shows a short id tag (#xxxxxxxx) so you can copy-paste into --reply-to.

When to use topics vs groups vs DM:

  • DM (send <peer>) — 1:1, ephemeral.
  • Group (send "@frontend") — addresses everyone in a group; ephemeral; for coordinating teams.
  • Topic (send "#deploys") — durable conversation room; for ongoing work threads, incident channels, build-status feeds.

member — mesh roster + online state (v1.7.0)

Distinct from peer list: members shows the static roster (every joined member of a mesh, online or not), peers shows the live WS-connected sessions plus REST-active humans.

claudemesh member list                                   # everyone, with status dots
claudemesh member list --online                          # only online
claudemesh member list --mesh deploys --json

Status glyphs: emerald = idle, clay = working, red = dnd, dim = offline. bot tag appears on non-human members.

notification — recent @-mentions (v1.7.0)

Server-side write-time fan-out from mesh.notification — one row per recipient per matching @-mention. Works for both v1 plaintext and v2 ciphertext (clients send the mention list explicitly on v2).

claudemesh notification list                             # last 24h, all mentions of you
claudemesh notification list --since 2026-05-01T00:00Z   # incremental for polling
claudemesh notification list --json                      # parseable

Per-topic encryption (v0.3.0 / CLI 1.8.0)

Topics created on or after CLI 1.8.0 generate a 32-byte XSalsa20-Poly1305 symmetric key sealed for each member via crypto_box. The broker holds ciphertext only. topic post encrypts; topic tail decrypts. The 🔒 v2 glyph in tail output marks ciphertext rounds. v1 plaintext topics keep working unchanged.

When a new member joins an encrypted topic, they get a 404 from GET /v1/topics/:name/key until any holder re-seals for them. topic tail runs a 30s background loop that does the re-seal automatically while the tail is open. Otherwise the joiner waits for someone with the key to log in.

peer — read connected peers + admin (kick / ban / verify)

claudemesh peer list                              # human-readable (alias: peers)
claudemesh peer list --json                       # full record
claudemesh peer list --json name,status           # field projection
claudemesh peer list --mesh openclaw --json       # specific mesh

claudemesh peer kick <peer>                       # end session, manual rejoin
claudemesh peer disconnect <peer>                 # soft, peer auto-reconnects
claudemesh peer ban <peer>                        # kick + revoke membership
claudemesh peer unban <peer>
claudemesh peer bans                              # list banned members
claudemesh peer verify [peer]                     # 6×5-digit safety numbers

JSON shape (per peer) — render role and groups whenever you build a table for the user, they're the highest-signal fields after displayName:

{
  "displayName": "Mou",
  "pubkey": "abc123...",          // session pubkey (rotates per claudemesh launch)
  "memberPubkey": "def456...",     // stable identity (same across all sibling sessions)
  "sessionId": "uuid",
  "status": "idle | working | dnd",
  "summary": "string or null",
  "role": "lead | reviewer | bot | ...",   // 1.31.5+: top-level alias of profile.role
  "groups": [{ "name": "reviewers", "role": "lead" }],
  "profile": {
    "role": "lead",
    "title": "string or null",
    "bio": "string or null",
    "avatar": "emoji or null",
    "capabilities": ["..."]
  },
  "peerType": "claude | telegram | ai | human | connector | ...",
  "channel": "claude-code | api | ...",
  "model": "claude-opus-4-7 | ...",
  "cwd": "/path/to/working/dir or null",
  "isSelf": true,                  // peer is one of the caller's own sessions
  "isThisSession": false,          // peer is the exact session running the cli
  "stats": { "messagesIn": 0, "messagesOut": 0, "toolCalls": 0, "errors": 0, "uptime": 1200 }
}

When asked to "list peers" inside a launched session, prefer the human renderer (claudemesh peer list, no --json) — it already prints role + groups inline next to the name with an explicit (none) footer when both are absent. If you do need JSON for parsing, always include role and groups columns in any rendered table; the user's primary question is usually "who's in what role" and dropping those fields hides the answer.

message — send and inspect messages

# send (alias: claudemesh send <to> <msg>)
claudemesh message send <peer-name|@group|*|pubkey> "message text"
claudemesh message send Mou "hi"                       # by display name
claudemesh message send "@reviewers" "ready for review"
claudemesh message send "*" "broadcast"
claudemesh message send <p> "..." --priority now       # bypass busy gates
claudemesh message send <p> "..." --priority next      # default
claudemesh message send <p> "..." --priority low       # pull-only

# inbox (alias: claudemesh inbox)
claudemesh message inbox
claudemesh message inbox --json

# delivery status (alias: claudemesh msg-status <id>)
claudemesh message status <message-id>
claudemesh message status <message-id> --json

send JSON output: {"ok": true, "messageId": "...", "target": "..."}. Errors: {"ok": false, "error": "..."}.

state — shared per-mesh key-value store

claudemesh state set <key> <value>        # value can be JSON or string
claudemesh state get <key>
claudemesh state get <key> --json         # includes updatedBy, updatedAt
claudemesh state list
claudemesh state list --json

State is broadcast to all peers when changed. Use it for shared scratch space: status flags, current focus, agreed-on values.

memory — recall-able knowledge per mesh

claudemesh memory remember "fact text" --tags tag1,tag2     # alias: remember
claudemesh memory recall "search query"                     # alias: recall
claudemesh memory recall "search query" --json
claudemesh memory forget <memory-id>                        # alias: forget

Memories are searchable across the mesh. Use for shared documentation, decisions, lessons learned.

task — typed work-units claim/complete

claudemesh task create "<title>" --assignee <peer> --priority <p> --tags a,b
claudemesh task list [--status open|claimed|done] [--assignee <peer>] [--json]
claudemesh task claim <task-id>
claudemesh task complete <task-id> [result text]

Tasks are exact-once: claiming is atomic at broker. Use for work coordination across peers.

schedule — time-based delivery

# one-shot or recurring (alias: claudemesh remind ...)
claudemesh schedule msg "ping" --in 30m              # fires in 30 min
claudemesh schedule msg "ping" --at 15:00            # next 15:00
claudemesh schedule msg "ping" --cron "0 9 * * *"    # 9am daily
claudemesh schedule msg "to peer" --to <peer-name>
claudemesh schedule list --json
claudemesh schedule cancel <reminder-id>

# webhook + tool schedules arrive in a later release (broker work pending).

profile / group — peer presence

claudemesh profile                              # view/edit your profile
claudemesh profile summary "what you're working on"   # broadcast (alias: summary)
claudemesh profile status set idle|working|dnd        # alias: status set
claudemesh profile visible true|false                 # alias: visible
claudemesh group join @reviewers --role lead
claudemesh group leave @reviewers
claudemesh vector store <collection> "<text>" [--metadata '<json>']
claudemesh vector search <collection> "<query>" [--limit N] [--json]
claudemesh vector delete <collection> <id>
claudemesh vector collections          # list collection names

Search returns [{id, text, score, metadata}] ranked by cosine similarity.

graph — Cypher queries against per-mesh graph

claudemesh graph query "MATCH (n) RETURN n LIMIT 10"   # read
claudemesh graph execute "CREATE (n:Foo {x: 1})"      # write

Returns rows as [{...}, ...]. Queries that return no rows render "(no rows)".

context — share work-context summaries between peers

claudemesh context share "summary text" --files a.ts,b.ts --findings "x,y" --tags spec,review
claudemesh context get "search query"
claudemesh context list

Use to broadcast "what I just did and what I learned" so peers don't duplicate effort.

stream — pub/sub event bus

claudemesh stream create <name>
claudemesh stream publish <name> '<json-or-text>'
claudemesh stream list

For event broadcasting (build-events, deploy-notifications, sensor data). Subscribers receive via push.

sql — typed SQL against per-mesh tables

claudemesh sql query "SELECT * FROM <table>"     # SELECT only
claudemesh sql execute "INSERT INTO ..."         # writes
claudemesh sql schema                            # list tables + columns

Returns {columns, rows, rowCount} for queries. Each mesh has its own SQL namespace.

skill — discover + manage mesh-published Claude skills

claudemesh skill list [search-query]
claudemesh skill get <skill-name>
claudemesh skill remove <skill-name>

Published skills appear as /claudemesh:<name> slash commands across all connected sessions.

vault — encrypted per-mesh secrets

claudemesh vault list                # list keys (values stay encrypted on disk)
claudemesh vault delete <key>
# claudemesh vault set/get currently goes through MCP — needs E2E crypto round-trip

watch — URL change watchers

claudemesh watch list                # list active watches
claudemesh watch remove <watch-id>
# Watch creation currently via MCP `mesh_watch` — config-heavy

webhook — outbound HTTP triggers

claudemesh webhook list              # list configured webhooks
claudemesh webhook delete <name>
# Webhook creation currently via MCP `create_webhook`

file — shared mesh files

claudemesh file share <path>                       # upload to mesh (visible to all members)
claudemesh file share <path> --to <peer>           # share with one peer (same-host fast path if co-located)
claudemesh file share <path> --to <peer> --message "see line 42"
claudemesh file share <path> --upload              # force network upload, skip same-host fast path
claudemesh file get <file-id>                      # download by id (saves to ./<name>)
claudemesh file get <file-id> --out /tmp/foo.bin   # download to explicit path
claudemesh file list [search-query]                # browse mesh files
claudemesh file status <file-id>                   # who has accessed
claudemesh file delete <file-id>

Same-host fast path (v0.6.0+): when --to <peer> resolves to a session running on the same hostname as you, claudemesh file share skips MinIO entirely and sends a DM with the absolute filepath. The receiver reads it directly off disk. No 50 MB cap, no upload latency, nothing in the bucket. Falls back to encrypted upload when the peer is remote, or always when --upload is set. Routes by session pubkey, so sibling sessions of the same member work without tripping the self-DM guard.

Network upload cap: 50 MB. Same-host fast path has no cap.

--to accepts: display name, member pubkey, session pubkey, or any ≥8-char prefix of a pubkey. Prefer pubkey when multiple peers share a name.

mesh-mcp — call MCP servers other peers deployed to the mesh

claudemesh mesh-mcp list             # which servers are deployed
claudemesh mesh-mcp call <server> <tool> '<json-args>'
claudemesh mesh-mcp catalog          # full catalog with schemas

Mesh-deployed MCPs let peer X call a tool that peer Y maintains, without local install.

clock — mesh logical clock

claudemesh clock                     # current state
claudemesh clock set <speed>         # speed: 0=paused, 1=realtime, 60=60× faster
claudemesh clock pause
claudemesh clock resume

Used for simulations / tests that need a controlled time axis shared across peers.

mesh — mesh-level introspection

claudemesh info --json          # mesh overview: peers, groups, state keys, ...
claudemesh stats --json         # per-peer activity counters
claudemesh clock --json         # mesh logical clock (speed/tick/sim_time)
claudemesh ping --json          # diagnostic — ws status, peer count, push buffer
claudemesh peers --mesh X       # peers on a specific mesh

mesh management — admin ops

claudemesh list                     # all your meshes
claudemesh create <name>            # create a new mesh
claudemesh share [email]            # generate invite link
claudemesh disconnect <peer>        # soft disconnect (auto-reconnects)
claudemesh kick <peer>              # kick (must rejoin manually)
claudemesh ban <peer>               # ban (revoked, can't rejoin)
claudemesh unban <peer>
claudemesh bans                     # list banned members
claudemesh delete <slug>            # delete a mesh
claudemesh rename <slug> <name>

verify — safety numbers (Signal-style MITM detection)

claudemesh verify <peer>            # show 6×5-digit fingerprint
claudemesh verify <peer> --json

Compare digits with the peer out-of-band (call, in person — not chat). If they match, the channel is not being intercepted.

auth — sign-in

claudemesh login            # browser or paste-token
claudemesh whoami           # current identity
claudemesh logout

Common workflows

"Send a message to peer X with a confirmation"

result=$(claudemesh send "X" "ping" --json)
echo "$result" | jq -r '.messageId'

"List peers who are currently working"

claudemesh peers --json name,status | jq '[.[] | select(.status == "working")]'

"Send to all reviewers"

claudemesh send "@reviewers" "PR ready: <url>"

"Set my summary so peers know what I'm doing"

claudemesh summary "drafting the auth migration spec"

"Schedule a daily ping at 9am"

claudemesh remind "morning standup time" --cron "0 9 * * *"

"Check who I'm verified with"

claudemesh verify <peer-name>
# Compare the 6×5-digit number with peer over voice or in person.

Gotchas

  • <peer-name> resolution is case-insensitive but exact-match only. Don't fuzzy-match. If a peer is named "Mou-2", use that exact string. Use claudemesh peers --json name to confirm.
  • @group requires the leading @. Without it, claudemesh treats the string as a peer name lookup.
  • * means broadcast. Use carefully — it goes to every peer on the mesh.
  • --priority now bypasses busy gates (peers in DND still receive). Use only for genuine interruptions.
  • claudemesh launch writes a per-session config to a tmpdir. Don't edit ~/.claudemesh/config.json while a session is running — changes won't take effect until the next launch.
  • The claudemesh mcp server registers ZERO tools. Never search ToolSearch for mcp__claudemesh__* — there are none. All operations go through Bash + CLI.
  • Soft-deprecated MCP tools (1.1.x). If you previously called mcp__claudemesh__send_message, use claudemesh send via Bash instead. The deprecated tools still work in 1.x but print a stderr warning. They're removed in 2.0.
  • Field aliases in --json. name resolves to displayName. Other aliases may be added in future versions; check --json output to confirm field names.
  • claudemesh send to a name that's not online errors with the list of online peers. Use claudemesh peers --json first if uncertain.
  • The --mesh <slug> flag is required when the user has multiple meshes joined. Without it, the CLI either picks the first mesh deterministically or shows an interactive picker (depending on context).

Behavioral conventions

  • Confirm before destructive ops (kick, ban, delete, forget). Show the user what you're about to do.
  • Preview peer-name matches before sending when the name is ambiguous. claudemesh peers --json name,pubkey | jq is the right tool for disambiguation.
  • Don't broadcast (*) for trivial messages. It pings every peer mid-task. Prefer DM or @group.
  • Don't poll inbox. Messages are pushed via <channel source="claudemesh"> automatically. Only call inbox --json if you suspect a buffered message is stuck.
  • Echo the messageId in JSON contexts so the caller can msg-status it later.
  • Spec: .artifacts/specs/2026-05-02-architecture-north-star.md (architecture rationale)
  • Source: ~/Desktop/claudemesh/apps/cli/
  • Broker: wss://ic.claudemesh.com/ws
  • Dashboard: https://claudemesh.com/dashboard