Skip to content

Commit bb042cf

Browse files
authored
Rewrite README for consumers; relocate developer content to docs/ (#3)
The README targeted developers — architecture map, workspace package table, the full self-hosting env contract. This rewrites it to address **consumers**: people installing the commy plugin so their Claude Code agents (and the humans alongside them) can talk to each other across sessions and machines. The register leans into the "commie" pun — comrades, the collective, seize the means of communication — confidently silly, but the install path stays plain underneath. **Install path kept accurate:** `claude plugin marketplace add CodeForBreakfast/commy`, `claude plugin install commy@commy`, the three required userConfig, and the sole prerequisite — `bun ≥ 1.3.13` on PATH, no Nix (matching the launcher landed in #2). Versioning section stays truthful (curated GitHub Release model). **Displaced developer/operator content is relocated, not dropped:** - `docs/architecture.md` — hexagonal overview, workspace package map, substrate-rationale and inbound-contract pointers. - `docs/self-hosting.md` — full environment contract, running outside Claude Code, the persistent post-only bot shape, and the inbound-is-host-work contract. - `AGENTS.md` front-door pointer repointed from the README to those two docs. Gate green locally on this base (`nix develop .#ci` → `bun install --frozen-lockfile && bun run check`: 847 tests, 0 fail, all 4 turbo tasks).
1 parent a2827ac commit bb042cf

4 files changed

Lines changed: 244 additions & 198 deletions

File tree

‎AGENTS.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# Agent Instructions
22

33
commy is a hexagonal MCP substrate for inter-agent communication — see
4-
[README.md](README.md) for the architecture overview and workspace package
5-
map, and [docs/](docs/) for the substrate rationale ([why
6-
Zulip](docs/why-zulip.md)), [bot naming conventions](docs/naming.md), and the
7-
[inbound event contract](docs/claude-channel-inbound-contract.md).
4+
[docs/architecture.md](docs/architecture.md) for the architecture overview and
5+
workspace package map, [docs/self-hosting.md](docs/self-hosting.md) for the
6+
operator's environment contract, and [docs/](docs/) for the substrate rationale
7+
([why Zulip](docs/why-zulip.md)), [bot naming conventions](docs/naming.md), and
8+
the [inbound event contract](docs/claude-channel-inbound-contract.md). The
9+
[README](README.md) is the consumer-facing front door.
810

911
## Build & Test
1012

‎README.md‎

Lines changed: 86 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -1,236 +1,128 @@
11
# commy
22

3-
A hexagonal (ports & adapters) [MCP][mcp] substrate for inter-agent
4-
communication. Agents — and the humans alongside them — discover each other,
5-
post into shared channels and threads, react, and read history through a small
6-
set of MCP tools, without coupling to any particular chat backend.
7-
8-
The domain core speaks in ports (`MessagePublisher`, `MessageInbox`,
9-
`HistoryReader`, `IdentityPort`, `Directory`). V1 is wired to a single **driven
10-
adapter backed by [Zulip][zulip]**: a Zulip realm provides the channels,
11-
threads, reactions, and presence; a *minter* user owns the per-agent bot
12-
identities the substrate hands out. Substrate selection becomes pluggable once a
13-
second adapter exists — the core has no Zulip in it.
3+
*Comrade — your agents have been labouring in isolation. That ends now.*
144

15-
[mcp]: https://modelcontextprotocol.io
16-
[zulip]: https://zulip.com
5+
**commy** is the people's substrate for inter-agent communication: a [Model
6+
Context Protocol][mcp] plugin that lets your Claude Code agents — and the humans
7+
toiling alongside them — talk to one another across sessions, across machines,
8+
across the whole collective. One agent posts; the rest read. Solidarity for your
9+
context windows.
1710

18-
## Architecture
11+
No more lonely sessions grinding away in parallel, each ignorant of the others'
12+
labour. commy gives every agent a seat in the shared channel: they discover each
13+
other, post into common threads, react, and read the history of the struggle —
14+
all through a handful of MCP tools. **Seize the means of communication.**
1915

20-
One line: **domain ports in `core` ← driven Zulip adapter in `zulip` → driving
21-
MCP adapter in `mcp`, composed bottom-up into a single Effect and run at the MCP
22-
SDK boundary.** The codebase is built on [Effect][effect].
23-
24-
[effect]: https://effect.website
16+
[mcp]: https://modelcontextprotocol.io
2517

26-
| Workspace package | Role |
27-
|---|---|
28-
| `@commy/core` | Domain ports, branded value types, and errors. No I/O, no Zulip. |
29-
| `@commy/zulip` | Driven adapter — implements the ports against a Zulip realm's HTTP + events APIs. |
30-
| `@commy/mcp` | Driving adapter — exposes the ports as MCP tools, plus bootstrap/identity lifecycle and the inbound event pump. |
31-
| `@commy/memory` | In-memory adapter used as a fast contract-test double for the ports. |
32-
| `@commy/testing` | Shared port contract tests, run against every adapter. |
33-
| `commy-plugin` | The Claude Code client adapter that packages the MCP server. Lives under `clients/` as a peer to future per-client adapters. See [`clients/claude-code/README.md`](clients/claude-code/README.md). |
18+
## What the collective gets you
3419

35-
The plugin README documents the tool surface, the inbound `<channel>` event
36-
format, the boot/identity model, and troubleshooting. Read it for anything about
37-
running commy *inside Claude Code* specifically.
20+
- **Agents that talk to each other.** A worker finishes a task and posts the
21+
result; a sibling session three machines away reads it and carries on. No
22+
shared filesystem, no copy-paste between terminals.
23+
- **Humans in the same room.** You're a comrade too. Post from your phone, get
24+
pinged when an agent needs a decision, answer inline. Agents and people share
25+
the channel as equals.
26+
- **Channels and threads.** One channel per project, named threads for distinct
27+
lines of work. The conversation has structure, not just a firehose.
28+
- **History that outlives the session.** Reactions, replies, and read-history
29+
survive any one context window. A fresh agent reads the thread and knows where
30+
the work got to.
3831

39-
## Installing
32+
Under the hood it speaks plain MCP and is wired to a [Zulip][zulip] realm for the
33+
channels, threads, reactions, and presence. You bring the realm; commy brings the
34+
comrades.
4035

41-
commy has two client adapters. Both talk to the same MCP server and need the
42-
same realm credentials — see [Self-hosting](#self-hosting-bring-your-own-realm)
43-
first, because commy has no hosted service: you supply your own Zulip realm and
44-
minter user before either client can connect.
36+
[zulip]: https://zulip.com
4537

46-
### Claude Code plugin
38+
## Installing — enlist your agents
4739

48-
The plugin ships from this repo's marketplace
49-
(`.claude-plugin/marketplace.json`). Register the marketplace and install:
40+
commy ships as a Claude Code plugin from the Code For Breakfast marketplace.
41+
Register the marketplace once, then install:
5042

5143
```sh
5244
# Register the Code For Breakfast marketplace (one-time, user scope).
5345
claude plugin marketplace add CodeForBreakfast/commy
5446

55-
# Install the plugin.
47+
# Enlist.
5648
claude plugin install commy@commy
5749
```
5850

59-
On first enable the plugin prompts for the three realm credentials
60-
(`ZULIP_SITE`, `ZULIP_MINTER_EMAIL`, `ZULIP_MINTER_API_KEY`) and the optional
61-
`COMMY_SUBSCRIBE`; the API key lands in the system keychain. To set them
62-
non-interactively, pass `--config ZULIP_SITE=… --config ZULIP_MINTER_EMAIL=…`
63-
(repeatable) on `install`. After install, `/mcp` lists `commy` in any Claude
64-
Code session running as the same user.
65-
66-
`claude plugin update commy@commy` pulls the latest released tag.
67-
The plugin requires [Nix](https://nixos.org/download) on the host PATH — it
68-
launches its pinned Bun via the plugin's own flake. Full configuration,
69-
run-shapes, and troubleshooting are in
70-
[`clients/claude-code/README.md`](clients/claude-code/README.md).
71-
72-
### Hermes adapter
51+
On first enable the plugin asks for **three credentials** — the realm and the
52+
minter user that owns your agents' bot identities (see
53+
[Bring your own realm](#bring-your-own-realm-no-central-committee-hosts-this)):
7354

74-
For non-Claude-Code hosts, `clients/hermes/` is a
75-
[Hermes Agent](https://github.com/NousResearch/hermes-agent) platform plugin
76-
that presents commy as a gateway platform. It is loaded by Hermes' directory
77-
scan (not a pip wheel), reads the same realm credentials plus `COMMY_SERVER_DIR`
78-
(a commy checkout) from the environment, and manages a per-topic connection
79-
lifecycle. The receive path and connection lifecycle are wired; **automated pod
80-
install into `~/.hermes/plugins/` is still in progress** (`comms-a7j.7`). See
81-
[`clients/hermes/README.md`](clients/hermes/README.md) for the current wiring,
82-
the environment contract, and how to run its tests.
83-
84-
## Build & test
85-
86-
Requires [Bun][bun] (the version is pinned in `package.json` under
87-
`packageManager`).
55+
| Config | Required | What it is |
56+
|---|---|---|
57+
| `ZULIP_SITE` | yes | Base URL of your Zulip realm, e.g. `https://chat.example.com`. |
58+
| `ZULIP_MINTER_EMAIL` | yes | Email of the minter user that owns every agent bot. Must be in the realm's `can_create_bots_group`. |
59+
| `ZULIP_MINTER_API_KEY` | yes | The minter's API key. Stored in the system keychain — never in `settings.json`. |
8860

89-
```sh
90-
bun install # install workspace deps
91-
bun run check # all quality gates via turbo: typecheck → lint → test (cached)
92-
bun run lint:fix # biome auto-fix (interactive use only)
93-
```
61+
There's also an optional `COMMY_SUBSCRIBE` (comma-separated auto-subscribe
62+
tokens, e.g. `channel:my-project,mentions`) for agents that should already be
63+
listening the moment they boot. To set any of these non-interactively, repeat
64+
`--config KEY=value` on the `install` line.
9465

95-
Always use `bun run check` for quality gates. The individual `test` /
96-
`typecheck` / `lint` scripts refuse to run outside turbo — they exist only as
97-
turbo task targets, so turbo's dependency ordering and caching are always in
98-
play.
66+
After install, `/mcp` lists `commy` in any Claude Code session running as the
67+
same user. `claude plugin update commy@commy` pulls the latest released tag.
9968

100-
Lint runs in two layers: **biome** for JS/TS style and generic lint, and
101-
**@effect/language-service** for Effect idiom (surfaced under `tsc --noEmit`, so
102-
it shows up in `bun run check`).
69+
> **One dependency:** the plugin runs on [Bun][bun], so all it needs is `bun`
70+
> (≥ 1.3.13) on the host `PATH` — nothing else. Full plugin configuration,
71+
> run-shapes, and troubleshooting live in
72+
> [`clients/claude-code/README.md`](clients/claude-code/README.md).
10373
10474
[bun]: https://bun.sh
10575

106-
### Live tests
107-
108-
`*.live.test.ts` files hit a real Zulip realm and can rate-limit other clients
109-
sharing it, so they are excluded from default discovery and gated on env vars.
110-
Set the live-test env (see below) and run:
76+
### Not running Claude Code?
11177

112-
```sh
113-
bun run test:live
114-
```
78+
For other hosts, `clients/hermes/` is a
79+
[Hermes Agent](https://github.com/NousResearch/hermes-agent) platform plugin that
80+
presents commy as a gateway platform. It reads the same realm credentials plus
81+
`COMMY_SERVER_DIR` (a commy checkout) from the environment. The receive path and
82+
connection lifecycle are wired; **automated install into `~/.hermes/plugins/` is
83+
still in progress** (`comms-a7j.7`). See
84+
[`clients/hermes/README.md`](clients/hermes/README.md) for the current wiring.
11585

116-
Without the env vars set, the live suite skips silently and the default `bun run
117-
check` stays green.
86+
## Bring your own realm (no central committee hosts this)
11887

119-
## Self-hosting: bring your own realm
88+
commy has no hosted service — there is no central committee running a server for
89+
you. You supply **your own [Zulip][zulip] realm** and a **minter user** on it.
90+
The minter is a human-type Zulip user that owns every agent bot the substrate
91+
hands out; it must belong to the realm's `can_create_bots_group`. Those are the
92+
three credentials the plugin prompts for above.
12093

121-
commy has no hosted service. To run it you supply **your own Zulip realm**
122-
and a **minter user** on that realm. The minter is a human-type Zulip user that
123-
owns every bot identity the substrate mints; it must be a member of the realm's
124-
`can_create_bots_group`.
94+
The full operator's manual — every environment variable, persistent vs.
95+
ephemeral identities, running the bare MCP server outside Claude Code, and the
96+
post-only bot shape — is in [`docs/self-hosting.md`](docs/self-hosting.md).
12597

126-
### Environment contract
98+
## How it's built
12799

128-
Three required credentials identify the realm and the minter. They are the same
129-
values whether you run the MCP server directly or via the Claude Code plugin
130-
(where they are prompted as plugin `userConfig` — the API key lands in the system
131-
keychain, never `settings.json`).
100+
commy is a hexagonal (ports & adapters) substrate: a Zulip-free domain core, a
101+
driven Zulip adapter, and a driving MCP adapter, composed bottom-up on
102+
[Effect][effect]. The workspace package map, the architecture rationale, and the
103+
host-neutral inbound event contract are documented for contributors in
104+
[`docs/architecture.md`](docs/architecture.md), with build and contribution
105+
workflow in [AGENTS.md](AGENTS.md).
132106

133-
| Env var | Required | Purpose |
134-
|---|---|---|
135-
| `ZULIP_SITE` | yes | Base URL of the Zulip realm, e.g. `https://chat.example.com`. Used by every Zulip HTTP call. |
136-
| `ZULIP_MINTER_EMAIL` | yes | Delivery email of the minter user that owns all managed bots. Must be in the realm's `can_create_bots_group`. |
137-
| `ZULIP_MINTER_API_KEY` | yes | The minter's API key. Used to mint and regenerate bot credentials. Sensitive — keep it out of source control. |
138-
139-
Optional knobs that shape boot-time behaviour (all default to sensible
140-
no-op-ish values when unset):
141-
142-
| Env var | Required | Purpose |
143-
|---|---|---|
144-
| `COMMY_BOT_NAME` | no | Persistent mode: a stable identity acquired eagerly at boot (for concierges / scheduled agents). Omit for ephemeral, per-session identities. |
145-
| `COMMY_PROJECT` | no | Project slug used for channel naming and the concierge's project subscriptions. When unset it is derived per-session from the calling cwd (git remote / git root). |
146-
| `COMMY_SUBSCRIBE` | no | Comma-separated auto-subscribe tokens applied at boot: `channel:<name>`, `thread:<channel>/<thread>`, `new-topics:<channel>`, `mentions`. Blank means no auto-subscription. |
147-
| `COMMY_CATCHUP_WINDOW_SECONDS` | no | How far back to fetch recent messages across the boot-time subscribe set on a persistent restart. Default `14400` (4 hours); `0` disables. |
148-
149-
The live-test suite additionally needs a channel to exercise against:
150-
151-
| Env var | Purpose |
152-
|---|---|
153-
| `ZULIP_LIVE_CHANNEL_NAME` | Name of an existing channel the live tests post into. |
154-
| `ZULIP_LIVE_CHANNEL_ID` | Id of that same channel. |
155-
156-
A template for the live-test env lives at `.env.example` — copy it to
157-
`.env.local` and supply your own realm's values.
158-
159-
### Running outside Claude Code
160-
161-
The MCP server is a plain **stdio** server with no Claude Code dependency at
162-
runtime: any host that can spawn a subprocess and speak MCP over stdin/stdout
163-
can drive it. Claude Code (via the plugin) is one such host; it is not required.
164-
The entry point is `packages/mcp/server.ts`, run under [Bun][bun]:
165-
166-
```sh
167-
bun packages/mcp/server.ts
168-
```
169-
170-
Node is not a supported runtime — the process boots through `@effect/platform-bun`
171-
and shells out via `Bun.spawnSync`, so a host that only has `node` must still
172-
put `bun` on its `PATH`. `nix` and `CLAUDE_PLUGIN_ROOT` are **not** runtime
173-
dependencies (the `nix run …#default` wrapper and `CLAUDE_PLUGIN_ROOT` exist
174-
only in the plugin's launcher metadata); invoke `bun` against a checkout
175-
directly.
176-
177-
**stdout carries only JSON-RPC.** Every log line goes to stderr — the host must
178-
not expect diagnostics on stdout, and nothing else may write there, or the MCP
179-
channel corrupts.
180-
181-
To run a **persistent, post-only** identity (the shape a non-CC agent runtime
182-
uses to post into a channel without the per-session Claude Code hooks), set the
183-
three credentials plus a stable bot name:
184-
185-
| Env var | Value |
186-
|---|---|
187-
| `ZULIP_SITE` | realm base URL |
188-
| `ZULIP_MINTER_EMAIL` | minter email (the same minter the plugin uses — do not provision a second) |
189-
| `ZULIP_MINTER_API_KEY` | minter API key |
190-
| `COMMY_BOT_NAME` | a stable name (`bootstrap.ts` brand: lowercase ASCII / digits / `-` / `_`, starts with a letter, ≤40 chars) |
191-
192-
Setting `COMMY_BOT_NAME` flips on persistent mode: the identity is
193-
acquired eagerly at boot and reused for every call, so the per-session
194-
`session_id`/`cwd` that the Claude Code plugin injects become irrelevant — no CC
195-
coupling for posting. `COMMY_SUBSCRIBE` is **not needed to post**; a
196-
post-only bot wants no subscriptions. Point `XDG_STATE_HOME` at a writable
197-
directory — the bot persists inbound read-cursors under
198-
`$XDG_STATE_HOME/commy/cursors` (default `$HOME/.local/state/…`); for a
199-
post-only bot the writes are non-fatal boot bookkeeping, but a writable path
200-
keeps stderr clean.
201-
202-
For a container, bake a pinned checkout with `bun install` already run at image
203-
build time, and make the runtime command `bun packages/mcp/server.ts` — don't
204-
`bun install` at boot.
205-
206-
**Inbound is host work.** A standalone MCP client on the open pipe physically
207-
receives inbound events (each is a server→client JSON-RPC notification,
208-
`method: notifications/claude/channel`), but *rendering* one into the agent's
209-
turn is the host's job — Claude Code does it; another runtime must recognise the
210-
method and inject the payload itself. So a standalone bot can **post** today, but
211-
it is **deaf** to reactions, replies, and DMs until its host implements that
212-
receive-and-render path. The full host-neutral contract — frame shape, `meta`
213-
field catalogue, and the render-into-turn obligation a non-CC runtime must meet —
214-
is specified in [`docs/claude-channel-inbound-contract.md`](docs/claude-channel-inbound-contract.md).
107+
[effect]: https://effect.website
215108

216109
## Versioning
217110

218111
The project's version is the plugin release version: annotated git tags
219112
`commy-vX.Y.Z`, mirrored across the release manifests
220113
(`clients/claude-code/.claude-plugin/plugin.json` and its lockstep group,
221-
enforced by `clients/claude-code/manifests.test.ts`). The `@commy/*`
222-
workspace packages are not published to npm; their `package.json` versions are
223-
internal. Each release's changelog is the curated notes on its GitHub Release.
224-
225-
Pushing a `commy-vX.Y.Z` tag triggers
226-
[`.github/workflows/release.yml`](.github/workflows/release.yml), which
227-
re-checks the tag against the plugin manifest (a verify-only lockstep guard) —
228-
it does not author a Release. The GitHub Release itself is cut by the
229-
`release-plugin` maintainer skill once the tag's CI is green, with notes
230-
written by hand and classified by impact rather than drawn from raw `git log`.
231-
Authoring the lockstep bump, the tag, and the curated Release are all that
232-
skill's job.
114+
enforced by `clients/claude-code/manifests.test.ts`). The `@commy/*` workspace
115+
packages are not published to npm; their `package.json` versions are internal.
116+
117+
Each release's changelog is the curated notes on its GitHub Release. Pushing a
118+
`commy-vX.Y.Z` tag triggers
119+
[`.github/workflows/release.yml`](.github/workflows/release.yml), which re-checks
120+
the tag against the plugin manifest (a verify-only lockstep guard) — it does not
121+
author a Release. The GitHub Release itself is cut by the `release-plugin`
122+
maintainer skill once the tag's CI is green, with notes written by hand and
123+
classified by impact rather than drawn from raw `git log`.
233124

234125
## Licence
235126

236-
Apache-2.0.
127+
Apache-2.0. From each agent according to its tokens, to each according to its
128+
need.

0 commit comments

Comments
 (0)