package main import ( "encoding/json" "fmt" "os" "path/filepath" ) // parseHookStdin reads a HookEvent from stdin (Claude Code sends JSON on stdin). func parseHookStdin() HookEvent { var event HookEvent json.NewDecoder(os.Stdin).Decode(&event) return event } // runSetup adds vmux hooks to ~/.claude/settings.json (idempotent). func runSetup() { home := os.Getenv("HOME") settingsPath := filepath.Join(home, ".claude", "settings.json") vmuxBin, err := os.Executable() if err != nil { vmuxBin = filepath.Join(home, "Code", "vibe", "vmux", "vmux") } data, err := os.ReadFile(settingsPath) if err != nil { fmt.Fprintf(os.Stderr, "Cannot read %s: %v\n", settingsPath, err) os.Exit(1) } var settings map[string]interface{} if err := json.Unmarshal(data, &settings); err != nil { fmt.Fprintf(os.Stderr, "Cannot parse %s: %v\n", settingsPath, err) os.Exit(1) } hooks, _ := settings["hooks"].(map[string]interface{}) if hooks == nil { hooks = make(map[string]interface{}) settings["hooks"] = hooks } hookCommand := vmuxBin + " hook" vmuxHook := map[string]interface{}{ "type": "command", "command": hookCommand, "timeout": 5, } events := []string{"Notification", "Stop", "PostToolUse", "PreToolUse"} changed := false for _, event := range events { entries, _ := hooks[event].([]interface{}) if hasVmuxHook(entries, hookCommand) { continue } entry := map[string]interface{}{ "hooks": []interface{}{vmuxHook}, } hooks[event] = append(entries, entry) changed = true } if !changed { fmt.Println("vmux hooks already configured.") return } out, err := json.MarshalIndent(settings, "", " ") if err != nil { fmt.Fprintf(os.Stderr, "Cannot marshal settings: %v\n", err) os.Exit(1) } if err := os.WriteFile(settingsPath, out, 0o644); err != nil { fmt.Fprintf(os.Stderr, "Cannot write %s: %v\n", settingsPath, err) os.Exit(1) } fmt.Printf("vmux hooks added to %s for events: %v\n", settingsPath, events) fmt.Println("Restart Claude Code sessions for hooks to take effect.") } func hasVmuxHook(entries []interface{}, hookCommand string) bool { for _, entry := range entries { e, _ := entry.(map[string]interface{}) hookList, _ := e["hooks"].([]interface{}) for _, h := range hookList { hm, _ := h.(map[string]interface{}) if cmd, _ := hm["command"].(string); cmd == hookCommand { return true } } } return false }