refactor: PEP8 compliance - directory rename and code formatting (#425)

* fix(orchestration): add WebFetch auto-trigger for infrastructure configuration

Problem: Infrastructure configuration changes (e.g., Traefik port settings)
were being made based on assumptions without consulting official documentation,
violating the 'Evidence > assumptions' principle in PRINCIPLES.md.

Solution:
- Added Infrastructure Configuration Validation section to MODE_Orchestration.md
- Auto-triggers WebFetch for infrastructure tools (Traefik, nginx, Docker, etc.)
- Enforces MODE_DeepResearch activation for investigation
- BLOCKS assumption-based configuration changes

Testing: Verified WebFetch successfully retrieves Traefik official docs (port 80 default)

This prevents production outages from infrastructure misconfiguration by ensuring
all technical recommendations are backed by official documentation.

* feat: Add PM Agent (Project Manager Agent) for seamless orchestration

Introduces PM Agent as the default orchestration layer that coordinates
all sub-agents and manages workflows automatically.

Key Features:
- Default orchestration: All user interactions handled by PM Agent
- Auto-delegation: Intelligent sub-agent selection based on task analysis
- Docker Gateway integration: Zero-token baseline with dynamic MCP loading
- Self-improvement loop: Automatic documentation of patterns and mistakes
- Optional override: Users can specify sub-agents explicitly if desired

Architecture:
- Agent spec: SuperClaude/Agents/pm-agent.md
- Command: SuperClaude/Commands/pm.md
- Updated docs: README.md (15→16 agents), agents.md (new Orchestration category)

User Experience:
- Default: PM Agent handles everything (seamless, no manual routing)
- Optional: Explicit --agent flag for direct sub-agent access
- Both modes available simultaneously (no user downside)

Implementation Status:
-  Specification complete
-  Documentation complete
-  Prototype implementation needed
-  Docker Gateway integration needed
-  Testing and validation needed

Refs: kazukinakai/docker-mcp-gateway (IRIS MCP Gateway integration)

* feat: Add Agent Orchestration rules for PM Agent default activation

Implements PM Agent as the default orchestration layer in RULES.md.

Key Changes:
- New 'Agent Orchestration' section (CRITICAL priority)
- PM Agent receives ALL user requests by default
- Manual override with @agent-[name] bypasses PM Agent
- Agent Selection Priority clearly defined:
  1. Manual override → Direct routing
  2. Default → PM Agent → Auto-delegation
  3. Delegation based on keywords, file types, complexity, context

User Experience:
- Default: PM Agent handles everything (seamless)
- Override: @agent-[name] for direct specialist access
- Transparent: PM Agent reports delegation decisions

This establishes PM Agent as the orchestration layer while
respecting existing auto-activation patterns and manual overrides.

Next Steps:
- Local testing in agiletec project
- Iteration based on actual behavior
- Documentation updates as needed

* refactor(pm-agent): redesign as self-improvement meta-layer

Problem Resolution:
PM Agent's initial design competed with existing auto-activation for task routing,
creating confusion about orchestration responsibilities and adding unnecessary complexity.

Design Change:
Redefined PM Agent as a meta-layer agent that operates AFTER specialist agents
complete tasks, focusing on:
- Post-implementation documentation and pattern recording
- Immediate mistake analysis with prevention checklists
- Monthly documentation maintenance and noise reduction
- Pattern extraction and knowledge synthesis

Two-Layer Orchestration System:
1. Task Execution Layer: Existing auto-activation handles task routing (unchanged)
2. Self-Improvement Layer: PM Agent meta-layer handles documentation (new)

Files Modified:
- SuperClaude/Agents/pm-agent.md: Complete rewrite with meta-layer design
  - Category: orchestration → meta
  - Triggers: All user interactions → Post-implementation, mistakes, monthly
  - Behavioral Mindset: Continuous learning system
  - Self-Improvement Workflow: BEFORE/DURING/AFTER/MISTAKE RECOVERY/MAINTENANCE

- SuperClaude/Core/RULES.md: Agent Orchestration section updated
  - Split into Task Execution Layer + Self-Improvement Layer
  - Added orchestration flow diagram
  - Clarified PM Agent activates AFTER task completion

- README.md: Updated PM Agent description
  - "orchestrates all interactions" → "ensures continuous learning"

- Docs/User-Guide/agents.md: PM Agent section rewritten
  - Section: Orchestration Agent → Meta-Layer Agent
  - Expertise: Project orchestration → Self-improvement workflow executor
  - Examples: Task coordination → Post-implementation documentation

- PR_DOCUMENTATION.md: Comprehensive PR documentation added
  - Summary, motivation, changes, testing, breaking changes
  - Two-layer orchestration system diagram
  - Verification checklist

Integration Validated:
Tested with agiletec project's self-improvement-workflow.md:
 PM Agent aligns with existing BEFORE/DURING/AFTER/MISTAKE RECOVERY phases
 Complements (not competes with) existing workflow
 agiletec workflow defines WHAT, PM Agent defines WHO executes it

Breaking Changes: None
- Existing auto-activation continues unchanged
- Specialist agents unaffected
- User workflows remain the same
- New capability: Automatic documentation and knowledge maintenance

Value Proposition:
Transforms SuperClaude into a continuously learning system that accumulates
knowledge, prevents recurring mistakes, and maintains fresh documentation
without manual intervention.

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

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: add Claude Code conversation history management research

Research covering .jsonl file structure, performance impact, and retention policies.

Content:
- Claude Code .jsonl file format and message types
- Performance issues from GitHub (memory leaks, conversation compaction)
- Retention policies (consumer vs enterprise)
- Rotation recommendations based on actual data
- File history snapshot tracking mechanics

Source: Moved from agiletec project (research applicable to all Claude Code projects)

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

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: add Development documentation structure

Phase 1: Documentation Structure complete

- Add Docs/Development/ directory for development documentation
- Add ARCHITECTURE.md - System architecture with PM Agent meta-layer
- Add ROADMAP.md - 5-phase development plan with checkboxes
- Add TASKS.md - Daily task tracking with progress indicators
- Add PROJECT_STATUS.md - Current status dashboard and metrics
- Add pm-agent-integration.md - Implementation guide for PM Agent mode

This establishes comprehensive documentation foundation for:
- System architecture understanding
- Development planning and tracking
- Implementation guidance
- Progress visibility

Related: #pm-agent-mode #documentation #phase-1

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

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: PM Agent session lifecycle and PDCA implementation

Phase 2: PM Agent Mode Integration (Design Phase)

Commands/pm.md updates:
- Add "Always-Active Foundation Layer" concept
- Add Session Lifecycle (Session Start/During Work/Session End)
- Add PDCA Cycle (Plan/Do/Check/Act) automation
- Add Serena MCP Memory Integration (list/read/write_memory)
- Document auto-activation triggers

Agents/pm-agent.md updates:
- Add Session Start Protocol (MANDATORY auto-activation)
- Add During Work PDCA Cycle with example workflows
- Add Session End Protocol with state preservation
- Add PDCA Self-Evaluation Pattern
- Add Documentation Strategy (temp → patterns/mistakes)
- Add Memory Operations Reference

Key Features:
- Session start auto-activation for context restoration
- 30-minute checkpoint saves during work
- Self-evaluation with think_about_* operations
- Systematic documentation lifecycle
- Knowledge evolution to CLAUDE.md

Implementation Status:
-  Design complete (Commands/pm.md, Agents/pm-agent.md)
-  Implementation pending (Core components)
-  Serena MCP integration pending

Salvaged from mistaken development in ~/.claude directory

Related: #pm-agent-mode #session-lifecycle #pdca-cycle #phase-2

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

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: disable Serena MCP auto-browser launch

Disable web dashboard and GUI log window auto-launch in Serena MCP server
to prevent intrusive browser popups on startup. Users can still manually
access the dashboard at http://localhost:24282/dashboard/ if needed.

Changes:
- Add CLI flags to Serena run command:
  - --enable-web-dashboard false
  - --enable-gui-log-window false
- Ensures Git-tracked configuration (no reliance on ~/.serena/serena_config.yml)
- Aligns with AIRIS MCP Gateway integration approach

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

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: rename directories to lowercase for PEP8 compliance

- Rename superclaude/Agents -> superclaude/agents
- Rename superclaude/Commands -> superclaude/commands
- Rename superclaude/Core -> superclaude/core
- Rename superclaude/Examples -> superclaude/examples
- Rename superclaude/MCP -> superclaude/mcp
- Rename superclaude/Modes -> superclaude/modes

This change follows Python PEP8 naming conventions for package directories.

* style: fix PEP8 violations and update package name to lowercase

Changes:
- Format all Python files with black (43 files reformatted)
- Update package name from 'SuperClaude' to 'superclaude' in pyproject.toml
- Fix import statements to use lowercase package name
- Add missing imports (timedelta, __version__)
- Remove old SuperClaude.egg-info directory

PEP8 violations reduced from 2672 to 701 (mostly E501 line length due to black's 88 char vs flake8's 79 char limit).

* docs: add PM Agent development documentation

Add comprehensive PM Agent development documentation:
- PM Agent ideal workflow (7-phase autonomous cycle)
- Project structure understanding (Git vs installed environment)
- Installation flow understanding (CommandsComponent behavior)
- Task management system (current-tasks.md)

Purpose: Eliminate repeated explanations and enable autonomous PDCA cycles

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

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(pm-agent): add self-correcting execution and warning investigation culture

## Changes

### superclaude/commands/pm.md
- Add "Self-Correcting Execution" section with root cause analysis protocol
- Add "Warning/Error Investigation Culture" section enforcing zero-tolerance for dismissal
- Define error detection protocol: STOP → Investigate → Hypothesis → Different Solution → Execute
- Document anti-patterns (retry without understanding) and correct patterns (research-first)

### docs/Development/hypothesis-pm-autonomous-enhancement-2025-10-14.md
- Add PDCA workflow hypothesis document for PM Agent autonomous enhancement

## Rationale

PM Agent must never retry failed operations without understanding root causes.
All warnings and errors require investigation via context7/WebFetch/documentation
to ensure production-quality code and prevent technical debt accumulation.

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

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(installer): add airis-mcp-gateway MCP server option

## Changes

- Add airis-mcp-gateway to MCP server options in installer
- Configuration: GitHub-based installation via uvx
- Repository: https://github.com/oraios/airis-mcp-gateway
- Purpose: Dynamic MCP Gateway for zero-token baseline and on-demand tool loading

## Implementation

Added to setup/components/mcp.py self.mcp_servers dictionary with:
- install_method: github
- install_command: uvx test installation
- run_command: uvx runtime execution
- required: False (optional server)

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

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: kazuki <kazuki@kazukinoMacBook-Air.local>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
kazuki nakai
2025-10-14 12:17:09 +09:00
committed by GitHub
parent 302c5851b1
commit 050d5ea2ab
194 changed files with 9698 additions and 3693 deletions

View File

@@ -8,10 +8,10 @@ from .modes import ModesComponent
from .mcp_docs import MCPDocsComponent
__all__ = [
'CoreComponent',
'CommandsComponent',
'MCPComponent',
'AgentsComponent',
'ModesComponent',
'MCPDocsComponent'
]
"CoreComponent",
"CommandsComponent",
"MCPComponent",
"AgentsComponent",
"ModesComponent",
"MCPDocsComponent",
]

