Conversation
Add lipgloss-based styling to the status command with colored dots, separator-delimited fields, session cards showing phase/files/tokens, and aggregate footer stats. Falls back to plain text for non-terminals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Entire-Checkpoint: 7c52cb84aa82
Drop session phase (active/idle/ended) and per-session file counts from status output. Token calculation validated correct (sums Input+CacheCreation+CacheRead+Output recursively, excludes APICallCount). Add comprehensive TTY/color detection tests: NO_COLOR env, regular file, non-TTY buffer, render toggle, plain-text section rules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add > chevron before first prompt in session cards - Remove repo name from Active Sessions section header - Remove total tokens from aggregate footer (keep session count) - Add blank line above the status line for visual separation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR SummaryLow Risk Overview Enhances the short/detailed settings line to include strategy display with Written by Cursor Bugbot for commit f48ef4b. Configure here. |
There was a problem hiding this comment.
Pull request overview
This PR enhances the entire status command with an improved terminal UI using the charmbracelet/lipgloss library for styled output. The changes introduce color-coded status indicators, better visual separation with section rules, and token usage display for active sessions.
Changes:
- Adds lipgloss as a direct dependency for terminal styling
- Introduces
status_style.gowith reusable styling utilities (colors, formatting helpers) - Updates
status.goto use the new styling system with visual indicators (● for enabled, ○ for disabled) - Adds token count display in active session summaries
- Comprehensive test coverage for new styling functions with accessibility considerations
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| go.mod | Moves lipgloss from indirect to direct dependency (correctly reflects new import) |
| cmd/entire/cli/status_style.go | New file containing styling utilities: color detection, terminal width, token formatting, and section rules |
| cmd/entire/cli/status.go | Updates status output formatting to use new styling system, adds token display, changes session header structure |
| cmd/entire/cli/status_test.go | Updates tests for new output format, adds comprehensive unit tests for styling functions |
cmd/entire/cli/status_style.go
Outdated
| func (s statusStyles) sectionRule(label string, width int) string { | ||
| prefix := "── " | ||
| content := label + " " | ||
| usedWidth := len(prefix) + len(content) |
There was a problem hiding this comment.
The width calculation uses len() which counts bytes, not runes. The box-drawing character '─' is a multi-byte UTF-8 character. This could lead to incorrect width calculations and visual misalignment. Use len([]rune(prefix)) and len([]rune(content)) to count actual displayed characters instead.
| usedWidth := len(prefix) + len(content) | |
| usedWidth := len([]rune(prefix)) + len([]rune(content)) |
cmd/entire/cli/status.go
Outdated
| stats = append(stats, "tokens "+sty.render(sty.bold, formatTokenCount(totalTokens(st.TokenUsage)))) | ||
|
|
||
| statsLine := strings.Join(stats, sty.render(sty.dim, " · ")) | ||
| fmt.Fprintln(w, sty.render(sty.dim, statsLine)) |
There was a problem hiding this comment.
The token count is rendered with bold styling and embedded in the stats array, then the entire statsLine (including the already-styled token count) is joined with styled separators and wrapped in dim styling. This creates nested styling which could lead to rendering issues. The bold token count will be wrapped in dim styling, potentially losing the bold emphasis. Consider not styling individual stats elements and only applying dim to the separator, or apply dim to each element separately before joining with plain separators.
cmd/entire/cli/status.go
Outdated
| for _, g := range sortedGroups { | ||
| fmt.Fprintln(w, sty.sectionRule("Active Sessions", sty.width)) | ||
| fmt.Fprintln(w) |
There was a problem hiding this comment.
The section header "Active Sessions" is now printed once per worktree group (inside the loop), whereas the previous implementation printed it once at the top. This means if there are multiple worktrees with active sessions, the heading will be repeated multiple times. Consider moving the header outside the loop (before line 283) if you want it to appear only once, or clarify in comments if the per-group header is intentional.
| for _, g := range sortedGroups { | |
| fmt.Fprintln(w, sty.sectionRule("Active Sessions", sty.width)) | |
| fmt.Fprintln(w) | |
| printedHeader := false | |
| for _, g := range sortedGroups { | |
| if !printedHeader { | |
| fmt.Fprintln(w, sty.sectionRule("Active Sessions", sty.width)) | |
| fmt.Fprintln(w) | |
| printedHeader = true | |
| } |
cmd/entire/cli/status.go
Outdated
|
|
||
| // Footer: horizontal rule + session count | ||
| fmt.Fprintln(w, sty.horizontalRule(sty.width)) | ||
| footer := fmt.Sprintf("%d sessions", totalSessions) |
There was a problem hiding this comment.
The footer always uses the plural form "sessions" even when totalSessions is 1. Consider using proper pluralization: if totalSessions == 1, use "1 session" (singular), otherwise use "%d sessions" (plural).
| footer := fmt.Sprintf("%d sessions", totalSessions) | |
| var footer string | |
| if totalSessions == 1 { | |
| footer = "1 session" | |
| } else { | |
| footer = fmt.Sprintf("%d sessions", totalSessions) | |
| } |
cmd/entire/cli/status_style.go
Outdated
| if w, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil && w > 0 { | ||
| if w > 80 { | ||
| return 80 | ||
| } | ||
| return w |
There was a problem hiding this comment.
The function always reads terminal width from os.Stdout, but the actual output may be written to a different io.Writer (passed to newStatusStyles). This is inconsistent and could cause incorrect width calculations when output is redirected or in tests. Consider accepting the writer as a parameter and checking its file descriptor if it's an *os.File, similar to shouldUseColor.
| if w, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil && w > 0 { | |
| if w > 80 { | |
| return 80 | |
| } | |
| return w | |
| // Prefer Stdout for width, but fall back to Stderr if Stdout is not a terminal | |
| files := []*os.File{os.Stdout, os.Stderr} | |
| for _, f := range files { | |
| if f == nil { | |
| continue | |
| } | |
| if w, _, err := term.GetSize(int(f.Fd())); err == nil && w > 0 { | |
| if w > 80 { | |
| return 80 | |
| } | |
| return w | |
| } |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
cmd/entire/cli/status.go
Outdated
| fmt.Fprintf(w, " %s\n", header) | ||
| for _, g := range sortedGroups { | ||
| fmt.Fprintln(w, sty.sectionRule("Active Sessions", sty.width)) | ||
| fmt.Fprintln(w) |
There was a problem hiding this comment.
Section header repeats inside worktree group loop
Medium Severity
The sectionRule("Active Sessions", ...) call is inside the for _, g := range sortedGroups loop, so it prints the "Active Sessions" header once per worktree group instead of once before the loop. The old code printed the header once before the loop. With multiple worktrees, users see repeated identical section headers with no indication of which worktree each group belongs to (the old per-group path/branch header was also removed).
| prefix := "── " | ||
| content := label + " " | ||
| usedWidth := len(prefix) + len(content) | ||
| trailing := width - usedWidth |
There was a problem hiding this comment.
Byte length used instead of display width for Unicode
Low Severity
sectionRule computes usedWidth via len(prefix) which returns the byte count, not the display column width. The prefix "── " contains two ─ characters (U+2500, 3 bytes each in UTF-8), so len() returns 7 instead of the correct display width of 3. This makes the trailing rule 4 columns shorter than the terminal width.
| // Styles | ||
| green lipgloss.Style | ||
| red lipgloss.Style | ||
| gray lipgloss.Style |
There was a problem hiding this comment.
Unused gray field defined but never referenced
Low Severity
The gray field in statusStyles is defined and initialized but never read anywhere. Since statusStyles is a new unexported type introduced in this PR, all usage is visible in the diff — sty.green, sty.red, sty.bold, sty.dim, sty.agent, and sty.cyan are all referenced, but sty.gray is not. This is dead code.
Additional Locations (1)
cmd/entire/cli/status.go
Outdated
|
|
||
| // Footer: horizontal rule + session count | ||
| fmt.Fprintln(w, sty.horizontalRule(sty.width)) | ||
| footer := fmt.Sprintf("%d sessions", totalSessions) |
There was a problem hiding this comment.
Footer displays "1 sessions" missing singular form
Low Severity
The footer uses fmt.Sprintf("%d sessions", totalSessions) which always prints the plural "sessions". When there is exactly one active session, this produces the grammatically incorrect string "1 sessions" instead of "1 session". Having a single active session is arguably the most common case.
getTerminalWidth now checks the output writer's fd first, then falls back to Stdout/Stderr. This ensures correct width when output is redirected to a different file descriptor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers getTerminalWidth, newStatusStyles width, sectionRule narrow edge case, activeTimeDisplay hours/days, and unit tests for formatSettingsStatusShort and formatSettingsStatus. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>


Updates
entire statusoutput to be better formatted and easier to read.Top is the current prod, bottom is the new implementation
