# Architecture ## Vue d'ensemble vmux suit une architecture **daemon + CLI** classique. Un daemon unique tourne en arrière-plan ; toutes les commandes `vmux` communiquent avec lui via une Unix socket. ``` Claude Code session │ hooks (HTTP localhost:3119) ▼ ┌─────────────┐ │ Daemon │◄── /proc scan (5s) │ │◄── JSONL reader (~/.claude/projects/) │ registry │◄── i3 IPC + X11 (workspace resolution) └──────┬──────┘ │ Unix socket (~/.vmux/vmux.sock) ▼ vmux list / switch / label / focus / i3bar ``` ## Composants ### Daemon (`daemon.go`) - Écoute sur `~/.vmux/vmux.sock` (Unix socket) - Lance un hook server HTTP sur `localhost:3119` - Maintient un `SessionRegistry` en mémoire (map sessionID → SessionInfo) - Poll `/proc` toutes les **5s** (ou **20s** si des hooks arrivent activement) ### Détection des processus (`proc.go`) Scrute `/proc/*/cmdline` pour trouver les processus dont la commande contient `claude`. Pour chaque processus trouvé, lit : - `/proc//cwd` — répertoire de travail courant - `/proc//fd/0` — PTY associé (pour la résolution workspace) ### Lecture JSONL (`session.go`) Claude Code écrit un fichier JSONL par session dans `~/.claude/projects//.jsonl`. vmux associe un PID au bon fichier par correspondance du `cwd`, puis lit les messages pour extraire : - `sessionId`, `gitBranch` — identité de la session - `type: "assistant"` — dernier message Claude (pour le state et le preview) ### Détection d'état (`state.go`) | État | Critère | |---|---| | `Working` | Dernier event hook = `PreToolUse` ou `PostToolUse` | | `Needs Input` | Dernier event hook = `Notification` ou `Stop` | | `Idle` | Aucun event récent, dernier message JSONL vieux de plus de 30s | Les hooks Claude Code sont prioritaires sur la détection JSONL : ils arrivent en temps réel. ### Hooks Claude Code (`hook.go`) Claude Code appelle `vmux hook` à chaque événement (via la config `~/.claude/settings.json`). Le binaire lit le JSON sur stdin et le transmet au daemon via la socket. Le daemon met à jour le registry et déclenche une notification si la session passe de `Working` à `Needs Input`. ### Résolution workspace i3 (`workspace.go`, `i3bridge.go`, `x11_resolver.go`) 1. `i3.GetTree()` — récupère l'arbre des fenêtres i3 2. Pour chaque fenêtre avec "Claude Code" dans le titre : lit son PID via `_NET_WM_PID` (X11) 3. Remonte la chaîne `/proc//fd/0` → PTY → processus parent → PID Claude 4. Associe le PID Claude au numéro de workspace i3 Dégradation gracieuse : si i3 ou X11 est indisponible, les workspaces restent vides (pas de crash). ### i3bar (`i3bar.go`) Implémente le [protocole i3bar](https://i3wm.org/docs/i3bar-protocol.html). Tourne en boucle infinie, interroge le daemon toutes les 2s, écrit du JSON sur stdout. ### Notifications (`notify.go`) Appelle `notify-send` avec `--urgency=critical` quand une session passe de `Working` à `Needs Input`. Le mode focus (`vmux focus `) supprime les notifications pendant la durée donnée. ## Fichiers | Fichier | Rôle | |---|---| | `main.go` | Entrypoint, routing des commandes CLI | | `daemon.go` | Daemon, registry, label store | | `proc.go` | Scan /proc pour les processus claude | | `session.go` | Lecture des fichiers JSONL Claude Code | | `state.go` | Détection d'état (Working/NeedsInput/Idle) | | `hook.go` | Réception et traitement des hooks Claude Code | | `workspace.go` | Résolution workspace via i3 tree | | `i3bridge.go` | Abstraction i3 IPC (testable) | | `x11_resolver.go` | Lecture _NET_WM_PID via XGB | | `i3bar.go` | Protocole i3bar | | `notify.go` | Notifications desktop (notify-send) | | `focus.go` | Timer de mode focus | | `display.go` | Affichage `vmux list` | | `client.go` | Client Unix socket | | `protocol.go` | Types Request/Response partagés | | `setup.go` | Injection des hooks dans settings.json | | `types.go` | Types de données (Session, SessionState…) | ## Données persistées | Chemin | Contenu | |---|---| | `~/.vmux/vmux.sock` | Unix socket du daemon | | `~/.vmux/labels.json` | Labels assignés par l'utilisateur | | `~/.claude/settings.json` | Hooks Claude Code (modifié par `vmux setup`) |