Claude hooks auto-backup: manual (backup_20250720_091250)

This commit is contained in:
Ryan Malloy 2025-07-20 03:12:51 -06:00
parent 9445e09c48
commit 392833187e
135 changed files with 16151 additions and 3439 deletions

View file

@ -1,133 +0,0 @@
#!/usr/bin/env python3
"""Claude Hooks Installation Script for NPM Distribution"""
import os
import sys
import json
import shutil
import subprocess
from pathlib import Path
# Colors for terminal output
class Colors:
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
NC = '\033[0m' # No Color
def print_colored(message, color):
print(f"{color}{message}{Colors.NC}")
def print_status(message, status="INFO"):
colors = {
"SUCCESS": Colors.GREEN,
"ERROR": Colors.RED,
"WARNING": Colors.YELLOW,
"INFO": Colors.BLUE
}
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
def main():
print_colored("Claude Code Hooks Initialization", Colors.BLUE)
print("=" * 40)
# Get package root (npm package location)
package_root = Path(__file__).parent.parent
claude_config_dir = Path.home() / ".config" / "claude"
hooks_config_file = claude_config_dir / "hooks.json"
# Check for existing installation
print("Checking for existing Claude Hooks installation...")
if hooks_config_file.exists():
print_status("Claude Hooks already configured!", "WARNING")
response = input("Overwrite existing configuration? (y/N): ").lower()
if response != 'y':
print("Installation cancelled.")
return 1
# Check Python version
print("Checking Python version...")
if sys.version_info < (3, 8):
print_status(f"Python 3.8+ required, found {sys.version_info.major}.{sys.version_info.minor}", "ERROR")
return 1
print_status(f"Python {sys.version_info.major}.{sys.version_info.minor} found", "SUCCESS")
# Create Claude config directory
print("Creating Claude config directory...")
claude_config_dir.mkdir(parents=True, exist_ok=True)
print_status("Claude config directory ready", "SUCCESS")
# Generate hooks configuration
print("Generating hooks configuration...")
hooks_template = package_root / "config" / "hooks.json.template"
if hooks_template.exists():
with open(hooks_template, 'r') as f:
config_content = f.read()
# Replace template variables
config_content = config_content.replace("{{INSTALL_PATH}}", str(package_root))
with open(hooks_config_file, 'w') as f:
f.write(config_content)
print_status("Hooks configuration generated", "SUCCESS")
else:
# Fallback: create basic configuration
hooks_config = {
"hooks": {
"UserPromptSubmit": f"python3 {package_root}/hooks/context_monitor.py",
"PreToolUse": {
"Bash": f"python3 {package_root}/hooks/command_validator.py"
},
"PostToolUse": {
"*": f"python3 {package_root}/hooks/session_logger.py"
},
"Stop": f"python3 {package_root}/hooks/session_finalizer.py"
}
}
with open(hooks_config_file, 'w') as f:
json.dump(hooks_config, f, indent=2)
print_status("Basic hooks configuration created", "SUCCESS")
# Test hook scripts
print("Testing hook scripts...")
test_hook = package_root / "hooks" / "context_monitor.py"
if test_hook.exists():
try:
result = subprocess.run([
sys.executable, str(test_hook)
], input='{"prompt": "test"}', text=True, capture_output=True, timeout=10)
if result.returncode == 0:
print_status("Hook scripts working", "SUCCESS")
else:
print_status("Hook scripts may have issues", "WARNING")
except subprocess.TimeoutExpired:
print_status("Hook script test timeout", "WARNING")
except Exception as e:
print_status(f"Hook test error: {e}", "WARNING")
else:
print_status("Hook scripts not found", "WARNING")
print()
print_colored("Initialization Complete!", Colors.GREEN)
print()
print_colored("Next Steps:", Colors.BLUE)
print("1. Restart Claude Code to activate the hooks")
print("2. Test with: claude-hooks test")
print("3. Try commands that commonly fail (pip vs pip3) to see learning in action")
print()
print(f"📁 Installation directory: {package_root}")
print(f"⚙️ Configuration file: {hooks_config_file}")
print()
print_colored("The hooks will start learning from your usage patterns automatically.", Colors.YELLOW)
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -1,114 +0,0 @@
#!/usr/bin/env python3
"""Manual Backup Script for Claude Hooks"""
import os
import sys
import json
from pathlib import Path
from datetime import datetime
# Add lib directory to path
sys.path.insert(0, str(Path(__file__).parent.parent / "lib"))
try:
from backup_manager import BackupManager
from session_state import SessionState
except ImportError as e:
print(f"Error importing backup modules: {e}")
print("Please ensure Claude Hooks is properly installed.")
sys.exit(1)
# Colors for terminal output
class Colors:
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
NC = '\033[0m' # No Color
def print_colored(message, color):
print(f"{color}{message}{Colors.NC}")
def print_status(message, status="INFO"):
colors = {
"SUCCESS": Colors.GREEN,
"ERROR": Colors.RED,
"WARNING": Colors.YELLOW,
"INFO": Colors.BLUE
}
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
def main():
print_colored("Claude Hooks Manual Backup", Colors.BLUE)
print("=" * 30)
# Get package root and setup paths
package_root = Path(__file__).parent.parent
runtime_dir = package_root / ".claude_hooks"
try:
# Initialize backup manager
backup_manager = BackupManager(str(runtime_dir / "backups"))
session_state = SessionState(str(runtime_dir))
print("Creating manual backup...")
# Get current working directory (where user is working)
cwd = Path.cwd()
print(f"Backing up from: {cwd}")
# Create backup context
backup_context = {
"trigger": "manual",
"timestamp": datetime.now().isoformat(),
"cwd": str(cwd),
"session_info": session_state.get_session_summary()
}
# Perform backup
backup_id = backup_manager.create_backup(
project_path=str(cwd),
context=backup_context,
force=True # Allow manual backup even if recent backup exists
)
if backup_id:
print_status(f"Backup created successfully: {backup_id}", "SUCCESS")
# Show backup details
backup_info = backup_manager.get_backup_info(backup_id)
if backup_info:
print()
print_colored("Backup Details:", Colors.BLUE)
print(f"📁 Backup ID: {backup_id}")
print(f"📅 Created: {backup_info.get('timestamp', 'Unknown')}")
print(f"📂 Location: {backup_info.get('path', 'Unknown')}")
if 'files_backed_up' in backup_info:
file_count = len(backup_info['files_backed_up'])
print(f"📄 Files: {file_count}")
if file_count > 0 and file_count <= 10:
print(" Files backed up:")
for file_path in backup_info['files_backed_up'][:10]:
print(f" - {file_path}")
elif file_count > 10:
print(" Sample files backed up:")
for file_path in backup_info['files_backed_up'][:5]:
print(f" - {file_path}")
print(f" ... and {file_count - 5} more")
print()
print_colored("Backup completed successfully! 🎉", Colors.GREEN)
else:
print_status("Backup failed - check logs for details", "ERROR")
return 1
except Exception as e:
print_status(f"Backup error: {e}", "ERROR")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -1,93 +0,0 @@
#!/usr/bin/env node
const { execSync, spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
console.log('📦 Claude Hooks post-install setup...');
// Check Python availability
try {
execSync('python3 --version', { stdio: 'pipe' });
console.log('✓ Python 3 found');
} catch {
console.error('❌ Python 3 is required but not found');
console.error('Please install Python 3.8+ and try again');
process.exit(1);
}
// Install Python dependencies
console.log('📋 Installing Python dependencies...');
const packageRoot = path.dirname(__dirname);
const requirementsPath = path.join(packageRoot, 'requirements.txt');
if (fs.existsSync(requirementsPath)) {
try {
execSync(`python3 -m pip install -r "${requirementsPath}" --user --quiet`, {
stdio: 'pipe',
cwd: packageRoot
});
console.log('✓ Python dependencies installed');
} catch (error) {
console.log('⚠️ Some Python dependencies may not have installed');
console.log('You can install them manually: pip3 install -r requirements.txt');
}
} else {
console.log('⚠️ requirements.txt not found, skipping Python dependencies');
}
// Make scripts executable
console.log('🔧 Setting up permissions...');
const scriptsDir = path.join(packageRoot, 'scripts');
const hooksDir = path.join(packageRoot, 'hooks');
[scriptsDir, hooksDir].forEach(dir => {
if (fs.existsSync(dir)) {
const files = fs.readdirSync(dir);
files.forEach(file => {
if (file.endsWith('.py') || file.endsWith('.sh')) {
const filePath = path.join(dir, file);
try {
fs.chmodSync(filePath, 0o755);
} catch (e) {
// Ignore chmod errors on systems that don't support it
}
}
});
}
});
console.log('✓ Permissions configured');
// Create runtime directories
console.log('📁 Creating runtime directories...');
const runtimeDir = path.join(packageRoot, '.claude_hooks');
const subdirs = ['backups', 'logs', 'patterns'];
try {
if (!fs.existsSync(runtimeDir)) {
fs.mkdirSync(runtimeDir, { recursive: true });
}
subdirs.forEach(subdir => {
const subdirPath = path.join(runtimeDir, subdir);
if (!fs.existsSync(subdirPath)) {
fs.mkdirSync(subdirPath, { recursive: true });
}
});
console.log('✓ Runtime directories created');
} catch (error) {
console.log('⚠️ Could not create runtime directories:', error.message);
}
console.log('');
console.log('🎉 Claude Hooks installed successfully!');
console.log('');
console.log('Next steps:');
console.log('1. Run: claude-hooks init');
console.log('2. Restart Claude Code to activate hooks');
console.log('3. Test with: claude-hooks test');
console.log('');
console.log('For help: claude-hooks --help');

View file

@ -1,33 +0,0 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const os = require('os');
console.log('🗑️ Claude Hooks pre-uninstall cleanup...');
// Check if hooks are configured
const hooksConfig = path.join(os.homedir(), '.config', 'claude', 'hooks.json');
if (fs.existsSync(hooksConfig)) {
console.log('⚠️ Claude Hooks configuration detected');
console.log('');
console.log('IMPORTANT: This will remove the NPM package but leave hooks active!');
console.log('');
console.log('To properly uninstall:');
console.log('1. Run: claude-hooks uninstall');
console.log('2. Then run: npm uninstall -g claude-hooks');
console.log('');
console.log('Or to force removal of hooks configuration:');
console.log(`rm -f "${hooksConfig}"`);
console.log('');
// Give user a moment to see the message
setTimeout(() => {
console.log('Continuing with NPM package removal...');
}, 2000);
} else {
console.log('✓ No active hooks configuration found');
}
console.log('✓ Pre-uninstall check complete');

View file

@ -1,172 +0,0 @@
#!/usr/bin/env python3
"""Claude Hooks Test Script for NPM Distribution"""
import os
import sys
import json
import subprocess
import tempfile
from pathlib import Path
# Colors for terminal output
class Colors:
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
NC = '\033[0m' # No Color
def print_colored(message, color):
print(f"{color}{message}{Colors.NC}")
def print_status(message, status="INFO"):
colors = {
"SUCCESS": Colors.GREEN,
"ERROR": Colors.RED,
"WARNING": Colors.YELLOW,
"INFO": Colors.BLUE
}
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
def test_hook_script(hook_path, test_input=None):
"""Test a single hook script"""
if not hook_path.exists():
return False, "Script not found"
try:
cmd = [sys.executable, str(hook_path)]
result = subprocess.run(
cmd,
input=test_input or "",
text=True,
capture_output=True,
timeout=10
)
if result.returncode == 0:
return True, "Success"
else:
return False, f"Exit code {result.returncode}: {result.stderr.strip()}"
except subprocess.TimeoutExpired:
return False, "Timeout"
except Exception as e:
return False, str(e)
def main():
print_colored("Claude Hooks Test Suite", Colors.BLUE)
print("=" * 30)
# Get package root
package_root = Path(__file__).parent.parent
hooks_dir = package_root / "hooks"
claude_config_dir = Path.home() / ".config" / "claude"
hooks_config_file = claude_config_dir / "hooks.json"
tests_passed = 0
tests_total = 0
# Test 1: Check configuration exists
print("Testing configuration...")
tests_total += 1
if hooks_config_file.exists():
print_status("✓ Hooks configuration found", "SUCCESS")
tests_passed += 1
else:
print_status("✗ Hooks configuration missing", "ERROR")
print(" Run: claude-hooks init")
# Test 2: Check Python version
print("Testing Python compatibility...")
tests_total += 1
if sys.version_info >= (3, 8):
print_status(f"✓ Python {sys.version_info.major}.{sys.version_info.minor} compatible", "SUCCESS")
tests_passed += 1
else:
print_status(f"✗ Python 3.8+ required, found {sys.version_info.major}.{sys.version_info.minor}", "ERROR")
# Test 3: Test hook scripts
hook_tests = [
("context_monitor.py", '{"prompt": "test prompt", "context_size": 1000}'),
("command_validator.py", '{"command": "pip install requests", "cwd": "/tmp"}'),
("session_logger.py", '{"tool": "Bash", "args": {"command": "echo test"}}'),
("session_finalizer.py", '{"session_duration": 300, "tools_used": ["Bash", "Edit"]}')
]
for script_name, test_input in hook_tests:
print(f"Testing {script_name}...")
tests_total += 1
hook_path = hooks_dir / script_name
success, message = test_hook_script(hook_path, test_input)
if success:
print_status(f"{script_name} working", "SUCCESS")
tests_passed += 1
else:
print_status(f"{script_name} failed: {message}", "ERROR")
# Test 4: Check runtime directories
print("Testing runtime directories...")
tests_total += 1
runtime_dir = package_root / ".claude_hooks"
required_dirs = ["backups", "logs", "patterns"]
missing_dirs = []
for dirname in required_dirs:
if not (runtime_dir / dirname).exists():
missing_dirs.append(dirname)
if not missing_dirs:
print_status("✓ Runtime directories present", "SUCCESS")
tests_passed += 1
else:
print_status(f"✗ Missing directories: {', '.join(missing_dirs)}", "ERROR")
# Test 5: Test shadow learner
print("Testing shadow learner...")
tests_total += 1
try:
# Import test
sys.path.insert(0, str(package_root))
from lib.shadow_learner import ShadowLearner
learner = ShadowLearner(str(package_root / ".claude_hooks" / "patterns"))
# Basic functionality test
learner.learn_command_failure("pip", "pip3", 0.9)
suggestion = learner.get_suggestion("pip")
if suggestion and suggestion.get('suggestion') == 'pip3':
print_status("✓ Shadow learner functional", "SUCCESS")
tests_passed += 1
else:
print_status("✗ Shadow learner not learning correctly", "ERROR")
except Exception as e:
print_status(f"✗ Shadow learner error: {e}", "ERROR")
# Summary
print()
print_colored("Test Results", Colors.BLUE)
print("-" * 20)
if tests_passed == tests_total:
print_status(f"All {tests_total} tests passed! 🎉", "SUCCESS")
print()
print_colored("Claude Hooks is ready to use!", Colors.GREEN)
print("Try running some commands to see the learning in action:")
print("- pip install requests (should suggest pip3)")
print("- python script.py (should suggest python3)")
return 0
else:
print_status(f"{tests_passed}/{tests_total} tests passed", "WARNING")
print()
print_colored("Some issues detected. Check installation:", Colors.YELLOW)
print("1. Run: claude-hooks init")
print("2. Restart Claude Code")
print("3. Run tests again")
return 1
if __name__ == "__main__":
sys.exit(main())

View file

@ -1,110 +0,0 @@
#!/usr/bin/env python3
"""Claude Hooks Uninstallation Script for NPM Distribution"""
import os
import sys
import json
import shutil
from pathlib import Path
# Colors for terminal output
class Colors:
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
NC = '\033[0m' # No Color
def print_colored(message, color):
print(f"{color}{message}{Colors.NC}")
def print_status(message, status="INFO"):
colors = {
"SUCCESS": Colors.GREEN,
"ERROR": Colors.RED,
"WARNING": Colors.YELLOW,
"INFO": Colors.BLUE
}
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
def main():
print_colored("Claude Code Hooks Uninstallation", Colors.BLUE)
print("=" * 40)
# Configuration paths
claude_config_dir = Path.home() / ".config" / "claude"
hooks_config_file = claude_config_dir / "hooks.json"
settings_file = claude_config_dir / "settings.json"
# Check if hooks are installed
if not hooks_config_file.exists():
print_status("No Claude Hooks configuration found", "WARNING")
print("Nothing to uninstall.")
return 0
print_colored("This will remove Claude Hooks configuration:", Colors.YELLOW)
print(f"- Remove hooks configuration: {hooks_config_file}")
print("- Remove hooks from Claude settings (if present)")
print("- Preserve hook data and backups")
print()
response = input("Continue with uninstallation? (y/n): ").lower()
if response != 'y':
print("Uninstallation cancelled.")
return 0
# Remove hooks configuration
print("Removing hooks configuration...")
try:
hooks_config_file.unlink()
print_status("Hooks configuration removed", "SUCCESS")
except FileNotFoundError:
print_status("Hooks configuration not found", "WARNING")
except Exception as e:
print_status(f"Error removing hooks configuration: {e}", "ERROR")
return 1
# Remove from Claude settings if it exists
if settings_file.exists():
print("Removing hooks from Claude settings...")
try:
with open(settings_file, 'r') as f:
settings = json.load(f)
if 'hooks' in settings:
# Backup settings
backup_file = settings_file.with_suffix('.json.backup')
shutil.copy2(settings_file, backup_file)
# Remove hooks key
del settings['hooks']
# Write back
with open(settings_file, 'w') as f:
json.dump(settings, f, indent=2)
print_status("Hooks removed from Claude settings", "SUCCESS")
else:
print_status("No hooks found in Claude settings", "WARNING")
except json.JSONDecodeError:
print_status("Could not parse Claude settings file", "WARNING")
except Exception as e:
print_status(f"Error updating Claude settings: {e}", "WARNING")
else:
print_status("No Claude settings file found", "WARNING")
print()
print_colored("Uninstallation Complete!", Colors.GREEN)
print()
print_colored("Next Steps:", Colors.BLUE)
print("1. Restart Claude Code to deactivate hooks")
print("2. Hook data and backups are preserved in case you reinstall")
print("3. To completely remove the package: npm uninstall -g claude-hooks")
print()
print_colored("Note: To completely remove all data, delete hook directories manually", Colors.YELLOW)
return 0
if __name__ == "__main__":
sys.exit(main())