Skip to main content
Glama
Catweazle74

claude-session-bus

by Catweazle74
README.md
# claude-session-bus

[![selftest](https://github.com/Catweazle74/claude-session-bus/actions/workflows/selftest.yml/badge.svg)](https://github.com/Catweazle74/claude-session-bus/actions/workflows/selftest.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Ein Koordinationsbus, der mehrere **Claude-Code-Sessions** (Linux, macOS, Windows) über eine bestehende
[m1k1o/chat](https://github.com/m1k1o/chat)-Instanz zusammenschaltet. Jede Session hängt als normaler
Chat-Teilnehmer im selben Raum, adressiert die anderen Sessions direkt mit `@nick`, meldet ihren
Status selbst und kann **blockierend auf die Zuarbeit einer anderen Session warten** – ohne dass ein
Mensch zwischen den Rechnern hin- und herspringt.

```
user:          @claude-linux bau Feature X
claude-linux:  gepusht nach feature/x (abc123). @claude-win @claude-mac bitte bauen und testen
claude-win:    Build ok, 12/12 Tests grün
claude-mac:    Build bricht ab: fehlende Symbole in libfoo. @claude-linux Log: ...
claude-linux:  Fix gepusht (def456). @claude-mac nochmal
```

Während `claude-linux` auf die Antwort wartet, blockiert es in `wait_for_message` – statt den Menschen
zu fragen – und wacht auf, sobald eine der anderen Sessions antwortet.

## Architektur

Ein Prozess pro Maschine (`bridge.py`), drei Bausteine in einem asyncio-Loop:

```
        m1k1o/chat  (z. B. chat-host:8484)
              ▲  Socket.IO
              │
      ┌───────┴────────────────────────────────┐
      │  bridge.py   (Linux / macOS / Windows) │
      │   ├─ ChatClient      Socket.IO         │
      │   ├─ MCP-Server      127.0.0.1:8787    │◄── HTTP-MCP ──┐
      │   ├─ Router          @nick-Regeln      │               │
      │   └─ ClaudeSession   Subprozess ───────┼──► claude -p ─┘
      └────────────────────────────────────────┘    (stream-json)
```

- **stdin-Injection** weckt eine untätige Session (neuer Turn).
- **MCP-Tools** bedienen die arbeitende Session (senden / warten / Peers abfragen) mitten im Turn.

## Adressierung (Schutz vor Endlosschleifen)

Eine Session reagiert **nur**, wenn sie mit `@<ihr-nick>`, `@all` oder `@alle` angesprochen wird.
Alle anderen Nachrichten liest sie nur mit und gibt sie beim nächsten eigenen Turn als Kontext weiter.
Zusätzlich greift nach `HOP_LIMIT` (Standard 12) aufeinanderfolgenden Bot→Bot-Runden **ohne**
menschliche Beteiligung eine Bremse; jede Nachricht eines Menschen oder `/reset` setzt sie zurück.

## MCP-Tools, die jede Session bekommt

| Tool | Zweck |
|------|-------|
| `chat_send_message(text, to=[…])` | Nachricht posten; `to` setzt `@nick`-Präfixe |
| `chat_wait_for_message(from_nick, contains, timeout, addressed_only)` | **blockierend** auf Zuarbeit warten |
| `chat_get_messages(limit)` | letzte Nachrichten lesen, ohne zu warten |
| `chat_list_peers()` | wer ist online, mit letztem gemeldeten Status |
| `chat_set_status(state, detail)` | `idle` / `working` / `blocked` / `done` |

## Slash-Kommandos (von der Bridge beantwortet, gehen nicht an Claude)

- `/status` – Nick, Zustand, Session-ID, Queue, Wartende, Hops, Uptime, Peers (antwortet von jeder Session)
- `/help` – Kurzhilfe
- `/reset` – **adressiert** (`@nick /reset`): neue Claude-Session mit frischem Kontext
- `/stop` – **adressiert**: laufenden Turn abbrechen (Prozess neu, Kontext bleibt über `--resume`)

## Chat-Server (m1k1o/chat) aufsetzen

Der Bus braucht **eine** zentrale [m1k1o/chat](https://github.com/m1k1o/chat)-Instanz im LAN, mit der
sich alle Sessions verbinden. Der Container lauscht intern auf Port **80**; in dieser README wird er
nach außen auf **8484** gemappt (passend zu `CHAT_URL=http://<host>:8484`).

Schnellstart per Docker:
```bash
docker run -d --name chat --restart unless-stopped -p 8484:80 -e CACHE_SIZE=200 m1k1o/chat:latest
```

Oder per `docker-compose.yml`:
```yaml
services:
  chat:
    image: m1k1o/chat:latest
    restart: unless-stopped
    ports:
      - "8484:80"
    environment:
      CACHE_SIZE: 200          # Anzahl serverseitig gepufferter Nachrichten für neu verbundene Clients
    healthcheck:
      test: ["CMD", "curl", "http://localhost"]
      interval: 60s
      timeout: 10s
      retries: 10
      start_period: 5s
```
```bash
docker compose up -d
```

- **`CACHE_SIZE`** – wie viele letzte Nachrichten der Server puffert und einer neu verbundenen Session
  als Verlauf schickt. Standard 0 (kein Verlauf). Für den Bus sinnvoll (z. B. 200), damit eine neu
  gestartete Session den letzten Kontext sieht; nicht zu hoch, da auch Bilder gecacht werden.
- Danach in jeder `config.env` `CHAT_URL=http://<host-ip>:8484` setzen – die IP/den Hostnamen des
  Docker-Hosts (aus dem LAN erreichbar), **nicht** `localhost`, sonst finden die anderen Maschinen den
  Server nicht.
- Prüfen: `curl http://<host-ip>:8484/` sollte `HTTP 200` liefern.

> ⚠️ **Sicherheit:** m1k1o/chat hat **keine Authentifizierung** (siehe Abschnitt „Sicherheit — bitte
> lesen“). Den Port nur im vertrauenswürdigen LAN/VPN exponieren, niemals ins Internet.

## Installation (je Maschine)

Voraussetzungen: **Python ≥ 3.10** und die **Claude Code CLI** (`claude`) im PATH.

### Linux / macOS

```bash
git clone https://github.com/Catweazle74/claude-session-bus.git
cd claude-session-bus
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp config.example.env config.env                 # anpassen: CLAUDE_CWD, CHAT_NICK, ...
```

### Windows (PowerShell)

```powershell
# Falls kein echtes Python vorhanden ist (der Store-Alias "python" zählt nicht):
winget install --id Python.Python.3.12 --scope user

git clone https://github.com/Catweazle74/claude-session-bus.git
cd claude-session-bus
python -m venv .venv
.\.venv\Scripts\pip install -r requirements.txt
Copy-Item config.example.env config.env          # anpassen: CLAUDE_CWD, CHAT_NICK, ...
```

`config.env` bearbeiten – vor allem `CLAUDE_CWD` auf das gemeinsame Projekt-Repo setzen
(unter Windows als nativer Pfad, z. B. `C:\Users\<user>\Sourcen\mein-projekt`).

### Verifikation (offline, ohne Chat und Claude)

```bash
.venv/bin/python selftest.py        # Windows: .venv\Scripts\python selftest.py
```

Deterministische Tests der Router-Logik (Adressierung, Pingpong-Bremse, wait/pending,
Slash-Kommandos). Sollten alle mit „alle Selbsttests bestanden“ enden.

## Starten

```bash
set -a; . ./config.env; set +a          # Windows PowerShell: siehe unten
.venv/bin/python bridge.py
```

`CHAT_URL` ist **Pflicht** – ohne gesetzte Variable (oder `--chat-url`) bricht die Bridge mit einer
Meldung ab, statt still gegen eine Beispieladresse zu verbinden.

Der Nick ergibt sich, wenn nicht gesetzt, aus der Plattform: `claude-linux` / `claude-mac` /
`claude-win`. Overrides auch per CLI-Flag: `.venv/bin/python bridge.py --nick claude-linux --cwd /pfad/zum/repo`.

Windows (PowerShell):
```powershell
Get-Content config.env | Where-Object { $_ -match '=' -and $_ -notmatch '^\s*#' } | ForEach-Object {
  $k,$v = $_ -split '=',2; [Environment]::SetEnvironmentVariable($k.Trim(), $v.Trim())
}
.venv\Scripts\python bridge.py
```

## Stoppen

- **Linux (systemd --user):** `systemctl --user stop claude-session-bus`
- **macOS (launchd):** `launchctl unload ~/Library/LaunchAgents/claude-session-bus.plist`
- **Windows:** `Get-Process python | Where-Object Path -like '*claude-session-bus*' | Stop-Process`
- Direkt im Vordergrund gestartet: einfach `Strg+C`.

## Test ohne Browser

```bash
.venv/bin/python test_client.py "@claude-linux liste die Dateien im Repo"
.venv/bin/python test_client.py --listen 120        # nur mitlesen
```

## Autostart

- **Linux (systemd --user):** `systemd/claude-session-bus.service` nach `~/.config/systemd/user/`
  kopieren, Pfade anpassen, dann `systemctl --user daemon-reload && systemctl --user enable --now claude-session-bus`.
- **macOS (launchd):** `launchd/claude-session-bus.plist` nach `~/Library/LaunchAgents/` kopieren,
  Pfade anpassen, `launchctl load ~/Library/LaunchAgents/claude-session-bus.plist`.
- **Windows (Aufgabenplanung):** `windows/install-task.ps1` (Pfade anpassen) in einer **als Administrator
  gestarteten** PowerShell ausführen – `Register-ScheduledTask` scheitert sonst mit „Zugriff verweigert“.
  Das Skript erzeugt dabei `windows/run-bridge.ps1` (maschinenspezifisch, nicht eingecheckt).
  Deinstallieren: `Unregister-ScheduledTask -TaskName "claude-session-bus" -Confirm:$false`.

  Adminfreie Alternative (kein Auto-Restart bei Absturz): einen versteckten Starter in den
  Autostart-Ordner legen (`shell:startup`), der `windows/run-bridge.ps1` aufruft, z. B. eine
  `claude-session-bus.vbs`:
  ```vbs
  Set sh = CreateObject("WScript.Shell")
  sh.Run "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File ""<pfad>\windows\run-bridge.ps1""", 0, False
  ```
  Nur **eine** der beiden Varianten aktivieren, sonst startet die Bridge doppelt.

## Sicherheit — bitte lesen

Der m1k1o-Chat hat **keine Authentifizierung**. Standardmäßig läuft die Bridge mit
`PERMISSION_MODE=bypassPermissions`. Das bedeutet: **wer den Chat erreicht, kann ohne Rückfrage
Befehle auf allen angebundenen Maschinen ausführen und Dateien ändern.** Deshalb:

- Port des Chats (z. B. 8484) **niemals** ins Internet exponieren – nur LAN oder VPN.
- `CLAUDE_CWD` möglichst eng auf das Projekt-Repo setzen, nicht auf `$HOME` oder `/`.
- Der MCP-Server bindet nur an `127.0.0.1` – nicht öffentlich machen.
- Restriktive Alternative (kein autonomes Schreiben/Ausführen):
  ```
  PERMISSION_MODE=acceptEdits
  ALLOWED_TOOLS=Read Grep Glob Bash(git *)
  ```
  Damit fragt Claude bei allem außerhalb der Whitelist zurück – was im Bus-Betrieb aber niemand
  bestätigen kann, die Session also effektiv blockiert. Für den unbeaufsichtigten Bus ist
  `bypassPermissions` in einem vertrauenswürdigen LAN der praktikable Kompromiss.

## Konfiguration

Alle Optionen sind in [`config.example.env`](config.example.env) dokumentiert und zusätzlich als
CLI-Flags verfügbar (`--help`).

---

## Funktionsstand

- ✅ Chat-Anbindung (Socket.IO), `@nick`-Adressierung, Pingpong-Bremse, Slash-Kommandos
- ✅ MCP-Tools für senden / blockierendes Warten / Peers / Status, in-process auf `127.0.0.1`
- ✅ Claude-Session als Subprozess (`claude -p`, stream-json) inklusive stdin-Injection und `--resume`
- ✅ Autostart-Vorlagen für systemd (Linux), launchd (macOS), Aufgabenplanung (Windows)
- ✅ Offline-Selbsttests der Router-Logik (`selftest.py`), in CI auf jedem Push
- ⚠️ Kein Auth-Konzept — die Sicherheit hängt vollständig am Netz, in dem der Chat läuft
  (siehe „Sicherheit — bitte lesen")

---

## Beitrag & Feedback

**Bekannte Limitierungen**:
- `bridge.py` liest `config.env` nicht selbst — die Variablen müssen vorher in die Umgebung
  (sourcen, `EnvironmentFile=` in systemd, Wrapper-Skript unter Windows/macOS).
- Der Windows-Autostart per `Register-ScheduledTask` braucht eine Administrator-PowerShell;
  ohne Adminrechte nur die Autostart-Ordner-Variante (kein Neustart bei Absturz).
- m1k1o/chat bietet keine Authentifizierung — nur im vertrauenswürdigen LAN/VPN betreiben.

**Bitte melden**:
- Schritte zum Reproduzieren
- Erwartetes vs. tatsächliches Verhalten
- Plattform, Python-Version, Ausgabe von `/status` und `selftest.py` (falls möglich)

---

## Abhängigkeiten

- **Python ≥ 3.10**
- **[Claude Code CLI](https://github.com/anthropics/claude-code)** (`claude` im PATH)
- **python-socketio[asyncio_client] ≥ 5.16, < 6** (Chat-Verbindung)
- **mcp ≥ 2.0, < 3** (MCP-Server für die Session-Tools; mit 1.x nicht kompatibel)
- **[m1k1o/chat](https://github.com/m1k1o/chat)** als zentrale Chat-Instanz im LAN (Docker)

---

## Lizenz

MIT — siehe [LICENSE](LICENSE).