- Show "Needs Input: permission" or "Needs Input: question" when WaitType set - No WaitType shown for Working state or when WaitType empty - No regression on existing display tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
160 lines
3.2 KiB
Go
160 lines
3.2 KiB
Go
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)
|
|
|
|
if s.Preview != "" {
|
|
lines := strings.Split(s.Preview, "\n")
|
|
for _, line := range lines {
|
|
fmt.Fprintf(w, " %s\n", line)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|