#!/bin/bash # Claude Code Tracker - Hook Installation Script # Automatically installs hooks for Claude Code using domain from environment set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CLAUDE_CONFIG_DIR="$HOME/.config/claude-code" CLAUDE_SETTINGS_FILE="$CLAUDE_CONFIG_DIR/settings.json" # Hook profiles available AVAILABLE_PROFILES=( "basic" "essential" "comprehensive" "developer" "power_user" "research" "team" ) print_header() { echo -e "${BLUE}╔════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ Claude Code Tracker Hooks ║${NC}" echo -e "${BLUE}║ Installation Script ║${NC}" echo -e "${BLUE}╚════════════════════════════════════════╝${NC}" echo "" } print_success() { echo -e "${GREEN}✓${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } print_info() { echo -e "${BLUE}ℹ${NC} $1" } check_requirements() { print_info "Checking requirements..." # Check if Claude Code is installed if ! command -v claude-code &> /dev/null; then print_error "Claude Code is not installed or not in PATH" print_info "Please install Claude Code first: https://claude.ai/code" exit 1 fi # Check if jq is available for JSON manipulation if ! command -v jq &> /dev/null; then print_warning "jq is not installed. Using fallback JSON handling." USE_JQ=false else USE_JQ=true fi # Check domain configuration if [[ -z "$DOMAIN" ]]; then # Try to read from .env file if [[ -f "$SCRIPT_DIR/.env" ]]; then source "$SCRIPT_DIR/.env" fi if [[ -z "$DOMAIN" ]]; then print_error "DOMAIN environment variable is not set" print_info "Please set DOMAIN environment variable or create .env file" print_info "Example: export DOMAIN=claude.l.supported.systems" exit 1 fi fi print_success "Domain configured: $DOMAIN" print_success "Requirements check passed" } create_claude_config_dir() { if [[ ! -d "$CLAUDE_CONFIG_DIR" ]]; then print_info "Creating Claude Code configuration directory..." mkdir -p "$CLAUDE_CONFIG_DIR" print_success "Created directory: $CLAUDE_CONFIG_DIR" fi } backup_existing_settings() { if [[ -f "$CLAUDE_SETTINGS_FILE" ]]; then local backup_file="${CLAUDE_SETTINGS_FILE}.backup.$(date +%Y%m%d_%H%M%S)" print_info "Backing up existing settings to: $backup_file" cp "$CLAUDE_SETTINGS_FILE" "$backup_file" print_success "Backup created" fi } get_hook_profile_path() { local profile="$1" echo "$SCRIPT_DIR/claude-hooks-${profile}.json" } list_available_profiles() { echo -e "${BLUE}Available hook profiles:${NC}" echo "" for profile in "${AVAILABLE_PROFILES[@]}"; do local profile_file profile_file=$(get_hook_profile_path "$profile") if [[ -f "$profile_file" ]]; then local description if [[ "$USE_JQ" == true ]]; then description=$(jq -r '.metadata.description // "No description available"' "$profile_file" 2>/dev/null || echo "No description available") else description="Hook configuration for $profile users" fi echo -e " ${GREEN}$profile${NC} - $description" else echo -e " ${RED}$profile${NC} - Profile file not found" fi done echo "" } update_hook_domain() { local input_file="$1" local output_file="$2" print_info "Updating hook URLs with domain: $DOMAIN" # Replace placeholder domain with actual domain sed "s/\${DOMAIN}/$DOMAIN/g; s/localhost:8000/$DOMAIN/g; s/http:\/\/$DOMAIN/https:\/\/$DOMAIN/g" "$input_file" > "$output_file" print_success "Hook URLs updated" } install_hooks() { local profile="$1" local profile_file profile_file=$(get_hook_profile_path "$profile") if [[ ! -f "$profile_file" ]]; then print_error "Hook profile file not found: $profile_file" exit 1 fi print_info "Installing hooks from profile: $profile" # Create temporary file with updated domain local temp_file="/tmp/claude-hooks-${profile}-$(date +%s).json" update_hook_domain "$profile_file" "$temp_file" # Merge with existing settings or create new local final_settings="$temp_file" if [[ -f "$CLAUDE_SETTINGS_FILE" ]] && [[ "$USE_JQ" == true ]]; then print_info "Merging with existing Claude Code settings..." # Merge hook configurations local merged_file="/tmp/claude-settings-merged-$(date +%s).json" jq -s '.[0] * .[1]' "$CLAUDE_SETTINGS_FILE" "$temp_file" > "$merged_file" final_settings="$merged_file" print_success "Settings merged" fi # Install the hooks cp "$final_settings" "$CLAUDE_SETTINGS_FILE" # Cleanup temp files rm -f "$temp_file" if [[ -f "$merged_file" ]]; then rm -f "$merged_file" fi print_success "Hooks installed successfully!" } show_installation_summary() { local profile="$1" echo "" echo -e "${GREEN}╔════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ Installation Complete! ║${NC}" echo -e "${GREEN}╚════════════════════════════════════════╝${NC}" echo "" echo -e "${BLUE}Profile installed:${NC} $profile" echo -e "${BLUE}Domain configured:${NC} $DOMAIN" echo -e "${BLUE}Settings file:${NC} $CLAUDE_SETTINGS_FILE" echo "" echo -e "${YELLOW}Next steps:${NC}" echo "1. Start using Claude Code in any project" echo "2. Visit https://$DOMAIN/dashboard to view your tracking data" echo "3. Check the documentation at https://$DOMAIN/dashboard/docs" echo "" echo -e "${BLUE}To test the installation:${NC}" echo " claude-code --help" echo "" } verify_installation() { print_info "Verifying installation..." if [[ -f "$CLAUDE_SETTINGS_FILE" ]]; then if [[ "$USE_JQ" == true ]]; then if jq empty "$CLAUDE_SETTINGS_FILE" 2>/dev/null; then print_success "Settings file is valid JSON" local hook_count hook_count=$(jq -r '.hooks | length // 0' "$CLAUDE_SETTINGS_FILE" 2>/dev/null || echo "0") print_success "Number of hooks configured: $hook_count" return 0 else print_error "Settings file contains invalid JSON" return 1 fi else print_success "Settings file exists" return 0 fi else print_error "Settings file was not created" return 1 fi } show_help() { echo "Claude Code Tracker - Hook Installation Script" echo "" echo "Usage: $0 [OPTIONS] [PROFILE]" echo "" echo "PROFILE: Hook profile to install (default: comprehensive)" echo "" echo "Options:" echo " -l, --list List available profiles" echo " -d, --domain DOMAIN Set the domain (overrides DOMAIN env var)" echo " -h, --help Show this help message" echo " --verify Verify existing installation" echo " --uninstall Remove all hooks from settings" echo "" echo "Examples:" echo " $0 # Install comprehensive profile" echo " $0 basic # Install basic profile" echo " $0 -d example.com # Install with custom domain" echo " $0 --list # List available profiles" echo "" echo "Environment Variables:" echo " DOMAIN Target domain for the Claude Code Tracker" echo "" } uninstall_hooks() { print_info "Uninstalling Claude Code Tracker hooks..." if [[ ! -f "$CLAUDE_SETTINGS_FILE" ]]; then print_warning "No settings file found, nothing to uninstall" return 0 fi backup_existing_settings if [[ "$USE_JQ" == true ]]; then # Remove hooks section but keep other settings jq 'del(.hooks)' "$CLAUDE_SETTINGS_FILE" > "${CLAUDE_SETTINGS_FILE}.tmp" mv "${CLAUDE_SETTINGS_FILE}.tmp" "$CLAUDE_SETTINGS_FILE" print_success "Hooks removed from settings" else print_warning "Cannot automatically remove hooks without jq. Manual removal required." print_info "Edit $CLAUDE_SETTINGS_FILE and remove the 'hooks' section" fi } main() { local profile="comprehensive" local list_profiles=false local verify_only=false local uninstall_only=false # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_help exit 0 ;; -l|--list) list_profiles=true shift ;; -d|--domain) DOMAIN="$2" shift 2 ;; --verify) verify_only=true shift ;; --uninstall) uninstall_only=true shift ;; -*) print_error "Unknown option: $1" show_help exit 1 ;; *) profile="$1" shift ;; esac done print_header # Handle special modes if [[ "$list_profiles" == true ]]; then check_requirements list_available_profiles exit 0 fi if [[ "$verify_only" == true ]]; then check_requirements verify_installation exit $? fi if [[ "$uninstall_only" == true ]]; then check_requirements create_claude_config_dir uninstall_hooks print_success "Uninstallation complete" exit 0 fi # Main installation flow check_requirements create_claude_config_dir backup_existing_settings # Validate profile if [[ ! " ${AVAILABLE_PROFILES[@]} " =~ " $profile " ]]; then print_error "Unknown profile: $profile" echo "" list_available_profiles exit 1 fi # Install hooks install_hooks "$profile" # Verify installation if verify_installation; then show_installation_summary "$profile" else print_error "Installation verification failed" exit 1 fi } # Run main function with all arguments main "$@"