View File

@@ -11,20 +11,20 @@ from setup import __version__
class AgentsComponent(Component):
"""SuperClaude specialized AI agents component"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize agents component"""
super().__init__(install_dir, Path("agents"))
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "agents",
"version": __version__,
"description": "15 specialized AI agents with domain expertise and intelligent routing",
"category": "agents"
"category": "agents",
}
def get_metadata_modifications(self) -> Dict[str, Any]:
"""Get metadata modifications for agents"""
return {
@@ -33,27 +33,29 @@ class AgentsComponent(Component):
"version": __version__,
"installed": True,
"agents_count": len(self.component_files),
"install_directory": str(self.install_component_subdir)
"install_directory": str(self.install_component_subdir),
}
}
}
def _install(self, config: Dict[str, Any]) -> bool:
"""Install agents component"""
self.logger.info("Installing SuperClaude specialized agents...")
# Call parent install method
success = super()._install(config)
if success:
# Run post-install setup
success = self._post_install()
if success:
self.logger.success(f"Successfully installed {len(self.component_files)} specialized agents")
self.logger.success(
f"Successfully installed {len(self.component_files)} specialized agents"
)
return success
def _post_install(self) -> bool:
"""Post-install setup for agents"""
try:
@@ -61,27 +63,30 @@ class AgentsComponent(Component):
metadata_mods = self.get_metadata_modifications()
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with agents configuration")
# Add component registration
self.settings_manager.add_component_registration("agents", {
"version": __version__,
"category": "agents",
"agents_count": len(self.component_files),
"agents_list": self.component_files
})
self.settings_manager.add_component_registration(
"agents",
{
"version": __version__,
"category": "agents",
"agents_count": len(self.component_files),
"agents_list": self.component_files,
},
)
self.logger.info("Registered agents component in metadata")
return True
except Exception as e:
self.logger.error(f"Failed to complete agents post-install: {e}")
return False
def uninstall(self) -> bool:
"""Uninstall agents component"""
try:
self.logger.info("Uninstalling SuperClaude agents component...")
# Remove agent files
removed_count = 0
for filename in self.component_files:
@@ -91,15 +96,17 @@ class AgentsComponent(Component):
self.logger.debug(f"Removed agent: {filename}")
else:
self.logger.warning(f"Could not remove agent: {filename}")
# Remove agents directory if empty
try:
if self.install_component_subdir.exists() and not any(self.install_component_subdir.iterdir()):
if self.install_component_subdir.exists() and not any(
self.install_component_subdir.iterdir()
):
self.install_component_subdir.rmdir()
self.logger.debug("Removed empty agents directory")
except Exception as e:
self.logger.warning(f"Could not remove agents directory: {e}")
# Update metadata to remove agents component
try:
if self.settings_manager.is_component_installed("agents"):
@@ -107,33 +114,39 @@ class AgentsComponent(Component):
self.logger.info("Removed agents component from metadata")
except Exception as e:
self.logger.warning(f"Could not update metadata: {e}")
self.logger.success(f"Agents component uninstalled ({removed_count} agents removed)")
self.logger.success(
f"Agents component uninstalled ({removed_count} agents removed)"
)
return True
except Exception as e:
self.logger.exception(f"Unexpected error during agents uninstallation: {e}")
return False
def get_dependencies(self) -> List[str]:
"""Get component dependencies"""
return ["core"]
def update(self, config: Dict[str, Any]) -> bool:
"""Update agents component"""
try:
self.logger.info("Updating SuperClaude agents component...")
# Check current version
current_version = self.settings_manager.get_component_version("agents")
target_version = self.get_metadata()["version"]
if current_version == target_version:
self.logger.info(f"Agents component already at version {target_version}")
self.logger.info(
f"Agents component already at version {target_version}"
)
return True
self.logger.info(f"Updating agents component from {current_version} to {target_version}")
self.logger.info(
f"Updating agents component from {current_version} to {target_version}"
)
# Create backup of existing agents
backup_files = []
for filename in self.component_files:
@@ -143,49 +156,54 @@ class AgentsComponent(Component):
if backup_path:
backup_files.append(backup_path)
self.logger.debug(f"Backed up agent: {filename}")
# Perform installation (will overwrite existing files)
if self._install(config):
self.logger.success(f"Agents component updated to version {target_version}")
self.logger.success(
f"Agents component updated to version {target_version}"
)
return True
else:
# Restore backups on failure
self.logger.error("Agents update failed, restoring backups...")
for backup_path in backup_files:
try:
original_path = self.install_component_subdir / backup_path.name.replace('.backup', '')
original_path = (
self.install_component_subdir
/ backup_path.name.replace(".backup", "")
)
self.file_manager.copy_file(backup_path, original_path)
self.logger.debug(f"Restored {original_path.name}")
except Exception as e:
self.logger.warning(f"Could not restore {backup_path}: {e}")
return False
except Exception as e:
self.logger.exception(f"Unexpected error during agents update: {e}")
return False
def _get_source_dir(self) -> Path:
"""Get source directory for agent files"""
# Assume we're in SuperClaude/setup/components/agents.py
# and agent files are in SuperClaude/SuperClaude/Agents/
# Assume we're in superclaude/setup/components/agents.py
# and agent files are in superclaude/superclaude/Agents/
project_root = Path(__file__).parent.parent.parent
return project_root / "SuperClaude" / "Agents"
return project_root / "superclaude" / "agents"
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 directories and metadata
total_size += 5120 # ~5KB overhead
return total_size
def get_installation_summary(self) -> Dict[str, Any]:
"""Get installation summary"""
return {
@@ -195,46 +213,48 @@ class AgentsComponent(Component):
"agent_files": self.component_files,
"estimated_size": self.get_size_estimate(),
"install_directory": str(self.install_component_subdir),
"dependencies": self.get_dependencies()
"dependencies": self.get_dependencies(),
}
def validate_installation(self) -> Tuple[bool, List[str]]:
"""Validate that agents component is correctly installed"""
errors = []
# Check if agents directory exists
if not self.install_component_subdir.exists():
errors.append(f"Agents directory not found: {self.install_component_subdir}")
errors.append(
f"Agents directory not found: {self.install_component_subdir}"
)
return False, errors
# Check if all agent files exist
missing_agents = []
for filename in self.component_files:
agent_path = self.install_component_subdir / filename
if not agent_path.exists():
missing_agents.append(filename)
if missing_agents:
errors.append(f"Missing agent files: {missing_agents}")
# Check version in metadata
if not self.get_installed_version():
errors.append("Agents component not registered in metadata")
# Check if at least some standard agents are present
expected_agents = [
"system-architect.md",
"frontend-architect.md",
"frontend-architect.md",
"backend-architect.md",
"security-engineer.md"
"security-engineer.md",
]
missing_core_agents = []
for agent in expected_agents:
if agent not in self.component_files:
missing_core_agents.append(agent)
if missing_core_agents:
errors.append(f"Missing core agent files: {missing_core_agents}")
return len(errors) == 0, errors
return len(errors) == 0, errors

