mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-29 16:16:08 +00:00
fix: unify metadata location and improve installer UX
## Changes ### Unified Metadata Location - All components now use `~/.claude/.superclaude-metadata.json` - Previously split between root and superclaude subdirectory - Automatic migration from old location on first load - Eliminates confusion from duplicate metadata files ### Improved Installation Messages - Changed WARNING to INFO for existing installations - Message now clearly states "will be updated" instead of implying problem - Reduces user confusion during reinstalls/updates ### Updated Makefile - `make install`: Development mode (uv, local source, editable) - `make install-release`: Production mode (pipx, from PyPI) - `make dev`: Alias for install - Improved help output with categorized commands ## Technical Details **Metadata Unification** (setup/services/settings.py): - SettingsService now always uses `~/.claude/.superclaude-metadata.json` - Added `_migrate_old_metadata()` for automatic migration - Deep merge strategy preserves existing data - Old file backed up as `.superclaude-metadata.json.migrated` **User File Protection**: - Verified: User-created files preserved during updates - Only SuperClaude-managed files (tracked in metadata) are updated - Obsolete framework files automatically removed ## Migration Path Existing installations automatically migrate on next `make install`: 1. Old metadata detected at `~/.claude/superclaude/.superclaude-metadata.json` 2. Merged into `~/.claude/.superclaude-metadata.json` 3. Old file backed up 4. No user action required 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
50
Makefile
50
Makefile
@@ -1,17 +1,21 @@
|
|||||||
.PHONY: install dev test clean lint format uninstall update translate help
|
.PHONY: install install-release dev test clean lint format uninstall update translate help
|
||||||
|
|
||||||
# Full installation (dependencies + SuperClaude components)
|
# Development installation (local source, editable)
|
||||||
install:
|
install:
|
||||||
@echo "Installing SuperClaude Framework..."
|
@echo "Installing SuperClaude Framework (development mode)..."
|
||||||
uv pip install -e ".[dev]"
|
uv pip install -e ".[dev]"
|
||||||
uv run superclaude install
|
uv run superclaude install
|
||||||
|
|
||||||
# Install dependencies and SuperClaude (for development)
|
# Production installation (from PyPI, recommended for users)
|
||||||
dev:
|
install-release:
|
||||||
@echo "Installing development dependencies..."
|
@echo "Installing SuperClaude Framework (production mode)..."
|
||||||
uv pip install -e ".[dev]"
|
@echo "Using pipx for isolated environment..."
|
||||||
@echo "Installing SuperClaude components..."
|
pipx install SuperClaude
|
||||||
uv run superclaude install
|
pipx upgrade SuperClaude
|
||||||
|
superclaude install
|
||||||
|
|
||||||
|
# Alias for development installation
|
||||||
|
dev: install
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
test:
|
test:
|
||||||
@@ -69,13 +73,21 @@ translate:
|
|||||||
help:
|
help:
|
||||||
@echo "SuperClaude Framework - Available commands:"
|
@echo "SuperClaude Framework - Available commands:"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make install - Full installation (dependencies + components)"
|
@echo "Installation:"
|
||||||
@echo " make dev - Install development dependencies only"
|
@echo " make install - Development installation (local source, editable with uv)"
|
||||||
@echo " make test - Run tests"
|
@echo " make install-release - Production installation (from PyPI with pipx)"
|
||||||
@echo " make lint - Run linter"
|
@echo " make dev - Alias for 'make install'"
|
||||||
@echo " make format - Format code"
|
@echo ""
|
||||||
@echo " make clean - Clean build artifacts"
|
@echo "Development:"
|
||||||
@echo " make uninstall - Uninstall SuperClaude components"
|
@echo " make test - Run tests"
|
||||||
@echo " make update - Update SuperClaude components"
|
@echo " make lint - Run linter"
|
||||||
@echo " make translate - Translate README to Chinese and Japanese (requires Ollama)"
|
@echo " make format - Format code"
|
||||||
@echo " make help - Show this help message"
|
@echo " make clean - Clean build artifacts"
|
||||||
|
@echo ""
|
||||||
|
@echo "Maintenance:"
|
||||||
|
@echo " make uninstall - Uninstall SuperClaude components"
|
||||||
|
@echo " make update - Update SuperClaude components"
|
||||||
|
@echo ""
|
||||||
|
@echo "Documentation:"
|
||||||
|
@echo " make translate - Translate README to Chinese and Japanese (requires Ollama)"
|
||||||
|
@echo " make help - Show this help message"
|
||||||
|
|||||||
@@ -720,8 +720,8 @@ def run(args: argparse.Namespace) -> int:
|
|||||||
# Check for existing installation
|
# Check for existing installation
|
||||||
if args.install_dir.exists() and not args.force:
|
if args.install_dir.exists() and not args.force:
|
||||||
if not args.dry_run:
|
if not args.dry_run:
|
||||||
logger.warning(
|
logger.info(
|
||||||
f"Installation directory already exists: {args.install_dir}"
|
f"Existing installation found: {args.install_dir} (will be updated)"
|
||||||
)
|
)
|
||||||
if not args.yes and not confirm(
|
if not args.yes and not confirm(
|
||||||
"Continue and update existing installation?", default=False
|
"Continue and update existing installation?", default=False
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ class SettingsService:
|
|||||||
"""
|
"""
|
||||||
self.install_dir = install_dir
|
self.install_dir = install_dir
|
||||||
self.settings_file = install_dir / "settings.json"
|
self.settings_file = install_dir / "settings.json"
|
||||||
self.metadata_file = install_dir / ".superclaude-metadata.json"
|
|
||||||
|
# Always use ~/.claude/ for metadata (unified location)
|
||||||
|
# This ensures all components share the same metadata regardless of install_dir
|
||||||
|
from ..utils.paths import get_home_directory
|
||||||
|
self.metadata_root = get_home_directory() / ".claude"
|
||||||
|
self.metadata_file = self.metadata_root / ".superclaude-metadata.json"
|
||||||
self.backup_dir = install_dir / "backups" / "settings"
|
self.backup_dir = install_dir / "backups" / "settings"
|
||||||
|
|
||||||
def load_settings(self) -> Dict[str, Any]:
|
def load_settings(self) -> Dict[str, Any]:
|
||||||
@@ -74,6 +79,9 @@ class SettingsService:
|
|||||||
Returns:
|
Returns:
|
||||||
Metadata dict (empty if file doesn't exist)
|
Metadata dict (empty if file doesn't exist)
|
||||||
"""
|
"""
|
||||||
|
# Migrate from old location if needed
|
||||||
|
self._migrate_old_metadata()
|
||||||
|
|
||||||
if not self.metadata_file.exists():
|
if not self.metadata_file.exists():
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -447,6 +455,44 @@ class SettingsService:
|
|||||||
|
|
||||||
return backup_file
|
return backup_file
|
||||||
|
|
||||||
|
def _migrate_old_metadata(self) -> None:
|
||||||
|
"""
|
||||||
|
Migrate metadata from old location (~/.claude/superclaude/) to unified location (~/.claude/)
|
||||||
|
This handles the transition from split metadata files to a single unified file.
|
||||||
|
"""
|
||||||
|
# Old metadata location (in superclaude subdirectory)
|
||||||
|
old_metadata_file = self.metadata_root / "superclaude" / ".superclaude-metadata.json"
|
||||||
|
|
||||||
|
# If unified metadata already exists, skip migration
|
||||||
|
if self.metadata_file.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
# If old metadata exists, merge it into the new location
|
||||||
|
if old_metadata_file.exists():
|
||||||
|
try:
|
||||||
|
with open(old_metadata_file, "r", encoding="utf-8") as f:
|
||||||
|
old_metadata = json.load(f)
|
||||||
|
|
||||||
|
# Load current metadata (if any)
|
||||||
|
current_metadata = {}
|
||||||
|
if self.metadata_file.exists():
|
||||||
|
with open(self.metadata_file, "r", encoding="utf-8") as f:
|
||||||
|
current_metadata = json.load(f)
|
||||||
|
|
||||||
|
# Deep merge old into current
|
||||||
|
merged_metadata = self._deep_merge(current_metadata, old_metadata)
|
||||||
|
|
||||||
|
# Save to unified location
|
||||||
|
self.save_metadata(merged_metadata)
|
||||||
|
|
||||||
|
# Optionally backup old file (don't delete yet for safety)
|
||||||
|
backup_file = old_metadata_file.parent / ".superclaude-metadata.json.migrated"
|
||||||
|
shutil.copy2(old_metadata_file, backup_file)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Log but don't fail - old metadata migration is optional
|
||||||
|
pass
|
||||||
|
|
||||||
def _cleanup_old_backups(self, keep_count: int = 10) -> None:
|
def _cleanup_old_backups(self, keep_count: int = 10) -> None:
|
||||||
"""
|
"""
|
||||||
Remove old backup files, keeping only the most recent
|
Remove old backup files, keeping only the most recent
|
||||||
|
|||||||
Reference in New Issue
Block a user