Major changes: - Add package.json with NPM packaging configuration - Create Node.js CLI interface (bin/claude-hooks.js) with full command set - Convert bash scripts to Python for better npm integration - Add npm postinstall/preuninstall hooks for automatic setup - Update bootstrap prompt to recommend NPM method with git fallback - Enhance README with NPM-first documentation - Maintain backward compatibility with existing git installation Features: - npm install -g claude-hooks for easy distribution - claude-hooks init/status/test/backup/uninstall commands - Automatic Python dependency installation - Conflict detection and prevention - Hybrid approach supporting both npm and git workflows This resolves installation complexity while maintaining developer flexibility. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
133 lines
4.6 KiB
Python
133 lines
4.6 KiB
Python
#!/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()) |