mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-29 16:16:08 +00:00
refactor: PEP8 compliance - directory rename and code formatting (#425)
* fix(orchestration): add WebFetch auto-trigger for infrastructure configuration Problem: Infrastructure configuration changes (e.g., Traefik port settings) were being made based on assumptions without consulting official documentation, violating the 'Evidence > assumptions' principle in PRINCIPLES.md. Solution: - Added Infrastructure Configuration Validation section to MODE_Orchestration.md - Auto-triggers WebFetch for infrastructure tools (Traefik, nginx, Docker, etc.) - Enforces MODE_DeepResearch activation for investigation - BLOCKS assumption-based configuration changes Testing: Verified WebFetch successfully retrieves Traefik official docs (port 80 default) This prevents production outages from infrastructure misconfiguration by ensuring all technical recommendations are backed by official documentation. * feat: Add PM Agent (Project Manager Agent) for seamless orchestration Introduces PM Agent as the default orchestration layer that coordinates all sub-agents and manages workflows automatically. Key Features: - Default orchestration: All user interactions handled by PM Agent - Auto-delegation: Intelligent sub-agent selection based on task analysis - Docker Gateway integration: Zero-token baseline with dynamic MCP loading - Self-improvement loop: Automatic documentation of patterns and mistakes - Optional override: Users can specify sub-agents explicitly if desired Architecture: - Agent spec: SuperClaude/Agents/pm-agent.md - Command: SuperClaude/Commands/pm.md - Updated docs: README.md (15→16 agents), agents.md (new Orchestration category) User Experience: - Default: PM Agent handles everything (seamless, no manual routing) - Optional: Explicit --agent flag for direct sub-agent access - Both modes available simultaneously (no user downside) Implementation Status: - ✅ Specification complete - ✅ Documentation complete - ⏳ Prototype implementation needed - ⏳ Docker Gateway integration needed - ⏳ Testing and validation needed Refs: kazukinakai/docker-mcp-gateway (IRIS MCP Gateway integration) * feat: Add Agent Orchestration rules for PM Agent default activation Implements PM Agent as the default orchestration layer in RULES.md. Key Changes: - New 'Agent Orchestration' section (CRITICAL priority) - PM Agent receives ALL user requests by default - Manual override with @agent-[name] bypasses PM Agent - Agent Selection Priority clearly defined: 1. Manual override → Direct routing 2. Default → PM Agent → Auto-delegation 3. Delegation based on keywords, file types, complexity, context User Experience: - Default: PM Agent handles everything (seamless) - Override: @agent-[name] for direct specialist access - Transparent: PM Agent reports delegation decisions This establishes PM Agent as the orchestration layer while respecting existing auto-activation patterns and manual overrides. Next Steps: - Local testing in agiletec project - Iteration based on actual behavior - Documentation updates as needed * refactor(pm-agent): redesign as self-improvement meta-layer Problem Resolution: PM Agent's initial design competed with existing auto-activation for task routing, creating confusion about orchestration responsibilities and adding unnecessary complexity. Design Change: Redefined PM Agent as a meta-layer agent that operates AFTER specialist agents complete tasks, focusing on: - Post-implementation documentation and pattern recording - Immediate mistake analysis with prevention checklists - Monthly documentation maintenance and noise reduction - Pattern extraction and knowledge synthesis Two-Layer Orchestration System: 1. Task Execution Layer: Existing auto-activation handles task routing (unchanged) 2. Self-Improvement Layer: PM Agent meta-layer handles documentation (new) Files Modified: - SuperClaude/Agents/pm-agent.md: Complete rewrite with meta-layer design - Category: orchestration → meta - Triggers: All user interactions → Post-implementation, mistakes, monthly - Behavioral Mindset: Continuous learning system - Self-Improvement Workflow: BEFORE/DURING/AFTER/MISTAKE RECOVERY/MAINTENANCE - SuperClaude/Core/RULES.md: Agent Orchestration section updated - Split into Task Execution Layer + Self-Improvement Layer - Added orchestration flow diagram - Clarified PM Agent activates AFTER task completion - README.md: Updated PM Agent description - "orchestrates all interactions" → "ensures continuous learning" - Docs/User-Guide/agents.md: PM Agent section rewritten - Section: Orchestration Agent → Meta-Layer Agent - Expertise: Project orchestration → Self-improvement workflow executor - Examples: Task coordination → Post-implementation documentation - PR_DOCUMENTATION.md: Comprehensive PR documentation added - Summary, motivation, changes, testing, breaking changes - Two-layer orchestration system diagram - Verification checklist Integration Validated: Tested with agiletec project's self-improvement-workflow.md: ✅ PM Agent aligns with existing BEFORE/DURING/AFTER/MISTAKE RECOVERY phases ✅ Complements (not competes with) existing workflow ✅ agiletec workflow defines WHAT, PM Agent defines WHO executes it Breaking Changes: None - Existing auto-activation continues unchanged - Specialist agents unaffected - User workflows remain the same - New capability: Automatic documentation and knowledge maintenance Value Proposition: Transforms SuperClaude into a continuously learning system that accumulates knowledge, prevents recurring mistakes, and maintains fresh documentation without manual intervention. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: add Claude Code conversation history management research Research covering .jsonl file structure, performance impact, and retention policies. Content: - Claude Code .jsonl file format and message types - Performance issues from GitHub (memory leaks, conversation compaction) - Retention policies (consumer vs enterprise) - Rotation recommendations based on actual data - File history snapshot tracking mechanics Source: Moved from agiletec project (research applicable to all Claude Code projects) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: add Development documentation structure Phase 1: Documentation Structure complete - Add Docs/Development/ directory for development documentation - Add ARCHITECTURE.md - System architecture with PM Agent meta-layer - Add ROADMAP.md - 5-phase development plan with checkboxes - Add TASKS.md - Daily task tracking with progress indicators - Add PROJECT_STATUS.md - Current status dashboard and metrics - Add pm-agent-integration.md - Implementation guide for PM Agent mode This establishes comprehensive documentation foundation for: - System architecture understanding - Development planning and tracking - Implementation guidance - Progress visibility Related: #pm-agent-mode #documentation #phase-1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: PM Agent session lifecycle and PDCA implementation Phase 2: PM Agent Mode Integration (Design Phase) Commands/pm.md updates: - Add "Always-Active Foundation Layer" concept - Add Session Lifecycle (Session Start/During Work/Session End) - Add PDCA Cycle (Plan/Do/Check/Act) automation - Add Serena MCP Memory Integration (list/read/write_memory) - Document auto-activation triggers Agents/pm-agent.md updates: - Add Session Start Protocol (MANDATORY auto-activation) - Add During Work PDCA Cycle with example workflows - Add Session End Protocol with state preservation - Add PDCA Self-Evaluation Pattern - Add Documentation Strategy (temp → patterns/mistakes) - Add Memory Operations Reference Key Features: - Session start auto-activation for context restoration - 30-minute checkpoint saves during work - Self-evaluation with think_about_* operations - Systematic documentation lifecycle - Knowledge evolution to CLAUDE.md Implementation Status: - ✅ Design complete (Commands/pm.md, Agents/pm-agent.md) - ⏳ Implementation pending (Core components) - ⏳ Serena MCP integration pending Salvaged from mistaken development in ~/.claude directory Related: #pm-agent-mode #session-lifecycle #pdca-cycle #phase-2 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: disable Serena MCP auto-browser launch Disable web dashboard and GUI log window auto-launch in Serena MCP server to prevent intrusive browser popups on startup. Users can still manually access the dashboard at http://localhost:24282/dashboard/ if needed. Changes: - Add CLI flags to Serena run command: - --enable-web-dashboard false - --enable-gui-log-window false - Ensures Git-tracked configuration (no reliance on ~/.serena/serena_config.yml) - Aligns with AIRIS MCP Gateway integration approach 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: rename directories to lowercase for PEP8 compliance - Rename superclaude/Agents -> superclaude/agents - Rename superclaude/Commands -> superclaude/commands - Rename superclaude/Core -> superclaude/core - Rename superclaude/Examples -> superclaude/examples - Rename superclaude/MCP -> superclaude/mcp - Rename superclaude/Modes -> superclaude/modes This change follows Python PEP8 naming conventions for package directories. * style: fix PEP8 violations and update package name to lowercase Changes: - Format all Python files with black (43 files reformatted) - Update package name from 'SuperClaude' to 'superclaude' in pyproject.toml - Fix import statements to use lowercase package name - Add missing imports (timedelta, __version__) - Remove old SuperClaude.egg-info directory PEP8 violations reduced from 2672 to 701 (mostly E501 line length due to black's 88 char vs flake8's 79 char limit). * docs: add PM Agent development documentation Add comprehensive PM Agent development documentation: - PM Agent ideal workflow (7-phase autonomous cycle) - Project structure understanding (Git vs installed environment) - Installation flow understanding (CommandsComponent behavior) - Task management system (current-tasks.md) Purpose: Eliminate repeated explanations and enable autonomous PDCA cycles 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(pm-agent): add self-correcting execution and warning investigation culture ## Changes ### superclaude/commands/pm.md - Add "Self-Correcting Execution" section with root cause analysis protocol - Add "Warning/Error Investigation Culture" section enforcing zero-tolerance for dismissal - Define error detection protocol: STOP → Investigate → Hypothesis → Different Solution → Execute - Document anti-patterns (retry without understanding) and correct patterns (research-first) ### docs/Development/hypothesis-pm-autonomous-enhancement-2025-10-14.md - Add PDCA workflow hypothesis document for PM Agent autonomous enhancement ## Rationale PM Agent must never retry failed operations without understanding root causes. All warnings and errors require investigation via context7/WebFetch/documentation to ensure production-quality code and prevent technical debt accumulation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(installer): add airis-mcp-gateway MCP server option ## Changes - Add airis-mcp-gateway to MCP server options in installer - Configuration: GitHub-based installation via uvx - Repository: https://github.com/oraios/airis-mcp-gateway - Purpose: Dynamic MCP Gateway for zero-token baseline and on-demand tool loading ## Implementation Added to setup/components/mcp.py self.mcp_servers dictionary with: - install_method: github - install_command: uvx test installation - run_command: uvx runtime execution - required: False (optional server) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: kazuki <kazuki@kazukinoMacBook-Air.local> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,8 +15,17 @@ from ...core.registry import ComponentRegistry
|
||||
from ...services.config import ConfigService
|
||||
from ...core.validator import Validator
|
||||
from ...utils.ui import (
|
||||
display_header, display_info, display_success, display_error,
|
||||
display_warning, Menu, confirm, ProgressBar, Colors, format_size, prompt_api_key
|
||||
display_header,
|
||||
display_info,
|
||||
display_success,
|
||||
display_error,
|
||||
display_warning,
|
||||
Menu,
|
||||
confirm,
|
||||
ProgressBar,
|
||||
Colors,
|
||||
format_size,
|
||||
prompt_api_key,
|
||||
)
|
||||
from ...utils.environment import setup_environment_variables
|
||||
from ...utils.logger import get_logger
|
||||
@@ -26,7 +35,7 @@ from . import OperationBase
|
||||
|
||||
class InstallOperation(OperationBase):
|
||||
"""Installation operation implementation"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("install")
|
||||
|
||||
@@ -34,7 +43,7 @@ class InstallOperation(OperationBase):
|
||||
def register_parser(subparsers, global_parser=None) -> argparse.ArgumentParser:
|
||||
"""Register installation CLI arguments"""
|
||||
parents = [global_parser] if global_parser else []
|
||||
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
"install",
|
||||
help="Install SuperClaude framework components",
|
||||
@@ -47,54 +56,51 @@ Examples:
|
||||
SuperClaude install --verbose --force # Verbose with force mode
|
||||
""",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
parents=parents
|
||||
parents=parents,
|
||||
)
|
||||
|
||||
|
||||
# Installation mode options
|
||||
|
||||
|
||||
parser.add_argument(
|
||||
"--components",
|
||||
type=str,
|
||||
nargs="+",
|
||||
help="Specific components to install"
|
||||
"--components", type=str, nargs="+", help="Specific components to install"
|
||||
)
|
||||
|
||||
|
||||
# Installation options
|
||||
parser.add_argument(
|
||||
"--no-backup",
|
||||
action="store_true",
|
||||
help="Skip backup creation"
|
||||
)
|
||||
|
||||
parser.add_argument("--no-backup", action="store_true", help="Skip backup creation")
|
||||
|
||||
parser.add_argument(
|
||||
"--list-components",
|
||||
action="store_true",
|
||||
help="List available components and exit"
|
||||
help="List available components and exit",
|
||||
)
|
||||
|
||||
|
||||
parser.add_argument(
|
||||
"--diagnose",
|
||||
action="store_true",
|
||||
help="Run system diagnostics and show installation help"
|
||||
help="Run system diagnostics and show installation help",
|
||||
)
|
||||
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def validate_system_requirements(validator: Validator, component_names: List[str]) -> bool:
|
||||
def validate_system_requirements(
|
||||
validator: Validator, component_names: List[str]
|
||||
) -> bool:
|
||||
"""Validate system requirements"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
logger.info("Validating system requirements...")
|
||||
|
||||
|
||||
try:
|
||||
# Load requirements configuration
|
||||
config_manager = ConfigService(DATA_DIR)
|
||||
requirements = config_manager.get_requirements_for_components(component_names)
|
||||
|
||||
|
||||
# Validate requirements
|
||||
success, errors = validator.validate_component_requirements(component_names, requirements)
|
||||
|
||||
success, errors = validator.validate_component_requirements(
|
||||
component_names, requirements
|
||||
)
|
||||
|
||||
if success:
|
||||
logger.success("All system requirements met")
|
||||
return True
|
||||
@@ -102,67 +108,79 @@ def validate_system_requirements(validator: Validator, component_names: List[str
|
||||
logger.error("System requirements not met:")
|
||||
for error in errors:
|
||||
logger.error(f" - {error}")
|
||||
|
||||
|
||||
# Provide additional guidance
|
||||
print(f"\n{Colors.CYAN}💡 Installation Help:{Colors.RESET}")
|
||||
print(" Run 'SuperClaude install --diagnose' for detailed system diagnostics")
|
||||
print(
|
||||
" Run 'superclaude install --diagnose' for detailed system diagnostics"
|
||||
)
|
||||
print(" and step-by-step installation instructions.")
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Could not validate system requirements: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_components_to_install(args: argparse.Namespace, registry: ComponentRegistry, config_manager: ConfigService) -> Optional[List[str]]:
|
||||
def get_components_to_install(
|
||||
args: argparse.Namespace, registry: ComponentRegistry, config_manager: ConfigService
|
||||
) -> Optional[List[str]]:
|
||||
"""Determine which components to install"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
# Explicit components specified
|
||||
if args.components:
|
||||
if 'all' in args.components:
|
||||
if "all" in args.components:
|
||||
components = ["core", "commands", "agents", "modes", "mcp", "mcp_docs"]
|
||||
else:
|
||||
components = args.components
|
||||
|
||||
# If mcp or mcp_docs is specified non-interactively, we should still ask which servers to install.
|
||||
if 'mcp' in components or 'mcp_docs' in components:
|
||||
if "mcp" in components or "mcp_docs" in components:
|
||||
selected_servers = select_mcp_servers(registry)
|
||||
if not hasattr(config_manager, '_installation_context'):
|
||||
if not hasattr(config_manager, "_installation_context"):
|
||||
config_manager._installation_context = {}
|
||||
config_manager._installation_context["selected_mcp_servers"] = selected_servers
|
||||
config_manager._installation_context["selected_mcp_servers"] = (
|
||||
selected_servers
|
||||
)
|
||||
|
||||
# If the user selected some servers, ensure both mcp and mcp_docs are included
|
||||
if selected_servers:
|
||||
if 'mcp' not in components:
|
||||
components.append('mcp')
|
||||
logger.debug(f"Auto-added 'mcp' component for selected servers: {selected_servers}")
|
||||
if 'mcp_docs' not in components:
|
||||
components.append('mcp_docs')
|
||||
logger.debug(f"Auto-added 'mcp_docs' component for selected servers: {selected_servers}")
|
||||
if "mcp" not in components:
|
||||
components.append("mcp")
|
||||
logger.debug(
|
||||
f"Auto-added 'mcp' component for selected servers: {selected_servers}"
|
||||
)
|
||||
if "mcp_docs" not in components:
|
||||
components.append("mcp_docs")
|
||||
logger.debug(
|
||||
f"Auto-added 'mcp_docs' component for selected servers: {selected_servers}"
|
||||
)
|
||||
|
||||
logger.info(f"Final components to install: {components}")
|
||||
|
||||
# If mcp_docs was explicitly requested but no servers selected, allow auto-detection
|
||||
elif not selected_servers and 'mcp_docs' in components:
|
||||
elif not selected_servers and "mcp_docs" in components:
|
||||
logger.info("mcp_docs component will auto-detect existing MCP servers")
|
||||
logger.info("Documentation will be installed for any detected servers")
|
||||
|
||||
return components
|
||||
|
||||
|
||||
# Interactive two-stage selection
|
||||
return interactive_component_selection(registry, config_manager)
|
||||
|
||||
|
||||
def collect_api_keys_for_servers(selected_servers: List[str], mcp_instance) -> Dict[str, str]:
|
||||
def collect_api_keys_for_servers(
|
||||
selected_servers: List[str], mcp_instance
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Collect API keys for servers that require them
|
||||
|
||||
|
||||
Args:
|
||||
selected_servers: List of selected server keys
|
||||
mcp_instance: MCP component instance
|
||||
|
||||
|
||||
Returns:
|
||||
Dictionary of environment variable names to API key values
|
||||
"""
|
||||
@@ -170,132 +188,164 @@ def collect_api_keys_for_servers(selected_servers: List[str], mcp_instance) -> D
|
||||
servers_needing_keys = [
|
||||
(server_key, mcp_instance.mcp_servers[server_key])
|
||||
for server_key in selected_servers
|
||||
if server_key in mcp_instance.mcp_servers and
|
||||
mcp_instance.mcp_servers[server_key].get("requires_api_key", False)
|
||||
if server_key in mcp_instance.mcp_servers
|
||||
and mcp_instance.mcp_servers[server_key].get("requires_api_key", False)
|
||||
]
|
||||
|
||||
|
||||
if not servers_needing_keys:
|
||||
return {}
|
||||
|
||||
|
||||
# Display API key configuration header
|
||||
print(f"\n{Colors.CYAN}{Colors.BRIGHT}=== API Key Configuration ==={Colors.RESET}")
|
||||
print(f"{Colors.YELLOW}The following servers require API keys for full functionality:{Colors.RESET}\n")
|
||||
|
||||
print(
|
||||
f"{Colors.YELLOW}The following servers require API keys for full functionality:{Colors.RESET}\n"
|
||||
)
|
||||
|
||||
collected_keys = {}
|
||||
for server_key, server_info in servers_needing_keys:
|
||||
api_key_env = server_info.get("api_key_env")
|
||||
service_name = server_info["name"]
|
||||
|
||||
|
||||
if api_key_env:
|
||||
key = prompt_api_key(service_name, api_key_env)
|
||||
if key:
|
||||
collected_keys[api_key_env] = key
|
||||
|
||||
|
||||
return collected_keys
|
||||
|
||||
|
||||
def select_mcp_servers(registry: ComponentRegistry) -> List[str]:
|
||||
"""Stage 1: MCP Server Selection with API Key Collection"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
try:
|
||||
# Get MCP component to access server list
|
||||
mcp_instance = registry.get_component_instance("mcp", get_home_directory() / ".claude")
|
||||
if not mcp_instance or not hasattr(mcp_instance, 'mcp_servers'):
|
||||
mcp_instance = registry.get_component_instance(
|
||||
"mcp", get_home_directory() / ".claude"
|
||||
)
|
||||
if not mcp_instance or not hasattr(mcp_instance, "mcp_servers"):
|
||||
logger.error("Could not access MCP server information")
|
||||
return []
|
||||
|
||||
|
||||
# Create MCP server menu
|
||||
mcp_servers = mcp_instance.mcp_servers
|
||||
server_options = []
|
||||
|
||||
|
||||
for server_key, server_info in mcp_servers.items():
|
||||
description = server_info["description"]
|
||||
api_key_note = " (requires API key)" if server_info.get("requires_api_key", False) else ""
|
||||
api_key_note = (
|
||||
" (requires API key)"
|
||||
if server_info.get("requires_api_key", False)
|
||||
else ""
|
||||
)
|
||||
server_options.append(f"{server_key} - {description}{api_key_note}")
|
||||
|
||||
|
||||
print(f"\n{Colors.CYAN}{Colors.BRIGHT}{'='*51}{Colors.RESET}")
|
||||
print(f"{Colors.CYAN}{Colors.BRIGHT}Stage 1: MCP Server Selection (Optional){Colors.RESET}")
|
||||
print(
|
||||
f"{Colors.CYAN}{Colors.BRIGHT}Stage 1: MCP Server Selection (Optional){Colors.RESET}"
|
||||
)
|
||||
print(f"{Colors.CYAN}{Colors.BRIGHT}{'='*51}{Colors.RESET}")
|
||||
print(f"\n{Colors.BLUE}MCP servers extend Claude Code with specialized capabilities.{Colors.RESET}")
|
||||
print(f"{Colors.BLUE}Select servers to configure (you can always add more later):{Colors.RESET}")
|
||||
|
||||
print(
|
||||
f"\n{Colors.BLUE}MCP servers extend Claude Code with specialized capabilities.{Colors.RESET}"
|
||||
)
|
||||
print(
|
||||
f"{Colors.BLUE}Select servers to configure (you can always add more later):{Colors.RESET}"
|
||||
)
|
||||
|
||||
# Add option to skip MCP
|
||||
server_options.append("Skip MCP Server installation")
|
||||
|
||||
menu = Menu("Select MCP servers to configure:", server_options, multi_select=True)
|
||||
|
||||
menu = Menu(
|
||||
"Select MCP servers to configure:", server_options, multi_select=True
|
||||
)
|
||||
selections = menu.display()
|
||||
|
||||
|
||||
if not selections:
|
||||
logger.info("No MCP servers selected")
|
||||
return []
|
||||
|
||||
|
||||
# Filter out the "skip" option and return server keys
|
||||
server_keys = list(mcp_servers.keys())
|
||||
selected_servers = []
|
||||
|
||||
|
||||
for i in selections:
|
||||
if i < len(server_keys): # Not the "skip" option
|
||||
selected_servers.append(server_keys[i])
|
||||
|
||||
|
||||
if selected_servers:
|
||||
logger.info(f"Selected MCP servers: {', '.join(selected_servers)}")
|
||||
|
||||
|
||||
# NEW: Collect API keys for selected servers
|
||||
collected_keys = collect_api_keys_for_servers(selected_servers, mcp_instance)
|
||||
|
||||
collected_keys = collect_api_keys_for_servers(
|
||||
selected_servers, mcp_instance
|
||||
)
|
||||
|
||||
# Set up environment variables
|
||||
if collected_keys:
|
||||
setup_environment_variables(collected_keys)
|
||||
|
||||
|
||||
# Store keys for MCP component to use during installation
|
||||
mcp_instance.collected_api_keys = collected_keys
|
||||
else:
|
||||
logger.info("No MCP servers selected")
|
||||
|
||||
|
||||
return selected_servers
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in MCP server selection: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def select_framework_components(registry: ComponentRegistry, config_manager: ConfigService, selected_mcp_servers: List[str]) -> List[str]:
|
||||
def select_framework_components(
|
||||
registry: ComponentRegistry,
|
||||
config_manager: ConfigService,
|
||||
selected_mcp_servers: List[str],
|
||||
) -> List[str]:
|
||||
"""Stage 2: Framework Component Selection"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
try:
|
||||
# Framework components (excluding MCP-related ones)
|
||||
framework_components = ["core", "modes", "commands", "agents"]
|
||||
|
||||
|
||||
# Create component menu
|
||||
component_options = []
|
||||
component_info = {}
|
||||
|
||||
|
||||
for component_name in framework_components:
|
||||
metadata = registry.get_component_metadata(component_name)
|
||||
if metadata:
|
||||
description = metadata.get("description", "No description")
|
||||
component_options.append(f"{component_name} - {description}")
|
||||
component_info[component_name] = metadata
|
||||
|
||||
|
||||
# Add MCP documentation option
|
||||
if selected_mcp_servers:
|
||||
mcp_docs_desc = f"MCP documentation for {', '.join(selected_mcp_servers)} (auto-selected)"
|
||||
component_options.append(f"mcp_docs - {mcp_docs_desc}")
|
||||
auto_selected_mcp_docs = True
|
||||
else:
|
||||
component_options.append("mcp_docs - MCP server documentation (none selected)")
|
||||
component_options.append(
|
||||
"mcp_docs - MCP server documentation (none selected)"
|
||||
)
|
||||
auto_selected_mcp_docs = False
|
||||
|
||||
|
||||
print(f"\n{Colors.CYAN}{Colors.BRIGHT}{'='*51}{Colors.RESET}")
|
||||
print(f"{Colors.CYAN}{Colors.BRIGHT}Stage 2: Framework Component Selection{Colors.RESET}")
|
||||
print(
|
||||
f"{Colors.CYAN}{Colors.BRIGHT}Stage 2: Framework Component Selection{Colors.RESET}"
|
||||
)
|
||||
print(f"{Colors.CYAN}{Colors.BRIGHT}{'='*51}{Colors.RESET}")
|
||||
print(f"\n{Colors.BLUE}Select SuperClaude framework components to install:{Colors.RESET}")
|
||||
|
||||
menu = Menu("Select components (Core is recommended):", component_options, multi_select=True)
|
||||
print(
|
||||
f"\n{Colors.BLUE}Select SuperClaude framework components to install:{Colors.RESET}"
|
||||
)
|
||||
|
||||
menu = Menu(
|
||||
"Select components (Core is recommended):",
|
||||
component_options,
|
||||
multi_select=True,
|
||||
)
|
||||
selections = menu.display()
|
||||
|
||||
|
||||
if not selections:
|
||||
# Default to core if nothing selected
|
||||
logger.info("No components selected, defaulting to core")
|
||||
@@ -303,11 +353,11 @@ def select_framework_components(registry: ComponentRegistry, config_manager: Con
|
||||
else:
|
||||
selected_components = []
|
||||
all_components = framework_components + ["mcp_docs"]
|
||||
|
||||
|
||||
for i in selections:
|
||||
if i < len(all_components):
|
||||
selected_components.append(all_components[i])
|
||||
|
||||
|
||||
# Auto-select MCP docs if not explicitly deselected and we have MCP servers
|
||||
if auto_selected_mcp_docs and "mcp_docs" not in selected_components:
|
||||
# Check if user explicitly deselected it
|
||||
@@ -316,82 +366,96 @@ def select_framework_components(registry: ComponentRegistry, config_manager: Con
|
||||
# User didn't select it, but we auto-select it
|
||||
selected_components.append("mcp_docs")
|
||||
logger.info("Auto-selected MCP documentation for configured servers")
|
||||
|
||||
|
||||
# Always include MCP component if servers were selected
|
||||
if selected_mcp_servers and "mcp" not in selected_components:
|
||||
selected_components.append("mcp")
|
||||
|
||||
|
||||
logger.info(f"Selected framework components: {', '.join(selected_components)}")
|
||||
return selected_components
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in framework component selection: {e}")
|
||||
return ["core"] # Fallback to core
|
||||
|
||||
|
||||
def interactive_component_selection(registry: ComponentRegistry, config_manager: ConfigService) -> Optional[List[str]]:
|
||||
def interactive_component_selection(
|
||||
registry: ComponentRegistry, config_manager: ConfigService
|
||||
) -> Optional[List[str]]:
|
||||
"""Two-stage interactive component selection"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
try:
|
||||
print(f"\n{Colors.CYAN}SuperClaude Interactive Installation{Colors.RESET}")
|
||||
print(f"{Colors.BLUE}Select components to install using the two-stage process:{Colors.RESET}")
|
||||
|
||||
print(
|
||||
f"{Colors.BLUE}Select components to install using the two-stage process:{Colors.RESET}"
|
||||
)
|
||||
|
||||
# Stage 1: MCP Server Selection
|
||||
selected_mcp_servers = select_mcp_servers(registry)
|
||||
|
||||
|
||||
# Stage 2: Framework Component Selection
|
||||
selected_components = select_framework_components(registry, config_manager, selected_mcp_servers)
|
||||
|
||||
selected_components = select_framework_components(
|
||||
registry, config_manager, selected_mcp_servers
|
||||
)
|
||||
|
||||
# Store selected MCP servers for components to use
|
||||
if not hasattr(config_manager, '_installation_context'):
|
||||
if not hasattr(config_manager, "_installation_context"):
|
||||
config_manager._installation_context = {}
|
||||
config_manager._installation_context["selected_mcp_servers"] = selected_mcp_servers
|
||||
|
||||
config_manager._installation_context["selected_mcp_servers"] = (
|
||||
selected_mcp_servers
|
||||
)
|
||||
|
||||
return selected_components
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in component selection: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def display_installation_plan(components: List[str], registry: ComponentRegistry, install_dir: Path) -> None:
|
||||
def display_installation_plan(
|
||||
components: List[str], registry: ComponentRegistry, install_dir: Path
|
||||
) -> None:
|
||||
"""Display installation plan"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
print(f"\n{Colors.CYAN}{Colors.BRIGHT}Installation Plan{Colors.RESET}")
|
||||
print("=" * 50)
|
||||
|
||||
|
||||
# Resolve dependencies
|
||||
try:
|
||||
ordered_components = registry.resolve_dependencies(components)
|
||||
|
||||
|
||||
print(f"{Colors.BLUE}Installation Directory:{Colors.RESET} {install_dir}")
|
||||
print(f"{Colors.BLUE}Components to install:{Colors.RESET}")
|
||||
|
||||
|
||||
total_size = 0
|
||||
for i, component_name in enumerate(ordered_components, 1):
|
||||
metadata = registry.get_component_metadata(component_name)
|
||||
if metadata:
|
||||
description = metadata.get("description", "No description")
|
||||
print(f" {i}. {component_name} - {description}")
|
||||
|
||||
|
||||
# Get size estimate if component supports it
|
||||
try:
|
||||
instance = registry.get_component_instance(component_name, install_dir)
|
||||
if instance and hasattr(instance, 'get_size_estimate'):
|
||||
instance = registry.get_component_instance(
|
||||
component_name, install_dir
|
||||
)
|
||||
if instance and hasattr(instance, "get_size_estimate"):
|
||||
size = instance.get_size_estimate()
|
||||
total_size += size
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
print(f" {i}. {component_name} - Unknown component")
|
||||
|
||||
|
||||
if total_size > 0:
|
||||
print(f"\n{Colors.BLUE}Estimated size:{Colors.RESET} {format_size(total_size)}")
|
||||
|
||||
print(
|
||||
f"\n{Colors.BLUE}Estimated size:{Colors.RESET} {format_size(total_size)}"
|
||||
)
|
||||
|
||||
print()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Could not resolve dependencies: {e}")
|
||||
raise
|
||||
@@ -400,101 +464,113 @@ def display_installation_plan(components: List[str], registry: ComponentRegistry
|
||||
def run_system_diagnostics(validator: Validator) -> None:
|
||||
"""Run comprehensive system diagnostics"""
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
print(f"\n{Colors.CYAN}{Colors.BRIGHT}SuperClaude System Diagnostics{Colors.RESET}")
|
||||
print("=" * 50)
|
||||
|
||||
|
||||
# Run diagnostics
|
||||
diagnostics = validator.diagnose_system()
|
||||
|
||||
|
||||
# Display platform info
|
||||
print(f"{Colors.BLUE}Platform:{Colors.RESET} {diagnostics['platform']}")
|
||||
|
||||
|
||||
# Display check results
|
||||
print(f"\n{Colors.BLUE}System Checks:{Colors.RESET}")
|
||||
all_passed = True
|
||||
|
||||
for check_name, check_info in diagnostics['checks'].items():
|
||||
status = check_info['status']
|
||||
message = check_info['message']
|
||||
|
||||
if status == 'pass':
|
||||
|
||||
for check_name, check_info in diagnostics["checks"].items():
|
||||
status = check_info["status"]
|
||||
message = check_info["message"]
|
||||
|
||||
if status == "pass":
|
||||
print(f" ✅ {check_name}: {message}")
|
||||
else:
|
||||
print(f" ❌ {check_name}: {message}")
|
||||
all_passed = False
|
||||
|
||||
|
||||
# Display issues and recommendations
|
||||
if diagnostics['issues']:
|
||||
if diagnostics["issues"]:
|
||||
print(f"\n{Colors.YELLOW}Issues Found:{Colors.RESET}")
|
||||
for issue in diagnostics['issues']:
|
||||
for issue in diagnostics["issues"]:
|
||||
print(f" ⚠️ {issue}")
|
||||
|
||||
|
||||
print(f"\n{Colors.CYAN}Recommendations:{Colors.RESET}")
|
||||
for recommendation in diagnostics['recommendations']:
|
||||
for recommendation in diagnostics["recommendations"]:
|
||||
print(recommendation)
|
||||
|
||||
|
||||
# Summary
|
||||
if all_passed:
|
||||
print(f"\n{Colors.GREEN}✅ All system checks passed! Your system is ready for SuperClaude.{Colors.RESET}")
|
||||
print(
|
||||
f"\n{Colors.GREEN}✅ All system checks passed! Your system is ready for superclaude.{Colors.RESET}"
|
||||
)
|
||||
else:
|
||||
print(f"\n{Colors.YELLOW}⚠️ Some issues found. Please address the recommendations above.{Colors.RESET}")
|
||||
|
||||
print(
|
||||
f"\n{Colors.YELLOW}⚠️ Some issues found. Please address the recommendations above.{Colors.RESET}"
|
||||
)
|
||||
|
||||
print(f"\n{Colors.BLUE}Next steps:{Colors.RESET}")
|
||||
if all_passed:
|
||||
print(" 1. Run 'SuperClaude install' to proceed with installation")
|
||||
print(" 2. Choose your preferred installation mode (quick, minimal, or custom)")
|
||||
print(" 1. Run 'superclaude install' to proceed with installation")
|
||||
print(
|
||||
" 2. Choose your preferred installation mode (quick, minimal, or custom)"
|
||||
)
|
||||
else:
|
||||
print(" 1. Install missing dependencies using the commands above")
|
||||
print(" 2. Restart your terminal after installing tools")
|
||||
print(" 3. Run 'SuperClaude install --diagnose' again to verify")
|
||||
print(" 3. Run 'superclaude install --diagnose' again to verify")
|
||||
|
||||
|
||||
def perform_installation(components: List[str], args: argparse.Namespace, config_manager: ConfigService = None) -> bool:
|
||||
def perform_installation(
|
||||
components: List[str],
|
||||
args: argparse.Namespace,
|
||||
config_manager: ConfigService = None,
|
||||
) -> bool:
|
||||
"""Perform the actual installation"""
|
||||
logger = get_logger()
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
try:
|
||||
# Create installer
|
||||
installer = Installer(args.install_dir, dry_run=args.dry_run)
|
||||
|
||||
|
||||
# Create component registry
|
||||
registry = ComponentRegistry(PROJECT_ROOT / "setup" / "components")
|
||||
registry.discover_components()
|
||||
|
||||
|
||||
# Create component instances
|
||||
component_instances = registry.create_component_instances(components, args.install_dir)
|
||||
|
||||
component_instances = registry.create_component_instances(
|
||||
components, args.install_dir
|
||||
)
|
||||
|
||||
if not component_instances:
|
||||
logger.error("No valid component instances created")
|
||||
return False
|
||||
|
||||
|
||||
# Register components with installer
|
||||
installer.register_components(list(component_instances.values()))
|
||||
|
||||
|
||||
# The 'components' list is already resolved, so we can use it directly.
|
||||
ordered_components = components
|
||||
|
||||
|
||||
# Setup progress tracking
|
||||
progress = ProgressBar(
|
||||
total=len(ordered_components),
|
||||
prefix="Installing: ",
|
||||
suffix=""
|
||||
total=len(ordered_components), prefix="Installing: ", suffix=""
|
||||
)
|
||||
|
||||
|
||||
# Install components
|
||||
logger.info(f"Installing {len(ordered_components)} components...")
|
||||
|
||||
|
||||
config = {
|
||||
"force": args.force,
|
||||
"backup": not args.no_backup,
|
||||
"dry_run": args.dry_run,
|
||||
"selected_mcp_servers": getattr(config_manager, '_installation_context', {}).get("selected_mcp_servers", [])
|
||||
"selected_mcp_servers": getattr(
|
||||
config_manager, "_installation_context", {}
|
||||
).get("selected_mcp_servers", []),
|
||||
}
|
||||
|
||||
|
||||
success = installer.install_components(ordered_components, config)
|
||||
|
||||
|
||||
# Update progress
|
||||
for i, component_name in enumerate(ordered_components):
|
||||
if component_name in installer.installed_components:
|
||||
@@ -502,32 +578,36 @@ def perform_installation(components: List[str], args: argparse.Namespace, config
|
||||
else:
|
||||
progress.update(i + 1, f"Failed {component_name}")
|
||||
time.sleep(0.1) # Brief pause for visual effect
|
||||
|
||||
|
||||
progress.finish("Installation complete")
|
||||
|
||||
|
||||
# Show results
|
||||
duration = time.time() - start_time
|
||||
|
||||
|
||||
if success:
|
||||
logger.success(f"Installation completed successfully in {duration:.1f} seconds")
|
||||
|
||||
logger.success(
|
||||
f"Installation completed successfully in {duration:.1f} seconds"
|
||||
)
|
||||
|
||||
# Show summary
|
||||
summary = installer.get_installation_summary()
|
||||
if summary['installed']:
|
||||
if summary["installed"]:
|
||||
logger.info(f"Installed components: {', '.join(summary['installed'])}")
|
||||
|
||||
if summary['backup_path']:
|
||||
|
||||
if summary["backup_path"]:
|
||||
logger.info(f"Backup created: {summary['backup_path']}")
|
||||
|
||||
|
||||
else:
|
||||
logger.error(f"Installation completed with errors in {duration:.1f} seconds")
|
||||
|
||||
logger.error(
|
||||
f"Installation completed with errors in {duration:.1f} seconds"
|
||||
)
|
||||
|
||||
summary = installer.get_installation_summary()
|
||||
if summary['failed']:
|
||||
if summary["failed"]:
|
||||
logger.error(f"Failed components: {', '.join(summary['failed'])}")
|
||||
|
||||
|
||||
return success
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Unexpected error during installation: {e}")
|
||||
return False
|
||||
@@ -547,18 +627,18 @@ def run(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
# Verify the resolved path is still within user home
|
||||
install_dir_resolved.relative_to(expected_home)
|
||||
|
||||
|
||||
# Additional check: if there's a symlink in the path, verify it doesn't escape user home
|
||||
if install_dir_original != install_dir_resolved:
|
||||
# Path contains symlinks - verify each component stays within user home
|
||||
current_path = expected_home
|
||||
parts = install_dir_original.parts
|
||||
home_parts = expected_home.parts
|
||||
|
||||
|
||||
# Skip home directory parts
|
||||
if len(parts) >= len(home_parts) and parts[:len(home_parts)] == home_parts:
|
||||
relative_parts = parts[len(home_parts):]
|
||||
|
||||
if len(parts) >= len(home_parts) and parts[: len(home_parts)] == home_parts:
|
||||
relative_parts = parts[len(home_parts) :]
|
||||
|
||||
for part in relative_parts:
|
||||
current_path = current_path / part
|
||||
if current_path.is_symlink():
|
||||
@@ -575,7 +655,7 @@ def run(args: argparse.Namespace) -> int:
|
||||
print(f"\n[x] Security validation failed: {e}")
|
||||
print(f" Please use a standard directory path within your user profile.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
try:
|
||||
# Validate global arguments
|
||||
success, errors = operation.validate_global_args(args)
|
||||
@@ -583,20 +663,21 @@ def run(args: argparse.Namespace) -> int:
|
||||
for error in errors:
|
||||
logger.error(error)
|
||||
return 1
|
||||
|
||||
|
||||
# Display header
|
||||
if not args.quiet:
|
||||
from setup.cli.base import __version__
|
||||
|
||||
display_header(
|
||||
f"SuperClaude Installation v{__version__}",
|
||||
"Installing SuperClaude framework components"
|
||||
"Installing SuperClaude framework components",
|
||||
)
|
||||
|
||||
|
||||
# Handle special modes
|
||||
if args.list_components:
|
||||
registry = ComponentRegistry(PROJECT_ROOT / "setup" / "components")
|
||||
registry.discover_components()
|
||||
|
||||
|
||||
components = registry.list_components()
|
||||
if components:
|
||||
print(f"\n{Colors.CYAN}Available Components:{Colors.RESET}")
|
||||
@@ -611,22 +692,22 @@ def run(args: argparse.Namespace) -> int:
|
||||
else:
|
||||
print("No components found")
|
||||
return 0
|
||||
|
||||
|
||||
# Handle diagnostic mode
|
||||
if args.diagnose:
|
||||
validator = Validator()
|
||||
run_system_diagnostics(validator)
|
||||
return 0
|
||||
|
||||
|
||||
# Create component registry and load configuration
|
||||
logger.info("Initializing installation system...")
|
||||
|
||||
|
||||
registry = ComponentRegistry(PROJECT_ROOT / "setup" / "components")
|
||||
registry.discover_components()
|
||||
|
||||
|
||||
config_manager = ConfigService(DATA_DIR)
|
||||
validator = Validator()
|
||||
|
||||
|
||||
# Validate configuration
|
||||
config_errors = config_manager.validate_config_files()
|
||||
if config_errors:
|
||||
@@ -634,9 +715,11 @@ def run(args: argparse.Namespace) -> int:
|
||||
for error in config_errors:
|
||||
logger.error(f" - {error}")
|
||||
return 1
|
||||
|
||||
|
||||
# Get components to install
|
||||
components_to_install = get_components_to_install(args, registry, config_manager)
|
||||
components_to_install = get_components_to_install(
|
||||
args, registry, config_manager
|
||||
)
|
||||
if not components_to_install:
|
||||
logger.error("No components selected for installation")
|
||||
return 1
|
||||
@@ -647,50 +730,58 @@ def run(args: argparse.Namespace) -> int:
|
||||
except ValueError as e:
|
||||
logger.error(f"Dependency resolution error: {e}")
|
||||
return 1
|
||||
|
||||
|
||||
# Validate system requirements for all components
|
||||
if not validate_system_requirements(validator, resolved_components):
|
||||
if not args.force:
|
||||
logger.error("System requirements not met. Use --force to override.")
|
||||
return 1
|
||||
else:
|
||||
logger.warning("System requirements not met, but continuing due to --force flag")
|
||||
|
||||
logger.warning(
|
||||
"System requirements not met, but continuing due to --force flag"
|
||||
)
|
||||
|
||||
# Check for existing installation
|
||||
if args.install_dir.exists() and not args.force:
|
||||
if not args.dry_run:
|
||||
logger.warning(f"Installation directory already exists: {args.install_dir}")
|
||||
if not args.yes and not confirm("Continue and update existing installation?", default=False):
|
||||
logger.warning(
|
||||
f"Installation directory already exists: {args.install_dir}"
|
||||
)
|
||||
if not args.yes and not confirm(
|
||||
"Continue and update existing installation?", default=False
|
||||
):
|
||||
logger.info("Installation cancelled by user")
|
||||
return 0
|
||||
|
||||
|
||||
# Display installation plan
|
||||
if not args.quiet:
|
||||
display_installation_plan(resolved_components, registry, args.install_dir)
|
||||
|
||||
|
||||
if not args.dry_run:
|
||||
if not args.yes and not confirm("Proceed with installation?", default=True):
|
||||
if not args.yes and not confirm(
|
||||
"Proceed with installation?", default=True
|
||||
):
|
||||
logger.info("Installation cancelled by user")
|
||||
return 0
|
||||
|
||||
|
||||
# Perform installation
|
||||
success = perform_installation(resolved_components, args, config_manager)
|
||||
|
||||
|
||||
if success:
|
||||
if not args.quiet:
|
||||
display_success("SuperClaude installation completed successfully!")
|
||||
|
||||
|
||||
if not args.dry_run:
|
||||
print(f"\n{Colors.CYAN}Next steps:{Colors.RESET}")
|
||||
print(f"1. Restart your Claude Code session")
|
||||
print(f"2. Framework files are now available in {args.install_dir}")
|
||||
print(f"3. Use SuperClaude commands and features in Claude Code")
|
||||
|
||||
|
||||
return 0
|
||||
else:
|
||||
display_error("Installation failed. Check logs for details.")
|
||||
return 1
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Colors.YELLOW}Installation cancelled by user{Colors.RESET}")
|
||||
return 130
|
||||
|
||||
Reference in New Issue
Block a user