View File

@@ -8,22 +8,23 @@ from pathlib import Path
from ..core.base import Component
from setup import __version__
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": __version__,
"description": "SuperClaude slash command definitions",
"category": "commands"
"category": "commands",
}
def get_metadata_modifications(self) -> Dict[str, Any]:
"""Get metadata modifications for commands component"""
return {
@@ -31,16 +32,12 @@ class CommandsComponent(Component):
"commands": {
"version": __version__,
"installed": True,
"files_count": len(self.component_files)
"files_count": len(self.component_files),
}
},
"commands": {
"enabled": True,
"version": __version__,
"auto_update": False
}
"commands": {"enabled": True, "version": __version__, "auto_update": False},
}
def _install(self, config: Dict[str, Any]) -> bool:
"""Install commands component"""
self.logger.info("Installing SuperClaude command definitions...")
@@ -48,7 +45,7 @@ class CommandsComponent(Component):
# Check for and migrate existing commands from old location
self._migrate_existing_commands()
return super()._install(config);
return super()._install(config)
def _post_install(self) -> bool:
# Update metadata
@@ -58,27 +55,30 @@ class CommandsComponent(Component):
self.logger.info("Updated metadata with commands configuration")
# Add component registration to metadata
self.settings_manager.add_component_registration("commands", {
"version": __version__,
"category": "commands",
"files_count": len(self.component_files)
})
self.settings_manager.add_component_registration(
"commands",
{
"version": __version__,
"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):
@@ -86,11 +86,11 @@ class CommandsComponent(Component):
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():
@@ -99,12 +99,14 @@ class CommandsComponent(Component):
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")
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():
@@ -112,17 +114,19 @@ class CommandsComponent(Component):
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")
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"):
@@ -135,37 +139,45 @@ class CommandsComponent(Component):
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)")
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}")
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}")
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}")
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
@@ -174,10 +186,10 @@ class CommandsComponent(Component):
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:
@@ -185,35 +197,37 @@ class CommandsComponent(Component):
backup_path.unlink()
except Exception:
pass # Ignore cleanup errors
self.logger.success(f"Commands component updated to version {target_version}")
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('')
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
@@ -221,7 +235,7 @@ class CommandsComponent(Component):
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")
@@ -230,32 +244,34 @@ class CommandsComponent(Component):
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}")
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/
# 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"
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 {
@@ -265,66 +281,84 @@ class CommandsComponent(Component):
"command_files": self.component_files,
"estimated_size": self.get_size_estimate(),
"install_directory": str(self.install_dir / "commands" / "sc"),
"dependencies": self.get_dependencies()
"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")
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}")
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")
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")
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.")
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()]
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")
self.logger.debug(
"Removed empty old commands directory"
)
except Exception as e:
self.logger.debug(f"Could not remove old commands directory: {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}")

