#!/bin/bash # # Reference Curator - Portable Installation Script # # Installs the Reference Curator skill suite on any machine. # Handles environment configuration, database setup, and skill registration. # # Usage: # ./install.sh # Interactive installation # ./install.sh --check # Verify installation status # ./install.sh --minimal # Firecrawl-only mode (no local crawlers) # ./install.sh --uninstall # Remove installation # set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # No Color # Paths SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="$HOME/.reference-curator.env" CONFIG_DIR="$HOME/.config/reference-curator" CLAUDE_COMMANDS_DIR="$HOME/.claude/commands" CLAUDE_SKILLS_DIR="$HOME/.claude/skills" # Default values DEFAULT_LIBRARY_PATH="$HOME/reference-library" DEFAULT_CRAWLER="firecrawl" # Skills and commands to register SKILLS=( "reference-discovery:01-reference-discovery" "web-crawler-orchestrator:02-web-crawler-orchestrator" "content-repository:03-content-repository" "content-distiller:04-content-distiller" "quality-reviewer:05-quality-reviewer" "markdown-exporter:06-markdown-exporter" ) COMMANDS=( "reference-discovery" "web-crawler" "content-repository" "content-distiller" "quality-reviewer" "markdown-exporter" ) # ============================================================================ # Helper Functions # ============================================================================ print_header() { echo "" echo -e "${BLUE}╔══════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ ${BOLD}Reference Curator - Portable Installation${NC}${BLUE} ║${NC}" echo -e "${BLUE}╚══════════════════════════════════════════════════════╝${NC}" echo "" } print_step() { echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${BOLD}$1${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" } print_substep() { echo -e "\n${BLUE}▶${NC} $1" } 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" } prompt_with_default() { local prompt="$1" local default="$2" local var_name="$3" if [[ -n "$default" ]]; then read -p " $prompt [$default]: " input eval "$var_name=\"${input:-$default}\"" else read -p " $prompt: " input eval "$var_name=\"$input\"" fi } prompt_password() { local prompt="$1" local var_name="$2" read -sp " $prompt: " input echo "" eval "$var_name=\"$input\"" } prompt_yes_no() { local prompt="$1" local default="$2" if [[ "$default" == "y" ]]; then read -p " $prompt [Y/n]: " input [[ ! "$input" =~ ^[Nn] ]] else read -p " $prompt [y/N]: " input [[ "$input" =~ ^[Yy] ]] fi } # ============================================================================ # Check Prerequisites # ============================================================================ check_prerequisites() { print_step "Step 1: Checking Prerequisites" local all_ok=true # Check OS print_substep "Operating System" case "$(uname -s)" in Darwin) print_success "macOS detected" ;; Linux) print_success "Linux detected" ;; *) print_error "Unsupported OS: $(uname -s)" all_ok=false ;; esac # Check MySQL print_substep "MySQL Client" if command -v mysql &> /dev/null; then local mysql_version=$(mysql --version 2>/dev/null | head -1) print_success "MySQL client installed: $mysql_version" else print_warning "MySQL client not found - database features will be limited" print_info "Install with: brew install mysql (macOS) or apt install mysql-client (Linux)" fi # Check Claude Code directories print_substep "Claude Code Directories" if [[ -d "$HOME/.claude" ]]; then print_success "~/.claude directory exists" else print_warning "~/.claude directory not found - will be created" fi # Check for existing installation print_substep "Existing Installation" if [[ -f "$ENV_FILE" ]]; then print_warning "Existing installation found at $ENV_FILE" if prompt_yes_no "Overwrite existing configuration?" "n"; then rm -f "$ENV_FILE" print_info "Existing configuration removed" else print_info "Will merge with existing configuration" fi else print_success "No existing installation found" fi echo "" if $all_ok; then return 0 else return 1 fi } # ============================================================================ # Configure Environment # ============================================================================ configure_environment() { print_step "Step 2: Environment Configuration" echo -e "\n ${BOLD}Environment variables will be saved to:${NC} $ENV_FILE" echo "" # Library path print_substep "Reference Library Path" prompt_with_default "Storage directory for reference library" "$DEFAULT_LIBRARY_PATH" "LIBRARY_PATH" # MySQL configuration print_substep "MySQL Configuration" if prompt_yes_no "Configure MySQL database?" "y"; then SETUP_MYSQL=true prompt_with_default "MySQL host" "localhost" "MYSQL_HOST" prompt_with_default "MySQL port" "3306" "MYSQL_PORT" prompt_with_default "MySQL username" "" "MYSQL_USER" prompt_password "MySQL password" "MYSQL_PASSWORD" if [[ -z "$MYSQL_PASSWORD" ]]; then print_warning "No password provided - MySQL connection may fail" fi else SETUP_MYSQL=false print_info "Skipping MySQL setup - database features will be unavailable" fi # Crawler configuration print_substep "Crawler Configuration" echo "" echo " Available crawler backends:" echo " 1) Firecrawl MCP only (recommended for most users)" echo " 2) Local crawlers + Firecrawl (requires our-seo-agent project)" echo "" read -p " Select option [1]: " crawler_option crawler_option=${crawler_option:-1} if [[ "$crawler_option" == "2" ]]; then prompt_with_default "Path to crawler project (our-seo-agent)" "$HOME/Project/our-seo-agent" "CRAWLER_PATH" if [[ -d "$CRAWLER_PATH" ]]; then print_success "Crawler project found at $CRAWLER_PATH" ENABLE_LOCAL_CRAWLERS=true DEFAULT_CRAWLER="nodejs" else print_warning "Crawler project not found - using Firecrawl only" ENABLE_LOCAL_CRAWLERS=false DEFAULT_CRAWLER="firecrawl" fi else ENABLE_LOCAL_CRAWLERS=false DEFAULT_CRAWLER="firecrawl" print_info "Using Firecrawl MCP as default crawler" fi # Save environment file print_substep "Saving Configuration" cat > "$ENV_FILE" << EOF # Reference Curator Environment Configuration # Generated: $(date -Iseconds) # # Source this file in your shell profile: # echo 'source ~/.reference-curator.env' >> ~/.zshrc # # Storage paths export REFERENCE_LIBRARY_PATH="$LIBRARY_PATH" # MySQL configuration export MYSQL_HOST="${MYSQL_HOST:-localhost}" export MYSQL_PORT="${MYSQL_PORT:-3306}" export MYSQL_USER="$MYSQL_USER" export MYSQL_PASSWORD='$MYSQL_PASSWORD' # Crawler configuration export DEFAULT_CRAWLER="$DEFAULT_CRAWLER" export CRAWLER_PROJECT_PATH="${CRAWLER_PATH:-}" export NODEJS_CRAWLER_ENABLED=${ENABLE_LOCAL_CRAWLERS:-false} export AIOHTTP_CRAWLER_ENABLED=${ENABLE_LOCAL_CRAWLERS:-false} export SCRAPY_CRAWLER_ENABLED=${ENABLE_LOCAL_CRAWLERS:-false} EOF chmod 600 "$ENV_FILE" print_success "Environment saved to $ENV_FILE" # Source the environment source "$ENV_FILE" } # ============================================================================ # Install Configuration Files # ============================================================================ install_configs() { print_step "Step 3: Installing Configuration Files" print_substep "Creating directories" mkdir -p "$CONFIG_DIR" print_success "$CONFIG_DIR" print_substep "Copying configuration files" for config in db_config.yaml crawl_config.yaml export_config.yaml; do if [[ -f "$SCRIPT_DIR/shared/config/$config" ]]; then cp "$SCRIPT_DIR/shared/config/$config" "$CONFIG_DIR/" print_success "$config" else print_error "$config - source not found" fi done } # ============================================================================ # Create Storage Directories # ============================================================================ create_directories() { print_step "Step 4: Creating Storage Directories" local lib_path="${REFERENCE_LIBRARY_PATH:-$DEFAULT_LIBRARY_PATH}" for dir in raw processed exports backups; do mkdir -p "$lib_path/$dir" print_success "$lib_path/$dir" done } # ============================================================================ # Setup MySQL Database # ============================================================================ setup_database() { print_step "Step 5: Setting Up MySQL Database" if [[ "$SETUP_MYSQL" != "true" ]]; then print_info "MySQL setup skipped" return 0 fi # Test connection print_substep "Testing MySQL connection" if mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1" &>/dev/null; then print_success "MySQL connection successful" else print_error "MySQL connection failed" print_info "Check your credentials and try again" return 1 fi # Check if database exists print_substep "Checking database" if mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "USE reference_library" &>/dev/null; then print_info "Database 'reference_library' already exists" local table_count=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -N -e \ "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='reference_library'" 2>/dev/null) if [[ "$table_count" -ge 9 ]]; then print_success "Schema already applied ($table_count tables)" return 0 fi else # Try to create database print_substep "Creating database" if mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e \ "CREATE DATABASE IF NOT EXISTS reference_library CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" &>/dev/null; then print_success "Database created" else print_warning "Could not create database - may need admin privileges" echo "" if prompt_yes_no "Do you have MySQL root access to create the database?" "n"; then prompt_password "MySQL root password" "ROOT_PASSWORD" mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u root -p"$ROOT_PASSWORD" << EOF CREATE DATABASE IF NOT EXISTS reference_library CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON reference_library.* TO '$MYSQL_USER'@'%'; FLUSH PRIVILEGES; EOF print_success "Database created and privileges granted" else print_error "Cannot proceed without database" return 1 fi fi fi # Apply schema print_substep "Applying schema" if [[ -f "$SCRIPT_DIR/shared/schema.sql" ]]; then if mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" reference_library < "$SCRIPT_DIR/shared/schema.sql" 2>/dev/null; then print_success "Schema applied successfully" else print_error "Failed to apply schema" return 1 fi else print_error "Schema file not found: $SCRIPT_DIR/shared/schema.sql" return 1 fi } # ============================================================================ # Register Claude Code Commands # ============================================================================ register_commands() { print_step "Step 6: Registering Claude Code Commands" mkdir -p "$CLAUDE_COMMANDS_DIR" for cmd in "${COMMANDS[@]}"; do local source="$SCRIPT_DIR/commands/${cmd}.md" local target="$CLAUDE_COMMANDS_DIR/${cmd}.md" if [[ -f "$source" ]]; then ln -sf "$source" "$target" print_success "/$cmd" else print_warning "/$cmd - source not found: $source" fi done } # ============================================================================ # Register Claude Desktop Skills # ============================================================================ register_skills() { print_step "Step 7: Registering Claude Desktop Skills" mkdir -p "$CLAUDE_SKILLS_DIR" for skill_entry in "${SKILLS[@]}"; do IFS=':' read -r skill_name skill_folder <<< "$skill_entry" local target="$SCRIPT_DIR/$skill_folder/desktop" if [[ -d "$target" ]]; then ln -sf "$target" "$CLAUDE_SKILLS_DIR/$skill_name" print_success "$skill_name" else print_warning "$skill_name - target not found: $target" fi done } # ============================================================================ # Post-Installation # ============================================================================ post_install() { print_step "Installation Complete!" echo "" echo -e "${GREEN}╔══════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ Installation Successful! ║${NC}" echo -e "${GREEN}╚══════════════════════════════════════════════════════╝${NC}" echo "" echo -e "${BOLD}Next Steps:${NC}" echo "" echo " 1. Add environment to your shell profile:" echo -e " ${CYAN}echo 'source ~/.reference-curator.env' >> ~/.zshrc${NC}" echo "" echo " 2. Reload your shell or run:" echo -e " ${CYAN}source ~/.reference-curator.env${NC}" echo "" echo " 3. Verify installation:" echo -e " ${CYAN}./install.sh --check${NC}" echo "" echo " 4. Start using the skills:" echo -e " ${CYAN}/reference-discovery Claude system prompts${NC}" echo "" if [[ "$DEFAULT_CRAWLER" == "firecrawl" ]]; then echo -e "${YELLOW}Note:${NC} Using Firecrawl MCP as default crawler." echo " Make sure firecrawl MCP server is configured in Claude Code settings." echo "" fi } # ============================================================================ # Check Installation Status # ============================================================================ check_status() { print_header echo -e "${BOLD}Installation Status Check${NC}" echo "" local all_ok=true # Check environment file print_substep "Environment Configuration" if [[ -f "$ENV_FILE" ]]; then print_success "$ENV_FILE" source "$ENV_FILE" else print_error "$ENV_FILE - not found" all_ok=false fi # Check config files print_substep "Configuration Files ($CONFIG_DIR)" for config in db_config.yaml crawl_config.yaml export_config.yaml; do if [[ -f "$CONFIG_DIR/$config" ]]; then print_success "$config" else print_error "$config - missing" all_ok=false fi done # Check storage directories print_substep "Storage Directories" local lib_path="${REFERENCE_LIBRARY_PATH:-$DEFAULT_LIBRARY_PATH}" for dir in raw processed exports; do if [[ -d "$lib_path/$dir" ]]; then print_success "$lib_path/$dir" else print_error "$lib_path/$dir - missing" all_ok=false fi done # Check MySQL print_substep "MySQL Database" if [[ -n "$MYSQL_USER" ]] && command -v mysql &>/dev/null; then if mysql -h "${MYSQL_HOST:-localhost}" -P "${MYSQL_PORT:-3306}" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "USE reference_library" &>/dev/null; then local table_count=$(mysql -h "${MYSQL_HOST:-localhost}" -P "${MYSQL_PORT:-3306}" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -N -e \ "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='reference_library'" 2>/dev/null) print_success "Database accessible ($table_count tables)" else print_warning "Database 'reference_library' not accessible" all_ok=false fi else print_info "MySQL not configured or client not installed" fi # Check Claude Code commands print_substep "Claude Code Commands ($CLAUDE_COMMANDS_DIR)" for cmd in "${COMMANDS[@]}"; do if [[ -L "$CLAUDE_COMMANDS_DIR/${cmd}.md" ]] || [[ -f "$CLAUDE_COMMANDS_DIR/${cmd}.md" ]]; then print_success "/$cmd" else print_error "/$cmd - not registered" all_ok=false fi done # Check Claude Desktop skills print_substep "Claude Desktop Skills ($CLAUDE_SKILLS_DIR)" for skill_entry in "${SKILLS[@]}"; do IFS=':' read -r skill_name skill_folder <<< "$skill_entry" if [[ -L "$CLAUDE_SKILLS_DIR/$skill_name" ]]; then print_success "$skill_name" else print_error "$skill_name - not registered" all_ok=false fi done echo "" if $all_ok; then echo -e "${GREEN}All components installed correctly.${NC}" return 0 else echo -e "${YELLOW}Some components need attention. Run ./install.sh to fix.${NC}" return 1 fi } # ============================================================================ # Uninstall # ============================================================================ uninstall() { print_header echo -e "${YELLOW}${BOLD}Uninstall Reference Curator${NC}" echo "" echo "This will remove:" echo " • Environment file: $ENV_FILE" echo " • Config directory: $CONFIG_DIR" echo " • Claude Code commands" echo " • Claude Desktop skill symlinks" echo "" echo -e "${GREEN}This will NOT remove:${NC}" echo " • Storage directories (your data is safe)" echo " • MySQL database (your data is safe)" echo "" if ! prompt_yes_no "Proceed with uninstall?" "n"; then echo "Uninstall cancelled." exit 0 fi echo "" # Remove environment file if [[ -f "$ENV_FILE" ]]; then rm -f "$ENV_FILE" print_success "Removed $ENV_FILE" fi # Remove config directory if [[ -d "$CONFIG_DIR" ]]; then rm -rf "$CONFIG_DIR" print_success "Removed $CONFIG_DIR" fi # Remove Claude Code commands for cmd in "${COMMANDS[@]}"; do local target="$CLAUDE_COMMANDS_DIR/${cmd}.md" if [[ -L "$target" ]] || [[ -f "$target" ]]; then rm -f "$target" print_success "Removed /$cmd command" fi done # Remove Claude Desktop skills for skill_entry in "${SKILLS[@]}"; do IFS=':' read -r skill_name skill_folder <<< "$skill_entry" local target="$CLAUDE_SKILLS_DIR/$skill_name" if [[ -L "$target" ]]; then rm -f "$target" print_success "Removed $skill_name skill" fi done echo "" echo -e "${GREEN}Uninstall complete.${NC}" echo "" echo "To reinstall, run: ./install.sh" } # ============================================================================ # Main Installation # ============================================================================ install() { print_header echo "This installer will set up the Reference Curator skill suite." echo "" echo "Components to install:" echo " • Environment configuration" echo " • Configuration files" echo " • Storage directories" echo " • MySQL database (optional)" echo " • Claude Code commands (6)" echo " • Claude Desktop skills (6)" echo "" if ! prompt_yes_no "Continue with installation?" "y"; then echo "Installation cancelled." exit 0 fi check_prerequisites configure_environment install_configs create_directories if [[ "$SETUP_MYSQL" == "true" ]]; then setup_database fi register_commands register_skills post_install } # ============================================================================ # Minimal Installation (Firecrawl only) # ============================================================================ install_minimal() { print_header echo -e "${BOLD}Minimal Installation Mode${NC}" echo "" echo "This will install Reference Curator with Firecrawl MCP only." echo "No local crawlers or MySQL database will be configured." echo "" if ! prompt_yes_no "Continue?" "y"; then echo "Installation cancelled." exit 0 fi # Minimal environment SETUP_MYSQL=false ENABLE_LOCAL_CRAWLERS=false DEFAULT_CRAWLER="firecrawl" LIBRARY_PATH="$DEFAULT_LIBRARY_PATH" cat > "$ENV_FILE" << EOF # Reference Curator Environment Configuration (Minimal) # Generated: $(date -Iseconds) export REFERENCE_LIBRARY_PATH="$LIBRARY_PATH" export DEFAULT_CRAWLER="firecrawl" EOF chmod 600 "$ENV_FILE" source "$ENV_FILE" install_configs create_directories register_commands register_skills post_install } # ============================================================================ # Entry Point # ============================================================================ case "${1:-}" in --check) check_status ;; --uninstall) uninstall ;; --minimal) install_minimal ;; --help|-h) echo "Reference Curator - Portable Installation Script" echo "" echo "Usage:" echo " ./install.sh Interactive installation" echo " ./install.sh --check Check installation status" echo " ./install.sh --minimal Firecrawl-only mode (no MySQL)" echo " ./install.sh --uninstall Remove installation" echo " ./install.sh --help Show this help" ;; *) install ;; esac