chat-across
README.md
# chat-across
English | [中文](README.zh-CN.md)
One tiny MCP server that lets **your AI coding agents work on the same project as one team** — Claude Code, Codex, and Kimi Code today, extensible to any agent that persists sessions ([adding a vendor](#adding-a-vendor) is ~60 lines). Anchor a session to a named environment with a single slash command, and every agent can read what the others are doing, straight from their **native** files, zero-copy.
**It adapts to *your* environment and workflow — not the other way around.** Install one MCP server and keep working exactly where you already work: Codex App, Claude Code / Desktop, Kimi Code, side by side or one at a time. No centralized GUI, no new TUI, no app to switch to, nothing to migrate. Your agents stay yours; they just stop being strangers to each other.
```
Claude Code ─┐
Codex App ──┼──► chat-across bridge (stdio MCP) ──► one small SQLite (pointers + briefs only)
Kimi Code ──┘ │
└─ reads peers' NATIVE session / memory / docs in place — nothing duplicated
```
## Slash commands
Five commands cover everything. In Claude Code and Kimi Code they are slash commands; in Codex type `$` and pick the skill of the same name.
| Command | What happens |
|---|---|
| `/chat-across <name>` | Anchor this session to environment `<name>` (first use creates it, repeat use joins it). The agent then **automatically syncs both ways**: it compacts its own conversation into a brief and pushes it, pulls every peer's latest brief, and tells you what each of them is doing — all from one command. |
| `/chat-across-sync` | Manually refresh: push an updated brief, pull peers' latest. Agents also do this on their own after every significant milestone, so you rarely need it. |
| `/chat-across-disconnect` | Detach. The session returns to full isolation — every shared-data tool fails closed until it anchors again. |
| `/chat-across-list` | List every environment on the bridge: which vendors are inside, how many sessions have touched it, when it last moved. |
| `/chat-across-delete <name>` | Delete an environment: wipe its briefs and pointers, free the name. Called without a name it lists environments and asks — it never deletes blind. Bridge bookkeeping only; nobody's transcripts, memories, or files are touched. |
Everything a command does is also reachable by plain language — "delete environment project1", "what environments are there", "continue what Codex was doing" all work.
## What you get
### Anchoring — environments as meeting points
An **environment** is just a unique name (`project1`, `mcp`, whatever). Sessions anchor to it; anchored sessions share; unanchored sessions are physically outside — **fail-closed**, no sharing, no pollution. Names are the identity: connecting with an existing name always joins it, so there are never two `project1`s.
| Tool | What it does |
|---|---|
| `chat_across_connect` | Create-or-join by name. Registers a **pointer** to this session's own native transcript (agents pass their session UUID so peers can read them back), and returns every peer's latest brief — joining alone tells you the state of the whole team. |
| `chat_across_disconnect` | Detach; all scoped tools fail closed again. |
### Briefs — the "what is everyone doing" layer
Each session keeps **one** compacted work brief on the bridge (goal / done / key decisions / next), upserted in place — bounded at 32KB, one row per session, so storage physically cannot grow. Briefs carry a **byte watermark** of the writer's transcript, so readers see `behindBytes`: exactly how much has happened since the brief was written. Staleness is a measured number, not a guess.
Peers are keyed by **native session UUID**, not by vendor — several sessions of one vendor stay distinct (two Claude seats in an automation loop each keep their own brief), while the same session reconnecting through a restarted MCP server dedups to one entry. Pass `sessionUuid` (shown on every peer) to any read-through tool to address one session precisely.
| Tool | What it does |
|---|---|
| `chat_across_sync` | Push own brief (upsert + watermark), pull peers' latest — one call, both directions. |
### Native read-through — the actual point of this project
Nothing is copied onto the bridge. When an agent wants to know what a peer did, the bridge resolves *where the peer's own app already persists it* and parses it **in place, on demand**:
| Tool | Reads | Detail |
|---|---|---|
| `get_peer_tail` | The peer's **native session transcript** (Claude project `.jsonl` / Codex rollout / Kimi wire) | Last N exchanges (default 10, max 50), byte-seeked from the file end — a live, write-locked 660MB rollout answers in single-digit milliseconds. Skips Codex guardian subthreads, filters harness noise, redacts secret-shaped strings. |
| `get_peer_memory` | The peer's **native memory store** | Claude: the per-project memory directory. Codex: the user-level store filtered by its per-entry `cwd` tags. No shared memory bank exists here — never rebuild what the host already has. |
| `get_peer_instructions` | The peer project's **standing rules** | `CLAUDE.md` / `AGENTS.md` / `KIMI.md` from the peer's project root. |
Together: **brief** = what they're doing, **tail** = the exact last words, **memory** = what they've learned, **instructions** = the project's rules. A full takeover context in one connect.
Project identity is *derived*, never configured: the peer's own artifacts carry its cwd (Codex `session_meta`, Claude jsonl records, Kimi `state.json`). Works even if the peer session is closed, crashed, or out of quota — the bridge reads files, not live processes.
### Lifecycle — visibility and cleanup
| Tool | What it does |
|---|---|
| `list_environments` | Every environment with vendors present, session count, and last activity — "which project1 is this" answers itself. |
| `chat_across_delete` | Wipe an environment by exact name and free it. Stale rows also self-prune after 14 days regardless. |
## Daily use
```
/chat-across myproject # Claude / Kimi (Codex: type $ and pick chat-across)
```
That's it. The agent anchors, pushes its brief, pulls everyone else's, and tells you what the other agents are up to. Then:
- *"Continue what Codex was doing"* → the agent pulls Codex's tail + memory + project docs and picks up from the last sentence.
- *"What environments are there?"* → `/chat-across-list` or just ask.
- *"Start project1 over, clean"* → `/chat-across-delete project1`, then `/chat-across project1`.
### Fresh session, same thread
Sessions are peers too — **including your own older ones**. Open a brand-new session (same vendor or a different one), anchor to the same environment, and the previous session's brief arrives with the connect. Say *"continue where my last session left off"* and the agent reads that session's native tail and resumes from the last sentence.
This works even if the old session is closed, crashed, or hit its context limit — the bridge reads its transcript **file**, not a live process. Vendor-native resume (`claude --resume`, the Codex session list) is still the first choice for plain same-vendor restarts; this is the structured fallback that also survives compaction, quota switches, and vendor hops. Only requirement: the old session anchored at least once while it was alive.
## Install
Four steps: install the package → register it in each host → install the slash-command templates → restart the hosts. Works on Windows / macOS / Linux (Node ≥ 20), no cloning, no build.
### 1. Install the package
```bash
npm install -g chat-across
```
This puts the `chat-across` command on your PATH and the package (server + templates) under npm's global directory — `npm root -g` prints the exact location; the package lives at `<that path>/chat-across`.
### 2. Register the server in each host
Same server everywhere, only the label differs:
```bash
# Claude Code
claude mcp add chat-across -s user --env CHATACROSS_HOST_LABEL=claude-code -- chat-across
```
```toml
# ~/.codex/config.toml — add this section
[mcp_servers.chat-across]
command = "chat-across"
env = { CHATACROSS_HOST_LABEL = "codex-app" }
```
```jsonc
// ~/.kimi-code/mcp.json — merge into mcpServers
{ "mcpServers": { "chat-across": {
"command": "chat-across",
"env": { "CHATACROSS_HOST_LABEL": "kimi-code" } } } }
```
Prefer not to install globally? Use `npx -y chat-across` as the command in all three configs instead. Developing or contributing? `git clone` + `npm install` + point the configs at `node <repo>/src/server.mjs`.
### 3. Install the slash commands
The [`examples/`](examples/) folder ships inside the npm package. Copy its templates into each host's command/skill directory — they give every agent the five `/chat-across*` commands and teach it to auto-compact + push + pull on connect and to pass its session UUID.
macOS / Linux:
```bash
EX="$(npm root -g)/chat-across/examples"
mkdir -p ~/.claude/commands ~/.kimi-code/commands ~/.codex/skills
cp "$EX"/claude-commands/*.md ~/.claude/commands/
cp "$EX"/claude-commands/*.md ~/.kimi-code/commands/
cp -r "$EX"/codex-skills/* ~/.codex/skills/
```
Windows (PowerShell):
```powershell
$ex = Join-Path (npm root -g) 'chat-across\examples'
New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\commands", "$env:USERPROFILE\.kimi-code\commands", "$env:USERPROFILE\.codex\skills" | Out-Null
Copy-Item "$ex\claude-commands\*.md" "$env:USERPROFILE\.claude\commands\"
Copy-Item "$ex\claude-commands\*.md" "$env:USERPROFILE\.kimi-code\commands\"
Copy-Item "$ex\codex-skills\*" "$env:USERPROFILE\.codex\skills\" -Recurse -Force
```
### 4. Restart and verify
Restart each host app so it reads the new MCP config, then check:
- Claude Code: `claude mcp list` → `chat-across … ✔ Connected`
- Codex: `codex mcp get chat-across` → `enabled: true`; typing `$` should offer the `chat-across` skills
- Kimi: ask it "what chat-across tools do you have"
Type `/chat-across test` in any of them — `created: true` means the bridge is alive.
### Where everything lives
| What | Where |
|---|---|
| Server + `examples/` templates | `$(npm root -g)/chat-across/` |
| The `chat-across` command | npm's global bin directory (already on PATH) |
| Bridge data (SQLite: briefs + pointers only) | `~/.chat-across/` on every OS — override with `CHATACROSS_DATA_DIR`. (Moved from `%APPDATA%` in v0.1.4: MSIX-packaged hosts get that path silently virtualized; home dotdirs don't.) |
| Slash commands you installed in step 3 | `~/.claude/commands/`, `~/.codex/skills/`, `~/.kimi-code/commands/` |
Uninstall = `npm uninstall -g chat-across` + remove the registrations and copied templates + delete the data dir. Nothing else is written anywhere.
## How this differs from the existing field
Cross-agent context tools are a crowded space — and they almost all take one of two other roads:
- **Memory-layer tools** (memorix, cass-memory, cross-agent-memory, OpenMemory, …) build their **own store**: stop hooks or ingest pipelines summarize your sessions into their vector DB / playbook, and agents must write into it. Powerful, but it's a second brain to maintain, and it holds *copies* that go stale.
- **Live-messaging bridges** (AgentBridge over Claude Channels ↔ Codex App Server, …) let two *running* sessions talk in real time. Great for co-editing; doesn't help when the peer session is closed, crashed, or out of quota — which is exactly when you need a takeover.
chat-across takes a third road: **no store, no hooks, no embeddings, no live coupling**. The hosts already persist everything — transcripts, memories, project docs — so the bridge just resolves *where* and parses *in place*, on demand. The only thing it writes is one bounded brief per session. If the bridge's DB is deleted, nothing of value is lost.
## Design rules
- **Zero-copy**: conversations, memories, and docs are read in place from each host's own storage. The bridge stores only pointers and one bounded brief per session (32KB cap, 14-day pruning). Local-only, no network.
- **Fail-closed**: an unanchored session cannot touch any environment.
- **Never rebuild what the host already has**: no shared memory bank, no transcript mirroring, no search engine. The reading agent has a model; raw text is enough.
- **Honest resolution**: transcript pointers are labeled `uuid` / `explicit` / `heuristic` so readers know how much to trust them.
- **Fail-loud**: `sync`/`connect` verify their commit actually landed before claiming success and report the DB file they wrote to; a corrupt database is quarantined (`bridge.sqlite.corrupt.<ts>`) and rebuilt loudly at startup — the bridge never runs wounded and never pretends.
## Adding a vendor
Each vendor is ~60 lines: a session root + a transcript line parser + a cwd deriver + (optionally) a memory-store reader. The current three (Claude Code / Codex / Kimi Code) are the template — agents like OpenCode, Hermes, Kiro, or anything MCP-capable that persists sessions to disk can be added the same way. See [CONTRIBUTING.md](CONTRIBUTING.md) for the adapter checklist and fixture requirements.
## Caveats
- **Claude Code auto-deletes session transcripts after 30 days** (cleanup runs on every launch). If you rely on long-lived takeover, raise `cleanupPeriodDays` in `~/.claude/settings.json` — otherwise a peer pointer can outlive its file.
- The three transcript formats are **undocumented vendor internals** and will drift; parsers are version-coupled by nature (tested against Claude Code 2.x, Codex 0.146–0.147, Kimi Code wire v2). PRs welcome.
- `sessionUuid` at connect is strongly recommended — without it the bridge falls back to newest-active-file matching, which can mis-map when several sessions of one vendor run at once.
- Tails/memories pass a basic secret-redaction filter; it is a seatbelt, not a guarantee.
- **Don't point `CHATACROSS_DATA_DIR` at `%APPDATA%` (or other known-folder paths) on Windows** if any of your agent hosts is an MSIX-packaged app: Windows AppData virtualization silently gives container processes their own overlay copy of those paths, splitting the bridge into parallel worlds that never converge. The default (`~/.chat-across`) is exempt. Field-diagnosed the hard way.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing