package main import ( "fmt" "io" "strings" "time" ) const ( colorReset = "\033[0m" colorGreen = "\033[32m" colorYellow = "\033[33m" colorRed = "\033[31m" colorGray = "\033[90m" colorBold = "\033[1m" ) // DisplaySessions writes a formatted list of sessions to w. func DisplaySessions(w io.Writer, sessions []Session, noColor bool) { if len(sessions) == 0 { fmt.Fprintln(w, "No active Claude Code sessions found.") return } for i, s := range sessions { if i > 0 { fmt.Fprintln(w) } stateLabel := stateColor(s.State, noColor) + s.State.String() + resetIfColor(noColor) branch := "" if s.GitBranch != "" { branch = " (" + s.GitBranch + ")" } worktreeInfo := "" if s.Worktree != "" && s.Worktree != s.CwdPath { worktreeInfo = " [worktree: " + s.Worktree + "]" } fmt.Fprintf(w, "[%s] %s%s%s\n", stateLabel, s.CwdPath, branch, worktreeInfo) if s.Preview != "" { lines := strings.Split(s.Preview, "\n") for _, line := range lines { fmt.Fprintf(w, " %s\n", line) } } } } // DisplaySessionInfos writes a formatted list of SessionInfo (from daemon) to w. // Includes workspace, label, and waiting duration. func DisplaySessionInfos(w io.Writer, sessions []SessionInfo, noColor bool, now time.Time) { if len(sessions) == 0 { fmt.Fprintln(w, "No active Claude Code sessions found.") return } for i, s := range sessions { if i > 0 { fmt.Fprintln(w) } stateDisplay := s.State if s.State == "Needs Input" && s.WaitType != "" { stateDisplay = s.State + ": " + s.WaitType } stateStr := stateColorStr(s.State, noColor) + stateDisplay + resetIfColor(noColor) branch := "" if s.GitBranch != "" { branch = " (" + s.GitBranch + ")" } workspace := "" if s.Workspace != "" { workspace = " [ws:" + s.Workspace + "]" } label := "" if s.Label != "" { label = fmt.Sprintf(" %q", s.Label) } waiting := "" if s.WaitingSince != nil { waiting = " (depuis " + formatDuration(now.Sub(*s.WaitingSince)) + ")" } fmt.Fprintf(w, "[%s] %s%s%s%s%s\n", stateStr, s.Cwd, branch, workspace, label, waiting) lines := strings.Split(s.Preview, "\n") for i := 0; i < previewLines; i++ { if i < len(lines) && lines[i] != "" { fmt.Fprintf(w, " %s\n", lines[i]) } else { fmt.Fprintln(w) } } } } // formatDuration returns a human-readable relative duration. func formatDuration(d time.Duration) string { if d < time.Minute { return "< 1 min" } hours := int(d.Hours()) minutes := int(d.Minutes()) % 60 if hours > 0 { if minutes > 0 { return fmt.Sprintf("%d h %d min", hours, minutes) } return fmt.Sprintf("%d h", hours) } return fmt.Sprintf("%d min", minutes) } func stateColorStr(state string, noColor bool) string { if noColor { return "" } switch state { case "Working": return colorGreen case "Needs Input": return colorYellow case "Idle": return colorGray default: return colorGray } } func stateColor(state SessionState, noColor bool) string { if noColor { return "" } switch state { case Working: return colorGreen case NeedsInput: return colorYellow case Idle: return colorGray default: return colorGray } } func resetIfColor(noColor bool) string { if noColor { return "" } return colorReset }