package main import "time" // HookEvent represents the JSON payload sent by Claude Code hooks. type HookEvent struct { SessionID string `json:"session_id"` TranscriptPath string `json:"transcript_path,omitempty"` Cwd string `json:"cwd,omitempty"` HookEventName string `json:"hook_event_name"` NotificationType string `json:"notification_type,omitempty"` Message string `json:"message,omitempty"` Title string `json:"title,omitempty"` LastAssistantMsg string `json:"last_assistant_message,omitempty"` StopHookActive bool `json:"stop_hook_active,omitempty"` ToolName string `json:"tool_name,omitempty"` } // processHookEvent maps a Claude Code hook event to a registry update. // Ignores empty session IDs and unknown event types. func (d *Daemon) processHookEvent(event HookEvent) { if event.SessionID == "" { return } var state, waitType string switch event.HookEventName { case "Notification": state = "Needs Input" switch event.NotificationType { case "permission_prompt": waitType = "permission" case "idle_prompt": waitType = "idle" default: waitType = "question" } case "Stop": state = "Needs Input" waitType = "question" case "PostToolUse", "PreToolUse": state = "Working" waitType = "" default: return } d.registry.UpdateFromHook(event.SessionID, state, waitType, event.Cwd) } // UpdateFromHook updates a session from a hook event. // Creates the entry if it doesn't exist yet (hook can arrive before first poll). func (r *SessionRegistry) UpdateFromHook(sessionID, state, waitType, cwd string) { r.mu.Lock() defer r.mu.Unlock() existing, ok := r.sessions[sessionID] if !ok { existing = &TrackedSession{} r.sessions[sessionID] = existing } prevState := existing.PrevState existing.Info.SessionID = sessionID existing.Info.State = state existing.Info.WaitType = waitType if cwd != "" { existing.Info.Cwd = cwd } // WaitingSince transition isWaiting := state == "Needs Input" wasWaiting := prevState == "Needs Input" if isWaiting && !wasWaiting { now := time.Now() existing.Info.WaitingSince = &now } else if !isWaiting { existing.Info.WaitingSince = nil } existing.PrevState = state }