View File

@@ -10,45 +10,46 @@ from ..core.base import Component
from ..services.claude_md import CLAUDEMdService
from setup import __version__
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": __version__,
"description": "SuperClaude framework documentation and core files",
"category": "core"
"category": "core",
}
def get_metadata_modifications(self) -> Dict[str, Any]:
"""Get metadata modifications for SuperClaude"""
return {
"framework": {
"version": __version__,
"name": "SuperClaude",
"name": "superclaude",
"description": "AI-enhanced development framework for Claude Code",
"installation_type": "global",
"components": ["core"]
"components": ["core"],
},
"superclaude": {
"enabled": True,
"version": __version__,
"profile": "default",
"auto_update": False
}
"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);
return super()._install(config)
def _post_install(self) -> bool:
# Create or update metadata
@@ -56,19 +57,24 @@ class CoreComponent(Component):
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": __version__,
"category": "core",
"files_count": len(self.component_files)
})
self.settings_manager.add_component_registration(
"core",
{
"version": __version__,
"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")
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
@@ -79,24 +85,25 @@ class CoreComponent(Component):
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}")
# Update CLAUDE.md with core framework imports
try:
manager = CLAUDEMdService(self.install_dir)
manager.add_imports(self.component_files, category="Core Framework")
self.logger.info("Updated CLAUDE.md with core framework imports")
except Exception as e:
self.logger.warning(f"Failed to update CLAUDE.md with core framework imports: {e}")
self.logger.warning(
f"Failed to update CLAUDE.md with core framework imports: {e}"
)
# Don't fail the whole installation for this
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:
@@ -106,7 +113,7 @@ class CoreComponent(Component):
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"):
@@ -121,33 +128,37 @@ class CoreComponent(Component):
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)")
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}")
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:
@@ -157,10 +168,10 @@ class CoreComponent(Component):
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:
@@ -168,29 +179,31 @@ class CoreComponent(Component):
backup_path.unlink()
except Exception:
pass # Ignore cleanup errors
self.logger.success(f"Core component updated to version {target_version}")
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('')
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
@@ -198,7 +211,7 @@ class CoreComponent(Component):
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")
@@ -207,8 +220,10 @@ class CoreComponent(Component):
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}")
errors.append(
f"Version mismatch: installed {installed_version}, expected {expected_version}"
)
# Check metadata structure
try:
framework_config = self.settings_manager.get_metadata_setting("framework")
@@ -221,31 +236,31 @@ class CoreComponent(Component):
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/
# 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"
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 {
@@ -255,5 +270,5 @@ class CoreComponent(Component):
"framework_files": self.component_files,
"estimated_size": self.get_size_estimate(),
"install_directory": str(self.install_dir),
"dependencies": self.get_dependencies()
"dependencies": self.get_dependencies(),
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,13 +12,13 @@ from ..services.claude_md import CLAUDEMdService
class MCPDocsComponent(Component):
"""MCP documentation component - installs docs for selected MCP servers"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize MCP docs component"""
# Initialize attributes before calling parent constructor
# because parent calls _discover_component_files() which needs these
self.selected_servers: List[str] = []
# Map server names to documentation files
self.server_docs_map = {
"context7": "MCP_Context7.md",
@@ -29,18 +29,18 @@ class MCPDocsComponent(Component):
"serena": "MCP_Serena.md",
"morphllm": "MCP_Morphllm.md",
"morphllm-fast-apply": "MCP_Morphllm.md", # Handle both naming conventions
"tavily": "MCP_Tavily.md"
"tavily": "MCP_Tavily.md",
}
super().__init__(install_dir, Path(""))
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "mcp_docs",
"version": __version__,
"description": "MCP server documentation and usage guides",
"category": "documentation"
"category": "documentation",
}
def is_reinstallable(self) -> bool:
@@ -54,11 +54,11 @@ class MCPDocsComponent(Component):
"""Set which MCP servers were selected for documentation installation"""
self.selected_servers = selected_servers
self.logger.debug(f"MCP docs will be installed for: {selected_servers}")
def get_files_to_install(self) -> List[Tuple[Path, Path]]:
"""
Return list of files to install based on selected MCP servers
Returns:
List of tuples (source_path, target_path)
"""
@@ -73,12 +73,16 @@ class MCPDocsComponent(Component):
target = self.install_dir / doc_file
if source.exists():
files.append((source, target))
self.logger.debug(f"Will install documentation for {server_name}: {doc_file}")
self.logger.debug(
f"Will install documentation for {server_name}: {doc_file}"
)
else:
self.logger.warning(f"Documentation file not found for {server_name}: {doc_file}")
self.logger.warning(
f"Documentation file not found for {server_name}: {doc_file}"
)
return files
def _discover_component_files(self) -> List[str]:
"""
Override parent method to dynamically discover files based on selected servers
@@ -90,7 +94,7 @@ class MCPDocsComponent(Component):
if server_name in self.server_docs_map:
files.append(self.server_docs_map[server_name])
return files
def _detect_existing_mcp_servers_from_config(self) -> List[str]:
"""Detect existing MCP servers from Claude Desktop config"""
detected_servers = []
@@ -101,8 +105,16 @@ class MCPDocsComponent(Component):
self.install_dir / "claude_desktop_config.json",
Path.home() / ".claude" / "claude_desktop_config.json",
Path.home() / ".claude.json", # Claude CLI config
Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json", # Windows
Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json", # macOS
Path.home()
/ "AppData"
/ "Roaming"
/ "Claude"
/ "claude_desktop_config.json", # Windows
Path.home()
/ "Library"
/ "Application Support"
/ "Claude"
/ "claude_desktop_config.json", # macOS
]
config_file = None
@@ -116,7 +128,8 @@ class MCPDocsComponent(Component):
return detected_servers
import json
with open(config_file, 'r') as f:
with open(config_file, "r") as f:
config = json.load(f)
# Extract MCP server names from mcpServers section
@@ -128,7 +141,9 @@ class MCPDocsComponent(Component):
detected_servers.append(normalized_name)
if detected_servers:
self.logger.info(f"Detected existing MCP servers from config: {detected_servers}")
self.logger.info(
f"Detected existing MCP servers from config: {detected_servers}"
)
except Exception as e:
self.logger.warning(f"Could not read Claude Desktop config: {e}")
@@ -152,7 +167,7 @@ class MCPDocsComponent(Component):
"serena": "serena",
"morphllm": "morphllm",
"morphllm-fast-apply": "morphllm",
"morph": "morphllm"
"morph": "morphllm",
}
return name_mappings.get(server_name)
@@ -169,7 +184,9 @@ class MCPDocsComponent(Component):
selected_servers = config.get("selected_mcp_servers", [])
# Get previously documented servers from metadata
previous_servers = self.settings_manager.get_metadata_setting("components.mcp_docs.servers_documented", [])
previous_servers = self.settings_manager.get_metadata_setting(
"components.mcp_docs.servers_documented", []
)
# Merge all server lists
all_servers = list(set(detected_servers + selected_servers + previous_servers))
@@ -178,13 +195,17 @@ class MCPDocsComponent(Component):
valid_servers = [s for s in all_servers if s in self.server_docs_map]
if not valid_servers:
self.logger.info("No MCP servers detected or selected for documentation installation")
self.logger.info(
"No MCP servers detected or selected for documentation installation"
)
# Still proceed to update metadata
self.set_selected_servers([])
self.component_files = []
return self._post_install()
self.logger.info(f"Installing documentation for MCP servers: {', '.join(valid_servers)}")
self.logger.info(
f"Installing documentation for MCP servers: {', '.join(valid_servers)}"
)
if detected_servers:
self.logger.info(f" - Detected from config: {detected_servers}")
if selected_servers:
@@ -225,12 +246,16 @@ class MCPDocsComponent(Component):
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)} documentation files copied successfully")
self.logger.error(
f"Only {success_count}/{len(files_to_install)} documentation files copied successfully"
)
return False
# Update component_files to only include successfully copied files
self.component_files = successfully_copied_files
self.logger.success(f"MCP documentation installed successfully ({success_count} files for {len(valid_servers)} servers)")
self.logger.success(
f"MCP documentation installed successfully ({success_count} files for {len(valid_servers)} servers)"
)
return self._post_install()
@@ -244,36 +269,38 @@ class MCPDocsComponent(Component):
"version": __version__,
"installed": True,
"files_count": len(self.component_files),
"servers_documented": self.selected_servers
"servers_documented": self.selected_servers,
}
}
}
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with MCP docs component registration")
# Update CLAUDE.md with MCP documentation imports
try:
manager = CLAUDEMdService(self.install_dir)
manager.add_imports(self.component_files, category="MCP Documentation")
self.logger.info("Updated CLAUDE.md with MCP documentation imports")
except Exception as e:
self.logger.warning(f"Failed to update CLAUDE.md with MCP documentation imports: {e}")
self.logger.warning(
f"Failed to update CLAUDE.md with MCP documentation imports: {e}"
)
# Don't fail the whole installation for this
return True
except Exception as e:
self.logger.error(f"Failed to update metadata: {e}")
return False
def uninstall(self) -> bool:
"""Uninstall MCP documentation component"""
try:
self.logger.info("Uninstalling MCP documentation component...")
# Remove all MCP documentation files
removed_count = 0
source_dir = self._get_source_dir()
if source_dir and source_dir.exists():
# Remove all possible MCP doc files
for doc_file in self.server_docs_map.values():
@@ -281,7 +308,7 @@ class MCPDocsComponent(Component):
if self.file_manager.remove_file(file_path):
removed_count += 1
self.logger.debug(f"Removed {doc_file}")
# Remove mcp directory if empty
try:
if self.install_component_subdir.exists():
@@ -291,7 +318,7 @@ class MCPDocsComponent(Component):
self.logger.debug("Removed empty mcp directory")
except Exception as e:
self.logger.warning(f"Could not remove mcp directory: {e}")
# Update settings.json
try:
if self.settings_manager.is_component_installed("mcp_docs"):
@@ -299,36 +326,40 @@ class MCPDocsComponent(Component):
self.logger.info("Removed MCP docs component from settings.json")
except Exception as e:
self.logger.warning(f"Could not update settings.json: {e}")
self.logger.success(f"MCP documentation uninstalled ({removed_count} files removed)")
self.logger.success(
f"MCP documentation uninstalled ({removed_count} files removed)"
)
return True
except Exception as e:
self.logger.exception(f"Unexpected error during MCP docs uninstallation: {e}")
self.logger.exception(
f"Unexpected error during MCP docs uninstallation: {e}"
)
return False
def get_dependencies(self) -> List[str]:
"""Get dependencies"""
return ["core"]
def _get_source_dir(self) -> Optional[Path]:
"""Get source directory for MCP documentation files"""
# Assume we're in SuperClaude/setup/components/mcp_docs.py
# and MCP docs are in SuperClaude/SuperClaude/MCP/
# Assume we're in superclaude/setup/components/mcp_docs.py
# and MCP docs are in superclaude/superclaude/MCP/
project_root = Path(__file__).parent.parent.parent
mcp_dir = project_root / "SuperClaude" / "MCP"
mcp_dir = project_root / "superclaude" / "mcp"
# Return None if directory doesn't exist to prevent warning
if not mcp_dir.exists():
return None
return mcp_dir
def get_size_estimate(self) -> int:
"""Get estimated installation size"""
source_dir = self._get_source_dir()
total_size = 0
if source_dir and source_dir.exists() and self.selected_servers:
for server_name in self.selected_servers:
if server_name in self.server_docs_map:
@@ -336,8 +367,8 @@ class MCPDocsComponent(Component):
file_path = source_dir / doc_file
if file_path.exists():
total_size += file_path.stat().st_size
# Minimum size estimate
total_size = max(total_size, 10240) # At least 10KB
return total_size
return total_size

View File

@@ -12,20 +12,20 @@ from ..services.claude_md import CLAUDEMdService
class ModesComponent(Component):
"""SuperClaude behavioral modes component"""
def __init__(self, install_dir: Optional[Path] = None):
"""Initialize modes component"""
super().__init__(install_dir, Path(""))
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "modes",
"version": __version__,
"description": "7 behavioral modes for enhanced Claude Code operation",
"category": "modes"
"category": "modes",
}
def _install(self, config: Dict[str, Any]) -> bool:
"""Install modes component"""
self.logger.info("Installing SuperClaude behavioral modes...")
@@ -48,7 +48,7 @@ class ModesComponent(Component):
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}")
@@ -56,10 +56,14 @@ class ModesComponent(Component):
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)} mode files copied successfully")
self.logger.error(
f"Only {success_count}/{len(files_to_install)} mode files copied successfully"
)
return False
self.logger.success(f"Modes component installed successfully ({success_count} mode files)")
self.logger.success(
f"Modes component installed successfully ({success_count} mode files)"
)
return self._post_install()
@@ -72,39 +76,41 @@ class ModesComponent(Component):
"modes": {
"version": __version__,
"installed": True,
"files_count": len(self.component_files)
"files_count": len(self.component_files),
}
}
}
self.settings_manager.update_metadata(metadata_mods)
self.logger.info("Updated metadata with modes component registration")
# Update CLAUDE.md with mode imports
try:
manager = CLAUDEMdService(self.install_dir)
manager.add_imports(self.component_files, category="Behavioral Modes")
self.logger.info("Updated CLAUDE.md with mode imports")
except Exception as e:
self.logger.warning(f"Failed to update CLAUDE.md with mode imports: {e}")
self.logger.warning(
f"Failed to update CLAUDE.md with mode imports: {e}"
)
# Don't fail the whole installation for this
return True
except Exception as e:
self.logger.error(f"Failed to update metadata: {e}")
return False
def uninstall(self) -> bool:
"""Uninstall modes component"""
try:
self.logger.info("Uninstalling SuperClaude modes component...")
# Remove mode files
removed_count = 0
for _, target in self.get_files_to_install():
if self.file_manager.remove_file(target):
removed_count += 1
self.logger.debug(f"Removed {target.name}")
# Remove modes directory if empty
try:
if self.install_component_subdir.exists():
@@ -114,7 +120,7 @@ class ModesComponent(Component):
self.logger.debug("Removed empty modes directory")
except Exception as e:
self.logger.warning(f"Could not remove modes directory: {e}")
# Update settings.json
try:
if self.settings_manager.is_component_installed("modes"):
@@ -122,43 +128,45 @@ class ModesComponent(Component):
self.logger.info("Removed modes component from settings.json")
except Exception as e:
self.logger.warning(f"Could not update settings.json: {e}")
self.logger.success(f"Modes component uninstalled ({removed_count} files removed)")
self.logger.success(
f"Modes component uninstalled ({removed_count} files removed)"
)
return True
except Exception as e:
self.logger.exception(f"Unexpected error during modes uninstallation: {e}")
return False
def get_dependencies(self) -> List[str]:
"""Get dependencies"""
return ["core"]
def _get_source_dir(self) -> Optional[Path]:
"""Get source directory for mode files"""
# Assume we're in SuperClaude/setup/components/modes.py
# and mode files are in SuperClaude/SuperClaude/Modes/
# Assume we're in superclaude/setup/components/modes.py
# and mode files are in superclaude/superclaude/Modes/
project_root = Path(__file__).parent.parent.parent
modes_dir = project_root / "SuperClaude" / "Modes"
modes_dir = project_root / "superclaude" / "modes"
# Return None if directory doesn't exist to prevent warning
if not modes_dir.exists():
return None
return modes_dir
def get_size_estimate(self) -> int:
"""Get estimated installation size"""
source_dir = self._get_source_dir()
total_size = 0
if source_dir and source_dir.exists():
for filename in self.component_files:
file_path = source_dir / filename
if file_path.exists():
total_size += file_path.stat().st_size
# Minimum size estimate
total_size = max(total_size, 20480) # At least 20KB
return total_size
return total_size