UNPKG

claudekit

Version:

CLI tools for Claude Code development workflow

246 lines (216 loc) 7.72 kB
// import pc from 'picocolors'; // Unused /** * Enhanced color utility that respects NO_COLOR environment variable * and provides fallback for non-TTY terminals */ interface ColorOptions { force?: boolean; } interface ColorFunction { (text: string): string; } interface PicocolorsLike { red: ColorFunction; green: ColorFunction; yellow: ColorFunction; blue: ColorFunction; magenta: ColorFunction; cyan: ColorFunction; white: ColorFunction; black: ColorFunction; gray: ColorFunction; grey: ColorFunction; bold: ColorFunction; dim: ColorFunction; italic: ColorFunction; underline: ColorFunction; strikethrough: ColorFunction; inverse: ColorFunction; bgRed: ColorFunction; bgGreen: ColorFunction; bgYellow: ColorFunction; bgBlue: ColorFunction; bgMagenta: ColorFunction; bgCyan: ColorFunction; bgWhite: ColorFunction; bgBlack: ColorFunction; bgGray: ColorFunction; bgGrey: ColorFunction; } /** * Check if colors should be enabled based on environment */ function shouldUseColors(options: ColorOptions = {}): boolean { // Force colors if explicitly requested if (options.force === true) { return true; } // Respect NO_COLOR environment variable (https://no-color.org/) if (process.env['NO_COLOR'] !== undefined) { return false; } // Check if colors are explicitly disabled if (process.env['FORCE_COLOR'] === '0') { return false; } // Check if stdout is a TTY (terminal) // Note: isTTY can be undefined in some environments if (process.stdout.isTTY !== true) { return false; } return true; } /** * Color utility functions that respect environment settings */ export class Colors { private static useColors = shouldUseColors(); /** * Force enable or disable colors (useful for testing) */ static setEnabled(enabled: boolean): void { Colors.useColors = enabled; } /** * Reset colors to environment-based detection */ static reset(): void { Colors.useColors = shouldUseColors(); } /** * Force colors to be enabled regardless of environment (mainly for testing) */ static forceEnable(): void { Colors.useColors = shouldUseColors({ force: true }); } /** * Check if colors are currently enabled */ static get enabled(): boolean { return Colors.useColors; } // Success messages (green) static success(text: string): string { return Colors.useColors ? `\x1b[32m${text}\x1b[0m` : text; } // Error messages (red) static error(text: string): string { return Colors.useColors ? `\x1b[31m${text}\x1b[0m` : text; } // Warning messages (yellow) static warn(text: string): string { return Colors.useColors ? `\x1b[33m${text}\x1b[0m` : text; } // Info messages (cyan) static info(text: string): string { return Colors.useColors ? `\x1b[36m${text}\x1b[0m` : text; } // Debug messages (gray) static debug(text: string): string { return Colors.useColors ? `\x1b[90m${text}\x1b[0m` : text; } // Accent/highlight text (blue) static accent(text: string): string { return Colors.useColors ? `\x1b[34m${text}\x1b[0m` : text; } // Bold text static bold(text: string): string { return Colors.useColors ? `\x1b[1m${text}\x1b[0m` : text; } // Dim/subtle text static dim(text: string): string { return Colors.useColors ? `\x1b[2m${text}\x1b[0m` : text; } // Underlined text static underline(text: string): string { return Colors.useColors ? `\x1b[4m${text}\x1b[0m` : text; } // Raw picocolors for complex styling (only when colors enabled) static get pc(): PicocolorsLike { if (Colors.useColors) { // Use direct ANSI codes when colors are forced (bypasses picocolors environment detection) return { red: (text: string): string => `\x1b[31m${text}\x1b[0m`, green: (text: string): string => `\x1b[32m${text}\x1b[0m`, yellow: (text: string): string => `\x1b[33m${text}\x1b[0m`, blue: (text: string): string => `\x1b[34m${text}\x1b[0m`, magenta: (text: string): string => `\x1b[35m${text}\x1b[0m`, cyan: (text: string): string => `\x1b[36m${text}\x1b[0m`, white: (text: string): string => `\x1b[37m${text}\x1b[0m`, black: (text: string): string => `\x1b[30m${text}\x1b[0m`, gray: (text: string): string => `\x1b[90m${text}\x1b[0m`, grey: (text: string): string => `\x1b[90m${text}\x1b[0m`, bold: (text: string): string => `\x1b[1m${text}\x1b[0m`, dim: (text: string): string => `\x1b[2m${text}\x1b[0m`, italic: (text: string): string => `\x1b[3m${text}\x1b[0m`, underline: (text: string): string => `\x1b[4m${text}\x1b[0m`, strikethrough: (text: string): string => `\x1b[9m${text}\x1b[0m`, inverse: (text: string): string => `\x1b[7m${text}\x1b[0m`, bgRed: (text: string): string => `\x1b[41m${text}\x1b[0m`, bgGreen: (text: string): string => `\x1b[42m${text}\x1b[0m`, bgYellow: (text: string): string => `\x1b[43m${text}\x1b[0m`, bgBlue: (text: string): string => `\x1b[44m${text}\x1b[0m`, bgMagenta: (text: string): string => `\x1b[45m${text}\x1b[0m`, bgCyan: (text: string): string => `\x1b[46m${text}\x1b[0m`, bgWhite: (text: string): string => `\x1b[47m${text}\x1b[0m`, bgBlack: (text: string): string => `\x1b[40m${text}\x1b[0m`, bgGray: (text: string): string => `\x1b[100m${text}\x1b[0m`, bgGrey: (text: string): string => `\x1b[100m${text}\x1b[0m`, }; } else { // Return no-op functions when colors are disabled return { red: (text: string): string => text, green: (text: string): string => text, yellow: (text: string): string => text, blue: (text: string): string => text, magenta: (text: string): string => text, cyan: (text: string): string => text, white: (text: string): string => text, black: (text: string): string => text, gray: (text: string): string => text, grey: (text: string): string => text, bold: (text: string): string => text, dim: (text: string): string => text, italic: (text: string): string => text, underline: (text: string): string => text, strikethrough: (text: string): string => text, inverse: (text: string): string => text, bgRed: (text: string): string => text, bgGreen: (text: string): string => text, bgYellow: (text: string): string => text, bgBlue: (text: string): string => text, bgMagenta: (text: string): string => text, bgCyan: (text: string): string => text, bgWhite: (text: string): string => text, bgBlack: (text: string): string => text, bgGray: (text: string): string => text, bgGrey: (text: string): string => text, }; } } } /** * Convenience functions for common color patterns */ // Checkmarks and symbols export const symbols = { success: Colors.success('✓'), error: Colors.error('✗'), warning: Colors.warn('⚠'), info: Colors.info('ℹ'), arrow: Colors.dim('→'), bullet: Colors.dim('•'), dash: Colors.dim('─'), }; // Status indicators export const status = { success: (text: string): string => `${symbols.success} ${Colors.success(text)}`, error: (text: string): string => `${symbols.error} ${Colors.error(text)}`, warning: (text: string): string => `${symbols.warning} ${Colors.warn(text)}`, info: (text: string): string => `${symbols.info} ${Colors.info(text)}`, }; // Convenience export for the main colors class export const colors = Colors; // Default export for easy importing export default Colors;