SuperClaude/src/superclaude/cli/install_commands.py

164 lines
4.5 KiB
Python
Raw Normal View History

feat: add comprehensive test suite, CI/CD workflows, and install command Major improvements to SuperClaude Framework infrastructure and testing: ## New Features - Add 'superclaude install' command to install slash commands (/research, /index-repo, /agent, /recommend) - Create comprehensive test suite with 71 tests (70 passing, 1 skipped) - Add GitHub Actions CI/CD workflows for automated testing - Add essential documentation files (PLANNING.md, TASK.md, KNOWLEDGE.md) ## Testing - tests/unit/: 59 tests covering PM Agent components - test_confidence.py: 13 tests for ConfidenceChecker - test_self_check.py: 14 tests for SelfCheckProtocol - test_reflexion.py: 9 tests for ReflexionPattern - test_token_budget.py: 12 tests for TokenBudgetManager - test_cli_install.py: 12 tests for install command (NEW) - tests/integration/: 11 tests for pytest plugin integration - tests/conftest.py: Shared fixtures for all tests ## CI/CD Workflows - .github/workflows/test.yml: Comprehensive test matrix - Tests on Python 3.10, 3.11, 3.12 - Lint and format checks with ruff - Pytest plugin verification - SuperClaude doctor health checks - Coverage reporting with Codecov - .github/workflows/quick-check.yml: Fast PR validation (~2-3 min) - .github/workflows/README.md: Workflow documentation ## Documentation - PLANNING.md: Architecture, design principles, absolute rules - TASK.md: Current tasks, priorities, backlog - KNOWLEDGE.md: Accumulated insights, best practices, troubleshooting ## Bug Fixes - Fix .gitignore contradictions (remove conflicting Claude Code patterns) - Fix TokenBudgetManager to properly validate and default invalid complexity - Update package.json version to 4.1.6 (sync with VERSION file) ## CLI Improvements - src/superclaude/cli/install_commands.py: Command installation logic - src/superclaude/cli/main.py: Add 'install' command with --list and --force options - README.md: Update installation instructions with correct commands ## Breaking Changes None - all changes are backwards compatible ## Migration Guide Users should run 'superclaude install' after upgrading to install slash commands Fixes #466 (indirectly by clarifying installation process) Refs #419 (plugin system - documentation updated) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 18:21:22 +01:00
"""
Command Installation
Installs SuperClaude slash commands to ~/.claude/commands/ directory.
"""
from pathlib import Path
from typing import List, Tuple
import shutil
def install_commands(
target_path: Path = None,
force: bool = False
) -> Tuple[bool, str]:
"""
Install all SuperClaude commands to Claude Code
Args:
target_path: Target installation directory (default: ~/.claude/commands)
force: Force reinstall if commands exist
Returns:
Tuple of (success: bool, message: str)
"""
# Default to ~/.claude/commands
if target_path is None:
target_path = Path.home() / ".claude" / "commands"
# Get command source directory
command_source = _get_commands_source()
if not command_source or not command_source.exists():
return False, f"Command source directory not found: {command_source}"
# Create target directory
target_path.mkdir(parents=True, exist_ok=True)
# Get all command files
command_files = list(command_source.glob("*.md"))
if not command_files:
return False, f"No command files found in {command_source}"
installed_commands = []
skipped_commands = []
failed_commands = []
for command_file in command_files:
target_file = target_path / command_file.name
command_name = command_file.stem
# Check if already exists
if target_file.exists() and not force:
skipped_commands.append(command_name)
continue
# Copy command file
try:
shutil.copy2(command_file, target_file)
installed_commands.append(command_name)
except Exception as e:
failed_commands.append(f"{command_name}: {e}")
# Build result message
messages = []
if installed_commands:
messages.append(f"✅ Installed {len(installed_commands)} commands:")
for cmd in installed_commands:
messages.append(f" - /{cmd}")
if skipped_commands:
messages.append(f"\n⚠️ Skipped {len(skipped_commands)} existing commands (use --force to reinstall):")
for cmd in skipped_commands:
messages.append(f" - /{cmd}")
if failed_commands:
messages.append(f"\n❌ Failed to install {len(failed_commands)} commands:")
for fail in failed_commands:
messages.append(f" - {fail}")
if not installed_commands and not skipped_commands:
return False, "No commands were installed"
messages.append(f"\n📁 Installation directory: {target_path}")
messages.append("\n💡 Tip: Restart Claude Code to use the new commands")
success = len(failed_commands) == 0
return success, "\n".join(messages)
def _get_commands_source() -> Path:
"""
Get source directory for commands
Commands are stored in:
plugins/superclaude/commands/
Returns:
Path to commands source directory
"""
# Get package root (src/superclaude/)
package_root = Path(__file__).resolve().parent.parent
# Check if running from source checkout
# package_root = src/superclaude/
# repo_root = src/superclaude/../../ = project root
repo_root = package_root.parent.parent
# Try plugins/superclaude/commands/ in project root
commands_dir = repo_root / "plugins" / "superclaude" / "commands"
if commands_dir.exists():
return commands_dir
# If not found, try relative to package (for installed package)
# This would be in site-packages/superclaude/commands/
alt_commands_dir = package_root / "commands"
if alt_commands_dir.exists():
return alt_commands_dir
return commands_dir # Return first candidate even if doesn't exist
def list_available_commands() -> List[str]:
"""
List all available commands
Returns:
List of command names
"""
command_source = _get_commands_source()
if not command_source.exists():
return []
commands = []
for file in command_source.glob("*.md"):
if file.stem != "README":
commands.append(file.stem)
return sorted(commands)
def list_installed_commands() -> List[str]:
"""
List installed commands in ~/.claude/commands/
Returns:
List of installed command names
"""
commands_dir = Path.home() / ".claude" / "commands"
if not commands_dir.exists():
return []
installed = []
for file in commands_dir.glob("*.md"):
if file.stem != "README":
installed.append(file.stem)
return sorted(installed)