ships v0.4.0 phase 3.
api: GET /v1/me/notifications aggregates the mesh.notification
table across every joined mesh in a 7-day window (?since=iso
overrides, ?include=all surfaces already-read). returns sender +
topic + mesh context plus a 240-char snippet for v1 plaintext
messages or raw ciphertext for v2 (the dashboard topic-key cache
decrypts client-side).
cli (1.12.0): claudemesh me notifications — terse unread feed
with @ dot, --all to include read, --since for custom window.
web: /dashboard/notifications mirrors the cli view in card form,
adds a notifications entry to the dashboard sidebar between
topics and invites. each card links straight to the topic chat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ships v0.4.0 phase 2: a cross-mesh topic feed.
api: GET /v1/me/topics aggregates topics across every mesh the
caller belongs to with per-topic unread counts (vs the user's
member-row last_read_at) and last-message timestamps. Sorted by
last activity.
cli (1.11.0): claudemesh me topics renders the feed; --unread
filters to topics with pending reads; --json returns raw.
web: /dashboard/topics ssr's the same view server-side (direct
db queries, no apikey-mint roundtrip) and adds a Topics entry
to the dashboard sidebar between Meshes and Invites.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/cli/src/cli/validators.ts — a small module of shape
validators (pubkey, pubkey prefix, message id, mesh slug) that return
discriminated results so callers can distinguish "shape is wrong"
(INVALID_ARGS exit) from "value is well-shaped, lookup failed"
(NOT_FOUND exit). Includes renderValidationError() for a consistent
three-tier error contract: what's wrong, what would be valid, closest
valid alternative.
First adopter is `claudemesh msg-status`:
- Validates id locally before opening WS — typos return immediately.
- Accepts 8-32 char prefixes (full ids are 32). Pastes that get
copy-truncated by the terminal still work.
- Distinct error messages for malformed input vs not-in-queue vs
ambiguous prefix; --json emits the structured shape.
Broker side: WS message_status handler validates idStr is 8-32
base62 before querying. Prefix lookups use LIKE 'prefix%' scoped to
the caller's mesh (no cross-mesh leak). Returns ambiguous_prefix
when more than one match.
Establishes the canonical pattern; rolling out to send / grant /
revoke / topic post --reply-to in subsequent patches.
Adds a reply_to_id column (self-FK on topic_message) plus end-to-end
plumbing so a message can mark itself as a reply to a previous one in
the same topic.
- Schema: 0027_topic_message_reply_to.sql adds reply_to_id with
ON DELETE SET NULL + index for backlink lookup.
- Broker: appendTopicMessage validates parent shares the topic, writes
reply_to_id; topicHistory + topic_history_response surface it; WS
push envelope now carries senderMemberId, senderName, topic name,
reply_to_id, and message_id so recipients have everything they need
to reply without a follow-up query.
- REST: POST /v1/messages accepts replyToId (validated server-side);
GET /messages and SSE /stream emit it per row.
- CLI: \`topic post --reply-to <id|prefix>\` resolves prefixes against
recent history; \`topic tail\` renders an "↳ in reply to <name>:
<snippet>" line above replies and shows a copyable #shortid tag on
every row.
- MCP push pipe: channel attributes now include from_pubkey,
from_member_id, message_id, topic, reply_to_id — the recipient can
thread a reply directly from the inbound notification.
- Skill + identity prompt updated to teach Claude how to use the new
attributes for replies.
Bumped CLI to 1.9.0.
Wire format:
topic_member_key.encrypted_key = base64(
<32-byte sender x25519 pubkey> || crypto_box(topic_key)
)
Embedding sender pubkey inline lets re-sealed copies (carrying a
different sender than the original creator-seal) decode the same
way as creator copies, without an extra schema column or join.
topic.encrypted_key_pubkey stays for backwards-compat metadata
but the wire truth is the inline prefix.
API (phase 3):
GET /v1/topics/:name/pending-seals list members without keys
POST /v1/topics/:name/seal submit a re-sealed copy
POST /v1/messages now accepts bodyVersion (1|2); v2 skips the
regex mention extraction (server can't read v2 ciphertext).
GET /messages + /stream now return bodyVersion per row.
Broker + web mutations updated to use the inline-sender format
when sealing. ensureGeneralTopic (web) also generates topic keys
per the bugfix that landed earlier today; both producers now
share one wire format.
CLI (claudemesh-cli@1.8.0):
+ apps/cli/src/services/crypto/topic-key.ts — fetch/decrypt/encrypt/seal
+ claudemesh topic post <name> <msg> — encrypted REST send (v2)
* claudemesh topic tail <name> — decrypts v2 on render, runs a
30s background re-seal loop for pending joiners
Web client stays on v1 plaintext until phase 3.5 (browser-side
persistent identity in IndexedDB). Mention fan-out from phase 1
already works for both versions, so /v1/notifications keeps
working through the cutover.
Spec at .artifacts/specs/2026-05-02-topic-key-onboarding.md
updated with the implemented inline-sender format and the
phase 3.5 web plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three new verbs that wrap the v1.6.x REST surface:
claudemesh topic tail <name> → live SSE consumer with N-message backfill
claudemesh member list → mesh roster decorated with online state
claudemesh notification list → recent @-mentions of you across topics
Each command auto-mints a 5-minute read-only apikey via the WS
broker and revokes on exit, so users don't manage tokens. SSE
client uses fetch + ReadableStream so the bearer stays in the
Authorization header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch release on top of 1.6.0:
- Revoke-by-id-prefix bug fix (broker.revokeApiKey now returns
structured status; CLI surfaces not_found / not_unique). Pasting
the 8-char prefix from `apikey list` output now works as users
expect, instead of silently no-op'ing with a misleading "✔
revoked" message. Already deployed to broker.
- whoami falls back to local mesh-config view when no web session
is signed in. Users who joined via invite (and never ran
`claudemesh login`) now see their member ids and pubkey prefixes
per mesh, instead of a "Not signed in" dead end.
- README updated: REST surface lives at claudemesh.com/api/v1/*
(web app), NOT ic.claudemesh.com/api/v1/* (broker). Surfaced
during CLI-only smoke test against prod when curl on the broker
host returned 404.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claudemesh apikey revoke <id> reported success even when the input
didn't match any row in mesh.api_key. The CLI's `apikey list` shows
truncated 8-char prefixes; users naturally paste those; broker did
exact-id match against meshApiKey.id; UPDATE affected 0 rows; old
revokeApiKey returned void so the CLI couldn't tell. Discovered via
end-to-end CLI smoke test against prod (roadmap validation pass).
Three-part fix:
- broker.revokeApiKey now returns
{ status: "revoked"|"not_found"|"not_unique"; id?, matches? } and
accepts either the full id or a unique prefix (>=6 chars). Prefix
matching is bounded to the caller's mesh and only succeeds if
exactly one row matches; ambiguous prefixes return not_unique so
we never silently revoke the wrong key.
- New WSApiKeyRevokeResponseMessage carries the structured status
back to the CLI. Old apikey_revoke_ok type removed before being
released — never shipped to users. The error path is no longer
used for not_found/not_unique cases; the unified response carries
both outcomes.
- CLI's apiKeyRevoke now resolves with { ok, id } | { ok: false,
code, message }. runApiKeyRevoke surfaces the code/message and
exits non-zero on failure (NOT_FOUND for missing, INVALID_ARGS
for ambiguous prefix).
Net effect: pasting `claudemesh apikey revoke vq0fwjdX` now actually
revokes the key whose id starts with vq0fwjdX (or fails loud if 0
or >1 keys match). Verified against prod via the new branch's CLI
binary before commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A bridge holds memberships in two meshes and relays messages on a
single topic between them. Federation-lite without a broker-to-broker
protocol.
SDK additions:
- Bridge class (start, stop, EventEmitter for forwarded/dropped/error)
- MeshClient.joinTopic / leaveTopic / createTopic methods
- Loop prevention: plaintext hop counter prefix __cmh<n>: with maxHops
default 2; echo guard via senderPubkey == own session pubkey
CLI additions:
- claudemesh bridge run <config.yaml> long-lived process
- claudemesh bridge init prints config template
- Zero-dep YAML parser for the flat bridge config shape
The hop prefix is visible in message bodies — minor wart, fixed in
v0.3.0 by moving loop tracking into broker primitives.
SDK kept as devDependency since Bun bundles it into dist; no impact
on npm publish or runtime resolution.
Spec: .artifacts/specs/2026-05-02-v0.2.0-scope.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Issuance flow over WS for now (REST endpoints come next slice).
Plaintext secret returned ONCE on create — never recoverable.
- broker: 3 WS handlers (apikey_create/list/revoke), wire types in
union, audit log on issuance + revoke
- ws-client: apiKeyCreate/List/Revoke with resolver maps, response
dispatch
- CLI: claudemesh apikey create <label> [--cap a,b] [--topic c,d]
[--expires ISO]; list shows status, scope, last-used; revoke by id
- policy: apikey create + revoke prompt by default (issuing or
disabling a credential is meaningful)
Default capability set is "send,read" — least privilege for unscoped
keys (admin must explicitly opt-in).
Spec: .artifacts/specs/2026-05-02-v0.2.0-scope.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Broker (apps/broker/src/index.ts)
- Unified disconnect/kick handler uses close code 1000 for disconnect
(CLI auto-reconnects) vs 4001 for kick (CLI exits, no reconnect).
- Ban now closes with code 4002.
- Hello handler: revoked members get a specific 'revoked' error with a
'Contact the mesh owner to rejoin' message, then ws.close(4002).
Previously banned users saw the generic 'unauthorized' error.
- list_bans handler returns { name, pubkey, revokedAt } for each
revoked member.
CLI (apps/cli)
- ws-client: close codes 4001 and 4002 set .closed = true and stash
.terminalClose so callers can surface a friendly message instead of
the low-level 'ws terminal close' error. Revoked error in hello is
also captured as a terminal close.
- withMesh catches terminalClose and prints:
4001 → 'Kicked from this mesh. Run claudemesh to rejoin.'
4002 → the broker's 'Contact the mesh owner to rejoin.' message
- kick.ts now exports runDisconnect + runKick with clear hints:
'disconnect' → 'They will auto-reconnect within seconds.'
'kick' → 'They can rejoin anytime by running claudemesh.'
- cli.ts adds 'disconnect' dispatch; HELP updated.
Semantics:
disconnect: session reset, no DB state, auto-reconnects
kick : session ends, no DB state, user must manually rejoin
ban : session ends + revokedAt set, cannot rejoin until unban
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promote CLI from 1.0.0-alpha.42 to stable 1.0.0 so
`npm i -g claudemesh-cli` installs the current release without
needing the @alpha dist-tag.
Both dist-tags now point at 1.0.0 — `@alpha` kept as an alias for
continuity so existing docs, install scripts, and scheduled upgrade
commands keep working.
upgrade + doctor commands updated to prefer the `latest` dist-tag
(falling back to `alpha`) and to suggest `npm i -g claudemesh-cli`
without the @alpha suffix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three bugs compounding when multiple peers share a display name:
1. list_peers (MCP + CLI) truncated pubkey to 12 hex chars with an
ellipsis. A truncated pubkey cannot be used as a routing key, so
the caller had no way to disambiguate visually.
2. send_message required the full 64-hex pubkey and refused prefix
input, forcing callers to rely on --json output to get a full key.
3. Name-based resolution returned the first exact match without
filtering the caller's own session — so "send to <my-own-name>"
would bounce against the broker's self-send guard when another
session of the same user was the intended target.
Fixes:
- list_peers now prints 16-char pubkey prefix labelled "pubkey: …"
(MCP) and appends it to CLI output
- send_message accepts any 8–64 hex-char prefix and resolves against
live peer lists across joined meshes; unique match routes, multi-
match returns a disambiguation error listing each candidate's
displayName + pubkey + cwd
- Name matches now skip the caller's own session pubkey; multiple
same-named matches fail loudly with a copy-pasteable pubkey
disambiguation hint instead of silently picking one
- Full 64-char pubkeys without a live match still queue at the
broker (preserves offline-delivery semantics)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
If the arg isn't a URL and matches a mesh already in local config,
print a hint pointing at `launch --mesh <slug>` instead of treating
the slug as an invite code. Avoids the 501 invite_v2_disabled confusion
when users try to "enter" a mesh they already own.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The peers command opens its own WS to each mesh, which briefly appears
as a hostname-PID peer. Filter it out by session pubkey.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Broker WS handlers:
- kick: disconnect peer(s) by name, --stale duration, or --all.
Authz: owner or admin only. Closes WS + marks presence disconnected.
- ban: kick + set revokedAt on mesh.member. Hello already rejects
revoked members, so ban is instant and permanent until unban.
- unban: clear revokedAt. Peer can rejoin with their existing keypair.
- list_bans: return all revoked members for a mesh.
Session-id dedup (previous commit): handleHello disconnects ghost
presences with matching (meshId, sessionId) before inserting the new
one. Eliminates duplicate entries after broker restarts.
CLI (alpha.37):
- claudemesh kick <peer|--stale 30m|--all>
- claudemesh ban/unban <peer>
- claudemesh bans [--json]
- Uses new sendAndWait() on ws-client for request-response pattern
over WS (generic _reqId resolver).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- info/inbox commands → unified render.ts
- install route: drop in-memory counter, rely on PostHog + structured logs
- docs: roadmap, CLAUDE.md reflect alpha.31 state
- tests workflow now also builds + smoke-tests the CLI bundle
- homebrew tap bootstrap kit in packaging/homebrew-tap-bootstrap/
(README + copy of the formula template for dropping into the tap repo)
- upstream Claude Code issue draft for rich <channel> UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- apps/cli/ is now the canonical CLI (was apps/cli-v2/).
- apps/cli/ legacy v0 archived as branch 'legacy-cli-archive' and tag
'cli-v0-legacy-final' before deletion; git history preserves it too.
- .github/workflows/release-cli.yml paths updated.
- pnpm-lock.yaml regenerated.
Broker-side peer-grant enforcement (spec: 2026-04-15-per-peer-capabilities):
- 0020_peer-grants.sql adds peer_grants jsonb + GIN index on mesh.member.
- handleSend in broker fetches recipient grant maps once per send, drops
messages silently when sender lacks the required capability.
- POST /cli/mesh/:slug/grants to update from CLI; broker_messages_dropped_by_grant_total metric.
- CLI grant/revoke/block now mirror to broker via syncToBroker.
Auto-migrate on broker startup:
- apps/broker/src/migrate.ts runs drizzle migrate with pg_advisory_lock
before the HTTP server binds. Exits non-zero on failure so Coolify
healthcheck fails closed.
- Dockerfile copies packages/db/migrations into /app/migrations.
- postgres 3.4.5 added as direct broker dep.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminals spawned by `claudemesh launch` were dropping keystrokes at
claude's prompt and showing the launch wizard re-rendering on top of
claude's TUI. Two compounding causes:
1. spawn() + child.on('exit') kept the parent node event loop alive
during claude's lifetime. Any stray readline 'data' listener or
late render from the wizard could fire on the inherited stdin/
stdout, stealing keystrokes or painting over claude's Ink TUI.
2. Raw mode / alt-screen / hidden cursor set by the wizard helpers
was not reliably restored before the handoff.
Fix:
- Swap spawn for spawnSync so the parent event loop is fully blocked
while claude runs. No listener or setImmediate can fire during
claude's lifetime.
- Hard TTY reset right before the spawn: setRawMode(false),
removeAllListeners on stdin, show cursor (ESC[?25h), exit alt
screen (ESC[?1049l). Defensive — survives partial wizard cleanup.
- Move cleanup() registration to process.on('exit') so it runs
synchronously on every exit path (normal, signal, throw).
- Preserve signal forwarding: if claude dies from a signal, re-raise
the same signal on the parent so exit codes propagate correctly.
Bumps to v0.10.6.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wires the v2 invite protocol end-to-end from a CLI user's perspective.
Broker foundation landed in c1fa3bc; this commit is the glue between
it and the human.
API (packages/api)
- createMyInvite now mints BOTH v1 token (legacy) AND v2 capability.
Two-phase insert: row first (to get invite.id), then UPDATE with
signed canonical bytes stored as JSON {canonical, signature} in the
capabilityV2 column. Broker's claim handler parses the same shape.
- canonicalInviteV2 locked to `v=2|mesh_id|invite_id|expires_at|role|
owner_pubkey_hex` — byte-identical to apps/broker/src/crypto.ts.
- brokerHttpBase() helper rewrites wss://host/ws → https://host for
server-to-server calls.
- POST /api/public/invites/:code/claim — thin proxy to broker;
passes status + body through, 502 broker_unreachable on fetch fail,
cache-control: no-store.
- POST /api/my/meshes/:id/invites/email — mints a normal v2 invite
via createMyInvite, records a pending_invite row, calls stubbed
sendEmailInvite (logs TODO for Postmark wiring in a later PR).
- New schemas: claimInviteInput/ResponseSchema,
createEmailInviteInput/ResponseSchema, v2 fields on
createMyInviteResponseSchema.
- v1 paths untouched — legacy /join/[token] and /api/public/invite/:token
continue to work throughout v0.1.x.
CLI (apps/cli)
- New `claudemesh join <code-or-url>` subcommand.
- Accepts bare code (abc12345), short URL (claudemesh.com/i/abc12345),
or legacy ic://join/<token>. Detects v2 vs v1 and dispatches.
- v2 path: generates fresh ephemeral x25519 keypair (separate from
the ed25519 identity) → POST /api/public/invites/:code/claim →
unseals sealed_root_key via crypto_box_seal_open → persists mesh
with inviteVersion: 2 and base64url rootKey to local config.
- Signature verification skipped with TODO — v0.1.x trusts broker;
seal-open is already authenticated.
- apps/cli/src/lib/invite-v2.ts: generateX25519Keypair, claimInviteV2,
parseV2InviteInput.
- state/config.ts: additive rootKey?/inviteVersion? fields.
CLI friction reducer
- apps/cli/src/index.ts: flag-first invocations
(`claudemesh --resume xxx`, `claudemesh -c`, `claudemesh -- --model
opus`) now route through `launch` automatically. Bare `claudemesh`
still shows welcome; known subcommands dispatch normally.
- Removes one word of cognitive load: users never type `launch`.
No schema changes. No new deps. v1 fully backward compatible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
claudemesh launch now supports:
--resume <id> / -r — resume a previous Claude Code session
--continue / -c — continue the most recent conversation
When resuming, skips generating a new session ID so the mesh peer
identity persists. The detectClaudeSessionId() fallback in ws/client.ts
picks up the existing session UUID from the .jsonl file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
claudemesh launch now generates a UUID and passes it to claude via
--session-id flag + CLAUDEMESH_SESSION_ID env var. The MCP server
reads this and sends it in the hello handshake.
Fallback: when launched without claudemesh launch (e.g., claude --resume),
detectClaudeSessionId() scans ~/.claude/projects/ for the most recent
.jsonl file and extracts the session UUID from the filename.
Benefits:
- Broker detects reconnections (same session = restore state)
- Multiple peers in same project dir get unique identities
- Session identity persists across --resume
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the foundation for deploying and managing MCP servers on the VPS
broker, with per-peer credential vaults and visibility scopes.
Architecture:
- One Docker container per mesh with a Node supervisor
- Each MCP server runs as a child process with its own stdio pipe
- claudemesh launch installs native MCP entries in ~/.claude.json
- Mid-session deploys fall back to svc__* dynamic tools + list_changed
New components:
- DB: mesh.service + mesh.vault_entry tables, mesh.skill extensions
- Broker: 19 wire protocol types, 11 message handlers, service catalog
in hello_ack with scope filtering, service-manager.ts (775 lines)
- CLI: 13 tool definitions, 12 WS client methods, tool call handlers,
startServiceProxy() for native MCP proxy mode
- Launch: catalog fetch, native MCP entry install, stale sweep, cleanup,
MCP_TIMEOUT=30s, MAX_MCP_OUTPUT_TOKENS=50k
Security: path sanitization on service names, column whitelist on
upsertService, returning()-based delete checks, vault E2E encryption.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After MCP registration and hooks setup, `claudemesh install` now checks
the config for joined meshes. If empty, it prints actionable guidance
(join command + dashboard URL) instead of the generic "Next:" line.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace in-memory-only setTimeout scheduling with a DB-backed system
that survives broker restarts. Adds:
- `scheduled_message` table in mesh schema (Drizzle + raw CREATE TABLE
for zero-downtime deploys)
- Minimal 5-field cron parser (no dependencies) with next-fire-time
calculation for recurring entries
- On broker boot, all non-cancelled entries are loaded from PostgreSQL
and timers re-armed automatically
- CLI `schedule_reminder` MCP tool accepts optional `cron` expression
- CLI `remind` command accepts `--cron` flag
- One-shot reminders remain backward compatible — no cron field = same
behavior as before
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 4 missing tools (cancel_scheduled, grant_file_access, list_scheduled,
schedule_reminder) and sort the array alphabetically for maintainability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Predefined mesh configurations (dev-team, research, ops-incident,
simulation, personal) let users bootstrap meshes with groups, roles,
state keys, and system prompt hints. Templates are bundled at build
time via Bun's JSON import support.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extend the WS hello handshake with optional peerType, channel, and model
fields so peers can advertise what kind of client they are. The broker
stores these in-memory on PeerConn and returns them (along with cwd) in
the peers_list response. CLI peers command and MCP list_peers tool now
display the new metadata.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace manual switch + HELP string with citty defineCommand/runMain.
Flag definitions in index.ts are now the single source of truth for
--help output. Remove parseArgs() from launch.ts; accept citty-parsed
flags + rawArgs (-- passthrough to claude preserved).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
broker: expand member groups to ancestor paths at drain time (pull model)
- @flexicar message reaches peers in @flexicar/core, @flexicar/output, etc.
- Resolved at drainForMember — no DB changes, fully backward-compatible
- Any depth: flexicar/team/backend also matches @flexicar and @flexicar/team
cli: wire --role all the way through to session config + env
- Config.role field added
- launch.ts stores role in sessionConfig, passes CLAUDEMESH_ROLE env var
- mcp/server.ts includes role in identity string
- manager.ts auto-joins groups from config on WS connect (--groups flag now works)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
--inbox: count-only notifications, no content in context
--no-messages: tools only, zero prompt injection risk
Default: push (real-time, current behavior)
Wizard shows mode picker when no flag provided.
MCP instructions tell Claude its current mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase A of the claudemesh spec. Peers can now join named groups
with roles, and messages route to @group targets.
Broker:
- @group routing in fan-out (matches peer group membership)
- @all alias for broadcast
- join_group/leave_group WS messages + DB persistence
- list_peers returns group metadata
- drainForMember matches @group targetSpecs in SQL
CLI:
- join_group/leave_group MCP tools
- send_message supports @group targets
- list_peers shows group membership
- PeerInfo includes groups array
- Peer name cache for push notifications
Launch:
- --role flag (optional peer role)
- --groups flag (comma-separated, e.g. "frontend:lead,reviewers")
- Interactive wizard for role + groups when flags omitted
- Groups written to session config for broker hello
Spec: SPEC.md added with full v0.2 vision (groups, state, memory)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Write displayName into tmpdir config.json so the MCP server reads
it directly. Env vars from claudemesh launch may not propagate to
MCP child processes spawned by Claude Code. Config file is reliable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
claudemesh launch now passes --dangerously-skip-permissions to
claude so peers can chat without per-tool-call approval prompts.
Shows a clear explanation before launch; user confirms with Enter.
Skip with -y/--yes for CI or repeat launches.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each WS connection generates its own ed25519 keypair (sessionPubkey)
sent in the hello handshake. The broker stores it on the presence
row and uses it for message routing + list_peers. This gives every
`claudemesh launch` a unique crypto identity without burning invite
uses — member auth stays permanent, session identity is ephemeral.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
withPayload crashes ALL routes with React #130 in standalone
output — even with admin page replaced by redirect. The wrapper
injects a client-side ConfigProvider that fails hydration.
Removed: withPayload wrapper, entire (payload) route group.
Kept: payload.config.ts, migrations, blog/changelog server-side
queries with graceful DB fallback.
Payload admin runs on local dev only (add withPayload back in
next.config when running pnpm dev). Production content via
static TSX pages or future API-based publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove Payload's /api/[...slug] route that conflicts with existing
/api/[...route]. Blog/changelog pages use Payload's local API.
Includes cli install.ts backup + assertNoMcpLoss guards (from
worktree agent).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>