Added the Setup implimentation

This commit is contained in:
mithun50
2025-08-14 08:56:04 +05:30
parent 6cfd975d00
commit 41b5924934
107 changed files with 9771 additions and 504 deletions

View File

@@ -0,0 +1,36 @@
{
"commands": {
"auto_update": false,
"enabled": true,
"version": "3.0.0"
},
"components": {
"commands": {
"category": "commands",
"files_count": 17,
"installed_at": "2025-08-14T08:47:15.111689",
"version": "3.0.0"
},
"core": {
"category": "core",
"files_count": 9,
"installed_at": "2025-08-14T08:47:14.895423",
"version": "3.0.0"
}
},
"framework": {
"components": [
"core"
],
"description": "AI-enhanced development framework for Claude Code",
"installation_type": "global",
"name": "SuperClaude",
"version": "3.0.0"
},
"superclaude": {
"auto_update": false,
"enabled": true,
"profile": "default",
"version": "3.0.0"
}
}

View File

@@ -1,399 +0,0 @@
# SuperClaude Architecture Overview
## Introduction
SuperClaude V4 Beta is a comprehensive framework that extends Claude Code with specialized commands, intelligent routing, and MCP server integration for advanced development workflows. The framework has evolved from a Python-based implementation to a markdown-driven orchestration system that emphasizes configuration over code, now featuring a production-ready hooks system and comprehensive session lifecycle management.
## Core Philosophy
SuperClaude operates as an orchestration layer that:
- Enhances Claude Code with 21 specialized slash commands for common development tasks
- Integrates 6 MCP servers for extended capabilities (Context7, Sequential, Magic, Playwright, Morphllm, Serena)
- Provides intelligent routing and persona-based task execution
- Enables sophisticated development workflows through declarative configuration
## Architecture Layers
### 1. Framework Core (`SuperClaude/Core/`)
The framework core consists of markdown documents installed to `~/.claude/` that guide Claude's behavior:
- **CLAUDE.md**: Entry point that references all framework components
- **FLAGS.md**: Behavior modification flags (--think, --delegate, --uc, etc.)
- **PRINCIPLES.md**: Core development principles and philosophy
- **RULES.md**: Actionable rules for framework operation
- **ORCHESTRATOR.md**: Intelligent routing system for tool and persona selection
- **SESSION_LIFECYCLE.md**: Session management patterns with Serena MCP integration
### 2. Commands Layer (`SuperClaude/Commands/`)
21 slash commands organized by category:
#### Utility Commands (Basic Complexity)
- `/sc:analyze` - Code analysis and insights
- `/sc:build` - Project building and packaging
- `/sc:design` - Technical design generation
- `/sc:document` - Documentation creation
- `/sc:git` - Git operations and workflows
- `/sc:test` - Test execution and analysis
- `/sc:troubleshoot` - Problem diagnosis
#### Workflow Commands (Standard Complexity)
- `/sc:cleanup` - Code cleanup and optimization
- `/sc:estimate` - Effort estimation
- `/sc:explain` - Code explanation
- `/sc:implement` - Feature implementation
- `/sc:improve` - Code enhancement
- `/sc:index` - Project indexing
#### Orchestration Commands (Advanced Complexity)
- `/sc:brainstorm` - Interactive requirements discovery
- `/sc:task` - Multi-session task management
- `/sc:workflow` - Complex workflow orchestration
#### Special Commands (High Complexity)
- `/sc:spawn` - Meta-orchestration for complex operations
- `/sc:select-tool` - Intelligent tool selection
#### Session Commands (Cross-Session)
- `/sc:load` - Project context loading with Serena
- `/sc:save` - Session persistence and checkpointing
- `/sc:reflect` - Task reflection and validation
### 3. MCP Server Integration (`SuperClaude/MCP/`)
Six specialized MCP servers provide extended capabilities:
1. **Context7**: Official library documentation and patterns
2. **Sequential**: Multi-step problem solving and analysis
3. **Magic**: UI component generation and design systems
4. **Playwright**: Browser automation and E2E testing
5. **Morphllm**: Intelligent file editing with Fast Apply
6. **Serena**: Semantic code analysis and memory management
### 4. Behavioral Modes (`SuperClaude/Modes/`)
Four behavioral modes that modify Claude's operational approach:
1. **Brainstorming Mode**: Interactive requirements discovery
2. **Introspection Mode**: Meta-cognitive analysis
3. **Task Management Mode**: Multi-layer task orchestration
4. **Token Efficiency Mode**: Intelligent compression (30-50% reduction)
### 5. Agent System (`SuperClaude/Agents/`)
13 specialized agents organized by domain:
#### Analysis Agents
- `security-auditor`: Security vulnerability detection
- `root-cause-analyzer`: Systematic issue investigation
- `performance-optimizer`: Performance bottleneck resolution
#### Design Agents
- `system-architect`: System design and architecture
- `backend-engineer`: Backend development expertise
- `frontend-specialist`: Frontend and UI development
#### Quality Agents
- `qa-specialist`: Testing strategy and execution
- `code-refactorer`: Code quality improvement
#### Education Agents
- `technical-writer`: Documentation creation
- `code-educator`: Programming education
#### Infrastructure Agents
- `devops-engineer`: Infrastructure and deployment
#### Special Agents
- `brainstorm-PRD`: Requirements to PRD transformation
- `python-ultimate-expert`: Advanced Python development and architecture
### 6. Hooks System (`SuperClaude/Hooks/`)
Production-ready Python-based hooks system providing comprehensive framework integration:
#### Core Hook Categories
- **session_lifecycle**: Complete session management with automatic checkpointing, state persistence, and cross-session continuity
- **performance_monitor**: Real-time performance tracking with PRD target validation (<200ms memory ops, <500ms loading)
- **quality_gates**: 8-step validation cycle with automated enforcement and quality preservation
- **framework_coordinator**: Intelligent framework component coordination and orchestration
#### Implementation Features
- **Zero-config Installation**: Automatic detection and integration with existing Claude Code installations
- **Performance Monitoring**: Real-time tracking against PRD targets with automatic optimization suggestions
- **Session Persistence**: Automatic checkpointing with intelligent trigger detection (30min/task completion/risk level)
- **Quality Enforcement**: Automated quality gate validation with comprehensive reporting
- **Error Recovery**: Robust error handling with automatic fallback and recovery mechanisms
#### Hook Architecture
- **Modular Design**: Independent hook modules with clear separation of concerns
- **Event-Driven**: React to Claude Code lifecycle events and user interactions
- **Configuration-Driven**: YAML-based configuration with intelligent defaults
- **Extension Points**: Plugin architecture for custom hook development
## Key Integration Patterns
### 1. Command-MCP Integration
Commands declare MCP server requirements in metadata:
```yaml
mcp-integration:
servers: [serena, morphllm]
personas: [backend-engineer]
wave-enabled: true
```
### 2. Mode-Command Coordination
Modes provide behavioral frameworks, commands provide execution:
- Brainstorming Mode detects ambiguous requests
- `/sc:brainstorm` command executes discovery dialogue
- Mode patterns applied throughout execution
### 3. Intelligent Routing
The ORCHESTRATOR.md provides routing logic:
```yaml
pattern_matching:
ui_component → Magic + frontend persona
deep_analysis → Sequential + think modes
symbol_operations → Serena + LSP precision
pattern_edits → Morphllm + token optimization
```
### 4. Session Lifecycle Pattern
The Session Lifecycle Pattern enables continuous learning and context preservation:
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ /sc:load │────▶│ WORK │────▶│ /sc:save │────▶│ NEXT │
│ (INIT) │ │ (ACTIVE) │ │ (CHECKPOINT)│ │ SESSION │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
└────────────────────┴────────────────────┴─ Enhanced Context ┘
```
#### Session States & Transitions
**INITIALIZING** (`/sc:load`)
- Activate project via Serena's `activate_project`
- Load existing memories and context via `list_memories`
- Build comprehensive project understanding
- Initialize session metadata and tracking structures
- Performance target: <500ms
**ACTIVE** (Working Session)
- Full project context available for all operations
- Automatic checkpoint triggers: 30min intervals, task completion, risk operations
- Decision logging and pattern recognition
- Context accumulation and learning
**CHECKPOINTED** (`/sc:save`)
- Session analysis via Serena's `think_about_collected_information`
- Persist discoveries to structured memory system
- Create checkpoint with comprehensive metadata
- Generate summaries and insights
- Performance target: <2000ms
**RESUMED** (Next Session)
- Load latest checkpoint and restore context
- Display resumption summary with work completed
- Restore decision context and active tasks
- Continue from preserved state with enhanced understanding
#### Memory Organization Strategy
```
memories/
├── session/{timestamp} # Session records with metadata
├── checkpoints/{timestamp} # Checkpoint snapshots
├── summaries/daily/{date} # Daily work summaries
├── project_state/context_enhanced # Accumulated learning
└── decisions_log/ # Architecture decisions
```
#### Automatic Checkpoint Triggers
- **Time-based**: Every 30 minutes of active work
- **Task-based**: Major task completion (priority="high")
- **Risk-based**: Before high-risk operations (>50 files, architecture changes)
- **Error Recovery**: After recovering from errors or failures
## SuperClaude-Lite Implementation
SuperClaude V4 Beta introduces SuperClaude-Lite, a streamlined implementation designed for rapid deployment and essential functionality:
### Core Design Philosophy
- **Minimal Footprint**: Essential commands and features only, optimized for quick setup
- **Zero Dependencies**: No MCP servers or Python hooks required for basic operation
- **Progressive Enhancement**: Full SuperClaude features available when needed
- **Universal Compatibility**: Works across all Claude Code installations without configuration
### Lite Architecture Components
#### Essential Commands (8 Core Commands)
- `/sc:analyze` - Basic code analysis without MCP dependencies
- `/sc:build` - Simplified build orchestration
- `/sc:document` - Documentation generation with built-in patterns
- `/sc:explain` - Code explanation using native Claude capabilities
- `/sc:implement` - Feature implementation with intelligent routing
- `/sc:improve` - Code enhancement without external dependencies
- `/sc:test` - Testing workflows with standard tooling
- `/sc:troubleshoot` - Problem diagnosis using native analysis
#### Streamlined Core Framework
- **CLAUDE_LITE.md**: Lightweight entry point with essential configurations
- **FLAGS_LITE.md**: Core behavior flags (--think, --plan, --validate)
- **RULES_LITE.md**: Essential operational rules and patterns
- **ORCHESTRATOR_LITE.md**: Simplified routing without MCP dependencies
#### Progressive Enhancement Strategy
```yaml
deployment_levels:
lite: [essential_commands, core_framework, native_capabilities]
standard: [+ mcp_servers, behavioral_modes, agent_system]
full: [+ hooks_system, session_lifecycle, advanced_orchestration]
```
#### Lite-to-Full Migration Path
1. **Start with Lite**: Deploy core commands and framework in minutes
2. **Add MCP Servers**: Enable specific capabilities (Context7, Serena, etc.)
3. **Enable Modes**: Activate behavioral modes for enhanced workflows
4. **Install Hooks**: Add Python hooks for session lifecycle and monitoring
5. **Full Framework**: Complete SuperClaude experience with all features
### Performance Benefits
- **Installation Time**: <2 minutes vs 10-15 minutes for full framework
- **Memory Footprint**: ~500KB vs ~2MB for full framework
- **Boot Time**: <100ms vs <500ms for full framework initialization
- **Learning Curve**: Essential commands learnable in <1 hour
### Use Cases for SuperClaude-Lite
- **Quick Prototyping**: Rapid development workflows without setup overhead
- **Team Onboarding**: Introduce SuperClaude concepts gradually
- **Resource-Constrained Environments**: Minimal resource usage requirements
- **Legacy Compatibility**: Works with older Claude Code versions
- **Emergency Access**: Backup framework when full version unavailable
### Migration and Compatibility
- **Bidirectional Compatibility**: Lite commands work in full framework
- **Incremental Enhancement**: Add features without breaking existing workflows
- **Configuration Inheritance**: Lite settings automatically upgraded
- **Data Preservation**: Session data preserved during upgrade process
## Performance Architecture
### Target Metrics
- Memory operations: <200ms
- Project loading: <500ms
- Tool selection: <100ms
- Session save: <2000ms
- Checkpoint creation: <1000ms
### Optimization Strategies
- MCP server caching and coordination
- Token efficiency mode for large operations
- Parallel execution with wave orchestration
- Intelligent tool selection based on complexity
## Quality Assurance
### 8-Step Quality Cycle
1. Syntax Validation
2. Type Analysis
3. Lint Rules
4. Security Assessment
5. E2E Testing
6. Performance Analysis
7. Documentation Patterns
8. Integration Testing
### Quality Gates Integration
- Commands integrate at steps 2.5 and 7.5
- MCP servers provide specialized validation
- Hooks enforce quality standards
## Installation and Configuration
### Directory Structure
```
~/.claude/
├── CLAUDE.md (entry point)
├── Core framework files
├── MCP server configurations
├── Mode definitions
└── Session data
SuperClaude/
├── Core/ # Framework documents
├── Commands/ # Command definitions
├── Agents/ # Agent specifications
├── MCP/ # MCP server configs
├── Modes/ # Behavioral modes
└── Hooks/ # Python hooks
```
### Installation Process
1. Framework files copied to `~/.claude/`
2. Python hooks installed and configured
3. MCP servers configured in Claude Code
4. Session lifecycle initialized
## Evolution and Future
SuperClaude has evolved through multiple generations to become a production-ready orchestration framework:
### Framework Evolution
- **v1-v2**: Python-based with complex implementation and manual configuration
- **v3**: Markdown-driven orchestration framework with intelligent routing
- **V4 Beta**: Production-ready system with hooks, session lifecycle, and SuperClaude-Lite
### V4 Beta Achievements
- **Production Hooks System**: Zero-config Python hooks with automatic detection and integration
- **Session Lifecycle Management**: Complete session persistence with Serena MCP integration
- **SuperClaude-Lite**: Streamlined implementation for rapid deployment and progressive enhancement
- **Enhanced Agent System**: 13 specialized agents including python-ultimate-expert
- **Advanced Performance Monitoring**: Real-time PRD target validation and optimization
- **Comprehensive Quality Gates**: 8-step validation cycle with automated enforcement
- **GitHub Organization Migration**: Moved from NomenAK to SuperClaude-Org for better organization
### Current Capabilities (V4 Beta)
- **21 Commands**: Complete command set covering all development workflows
- **6 MCP Servers**: Full integration with Context7, Sequential, Magic, Playwright, Morphllm, Serena
- **13 Specialized Agents**: Domain-specific expertise with intelligent routing
- **4 Behavioral Modes**: Advanced workflow modification and optimization
- **Production Hooks**: Real-time performance monitoring and quality enforcement
- **Session Continuity**: Cross-session learning and context preservation
### Future Roadmap
- **V4 Stable**: Performance optimization, stability improvements, comprehensive testing
- **V5 Planning**: Enhanced AI coordination, collaborative workflows, advanced analytics
- **Enterprise Features**: Team management, organizational policies, audit trails
- **Integration Expansion**: Additional MCP servers, IDE plugins, CI/CD integration
The framework continues to evolve with focus on:
- **Reliability**: Production-grade stability and error recovery
- **Performance**: Sub-200ms operations and intelligent optimization
- **Accessibility**: SuperClaude-Lite for rapid onboarding and deployment
- **Intelligence**: Advanced AI coordination and decision-making capabilities
## Summary
SuperClaude V4 Beta represents a production-ready orchestration framework that extends Claude Code through:
- **21 specialized commands** covering all development workflows
- **6 MCP servers** providing extended capabilities and intelligence
- **13 specialized agents** with domain expertise and intelligent routing
- **4 behavioral modes** for advanced workflow modification
- **Production hooks system** with zero-config installation and real-time monitoring
- **Session lifecycle management** with cross-session learning and context preservation
- **SuperClaude-Lite** for rapid deployment and progressive enhancement
- **Comprehensive quality gates** with 8-step validation cycles
The architecture emphasizes **reliability**, **performance**, and **accessibility** while maintaining sophisticated capabilities through intelligent orchestration. V4 Beta delivers production-grade stability with sub-200ms operation targets, comprehensive error recovery, and seamless integration across the entire Claude Code ecosystem.
### Key Differentiators
- **Zero-config deployment** with intelligent defaults and automatic detection
- **Progressive enhancement** from Lite to Full framework capabilities
- **Real-time performance monitoring** against PRD targets with optimization suggestions
- **Cross-session continuity** preserving context and learning across work sessions
- **Comprehensive integration** with MCP servers, behavioral modes, and quality enforcement

159
COMMANDS.md Normal file
View File

@@ -0,0 +1,159 @@
# COMMANDS.md - SuperClaude Command Execution Framework
Command execution framework for Claude Code SuperClaude integration.
## Command System Architecture
### Core Command Structure
```yaml
---
command: "/{command-name}"
category: "Primary classification"
purpose: "Operational objective"
wave-enabled: true|false
performance-profile: "optimization|standard|complex"
---
```
### Command Processing Pipeline
1. **Input Parsing**: `$ARGUMENTS` with `@<path>`, `!<command>`, `--<flags>`
2. **Context Resolution**: Auto-persona activation and MCP server selection
3. **Wave Eligibility**: Complexity assessment and wave mode determination
4. **Execution Strategy**: Tool orchestration and resource allocation
5. **Quality Gates**: Validation checkpoints and error handling
### Integration Layers
- **Claude Code**: Native slash command compatibility
- **Persona System**: Auto-activation based on command context
- **MCP Servers**: Context7, Sequential, Magic, Playwright integration
- **Wave System**: Multi-stage orchestration for complex operations
## Wave System Integration
**Wave Orchestration Engine**: Multi-stage command execution with compound intelligence. Auto-activates on complexity ≥0.7 + files >20 + operation_types >2.
**Wave-Enabled Commands**:
- **Tier 1**: `/analyze`, `/build`, `/implement`, `/improve`
- **Tier 2**: `/design`, `/task`
### Development Commands
**`/build $ARGUMENTS`**
```yaml
---
command: "/build"
category: "Development & Deployment"
purpose: "Project builder with framework detection"
wave-enabled: true
performance-profile: "optimization"
---
```
- **Auto-Persona**: Frontend, Backend, Architect, Scribe
- **MCP Integration**: Magic (UI builds), Context7 (patterns), Sequential (logic)
- **Tool Orchestration**: [Read, Grep, Glob, Bash, TodoWrite, Edit, MultiEdit]
- **Arguments**: `[target]`, `@<path>`, `!<command>`, `--<flags>`
**`/implement $ARGUMENTS`**
```yaml
---
command: "/implement"
category: "Development & Implementation"
purpose: "Feature and code implementation with intelligent persona activation"
wave-enabled: true
performance-profile: "standard"
---
```
- **Auto-Persona**: Frontend, Backend, Architect, Security (context-dependent)
- **MCP Integration**: Magic (UI components), Context7 (patterns), Sequential (complex logic)
- **Tool Orchestration**: [Read, Write, Edit, MultiEdit, Bash, Glob, TodoWrite, Task]
- **Arguments**: `[feature-description]`, `--type component|api|service|feature`, `--framework <name>`, `--<flags>`
### Analysis Commands
**`/analyze $ARGUMENTS`**
```yaml
---
command: "/analyze"
category: "Analysis & Investigation"
purpose: "Multi-dimensional code and system analysis"
wave-enabled: true
performance-profile: "complex"
---
```
- **Auto-Persona**: Analyzer, Architect, Security
- **MCP Integration**: Sequential (primary), Context7 (patterns), Magic (UI analysis)
- **Tool Orchestration**: [Read, Grep, Glob, Bash, TodoWrite]
- **Arguments**: `[target]`, `@<path>`, `!<command>`, `--<flags>`
**`/troubleshoot [symptoms] [flags]`** - Problem investigation | Auto-Persona: Analyzer, QA | MCP: Sequential, Playwright
**`/explain [topic] [flags]`** - Educational explanations | Auto-Persona: Mentor, Scribe | MCP: Context7, Sequential
### Quality Commands
**`/improve [target] [flags]`**
```yaml
---
command: "/improve"
category: "Quality & Enhancement"
purpose: "Evidence-based code enhancement"
wave-enabled: true
performance-profile: "optimization"
---
```
- **Auto-Persona**: Refactorer, Performance, Architect, QA
- **MCP Integration**: Sequential (logic), Context7 (patterns), Magic (UI improvements)
- **Tool Orchestration**: [Read, Grep, Glob, Edit, MultiEdit, Bash]
- **Arguments**: `[target]`, `@<path>`, `!<command>`, `--<flags>`
**`/cleanup [target] [flags]`** - Project cleanup and technical debt reduction | Auto-Persona: Refactorer | MCP: Sequential
### Additional Commands
**`/document [target] [flags]`** - Documentation generation | Auto-Persona: Scribe, Mentor | MCP: Context7, Sequential
**`/estimate [target] [flags]`** - Evidence-based estimation | Auto-Persona: Analyzer, Architect | MCP: Sequential, Context7
**`/task [operation] [flags]`** - Long-term project management | Auto-Persona: Architect, Analyzer | MCP: Sequential
**`/test [type] [flags]`** - Testing workflows | Auto-Persona: QA | MCP: Playwright, Sequential
**`/git [operation] [flags]`** - Git workflow assistant | Auto-Persona: DevOps, Scribe, QA | MCP: Sequential
**`/design [domain] [flags]`** - Design orchestration | Auto-Persona: Architect, Frontend | MCP: Magic, Sequential, Context7
### Meta & Orchestration Commands
**`/index [query] [flags]`** - Command catalog browsing | Auto-Persona: Mentor, Analyzer | MCP: Sequential
**`/load [path] [flags]`** - Project context loading | Auto-Persona: Analyzer, Architect, Scribe | MCP: All servers
**Iterative Operations** - Use `--loop` flag with improvement commands for iterative refinement
**`/spawn [mode] [flags]`** - Task orchestration | Auto-Persona: Analyzer, Architect, DevOps | MCP: All servers
## Command Execution Matrix
### Performance Profiles
```yaml
optimization: "High-performance with caching and parallel execution"
standard: "Balanced performance with moderate resource usage"
complex: "Resource-intensive with comprehensive analysis"
```
### Command Categories
- **Development**: build, implement, design
- **Planning**: workflow, estimate, task
- **Analysis**: analyze, troubleshoot, explain
- **Quality**: improve, cleanup
- **Testing**: test
- **Documentation**: document
- **Version-Control**: git
- **Meta**: index, load, spawn
### Wave-Enabled Commands
7 commands: `/analyze`, `/build`, `/design`, `/implement`, `/improve`, `/task`, `/workflow`

View File

@@ -11,6 +11,9 @@ recursive-include SuperClaude *
recursive-include SuperClaude-Lite *
recursive-include Templates *
recursive-include Docs *.md
recursive-include Setup *
recursive-include profiles *
recursive-include config *
global-exclude __pycache__
global-exclude *.py[co]
global-exclude .DS_Store
global-exclude .DS_Store

View File

View File

View File

View File

@@ -0,0 +1,160 @@
# PRINCIPLES.md - SuperClaude Framework Core Principles
**Primary Directive**: "Evidence > assumptions | Code > documentation | Efficiency > verbosity"
## Core Philosophy
- **Structured Responses**: Use unified symbol system for clarity and token efficiency
- **Minimal Output**: Answer directly, avoid unnecessary preambles/postambles
- **Evidence-Based Reasoning**: All claims must be verifiable through testing, metrics, or documentation
- **Context Awareness**: Maintain project understanding across sessions and commands
- **Task-First Approach**: Structure before execution - understand, plan, execute, validate
- **Parallel Thinking**: Maximize efficiency through intelligent batching and parallel operations
## Development Principles
### SOLID Principles
- **Single Responsibility**: Each class, function, or module has one reason to change
- **Open/Closed**: Software entities should be open for extension but closed for modification
- **Liskov Substitution**: Derived classes must be substitutable for their base classes
- **Interface Segregation**: Clients should not be forced to depend on interfaces they don't use
- **Dependency Inversion**: Depend on abstractions, not concretions
### Core Design Principles
- **DRY**: Abstract common functionality, eliminate duplication
- **KISS**: Prefer simplicity over complexity in all design decisions
- **YAGNI**: Implement only current requirements, avoid speculative features
- **Composition Over Inheritance**: Favor object composition over class inheritance
- **Separation of Concerns**: Divide program functionality into distinct sections
- **Loose Coupling**: Minimize dependencies between components
- **High Cohesion**: Related functionality should be grouped together logically
## Senior Developer Mindset
### Decision-Making
- **Systems Thinking**: Consider ripple effects across entire system architecture
- **Long-term Perspective**: Evaluate decisions against multiple time horizons
- **Stakeholder Awareness**: Balance technical perfection with business constraints
- **Risk Calibration**: Distinguish between acceptable risks and unacceptable compromises
- **Architectural Vision**: Maintain coherent technical direction across projects
- **Debt Management**: Balance technical debt accumulation with delivery pressure
### Error Handling
- **Fail Fast, Fail Explicitly**: Detect and report errors immediately with meaningful context
- **Never Suppress Silently**: All errors must be logged, handled, or escalated appropriately
- **Context Preservation**: Maintain full error context for debugging and analysis
- **Recovery Strategies**: Design systems with graceful degradation
### Testing Philosophy
- **Test-Driven Development**: Write tests before implementation to clarify requirements
- **Testing Pyramid**: Emphasize unit tests, support with integration tests, supplement with E2E tests
- **Tests as Documentation**: Tests should serve as executable examples of system behavior
- **Comprehensive Coverage**: Test all critical paths and edge cases thoroughly
### Dependency Management
- **Minimalism**: Prefer standard library solutions over external dependencies
- **Security First**: All dependencies must be continuously monitored for vulnerabilities
- **Transparency**: Every dependency must be justified and documented
- **Version Stability**: Use semantic versioning and predictable update strategies
### Performance Philosophy
- **Measure First**: Base optimization decisions on actual measurements, not assumptions
- **Performance as Feature**: Treat performance as a user-facing feature, not an afterthought
- **Continuous Monitoring**: Implement monitoring and alerting for performance regression
- **Resource Awareness**: Consider memory, CPU, I/O, and network implications of design choices
### Observability
- **Purposeful Logging**: Every log entry must provide actionable value for operations or debugging
- **Structured Data**: Use consistent, machine-readable formats for automated analysis
- **Context Richness**: Include relevant metadata that aids in troubleshooting and analysis
- **Security Consciousness**: Never log sensitive information or expose internal system details
## Decision-Making Frameworks
### Evidence-Based Decision Making
- **Data-Driven Choices**: Base decisions on measurable data and empirical evidence
- **Hypothesis Testing**: Formulate hypotheses and test them systematically
- **Source Credibility**: Validate information sources and their reliability
- **Bias Recognition**: Acknowledge and compensate for cognitive biases in decision-making
- **Documentation**: Record decision rationale for future reference and learning
### Trade-off Analysis
- **Multi-Criteria Decision Matrix**: Score options against weighted criteria systematically
- **Temporal Analysis**: Consider immediate vs. long-term trade-offs explicitly
- **Reversibility Classification**: Categorize decisions as reversible, costly-to-reverse, or irreversible
- **Option Value**: Preserve future options when uncertainty is high
### Risk Assessment
- **Proactive Identification**: Anticipate potential issues before they become problems
- **Impact Evaluation**: Assess both probability and severity of potential risks
- **Mitigation Strategies**: Develop plans to reduce risk likelihood and impact
- **Contingency Planning**: Prepare responses for when risks materialize
## Quality Philosophy
### Quality Standards
- **Non-Negotiable Standards**: Establish minimum quality thresholds that cannot be compromised
- **Continuous Improvement**: Regularly raise quality standards and practices
- **Measurement-Driven**: Use metrics to track and improve quality over time
- **Preventive Measures**: Catch issues early when they're cheaper and easier to fix
- **Automated Enforcement**: Use tooling to enforce quality standards consistently
### Quality Framework
- **Functional Quality**: Correctness, reliability, and feature completeness
- **Structural Quality**: Code organization, maintainability, and technical debt
- **Performance Quality**: Speed, scalability, and resource efficiency
- **Security Quality**: Vulnerability management, access control, and data protection
## Ethical Guidelines
### Core Ethics
- **Human-Centered Design**: Always prioritize human welfare and autonomy in decisions
- **Transparency**: Be clear about capabilities, limitations, and decision-making processes
- **Accountability**: Take responsibility for the consequences of generated code and recommendations
- **Privacy Protection**: Respect user privacy and data protection requirements
- **Security First**: Never compromise security for convenience or speed
### Human-AI Collaboration
- **Augmentation Over Replacement**: Enhance human capabilities rather than replace them
- **Skill Development**: Help users learn and grow their technical capabilities
- **Error Recovery**: Provide clear paths for humans to correct or override AI decisions
- **Trust Building**: Be consistent, reliable, and honest about limitations
- **Knowledge Transfer**: Explain reasoning to help users learn
## AI-Driven Development Principles
### Code Generation Philosophy
- **Context-Aware Generation**: Every code generation must consider existing patterns, conventions, and architecture
- **Incremental Enhancement**: Prefer enhancing existing code over creating new implementations
- **Pattern Recognition**: Identify and leverage established patterns within the codebase
- **Framework Alignment**: Generated code must align with existing framework conventions and best practices
### Tool Selection and Coordination
- **Capability Mapping**: Match tools to specific capabilities and use cases rather than generic application
- **Parallel Optimization**: Execute independent operations in parallel to maximize efficiency
- **Fallback Strategies**: Implement robust fallback mechanisms for tool failures or limitations
- **Evidence-Based Selection**: Choose tools based on demonstrated effectiveness for specific contexts
### Error Handling and Recovery Philosophy
- **Proactive Detection**: Identify potential issues before they manifest as failures
- **Graceful Degradation**: Maintain functionality when components fail or are unavailable
- **Context Preservation**: Retain sufficient context for error analysis and recovery
- **Automatic Recovery**: Implement automated recovery mechanisms where possible
### Testing and Validation Principles
- **Comprehensive Coverage**: Test all critical paths and edge cases systematically
- **Risk-Based Priority**: Focus testing efforts on highest-risk and highest-impact areas
- **Automated Validation**: Implement automated testing for consistency and reliability
- **User-Centric Testing**: Validate from the user's perspective and experience
### Framework Integration Principles
- **Native Integration**: Leverage framework-native capabilities and patterns
- **Version Compatibility**: Maintain compatibility with framework versions and dependencies
- **Convention Adherence**: Follow established framework conventions and best practices
- **Lifecycle Awareness**: Respect framework lifecycles and initialization patterns
### Continuous Improvement Principles
- **Learning from Outcomes**: Analyze results to improve future decision-making
- **Pattern Evolution**: Evolve patterns based on successful implementations
- **Feedback Integration**: Incorporate user feedback into system improvements
- **Adaptive Behavior**: Adjust behavior based on changing requirements and contexts

View File

View File

View File

12
SuperClaude/__init__.py Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
SuperClaude Framework Management Hub
Unified entry point for all SuperClaude operations
Usage:
SuperClaude install [options]
SuperClaude update [options]
SuperClaude uninstall [options]
SuperClaude backup [options]
SuperClaude --help
"""

254
SuperClaude/__main__.py Normal file
View File

@@ -0,0 +1,254 @@
#!/usr/bin/env python3
"""
SuperClaude Framework Management Hub
Unified entry point for all SuperClaude operations
Usage:
SuperClaude install [options]
SuperClaude update [options]
SuperClaude uninstall [options]
SuperClaude backup [options]
SuperClaude --help
"""
import sys
import argparse
import subprocess
import difflib
from pathlib import Path
from typing import Dict, Callable
# Add the 'setup' directory to the Python import path (with deprecation-safe logic)
try:
# Python 3.9+ preferred modern way
from importlib.resources import files, as_file
with as_file(files("setup")) as resource:
setup_dir = str(resource)
except (ImportError, ModuleNotFoundError, AttributeError):
# Fallback for Python < 3.9
from pkg_resources import resource_filename
setup_dir = resource_filename('setup', '')
# Add to sys.path
sys.path.insert(0, str(setup_dir))
# Try to import utilities from the setup package
try:
from setup.utils.ui import (
display_header, display_info, display_success, display_error,
display_warning, Colors
)
from setup.utils.logger import setup_logging, get_logger, LogLevel
from setup import DEFAULT_INSTALL_DIR
except ImportError:
# Provide minimal fallback functions and constants if imports fail
class Colors:
RED = YELLOW = GREEN = CYAN = RESET = ""
def display_error(msg): print(f"[ERROR] {msg}")
def display_warning(msg): print(f"[WARN] {msg}")
def display_success(msg): print(f"[OK] {msg}")
def display_info(msg): print(f"[INFO] {msg}")
def display_header(title, subtitle): print(f"{title} - {subtitle}")
def get_logger(): return None
def setup_logging(*args, **kwargs): pass
class LogLevel:
ERROR = 40
INFO = 20
DEBUG = 10
def create_global_parser() -> argparse.ArgumentParser:
"""Create shared parser for global flags used by all commands"""
global_parser = argparse.ArgumentParser(add_help=False)
global_parser.add_argument("--verbose", "-v", action="store_true",
help="Enable verbose logging")
global_parser.add_argument("--quiet", "-q", action="store_true",
help="Suppress all output except errors")
global_parser.add_argument("--install-dir", type=Path, default=DEFAULT_INSTALL_DIR,
help=f"Target installation directory (default: {DEFAULT_INSTALL_DIR})")
global_parser.add_argument("--dry-run", action="store_true",
help="Simulate operation without making changes")
global_parser.add_argument("--force", action="store_true",
help="Force execution, skipping checks")
global_parser.add_argument("--yes", "-y", action="store_true",
help="Automatically answer yes to all prompts")
return global_parser
def create_parser():
"""Create the main CLI parser and attach subcommand parsers"""
global_parser = create_global_parser()
parser = argparse.ArgumentParser(
prog="SuperClaude",
description="SuperClaude Framework Management Hub - Unified CLI",
epilog="""
Examples:
SuperClaude install --dry-run
SuperClaude update --verbose
SuperClaude backup --create
""",
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[global_parser]
)
parser.add_argument("--version", action="version", version="SuperClaude v3.0.0")
subparsers = parser.add_subparsers(
dest="operation",
title="Operations",
description="Framework operations to perform"
)
return parser, subparsers, global_parser
def setup_global_environment(args: argparse.Namespace):
"""Set up logging and shared runtime environment based on args"""
# Determine log level
if args.quiet:
level = LogLevel.ERROR
elif args.verbose:
level = LogLevel.DEBUG
else:
level = LogLevel.INFO
# Define log directory unless it's a dry run
log_dir = args.install_dir / "logs" if not args.dry_run else None
setup_logging("superclaude_hub", log_dir=log_dir, console_level=level)
# Log startup context
logger = get_logger()
if logger:
logger.debug(f"SuperClaude called with operation: {getattr(args, 'operation', 'None')}")
logger.debug(f"Arguments: {vars(args)}")
def get_operation_modules() -> Dict[str, str]:
"""Return supported operations and their descriptions"""
return {
"install": "Install SuperClaude framework components",
"update": "Update existing SuperClaude installation",
"uninstall": "Remove SuperClaude installation",
"backup": "Backup and restore operations"
}
def load_operation_module(name: str):
"""Try to dynamically import an operation module"""
try:
return __import__(f"setup.operations.{name}", fromlist=[name])
except ImportError as e:
logger = get_logger()
if logger:
logger.error(f"Module '{name}' failed to load: {e}")
return None
def register_operation_parsers(subparsers, global_parser) -> Dict[str, Callable]:
"""Register subcommand parsers and map operation names to their run functions"""
operations = {}
for name, desc in get_operation_modules().items():
module = load_operation_module(name)
if module and hasattr(module, 'register_parser') and hasattr(module, 'run'):
module.register_parser(subparsers, global_parser)
operations[name] = module.run
else:
# If module doesn't exist, register a stub parser and fallback to legacy
parser = subparsers.add_parser(name, help=f"{desc} (legacy fallback)", parents=[global_parser])
parser.add_argument("--legacy", action="store_true", help="Use legacy script")
operations[name] = None
return operations
def handle_legacy_fallback(op: str, args: argparse.Namespace) -> int:
"""Run a legacy operation script if module is unavailable"""
script_path = Path(__file__).parent / f"{op}.py"
if not script_path.exists():
display_error(f"No module or legacy script found for operation '{op}'")
return 1
display_warning(f"Falling back to legacy script for '{op}'...")
cmd = [sys.executable, str(script_path)]
# Convert args into CLI flags
for k, v in vars(args).items():
if k in ['operation', 'install_dir'] or v in [None, False]:
continue
flag = f"--{k.replace('_', '-')}"
if v is True:
cmd.append(flag)
else:
cmd.extend([flag, str(v)])
try:
return subprocess.call(cmd)
except Exception as e:
display_error(f"Legacy execution failed: {e}")
return 1
def main() -> int:
"""Main entry point"""
try:
parser, subparsers, global_parser = create_parser()
operations = register_operation_parsers(subparsers, global_parser)
args = parser.parse_args()
# No operation provided? Show help manually unless in quiet mode
if not args.operation:
if not args.quiet:
display_header("SuperClaude Framework v3.0", "Unified CLI for all operations")
print(f"{Colors.CYAN}Available operations:{Colors.RESET}")
for op, desc in get_operation_modules().items():
print(f" {op:<12} {desc}")
return 0
# Handle unknown operations and suggest corrections
if args.operation not in operations:
close = difflib.get_close_matches(args.operation, operations.keys(), n=1)
suggestion = f"Did you mean: {close[0]}?" if close else ""
display_error(f"Unknown operation: '{args.operation}'. {suggestion}")
return 1
# Setup global context (logging, install path, etc.)
setup_global_environment(args)
logger = get_logger()
# Execute operation
run_func = operations.get(args.operation)
if run_func:
if logger:
logger.info(f"Executing operation: {args.operation}")
return run_func(args)
else:
# Fallback to legacy script
if logger:
logger.warning(f"Module for '{args.operation}' missing, using legacy fallback")
return handle_legacy_fallback(args.operation, args)
except KeyboardInterrupt:
print(f"\n{Colors.YELLOW}Operation cancelled by user{Colors.RESET}")
return 130
except Exception as e:
try:
logger = get_logger()
if logger:
logger.exception(f"Unhandled error: {e}")
except:
print(f"{Colors.RED}[ERROR] {e}{Colors.RESET}")
return 1
# Entrypoint guard
if __name__ == "__main__":
sys.exit(main())

0
Templates/__init__.py Normal file
View File

0
config/__init__.py Normal file
View File

View File

@@ -0,0 +1,65 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/framework_coordinator/hook_wrapper.py\" pre",
"timeout": 5
},
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/performance_monitor/hook_wrapper.py\" pre",
"timeout": 1
}
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/framework_coordinator/hook_wrapper.py\" post",
"timeout": 5
},
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/session_lifecycle/hook_wrapper.py\" post",
"timeout": 3
},
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/performance_monitor/hook_wrapper.py\" post",
"timeout": 1
}
]
},
{
"matcher": "Write|Edit|MultiEdit|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/quality_gates/hook_wrapper.py\" post",
"timeout": 4
}
]
}
],
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python \"${CLAUDE_PROJECT_DIR}/.claude/SuperClaude/Hooks/session_lifecycle/hook_wrapper.py\" session_start",
"timeout": 3
}
]
}
]
}
}

49
config/features.json Normal file
View File

@@ -0,0 +1,49 @@
{
"components": {
"core": {
"name": "core",
"version": "3.0.0",
"description": "SuperClaude framework documentation and core files",
"category": "core",
"dependencies": [],
"enabled": true,
"required_tools": []
},
"commands": {
"name": "commands",
"version": "3.0.0",
"description": "SuperClaude slash command definitions",
"category": "commands",
"dependencies": ["core"],
"enabled": true,
"required_tools": []
},
"mcp": {
"name": "mcp",
"version": "3.0.0",
"description": "MCP server integration (Context7, Sequential, Magic, Playwright, Morphllm, Serena)",
"category": "integration",
"dependencies": ["core"],
"enabled": true,
"required_tools": ["node", "claude_cli"]
},
"serena": {
"name": "serena",
"version": "3.0.0",
"description": "Semantic code analysis and intelligent editing with project-aware context management",
"category": "integration",
"dependencies": ["core", "mcp"],
"enabled": true,
"required_tools": ["uvx", "python3", "claude_cli"]
},
"hooks": {
"name": "hooks",
"version": "2.0.0",
"description": "Enhanced Task Management System - Hook Infrastructure",
"category": "integration",
"dependencies": ["core"],
"enabled": true,
"required_tools": ["python3"]
}
}
}

367
config/hooks-config.json Normal file
View File

@@ -0,0 +1,367 @@
{
"version": "1.0.0",
"description": "SuperClaude Hooks Configuration - Enhanced Task Management System v2.0",
"general": {
"enabled": true,
"verbosity": "verbose",
"auto_load": true,
"performance_monitoring": true,
"security_level": "standard",
"max_concurrent_hooks": 5,
"default_timeout_ms": 100,
"log_level": "INFO"
},
"security": {
"input_validation": true,
"path_sanitization": true,
"execution_sandboxing": true,
"max_input_size_bytes": 10000,
"max_memory_usage_mb": 50,
"allowed_file_extensions": [
".txt", ".json", ".yaml", ".yml", ".md",
".py", ".js", ".ts", ".html", ".css",
".log", ".conf", ".config", ".ini"
],
"blocked_file_extensions": [
".exe", ".dll", ".so", ".dylib", ".bat",
".cmd", ".ps1", ".sh", ".bash", ".zsh"
]
},
"performance": {
"profiling_enabled": true,
"metrics_collection": true,
"warning_threshold_ms": 80,
"critical_threshold_ms": 100,
"memory_monitoring": true,
"benchmark_tracking": true,
"history_retention_count": 100
},
"storage": {
"persistence_enabled": true,
"auto_save": true,
"save_interval_seconds": 30,
"backup_enabled": true,
"cleanup_completed_hours": 24,
"max_task_history": 1000
},
"compatibility": {
"claude_code_integration": true,
"backward_compatibility": true,
"native_tools_priority": true,
"fallback_enabled": true
},
"task_management": {
"cross_session_persistence": true,
"dependency_tracking": true,
"priority_scheduling": true,
"progress_monitoring": true,
"automatic_cleanup": true,
"session_isolation": false
},
"hooks": {
"task_validator": {
"enabled": true,
"priority": "high",
"timeout_ms": 50,
"triggers": ["task_create", "task_update", "task_execute"],
"description": "Validates task data and execution context"
},
"execution_monitor": {
"enabled": true,
"priority": "normal",
"timeout_ms": 25,
"triggers": ["hook_start", "hook_complete"],
"description": "Monitors hook execution performance and compliance"
},
"state_synchronizer": {
"enabled": true,
"priority": "high",
"timeout_ms": 75,
"triggers": ["task_state_change", "session_start", "session_end"],
"description": "Synchronizes task states across sessions"
},
"dependency_resolver": {
"enabled": true,
"priority": "normal",
"timeout_ms": 100,
"triggers": ["task_schedule", "dependency_update"],
"description": "Resolves task dependencies and scheduling"
},
"integration_bridge": {
"enabled": true,
"priority": "critical",
"timeout_ms": 50,
"triggers": ["command_execute", "tool_call"],
"description": "Bridges hooks with Claude Code native tools"
},
"map_update_checker": {
"enabled": true,
"priority": "medium",
"timeout_ms": 100,
"triggers": ["post_tool_use"],
"tools": ["Write", "Edit", "MultiEdit"],
"script": "map-update-checker.py",
"description": "Detects file changes that affect CodeBase.md sections",
"config": {
"check_codebase_md": true,
"track_changes": true,
"suggestion_threshold": 1
}
},
"map_session_check": {
"enabled": true,
"priority": "low",
"timeout_ms": 50,
"triggers": ["session_start"],
"script": "map-session-check.py",
"description": "Checks CodeBase.md freshness at session start",
"config": {
"freshness_hours": 24,
"stale_hours": 72,
"cleanup_tracking": true
}
},
"quality_gate_trigger": {
"enabled": true,
"priority": "high",
"timeout_ms": 50,
"triggers": ["post_tool_use"],
"tools": ["Write", "Edit", "MultiEdit"],
"script": "quality_gate_trigger.py",
"description": "Automated quality gate validation with workflow step tracking",
"config": {
"enable_syntax_validation": true,
"enable_type_analysis": true,
"enable_documentation_patterns": true,
"quality_score_threshold": 0.7,
"intermediate_checkpoint": true,
"comprehensive_checkpoint": true
}
},
"mcp_router_advisor": {
"enabled": true,
"priority": "medium",
"timeout_ms": 30,
"triggers": ["pre_tool_use"],
"tools": "*",
"script": "mcp_router_advisor.py",
"description": "Intelligent MCP server routing with performance optimization",
"config": {
"context7_threshold": 0.4,
"sequential_threshold": 0.6,
"magic_threshold": 0.3,
"playwright_threshold": 0.5,
"token_efficiency_target": 0.25,
"performance_gain_target": 0.35
}
},
"cache_invalidator": {
"enabled": true,
"priority": "high",
"timeout_ms": 100,
"triggers": ["post_tool_use"],
"tools": ["Write", "Edit", "MultiEdit"],
"script": "cache_invalidator.py",
"description": "Intelligent project context cache invalidation when key files change",
"config": {
"key_files": [
"package.json", "pyproject.toml", "Cargo.toml", "go.mod",
"requirements.txt", "composer.json", "pom.xml", "build.gradle",
"tsconfig.json", "webpack.config.js", "vite.config.js",
".env", "config.json", "settings.json", "app.config.js"
],
"directory_patterns": [
"src/config/", "config/", "configs/", "settings/",
"lib/", "libs/", "shared/", "common/", "utils/"
],
"cache_types": ["project_context", "dependency_cache", "config_cache"]
}
},
"evidence_collector": {
"enabled": true,
"priority": "medium",
"timeout_ms": 20,
"triggers": ["post_tool_use"],
"tools": "*",
"script": "evidence_collector.py",
"description": "Real-time evidence collection and documentation system",
"config": {
"evidence_categories": {
"file_operations": 0.25,
"analysis_results": 0.20,
"test_outcomes": 0.20,
"quality_metrics": 0.15,
"performance_data": 0.10,
"error_handling": 0.10
},
"claudedocs_integration": true,
"real_time_updates": true,
"cross_reference_threshold": 0.3,
"validation_score_target": 0.95
}
},
"hook_coordinator": {
"enabled": true,
"priority": "critical",
"timeout_ms": 100,
"triggers": ["pre_tool_use", "post_tool_use"],
"tools": "*",
"script": "hook_coordinator.py",
"description": "Central coordination system for all SuperClaude automation hooks",
"config": {
"coordinate_hooks": true,
"parallel_execution": true,
"performance_monitoring": true,
"error_recovery": true,
"max_execution_time_ms": 100,
"quality_improvement_target": 0.15,
"validation_success_target": 0.95,
"token_efficiency_target": 0.25
}
}
},
"platforms": {
"windows": {
"supported": true,
"specific_settings": {
"file_locking": "windows_style",
"path_separator": "\\",
"temp_directory": "%TEMP%\\superclaude"
}
},
"macos": {
"supported": true,
"specific_settings": {
"file_locking": "unix_style",
"path_separator": "/",
"temp_directory": "/tmp/superclaude"
}
},
"linux": {
"supported": true,
"specific_settings": {
"file_locking": "unix_style",
"path_separator": "/",
"temp_directory": "/tmp/superclaude"
}
}
},
"directories": {
"config_dir": "~/.config/superclaude/hooks",
"data_dir": "~/.local/share/superclaude/hooks",
"temp_dir": "/tmp/superclaude/hooks",
"log_dir": "~/.local/share/superclaude/logs",
"backup_dir": "~/.local/share/superclaude/backups"
},
"integration": {
"installer_compatibility": true,
"existing_infrastructure": true,
"platform_modules": [
"installer-platform",
"installer-performance",
"installer-migration"
],
"required_dependencies": [
"pathlib",
"json",
"threading",
"asyncio"
],
"optional_dependencies": [
"psutil",
"resource"
]
},
"development": {
"debug_mode": false,
"verbose_logging": false,
"performance_profiling": true,
"test_mode": false,
"mock_execution": false
},
"monitoring": {
"health_checks": true,
"performance_alerts": true,
"error_reporting": true,
"metrics_export": false,
"dashboard_enabled": false
},
"profiles": {
"minimal": {
"description": "Essential hooks for basic functionality",
"hooks": ["map_session_check", "task_validator", "integration_bridge"],
"target_users": ["beginners", "light_usage"]
},
"developer": {
"description": "Productivity hooks for active development",
"hooks": [
"map_update_checker", "map_session_check", "quality_gate_trigger",
"mcp_router_advisor", "cache_invalidator", "task_validator",
"execution_monitor", "integration_bridge"
],
"target_users": ["developers", "power_users"]
},
"enterprise": {
"description": "Complete automation suite for enterprise use",
"hooks": [
"map_update_checker", "map_session_check", "quality_gate_trigger",
"mcp_router_advisor", "cache_invalidator", "evidence_collector",
"hook_coordinator", "task_validator", "execution_monitor",
"state_synchronizer", "dependency_resolver", "integration_bridge"
],
"target_users": ["teams", "enterprise", "production"]
}
},
"installation_targets": {
"performance_expectations": {
"quality_improvement": "15-30%",
"performance_gains": "20-40%",
"validation_success": "95%+",
"execution_time": "<100ms"
},
"claude_code_integration": {
"settings_file": "~/.claude/settings.json",
"hooks_directory": "~/.claude/SuperClaude/Hooks/",
"backup_enabled": true,
"validation_required": true
},
"installer_compatibility": {
"installer_core": true,
"installer_wizard": true,
"installer_profiles": true,
"installer_platform": true,
"cross_platform": true
}
}
}

54
config/requirements.json Normal file
View File

@@ -0,0 +1,54 @@
{
"python": {
"min_version": "3.8.0"
},
"node": {
"min_version": "16.0.0",
"required_for": ["mcp"]
},
"disk_space_mb": 500,
"external_tools": {
"claude_cli": {
"command": "claude --version",
"min_version": "0.1.0",
"required_for": ["mcp"],
"optional": false
},
"git": {
"command": "git --version",
"min_version": "2.0.0",
"required_for": ["development"],
"optional": true
}
},
"installation_commands": {
"python": {
"linux": "sudo apt update && sudo apt install python3 python3-pip",
"darwin": "brew install python3",
"win32": "Download Python from https://python.org/downloads/",
"description": "Python 3.8+ is required for SuperClaude framework"
},
"node": {
"linux": "sudo apt update && sudo apt install nodejs npm",
"darwin": "brew install node",
"win32": "Download Node.js from https://nodejs.org/",
"description": "Node.js 16+ is required for MCP server integration"
},
"claude_cli": {
"all": "Visit https://claude.ai/code for installation instructions",
"description": "Claude CLI is required for MCP server management"
},
"git": {
"linux": "sudo apt update && sudo apt install git",
"darwin": "brew install git",
"win32": "Download Git from https://git-scm.com/downloads",
"description": "Git is recommended for development workflows"
},
"npm": {
"linux": "sudo apt update && sudo apt install npm",
"darwin": "npm is included with Node.js",
"win32": "npm is included with Node.js",
"description": "npm is required for installing MCP servers"
}
}
}

View File

@@ -0,0 +1,161 @@
{
"superclaude": {
"version": "3.1.0",
"hooks_system": {
"enabled": true,
"version": "1.0.0",
"performance_target_ms": 100,
"graceful_degradation": true,
"logging": {
"enabled": true,
"level": "INFO",
"file": "${CLAUDE_HOME}/superclaude-hooks.log"
}
},
"framework_coordination": {
"enabled": true,
"auto_activation": {
"enabled": true,
"confidence_threshold": 0.7,
"mcp_server_suggestions": true
},
"compliance_validation": {
"enabled": true,
"rules_checking": true,
"warnings_only": false
},
"orchestrator_routing": {
"enabled": true,
"pattern_matching": true,
"resource_zone_awareness": true
}
},
"session_lifecycle": {
"enabled": true,
"auto_load": {
"enabled": true,
"new_projects": true
},
"checkpoint_automation": {
"enabled": true,
"time_based": {
"enabled": true,
"interval_minutes": 30
},
"task_based": {
"enabled": true,
"high_priority_tasks": true
},
"risk_based": {
"enabled": true,
"major_operations": true
}
},
"session_persistence": {
"enabled": true,
"cross_session_learning": true
}
},
"quality_gates": {
"enabled": true,
"validation_triggers": {
"write_operations": true,
"edit_operations": true,
"major_changes": true
},
"validation_steps": {
"syntax_validation": true,
"type_analysis": true,
"lint_rules": true,
"security_assessment": true,
"performance_analysis": true,
"documentation_check": true
},
"quality_thresholds": {
"minimum_score": 0.8,
"warning_threshold": 0.7,
"auto_fix_threshold": 0.9
}
},
"performance_monitoring": {
"enabled": true,
"metrics": {
"execution_time": true,
"resource_usage": true,
"framework_compliance": true,
"mcp_server_efficiency": true
},
"targets": {
"hook_execution_ms": 100,
"memory_operations_ms": 200,
"session_load_ms": 500,
"context_retention_percent": 90
},
"alerting": {
"enabled": true,
"threshold_violations": true,
"performance_degradation": true
}
},
"mcp_coordination": {
"enabled": true,
"intelligent_routing": true,
"server_selection": {
"context7": {
"auto_activate": ["library", "framework", "documentation"],
"complexity_threshold": 0.3
},
"sequential": {
"auto_activate": ["analysis", "debugging", "complex"],
"complexity_threshold": 0.7
},
"magic": {
"auto_activate": ["ui", "component", "frontend"],
"complexity_threshold": 0.3
},
"serena": {
"auto_activate": ["files>10", "symbol_ops", "multi_lang"],
"complexity_threshold": 0.6
},
"morphllm": {
"auto_activate": ["pattern_edit", "token_opt", "simple_edit"],
"complexity_threshold": 0.4
},
"playwright": {
"auto_activate": ["testing", "browser", "e2e"],
"complexity_threshold": 0.6
}
}
},
"hook_configurations": {
"framework_coordinator": {
"name": "superclaude-framework-coordinator",
"description": "Central intelligence for SuperClaude framework coordination",
"priority": "critical",
"retry": 2,
"enabled": true
},
"session_lifecycle": {
"name": "superclaude-session-lifecycle",
"description": "Automatic session management and checkpoints",
"priority": "high",
"retry": 1,
"enabled": true
},
"quality_gates": {
"name": "superclaude-quality-gates",
"description": "Systematic quality validation enforcement",
"priority": "high",
"retry": 1,
"enabled": true
},
"performance_monitor": {
"name": "superclaude-performance-monitor",
"description": "Real-time performance tracking",
"priority": "medium",
"retry": 1,
"enabled": true
}
}
}
}

12
profiles/__init__.py Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
SuperClaude Framework Management Hub
Unified entry point for all SuperClaude operations
Usage:
SuperClaude install [options]
SuperClaude update [options]
SuperClaude uninstall [options]
SuperClaude backup [options]
SuperClaude --help
"""

17
profiles/developer.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "Developer Installation",
"description": "Full installation with all components including MCP servers",
"components": [
"core",
"commands",
"mcp"
],
"features": {
"auto_update": false,
"backup_enabled": true,
"validation_level": "comprehensive"
},
"target_users": ["developers", "power_users"],
"estimated_time_minutes": 5,
"disk_space_mb": 100
}

15
profiles/minimal.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "Minimal Installation",
"description": "Core framework files only",
"components": [
"core"
],
"features": {
"auto_update": false,
"backup_enabled": true,
"validation_level": "basic"
},
"target_users": ["testing", "basic"],
"estimated_time_minutes": 1,
"disk_space_mb": 20
}

16
profiles/quick.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "Quick Installation",
"description": "Recommended installation with core framework and essential components",
"components": [
"core",
"commands"
],
"features": {
"auto_update": false,
"backup_enabled": true,
"validation_level": "standard"
},
"target_users": ["general", "developers"],
"estimated_time_minutes": 2,
"disk_space_mb": 50
}

View File

@@ -1,62 +0,0 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "SuperClaude"
dynamic = ["version"]
description = "SuperClaude V4 Beta - Advanced orchestration framework for Claude Code with 21 commands, 13 agents, and 6 MCP servers"
readme = "README.md"
license = {text = "MIT"}
authors = [
{name = "Mithun Gowda B", email = "contact@superclaude.dev"},
{name = "NomenAK", email = "contact@superclaude.dev"},
]
requires-python = ">=3.8"
dependencies = [
"setuptools>=45.0.0",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
[project.urls]
Homepage = "https://github.com/SuperClaude-Org/SuperClaude_Framework"
Repository = "https://github.com/SuperClaude-Org/SuperClaude_Framework"
"Bug Tracker" = "https://github.com/SuperClaude-Org/SuperClaude_Framework/issues"
"GitHub" = "https://github.com/SuperClaude-Org/SuperClaude_Framework"
"Mithun Gowda B" = "https://github.com/mithun50"
"NomenAK" = "https://github.com/NomenAK"
"Anton Nesterov" = "https://github.com/anton-nesterov"
[project.scripts]
SuperClaude = "SuperClaude.__main__:main"
[tool.hatch.version]
path = "VERSION"
pattern = "(?P<version>.*)"
[tool.hatch.build.targets.wheel]
packages = ["SuperClaude"]
[tool.hatch.build.targets.sdist]
include = [
"SuperClaude/",
"config/",
"profiles/",
"setup/",
"VERSION",
"README.md",
"LICENSE",
"MANIFEST.in",
]

88
setup.py Normal file
View File

@@ -0,0 +1,88 @@
import setuptools
import sys
import logging
# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def get_version():
"""Get version from VERSION file with proper error handling."""
try:
with open("VERSION", "r") as f:
return f.read().strip()
except FileNotFoundError:
logger.warning("VERSION file not found, using fallback version")
return "3.0.0"
except Exception as e:
logger.error(f"Error reading VERSION file: {e}")
return "3.0.0"
def get_long_description():
"""Get long description from README with error handling."""
try:
with open("README.md", "r", encoding="utf-8") as fh:
return fh.read()
except FileNotFoundError:
logger.warning("README.md not found")
return "SuperClaude Framework Management Hub"
except Exception as e:
logger.error(f"Error reading README.md: {e}")
return "SuperClaude Framework Management Hub"
def get_install_requires():
"""Get install requirements with proper dependency management."""
base_requires = ["setuptools>=45.0.0"]
# Add Python version-specific dependencies
if sys.version_info < (3, 8):
base_requires.append("importlib-metadata>=1.0.0")
# Add other dependencies your project needs
# base_requires.extend([
# "requests>=2.25.0",
# "click>=7.0",
# # etc.
# ])
return base_requires
# Main setup configuration
setuptools.setup(
name="SuperClaude",
version=get_version(),
author="Mithun Gowda B, NomenAK",
author_email="contact@superclaude.dev",
description="SuperClaude Framework Management Hub",
long_description=get_long_description(),
long_description_content_type="text/markdown",
url="https://github.com/SuperClaude-Org/SuperClaude_Framework",
packages=setuptools.find_packages(),
include_package_data=True,
install_requires=get_install_requires(),
entry_points={
"console_scripts": [
"SuperClaude=SuperClaude.__main__:main",
"superclaude=SuperClaude.__main__:main",
],
},
python_requires=">=3.8",
project_urls={
"GitHub": "https://github.com/SuperClaude-Org/SuperClaude_Framework",
"Mithun Gowda B": "https://github.com/mithun50",
"NomenAK": "https://github.com/NomenAK",
"Bug Tracker": "https://github.com/SuperClaude-Org/SuperClaude_Framework/issues",
},
classifiers=[
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Operating System :: OS Independent",
"License :: OSI Approved :: MIT License",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
],
)

18
setup/__init__.py Normal file
View File

@@ -0,0 +1,18 @@
"""
SuperClaude Installation Suite
Pure Python installation system for SuperClaude framework
"""
__version__ = "3.0.0"
__author__ = "SuperClaude Team"
from pathlib import Path
# Core paths
SETUP_DIR = Path(__file__).parent
PROJECT_ROOT = SETUP_DIR.parent
CONFIG_DIR = PROJECT_ROOT / "config"
PROFILES_DIR = PROJECT_ROOT / "profiles"
# Installation target
DEFAULT_INSTALL_DIR = Path.home() / ".claude"

6
setup/base/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
"""Base classes for SuperClaude installation system"""
from .component import Component
from .installer import Installer
__all__ = ['Component', 'Installer']

361
setup/base/component.py Normal file
View File

@@ -0,0 +1,361 @@
"""
Abstract base class for installable components
"""
from abc import ABC, abstractmethod
from typing import List, Dict, Tuple, Optional, Any
from pathlib import Path
import json
from ..managers.file_manager import FileManager
from ..managers.settings_manager import SettingsManager
from ..utils.logger import get_logger
from ..utils.security import SecurityValidator
class Component(ABC):
"""Base class for all installable components"""
def __init__(self, install_dir: Optional[Path] = None, component_subdir: Path = Path('')):
"""
Initialize component with installation directory
Args:
install_dir: Target installation directory (defaults to ~/.claude)
"""
from .. import DEFAULT_INSTALL_DIR
self.install_dir = install_dir or DEFAULT_INSTALL_DIR
self.settings_manager = SettingsManager(self.install_dir)
self.logger = get_logger()
self.component_files = self._discover_component_files()
self.file_manager = FileManager()
self.install_component_subdir = self.install_dir / component_subdir
@abstractmethod
def get_metadata(self) -> Dict[str, str]:
"""
Return component metadata
Returns:
Dict containing:
- name: Component name
- version: Component version
- description: Component description
- category: Component category (core, command, integration, etc.)
"""
pass
def validate_prerequisites(self, installSubPath: Optional[Path] = None) -> Tuple[bool, List[str]]:
"""
Check prerequisites for this component
Returns:
Tuple of (success: bool, error_messages: List[str])
"""
errors = []
# Check if we have read access to source files
source_dir = self._get_source_dir()
if not source_dir or (source_dir and not source_dir.exists()):
errors.append(f"Source directory not found: {source_dir}")
return False, errors
# Check if all required framework files exist
missing_files = []
for filename in self.component_files:
source_file = source_dir / filename
if not source_file.exists():
missing_files.append(filename)
if missing_files:
errors.append(f"Missing component files: {missing_files}")
# Check write permissions to install directory
has_perms, missing = SecurityValidator.check_permissions(
self.install_dir, {'write'}
)
if not has_perms:
errors.append(f"No write permissions to {self.install_dir}: {missing}")
# Validate installation target
is_safe, validation_errors = SecurityValidator.validate_installation_target(self.install_component_subdir)
if not is_safe:
errors.extend(validation_errors)
# Get files to install
files_to_install = self.get_files_to_install()
# Validate all files for security
is_safe, security_errors = SecurityValidator.validate_component_files(
files_to_install, source_dir, self.install_component_subdir
)
if not is_safe:
errors.extend(security_errors)
if not self.file_manager.ensure_directory(self.install_component_subdir):
errors.append(f"Could not create install directory: {self.install_component_subdir}")
return len(errors) == 0, errors
def get_files_to_install(self) -> List[Tuple[Path, Path]]:
"""
Return list of files to install
Returns:
List of tuples (source_path, target_path)
"""
source_dir = self._get_source_dir()
files = []
if source_dir:
for filename in self.component_files:
source = source_dir / filename
target = self.install_component_subdir / filename
files.append((source, target))
return files
def get_settings_modifications(self) -> Dict[str, Any]:
"""
Return settings.json modifications to apply
(now only Claude Code compatible settings)
Returns:
Dict of settings to merge into settings.json
"""
# Return empty dict as we don't modify Claude Code settings
return {}
def install(self, config: Dict[str, Any]) -> bool:
try:
return self._install(config)
except Exception as e:
self.logger.exception(f"Unexpected error during {repr(self)} installation: {e}")
return False
@abstractmethod
def _install(self, config: Dict[str, Any]) -> bool:
"""
Perform component-specific installation logic
Args:
config: Installation configuration
Returns:
True if successful, False otherwise
"""
# Validate installation
success, errors = self.validate_prerequisites()
if not success:
for error in errors:
self.logger.error(error)
return False
# Get files to install
files_to_install = self.get_files_to_install()
# Copy framework files
success_count = 0
for source, target in files_to_install:
self.logger.debug(f"Copying {source.name} to {target}")
if self.file_manager.copy_file(source, target):
success_count += 1
self.logger.debug(f"Successfully copied {source.name}")
else:
self.logger.error(f"Failed to copy {source.name}")
if success_count != len(files_to_install):
self.logger.error(f"Only {success_count}/{len(files_to_install)} files copied successfully")
return False
self.logger.success(f"{repr(self)} component installed successfully ({success_count} files)")
return self._post_install()
@abstractmethod
def _post_install(self) -> bool:
pass
@abstractmethod
def uninstall(self) -> bool:
"""
Remove component
Returns:
True if successful, False otherwise
"""
pass
@abstractmethod
def get_dependencies(self) -> List[str]:
"""
Return list of component dependencies
Returns:
List of component names this component depends on
"""
pass
@abstractmethod
def _get_source_dir(self) -> Optional[Path]:
"""Get source directory for component files"""
pass
def update(self, config: Dict[str, Any]) -> bool:
"""
Update component (default: uninstall then install)
Args:
config: Installation configuration
Returns:
True if successful, False otherwise
"""
# Default implementation: uninstall and reinstall
if self.uninstall():
return self.install(config)
return False
def get_installed_version(self) -> Optional[str]:
"""
Get currently installed version of component
Returns:
Version string if installed, None otherwise
"""
print("GETTING INSTALLED VERSION")
settings_file = self.install_dir / "settings.json"
if settings_file.exists():
print("SETTINGS.JSON EXISTS")
try:
with open(settings_file, 'r') as f:
settings = json.load(f)
component_name = self.get_metadata()['name']
return settings.get('components', {}).get(component_name, {}).get('version')
except Exception:
pass
print("SETTINGS.JSON DOESNT EXIST RETURNING NONE")
return None
def is_installed(self) -> bool:
"""
Check if component is installed
Returns:
True if installed, False otherwise
"""
return self.get_installed_version() is not None
def validate_installation(self) -> Tuple[bool, List[str]]:
"""
Validate that component is correctly installed
Returns:
Tuple of (success: bool, error_messages: List[str])
"""
errors = []
# Check if all files exist
for _, target in self.get_files_to_install():
if not target.exists():
errors.append(f"Missing file: {target}")
# Check version in settings
if not self.get_installed_version():
errors.append("Component not registered in settings.json")
return len(errors) == 0, errors
def get_size_estimate(self) -> int:
"""
Estimate installed size in bytes
Returns:
Estimated size in bytes
"""
total_size = 0
for source, _ in self.get_files_to_install():
if source.exists():
if source.is_file():
total_size += source.stat().st_size
elif source.is_dir():
total_size += sum(f.stat().st_size for f in source.rglob('*') if f.is_file())
return total_size
def _discover_component_files(self) -> List[str]:
"""
Dynamically discover framework .md files in the Core directory
Returns:
List of framework filenames (e.g., ['CLAUDE.md', 'COMMANDS.md', ...])
"""
source_dir = self._get_source_dir()
if not source_dir:
return []
return self._discover_files_in_directory(
source_dir,
extension='.md',
exclude_patterns=['README.md', 'CHANGELOG.md', 'LICENSE.md']
)
def _discover_files_in_directory(self, directory: Path, extension: str = '.md',
exclude_patterns: Optional[List[str]] = None) -> List[str]:
"""
Shared utility for discovering files in a directory
Args:
directory: Directory to scan
extension: File extension to look for (default: '.md')
exclude_patterns: List of filename patterns to exclude
Returns:
List of filenames found in the directory
"""
if exclude_patterns is None:
exclude_patterns = []
try:
if not directory.exists():
self.logger.warning(f"Source directory not found: {directory}")
return []
if not directory.is_dir():
self.logger.warning(f"Source path is not a directory: {directory}")
return []
# Discover files with the specified extension
files = []
for file_path in directory.iterdir():
if (file_path.is_file() and
file_path.suffix.lower() == extension.lower() and
file_path.name not in exclude_patterns):
files.append(file_path.name)
# Sort for consistent ordering
files.sort()
self.logger.debug(f"Discovered {len(files)} {extension} files in {directory}")
if files:
self.logger.debug(f"Files found: {files}")
return files
except PermissionError:
self.logger.error(f"Permission denied accessing directory: {directory}")
return []
except Exception as e:
self.logger.error(f"Error discovering files in {directory}: {e}")
return []
def __str__(self) -> str:
"""String representation of component"""
metadata = self.get_metadata()
return f"{metadata['name']} v{metadata['version']}"
def __repr__(self) -> str:
"""Developer representation of component"""
return f"<{self.__class__.__name__}({self.get_metadata()['name']})>"

331
setup/base/installer.py Normal file
View File

@@ -0,0 +1,331 @@
"""
Base installer logic for SuperClaude installation system fixed some issues
"""
from typing import List, Dict, Optional, Set, Tuple, Any
from pathlib import Path
import shutil
import tempfile
from datetime import datetime
from .component import Component
class Installer:
"""Main installer orchestrator"""
def __init__(self,
install_dir: Optional[Path] = None,
dry_run: bool = False):
"""
Initialize installer
Args:
install_dir: Target installation directory
dry_run: If True, only simulate installation
"""
from .. import DEFAULT_INSTALL_DIR
self.install_dir = install_dir or DEFAULT_INSTALL_DIR
self.dry_run = dry_run
self.components: Dict[str, Component] = {}
self.installed_components: Set[str] = set()
self.updated_components: Set[str] = set()
self.failed_components: Set[str] = set()
self.skipped_components: Set[str] = set()
self.backup_path: Optional[Path] = None
def register_component(self, component: Component) -> None:
"""
Register a component for installation
Args:
component: Component instance to register
"""
metadata = component.get_metadata()
self.components[metadata['name']] = component
def register_components(self, components: List[Component]) -> None:
"""
Register multiple components
Args:
components: List of component instances
"""
for component in components:
self.register_component(component)
def resolve_dependencies(self, component_names: List[str]) -> List[str]:
"""
Resolve component dependencies in correct installation order
Args:
component_names: List of component names to install
Returns:
Ordered list of component names including dependencies
Raises:
ValueError: If circular dependencies detected or unknown component
"""
resolved = []
resolving = set()
def resolve(name: str):
if name in resolved:
return
if name in resolving:
raise ValueError(
f"Circular dependency detected involving {name}")
if name not in self.components:
raise ValueError(f"Unknown component: {name}")
resolving.add(name)
# Resolve dependencies first
for dep in self.components[name].get_dependencies():
resolve(dep)
resolving.remove(name)
resolved.append(name)
# Resolve each requested component
for name in component_names:
resolve(name)
return resolved
def validate_system_requirements(self) -> Tuple[bool, List[str]]:
"""
Validate system requirements for all registered components
Returns:
Tuple of (success: bool, error_messages: List[str])
"""
errors = []
# Check disk space (500MB minimum)
try:
stat = shutil.disk_usage(self.install_dir.parent)
free_mb = stat.free / (1024 * 1024)
if free_mb < 500:
errors.append(
f"Insufficient disk space: {free_mb:.1f}MB free (500MB required)"
)
except Exception as e:
errors.append(f"Could not check disk space: {e}")
# Check write permissions
test_file = self.install_dir / ".write_test"
try:
self.install_dir.mkdir(parents=True, exist_ok=True)
test_file.touch()
test_file.unlink()
except Exception as e:
errors.append(f"No write permission to {self.install_dir}: {e}")
return len(errors) == 0, errors
def create_backup(self) -> Optional[Path]:
"""
Create backup of existing installation
Returns:
Path to backup archive or None if no existing installation
"""
if not self.install_dir.exists():
return None
if self.dry_run:
return self.install_dir / "backup_dryrun.tar.gz"
# Create backup directory
backup_dir = self.install_dir / "backups"
backup_dir.mkdir(exist_ok=True)
# Create timestamped backup
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_name = f"superclaude_backup_{timestamp}"
backup_path = backup_dir / f"{backup_name}.tar.gz"
# Create temporary directory for backup
with tempfile.TemporaryDirectory() as temp_dir:
temp_backup = Path(temp_dir) / backup_name
# Ensure temp backup directory exists
temp_backup.mkdir(parents=True, exist_ok=True)
# Copy all files except backups directory
for item in self.install_dir.iterdir():
if item.name != "backups":
try:
if item.is_file():
shutil.copy2(item, temp_backup / item.name)
elif item.is_dir():
shutil.copytree(item, temp_backup / item.name)
except Exception as e:
# Log warning but continue backup process
print(f"Warning: Could not backup {item.name}: {e}")
# Create archive only if there are files to backup
if any(temp_backup.iterdir()):
shutil.make_archive(backup_path.with_suffix(''), 'gztar',
temp_dir, backup_name)
else:
# Create empty backup file to indicate backup was attempted
backup_path.touch()
print(
f"Warning: No files to backup, created empty backup marker: {backup_path.name}"
)
self.backup_path = backup_path
return backup_path
def install_component(self, component_name: str,
config: Dict[str, Any]) -> bool:
"""
Install a single component
Args:
component_name: Name of component to install
config: Installation configuration
Returns:
True if successful, False otherwise
"""
if component_name not in self.components:
raise ValueError(f"Unknown component: {component_name}")
component = self.components[component_name]
# Skip if already installed
if component_name in self.installed_components:
return True
# Check prerequisites
success, errors = component.validate_prerequisites()
if not success:
print(f"Prerequisites failed for {component_name}:")
for error in errors:
print(f" - {error}")
self.failed_components.add(component_name)
return False
# Perform installation
try:
if self.dry_run:
print(f"[DRY RUN] Would install {component_name}")
success = True
else:
success = component.install(config)
if success:
self.installed_components.add(component_name)
self.updated_components.add(component_name)
else:
self.failed_components.add(component_name)
return success
except Exception as e:
print(f"Error installing {component_name}: {e}")
self.failed_components.add(component_name)
return False
def install_components(self,
component_names: List[str],
config: Optional[Dict[str, Any]] = None) -> bool:
"""
Install multiple components in dependency order
Args:
component_names: List of component names to install
config: Installation configuration
Returns:
True if all successful, False if any failed
"""
config = config or {}
# Resolve dependencies
try:
ordered_names = self.resolve_dependencies(component_names)
except ValueError as e:
print(f"Dependency resolution error: {e}")
return False
# Validate system requirements
success, errors = self.validate_system_requirements()
if not success:
print("System requirements not met:")
for error in errors:
print(f" - {error}")
return False
# Create backup if updating
if self.install_dir.exists() and not self.dry_run:
print("Creating backup of existing installation...")
self.create_backup()
# Install each component
all_success = True
for name in ordered_names:
print(f"\nInstalling {name}...")
if not self.install_component(name, config):
all_success = False
# Continue installing other components even if one fails
if not self.dry_run:
self._run_post_install_validation()
return all_success
def _run_post_install_validation(self) -> None:
"""Run post-installation validation for all installed components"""
print("\nRunning post-installation validation...")
all_valid = True
for name in self.installed_components:
component = self.components[name]
success, errors = component.validate_installation()
if success:
print(f"{name}: Valid")
else:
print(f"{name}: Invalid")
for error in errors:
print(f" - {error}")
all_valid = False
if all_valid:
print("\nAll components validated successfully!")
else:
print("\nSome components failed validation. Check errors above.")
def update_components(self, component_names: List[str], config: Dict[str, Any]) -> bool:
"""Alias for update operation (uses install logic)"""
return self.install_components(component_names, config)
def get_installation_summary(self) -> Dict[str, Any]:
"""
Get summary of installation results
Returns:
Dict with installation statistics and results
"""
return {
'installed': list(self.installed_components),
'failed': list(self.failed_components),
'skipped': list(self.skipped_components),
'backup_path': str(self.backup_path) if self.backup_path else None,
'install_dir': str(self.install_dir),
'dry_run': self.dry_run
}
def get_update_summary(self) -> Dict[str, Any]:
return {
'updated': list(self.updated_components),
'failed': list(self.failed_components),
'backup_path': str(self.backup_path) if self.backup_path else None
}

View File

@@ -0,0 +1,13 @@
"""Component implementations for SuperClaude installation system"""
from .core import CoreComponent
from .commands import CommandsComponent
from .mcp import MCPComponent
from .hooks import HooksComponent
__all__ = [
'CoreComponent',
'CommandsComponent',
'MCPComponent',
'HooksComponent'
]

View File

@@ -0,0 +1,329 @@
"""
Commands component for SuperClaude slash command definitions
"""
from typing import Dict, List, Tuple, Optional, Any
from pathlib import Path
from ..base.component import Component
class CommandsComponent(Component):
"""SuperClaude slash commands component"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize commands component"""
super().__init__(install_dir, Path("commands/sc"))
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "commands",
"version": "3.0.0",
"description": "SuperClaude slash command definitions",
"category": "commands"
}
def get_metadata_modifications(self) -> Dict[str, Any]:
"""Get metadata modifications for commands component"""
return {
"components": {
"commands": {
"version": "3.0.0",
"installed": True,
"files_count": len(self.component_files)
}
},
"commands": {
"enabled": True,
"version": "3.0.0",
"auto_update": False
}
}
def _install(self, config: Dict[str, Any]) -> bool:
"""Install commands component"""
self.logger.info("Installing SuperClaude command definitions...")
# Check for and migrate existing commands from old location
self._migrate_existing_commands()
return super()._install(config);
def _post_install(self):
# Update metadata
try:
metadata_mods = self.get_metadata_modifications()
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with commands configuration")
# Add component registration to metadata
self.settings_manager.add_component_registration("commands", {
"version": "3.0.0",
"category": "commands",
"files_count": len(self.component_files)
})
self.logger.info("Updated metadata with commands component registration")
except Exception as e:
self.logger.error(f"Failed to update metadata: {e}")
return False
return True
def uninstall(self) -> bool:
"""Uninstall commands component"""
try:
self.logger.info("Uninstalling SuperClaude commands component...")
# Remove command files from sc subdirectory
commands_dir = self.install_dir / "commands" / "sc"
removed_count = 0
for filename in self.component_files:
file_path = commands_dir / filename
if self.file_manager.remove_file(file_path):
removed_count += 1
self.logger.debug(f"Removed {filename}")
else:
self.logger.warning(f"Could not remove {filename}")
# Also check and remove any old commands in root commands directory
old_commands_dir = self.install_dir / "commands"
old_removed_count = 0
for filename in self.component_files:
old_file_path = old_commands_dir / filename
if old_file_path.exists() and old_file_path.is_file():
if self.file_manager.remove_file(old_file_path):
old_removed_count += 1
self.logger.debug(f"Removed old {filename}")
else:
self.logger.warning(f"Could not remove old {filename}")
if old_removed_count > 0:
self.logger.info(f"Also removed {old_removed_count} commands from old location")
removed_count += old_removed_count
# Remove sc subdirectory if empty
try:
if commands_dir.exists():
remaining_files = list(commands_dir.iterdir())
if not remaining_files:
commands_dir.rmdir()
self.logger.debug("Removed empty sc commands directory")
# Also remove parent commands directory if empty
parent_commands_dir = self.install_dir / "commands"
if parent_commands_dir.exists():
remaining_files = list(parent_commands_dir.iterdir())
if not remaining_files:
parent_commands_dir.rmdir()
self.logger.debug("Removed empty parent commands directory")
except Exception as e:
self.logger.warning(f"Could not remove commands directory: {e}")
# Update metadata to remove commands component
try:
if self.settings_manager.is_component_installed("commands"):
self.settings_manager.remove_component_registration("commands")
# Also remove commands configuration from metadata
metadata = self.settings_manager.load_metadata()
if "commands" in metadata:
del metadata["commands"]
self.settings_manager.save_metadata(metadata)
self.logger.info("Removed commands component from metadata")
except Exception as e:
self.logger.warning(f"Could not update metadata: {e}")
self.logger.success(f"Commands component uninstalled ({removed_count} files removed)")
return True
except Exception as e:
self.logger.exception(f"Unexpected error during commands uninstallation: {e}")
return False
def get_dependencies(self) -> List[str]:
"""Get dependencies"""
return ["core"]
def update(self, config: Dict[str, Any]) -> bool:
"""Update commands component"""
try:
self.logger.info("Updating SuperClaude commands component...")
# Check current version
current_version = self.settings_manager.get_component_version("commands")
target_version = self.get_metadata()["version"]
if current_version == target_version:
self.logger.info(f"Commands component already at version {target_version}")
return True
self.logger.info(f"Updating commands component from {current_version} to {target_version}")
# Create backup of existing command files
commands_dir = self.install_dir / "commands" / "sc"
backup_files = []
if commands_dir.exists():
for filename in self.component_files:
file_path = commands_dir / filename
if file_path.exists():
backup_path = self.file_manager.backup_file(file_path)
if backup_path:
backup_files.append(backup_path)
self.logger.debug(f"Backed up {filename}")
# Perform installation (overwrites existing files)
success = self.install(config)
if success:
# Remove backup files on successful update
for backup_path in backup_files:
try:
backup_path.unlink()
except Exception:
pass # Ignore cleanup errors
self.logger.success(f"Commands component updated to version {target_version}")
else:
# Restore from backup on failure
self.logger.warning("Update failed, restoring from backup...")
for backup_path in backup_files:
try:
original_path = backup_path.with_suffix('')
backup_path.rename(original_path)
self.logger.debug(f"Restored {original_path.name}")
except Exception as e:
self.logger.error(f"Could not restore {backup_path}: {e}")
return success
except Exception as e:
self.logger.exception(f"Unexpected error during commands update: {e}")
return False
def validate_installation(self) -> Tuple[bool, List[str]]:
"""Validate commands component installation"""
errors = []
# Check if sc commands directory exists
commands_dir = self.install_dir / "commands" / "sc"
if not commands_dir.exists():
errors.append("SC commands directory not found")
return False, errors
# Check if all command files exist
for filename in self.component_files:
file_path = commands_dir / filename
if not file_path.exists():
errors.append(f"Missing command file: {filename}")
elif not file_path.is_file():
errors.append(f"Command file is not a regular file: {filename}")
# Check metadata registration
if not self.settings_manager.is_component_installed("commands"):
errors.append("Commands component not registered in metadata")
else:
# Check version matches
installed_version = self.settings_manager.get_component_version("commands")
expected_version = self.get_metadata()["version"]
if installed_version != expected_version:
errors.append(f"Version mismatch: installed {installed_version}, expected {expected_version}")
return len(errors) == 0, errors
def _get_source_dir(self) -> Path:
"""Get source directory for command files"""
# Assume we're in SuperClaude/setup/components/commands.py
# and command files are in SuperClaude/SuperClaude/Commands/
project_root = Path(__file__).parent.parent.parent
return project_root / "SuperClaude" / "Commands"
def get_size_estimate(self) -> int:
"""Get estimated installation size"""
total_size = 0
source_dir = self._get_source_dir()
for filename in self.component_files:
file_path = source_dir / filename
if file_path.exists():
total_size += file_path.stat().st_size
# Add overhead for directory and settings
total_size += 5120 # ~5KB overhead
return total_size
def get_installation_summary(self) -> Dict[str, Any]:
"""Get installation summary"""
return {
"component": self.get_metadata()["name"],
"version": self.get_metadata()["version"],
"files_installed": len(self.component_files),
"command_files": self.component_files,
"estimated_size": self.get_size_estimate(),
"install_directory": str(self.install_dir / "commands" / "sc"),
"dependencies": self.get_dependencies()
}
def _migrate_existing_commands(self) -> None:
"""Migrate existing commands from old location to new sc subdirectory"""
try:
old_commands_dir = self.install_dir / "commands"
new_commands_dir = self.install_dir / "commands" / "sc"
# Check if old commands exist in root commands directory
migrated_count = 0
commands_to_migrate = []
if old_commands_dir.exists():
for filename in self.component_files:
old_file_path = old_commands_dir / filename
if old_file_path.exists() and old_file_path.is_file():
commands_to_migrate.append(filename)
if commands_to_migrate:
self.logger.info(f"Found {len(commands_to_migrate)} existing commands to migrate to sc/ subdirectory")
# Ensure new directory exists
if not self.file_manager.ensure_directory(new_commands_dir):
self.logger.error(f"Could not create sc commands directory: {new_commands_dir}")
return
# Move files from old to new location
for filename in commands_to_migrate:
old_file_path = old_commands_dir / filename
new_file_path = new_commands_dir / filename
try:
# Copy file to new location
if self.file_manager.copy_file(old_file_path, new_file_path):
# Remove old file
if self.file_manager.remove_file(old_file_path):
migrated_count += 1
self.logger.debug(f"Migrated {filename} to sc/ subdirectory")
else:
self.logger.warning(f"Could not remove old {filename}")
else:
self.logger.warning(f"Could not copy {filename} to sc/ subdirectory")
except Exception as e:
self.logger.warning(f"Error migrating {filename}: {e}")
if migrated_count > 0:
self.logger.success(f"Successfully migrated {migrated_count} commands to /sc: namespace")
self.logger.info("Commands are now available as /sc:analyze, /sc:build, etc.")
# Try to remove old commands directory if empty
try:
if old_commands_dir.exists():
remaining_files = [f for f in old_commands_dir.iterdir() if f.is_file()]
if not remaining_files:
# Only remove if no user files remain
old_commands_dir.rmdir()
self.logger.debug("Removed empty old commands directory")
except Exception as e:
self.logger.debug(f"Could not remove old commands directory: {e}")
except Exception as e:
self.logger.warning(f"Error during command migration: {e}")

248
setup/components/core.py Normal file
View File

@@ -0,0 +1,248 @@
"""
Core component for SuperClaude framework files installation
"""
from typing import Dict, List, Tuple, Optional, Any
from pathlib import Path
import shutil
from ..base.component import Component
class CoreComponent(Component):
"""Core SuperClaude framework files component"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize core component"""
super().__init__(install_dir)
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "core",
"version": "3.0.0",
"description": "SuperClaude framework documentation and core files",
"category": "core"
}
def get_metadata_modifications(self) -> Dict[str, Any]:
"""Get metadata modifications for SuperClaude"""
return {
"framework": {
"version": "3.0.0",
"name": "SuperClaude",
"description": "AI-enhanced development framework for Claude Code",
"installation_type": "global",
"components": ["core"]
},
"superclaude": {
"enabled": True,
"version": "3.0.0",
"profile": "default",
"auto_update": False
}
}
def _install(self, config: Dict[str, Any]) -> bool:
"""Install core component"""
self.logger.info("Installing SuperClaude core framework files...")
return super()._install(config);
def _post_install(self):
# Create or update metadata
try:
metadata_mods = self.get_metadata_modifications()
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with framework configuration")
# Add component registration to metadata
self.settings_manager.add_component_registration("core", {
"version": "3.0.0",
"category": "core",
"files_count": len(self.component_files)
})
self.logger.info("Updated metadata with core component registration")
# Migrate any existing SuperClaude data from settings.json
if self.settings_manager.migrate_superclaude_data():
self.logger.info("Migrated existing SuperClaude data from settings.json")
except Exception as e:
self.logger.error(f"Failed to update metadata: {e}")
return False
# Create additional directories for other components
additional_dirs = ["commands", "hooks", "backups", "logs"]
for dirname in additional_dirs:
dir_path = self.install_dir / dirname
if not self.file_manager.ensure_directory(dir_path):
self.logger.warning(f"Could not create directory: {dir_path}")
return True
def uninstall(self) -> bool:
"""Uninstall core component"""
try:
self.logger.info("Uninstalling SuperClaude core component...")
# Remove framework files
removed_count = 0
for filename in self.component_files:
file_path = self.install_dir / filename
if self.file_manager.remove_file(file_path):
removed_count += 1
self.logger.debug(f"Removed {filename}")
else:
self.logger.warning(f"Could not remove {filename}")
# Update metadata to remove core component
try:
if self.settings_manager.is_component_installed("core"):
self.settings_manager.remove_component_registration("core")
metadata_mods = self.get_metadata_modifications()
metadata = self.settings_manager.load_metadata()
for key in metadata_mods.keys():
if key in metadata:
del metadata[key]
self.settings_manager.save_metadata(metadata)
self.logger.info("Removed core component from metadata")
except Exception as e:
self.logger.warning(f"Could not update metadata: {e}")
self.logger.success(f"Core component uninstalled ({removed_count} files removed)")
return True
except Exception as e:
self.logger.exception(f"Unexpected error during core uninstallation: {e}")
return False
def get_dependencies(self) -> List[str]:
"""Get component dependencies (core has none)"""
return []
def update(self, config: Dict[str, Any]) -> bool:
"""Update core component"""
try:
self.logger.info("Updating SuperClaude core component...")
# Check current version
current_version = self.settings_manager.get_component_version("core")
target_version = self.get_metadata()["version"]
if current_version == target_version:
self.logger.info(f"Core component already at version {target_version}")
return True
self.logger.info(f"Updating core component from {current_version} to {target_version}")
# Create backup of existing files
backup_files = []
for filename in self.component_files:
file_path = self.install_dir / filename
if file_path.exists():
backup_path = self.file_manager.backup_file(file_path)
if backup_path:
backup_files.append(backup_path)
self.logger.debug(f"Backed up {filename}")
# Perform installation (overwrites existing files)
success = self.install(config)
if success:
# Remove backup files on successful update
for backup_path in backup_files:
try:
backup_path.unlink()
except Exception:
pass # Ignore cleanup errors
self.logger.success(f"Core component updated to version {target_version}")
else:
# Restore from backup on failure
self.logger.warning("Update failed, restoring from backup...")
for backup_path in backup_files:
try:
original_path = backup_path.with_suffix('')
shutil.move(str(backup_path), str(original_path))
self.logger.debug(f"Restored {original_path.name}")
except Exception as e:
self.logger.error(f"Could not restore {backup_path}: {e}")
return success
except Exception as e:
self.logger.exception(f"Unexpected error during core update: {e}")
return False
def validate_installation(self) -> Tuple[bool, List[str]]:
"""Validate core component installation"""
errors = []
# Check if all framework files exist
for filename in self.component_files:
file_path = self.install_dir / filename
if not file_path.exists():
errors.append(f"Missing framework file: {filename}")
elif not file_path.is_file():
errors.append(f"Framework file is not a regular file: {filename}")
# Check metadata registration
if not self.settings_manager.is_component_installed("core"):
errors.append("Core component not registered in metadata")
else:
# Check version matches
installed_version = self.settings_manager.get_component_version("core")
expected_version = self.get_metadata()["version"]
if installed_version != expected_version:
errors.append(f"Version mismatch: installed {installed_version}, expected {expected_version}")
# Check metadata structure
try:
framework_config = self.settings_manager.get_metadata_setting("framework")
if not framework_config:
errors.append("Missing framework configuration in metadata")
else:
required_keys = ["version", "name", "description"]
for key in required_keys:
if key not in framework_config:
errors.append(f"Missing framework.{key} in metadata")
except Exception as e:
errors.append(f"Could not validate metadata: {e}")
return len(errors) == 0, errors
def _get_source_dir(self):
"""Get source directory for framework files"""
# Assume we're in SuperClaude/setup/components/core.py
# and framework files are in SuperClaude/SuperClaude/Core/
project_root = Path(__file__).parent.parent.parent
return project_root / "SuperClaude" / "Core"
def get_size_estimate(self) -> int:
"""Get estimated installation size"""
total_size = 0
source_dir = self._get_source_dir()
for filename in self.component_files:
file_path = source_dir / filename
if file_path.exists():
total_size += file_path.stat().st_size
# Add overhead for settings.json and directories
total_size += 10240 # ~10KB overhead
return total_size
def get_installation_summary(self) -> Dict[str, Any]:
"""Get installation summary"""
return {
"component": self.get_metadata()["name"],
"version": self.get_metadata()["version"],
"files_installed": len(self.component_files),
"framework_files": self.component_files,
"estimated_size": self.get_size_estimate(),
"install_directory": str(self.install_dir),
"dependencies": self.get_dependencies()
}

366
setup/components/hooks.py Normal file
View File

@@ -0,0 +1,366 @@
"""
Hooks component for Claude Code hooks integration (future-ready)
"""
from typing import Dict, List, Tuple, Optional, Any
from pathlib import Path
from ..base.component import Component
class HooksComponent(Component):
"""Claude Code hooks integration component"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize hooks component"""
super().__init__(install_dir, Path("hooks"))
# Define hook files to install (when hooks are ready)
self.hook_files = [
"pre_tool_use.py",
"post_tool_use.py",
"error_handler.py",
"context_accumulator.py",
"performance_monitor.py"
]
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "hooks",
"version": "3.0.0",
"description": "Claude Code hooks integration (future-ready)",
"category": "integration"
}
def get_metadata_modifications(self) -> Dict[str, Any]:
# Build hooks configuration based on available files
hook_config = {}
for filename in self.hook_files:
hook_path = self.install_component_subdir / filename
if hook_path.exists():
hook_name = filename.replace('.py', '')
hook_config[hook_name] = [str(hook_path)]
metadata_mods = {
"components": {
"hooks": {
"version": "3.0.0",
"installed": True,
"files_count": len(hook_config)
}
}
}
# Only add hooks configuration if we have actual hook files
if hook_config:
metadata_mods["hooks"] = {
"enabled": True,
**hook_config
}
return metadata_mods
def _install(self, config: Dict[str, Any]) -> bool:
"""Install hooks component"""
self.logger.info("Installing SuperClaude hooks component...")
# This component is future-ready - hooks aren't implemented yet
source_dir = self._get_source_dir()
if not source_dir.exists() or (source_dir / "PLACEHOLDER.py").exists :
self.logger.info("Hooks are not yet implemented - installing placeholder component")
# Create placeholder hooks directory
if not self.file_manager.ensure_directory(self.install_component_subdir):
self.logger.error(f"Could not create hooks directory: {self.install_component_subdir}")
return False
# Create placeholder file
placeholder_content = '''"""
SuperClaude Hooks - Future Implementation
This directory is reserved for Claude Code hooks integration.
Hooks will provide lifecycle management and automation capabilities.
Planned hooks:
- pre_tool_use: Execute before tool usage
- post_tool_use: Execute after tool completion
- error_handler: Handle tool errors and recovery
- context_accumulator: Manage context across operations
- performance_monitor: Track and optimize performance
For more information, see SuperClaude documentation.
"""
# Placeholder for future hooks implementation
def placeholder_hook():
"""Placeholder hook function"""
pass
'''
placeholder_path = self.install_component_subdir / "PLACEHOLDER.py"
try:
with open(placeholder_path, 'w') as f:
f.write(placeholder_content)
self.logger.debug("Created hooks placeholder file")
except Exception as e:
self.logger.warning(f"Could not create placeholder file: {e}")
# Update settings with placeholder registration
try:
metadata_mods = {
"components": {
"hooks": {
"version": "3.0.0",
"installed": True,
"status": "placeholder",
"files_count": 0
}
}
}
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with hooks component registration")
except Exception as e:
self.logger.error(f"Failed to update metadata for hooks component: {e}")
return False
self.logger.success("Hooks component installed successfully (placeholder)")
return True
# If hooks source directory exists, install actual hooks
self.logger.info("Installing actual hook files...")
# Validate installation
success, errors = self.validate_prerequisites(Path("hooks"))
if not success:
for error in errors:
self.logger.error(error)
return False
# Get files to install
files_to_install = self.get_files_to_install()
if not files_to_install:
self.logger.warning("No hook files found to install")
return False
# Copy hook files
success_count = 0
for source, target in files_to_install:
self.logger.debug(f"Copying {source.name} to {target}")
if self.file_manager.copy_file(source, target):
success_count += 1
self.logger.debug(f"Successfully copied {source.name}")
else:
self.logger.error(f"Failed to copy {source.name}")
if success_count != len(files_to_install):
self.logger.error(f"Only {success_count}/{len(files_to_install)} hook files copied successfully")
return False
self.logger.success(f"Hooks component installed successfully ({success_count} hook files)")
return self._post_install()
def _post_install(self):
# Update metadata
try:
metadata_mods = self.get_metadata_modifications()
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with hooks configuration")
# Add hook registration to metadata
self.settings_manager.add_component_registration("hooks", {
"version": "3.0.0",
"category": "commands",
"files_count": len(self.hook_files)
})
self.logger.info("Updated metadata with commands component registration")
except Exception as e:
self.logger.error(f"Failed to update metadata: {e}")
return False
return True
def uninstall(self) -> bool:
"""Uninstall hooks component"""
try:
self.logger.info("Uninstalling SuperClaude hooks component...")
# Remove hook files and placeholder
removed_count = 0
# Remove actual hook files
for filename in self.hook_files:
file_path = self.install_component_subdir / filename
if self.file_manager.remove_file(file_path):
removed_count += 1
self.logger.debug(f"Removed {filename}")
# Remove placeholder file
placeholder_path = self.install_component_subdir / "PLACEHOLDER.py"
if self.file_manager.remove_file(placeholder_path):
removed_count += 1
self.logger.debug("Removed hooks placeholder")
# Remove hooks directory if empty
try:
if self.install_component_subdir.exists():
remaining_files = list(self.install_component_subdir.iterdir())
if not remaining_files:
self.install_component_subdir.rmdir()
self.logger.debug("Removed empty hooks directory")
except Exception as e:
self.logger.warning(f"Could not remove hooks directory: {e}")
# Update settings.json to remove hooks component and configuration
try:
if self.settings_manager.is_component_installed("hooks"):
self.settings_manager.remove_component_registration("hooks")
# Also remove hooks configuration section if it exists
settings = self.settings_manager.load_settings()
if "hooks" in settings:
del settings["hooks"]
self.settings_manager.save_settings(settings)
self.logger.info("Removed hooks component and configuration from settings.json")
except Exception as e:
self.logger.warning(f"Could not update settings.json: {e}")
self.logger.success(f"Hooks component uninstalled ({removed_count} files removed)")
return True
except Exception as e:
self.logger.exception(f"Unexpected error during hooks uninstallation: {e}")
return False
def get_dependencies(self) -> List[str]:
"""Get dependencies"""
return ["core"]
def update(self, config: Dict[str, Any]) -> bool:
"""Update hooks component"""
try:
self.logger.info("Updating SuperClaude hooks component...")
# Check current version
current_version = self.settings_manager.get_component_version("hooks")
target_version = self.get_metadata()["version"]
if current_version == target_version:
self.logger.info(f"Hooks component already at version {target_version}")
return True
self.logger.info(f"Updating hooks component from {current_version} to {target_version}")
# Create backup of existing hook files
backup_files = []
if self.install_component_subdir.exists():
for filename in self.hook_files + ["PLACEHOLDER.py"]:
file_path = self.install_component_subdir / filename
if file_path.exists():
backup_path = self.file_manager.backup_file(file_path)
if backup_path:
backup_files.append(backup_path)
self.logger.debug(f"Backed up {filename}")
# Perform installation (overwrites existing files)
success = self.install(config)
if success:
# Remove backup files on successful update
for backup_path in backup_files:
try:
backup_path.unlink()
except Exception:
pass # Ignore cleanup errors
self.logger.success(f"Hooks component updated to version {target_version}")
else:
# Restore from backup on failure
self.logger.warning("Update failed, restoring from backup...")
for backup_path in backup_files:
try:
original_path = backup_path.with_suffix('')
backup_path.rename(original_path)
self.logger.debug(f"Restored {original_path.name}")
except Exception as e:
self.logger.error(f"Could not restore {backup_path}: {e}")
return success
except Exception as e:
self.logger.exception(f"Unexpected error during hooks update: {e}")
return False
def validate_installation(self) -> Tuple[bool, List[str]]:
"""Validate hooks component installation"""
errors = []
# Check if hooks directory exists
if not self.install_component_subdir.exists():
errors.append("Hooks directory not found")
return False, errors
# Check settings.json registration
if not self.settings_manager.is_component_installed("hooks"):
errors.append("Hooks component not registered in settings.json")
else:
# Check version matches
installed_version = self.settings_manager.get_component_version("hooks")
expected_version = self.get_metadata()["version"]
if installed_version != expected_version:
errors.append(f"Version mismatch: installed {installed_version}, expected {expected_version}")
# Check if we have either actual hooks or placeholder
has_placeholder = (self.install_component_subdir / "PLACEHOLDER.py").exists()
has_actual_hooks = any((self.install_component_subdir / filename).exists() for filename in self.hook_files)
if not has_placeholder and not has_actual_hooks:
errors.append("No hook files or placeholder found")
return len(errors) == 0, errors
def _get_source_dir(self) -> Path:
"""Get source directory for hook files"""
# Assume we're in SuperClaude/setup/components/hooks.py
# and hook files are in SuperClaude/SuperClaude/Hooks/
project_root = Path(__file__).parent.parent.parent
return project_root / "SuperClaude" / "Hooks"
def get_size_estimate(self) -> int:
"""Get estimated installation size"""
# Estimate based on placeholder or actual files
source_dir = self._get_source_dir()
total_size = 0
if source_dir.exists():
for filename in self.hook_files:
file_path = source_dir / filename
if file_path.exists():
total_size += file_path.stat().st_size
# Add placeholder overhead or minimum size
total_size = max(total_size, 10240) # At least 10KB
return total_size
def get_installation_summary(self) -> Dict[str, Any]:
"""Get installation summary"""
source_dir = self._get_source_dir()
status = "placeholder" if not source_dir.exists() else "implemented"
return {
"component": self.get_metadata()["name"],
"version": self.get_metadata()["version"],
"status": status,
"hook_files": self.hook_files if source_dir.exists() else ["PLACEHOLDER.py"],
"estimated_size": self.get_size_estimate(),
"install_directory": str(self.install_dir / "hooks"),
"dependencies": self.get_dependencies()
}

498
setup/components/mcp.py Normal file
View File

@@ -0,0 +1,498 @@
"""
MCP component for MCP server integration
"""
import subprocess
import sys
from typing import Dict, List, Tuple, Optional, Any
from pathlib import Path
from ..base.component import Component
from ..utils.ui import display_info, display_warning
class MCPComponent(Component):
"""MCP servers integration component"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize MCP component"""
super().__init__(install_dir)
# Define MCP servers to install
self.mcp_servers = {
"sequential-thinking": {
"name": "sequential-thinking",
"description": "Multi-step problem solving and systematic analysis",
"npm_package": "@modelcontextprotocol/server-sequential-thinking",
"required": True
},
"context7": {
"name": "context7",
"description": "Official library documentation and code examples",
"npm_package": "@upstash/context7-mcp",
"required": True
},
"magic": {
"name": "magic",
"description": "Modern UI component generation and design systems",
"npm_package": "@21st-dev/magic",
"required": False,
"api_key_env": "TWENTYFIRST_API_KEY",
"api_key_description": "21st.dev API key for UI component generation"
},
"playwright": {
"name": "playwright",
"description": "Cross-browser E2E testing and automation",
"npm_package": "@playwright/mcp@latest",
"required": False
}
}
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "mcp",
"version": "3.0.0",
"description": "MCP server integration (Context7, Sequential, Magic, Playwright)",
"category": "integration"
}
def validate_prerequisites(self, installSubPath: Optional[Path] = None) -> Tuple[bool, List[str]]:
"""Check prerequisites"""
errors = []
# Check if Node.js is available
try:
result = subprocess.run(
["node", "--version"],
capture_output=True,
text=True,
timeout=10,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
errors.append("Node.js not found - required for MCP servers")
else:
version = result.stdout.strip()
self.logger.debug(f"Found Node.js {version}")
# Check version (require 18+)
try:
version_num = int(version.lstrip('v').split('.')[0])
if version_num < 18:
errors.append(f"Node.js version {version} found, but version 18+ required")
except:
self.logger.warning(f"Could not parse Node.js version: {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("Node.js not found - required for MCP servers")
# Check if Claude CLI is available
try:
result = subprocess.run(
["claude", "--version"],
capture_output=True,
text=True,
timeout=10,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
errors.append("Claude CLI not found - required for MCP server management")
else:
version = result.stdout.strip()
self.logger.debug(f"Found Claude CLI {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("Claude CLI not found - required for MCP server management")
# Check if npm is available
try:
result = subprocess.run(
["npm", "--version"],
capture_output=True,
text=True,
timeout=10,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
errors.append("npm not found - required for MCP server installation")
else:
version = result.stdout.strip()
self.logger.debug(f"Found npm {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("npm not found - required for MCP server installation")
return len(errors) == 0, errors
def get_files_to_install(self) -> List[Tuple[Path, Path]]:
"""Get files to install (none for MCP component)"""
return []
def get_metadata_modifications(self) -> Dict[str, Any]:
"""Get metadata modifications for MCP component"""
return {
"components": {
"mcp": {
"version": "3.0.0",
"installed": True,
"servers_count": len(self.mcp_servers)
}
},
"mcp": {
"enabled": True,
"servers": list(self.mcp_servers.keys()),
"auto_update": False
}
}
def _check_mcp_server_installed(self, server_name: str) -> bool:
"""Check if MCP server is already installed"""
try:
result = subprocess.run(
["claude", "mcp", "list"],
capture_output=True,
text=True,
timeout=15,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
self.logger.warning(f"Could not list MCP servers: {result.stderr}")
return False
# Parse output to check if server is installed
output = result.stdout.lower()
return server_name.lower() in output
except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
self.logger.warning(f"Error checking MCP server status: {e}")
return False
def _install_mcp_server(self, server_info: Dict[str, Any], config: Dict[str, Any]) -> bool:
"""Install a single MCP server"""
server_name = server_info["name"]
npm_package = server_info["npm_package"]
command = "npx"
try:
self.logger.info(f"Installing MCP server: {server_name}")
# Check if already installed
if self._check_mcp_server_installed(server_name):
self.logger.info(f"MCP server {server_name} already installed")
return True
# Handle API key requirements
if "api_key_env" in server_info:
api_key_env = server_info["api_key_env"]
api_key_desc = server_info.get("api_key_description", f"API key for {server_name}")
if not config.get("dry_run", False):
display_info(f"MCP server '{server_name}' requires an API key")
display_info(f"Environment variable: {api_key_env}")
display_info(f"Description: {api_key_desc}")
# Check if API key is already set
import os
if not os.getenv(api_key_env):
display_warning(f"API key {api_key_env} not found in environment")
self.logger.warning(f"Proceeding without {api_key_env} - server may not function properly")
# Install using Claude CLI
if config.get("dry_run"):
self.logger.info(f"Would install MCP server (user scope): claude mcp add -s user {server_name} {command} -y {npm_package}")
return True
self.logger.debug(f"Running: claude mcp add -s user {server_name} {command} -y {npm_package}")
result = subprocess.run(
["claude", "mcp", "add", "-s", "user", "--", server_name, command, "-y", npm_package],
capture_output=True,
text=True,
timeout=120, # 2 minutes timeout for installation
shell=(sys.platform == "win32")
)
if result.returncode == 0:
self.logger.success(f"Successfully installed MCP server (user scope): {server_name}")
return True
else:
error_msg = result.stderr.strip() if result.stderr else "Unknown error"
self.logger.error(f"Failed to install MCP server {server_name}: {error_msg}")
return False
except subprocess.TimeoutExpired:
self.logger.error(f"Timeout installing MCP server {server_name}")
return False
except Exception as e:
self.logger.error(f"Error installing MCP server {server_name}: {e}")
return False
def _uninstall_mcp_server(self, server_name: str) -> bool:
"""Uninstall a single MCP server"""
try:
self.logger.info(f"Uninstalling MCP server: {server_name}")
# Check if installed
if not self._check_mcp_server_installed(server_name):
self.logger.info(f"MCP server {server_name} not installed")
return True
self.logger.debug(f"Running: claude mcp remove {server_name} (auto-detect scope)")
result = subprocess.run(
["claude", "mcp", "remove", server_name],
capture_output=True,
text=True,
timeout=60,
shell=(sys.platform == "win32")
)
if result.returncode == 0:
self.logger.success(f"Successfully uninstalled MCP server: {server_name}")
return True
else:
error_msg = result.stderr.strip() if result.stderr else "Unknown error"
self.logger.error(f"Failed to uninstall MCP server {server_name}: {error_msg}")
return False
except subprocess.TimeoutExpired:
self.logger.error(f"Timeout uninstalling MCP server {server_name}")
return False
except Exception as e:
self.logger.error(f"Error uninstalling MCP server {server_name}: {e}")
return False
def _install(self, config: Dict[str, Any]) -> bool:
"""Install MCP component"""
self.logger.info("Installing SuperClaude MCP servers...")
# Validate prerequisites
success, errors = self.validate_prerequisites()
if not success:
for error in errors:
self.logger.error(error)
return False
# Install each MCP server
installed_count = 0
failed_servers = []
for server_name, server_info in self.mcp_servers.items():
if self._install_mcp_server(server_info, config):
installed_count += 1
else:
failed_servers.append(server_name)
# Check if this is a required server
if server_info.get("required", False):
self.logger.error(f"Required MCP server {server_name} failed to install")
return False
# Verify installation
if not config.get("dry_run", False):
self.logger.info("Verifying MCP server installation...")
try:
result = subprocess.run(
["claude", "mcp", "list"],
capture_output=True,
text=True,
timeout=15,
shell=(sys.platform == "win32")
)
if result.returncode == 0:
self.logger.debug("MCP servers list:")
for line in result.stdout.strip().split('\n'):
if line.strip():
self.logger.debug(f" {line.strip()}")
else:
self.logger.warning("Could not verify MCP server installation")
except Exception as e:
self.logger.warning(f"Could not verify MCP installation: {e}")
if failed_servers:
self.logger.warning(f"Some MCP servers failed to install: {failed_servers}")
self.logger.success(f"MCP component partially installed ({installed_count} servers)")
else:
self.logger.success(f"MCP component installed successfully ({installed_count} servers)")
return self._post_install()
def _post_install(self) -> bool:
# Update metadata
try:
metadata_mods = self.get_metadata_modifications()
self.settings_manager.update_metadata(metadata_mods)
# Add component registration to metadata
self.settings_manager.add_component_registration("mcp", {
"version": "3.0.0",
"category": "integration",
"servers_count": len(self.mcp_servers)
})
self.logger.info("Updated metadata with MCP component registration")
except Exception as e:
self.logger.error(f"Failed to update metadata: {e}")
return False
return True
def uninstall(self) -> bool:
"""Uninstall MCP component"""
try:
self.logger.info("Uninstalling SuperClaude MCP servers...")
# Uninstall each MCP server
uninstalled_count = 0
for server_name in self.mcp_servers.keys():
if self._uninstall_mcp_server(server_name):
uninstalled_count += 1
# Update metadata to remove MCP component
try:
if self.settings_manager.is_component_installed("mcp"):
self.settings_manager.remove_component_registration("mcp")
# Also remove MCP configuration from metadata
metadata = self.settings_manager.load_metadata()
if "mcp" in metadata:
del metadata["mcp"]
self.settings_manager.save_metadata(metadata)
self.logger.info("Removed MCP component from metadata")
except Exception as e:
self.logger.warning(f"Could not update metadata: {e}")
self.logger.success(f"MCP component uninstalled ({uninstalled_count} servers removed)")
return True
except Exception as e:
self.logger.exception(f"Unexpected error during MCP uninstallation: {e}")
return False
def get_dependencies(self) -> List[str]:
"""Get dependencies"""
return ["core"]
def update(self, config: Dict[str, Any]) -> bool:
"""Update MCP component"""
try:
self.logger.info("Updating SuperClaude MCP servers...")
# Check current version
current_version = self.settings_manager.get_component_version("mcp")
target_version = self.get_metadata()["version"]
if current_version == target_version:
self.logger.info(f"MCP component already at version {target_version}")
return True
self.logger.info(f"Updating MCP component from {current_version} to {target_version}")
# For MCP servers, update means reinstall to get latest versions
updated_count = 0
failed_servers = []
for server_name, server_info in self.mcp_servers.items():
try:
# Uninstall old version
if self._check_mcp_server_installed(server_name):
self._uninstall_mcp_server(server_name)
# Install new version
if self._install_mcp_server(server_info, config):
updated_count += 1
else:
failed_servers.append(server_name)
except Exception as e:
self.logger.error(f"Error updating MCP server {server_name}: {e}")
failed_servers.append(server_name)
# Update metadata
try:
# Update component version in metadata
metadata = self.settings_manager.load_metadata()
if "components" in metadata and "mcp" in metadata["components"]:
metadata["components"]["mcp"]["version"] = target_version
metadata["components"]["mcp"]["servers_count"] = len(self.mcp_servers)
if "mcp" in metadata:
metadata["mcp"]["servers"] = list(self.mcp_servers.keys())
self.settings_manager.save_metadata(metadata)
except Exception as e:
self.logger.warning(f"Could not update metadata: {e}")
if failed_servers:
self.logger.warning(f"Some MCP servers failed to update: {failed_servers}")
return False
else:
self.logger.success(f"MCP component updated to version {target_version}")
return True
except Exception as e:
self.logger.exception(f"Unexpected error during MCP update: {e}")
return False
def validate_installation(self) -> Tuple[bool, List[str]]:
"""Validate MCP component installation"""
errors = []
# Check metadata registration
if not self.settings_manager.is_component_installed("mcp"):
errors.append("MCP component not registered in metadata")
return False, errors
# Check version matches
installed_version = self.settings_manager.get_component_version("mcp")
expected_version = self.get_metadata()["version"]
if installed_version != expected_version:
errors.append(f"Version mismatch: installed {installed_version}, expected {expected_version}")
# Check if Claude CLI is available
try:
result = subprocess.run(
["claude", "mcp", "list"],
capture_output=True,
text=True,
timeout=15,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
errors.append("Could not communicate with Claude CLI for MCP server verification")
else:
# Check if required servers are installed
output = result.stdout.lower()
for server_name, server_info in self.mcp_servers.items():
if server_info.get("required", False):
if server_name.lower() not in output:
errors.append(f"Required MCP server not found: {server_name}")
except Exception as e:
errors.append(f"Could not verify MCP server installation: {e}")
return len(errors) == 0, errors
def _get_source_dir(self):
"""Get source directory for framework files"""
return None
def get_size_estimate(self) -> int:
"""Get estimated installation size"""
# MCP servers are installed via npm, estimate based on typical sizes
base_size = 50 * 1024 * 1024 # ~50MB for all servers combined
return base_size
def get_installation_summary(self) -> Dict[str, Any]:
"""Get installation summary"""
return {
"component": self.get_metadata()["name"],
"version": self.get_metadata()["version"],
"servers_count": len(self.mcp_servers),
"mcp_servers": list(self.mcp_servers.keys()),
"estimated_size": self.get_size_estimate(),
"dependencies": self.get_dependencies(),
"required_tools": ["node", "npm", "claude"]
}

9
setup/core/__init__.py Normal file
View File

@@ -0,0 +1,9 @@
"""Core modules for SuperClaude installation system"""
from .validator import Validator
from .registry import ComponentRegistry
__all__ = [
'Validator',
'ComponentRegistry'
]

395
setup/core/registry.py Normal file
View File

@@ -0,0 +1,395 @@
"""
Component registry for auto-discovery and dependency resolution
"""
import importlib
import inspect
from typing import Dict, List, Set, Optional, Type
from pathlib import Path
from ..base.component import Component
class ComponentRegistry:
"""Auto-discovery and management of installable components"""
def __init__(self, components_dir: Path):
"""
Initialize component registry
Args:
components_dir: Directory containing component modules
"""
self.components_dir = components_dir
self.component_classes: Dict[str, Type[Component]] = {}
self.component_instances: Dict[str, Component] = {}
self.dependency_graph: Dict[str, Set[str]] = {}
self._discovered = False
def discover_components(self, force_reload: bool = False) -> None:
"""
Auto-discover all component classes in components directory
Args:
force_reload: Force rediscovery even if already done
"""
if self._discovered and not force_reload:
return
self.component_classes.clear()
self.component_instances.clear()
self.dependency_graph.clear()
if not self.components_dir.exists():
return
# Add components directory to Python path temporarily
import sys
original_path = sys.path.copy()
try:
# Add parent directory to path so we can import setup.components
setup_dir = self.components_dir.parent
if str(setup_dir) not in sys.path:
sys.path.insert(0, str(setup_dir))
# Discover all Python files in components directory
for py_file in self.components_dir.glob("*.py"):
if py_file.name.startswith("__"):
continue
module_name = py_file.stem
self._load_component_module(module_name)
finally:
# Restore original Python path
sys.path = original_path
# Build dependency graph
self._build_dependency_graph()
self._discovered = True
def _load_component_module(self, module_name: str) -> None:
"""
Load component classes from a module
Args:
module_name: Name of module to load
"""
try:
# Import the module
full_module_name = f"setup.components.{module_name}"
module = importlib.import_module(full_module_name)
# Find all Component subclasses in the module
for name, obj in inspect.getmembers(module):
if (inspect.isclass(obj) and
issubclass(obj, Component) and
obj is not Component):
# Create instance to get metadata
try:
instance = obj()
metadata = instance.get_metadata()
component_name = metadata["name"]
self.component_classes[component_name] = obj
self.component_instances[component_name] = instance
except Exception as e:
print(f"Warning: Could not instantiate component {name}: {e}")
except Exception as e:
print(f"Warning: Could not load component module {module_name}: {e}")
def _build_dependency_graph(self) -> None:
"""Build dependency graph for all discovered components"""
for name, instance in self.component_instances.items():
try:
dependencies = instance.get_dependencies()
self.dependency_graph[name] = set(dependencies)
except Exception as e:
print(f"Warning: Could not get dependencies for {name}: {e}")
self.dependency_graph[name] = set()
def get_component_class(self, component_name: str) -> Optional[Type[Component]]:
"""
Get component class by name
Args:
component_name: Name of component
Returns:
Component class or None if not found
"""
self.discover_components()
return self.component_classes.get(component_name)
def get_component_instance(self, component_name: str, install_dir: Optional[Path] = None) -> Optional[Component]:
"""
Get component instance by name
Args:
component_name: Name of component
install_dir: Installation directory (creates new instance with this dir)
Returns:
Component instance or None if not found
"""
self.discover_components()
if install_dir is not None:
# Create new instance with specified install directory
component_class = self.component_classes.get(component_name)
if component_class:
try:
return component_class(install_dir)
except Exception as e:
print(f"Error creating component instance {component_name}: {e}")
return None
return self.component_instances.get(component_name)
def list_components(self) -> List[str]:
"""
Get list of all discovered component names
Returns:
List of component names
"""
self.discover_components()
return list(self.component_classes.keys())
def get_component_metadata(self, component_name: str) -> Optional[Dict[str, str]]:
"""
Get metadata for a component
Args:
component_name: Name of component
Returns:
Component metadata dict or None if not found
"""
self.discover_components()
instance = self.component_instances.get(component_name)
if instance:
try:
return instance.get_metadata()
except Exception:
return None
return None
def resolve_dependencies(self, component_names: List[str]) -> List[str]:
"""
Resolve component dependencies in correct installation order
Args:
component_names: List of component names to install
Returns:
Ordered list of component names including dependencies
Raises:
ValueError: If circular dependencies detected or unknown component
"""
self.discover_components()
resolved = []
resolving = set()
def resolve(name: str):
if name in resolved:
return
if name in resolving:
raise ValueError(f"Circular dependency detected involving {name}")
if name not in self.dependency_graph:
raise ValueError(f"Unknown component: {name}")
resolving.add(name)
# Resolve dependencies first
for dep in self.dependency_graph[name]:
resolve(dep)
resolving.remove(name)
resolved.append(name)
# Resolve each requested component
for name in component_names:
resolve(name)
return resolved
def get_dependencies(self, component_name: str) -> Set[str]:
"""
Get direct dependencies for a component
Args:
component_name: Name of component
Returns:
Set of dependency component names
"""
self.discover_components()
return self.dependency_graph.get(component_name, set())
def get_dependents(self, component_name: str) -> Set[str]:
"""
Get components that depend on the given component
Args:
component_name: Name of component
Returns:
Set of component names that depend on this component
"""
self.discover_components()
dependents = set()
for name, deps in self.dependency_graph.items():
if component_name in deps:
dependents.add(name)
return dependents
def validate_dependency_graph(self) -> List[str]:
"""
Validate dependency graph for cycles and missing dependencies
Returns:
List of validation errors (empty if valid)
"""
self.discover_components()
errors = []
# Check for missing dependencies
all_components = set(self.dependency_graph.keys())
for name, deps in self.dependency_graph.items():
missing_deps = deps - all_components
if missing_deps:
errors.append(f"Component {name} has missing dependencies: {missing_deps}")
# Check for circular dependencies
for name in all_components:
try:
self.resolve_dependencies([name])
except ValueError as e:
errors.append(str(e))
return errors
def get_components_by_category(self, category: str) -> List[str]:
"""
Get components filtered by category
Args:
category: Component category to filter by
Returns:
List of component names in the category
"""
self.discover_components()
components = []
for name, instance in self.component_instances.items():
try:
metadata = instance.get_metadata()
if metadata.get("category") == category:
components.append(name)
except Exception:
continue
return components
def get_installation_order(self, component_names: List[str]) -> List[List[str]]:
"""
Get installation order grouped by dependency levels
Args:
component_names: List of component names to install
Returns:
List of lists, where each inner list contains components
that can be installed in parallel at that dependency level
"""
self.discover_components()
# Get all components including dependencies
all_components = set(self.resolve_dependencies(component_names))
# Group by dependency level
levels = []
remaining = all_components.copy()
while remaining:
# Find components with no unresolved dependencies
current_level = []
for name in list(remaining):
deps = self.dependency_graph.get(name, set())
unresolved_deps = deps & remaining
if not unresolved_deps:
current_level.append(name)
if not current_level:
# This shouldn't happen if dependency graph is valid
raise ValueError("Circular dependency detected in installation order calculation")
levels.append(current_level)
remaining -= set(current_level)
return levels
def create_component_instances(self, component_names: List[str], install_dir: Optional[Path] = None) -> Dict[str, Component]:
"""
Create instances for multiple components
Args:
component_names: List of component names
install_dir: Installation directory for instances
Returns:
Dict mapping component names to instances
"""
self.discover_components()
instances = {}
for name in component_names:
instance = self.get_component_instance(name, install_dir)
if instance:
instances[name] = instance
else:
print(f"Warning: Could not create instance for component {name}")
return instances
def get_registry_info(self) -> Dict[str, any]:
"""
Get comprehensive registry information
Returns:
Dict with registry statistics and component info
"""
self.discover_components()
# Group components by category
categories = {}
for name, instance in self.component_instances.items():
try:
metadata = instance.get_metadata()
category = metadata.get("category", "unknown")
if category not in categories:
categories[category] = []
categories[category].append(name)
except Exception:
if "unknown" not in categories:
categories["unknown"] = []
categories["unknown"].append(name)
return {
"total_components": len(self.component_classes),
"categories": categories,
"dependency_graph": {name: list(deps) for name, deps in self.dependency_graph.items()},
"validation_errors": self.validate_dependency_graph()
}

685
setup/core/validator.py Normal file
View File

@@ -0,0 +1,685 @@
"""
System validation for SuperClaude installation requirements
"""
import subprocess
import sys
import shutil
from typing import Tuple, List, Dict, Any, Optional
from pathlib import Path
import re
# Handle packaging import - if not available, use a simple version comparison
try:
from packaging import version
PACKAGING_AVAILABLE = True
except ImportError:
PACKAGING_AVAILABLE = False
class SimpleVersion:
def __init__(self, version_str: str):
self.version_str = version_str
# Simple version parsing: split by dots and convert to integers
try:
self.parts = [int(x) for x in version_str.split('.')]
except ValueError:
self.parts = [0, 0, 0]
def __lt__(self, other):
if isinstance(other, str):
other = SimpleVersion(other)
# Pad with zeros to same length
max_len = max(len(self.parts), len(other.parts))
self_parts = self.parts + [0] * (max_len - len(self.parts))
other_parts = other.parts + [0] * (max_len - len(other.parts))
return self_parts < other_parts
def __gt__(self, other):
if isinstance(other, str):
other = SimpleVersion(other)
return not (self < other) and not (self == other)
def __eq__(self, other):
if isinstance(other, str):
other = SimpleVersion(other)
return self.parts == other.parts
class version:
@staticmethod
def parse(version_str: str):
return SimpleVersion(version_str)
class Validator:
"""System requirements validator"""
def __init__(self):
"""Initialize validator"""
self.validation_cache: Dict[str, Any] = {}
def check_python(self, min_version: str = "3.8", max_version: Optional[str] = None) -> Tuple[bool, str]:
"""
Check Python version requirements
Args:
min_version: Minimum required Python version
max_version: Maximum supported Python version (optional)
Returns:
Tuple of (success: bool, message: str)
"""
cache_key = f"python_{min_version}_{max_version}"
if cache_key in self.validation_cache:
return self.validation_cache[cache_key]
try:
# Get current Python version
current_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
# Check minimum version
if version.parse(current_version) < version.parse(min_version):
help_msg = self.get_installation_help("python")
result = (False, f"Python {min_version}+ required, found {current_version}{help_msg}")
self.validation_cache[cache_key] = result
return result
# Check maximum version if specified
if max_version and version.parse(current_version) > version.parse(max_version):
result = (False, f"Python version {current_version} exceeds maximum supported {max_version}")
self.validation_cache[cache_key] = result
return result
result = (True, f"Python {current_version} meets requirements")
self.validation_cache[cache_key] = result
return result
except Exception as e:
result = (False, f"Could not check Python version: {e}")
self.validation_cache[cache_key] = result
return result
def check_node(self, min_version: str = "16.0", max_version: Optional[str] = None) -> Tuple[bool, str]:
"""
Check Node.js version requirements
Args:
min_version: Minimum required Node.js version
max_version: Maximum supported Node.js version (optional)
Returns:
Tuple of (success: bool, message: str)
"""
cache_key = f"node_{min_version}_{max_version}"
if cache_key in self.validation_cache:
return self.validation_cache[cache_key]
try:
# Check if node is installed - use shell=True on Windows for better PATH resolution
result = subprocess.run(
['node', '--version'],
capture_output=True,
text=True,
timeout=10,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
help_msg = self.get_installation_help("node")
result_tuple = (False, f"Node.js not found in PATH{help_msg}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
# Parse version (format: v18.17.0)
version_output = result.stdout.strip()
if version_output.startswith('v'):
current_version = version_output[1:]
else:
current_version = version_output
# Check minimum version
if version.parse(current_version) < version.parse(min_version):
help_msg = self.get_installation_help("node")
result_tuple = (False, f"Node.js {min_version}+ required, found {current_version}{help_msg}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
# Check maximum version if specified
if max_version and version.parse(current_version) > version.parse(max_version):
result_tuple = (False, f"Node.js version {current_version} exceeds maximum supported {max_version}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
result_tuple = (True, f"Node.js {current_version} meets requirements")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except subprocess.TimeoutExpired:
result_tuple = (False, "Node.js version check timed out")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except FileNotFoundError:
help_msg = self.get_installation_help("node")
result_tuple = (False, f"Node.js not found in PATH{help_msg}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except Exception as e:
result_tuple = (False, f"Could not check Node.js version: {e}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
def check_claude_cli(self, min_version: Optional[str] = None) -> Tuple[bool, str]:
"""
Check Claude CLI installation and version
Args:
min_version: Minimum required Claude CLI version (optional)
Returns:
Tuple of (success: bool, message: str)
"""
cache_key = f"claude_cli_{min_version}"
if cache_key in self.validation_cache:
return self.validation_cache[cache_key]
try:
# Check if claude is installed - use shell=True on Windows for better PATH resolution
result = subprocess.run(
['claude', '--version'],
capture_output=True,
text=True,
timeout=10,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
help_msg = self.get_installation_help("claude_cli")
result_tuple = (False, f"Claude CLI not found in PATH{help_msg}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
# Parse version from output
version_output = result.stdout.strip()
version_match = re.search(r'(\d+\.\d+\.\d+)', version_output)
if not version_match:
result_tuple = (True, "Claude CLI found (version format unknown)")
self.validation_cache[cache_key] = result_tuple
return result_tuple
current_version = version_match.group(1)
# Check minimum version if specified
if min_version and version.parse(current_version) < version.parse(min_version):
result_tuple = (False, f"Claude CLI {min_version}+ required, found {current_version}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
result_tuple = (True, f"Claude CLI {current_version} found")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except subprocess.TimeoutExpired:
result_tuple = (False, "Claude CLI version check timed out")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except FileNotFoundError:
help_msg = self.get_installation_help("claude_cli")
result_tuple = (False, f"Claude CLI not found in PATH{help_msg}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except Exception as e:
result_tuple = (False, f"Could not check Claude CLI: {e}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
def check_external_tool(self, tool_name: str, command: str, min_version: Optional[str] = None) -> Tuple[bool, str]:
"""
Check external tool availability and version
Args:
tool_name: Display name of tool
command: Command to check version
min_version: Minimum required version (optional)
Returns:
Tuple of (success: bool, message: str)
"""
cache_key = f"tool_{tool_name}_{command}_{min_version}"
if cache_key in self.validation_cache:
return self.validation_cache[cache_key]
try:
# Split command into parts
cmd_parts = command.split()
result = subprocess.run(
cmd_parts,
capture_output=True,
text=True,
timeout=10,
shell=(sys.platform == "win32")
)
if result.returncode != 0:
result_tuple = (False, f"{tool_name} not found or command failed")
self.validation_cache[cache_key] = result_tuple
return result_tuple
# Extract version if min_version specified
if min_version:
version_output = result.stdout + result.stderr
version_match = re.search(r'(\d+\.\d+(?:\.\d+)?)', version_output)
if version_match:
current_version = version_match.group(1)
if version.parse(current_version) < version.parse(min_version):
result_tuple = (False, f"{tool_name} {min_version}+ required, found {current_version}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
result_tuple = (True, f"{tool_name} {current_version} found")
self.validation_cache[cache_key] = result_tuple
return result_tuple
else:
result_tuple = (True, f"{tool_name} found (version unknown)")
self.validation_cache[cache_key] = result_tuple
return result_tuple
else:
result_tuple = (True, f"{tool_name} found")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except subprocess.TimeoutExpired:
result_tuple = (False, f"{tool_name} check timed out")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except FileNotFoundError:
result_tuple = (False, f"{tool_name} not found in PATH")
self.validation_cache[cache_key] = result_tuple
return result_tuple
except Exception as e:
result_tuple = (False, f"Could not check {tool_name}: {e}")
self.validation_cache[cache_key] = result_tuple
return result_tuple
def check_disk_space(self, path: Path, required_mb: int = 500) -> Tuple[bool, str]:
"""
Check available disk space
Args:
path: Path to check (file or directory)
required_mb: Required free space in MB
Returns:
Tuple of (success: bool, message: str)
"""
cache_key = f"disk_{path}_{required_mb}"
if cache_key in self.validation_cache:
return self.validation_cache[cache_key]
try:
# Get parent directory if path is a file
check_path = path.parent if path.is_file() else path
# Get disk usage
stat_result = shutil.disk_usage(check_path)
free_mb = stat_result.free / (1024 * 1024)
if free_mb < required_mb:
result = (False, f"Insufficient disk space: {free_mb:.1f}MB free, {required_mb}MB required")
else:
result = (True, f"Sufficient disk space: {free_mb:.1f}MB free")
self.validation_cache[cache_key] = result
return result
except Exception as e:
result = (False, f"Could not check disk space: {e}")
self.validation_cache[cache_key] = result
return result
def check_write_permissions(self, path: Path) -> Tuple[bool, str]:
"""
Check write permissions for path
Args:
path: Path to check
Returns:
Tuple of (success: bool, message: str)
"""
cache_key = f"write_{path}"
if cache_key in self.validation_cache:
return self.validation_cache[cache_key]
try:
# Create parent directories if needed
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
# Test write access
test_file = path / ".write_test"
test_file.touch()
test_file.unlink()
result = (True, f"Write access confirmed for {path}")
self.validation_cache[cache_key] = result
return result
except Exception as e:
result = (False, f"No write access to {path}: {e}")
self.validation_cache[cache_key] = result
return result
def validate_requirements(self, requirements: Dict[str, Any]) -> Tuple[bool, List[str]]:
"""
Validate all system requirements
Args:
requirements: Requirements configuration dict
Returns:
Tuple of (all_passed: bool, error_messages: List[str])
"""
errors = []
# Check Python requirements
if "python" in requirements:
python_req = requirements["python"]
success, message = self.check_python(
python_req["min_version"],
python_req.get("max_version")
)
if not success:
errors.append(f"Python: {message}")
# Check Node.js requirements
if "node" in requirements:
node_req = requirements["node"]
success, message = self.check_node(
node_req["min_version"],
node_req.get("max_version")
)
if not success:
errors.append(f"Node.js: {message}")
# Check disk space
if "disk_space_mb" in requirements:
success, message = self.check_disk_space(
Path.home(),
requirements["disk_space_mb"]
)
if not success:
errors.append(f"Disk space: {message}")
# Check external tools
if "external_tools" in requirements:
for tool_name, tool_req in requirements["external_tools"].items():
# Skip optional tools that fail
is_optional = tool_req.get("optional", False)
success, message = self.check_external_tool(
tool_name,
tool_req["command"],
tool_req.get("min_version")
)
if not success and not is_optional:
errors.append(f"{tool_name}: {message}")
return len(errors) == 0, errors
def validate_component_requirements(self, component_names: List[str], all_requirements: Dict[str, Any]) -> Tuple[bool, List[str]]:
"""
Validate requirements for specific components
Args:
component_names: List of component names to validate
all_requirements: Full requirements configuration
Returns:
Tuple of (all_passed: bool, error_messages: List[str])
"""
errors = []
# Start with base requirements
base_requirements = {
"python": all_requirements.get("python", {}),
"disk_space_mb": all_requirements.get("disk_space_mb", 500)
}
# Add conditional requirements based on components
external_tools = {}
# Check if any component needs Node.js
node_components = []
for component in component_names:
# This would be enhanced with actual component metadata
if component in ["mcp"]: # MCP component needs Node.js
node_components.append(component)
if node_components and "node" in all_requirements:
base_requirements["node"] = all_requirements["node"]
# Add external tools needed by components
if "external_tools" in all_requirements:
for tool_name, tool_req in all_requirements["external_tools"].items():
required_for = tool_req.get("required_for", [])
# Check if any of our components need this tool
if any(comp in required_for for comp in component_names):
external_tools[tool_name] = tool_req
if external_tools:
base_requirements["external_tools"] = external_tools
# Validate consolidated requirements
return self.validate_requirements(base_requirements)
def get_system_info(self) -> Dict[str, Any]:
"""
Get comprehensive system information
Returns:
Dict with system information
"""
info = {
"platform": sys.platform,
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
"python_executable": sys.executable
}
# Add Node.js info if available
node_success, node_msg = self.check_node()
info["node_available"] = node_success
if node_success:
info["node_message"] = node_msg
# Add Claude CLI info if available
claude_success, claude_msg = self.check_claude_cli()
info["claude_cli_available"] = claude_success
if claude_success:
info["claude_cli_message"] = claude_msg
# Add disk space info
try:
home_path = Path.home()
stat_result = shutil.disk_usage(home_path)
info["disk_space"] = {
"total_gb": stat_result.total / (1024**3),
"free_gb": stat_result.free / (1024**3),
"used_gb": (stat_result.total - stat_result.free) / (1024**3)
}
except Exception:
info["disk_space"] = {"error": "Could not determine disk space"}
return info
def get_platform(self) -> str:
"""
Get current platform for installation commands
Returns:
Platform string (linux, darwin, win32)
"""
return sys.platform
def load_installation_commands(self) -> Dict[str, Any]:
"""
Load installation commands from requirements configuration
Returns:
Installation commands dict
"""
try:
from ..managers.config_manager import ConfigManager
from .. import PROJECT_ROOT
config_manager = ConfigManager(PROJECT_ROOT / "config")
requirements = config_manager.load_requirements()
return requirements.get("installation_commands", {})
except Exception:
return {}
def get_installation_help(self, tool_name: str, platform: Optional[str] = None) -> str:
"""
Get installation help for a specific tool
Args:
tool_name: Name of tool to get help for
platform: Target platform (auto-detected if None)
Returns:
Installation help string
"""
if platform is None:
platform = self.get_platform()
commands = self.load_installation_commands()
tool_commands = commands.get(tool_name, {})
if not tool_commands:
return f"No installation instructions available for {tool_name}"
# Get platform-specific command or fallback to 'all'
install_cmd = tool_commands.get(platform, tool_commands.get("all", ""))
description = tool_commands.get("description", "")
if install_cmd:
help_text = f"\n💡 Installation Help for {tool_name}:\n"
if description:
help_text += f" {description}\n"
help_text += f" Command: {install_cmd}\n"
return help_text
return f"No installation instructions available for {tool_name} on {platform}"
def diagnose_system(self) -> Dict[str, Any]:
"""
Perform comprehensive system diagnostics
Returns:
Diagnostic information dict
"""
diagnostics = {
"platform": self.get_platform(),
"checks": {},
"issues": [],
"recommendations": []
}
# Check Python
python_success, python_msg = self.check_python()
diagnostics["checks"]["python"] = {
"status": "pass" if python_success else "fail",
"message": python_msg
}
if not python_success:
diagnostics["issues"].append("Python version issue")
diagnostics["recommendations"].append(self.get_installation_help("python"))
# Check Node.js
node_success, node_msg = self.check_node()
diagnostics["checks"]["node"] = {
"status": "pass" if node_success else "fail",
"message": node_msg
}
if not node_success:
diagnostics["issues"].append("Node.js not found or version issue")
diagnostics["recommendations"].append(self.get_installation_help("node"))
# Check Claude CLI
claude_success, claude_msg = self.check_claude_cli()
diagnostics["checks"]["claude_cli"] = {
"status": "pass" if claude_success else "fail",
"message": claude_msg
}
if not claude_success:
diagnostics["issues"].append("Claude CLI not found")
diagnostics["recommendations"].append(self.get_installation_help("claude_cli"))
# Check disk space
disk_success, disk_msg = self.check_disk_space(Path.home())
diagnostics["checks"]["disk_space"] = {
"status": "pass" if disk_success else "fail",
"message": disk_msg
}
if not disk_success:
diagnostics["issues"].append("Insufficient disk space")
# Check common PATH issues
self._diagnose_path_issues(diagnostics)
return diagnostics
def _diagnose_path_issues(self, diagnostics: Dict[str, Any]) -> None:
"""Add PATH-related diagnostics"""
path_issues = []
# Check if tools are in PATH, with alternatives for some tools
tool_checks = [
# For Python, check if either python3 OR python is available
(["python3", "python"], "Python (python3 or python)"),
(["node"], "Node.js"),
(["npm"], "npm"),
(["claude"], "Claude CLI")
]
for tool_alternatives, display_name in tool_checks:
tool_found = False
for tool in tool_alternatives:
try:
result = subprocess.run(
["which" if sys.platform != "win32" else "where", tool],
capture_output=True,
text=True,
timeout=5,
shell=(sys.platform == "win32")
)
if result.returncode == 0:
tool_found = True
break
except Exception:
continue
if not tool_found:
# Only report as missing if none of the alternatives were found
if len(tool_alternatives) > 1:
path_issues.append(f"{display_name} not found in PATH")
else:
path_issues.append(f"{tool_alternatives[0]} not found in PATH")
if path_issues:
diagnostics["issues"].extend(path_issues)
diagnostics["recommendations"].append(
"\n💡 PATH Issue Help:\n"
" Some tools may not be in your PATH. Try:\n"
" - Restart your terminal after installation\n"
" - Check your shell configuration (.bashrc, .zshrc)\n"
" - Use full paths to tools if needed\n"
)
def clear_cache(self) -> None:
"""Clear validation cache"""
self.validation_cache.clear()

View File

@@ -0,0 +1,9 @@
from .config_manager import ConfigManager
from .settings_manager import SettingsManager
from .file_manager import FileManager
__all__ = [
'ConfigManager',
'SettingsManager',
'FileManager'
]

View File

@@ -0,0 +1,399 @@
"""
Configuration management for SuperClaude installation system
"""
import json
from typing import Dict, Any, List, Optional
from pathlib import Path
# Handle jsonschema import - if not available, use basic validation
try:
import jsonschema
from jsonschema import validate, ValidationError
JSONSCHEMA_AVAILABLE = True
except ImportError:
JSONSCHEMA_AVAILABLE = False
class ValidationError(Exception):
"""Simple validation error for when jsonschema is not available"""
def __init__(self, message):
self.message = message
super().__init__(message)
def validate(instance, schema):
"""Dummy validation function"""
# Basic type checking only
if "type" in schema:
expected_type = schema["type"]
if expected_type == "object" and not isinstance(instance, dict):
raise ValidationError(f"Expected object, got {type(instance).__name__}")
elif expected_type == "array" and not isinstance(instance, list):
raise ValidationError(f"Expected array, got {type(instance).__name__}")
elif expected_type == "string" and not isinstance(instance, str):
raise ValidationError(f"Expected string, got {type(instance).__name__}")
elif expected_type == "integer" and not isinstance(instance, int):
raise ValidationError(f"Expected integer, got {type(instance).__name__}")
# Skip detailed validation if jsonschema not available
class ConfigManager:
"""Manages configuration files and validation"""
def __init__(self, config_dir: Path):
"""
Initialize config manager
Args:
config_dir: Directory containing configuration files
"""
self.config_dir = config_dir
self.features_file = config_dir / "features.json"
self.requirements_file = config_dir / "requirements.json"
self._features_cache = None
self._requirements_cache = None
# Schema for features.json
self.features_schema = {
"type": "object",
"properties": {
"components": {
"type": "object",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
"type": "object",
"properties": {
"name": {"type": "string"},
"version": {"type": "string"},
"description": {"type": "string"},
"category": {"type": "string"},
"dependencies": {
"type": "array",
"items": {"type": "string"}
},
"enabled": {"type": "boolean"},
"required_tools": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["name", "version", "description", "category"],
"additionalProperties": False
}
}
}
},
"required": ["components"],
"additionalProperties": False
}
# Schema for requirements.json
self.requirements_schema = {
"type": "object",
"properties": {
"python": {
"type": "object",
"properties": {
"min_version": {"type": "string"},
"max_version": {"type": "string"}
},
"required": ["min_version"]
},
"node": {
"type": "object",
"properties": {
"min_version": {"type": "string"},
"max_version": {"type": "string"},
"required_for": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["min_version"]
},
"disk_space_mb": {"type": "integer"},
"external_tools": {
"type": "object",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_-]*$": {
"type": "object",
"properties": {
"command": {"type": "string"},
"min_version": {"type": "string"},
"required_for": {
"type": "array",
"items": {"type": "string"}
},
"optional": {"type": "boolean"}
},
"required": ["command"],
"additionalProperties": False
}
}
},
"installation_commands": {
"type": "object",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_-]*$": {
"type": "object",
"properties": {
"linux": {"type": "string"},
"darwin": {"type": "string"},
"win32": {"type": "string"},
"all": {"type": "string"},
"description": {"type": "string"}
},
"additionalProperties": False
}
}
}
},
"required": ["python", "disk_space_mb"],
"additionalProperties": False
}
def load_features(self) -> Dict[str, Any]:
"""
Load and validate features configuration
Returns:
Features configuration dict
Raises:
FileNotFoundError: If features.json not found
ValidationError: If features.json is invalid
"""
if self._features_cache is not None:
return self._features_cache
if not self.features_file.exists():
raise FileNotFoundError(f"Features config not found: {self.features_file}")
try:
with open(self.features_file, 'r') as f:
features = json.load(f)
# Validate schema
validate(instance=features, schema=self.features_schema)
self._features_cache = features
return features
except json.JSONDecodeError as e:
raise ValidationError(f"Invalid JSON in {self.features_file}: {e}")
except ValidationError as e:
raise ValidationError(f"Invalid features schema: {e.message}")
def load_requirements(self) -> Dict[str, Any]:
"""
Load and validate requirements configuration
Returns:
Requirements configuration dict
Raises:
FileNotFoundError: If requirements.json not found
ValidationError: If requirements.json is invalid
"""
if self._requirements_cache is not None:
return self._requirements_cache
if not self.requirements_file.exists():
raise FileNotFoundError(f"Requirements config not found: {self.requirements_file}")
try:
with open(self.requirements_file, 'r') as f:
requirements = json.load(f)
# Validate schema
validate(instance=requirements, schema=self.requirements_schema)
self._requirements_cache = requirements
return requirements
except json.JSONDecodeError as e:
raise ValidationError(f"Invalid JSON in {self.requirements_file}: {e}")
except ValidationError as e:
raise ValidationError(f"Invalid requirements schema: {e.message}")
def get_component_info(self, component_name: str) -> Optional[Dict[str, Any]]:
"""
Get information about a specific component
Args:
component_name: Name of component
Returns:
Component info dict or None if not found
"""
features = self.load_features()
return features.get("components", {}).get(component_name)
def get_enabled_components(self) -> List[str]:
"""
Get list of enabled component names
Returns:
List of enabled component names
"""
features = self.load_features()
enabled = []
for name, info in features.get("components", {}).items():
if info.get("enabled", True): # Default to enabled
enabled.append(name)
return enabled
def get_components_by_category(self, category: str) -> List[str]:
"""
Get component names by category
Args:
category: Component category
Returns:
List of component names in category
"""
features = self.load_features()
components = []
for name, info in features.get("components", {}).items():
if info.get("category") == category:
components.append(name)
return components
def get_component_dependencies(self, component_name: str) -> List[str]:
"""
Get dependencies for a component
Args:
component_name: Name of component
Returns:
List of dependency component names
"""
component_info = self.get_component_info(component_name)
if component_info:
return component_info.get("dependencies", [])
return []
def load_profile(self, profile_path: Path) -> Dict[str, Any]:
"""
Load installation profile
Args:
profile_path: Path to profile JSON file
Returns:
Profile configuration dict
Raises:
FileNotFoundError: If profile not found
ValidationError: If profile is invalid
"""
if not profile_path.exists():
raise FileNotFoundError(f"Profile not found: {profile_path}")
try:
with open(profile_path, 'r') as f:
profile = json.load(f)
# Basic validation
if "components" not in profile:
raise ValidationError("Profile must contain 'components' field")
if not isinstance(profile["components"], list):
raise ValidationError("Profile 'components' must be a list")
# Validate that all components exist
features = self.load_features()
available_components = set(features.get("components", {}).keys())
for component in profile["components"]:
if component not in available_components:
raise ValidationError(f"Unknown component in profile: {component}")
return profile
except json.JSONDecodeError as e:
raise ValidationError(f"Invalid JSON in {profile_path}: {e}")
def get_system_requirements(self) -> Dict[str, Any]:
"""
Get system requirements
Returns:
System requirements dict
"""
return self.load_requirements()
def get_requirements_for_components(self, component_names: List[str]) -> Dict[str, Any]:
"""
Get consolidated requirements for specific components
Args:
component_names: List of component names
Returns:
Consolidated requirements dict
"""
requirements = self.load_requirements()
features = self.load_features()
# Start with base requirements
result = {
"python": requirements["python"],
"disk_space_mb": requirements["disk_space_mb"],
"external_tools": {}
}
# Add Node.js requirements if needed
node_required = False
for component_name in component_names:
component_info = features.get("components", {}).get(component_name, {})
required_tools = component_info.get("required_tools", [])
if "node" in required_tools:
node_required = True
break
if node_required and "node" in requirements:
result["node"] = requirements["node"]
# Add external tool requirements
for component_name in component_names:
component_info = features.get("components", {}).get(component_name, {})
required_tools = component_info.get("required_tools", [])
for tool in required_tools:
if tool in requirements.get("external_tools", {}):
result["external_tools"][tool] = requirements["external_tools"][tool]
return result
def validate_config_files(self) -> List[str]:
"""
Validate all configuration files
Returns:
List of validation errors (empty if all valid)
"""
errors = []
try:
self.load_features()
except Exception as e:
errors.append(f"Features config error: {e}")
try:
self.load_requirements()
except Exception as e:
errors.append(f"Requirements config error: {e}")
return errors
def clear_cache(self) -> None:
"""Clear cached configuration data"""
self._features_cache = None
self._requirements_cache = None

View File

@@ -0,0 +1,428 @@
"""
Cross-platform file management for SuperClaude installation system
"""
import shutil
import stat
from typing import List, Optional, Callable, Dict, Any
from pathlib import Path
import fnmatch
import hashlib
class FileManager:
"""Cross-platform file operations manager"""
def __init__(self, dry_run: bool = False):
"""
Initialize file manager
Args:
dry_run: If True, only simulate file operations
"""
self.dry_run = dry_run
self.copied_files: List[Path] = []
self.created_dirs: List[Path] = []
def copy_file(self, source: Path, target: Path, preserve_permissions: bool = True) -> bool:
"""
Copy single file with permission preservation
Args:
source: Source file path
target: Target file path
preserve_permissions: Whether to preserve file permissions
Returns:
True if successful, False otherwise
"""
if not source.exists():
raise FileNotFoundError(f"Source file not found: {source}")
if not source.is_file():
raise ValueError(f"Source is not a file: {source}")
if self.dry_run:
print(f"[DRY RUN] Would copy {source} -> {target}")
return True
try:
# Ensure target directory exists
target.parent.mkdir(parents=True, exist_ok=True)
# Copy file
if preserve_permissions:
shutil.copy2(source, target)
else:
shutil.copy(source, target)
self.copied_files.append(target)
return True
except Exception as e:
print(f"Error copying {source} to {target}: {e}")
return False
def copy_directory(self, source: Path, target: Path, ignore_patterns: Optional[List[str]] = None) -> bool:
"""
Recursively copy directory with gitignore-style patterns
Args:
source: Source directory path
target: Target directory path
ignore_patterns: List of patterns to ignore (gitignore style)
Returns:
True if successful, False otherwise
"""
if not source.exists():
raise FileNotFoundError(f"Source directory not found: {source}")
if not source.is_dir():
raise ValueError(f"Source is not a directory: {source}")
ignore_patterns = ignore_patterns or []
default_ignores = ['.git', '.gitignore', '__pycache__', '*.pyc', '.DS_Store']
all_ignores = ignore_patterns + default_ignores
if self.dry_run:
print(f"[DRY RUN] Would copy directory {source} -> {target}")
return True
try:
# Create ignore function
def ignore_func(directory: str, contents: List[str]) -> List[str]:
ignored = []
for item in contents:
item_path = Path(directory) / item
rel_path = item_path.relative_to(source)
# Check against ignore patterns
for pattern in all_ignores:
if fnmatch.fnmatch(item, pattern) or fnmatch.fnmatch(str(rel_path), pattern):
ignored.append(item)
break
return ignored
# Copy tree
shutil.copytree(source, target, ignore=ignore_func, dirs_exist_ok=True)
# Track created directories and files
for item in target.rglob('*'):
if item.is_dir():
self.created_dirs.append(item)
else:
self.copied_files.append(item)
return True
except Exception as e:
print(f"Error copying directory {source} to {target}: {e}")
return False
def ensure_directory(self, directory: Path, mode: int = 0o755) -> bool:
"""
Create directory and parents if they don't exist
Args:
directory: Directory path to create
mode: Directory permissions (Unix only)
Returns:
True if successful, False otherwise
"""
if self.dry_run:
print(f"[DRY RUN] Would create directory {directory}")
return True
try:
directory.mkdir(parents=True, exist_ok=True, mode=mode)
if directory not in self.created_dirs:
self.created_dirs.append(directory)
return True
except Exception as e:
print(f"Error creating directory {directory}: {e}")
return False
def remove_file(self, file_path: Path) -> bool:
"""
Remove single file
Args:
file_path: Path to file to remove
Returns:
True if successful, False otherwise
"""
if not file_path.exists():
return True # Already gone
if self.dry_run:
print(f"[DRY RUN] Would remove file {file_path}")
return True
try:
if file_path.is_file():
file_path.unlink()
else:
print(f"Warning: {file_path} is not a file, skipping")
return False
# Remove from tracking
if file_path in self.copied_files:
self.copied_files.remove(file_path)
return True
except Exception as e:
print(f"Error removing file {file_path}: {e}")
return False
def remove_directory(self, directory: Path, recursive: bool = False) -> bool:
"""
Remove directory
Args:
directory: Directory path to remove
recursive: Whether to remove recursively
Returns:
True if successful, False otherwise
"""
if not directory.exists():
return True # Already gone
if self.dry_run:
action = "recursively remove" if recursive else "remove"
print(f"[DRY RUN] Would {action} directory {directory}")
return True
try:
if recursive:
shutil.rmtree(directory)
else:
directory.rmdir() # Only works if empty
# Remove from tracking
if directory in self.created_dirs:
self.created_dirs.remove(directory)
return True
except Exception as e:
print(f"Error removing directory {directory}: {e}")
return False
def resolve_home_path(self, path: str) -> Path:
"""
Convert path with ~ to actual home path on any OS
Args:
path: Path string potentially containing ~
Returns:
Resolved Path object
"""
return Path(path).expanduser().resolve()
def make_executable(self, file_path: Path) -> bool:
"""
Make file executable (Unix/Linux/macOS)
Args:
file_path: Path to file to make executable
Returns:
True if successful, False otherwise
"""
if not file_path.exists():
return False
if self.dry_run:
print(f"[DRY RUN] Would make {file_path} executable")
return True
try:
# Get current permissions
current_mode = file_path.stat().st_mode
# Add execute permissions for owner, group, and others
new_mode = current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
file_path.chmod(new_mode)
return True
except Exception as e:
print(f"Error making {file_path} executable: {e}")
return False
def get_file_hash(self, file_path: Path, algorithm: str = 'sha256') -> Optional[str]:
"""
Calculate file hash
Args:
file_path: Path to file
algorithm: Hash algorithm (md5, sha1, sha256, etc.)
Returns:
Hex hash string or None if error
"""
if not file_path.exists() or not file_path.is_file():
return None
try:
hasher = hashlib.new(algorithm)
with open(file_path, 'rb') as f:
# Read in chunks for large files
for chunk in iter(lambda: f.read(8192), b""):
hasher.update(chunk)
return hasher.hexdigest()
except Exception:
return None
def verify_file_integrity(self, file_path: Path, expected_hash: str, algorithm: str = 'sha256') -> bool:
"""
Verify file integrity using hash
Args:
file_path: Path to file to verify
expected_hash: Expected hash value
algorithm: Hash algorithm used
Returns:
True if file matches expected hash, False otherwise
"""
actual_hash = self.get_file_hash(file_path, algorithm)
return actual_hash is not None and actual_hash.lower() == expected_hash.lower()
def get_directory_size(self, directory: Path) -> int:
"""
Calculate total size of directory in bytes
Args:
directory: Directory path
Returns:
Total size in bytes
"""
if not directory.exists() or not directory.is_dir():
return 0
total_size = 0
try:
for file_path in directory.rglob('*'):
if file_path.is_file():
total_size += file_path.stat().st_size
except Exception:
pass # Skip files we can't access
return total_size
def find_files(self, directory: Path, pattern: str = '*', recursive: bool = True) -> List[Path]:
"""
Find files matching pattern
Args:
directory: Directory to search
pattern: Glob pattern to match
recursive: Whether to search recursively
Returns:
List of matching file paths
"""
if not directory.exists() or not directory.is_dir():
return []
try:
if recursive:
return list(directory.rglob(pattern))
else:
return list(directory.glob(pattern))
except Exception:
return []
def backup_file(self, file_path: Path, backup_suffix: str = '.backup') -> Optional[Path]:
"""
Create backup copy of file
Args:
file_path: Path to file to backup
backup_suffix: Suffix to add to backup file
Returns:
Path to backup file or None if failed
"""
if not file_path.exists() or not file_path.is_file():
return None
backup_path = file_path.with_suffix(file_path.suffix + backup_suffix)
if self.copy_file(file_path, backup_path):
return backup_path
return None
def get_free_space(self, path: Path) -> int:
"""
Get free disk space at path in bytes
Args:
path: Path to check (can be file or directory)
Returns:
Free space in bytes
"""
try:
if path.is_file():
path = path.parent
stat_result = shutil.disk_usage(path)
return stat_result.free
except Exception:
return 0
def cleanup_tracked_files(self) -> None:
"""Remove all files and directories created during this session"""
if self.dry_run:
print("[DRY RUN] Would cleanup tracked files")
return
# Remove files first
for file_path in reversed(self.copied_files):
try:
if file_path.exists():
file_path.unlink()
except Exception:
pass
# Remove directories (in reverse order of creation)
for directory in reversed(self.created_dirs):
try:
if directory.exists() and not any(directory.iterdir()):
directory.rmdir()
except Exception:
pass
self.copied_files.clear()
self.created_dirs.clear()
def get_operation_summary(self) -> Dict[str, Any]:
"""
Get summary of file operations performed
Returns:
Dict with operation statistics
"""
return {
'files_copied': len(self.copied_files),
'directories_created': len(self.created_dirs),
'dry_run': self.dry_run,
'copied_files': [str(f) for f in self.copied_files],
'created_directories': [str(d) for d in self.created_dirs]
}

View File

@@ -0,0 +1,515 @@
"""
Settings management for SuperClaude installation system
Handles settings.json migration to the new SuperClaude metadata json file
Allows for manipulation of these json files with deep merge and backup
"""
import json
import shutil
from typing import Dict, Any, Optional, List
from pathlib import Path
from datetime import datetime
import copy
class SettingsManager:
"""Manages settings.json file operations"""
def __init__(self, install_dir: Path):
"""
Initialize settings manager
Args:
install_dir: Installation directory containing settings.json
"""
self.install_dir = install_dir
self.settings_file = install_dir / "settings.json"
self.metadata_file = install_dir / ".superclaude-metadata.json"
self.backup_dir = install_dir / "backups" / "settings"
def load_settings(self) -> Dict[str, Any]:
"""
Load settings from settings.json
Returns:
Settings dict (empty if file doesn't exist)
"""
if not self.settings_file.exists():
return {}
try:
with open(self.settings_file, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
raise ValueError(f"Could not load settings from {self.settings_file}: {e}")
def save_settings(self, settings: Dict[str, Any], create_backup: bool = True) -> None:
"""
Save settings to settings.json with optional backup
Args:
settings: Settings dict to save
create_backup: Whether to create backup before saving
"""
# Create backup if requested and file exists
if create_backup and self.settings_file.exists():
self._create_settings_backup()
# Ensure directory exists
self.settings_file.parent.mkdir(parents=True, exist_ok=True)
# Save with pretty formatting
try:
with open(self.settings_file, 'w', encoding='utf-8') as f:
json.dump(settings, f, indent=2, ensure_ascii=False, sort_keys=True)
except IOError as e:
raise ValueError(f"Could not save settings to {self.settings_file}: {e}")
def load_metadata(self) -> Dict[str, Any]:
"""
Load SuperClaude metadata from .superclaude-metadata.json
Returns:
Metadata dict (empty if file doesn't exist)
"""
if not self.metadata_file.exists():
return {}
try:
with open(self.metadata_file, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
raise ValueError(f"Could not load metadata from {self.metadata_file}: {e}")
def save_metadata(self, metadata: Dict[str, Any]) -> None:
"""
Save SuperClaude metadata to .superclaude-metadata.json
Args:
metadata: Metadata dict to save
"""
# Ensure directory exists
self.metadata_file.parent.mkdir(parents=True, exist_ok=True)
# Save with pretty formatting
try:
with open(self.metadata_file, 'w', encoding='utf-8') as f:
json.dump(metadata, f, indent=2, ensure_ascii=False, sort_keys=True)
except IOError as e:
raise ValueError(f"Could not save metadata to {self.metadata_file}: {e}")
def merge_metadata(self, modifications: Dict[str, Any]) -> Dict[str, Any]:
"""
Deep merge modifications into existing settings
Args:
modifications: Settings modifications to merge
Returns:
Merged settings dict
"""
existing = self.load_metadata()
return self._deep_merge(existing, modifications)
def update_metadata(self, modifications: Dict[str, Any]) -> None:
"""
Update settings with modifications
Args:
modifications: Settings modifications to apply
create_backup: Whether to create backup before updating
"""
merged = self.merge_metadata(modifications)
self.save_metadata(merged)
def migrate_superclaude_data(self) -> bool:
"""
Migrate SuperClaude-specific data from settings.json to metadata file
Returns:
True if migration occurred, False if no data to migrate
"""
settings = self.load_settings()
# SuperClaude-specific fields to migrate
superclaude_fields = ["components", "framework", "superclaude", "mcp"]
data_to_migrate = {}
fields_found = False
# Extract SuperClaude data
for field in superclaude_fields:
if field in settings:
data_to_migrate[field] = settings[field]
fields_found = True
if not fields_found:
return False
# Load existing metadata (if any) and merge
existing_metadata = self.load_metadata()
merged_metadata = self._deep_merge(existing_metadata, data_to_migrate)
# Save to metadata file
self.save_metadata(merged_metadata)
# Remove SuperClaude fields from settings
clean_settings = {k: v for k, v in settings.items() if k not in superclaude_fields}
# Save cleaned settings
self.save_settings(clean_settings, create_backup=True)
return True
def merge_settings(self, modifications: Dict[str, Any]) -> Dict[str, Any]:
"""
Deep merge modifications into existing settings
Args:
modifications: Settings modifications to merge
Returns:
Merged settings dict
"""
existing = self.load_settings()
return self._deep_merge(existing, modifications)
def update_settings(self, modifications: Dict[str, Any], create_backup: bool = True) -> None:
"""
Update settings with modifications
Args:
modifications: Settings modifications to apply
create_backup: Whether to create backup before updating
"""
merged = self.merge_settings(modifications)
self.save_settings(merged, create_backup)
def get_setting(self, key_path: str, default: Any = None) -> Any:
"""
Get setting value using dot-notation path
Args:
key_path: Dot-separated path (e.g., "hooks.enabled")
default: Default value if key not found
Returns:
Setting value or default
"""
settings = self.load_settings()
try:
value = settings
for key in key_path.split('.'):
value = value[key]
return value
except (KeyError, TypeError):
return default
def set_setting(self, key_path: str, value: Any, create_backup: bool = True) -> None:
"""
Set setting value using dot-notation path
Args:
key_path: Dot-separated path (e.g., "hooks.enabled")
value: Value to set
create_backup: Whether to create backup before updating
"""
# Build nested dict structure
keys = key_path.split('.')
modification = {}
current = modification
for key in keys[:-1]:
current[key] = {}
current = current[key]
current[keys[-1]] = value
self.update_settings(modification, create_backup)
def remove_setting(self, key_path: str, create_backup: bool = True) -> bool:
"""
Remove setting using dot-notation path
Args:
key_path: Dot-separated path to remove
create_backup: Whether to create backup before updating
Returns:
True if setting was removed, False if not found
"""
settings = self.load_settings()
keys = key_path.split('.')
# Navigate to parent of target key
current = settings
try:
for key in keys[:-1]:
current = current[key]
# Remove the target key
if keys[-1] in current:
del current[keys[-1]]
self.save_settings(settings, create_backup)
return True
else:
return False
except (KeyError, TypeError):
return False
def add_component_registration(self, component_name: str, component_info: Dict[str, Any]) -> None:
"""
Add component to registry in metadata
Args:
component_name: Name of component
component_info: Component metadata dict
"""
metadata = self.load_metadata()
if "components" not in metadata:
metadata["components"] = {}
metadata["components"][component_name] = {
**component_info,
"installed_at": datetime.now().isoformat()
}
self.save_metadata(metadata)
def remove_component_registration(self, component_name: str) -> bool:
"""
Remove component from registry in metadata
Args:
component_name: Name of component to remove
Returns:
True if component was removed, False if not found
"""
metadata = self.load_metadata()
if "components" in metadata and component_name in metadata["components"]:
del metadata["components"][component_name]
self.save_metadata(metadata)
return True
return False
def get_installed_components(self) -> Dict[str, Dict[str, Any]]:
"""
Get all installed components from registry
Returns:
Dict of component_name -> component_info
"""
metadata = self.load_metadata()
return metadata.get("components", {})
def is_component_installed(self, component_name: str) -> bool:
"""
Check if component is registered as installed
Args:
component_name: Name of component to check
Returns:
True if component is installed, False otherwise
"""
components = self.get_installed_components()
return component_name in components
def get_component_version(self, component_name: str) -> Optional[str]:
"""
Get installed version of component
Args:
component_name: Name of component
Returns:
Version string or None if not installed
"""
components = self.get_installed_components()
component_info = components.get(component_name, {})
return component_info.get("version")
def update_framework_version(self, version: str) -> None:
"""
Update SuperClaude framework version in metadata
Args:
version: Framework version string
"""
metadata = self.load_metadata()
if "framework" not in metadata:
metadata["framework"] = {}
metadata["framework"]["version"] = version
metadata["framework"]["updated_at"] = datetime.now().isoformat()
self.save_metadata(metadata)
def check_installation_exists(self) -> bool:
"""
Get SuperClaude framework version from metadata
Returns:
Version string or None if not set
"""
return self.metadata_file.exists()
def check_v2_installation_exists(self) -> bool:
"""
Get SuperClaude framework version from metadata
Returns:
Version string or None if not set
"""
return self.settings_file.exists()
def get_metadata_setting(self, key_path: str, default: Any = None) -> Any:
"""
Get metadata value using dot-notation path
Args:
key_path: Dot-separated path (e.g., "framework.version")
default: Default value if key not found
Returns:
Metadata value or default
"""
metadata = self.load_metadata()
try:
value = metadata
for key in key_path.split('.'):
value = value[key]
return value
except (KeyError, TypeError):
return default
def _deep_merge(self, base: Dict[str, Any], overlay: Dict[str, Any]) -> Dict[str, Any]:
"""
Deep merge two dictionaries
Args:
base: Base dictionary
overlay: Dictionary to merge on top
Returns:
Merged dictionary
"""
result = copy.deepcopy(base)
for key, value in overlay.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = self._deep_merge(result[key], value)
else:
result[key] = copy.deepcopy(value)
return result
def _create_settings_backup(self) -> Path:
"""
Create timestamped backup of settings.json
Returns:
Path to backup file
"""
if not self.settings_file.exists():
raise ValueError("Cannot backup non-existent settings file")
# Create backup directory
self.backup_dir.mkdir(parents=True, exist_ok=True)
# Create timestamped backup
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = self.backup_dir / f"settings_{timestamp}.json"
shutil.copy2(self.settings_file, backup_file)
# Keep only last 10 backups
self._cleanup_old_backups()
return backup_file
def _cleanup_old_backups(self, keep_count: int = 10) -> None:
"""
Remove old backup files, keeping only the most recent
Args:
keep_count: Number of backups to keep
"""
if not self.backup_dir.exists():
return
# Get all backup files sorted by modification time
backup_files = []
for file in self.backup_dir.glob("settings_*.json"):
backup_files.append((file.stat().st_mtime, file))
backup_files.sort(reverse=True) # Most recent first
# Remove old backups
for _, file in backup_files[keep_count:]:
try:
file.unlink()
except OSError:
pass # Ignore errors when cleaning up
def list_backups(self) -> List[Dict[str, Any]]:
"""
List available settings backups
Returns:
List of backup info dicts with name, path, and timestamp
"""
if not self.backup_dir.exists():
return []
backups = []
for file in self.backup_dir.glob("settings_*.json"):
try:
stat = file.stat()
backups.append({
"name": file.name,
"path": str(file),
"size": stat.st_size,
"created": datetime.fromtimestamp(stat.st_ctime).isoformat(),
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat()
})
except OSError:
continue
# Sort by creation time, most recent first
backups.sort(key=lambda x: x["created"], reverse=True)
return backups
def restore_backup(self, backup_name: str) -> bool:
"""
Restore settings from backup
Args:
backup_name: Name of backup file to restore
Returns:
True if successful, False otherwise
"""
backup_file = self.backup_dir / backup_name
if not backup_file.exists():
return False
try:
# Validate backup file first
with open(backup_file, 'r', encoding='utf-8') as f:
json.load(f) # Will raise exception if invalid
# Create backup of current settings
if self.settings_file.exists():
self._create_settings_backup()
# Restore backup
shutil.copy2(backup_file, self.settings_file)
return True
except (json.JSONDecodeError, IOError):
return False

View File

@@ -0,0 +1,85 @@
"""
SuperClaude Operations Module
This module contains all SuperClaude management operations that can be
executed through the unified CLI hub (SuperClaude).
Each operation module should implement:
- register_parser(subparsers): Register CLI arguments for the operation
- run(args): Execute the operation with parsed arguments
Available operations:
- install: Install SuperClaude framework components
- update: Update existing SuperClaude installation
- uninstall: Remove SuperClaude framework installation
- backup: Backup and restore SuperClaude installations
"""
__version__ = "3.0.0"
__all__ = ["install", "update", "uninstall", "backup"]
def get_operation_info():
"""Get information about available operations"""
return {
"install": {
"name": "install",
"description": "Install SuperClaude framework components",
"module": "setup.operations.install"
},
"update": {
"name": "update",
"description": "Update existing SuperClaude installation",
"module": "setup.operations.update"
},
"uninstall": {
"name": "uninstall",
"description": "Remove SuperClaude framework installation",
"module": "setup.operations.uninstall"
},
"backup": {
"name": "backup",
"description": "Backup and restore SuperClaude installations",
"module": "setup.operations.backup"
}
}
class OperationBase:
"""Base class for all operations providing common functionality"""
def __init__(self, operation_name: str):
self.operation_name = operation_name
self.logger = None
def setup_operation_logging(self, args):
"""Setup operation-specific logging"""
from ..utils.logger import get_logger
self.logger = get_logger()
self.logger.info(f"Starting {self.operation_name} operation")
def validate_global_args(self, args):
"""Validate global arguments common to all operations"""
errors = []
# Validate install directory
if hasattr(args, 'install_dir') and args.install_dir:
from ..utils.security import SecurityValidator
is_safe, validation_errors = SecurityValidator.validate_installation_target(args.install_dir)
if not is_safe:
errors.extend(validation_errors)
# Check for conflicting flags
if hasattr(args, 'verbose') and hasattr(args, 'quiet'):
if args.verbose and args.quiet:
errors.append("Cannot specify both --verbose and --quiet")
return len(errors) == 0, errors
def handle_operation_error(self, operation: str, error: Exception):
"""Standard error handling for operations"""
if self.logger:
self.logger.exception(f"Error in {operation} operation: {error}")
else:
print(f"Error in {operation} operation: {error}")
return 1

589
setup/operations/backup.py Normal file
View File

@@ -0,0 +1,589 @@
"""
SuperClaude Backup Operation Module
Refactored from backup.py for unified CLI hub
"""
import sys
import time
import tarfile
import json
from pathlib import Path
from datetime import datetime
from typing import List, Optional, Dict, Any, Tuple
import argparse
from ..managers.settings_manager import SettingsManager
from ..utils.ui import (
display_header, display_info, display_success, display_error,
display_warning, Menu, confirm, ProgressBar, Colors, format_size
)
from ..utils.logger import get_logger
from .. import DEFAULT_INSTALL_DIR
from . import OperationBase
class BackupOperation(OperationBase):
"""Backup operation implementation"""
def __init__(self):
super().__init__("backup")
def register_parser(subparsers, global_parser=None) -> argparse.ArgumentParser:
"""Register backup CLI arguments"""
parents = [global_parser] if global_parser else []
parser = subparsers.add_parser(
"backup",
help="Backup and restore SuperClaude installations",
description="Create, list, restore, and manage SuperClaude installation backups",
epilog="""
Examples:
SuperClaude backup --create # Create new backup
SuperClaude backup --list --verbose # List available backups (verbose)
SuperClaude backup --restore # Interactive restore
SuperClaude backup --restore backup.tar.gz # Restore specific backup
SuperClaude backup --info backup.tar.gz # Show backup information
SuperClaude backup --cleanup --force # Clean up old backups (forced)
""",
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=parents
)
# Backup operations (mutually exclusive)
operation_group = parser.add_mutually_exclusive_group(required=True)
operation_group.add_argument(
"--create",
action="store_true",
help="Create a new backup"
)
operation_group.add_argument(
"--list",
action="store_true",
help="List available backups"
)
operation_group.add_argument(
"--restore",
nargs="?",
const="interactive",
help="Restore from backup (optionally specify backup file)"
)
operation_group.add_argument(
"--info",
type=str,
help="Show information about a specific backup file"
)
operation_group.add_argument(
"--cleanup",
action="store_true",
help="Clean up old backup files"
)
# Backup options
parser.add_argument(
"--backup-dir",
type=Path,
help="Backup directory (default: <install-dir>/backups)"
)
parser.add_argument(
"--name",
type=str,
help="Custom backup name (for --create)"
)
parser.add_argument(
"--compress",
choices=["none", "gzip", "bzip2"],
default="gzip",
help="Compression method (default: gzip)"
)
# Restore options
parser.add_argument(
"--overwrite",
action="store_true",
help="Overwrite existing files during restore"
)
# Cleanup options
parser.add_argument(
"--keep",
type=int,
default=5,
help="Number of backups to keep during cleanup (default: 5)"
)
parser.add_argument(
"--older-than",
type=int,
help="Remove backups older than N days"
)
return parser
def get_backup_directory(args: argparse.Namespace) -> Path:
"""Get the backup directory path"""
if args.backup_dir:
return args.backup_dir
else:
return args.install_dir / "backups"
def check_installation_exists(install_dir: Path) -> bool:
"""Check if SuperClaude installation (v2 included) exists"""
settings_manager = SettingsManager(install_dir)
return settings_manager.check_installation_exists() or settings_manager.check_v2_installation_exists()
def get_backup_info(backup_path: Path) -> Dict[str, Any]:
"""Get information about a backup file"""
info = {
"path": backup_path,
"exists": backup_path.exists(),
"size": 0,
"created": None,
"metadata": {}
}
if not backup_path.exists():
return info
try:
# Get file stats
stats = backup_path.stat()
info["size"] = stats.st_size
info["created"] = datetime.fromtimestamp(stats.st_mtime)
# Try to read metadata from backup
if backup_path.suffix == ".gz":
mode = "r:gz"
elif backup_path.suffix == ".bz2":
mode = "r:bz2"
else:
mode = "r"
with tarfile.open(backup_path, mode) as tar:
# Look for metadata file
try:
metadata_member = tar.getmember("backup_metadata.json")
metadata_file = tar.extractfile(metadata_member)
if metadata_file:
info["metadata"] = json.loads(metadata_file.read().decode())
except KeyError:
pass # No metadata file
# Get list of files in backup
info["files"] = len(tar.getnames())
except Exception as e:
info["error"] = str(e)
return info
def list_backups(backup_dir: Path) -> List[Dict[str, Any]]:
"""List all available backups"""
backups = []
if not backup_dir.exists():
return backups
# Find all backup files
for backup_file in backup_dir.glob("*.tar*"):
if backup_file.is_file():
info = get_backup_info(backup_file)
backups.append(info)
# Sort by creation date (newest first)
backups.sort(key=lambda x: x.get("created", datetime.min), reverse=True)
return backups
def display_backup_list(backups: List[Dict[str, Any]]) -> None:
"""Display list of available backups"""
print(f"\n{Colors.CYAN}{Colors.BRIGHT}Available Backups{Colors.RESET}")
print("=" * 70)
if not backups:
print(f"{Colors.YELLOW}No backups found{Colors.RESET}")
return
print(f"{'Name':<30} {'Size':<10} {'Created':<20} {'Files':<8}")
print("-" * 70)
for backup in backups:
name = backup["path"].name
size = format_size(backup["size"]) if backup["size"] > 0 else "unknown"
created = backup["created"].strftime("%Y-%m-%d %H:%M") if backup["created"] else "unknown"
files = str(backup.get("files", "unknown"))
print(f"{name:<30} {size:<10} {created:<20} {files:<8}")
print()
def create_backup_metadata(install_dir: Path) -> Dict[str, Any]:
"""Create metadata for the backup"""
metadata = {
"backup_version": "3.0.0",
"created": datetime.now().isoformat(),
"install_dir": str(install_dir),
"components": {},
"framework_version": "unknown"
}
try:
# Get installed components from metadata
settings_manager = SettingsManager(install_dir)
framework_config = settings_manager.get_metadata_setting("framework")
if framework_config:
metadata["framework_version"] = framework_config.get("version", "unknown")
if "components" in framework_config:
for component_name in framework_config["components"]:
version = settings_manager.get_component_version(component_name)
if version:
metadata["components"][component_name] = version
except Exception:
pass # Continue without metadata
return metadata
def create_backup(args: argparse.Namespace) -> bool:
"""Create a new backup"""
logger = get_logger()
try:
# Check if installation exists
if not check_installation_exists(args.install_dir):
logger.error(f"No SuperClaude installation found in {args.install_dir}")
return False
# Setup backup directory
backup_dir = get_backup_directory(args)
backup_dir.mkdir(parents=True, exist_ok=True)
# Generate backup filename
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
if args.name:
backup_name = f"{args.name}_{timestamp}"
else:
backup_name = f"superclaude_backup_{timestamp}"
# Determine compression
if args.compress == "gzip":
backup_file = backup_dir / f"{backup_name}.tar.gz"
mode = "w:gz"
elif args.compress == "bzip2":
backup_file = backup_dir / f"{backup_name}.tar.bz2"
mode = "w:bz2"
else:
backup_file = backup_dir / f"{backup_name}.tar"
mode = "w"
logger.info(f"Creating backup: {backup_file}")
# Create metadata
metadata = create_backup_metadata(args.install_dir)
# Create backup
start_time = time.time()
with tarfile.open(backup_file, mode) as tar:
# Add metadata file
import tempfile
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as temp_file:
json.dump(metadata, temp_file, indent=2)
temp_file.flush()
tar.add(temp_file.name, arcname="backup_metadata.json")
Path(temp_file.name).unlink() # Clean up temp file
# Add installation directory contents
files_added = 0
for item in args.install_dir.rglob("*"):
if item.is_file() and item != backup_file:
try:
# Create relative path for archive
rel_path = item.relative_to(args.install_dir)
tar.add(item, arcname=str(rel_path))
files_added += 1
if files_added % 10 == 0:
logger.debug(f"Added {files_added} files to backup")
except Exception as e:
logger.warning(f"Could not add {item} to backup: {e}")
duration = time.time() - start_time
file_size = backup_file.stat().st_size
logger.success(f"Backup created successfully in {duration:.1f} seconds")
logger.info(f"Backup file: {backup_file}")
logger.info(f"Files archived: {files_added}")
logger.info(f"Backup size: {format_size(file_size)}")
return True
except Exception as e:
logger.exception(f"Failed to create backup: {e}")
return False
def restore_backup(backup_path: Path, args: argparse.Namespace) -> bool:
"""Restore from a backup file"""
logger = get_logger()
try:
if not backup_path.exists():
logger.error(f"Backup file not found: {backup_path}")
return False
# Check backup file
info = get_backup_info(backup_path)
if "error" in info:
logger.error(f"Invalid backup file: {info['error']}")
return False
logger.info(f"Restoring from backup: {backup_path}")
# Determine compression
if backup_path.suffix == ".gz":
mode = "r:gz"
elif backup_path.suffix == ".bz2":
mode = "r:bz2"
else:
mode = "r"
# Create backup of current installation if it exists
if check_installation_exists(args.install_dir) and not args.dry_run:
logger.info("Creating backup of current installation before restore")
# This would call create_backup internally
# Extract backup
start_time = time.time()
files_restored = 0
with tarfile.open(backup_path, mode) as tar:
# Extract all files except metadata
for member in tar.getmembers():
if member.name == "backup_metadata.json":
continue
try:
target_path = args.install_dir / member.name
# Check if file exists and overwrite flag
if target_path.exists() and not args.overwrite:
logger.warning(f"Skipping existing file: {target_path}")
continue
# Extract file
tar.extract(member, args.install_dir)
files_restored += 1
if files_restored % 10 == 0:
logger.debug(f"Restored {files_restored} files")
except Exception as e:
logger.warning(f"Could not restore {member.name}: {e}")
duration = time.time() - start_time
logger.success(f"Restore completed successfully in {duration:.1f} seconds")
logger.info(f"Files restored: {files_restored}")
return True
except Exception as e:
logger.exception(f"Failed to restore backup: {e}")
return False
def interactive_restore_selection(backups: List[Dict[str, Any]]) -> Optional[Path]:
"""Interactive backup selection for restore"""
if not backups:
print(f"{Colors.YELLOW}No backups available for restore{Colors.RESET}")
return None
print(f"\n{Colors.CYAN}Select Backup to Restore:{Colors.RESET}")
# Create menu options
backup_options = []
for backup in backups:
name = backup["path"].name
size = format_size(backup["size"]) if backup["size"] > 0 else "unknown"
created = backup["created"].strftime("%Y-%m-%d %H:%M") if backup["created"] else "unknown"
backup_options.append(f"{name} ({size}, {created})")
menu = Menu("Select backup:", backup_options)
choice = menu.display()
if choice == -1 or choice >= len(backups):
return None
return backups[choice]["path"]
def cleanup_old_backups(backup_dir: Path, args: argparse.Namespace) -> bool:
"""Clean up old backup files"""
logger = get_logger()
try:
backups = list_backups(backup_dir)
if not backups:
logger.info("No backups found to clean up")
return True
to_remove = []
# Remove by age
if args.older_than:
cutoff_date = datetime.now() - timedelta(days=args.older_than)
for backup in backups:
if backup["created"] and backup["created"] < cutoff_date:
to_remove.append(backup)
# Keep only N most recent
if args.keep and len(backups) > args.keep:
# Sort by date and take oldest ones to remove
backups.sort(key=lambda x: x.get("created", datetime.min), reverse=True)
to_remove.extend(backups[args.keep:])
# Remove duplicates
to_remove = list({backup["path"]: backup for backup in to_remove}.values())
if not to_remove:
logger.info("No backups need to be cleaned up")
return True
logger.info(f"Cleaning up {len(to_remove)} old backups")
for backup in to_remove:
try:
backup["path"].unlink()
logger.info(f"Removed backup: {backup['path'].name}")
except Exception as e:
logger.warning(f"Could not remove {backup['path'].name}: {e}")
return True
except Exception as e:
logger.exception(f"Failed to cleanup backups: {e}")
return False
def run(args: argparse.Namespace) -> int:
"""Execute backup operation with parsed arguments"""
operation = BackupOperation()
operation.setup_operation_logging(args)
logger = get_logger()
# ✅ Inserted validation code
expected_home = Path.home().resolve()
actual_dir = args.install_dir.resolve()
if not str(actual_dir).startswith(str(expected_home)):
print(f"\n[✗] Installation must be inside your user profile directory.")
print(f" Expected prefix: {expected_home}")
print(f" Provided path: {actual_dir}")
sys.exit(1)
try:
# Validate global arguments
success, errors = operation.validate_global_args(args)
if not success:
for error in errors:
logger.error(error)
return 1
# Display header
if not args.quiet:
display_header(
"SuperClaude Backup v3.0",
"Backup and restore SuperClaude installations"
)
backup_dir = get_backup_directory(args)
# Handle different backup operations
if args.create:
success = create_backup(args)
elif args.list:
backups = list_backups(backup_dir)
display_backup_list(backups)
success = True
elif args.restore:
if args.restore == "interactive":
# Interactive restore
backups = list_backups(backup_dir)
backup_path = interactive_restore_selection(backups)
if not backup_path:
logger.info("Restore cancelled by user")
return 0
else:
# Specific backup file
backup_path = Path(args.restore)
if not backup_path.is_absolute():
backup_path = backup_dir / backup_path
success = restore_backup(backup_path, args)
elif args.info:
backup_path = Path(args.info)
if not backup_path.is_absolute():
backup_path = backup_dir / backup_path
info = get_backup_info(backup_path)
if info["exists"]:
print(f"\n{Colors.CYAN}Backup Information:{Colors.RESET}")
print(f"File: {info['path']}")
print(f"Size: {format_size(info['size'])}")
print(f"Created: {info['created']}")
print(f"Files: {info.get('files', 'unknown')}")
if info["metadata"]:
metadata = info["metadata"]
print(f"Framework Version: {metadata.get('framework_version', 'unknown')}")
if metadata.get("components"):
print("Components:")
for comp, ver in metadata["components"].items():
print(f" {comp}: v{ver}")
else:
logger.error(f"Backup file not found: {backup_path}")
success = False
success = True
elif args.cleanup:
success = cleanup_old_backups(backup_dir, args)
else:
logger.error("No backup operation specified")
success = False
if success:
if not args.quiet and args.create:
display_success("Backup operation completed successfully!")
elif not args.quiet and args.restore:
display_success("Restore operation completed successfully!")
return 0
else:
display_error("Backup operation failed. Check logs for details.")
return 1
except KeyboardInterrupt:
print(f"\n{Colors.YELLOW}Backup operation cancelled by user{Colors.RESET}")
return 130
except Exception as e:
return operation.handle_operation_error("backup", e)

542
setup/operations/install.py Normal file
View File

@@ -0,0 +1,542 @@
"""
SuperClaude Installation Operation Module
Refactored from install.py for unified CLI hub
"""
import sys
import time
from pathlib import Path
from typing import List, Optional, Dict, Any
import argparse
from ..base.installer import Installer
from ..core.registry import ComponentRegistry
from ..managers.config_manager import ConfigManager
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
)
from ..utils.logger import get_logger
from .. import DEFAULT_INSTALL_DIR, PROJECT_ROOT
from . import OperationBase
class InstallOperation(OperationBase):
"""Installation operation implementation"""
def __init__(self):
super().__init__("install")
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",
description="Install SuperClaude Framework with various options and profiles",
epilog="""
Examples:
SuperClaude install # Interactive installation
SuperClaude install --quick --dry-run # Quick installation (dry-run)
SuperClaude install --profile developer # Developer profile
SuperClaude install --components core mcp # Specific components
SuperClaude install --verbose --force # Verbose with force mode
""",
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=parents
)
# Installation mode options
parser.add_argument(
"--quick",
action="store_true",
help="Quick installation with pre-selected components"
)
parser.add_argument(
"--minimal",
action="store_true",
help="Minimal installation (core only)"
)
parser.add_argument(
"--profile",
type=str,
help="Installation profile (quick, minimal, developer, etc.)"
)
parser.add_argument(
"--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(
"--list-components",
action="store_true",
help="List available components and exit"
)
parser.add_argument(
"--diagnose",
action="store_true",
help="Run system diagnostics and show installation help"
)
return parser
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 = ConfigManager(PROJECT_ROOT / "config")
requirements = config_manager.get_requirements_for_components(component_names)
# Validate requirements
success, errors = validator.validate_component_requirements(component_names, requirements)
if success:
logger.success("All system requirements met")
return True
else:
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(" 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: ConfigManager) -> Optional[List[str]]:
"""Determine which components to install"""
logger = get_logger()
# Explicit components specified
if args.components:
if 'all' in args.components:
return ["core", "commands", "hooks", "mcp"]
return args.components
# Profile-based selection
if args.profile:
try:
profile_path = PROJECT_ROOT / "profiles" / f"{args.profile}.json"
profile = config_manager.load_profile(profile_path)
return profile["components"]
except Exception as e:
logger.error(f"Could not load profile '{args.profile}': {e}")
return None
# Quick installation
if args.quick:
try:
profile_path = PROJECT_ROOT / "profiles" / "quick.json"
profile = config_manager.load_profile(profile_path)
return profile["components"]
except Exception as e:
logger.warning(f"Could not load quick profile: {e}")
return ["core"] # Fallback to core only
# Minimal installation
if args.minimal:
return ["core"]
# Interactive selection
return interactive_component_selection(registry, config_manager)
def interactive_component_selection(registry: ComponentRegistry, config_manager: ConfigManager) -> Optional[List[str]]:
"""Interactive component selection"""
logger = get_logger()
try:
# Get available components
available_components = registry.list_components()
if not available_components:
logger.error("No components available for installation")
return None
# Create component menu with descriptions
menu_options = []
component_info = {}
for component_name in available_components:
metadata = registry.get_component_metadata(component_name)
if metadata:
description = metadata.get("description", "No description")
category = metadata.get("category", "unknown")
menu_options.append(f"{component_name} ({category}) - {description}")
component_info[component_name] = metadata
else:
menu_options.append(f"{component_name} - Component description unavailable")
component_info[component_name] = {"description": "Unknown"}
# Add preset options
preset_options = [
"Quick Installation (recommended components)",
"Minimal Installation (core only)",
"Custom Selection"
]
print(f"\n{Colors.CYAN}SuperClaude Installation Options:{Colors.RESET}")
menu = Menu("Select installation type:", preset_options)
choice = menu.display()
if choice == -1: # Cancelled
return None
elif choice == 0: # Quick
try:
profile_path = PROJECT_ROOT / "profiles" / "quick.json"
profile = config_manager.load_profile(profile_path)
return profile["components"]
except Exception:
return ["core"]
elif choice == 1: # Minimal
return ["core"]
elif choice == 2: # Custom
print(f"\n{Colors.CYAN}Available Components:{Colors.RESET}")
component_menu = Menu("Select components to install:", menu_options, multi_select=True)
selections = component_menu.display()
if not selections:
logger.warning("No components selected")
return None
return [available_components[i] for i in selections]
return None
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:
"""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'):
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()
except Exception as e:
logger.error(f"Could not resolve dependencies: {e}")
raise
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':
print(f"{check_name}: {message}")
else:
print(f"{check_name}: {message}")
all_passed = False
# Display issues and recommendations
if diagnostics['issues']:
print(f"\n{Colors.YELLOW}Issues Found:{Colors.RESET}")
for issue in diagnostics['issues']:
print(f" ⚠️ {issue}")
print(f"\n{Colors.CYAN}Recommendations:{Colors.RESET}")
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}")
else:
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)")
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")
def perform_installation(components: List[str], args: argparse.Namespace) -> 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)
if not component_instances:
logger.error("No valid component instances created")
return False
# Register components with installer
installer.register_components(list(component_instances.values()))
# Resolve dependencies
ordered_components = registry.resolve_dependencies(components)
# Setup progress tracking
progress = ProgressBar(
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
}
success = installer.install_components(ordered_components, config)
# Update progress
for i, component_name in enumerate(ordered_components):
if component_name in installer.installed_components:
progress.update(i + 1, f"Installed {component_name}")
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")
# Show summary
summary = installer.get_installation_summary()
if summary['installed']:
logger.info(f"Installed components: {', '.join(summary['installed'])}")
if summary['backup_path']:
logger.info(f"Backup created: {summary['backup_path']}")
else:
logger.error(f"Installation completed with errors in {duration:.1f} seconds")
summary = installer.get_installation_summary()
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
def run(args: argparse.Namespace) -> int:
"""Execute installation operation with parsed arguments"""
operation = InstallOperation()
operation.setup_operation_logging(args)
logger = get_logger()
# ✅ Inserted validation code
expected_home = Path.home().resolve()
actual_dir = args.install_dir.resolve()
if not str(actual_dir).startswith(str(expected_home)):
print(f"\n[✗] Installation must be inside your user profile directory.")
print(f" Expected prefix: {expected_home}")
print(f" Provided path: {actual_dir}")
sys.exit(1)
try:
# Validate global arguments
success, errors = operation.validate_global_args(args)
if not success:
for error in errors:
logger.error(error)
return 1
# Display header
if not args.quiet:
display_header(
"SuperClaude Installation v3.0",
"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}")
for component_name in components:
metadata = registry.get_component_metadata(component_name)
if metadata:
desc = metadata.get("description", "No description")
category = metadata.get("category", "unknown")
print(f" {component_name} ({category}) - {desc}")
else:
print(f" {component_name} - Unknown component")
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 = ConfigManager(PROJECT_ROOT / "config")
validator = Validator()
# Validate configuration
config_errors = config_manager.validate_config_files()
if config_errors:
logger.error("Configuration validation failed:")
for error in config_errors:
logger.error(f" - {error}")
return 1
# Get components to install
components = get_components_to_install(args, registry, config_manager)
if not components:
logger.error("No components selected for installation")
return 1
# Validate system requirements
if not validate_system_requirements(validator, 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")
# 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.info("Installation cancelled by user")
return 0
# Display installation plan
if not args.quiet:
display_installation_plan(components, registry, args.install_dir)
if not args.dry_run:
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(components, args)
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
except Exception as e:
return operation.handle_operation_error("install", e)

Some files were not shown because too many files have changed in this diff Show More