claude-hooks/scripts/install.py
Ryan Malloy 9445e09c48 Add NPM distribution support with hybrid installation approach
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>
2025-07-19 21:03:13 -06:00

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())