Files
SuperClaude/setup/utils/ui.py
kazuki bf1fd50ca8 refactor: rename CoreComponent to FrameworkDocsComponent and add PM token tracking
## Changes

### Component Renaming (setup/components/)
- Renamed CoreComponent → FrameworkDocsComponent for clarity
- Updated all imports in __init__.py, agents.py, commands.py, mcp_docs.py, modes.py
- Better reflects the actual purpose (framework documentation files)

### PM Agent Enhancement (superclaude/commands/pm.md)
- Added token usage tracking instructions
- PM Agent now reports:
  1. Current token usage from system warnings
  2. Percentage used (e.g., "27% used" for 54K/200K)
  3. Status zone: 🟢 <75% | 🟡 75-85% | 🔴 >85%
- Helps prevent token exhaustion during long sessions

### UI Utilities (setup/utils/ui.py)
- Added new UI utility module for installer
- Provides consistent user interface components

## Benefits
-  Clearer component naming (FrameworkDocs vs Core)
-  PM Agent token awareness for efficiency
-  Better visual feedback with status zones

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 05:28:23 +09:00

204 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Minimal backward-compatible UI utilities
Stub implementation for legacy installer code
"""
class Colors:
"""ANSI color codes for terminal output"""
RESET = "\033[0m"
BRIGHT = "\033[1m"
DIM = "\033[2m"
BLACK = "\033[30m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
BG_BLACK = "\033[40m"
BG_RED = "\033[41m"
BG_GREEN = "\033[42m"
BG_YELLOW = "\033[43m"
BG_BLUE = "\033[44m"
BG_MAGENTA = "\033[45m"
BG_CYAN = "\033[46m"
BG_WHITE = "\033[47m"
def display_header(title: str, subtitle: str = "") -> None:
"""Display a formatted header"""
print(f"\n{Colors.CYAN}{Colors.BRIGHT}{title}{Colors.RESET}")
if subtitle:
print(f"{Colors.DIM}{subtitle}{Colors.RESET}")
print()
def display_success(message: str) -> None:
"""Display a success message"""
print(f"{Colors.GREEN}{message}{Colors.RESET}")
def display_error(message: str) -> None:
"""Display an error message"""
print(f"{Colors.RED}{message}{Colors.RESET}")
def display_warning(message: str) -> None:
"""Display a warning message"""
print(f"{Colors.YELLOW}{message}{Colors.RESET}")
def display_info(message: str) -> None:
"""Display an info message"""
print(f"{Colors.CYAN} {message}{Colors.RESET}")
def confirm(prompt: str, default: bool = True) -> bool:
"""
Simple confirmation prompt
Args:
prompt: The prompt message
default: Default response if user just presses Enter
Returns:
True if confirmed, False otherwise
"""
default_str = "Y/n" if default else "y/N"
response = input(f"{prompt} [{default_str}]: ").strip().lower()
if not response:
return default
return response in ("y", "yes")
class Menu:
"""Minimal menu implementation"""
def __init__(self, title: str, options: list, multi_select: bool = False):
self.title = title
self.options = options
self.multi_select = multi_select
def display(self):
"""Display menu and get selection"""
print(f"\n{Colors.CYAN}{Colors.BRIGHT}{self.title}{Colors.RESET}\n")
for i, option in enumerate(self.options, 1):
print(f"{i}. {option}")
if self.multi_select:
print(f"\n{Colors.DIM}Enter comma-separated numbers (e.g., 1,3,5) or 'all' for all options{Colors.RESET}")
while True:
try:
choice = input(f"Select [1-{len(self.options)}]: ").strip().lower()
if choice == "all":
return list(range(len(self.options)))
if not choice:
return []
selections = [int(x.strip()) - 1 for x in choice.split(",")]
if all(0 <= s < len(self.options) for s in selections):
return selections
print(f"{Colors.RED}Invalid selection{Colors.RESET}")
except (ValueError, KeyboardInterrupt):
print(f"\n{Colors.RED}Invalid input{Colors.RESET}")
else:
while True:
try:
choice = input(f"\nSelect [1-{len(self.options)}]: ").strip()
choice_num = int(choice)
if 1 <= choice_num <= len(self.options):
return choice_num - 1
print(f"{Colors.RED}Invalid selection{Colors.RESET}")
except (ValueError, KeyboardInterrupt):
print(f"\n{Colors.RED}Invalid input{Colors.RESET}")
class ProgressBar:
"""Minimal progress bar implementation"""
def __init__(self, total: int, prefix: str = "", suffix: str = ""):
self.total = total
self.prefix = prefix
self.suffix = suffix
self.current = 0
def update(self, current: int = None, message: str = None) -> None:
"""Update progress"""
if current is not None:
self.current = current
else:
self.current += 1
percent = int((self.current / self.total) * 100) if self.total > 0 else 100
display_msg = message or f"{self.prefix}{self.current}/{self.total} {self.suffix}"
print(f"\r{display_msg} {percent}%", end="", flush=True)
if self.current >= self.total:
print() # New line when complete
def finish(self, message: str = "Complete") -> None:
"""Finish progress bar"""
self.current = self.total
print(f"\r{message} 100%")
def close(self) -> None:
"""Close progress bar"""
if self.current < self.total:
print()
def format_size(size: int) -> str:
"""
Format size in bytes to human-readable string
Args:
size: Size in bytes
Returns:
Formatted size string (e.g., "1.5 MB", "256 KB")
"""
if size < 1024:
return f"{size} B"
elif size < 1024 * 1024:
return f"{size / 1024:.1f} KB"
elif size < 1024 * 1024 * 1024:
return f"{size / (1024 * 1024):.1f} MB"
else:
return f"{size / (1024 * 1024 * 1024):.1f} GB"
def prompt_api_key(service_name: str, env_var_name: str) -> str:
"""
Prompt user for API key
Args:
service_name: Name of the service requiring the key
env_var_name: Environment variable name for the key
Returns:
API key string (empty if user skips)
"""
print(f"\n{Colors.CYAN}{service_name} API Key{Colors.RESET}")
print(f"{Colors.DIM}Environment variable: {env_var_name}{Colors.RESET}")
print(f"{Colors.YELLOW}Press Enter to skip{Colors.RESET}")
try:
# Use getpass for password-like input (hidden)
import getpass
key = getpass.getpass("Enter API key: ").strip()
return key
except (EOFError, KeyboardInterrupt):
print(f"\n{Colors.YELLOW}Skipped{Colors.RESET}")
return ""