From bf1fd50ca86c8c97e7ae0237758f7ac368895acd Mon Sep 17 00:00:00 2001 From: kazuki Date: Fri, 17 Oct 2025 05:28:23 +0900 Subject: [PATCH] refactor: rename CoreComponent to FrameworkDocsComponent and add PM token tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 --- setup/components/__init__.py | 4 +- setup/components/agents.py | 2 +- setup/components/commands.py | 2 +- setup/components/mcp_docs.py | 2 +- setup/components/modes.py | 2 +- setup/utils/ui.py | 203 +++++++++++++++++++++++++++++++++++ superclaude/commands/pm.md | 10 +- 7 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 setup/utils/ui.py diff --git a/setup/components/__init__.py b/setup/components/__init__.py index 41d2896..c9a5d7b 100644 --- a/setup/components/__init__.py +++ b/setup/components/__init__.py @@ -1,6 +1,6 @@ """Component implementations for SuperClaude installation system""" -from .core import CoreComponent +from .framework_docs import FrameworkDocsComponent from .commands import CommandsComponent from .mcp import MCPComponent from .agents import AgentsComponent @@ -8,7 +8,7 @@ from .modes import ModesComponent from .mcp_docs import MCPDocsComponent __all__ = [ - "CoreComponent", + "FrameworkDocsComponent", "CommandsComponent", "MCPComponent", "AgentsComponent", diff --git a/setup/components/agents.py b/setup/components/agents.py index e462bce..c32df5d 100644 --- a/setup/components/agents.py +++ b/setup/components/agents.py @@ -126,7 +126,7 @@ class AgentsComponent(Component): def get_dependencies(self) -> List[str]: """Get component dependencies""" - return ["core"] + return ["framework_docs"] def update(self, config: Dict[str, Any]) -> bool: """Update agents component""" diff --git a/setup/components/commands.py b/setup/components/commands.py index 21ba41e..2715c2e 100644 --- a/setup/components/commands.py +++ b/setup/components/commands.py @@ -153,7 +153,7 @@ class CommandsComponent(Component): def get_dependencies(self) -> List[str]: """Get dependencies""" - return ["core"] + return ["framework_docs"] def update(self, config: Dict[str, Any]) -> bool: """Update commands component""" diff --git a/setup/components/mcp_docs.py b/setup/components/mcp_docs.py index b2ee722..2792176 100644 --- a/setup/components/mcp_docs.py +++ b/setup/components/mcp_docs.py @@ -340,7 +340,7 @@ class MCPDocsComponent(Component): def get_dependencies(self) -> List[str]: """Get dependencies""" - return ["core"] + return ["framework_docs"] def _get_source_dir(self) -> Optional[Path]: """Get source directory for MCP documentation files""" diff --git a/setup/components/modes.py b/setup/components/modes.py index 82dea98..570fb2c 100644 --- a/setup/components/modes.py +++ b/setup/components/modes.py @@ -140,7 +140,7 @@ class ModesComponent(Component): def get_dependencies(self) -> List[str]: """Get dependencies""" - return ["core"] + return ["framework_docs"] def _get_source_dir(self) -> Optional[Path]: """Get source directory for mode files""" diff --git a/setup/utils/ui.py b/setup/utils/ui.py new file mode 100644 index 0000000..a890ad8 --- /dev/null +++ b/setup/utils/ui.py @@ -0,0 +1,203 @@ +""" +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 "" diff --git a/superclaude/commands/pm.md b/superclaude/commands/pm.md index 9afc53a..66ae26c 100644 --- a/superclaude/commands/pm.md +++ b/superclaude/commands/pm.md @@ -8,7 +8,13 @@ personas: [pm-agent] --- ⏺ PM Agent ready. Bootstrap complete (150 tokens) - - Repository: Detected - - Memory: docs/memory/ ready + 📊 Context: Check system warnings for current token usage (Budget: 200K tokens) + 📁 Repository: Detected + 🧠 Memory: docs/memory/ ready + + **Instructions**: After loading this prompt, immediately report: + 1. Current token usage from most recent system warning + 2. Percentage used (e.g., "27% used" for 54K/200K) + 3. Status zone: 🟢 <75% | 🟡 75-85% | 🔴 >85% What would you like me to help with?