module Tryouts::Console
def style(*att, io: nil)
def style(*att, io: nil) # Only output ANSI codes if colors are supported target_io = io || $stdout # Explicit color control via environment variables # FORCE_COLOR/CLICOLOR_FORCE override NO_COLOR return "\e[%sm" % att.join(';') if ENV['FORCE_COLOR'] || ENV['CLICOLOR_FORCE'] return '' if ENV['NO_COLOR'] # Check if we're outputting to a real TTY tty_output = (target_io.respond_to?(:tty?) && target_io.tty?) || ($stdout.respond_to?(:tty?) && $stdout.tty?) || ($stderr.respond_to?(:tty?) && $stderr.tty?) # If we have a real TTY, always use colors return "\e[%sm" % att.join(';') if tty_output # For environments like Claude Code where TTY detection fails but we want colors # Check if output appears to be redirected to a file/pipe if ENV['TERM'] && ENV['TERM'] != 'dumb' # Check if stdout/stderr look like they're redirected using file stats begin stdout_stat = $stdout.stat stderr_stat = $stderr.stat # If either stdout or stderr looks like a regular file or pipe, disable colors stdout_redirected = stdout_stat.file? || stdout_stat.pipe? stderr_redirected = stderr_stat.file? || stderr_stat.pipe? # Enable colors if neither appears redirected return "\e[%sm" % att.join(';') unless stdout_redirected || stderr_redirected rescue StandardError # If stat fails, fall back to enabling colors with TERM set return "\e[%sm" % att.join(';') end end # Default: no colors '' end