--- phase: 04-notifications-et-i3bar plan: 02 type: execute wave: 1 depends_on: [] files_modified: [i3bar.go, i3bar_test.go, main.go] autonomous: false requirements: [I3-03] must_haves: truths: - "vmux i3bar affiche le statut des sessions en format i3bar JSON sur stdout" - "Le format est vmux: auth[!] portal[W] neia[I] avec noms courts" - "Quand aucune session n'attend: vmux: all working (3)" - "Couleur rouge si >= 1 session attend, vert sinon" - "Le widget wrap i3status pour garder les infos systeme" artifacts: - path: "i3bar.go" provides: "I3BarBlock, formatI3BarBlocks, shortName, runI3Bar" exports: ["I3BarBlock", "formatI3BarBlocks", "runI3Bar"] - path: "i3bar_test.go" provides: "Tests formatage i3bar" key_links: - from: "i3bar.go" to: "protocol.go" via: "formatI3BarBlocks consomme []SessionInfo" pattern: "formatI3BarBlocks.*SessionInfo" - from: "main.go" to: "i3bar.go" via: "case i3bar appelle runI3Bar" pattern: "case \"i3bar\"" --- Widget i3bar affichant le statut des sessions vmux en temps reel. Purpose: L'utilisateur voit dans sa barre i3 quelles sessions Claude Code ont besoin de lui, sans ouvrir vmux. Output: i3bar.go avec formatage et boucle i3bar protocol, sous-commande `vmux i3bar`, wrapping i3status. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/04-notifications-et-i3bar/04-RESEARCH.md From protocol.go: ```go type SessionInfo struct { PID int `json:"pid"` SessionID string `json:"session_id"` Cwd string `json:"cwd"` GitBranch string `json:"git_branch"` State string `json:"state"` Preview string `json:"preview"` Workspace string `json:"workspace"` Label string `json:"label,omitempty"` WaitType string `json:"wait_type,omitempty"` WaitingSince *time.Time `json:"waiting_since,omitempty"` } type Request struct { Action string `json:"action"` Args json.RawMessage `json:"args,omitempty"` } type Response struct { OK bool `json:"ok"` Error string `json:"error,omitempty"` Sessions []SessionInfo `json:"sessions,omitempty"` } ``` From client.go (inferred from main.go usage): ```go func NewClient(sockPath string) *Client func (c *Client) Send(req Request) (*Response, error) ``` From main.go: ```go // CLI dispatch: switch filteredArgs[0] { case "list", "switch", "label", "stop", "daemon" } // runDaemon(sockPath) starts the daemon // printUsage() shows help ``` Task 1: Formatage i3bar et tests i3bar.go, i3bar_test.go - protocol.go (SessionInfo struct) - display.go (pattern formatage existant, shortName si deja defini) - notify.go (shortName si cree en plan 01, sinon le creer ici) - TestFormatI3BarBlocks_MixedStates: 3 sessions (Working, Needs Input, Idle) -> full_text="vmux: auth[!] portal[W] neia[I]", color="#ff0000" - TestFormatI3BarBlocks_AllWorking: 3 sessions Working -> full_text="vmux: all working (3)", color="#00ff00" (per D-07) - TestFormatI3BarBlocks_NoSessions: [] -> full_text="vmux: no sessions", color="#00ff00" - TestFormatI3BarBlocks_ColorRed: >= 1 Needs Input -> color="#ff0000" (per D-08) - TestFormatI3BarBlocks_ColorGreen: 0 Needs Input -> color="#00ff00" (per D-08) - TestFormatI3BarBlocks_UsesLabel: session avec Label="auth" -> "auth[W]" pas le cwd (per D-06) - TestFormatI3BarBlocks_UsesCwdBase: session sans label, cwd="/home/pierre/Code/vibe/vmux" -> "vmux[W]" (per D-06) - TestFormatI3BarBlocks_NeedsInputMarker: session Needs Input -> "[!]" (per D-06) - TestFormatI3BarBlocks_IdleMarker: session Idle -> "[I]" (per D-06) Creer 2 fichiers. TDD: tests d'abord. **i3bar.go:** - `I3BarBlock` struct avec champs JSON: `full_text`, `short_text` (omitempty), `color`, `name`, `markup` (="none"). - `formatI3BarBlocks(sessions []SessionInfo) []I3BarBlock`: - Iterer les sessions. Pour chaque, `shortName(s)` (fonction de notify.go ou locale si plan 01 pas encore execute) + suffixe selon State: - "Needs Input" -> `[!]`, set `hasWaiting = true` - "Working" -> `[W]` - "Idle" -> `[I]` - Si `!hasWaiting && len(sessions) > 0`: text = `vmux: all working (N)` per D-07 - Si `len(sessions) == 0`: text = `vmux: no sessions` - Sinon: text = `vmux: ` + parts jointes par espace per D-06 - Color: `#ff0000` si hasWaiting, `#00ff00` sinon per D-08 - Retourner un seul bloc `[]I3BarBlock{{FullText: text, Color: color, Name: "vmux"}}` **Note fichiers partages avec plan 01:** Si plan 01 est execute en parallele et definit `shortName` dans notify.go, cette fonction sera deja disponible. Sinon, definir `shortName` dans i3bar.go (et deplacer plus tard si doublon). Le plan 01 a la priorite sur shortName. **Pitfall stdout buffering (de RESEARCH):** Pas concerne dans cette tache (formatage pur). Gere en Task 2. cd /home/pierre/Code/vibe/vmux && nix-shell -p go --run "go test -run 'TestFormatI3Bar' -count=1 -v" - grep -q "type I3BarBlock struct" i3bar.go - grep -q "func formatI3BarBlocks" i3bar.go - grep -q "all working" i3bar.go - grep -q "#ff0000" i3bar.go - grep -q "#00ff00" i3bar.go - grep -q "TestFormatI3BarBlocks" i3bar_test.go formatI3BarBlocks produit le format compact D-06/D-07/D-08. Tous les tests passent. Task 2: Boucle i3bar protocol et CLI i3bar.go, main.go - i3bar.go (I3BarBlock et formatI3BarBlocks crees en Task 1) - main.go (switch CLI, sockPath, EnsureDaemon pattern) - protocol.go (Request/Response) - 04-RESEARCH.md (pattern i3bar protocol, i3status wrapping, pitfall stdout buffering) **i3bar.go — runI3Bar(sockPath, i3statusCmd string):** Boucle infinie qui parle le protocole i3bar v1 sur stdout. Deux modes selon i3statusCmd: **Mode standalone (i3statusCmd vide):** 1. Ecrire header: `{"version":1}` + newline 2. Ecrire `[` + newline 3. Boucle: query daemon via socket (`action: "list"`), formater avec `formatI3BarBlocks`, json.Marshal le tableau de blocs, ecrire `,` (sauf premier) + ligne JSON + newline. `os.Stdout.Write()` + flush explicite (per pitfall stdout buffering de RESEARCH). 4. Sleep 2 secondes entre chaque iteration. 5. Si le daemon est injoignable, afficher bloc `vmux: daemon offline` en gris. **Mode wrap i3status (i3statusCmd non-vide):** 1. Lancer i3statusCmd en subprocess (`exec.Command(i3statusCmd)`) 2. Lire et forwarder le header JSON (`{"version":1}`) 3. Lire et forwarder le `[` 4. Pour chaque ligne lue de i3status: trim le `,` initial, json.Unmarshal en `[]I3BarBlock`, prepend le bloc vmux (query daemon), re-Marshal, ecrire avec `,` prefix 5. Si i3status se termine, continuer en mode standalone **main.go:** - Ajouter case "i3bar" dans le switch CLI: - `EnsureDaemon(sockPath)` pour s'assurer que le daemon tourne - Detecter i3statusCmd: chercher `i3status` dans PATH. Si trouve, utiliser comme wrapping. Sinon, mode standalone. - Appeler `runI3Bar(sockPath, i3statusCmd)` - Ajouter dans printUsage: ` i3bar Output i3bar JSON (use as status_command in i3 config)` **Pitfall stdout buffering:** Utiliser `os.Stdout.Write(data)` suivi de `os.Stdout.Sync()` ou un `bufio.Writer` avec Flush() apres chaque ligne complete. **Pitfall i3status wrapping (de RESEARCH):** i3status peut prefixer les lignes de blocs avec `,`. Le parser doit les trimmer avant json.Unmarshal. cd /home/pierre/Code/vibe/vmux && nix-shell -p go --run "go build -o /dev/null . && echo 'build OK'" - grep -q "func runI3Bar" i3bar.go - grep -q "version.*1" i3bar.go - grep -q 'case "i3bar"' main.go - grep -q "i3bar" main.go - grep -q "i3status" i3bar.go - grep -q "Stdout" i3bar.go vmux i3bar fonctionne en mode standalone et wrap i3status. Le protocole i3bar v1 est respecte (header + array JSON). La sous-commande est accessible. Task 3: Verification visuelle du widget i3bar i3bar.go, main.go Verification humaine du widget i3bar. Pas de code a ecrire. Ce qui a ete construit: Widget i3bar vmux avec format compact, couleurs, wrapping i3status. Etapes de verification: 1. Compiler: `nix-shell -p go --run "go build -o vmux ."` 2. S'assurer que le daemon tourne: `./vmux list` 3. Tester le mode standalone: `./vmux i3bar` (Ctrl+C apres quelques lignes) - Verifier: header `{"version":1}`, puis `[`, puis lignes JSON avec bloc vmux - Format attendu: `vmux: auth[!] portal[W]` ou `vmux: all working (N)` 4. Configurer i3bar (optionnel): - Dans `~/.config/i3/config`, remplacer `status_command i3status` par `status_command /chemin/vers/vmux i3bar` - Recharger i3: `i3-msg reload` - Verifier que le bloc vmux apparait dans la barre avec les infos systeme 5. Verifier les couleurs: rouge si session attend, vert sinon User confirms: "approved" or describes issues L'utilisateur a valide visuellement que le widget i3bar fonctionne correctement dans sa barre i3. - `nix-shell -p go --run "go test ./... -count=1 -race"` passe sans erreur - `nix-shell -p go --run "go build -o /dev/null ."` compile sans erreur - `vmux i3bar` produit du JSON i3bar valide sur stdout 1. `vmux i3bar` produit un flux JSON i3bar protocol v1 valide 2. Le format compact respecte D-06 (noms courts + suffixes [!] [W] [I]) 3. "all working (N)" quand aucune session n'attend (D-07) 4. Couleurs rouge/vert selon urgence (D-08) 5. Le widget wrap i3status pour conserver les infos systeme 6. Tous les tests passent avec -race After completion, create `.planning/phases/04-notifications-et-i3bar/04-02-SUMMARY.md`