Skip to content

Commit cbaec5e

Browse files
CybotTMclaude
andcommitted
feat(audit): add color support to audit table output
Added colorized output to make audit, make outdated, and all audit commands for improved visual distinction of tool statuses. Changes: 1. Added CLI_AUDIT_COLOR environment variable (default: 1/enabled) 2. Added ANSI color codes to cli_audit/render.py: - GREEN: Up-to-date tools - YELLOW: Outdated installed versions - BOLD_GREEN: Newer available versions - BLUE: Not installed tools 3. Updated render_table() to apply colors based on status 4. Added colorize() helper function 5. Updated Makefile targets to enable colors: - audit, audit-offline, outdated - audit-%, audit-offline-% Color scheme: - UP-TO-DATE: green installed, green latest - OUTDATED: yellow installed, bold green latest - NOT INSTALLED: blue installed, blue latest - CONFLICT: yellow installed, bold green latest Disable with: CLI_AUDIT_COLOR=0 make audit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e5c086c commit cbaec5e

2 files changed

Lines changed: 54 additions & 9 deletions

File tree

Makefile.d/user.mk

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@ audit: ## Render audit from snapshot (no network, <100ms)
2020
echo " Consider running '\''make update'\'' for fresh version data." >&2; \
2121
fi; \
2222
fi; \
23-
set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 $(PYTHON) audit.py | \
23+
set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py | \
2424
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
2525

2626
audit-offline: ## Offline audit with hints (fast local scan)
27-
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 $(PYTHON) audit.py | \
27+
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py | \
2828
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
2929

3030
outdated: ## Show only missing and outdated tools
31-
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_FILTER_STATUS="NOT INSTALLED,OUTDATED" $(PYTHON) audit.py | \
31+
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 CLI_AUDIT_FILTER_STATUS="NOT INSTALLED,OUTDATED" $(PYTHON) audit.py | \
3232
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
3333

3434
audit-%: scripts-perms ## Audit single tool (e.g., make audit-ripgrep)
35-
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 $(PYTHON) audit.py $* | \
35+
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py $* | \
3636
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
3737

3838
audit-offline-%: scripts-perms ## Offline audit subset (e.g., make audit-offline-python-core)
39-
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 $(PYTHON) audit.py $* | \
39+
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py $* | \
4040
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
4141

4242
SNAP_FILE?=$(shell python3 -c "import os;print(os.environ.get('CLI_AUDIT_SNAPSHOT_FILE','tools_snapshot.json'))")

cli_audit/render.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
# Environment options
1313
USE_EMOJI = os.environ.get("CLI_AUDIT_EMOJI", "1") == "1"
1414
ENABLE_LINKS = os.environ.get("CLI_AUDIT_LINKS", "1") == "1"
15+
USE_COLOR = os.environ.get("CLI_AUDIT_COLOR", "1") == "1"
16+
17+
# ANSI color codes
18+
GREEN = "\033[32m"
19+
BOLD_GREEN = "\033[1;32m"
20+
YELLOW = "\033[33m"
21+
BLUE = "\033[34m"
22+
RED = "\033[31m"
23+
RESET = "\033[0m"
1524

1625

1726
def status_icon(status: str, installed: str) -> str:
@@ -47,6 +56,21 @@ def status_icon(status: str, installed: str) -> str:
4756
return "❓"
4857

4958

59+
def colorize(text: str, color: str) -> str:
60+
"""Apply color to text.
61+
62+
Args:
63+
text: Text to colorize
64+
color: ANSI color code
65+
66+
Returns:
67+
Colored text or plain text if colors disabled
68+
"""
69+
if not USE_COLOR or not text:
70+
return text
71+
return f"{color}{text}{RESET}"
72+
73+
5074
def osc8(url: str, text: str) -> str:
5175
"""Create OSC8 hyperlink.
5276
@@ -92,9 +116,30 @@ def render_table(tools: list[dict[str, Any]], show_hints: bool = False) -> None:
92116
# Icon
93117
icon = status_icon(status, installed)
94118

119+
# Determine colors based on status
120+
if status == "UP-TO-DATE":
121+
inst_color = GREEN
122+
latest_color = GREEN
123+
elif status == "OUTDATED":
124+
inst_color = YELLOW
125+
latest_color = BOLD_GREEN # Latest is newer, make it bold green
126+
elif status == "CONFLICT":
127+
inst_color = YELLOW
128+
latest_color = BOLD_GREEN
129+
else: # NOT INSTALLED, UNKNOWN
130+
inst_color = BLUE
131+
latest_color = BLUE
132+
95133
# Hyperlinks
96134
name_display = osc8(tool_url, name) if tool_url else name
97-
latest_display = osc8(latest_url, latest) if latest_url else latest
135+
136+
# Apply colors to installed and latest (before adding markers/hints)
137+
installed_display = colorize(installed, inst_color)
138+
latest_display = colorize(latest, latest_color)
139+
140+
# Apply hyperlinks (after colorization, hyperlinks wrap the colored text)
141+
if latest_url:
142+
latest_display = osc8(latest_url, latest_display)
98143

99144
# Add pinned/skip markers
100145
markers = []
@@ -113,10 +158,10 @@ def render_table(tools: list[dict[str, Any]], show_hints: bool = False) -> None:
113158
latest_display = f"{latest_display} [{hint}]"
114159

115160
# Add CONFLICT message to installed display
116-
if status == "CONFLICT" and installed.startswith("CONFLICT:"):
117-
installed = installed.replace("CONFLICT: ", "") # Show clean message
161+
if status == "CONFLICT" and installed_display.startswith("CONFLICT:"):
162+
installed_display = installed_display.replace("CONFLICT: ", "") # Show clean message
118163

119-
print("|".join((icon, name_display, installed, latest_display)))
164+
print("|".join((icon, name_display, installed_display, latest_display)))
120165

121166

122167
def print_summary(snapshot: dict[str, Any], tools: list[dict[str, Any]]) -> None:

0 commit comments

Comments
 (0)