-
Notifications
You must be signed in to change notification settings - Fork 235
CLI Colors #1006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
CLI Colors #1006
Conversation
24caeb3 to
05ee4e3
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1006 +/- ##
==========================================
+ Coverage 72.97% 76.29% +3.31%
==========================================
Files 26 28 +2
Lines 1839 2417 +578
Branches 347 458 +111
==========================================
+ Hits 1342 1844 +502
- Misses 394 453 +59
- Partials 103 120 +17 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
604b533 to
53654da
Compare
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
Port vcspull's intuitive color practices into tmuxp CLI: - Add _colors.py module with ColorMode enum and Colors class - Add global --color flag (auto/always/never) to CLI - Support NO_COLOR and FORCE_COLOR environment variables - Update load.py with semantic colors (info, success, error, etc.) - Update ls.py and debug_info.py to use Colors class The Colors class wraps tmuxp's existing style() function with semantic methods (success, warning, error, info, highlight, muted) and handles TTY detection following CPython's _colorize patterns.
Add 25 tests covering: - ColorMode detection (TTY, NO_COLOR, FORCE_COLOR) - Semantic color methods (success, error, warning, etc.) - get_color_mode() helper function - Colors class behavior when disabled
Apply semantic color system to freeze command output: - error() for exception messages (session not found) - muted() for banner text and separators - info() for URLs and file paths - warning() for "file exists" messages - success() + info() for "Saved to <path>" message Add CLIFreezeNamespace.color field for color mode from global --color flag. Includes 9 unit tests for color output formatting.
Apply semantic color system to import command output: - error() for "Unknown config format" error - muted() for separator and banner text - info() for URLs and file paths - success() + info() for "Saved to <path>" message Update command_import_teamocil() and command_import_tmuxinator() to accept color parameter from global --color flag. Includes 9 unit tests for color output formatting.
Apply semantic color system to convert command output: - highlight() for format type (json/yaml) in prompts - info() for file paths in prompts - success() + info() for "saved to <path>" message Add color parameter to command_convert() from global --color flag. Includes 7 unit tests for color output formatting.
Apply semantic color system to shell command output:
- muted() for static text ("Launching", "shell for session", "...")
- highlight(bold=False) for shell type (ipython, pdb, etc.)
- info() for session name
Add CLIShellNamespace.color field for color mode from global --color flag.
Note: 'colors' field (56/88 for tmux palette) is separate from 'color' (CLI output).
Includes 7 unit tests for color output formatting.
Apply semantic color system to edit command output:
- muted() for static text ("Opening", "in", "...")
- info() for workspace file path
- highlight(bold=False) for editor name
Add color parameter to command_edit() from global --color flag.
Includes 6 unit tests for color output formatting.
Add comprehensive integration tests verifying --color flag behavior: - --color=auto respects TTY detection - --color=always forces colors even without TTY - --color=never disables colors even with TTY - NO_COLOR environment variable overrides all modes - FORCE_COLOR enables colors in auto mode without TTY - NO_COLOR takes precedence over FORCE_COLOR - All semantic methods respect enabled/disabled state - get_color_mode() handles all edge cases Includes 15 integration tests for cross-command color consistency.
Apply semantic color system to prompt utilities: - prompt(): default value [path] uses info() (cyan) - prompt_bool(): choice indicator [Y/n] uses muted() (blue) - prompt_choices(): options list (a, b) uses muted(), default uses info() Fix circular import between utils.py and _colors.py by using lazy import of style() inside _colorize() method. Includes 7 function-based tests for prompt color output.
Follow project conventions per AGENTS.md - use function-based tests instead of class-based tests for color modules. Converted files: - test_colors.py - test_cli_colors_integration.py - test_convert_colors.py - test_edit_colors.py - test_freeze_colors.py - test_import_colors.py - test_shell_colors.py
Add PrivatePath class that collapses home directory to ~ in string output, preventing PII exposure in logs and debug output. - PrivatePath: pathlib.Path subclass with masked __str__/__repr__ - collapse_home_in_string(): Helper for PATH-like colon-separated strings - Comprehensive tests for both utilities
Add reusable formatting methods to Colors class for syntax highlighting in structured output like debug-info: - format_label(): Bold magenta for key/label text - format_path(): Cyan for file paths - format_version(): Cyan for version strings - format_separator(): Muted separator lines - format_kv(): Key: value pairs with highlighted key - format_tmux_option(): Handles "key value" and "key=value" formats
Apply semantic colors and privacy masking to debug-info output: - Labels highlighted in bold magenta - Paths and versions in cyan - Separators in muted blue - tmux options with key=value highlighting - Home directory paths collapsed to ~ for privacy - System PATH env var also privacy-masked
- Prioritize space-separated format to handle values containing '=' (e.g., status-format[0] "#[align=left]") - Add support for empty array options like 'pane-colours' (key only) - Add test coverage for array-indexed options (status-format[0]) - Update docstring to document all supported formats
Per CLAUDE.md guidelines, use narrative descriptions for test sections rather than inline comments.
Apply semantic colors to print statements that were missed during the initial color system implementation.
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
Per AGENTS.md guidelines, convert inline comments in doctests to narrative descriptions outside the code blocks.
Consolidate all color/styling code in _colors.py to eliminate circular dependency. This fixes doctest compatibility with python -m doctest. Moved: style(), unstyle(), strip_ansi(), _interpret_color(), _ansi_colors, _ansi_reset_all, UnknownStyleColor
Only include stderr in output when it contains content, avoiding extra blank lines in debug-info output.
Mask home directory in CLI path outputs for privacy. All commands now display ~/... instead of /home/user/... when showing paths. Updated: load, freeze, convert, edit, import_config
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
- Add PrivatePath to load.py empty workspace warning - Remove extra angle brackets from convert.py success message to match freeze.py style: "Saved to ~/.tmuxp/file.json." - Update test to match new format
Add output formatting utilities for CLI commands supporting: - HUMAN mode: Colored human-readable output (default) - JSON mode: Buffered array output for machine parsing - NDJSON mode: Streaming newline-delimited JSON Provides get_output_mode() to determine mode from CLI flags. Pattern based on vcspull's output module.
Enhance ls command with machine-readable output formats: - --json: Output workspace list as JSON array - --ndjson: Output as newline-delimited JSON (one per line) - --tree: Group workspaces by directory with colored headers Extended JSON metadata includes: - name: workspace name (file stem) - path: file path (with ~ contraction) - format: yaml or json - size: file size in bytes - mtime: modification time (ISO format) - session_name: from config if parseable Tree mode shows session_name when it differs from filename. Pattern based on vcspull list command.
Apply PrivatePath to all user-facing path displays in freeze command: - Line 190: "Save to:" prompt now masks home directory - Line 246: "Save to ...?" confirmation now masks home directory Add tests for prompt masking behavior in test_freeze_colors.py.
Add find_local_workspace_files() function that searches for .tmuxp.yaml, .tmuxp.yml, or .tmuxp.json files from a start directory upward to home. Features: - Traverses parent directories like git does for .git/ - Stops at user home directory by default (configurable) - Returns files ordered from closest to farthest - Prioritizes .yaml > .yml > .json when multiple exist Add LOCAL_WORKSPACE_FILES constant for dotfile names. Tests cover: upward traversal, multiple ancestors, format precedence, edge cases (home dir, symlinks, filesystem root).
Add combined view of workspace files from both local directories (cwd and parents) and global directory (~/.tmuxp/). Changes: - Add `source` field to WorkspaceInfo: "local" or "global" - Import and use find_local_workspace_files() from finders - Update _output_flat() to group workspaces by source with headers - Local workspaces show path (useful since they may be in parent dirs) - Global workspaces show only name (path is always ~/.tmuxp/) - Update command_ls() to collect from both sources Tests added: - test_get_workspace_info_source_local - test_ls_finds_local_workspace_in_cwd - test_ls_finds_local_workspace_in_parent - test_ls_shows_local_and_global - test_ls_json_includes_source_for_local - test_ls_local_shows_path All existing tests updated with proper monkeypatching to avoid picking up real .tmuxp.yaml from the project directory.
Add --full flag to tmuxp ls command for detailed workspace inspection.
Changes:
- Add --full argument to subparser
- Update _get_workspace_info() with include_config parameter
- Add _render_config_tree() helper for human-readable window/pane hierarchy
- Update _output_flat() and _output_tree() to display config tree when full=True
- JSON/NDJSON output with --full includes full parsed config object
- Human output with --full shows tree: windows with layout, panes with commands
Example human output with --full:
dev
├── editor [main-horizontal]
│ ├── pane 0: vim
│ └── pane 1: git status
└── shell
└── pane 0
Example JSON with --full:
{"name": "dev", ..., "config": {"session_name": "dev", "windows": [...]}}
Tests added:
- test_ls_full_flag_subparser
- test_get_workspace_info_include_config
- test_get_workspace_info_no_config_by_default
- test_ls_json_full_includes_config
- test_ls_full_tree_shows_windows
- test_ls_full_flat_shows_windows
- test_ls_full_without_json_no_config_in_output
Add foundation for tmuxp search command inspired by vcspull's search: - SearchToken and SearchPattern types for query representation - Field alias support (n:, s:, p:, w: shortcuts) - Smart parsing: unknown prefixes treated as literal patterns - Regex compilation with ignore_case, smart_case, fixed_strings, word_regexp - InvalidFieldError for explicit --field validation
Add workspace search functionality: - WorkspaceFields TypedDict: name, path, session_name, windows, panes - WorkspaceSearchResult: filepath, source, fields, matches for output - extract_workspace_fields: parses config for searchable content - evaluate_match: AND/OR pattern matching with match tracking - find_search_matches: coordinates search across workspace list Match results include actual matched text for highlighting support.
Add search result output functionality: - highlight_matches: regex-based highlighting with span merging - _output_search_results: grouped output (local/global) with colors - JSON output includes matched_fields and matches for scripting - Human output shows session_name, windows, panes when matched
Complete CLI integration for tmuxp search command: - SEARCH_DESCRIPTION with usage examples - CLISearchNamespace for typed argument parsing - create_search_subparser() with all search options - command_search() entrypoint connecting all components - Register in __init__.py with routing - Add search to main CLI description examples Supports: -i/-S (case), -F (fixed), -w (word), -v (invert), --any (OR logic), --json/--ndjson output modes.
Add 59 tests covering all search functionality: - normalize_fields: field aliases, case handling, validation - parse_query_terms: prefixes, URLs, empty patterns - compile_search_patterns: all matching modes (case, word, fixed, regex) - extract_workspace_fields: config parsing, panes, windows - evaluate_match: AND/OR logic, field searches - find_search_matches: basic, invert, multiple workspaces - highlight_matches: colors, multiple matches - CLI subparser: options, output formats - Output formatting: JSON, NDJSON, human readable
Update CLI_DESCRIPTION with new command options: ls examples (from 57faf91, 67a4af2): - --tree: grouped by directory - --full: show window/pane details - --json: machine-readable output search examples: - name:myproject: field-scoped search - -i DEV: case-insensitive matching - --json dev: machine-readable output
…ions - Add 'search' to valid_subcommands in test_help_examples.py - Add 'search' to parametrize decorator for subcommand tests - Add test_search_subcommand_examples_are_valid test - Fix mypy errors by explicitly typing WorkspaceSearchResult dicts
Use argparse set_defaults pattern to store print_help callable, then invoke it when no query terms are provided. This makes `tmuxp search` equivalent to `tmuxp search --help`.
Verify that `tmuxp search` with no arguments shows help output and exits with code 0, equivalent to `tmuxp search --help`.
- Add --json flag to output structured JSON for machine parsing - Use PrivatePath for all paths (home → ~) in JSON output - Refactor to _collect_debug_info() and _format_human_output() - Update help examples in debug-info and main CLI - Add comprehensive tests with NamedTuple parametrization: - test_debug_info_output_modes (parametrized human/JSON) - test_debug_info_json_output (structure validation) - test_debug_info_json_no_ansi (no ANSI codes in JSON) - test_debug_info_json_paths_use_private_path (privacy)
Document new features added to CLI Colors PR #1006: - New tmuxp search command with field-scoped search - Enhanced tmuxp ls with --tree, --full, --json, --ndjson - Local workspace discovery from cwd and parents - tmuxp debug-info --json for machine-readable output
Add explicit mentions of `jq` piping and automation use cases for all JSON output features (search, ls, debug-info) to improve discoverability.
…ce dirs - Add heading() method to Colors class for section headers (cyan+bold) - Use highlight() for workspace names (magenta+bold) to distinguish from headers - Add get_workspace_dir_candidates() to show all checked directories - Display active directory in header: "Global workspaces (~/.tmuxp):" - Show candidate directories with source labels (Legacy, XDG, etc.) - Add comprehensive tests for new functionality Color hierarchy: - L0 Headers: heading() cyan+bold - L1 Items: highlight() magenta+bold - L2 Paths: info() cyan - L3 Labels: muted() blue
Document the semantic color system for CLI output: - Hierarchy-based color assignment (L0-L3 levels) - Status-based color overrides - Design principles from jq, ripgrep, mise/just - Available Colors class methods - Key rule: never use same color for adjacent hierarchy levels
Consistency with ls.py - section headers should use heading() (cyan+bold) instead of muted() (blue dim) per Color Semantics Rev 1.
Replace inline separator strings with colors.format_separator() for consistency with debug-info and other CLI output patterns. Files: freeze.py, import_config.py
The prompt() function now masks home directory in the displayed default value using PrivatePath, showing ~ instead of /home/user. Before: Save to: ~/.tmuxp/session.yaml [/home/user/.tmuxp/session.yaml] After: Save to: ~/.tmuxp/session.yaml [~/.tmuxp/session.yaml] The actual returned value remains the full path for file operations.
Previously, `tmuxp search gp-libs` would not find workspaces with a window named "gp-libs" because window/pane fields required explicit `window:` or `pane:` prefixes. Now searches all fields by default: - name (workspace filename) - session_name - path - window (window names) - pane (pane shell commands)
- Add create_themed_formatter() factory to inject theme into argparse formatters - Help output now colorizes examples when FORCE_COLOR=1 or on TTY - Respects NO_COLOR environment variable per no-color.org standard - Add _theme class attribute to TmuxpHelpFormatter for proper typing Also fixes: - Remove forbidden # doctest: +SKIP from _output.py (AGENTS.md violation) - Update search.py doctests for new DEFAULT_FIELDS with window/pane - Add missing pathlib import to test_prompt_colors.py Tests: Add tests/cli/test_formatter.py with 12 tests for factory and colorization
… codes
rstrip("\033[0m") strips individual characters (\, 0, 3, [, m), not
the string as a suffix. This corrupted ANSI sequences, leaving broken
codes like '\x1b[1' instead of '\x1b[1m'.
removesuffix() correctly removes the exact suffix string.
PrivatePath("") returns "." (current directory), which would show
"shell: ." in debug-info output when SHELL env var is unset/empty.
Now empty strings return "" like None does.
Summary
Port vcspull's intuitive color practices into tmuxp CLI, incorporating CPython best practices.
_colors.pymodule withColorModeenum andColorsclass--colorflag (auto/always/never) to CLI root parserNO_COLORandFORCE_COLORenvironment variables (CPython-style)load.pywith semantic colors (info, success, error, highlight, etc.)ls.pyanddebug_info.pyto use Colors classDesign
The
Colorsclass wraps tmuxp's existingstyle()function with semantic methods:success()- green, for successful operationswarning()- yellow, for warningserror()- red, for errorsinfo()- cyan, for informational messageshighlight()- magenta (bold), for important textmuted()- blue, for secondary textUsage
Test plan