* Fix SEO skill 34 bugs, Korean labels, and transition Ahrefs refs to our-seo-agent P0: Fix report_aggregator.py — wrong SKILL_REGISTRY[33] mapping, missing CATEGORY_WEIGHTS for 7 categories, and break bug in health score parsing that exited loop even on parse failure. P1: Remove VIEW tab references from skill 20, expand skill 32 docs, replace Ahrefs MCP references across all 16 skills (19-28, 31-34) with our-seo-agent CLI data source references. P2: Fix Korean labels in executive_report.py and dashboard_generator.py, add tenacity to base requirements, sync skill 34 base_client.py with canonical version from skill 12. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add Claude Code slash commands for SEO skills 19-34 and fix stale paths Create 14 new slash command files for skills 19-28, 31-34 so they appear as /seo-* commands in Claude Code. Also fix stale directory paths in 8 existing commands (skills 12-18, 29-30) that referenced pre-renumbering skill directories. Update .gitignore to track .claude/commands/ while keeping other .claude/ files ignored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add 8 slash commands, enhance reference-curator with depth/output options - Add slash commands: ourdigital-brand-guide, notion-writer, notebooklm-agent, notebooklm-automation, notebooklm-studio, notebooklm-research, reference-curator, multi-agent-guide - Add --depth (light/standard/deep/full) with Firecrawl parameter mapping - Add --output with ~/Documents/reference-library/ default and user confirmation - Increase --max-sources default from 10 to 100 - Rename /reference-curator-pipeline to /reference-curator - Simplify web-crawler-orchestrator label to web-crawler in docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Convert slash command script paths to absolute for global availability Symlinked all 39 project commands to ~/.claude/commands/ so they work from any project directory. Converted 126 relative custom-skills/ paths to absolute /Users/ourdigital/Projects/our-claude-skills/custom-skills/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update installer to support global slash command installation Add symlink-based global command setup so all 39 custom skills work from any project directory. New --commands flag for quick re-sync, updated --validate/--update/--uninstall to handle symlinks, and expanded skill listing to cover all 7 domains. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add user guides in English and Korean for all 52 custom skills Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
624 lines
19 KiB
Bash
Executable File
624 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# OurDigital Skills Installer
|
|
# Portable installation tool for OurDigital Claude Skills package
|
|
#
|
|
# Usage:
|
|
# ./install.sh # Interactive install
|
|
# ./install.sh --update # Update existing installation
|
|
# ./install.sh --uninstall # Remove installation
|
|
# ./install.sh --commands # Only install/update global slash commands
|
|
# ./install.sh --help # Show help
|
|
#
|
|
|
|
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'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SKILLS_DIR="$(dirname "$SCRIPT_DIR")"
|
|
REPO_DIR="$(dirname "$SKILLS_DIR")"
|
|
ENV_FILE="$HOME/.env.ourdigital"
|
|
CONFIG_DIR="$HOME/.ourdigital"
|
|
VENV_DIR="$SKILLS_DIR/.venv-ourdigital"
|
|
CLAUDE_COMMANDS_DIR="$HOME/.claude/commands"
|
|
REPO_COMMANDS_DIR="$REPO_DIR/.claude/commands"
|
|
|
|
# Print banner
|
|
print_banner() {
|
|
echo -e "${CYAN}"
|
|
echo "╔═══════════════════════════════════════════════════════════╗"
|
|
echo "║ ║"
|
|
echo "║ OurDigital Skills Installer ║"
|
|
echo "║ 사람, 디지털, 그리고 문화 ║"
|
|
echo "║ ║"
|
|
echo "╚═══════════════════════════════════════════════════════════╝"
|
|
echo -e "${NC}"
|
|
}
|
|
|
|
# Print colored messages
|
|
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
# Check prerequisites
|
|
check_prerequisites() {
|
|
info "Checking prerequisites..."
|
|
|
|
local missing=()
|
|
|
|
# Check Python 3.11+
|
|
if command -v python3 &> /dev/null; then
|
|
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
|
if [[ $(echo "$PYTHON_VERSION >= 3.11" | bc -l) -eq 1 ]]; then
|
|
success "Python $PYTHON_VERSION"
|
|
else
|
|
warn "Python $PYTHON_VERSION (3.11+ recommended)"
|
|
fi
|
|
else
|
|
missing+=("python3")
|
|
fi
|
|
|
|
# Check git
|
|
if command -v git &> /dev/null; then
|
|
success "Git $(git --version | cut -d' ' -f3)"
|
|
else
|
|
missing+=("git")
|
|
fi
|
|
|
|
# Check Claude Code CLI
|
|
if command -v claude &> /dev/null; then
|
|
success "Claude Code CLI available"
|
|
else
|
|
warn "Claude Code CLI not found (slash commands require it)"
|
|
fi
|
|
|
|
# Check 1Password CLI (optional)
|
|
if command -v op &> /dev/null; then
|
|
success "1Password CLI available"
|
|
HAS_1PASSWORD=true
|
|
else
|
|
warn "1Password CLI not found (credentials will need manual entry)"
|
|
HAS_1PASSWORD=false
|
|
fi
|
|
|
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
error "Missing required tools: ${missing[*]}"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Create directory structure
|
|
setup_directories() {
|
|
info "Setting up directories..."
|
|
|
|
# Create config directory
|
|
mkdir -p "$CONFIG_DIR/credentials"
|
|
chmod 700 "$CONFIG_DIR"
|
|
chmod 700 "$CONFIG_DIR/credentials"
|
|
success "Created $CONFIG_DIR/"
|
|
|
|
# Create logs directory
|
|
mkdir -p "$CONFIG_DIR/logs"
|
|
success "Created $CONFIG_DIR/logs/"
|
|
|
|
# Create Claude commands directory
|
|
mkdir -p "$CLAUDE_COMMANDS_DIR"
|
|
success "Created $CLAUDE_COMMANDS_DIR/"
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Install global slash commands via symlinks
|
|
setup_global_commands() {
|
|
info "Setting up global slash commands..."
|
|
|
|
if [[ ! -d "$REPO_COMMANDS_DIR" ]]; then
|
|
error "Commands directory not found: $REPO_COMMANDS_DIR"
|
|
echo " Make sure you're running from the our-claude-skills repository."
|
|
return 1
|
|
fi
|
|
|
|
mkdir -p "$CLAUDE_COMMANDS_DIR"
|
|
|
|
local linked=0
|
|
local skipped=0
|
|
local updated=0
|
|
|
|
for cmd_file in "$REPO_COMMANDS_DIR"/*.md; do
|
|
[[ -f "$cmd_file" ]] || continue
|
|
local filename
|
|
filename=$(basename "$cmd_file")
|
|
local target="$CLAUDE_COMMANDS_DIR/$filename"
|
|
|
|
if [[ -L "$target" ]]; then
|
|
local current_link
|
|
current_link=$(readlink "$target")
|
|
if [[ "$current_link" == "$cmd_file" ]]; then
|
|
((skipped++))
|
|
else
|
|
rm "$target"
|
|
ln -s "$cmd_file" "$target"
|
|
((updated++))
|
|
fi
|
|
elif [[ -f "$target" ]]; then
|
|
warn "Skipping $filename (non-symlink file exists at $target)"
|
|
((skipped++))
|
|
else
|
|
ln -s "$cmd_file" "$target"
|
|
((linked++))
|
|
fi
|
|
done
|
|
|
|
[[ $linked -gt 0 ]] && success "Linked $linked new commands"
|
|
[[ $updated -gt 0 ]] && success "Updated $updated existing symlinks"
|
|
[[ $skipped -gt 0 ]] && info "Skipped $skipped (already current)"
|
|
|
|
# Count total symlinks pointing to our repo
|
|
local total
|
|
total=$(find "$CLAUDE_COMMANDS_DIR" -maxdepth 1 -type l -lname "$REPO_COMMANDS_DIR/*" 2>/dev/null | wc -l | tr -d ' ')
|
|
success "Total global commands from this repo: $total"
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Remove global slash command symlinks
|
|
remove_global_commands() {
|
|
info "Removing global slash command symlinks..."
|
|
|
|
if [[ ! -d "$CLAUDE_COMMANDS_DIR" ]]; then
|
|
info "No commands directory found, nothing to remove"
|
|
return
|
|
fi
|
|
|
|
local removed=0
|
|
for link in "$CLAUDE_COMMANDS_DIR"/*.md; do
|
|
[[ -L "$link" ]] || continue
|
|
local link_target
|
|
link_target=$(readlink "$link")
|
|
if [[ "$link_target" == "$REPO_COMMANDS_DIR/"* ]]; then
|
|
rm "$link"
|
|
((removed++))
|
|
fi
|
|
done
|
|
|
|
if [[ $removed -gt 0 ]]; then
|
|
success "Removed $removed command symlinks"
|
|
else
|
|
info "No symlinks from this repo found"
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Setup environment file
|
|
setup_environment() {
|
|
info "Setting up environment file..."
|
|
|
|
if [[ -f "$ENV_FILE" ]]; then
|
|
warn "$ENV_FILE already exists"
|
|
read -p "Overwrite? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
info "Keeping existing environment file"
|
|
return
|
|
fi
|
|
cp "$ENV_FILE" "$ENV_FILE.backup.$(date +%Y%m%d%H%M%S)"
|
|
success "Backed up existing file"
|
|
fi
|
|
|
|
cp "$SCRIPT_DIR/.env.ourdigital.template" "$ENV_FILE"
|
|
chmod 600 "$ENV_FILE"
|
|
success "Created $ENV_FILE"
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Setup credentials from 1Password or manual
|
|
setup_credentials() {
|
|
info "Setting up credentials..."
|
|
|
|
if [[ "$HAS_1PASSWORD" == true ]]; then
|
|
read -p "Fetch credentials from 1Password? (Y/n): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
fetch_from_1password
|
|
return
|
|
fi
|
|
fi
|
|
|
|
echo -e "${YELLOW}Manual credential setup required.${NC}"
|
|
echo "Edit $ENV_FILE with your API keys:"
|
|
echo " - NOTION_API_TOKEN"
|
|
echo " - GHOST_BLOG_ADMIN_KEY"
|
|
echo " - GHOST_JOURNAL_ADMIN_KEY"
|
|
echo " - FIGMA_ACCESS_TOKEN"
|
|
echo ""
|
|
}
|
|
|
|
# Fetch credentials from 1Password
|
|
fetch_from_1password() {
|
|
info "Fetching credentials from 1Password..."
|
|
|
|
# Check if signed in
|
|
if ! op account list &> /dev/null; then
|
|
warn "Please sign in to 1Password CLI first"
|
|
echo "Run: eval \$(op signin)"
|
|
return
|
|
fi
|
|
|
|
# Fetch each credential
|
|
local credentials=(
|
|
"NOTION_API_TOKEN:OurDigital Notion:api_token"
|
|
"GHOST_BLOG_ADMIN_KEY:OurDigital Ghost Blog:api_key"
|
|
"GHOST_JOURNAL_ADMIN_KEY:OurDigital Ghost Journal:api_key"
|
|
"FIGMA_ACCESS_TOKEN:OurDigital Figma:api_token"
|
|
)
|
|
|
|
for cred in "${credentials[@]}"; do
|
|
IFS=':' read -r env_var item_name field <<< "$cred"
|
|
|
|
if value=$(op item get "$item_name" --fields "$field" 2>/dev/null); then
|
|
sed -i '' "s|^$env_var=.*|$env_var=$value|" "$ENV_FILE" 2>/dev/null || \
|
|
sed -i "s|^$env_var=.*|$env_var=$value|" "$ENV_FILE"
|
|
success "Fetched $env_var"
|
|
else
|
|
warn "Could not fetch $env_var from 1Password"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Setup Python virtual environment
|
|
setup_python_env() {
|
|
info "Setting up Python virtual environment..."
|
|
|
|
if [[ -d "$VENV_DIR" ]]; then
|
|
warn "Virtual environment already exists"
|
|
read -p "Recreate? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
rm -rf "$VENV_DIR"
|
|
else
|
|
info "Using existing virtual environment"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
python3 -m venv "$VENV_DIR"
|
|
success "Created virtual environment"
|
|
|
|
# Activate and install dependencies
|
|
source "$VENV_DIR/bin/activate"
|
|
pip install --upgrade pip -q
|
|
pip install -r "$SCRIPT_DIR/requirements/base.txt" -q
|
|
success "Installed base dependencies"
|
|
|
|
# Install code-specific if requested
|
|
read -p "Install Claude Code dependencies? (Y/n): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
pip install -r "$SCRIPT_DIR/requirements/code.txt" -q
|
|
success "Installed Code dependencies"
|
|
fi
|
|
|
|
deactivate
|
|
echo ""
|
|
}
|
|
|
|
# Copy config file
|
|
setup_config() {
|
|
info "Setting up configuration..."
|
|
|
|
if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then
|
|
cp "$SCRIPT_DIR/config/ourdigital.yaml" "$CONFIG_DIR/config.yaml"
|
|
success "Created $CONFIG_DIR/config.yaml"
|
|
else
|
|
info "Config file already exists, skipping"
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Validate installation
|
|
validate_installation() {
|
|
info "Validating installation..."
|
|
|
|
local checks_passed=0
|
|
local checks_total=7
|
|
|
|
# Check directories
|
|
[[ -d "$CONFIG_DIR" ]] && ((checks_passed++)) && success "Config directory exists" || warn "Config directory missing"
|
|
[[ -d "$CONFIG_DIR/credentials" ]] && ((checks_passed++)) && success "Credentials directory exists" || warn "Credentials directory missing"
|
|
|
|
# Check files
|
|
[[ -f "$ENV_FILE" ]] && ((checks_passed++)) && success "Environment file exists" || warn "Environment file missing"
|
|
[[ -f "$CONFIG_DIR/config.yaml" ]] && ((checks_passed++)) && success "Config file exists" || warn "Config file missing"
|
|
|
|
# Check virtual environment
|
|
[[ -d "$VENV_DIR" ]] && ((checks_passed++)) && success "Virtual environment exists" || warn "Virtual environment missing"
|
|
|
|
# Check skills directory
|
|
[[ -d "$SKILLS_DIR/01-ourdigital-brand-guide" ]] && ((checks_passed++)) && success "Skills directory valid" || warn "Skills not found"
|
|
|
|
# Check global commands
|
|
local cmd_count
|
|
cmd_count=$(find "$CLAUDE_COMMANDS_DIR" -maxdepth 1 -type l -lname "$REPO_COMMANDS_DIR/*" 2>/dev/null | wc -l | tr -d ' ')
|
|
if [[ $cmd_count -gt 0 ]]; then
|
|
((checks_passed++))
|
|
success "Global slash commands installed ($cmd_count commands)"
|
|
else
|
|
warn "No global slash commands found (run with --commands to install)"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${CYAN}Validation: $checks_passed/$checks_total checks passed${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Show installed skills
|
|
show_skills() {
|
|
info "Installed Skills:"
|
|
echo ""
|
|
|
|
local categories=(
|
|
"OurDigital Core:0{1,2,3,4,5,6,7,8,9}-ourdigital-*,10-ourdigital-*"
|
|
"SEO Tools:1{1,2,3,4,5,6,7,8,9}-seo-*,2{0,1,2,3,4,5,6,7,8,9}-seo-*,3{0,1,2,3,4}-seo-*"
|
|
"Jamie Clinic:4{0,1,2,3,4,5}-jamie-*"
|
|
"NotebookLM:5{0,1,2,3}-notebooklm-*"
|
|
"GTM/GA:6{0,1,2}-gtm-*"
|
|
"Notion:3{1,2}-notion-*"
|
|
"Reference & Multi-Agent:9{0,1}-*"
|
|
)
|
|
|
|
for category in "${categories[@]}"; do
|
|
IFS=':' read -r label patterns <<< "$category"
|
|
local count=0
|
|
local names=()
|
|
|
|
IFS=',' read -ra pattern_arr <<< "$patterns"
|
|
for pattern in "${pattern_arr[@]}"; do
|
|
for dir in "$SKILLS_DIR"/$pattern; do
|
|
if [[ -d "$dir" ]]; then
|
|
((count++))
|
|
names+=("$(basename "$dir")")
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [[ $count -gt 0 ]]; then
|
|
echo -e " ${CYAN}$label${NC} ($count skills)"
|
|
for name in "${names[@]}"; do
|
|
echo -e " ${GREEN}✓${NC} $name"
|
|
done
|
|
echo ""
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Show global command summary
|
|
show_global_commands() {
|
|
info "Global Slash Commands (available from any project):"
|
|
echo ""
|
|
|
|
if [[ ! -d "$CLAUDE_COMMANDS_DIR" ]]; then
|
|
warn "No commands directory found"
|
|
return
|
|
fi
|
|
|
|
local cmd_count=0
|
|
local categories=()
|
|
|
|
for link in "$CLAUDE_COMMANDS_DIR"/*.md; do
|
|
[[ -L "$link" ]] || continue
|
|
local link_target
|
|
link_target=$(readlink "$link")
|
|
if [[ "$link_target" == "$REPO_COMMANDS_DIR/"* ]]; then
|
|
((cmd_count++))
|
|
local name
|
|
name=$(basename "$link" .md)
|
|
categories+=("$name")
|
|
fi
|
|
done
|
|
|
|
if [[ $cmd_count -eq 0 ]]; then
|
|
warn "No commands symlinked from this repo"
|
|
return
|
|
fi
|
|
|
|
# Group by prefix
|
|
local seo=0 gtm=0 jamie=0 notebooklm=0 notion=0 ourdigital=0 other=0
|
|
for name in "${categories[@]}"; do
|
|
case "$name" in
|
|
seo-*) ((seo++)) ;;
|
|
gtm-*) ((gtm++)) ;;
|
|
jamie-*) ((jamie++)) ;;
|
|
notebooklm-*) ((notebooklm++)) ;;
|
|
notion-*) ((notion++)) ;;
|
|
ourdigital-*) ((ourdigital++)) ;;
|
|
*) ((other++)) ;;
|
|
esac
|
|
done
|
|
|
|
[[ $seo -gt 0 ]] && echo -e " ${GREEN}SEO${NC}: $seo commands (/seo-technical, /seo-keyword-strategy, ...)"
|
|
[[ $gtm -gt 0 ]] && echo -e " ${GREEN}GTM${NC}: $gtm commands (/gtm-audit, /gtm-manager)"
|
|
[[ $jamie -gt 0 ]] && echo -e " ${GREEN}Jamie${NC}: $jamie commands (/jamie-editor, /jamie-audit)"
|
|
[[ $notebooklm -gt 0 ]] && echo -e " ${GREEN}NotebookLM${NC}: $notebooklm commands (/notebooklm-agent, ...)"
|
|
[[ $notion -gt 0 ]] && echo -e " ${GREEN}Notion${NC}: $notion commands (/notion-writer, /notion-organizer)"
|
|
[[ $ourdigital -gt 0 ]] && echo -e " ${GREEN}OurDigital${NC}: $ourdigital commands (/ourdigital-research, ...)"
|
|
[[ $other -gt 0 ]] && echo -e " ${GREEN}Other${NC}: $other commands"
|
|
echo ""
|
|
echo -e " ${CYAN}Total: $cmd_count global commands${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Print usage instructions
|
|
print_usage() {
|
|
echo -e "${CYAN}Usage Instructions:${NC}"
|
|
echo ""
|
|
echo "1. Activate the virtual environment:"
|
|
echo " source $VENV_DIR/bin/activate"
|
|
echo ""
|
|
echo "2. Edit credentials if needed:"
|
|
echo " nano $ENV_FILE"
|
|
echo ""
|
|
echo "3. Use slash commands from any project (globally installed):"
|
|
echo " /seo-technical https://example.com"
|
|
echo " /reference-curator \"Claude Code best practices\""
|
|
echo " /ourdigital-research \"topic\""
|
|
echo " /notebooklm-agent"
|
|
echo ""
|
|
echo "4. Update commands after pulling repo changes:"
|
|
echo " ./install.sh --commands"
|
|
echo ""
|
|
}
|
|
|
|
# Uninstall
|
|
uninstall() {
|
|
warn "This will remove OurDigital configuration and global commands (not skill source files)."
|
|
read -p "Continue? (y/N): " -n 1 -r
|
|
echo
|
|
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
info "Cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
# Remove global command symlinks
|
|
remove_global_commands
|
|
|
|
# Backup before removing
|
|
if [[ -f "$ENV_FILE" ]]; then
|
|
cp "$ENV_FILE" "$ENV_FILE.uninstall.backup"
|
|
rm "$ENV_FILE"
|
|
success "Removed $ENV_FILE (backup saved)"
|
|
fi
|
|
|
|
if [[ -d "$VENV_DIR" ]]; then
|
|
rm -rf "$VENV_DIR"
|
|
success "Removed virtual environment"
|
|
fi
|
|
|
|
info "Config directory preserved at $CONFIG_DIR"
|
|
info "To fully remove: rm -rf $CONFIG_DIR"
|
|
|
|
success "Uninstall complete"
|
|
}
|
|
|
|
# Show help
|
|
show_help() {
|
|
echo "OurDigital Skills Installer"
|
|
echo ""
|
|
echo "Usage: ./install.sh [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --help, -h Show this help message"
|
|
echo " --update, -u Update existing installation"
|
|
echo " --uninstall Remove installation (preserves skill source files)"
|
|
echo " --validate Only run validation checks"
|
|
echo " --commands Only install/update global slash commands"
|
|
echo " --skip-creds Skip credentials setup"
|
|
echo " --skip-venv Skip virtual environment setup"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " ./install.sh # Full interactive install"
|
|
echo " ./install.sh --update # Update existing setup"
|
|
echo " ./install.sh --commands # Just install/refresh global commands"
|
|
echo " ./install.sh --skip-creds # Install without credentials"
|
|
echo ""
|
|
echo "Global Commands:"
|
|
echo " Slash commands are symlinked to ~/.claude/commands/ so they"
|
|
echo " work from any project directory in Claude Code."
|
|
echo ""
|
|
}
|
|
|
|
# Main installation flow
|
|
main_install() {
|
|
print_banner
|
|
check_prerequisites
|
|
setup_directories
|
|
setup_global_commands
|
|
setup_environment
|
|
|
|
if [[ "$SKIP_CREDS" != true ]]; then
|
|
setup_credentials
|
|
fi
|
|
|
|
if [[ "$SKIP_VENV" != true ]]; then
|
|
setup_python_env
|
|
fi
|
|
|
|
setup_config
|
|
validate_installation
|
|
show_skills
|
|
show_global_commands
|
|
print_usage
|
|
|
|
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${GREEN}║ Installation Complete! ║${NC}"
|
|
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════╝${NC}"
|
|
}
|
|
|
|
# Parse arguments
|
|
SKIP_CREDS=false
|
|
SKIP_VENV=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--help|-h)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
--uninstall)
|
|
uninstall
|
|
exit 0
|
|
;;
|
|
--validate)
|
|
validate_installation
|
|
show_global_commands
|
|
exit 0
|
|
;;
|
|
--commands)
|
|
info "Installing/updating global slash commands..."
|
|
setup_global_commands
|
|
show_global_commands
|
|
exit 0
|
|
;;
|
|
--update|-u)
|
|
info "Updating installation..."
|
|
setup_global_commands
|
|
setup_python_env
|
|
validate_installation
|
|
show_global_commands
|
|
exit 0
|
|
;;
|
|
--skip-creds)
|
|
SKIP_CREDS=true
|
|
shift
|
|
;;
|
|
--skip-venv)
|
|
SKIP_VENV=true
|
|
shift
|
|
;;
|
|
*)
|
|
error "Unknown option: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Run main install
|
|
main_install
|