Context
The "should we emit ANSI escapes?" decision (explicit mode > NoColor flag > NO_COLOR env > TTY detection) is implemented twice and will drift as soon as one side learns a new rule (e.g. CLICOLOR_FORCE).
Current state (verified locations)
internal/list/list.go:113-145 — colorEnabled(opts) + isTerminal(f *os.File)
internal/preview/preview.go:400-429 — colorEnabled(opts) (used at preview.go:400)
Both packages already depend on internal/ansi for escape constants.
Task
- Compare the two implementations line by line and confirm the precedence is identical (mode "always"/"never" → NoColor bool →
NO_COLOR env → TTY check). If they differ, note the difference in the PR description and preserve each caller's current behavior via parameters rather than silently changing one.
- Add to
internal/ansi (new file color.go):
// Enabled reports whether ANSI escapes should be emitted.
// Precedence: mode ("always"/"never") > noColor > NO_COLOR env > out is a TTY.
func Enabled(mode string, noColor bool, out *os.File) bool
Move isTerminal there as an unexported helper.
- Replace both
colorEnabled implementations with calls to ansi.Enabled, mapping each package's Options fields at the call site.
- Move the existing color-decision tests from
internal/list / internal/preview into internal/ansi/color_test.go (table-driven: every mode × NoColor × NO_COLOR combination); keep one thin test per caller proving the Options mapping.
Acceptance criteria
- One implementation of the precedence logic, in
internal/ansi.
ccsession preview <id> | cat and NO_COLOR=1 ccsession list behave exactly as before (covered by tests).
go test ./..., go vet ./..., golangci-lint run, gofmt -l . pass/clean.
Context
The "should we emit ANSI escapes?" decision (explicit mode > NoColor flag > NO_COLOR env > TTY detection) is implemented twice and will drift as soon as one side learns a new rule (e.g. CLICOLOR_FORCE).
Current state (verified locations)
internal/list/list.go:113-145—colorEnabled(opts)+isTerminal(f *os.File)internal/preview/preview.go:400-429—colorEnabled(opts)(used atpreview.go:400)Both packages already depend on
internal/ansifor escape constants.Task
NO_COLORenv → TTY check). If they differ, note the difference in the PR description and preserve each caller's current behavior via parameters rather than silently changing one.internal/ansi(new filecolor.go):isTerminalthere as an unexported helper.colorEnabledimplementations with calls toansi.Enabled, mapping each package'sOptionsfields at the call site.internal/list/internal/previewintointernal/ansi/color_test.go(table-driven: every mode × NoColor × NO_COLOR combination); keep one thin test per caller proving the Options mapping.Acceptance criteria
internal/ansi.ccsession preview <id> | catandNO_COLOR=1 ccsession listbehave exactly as before (covered by tests).go test ./...,go vet ./...,golangci-lint run,gofmt -l .pass/clean.