#!/bin/bash # AI Assets Installer — Agents & Skills for Cursor, Windsurf, Cline, etc. # Run from your repo root (where /assets/ lives) set -euo pipefail echo "=== AI Assets Installer (Agents & Skills) ===" echo "Popular tools supported in 2026: Cursor, Windsurf, Cline, RooCode, etc." echo ASSETS_DIR="${ASSETS_DIR:-./assets}" AGENTS_DIR="$ASSETS_DIR/agents" SKILLS_DIR="$ASSETS_DIR/skills" GLOBAL_ASSETS_DIR="${GLOBAL_ASSETS_DIR:-$HOME/.agents}" # ================== PRODUCTS (easy to edit) ================== # Format: "Display Name:folder_name" PRODUCTS=( "Cursor:.cursor" "Windsurf:.windsurf" "Cline:.cline" "RooCode:.roo" "Continue:.continue" "VS Code / Copilot:.vscode" "GitHub / Copilot:.github" "Claude Code:.claude" # Add more here, e.g. "Aider:.aider" ) # ================== SELECT HELPERS ================== select_items() { local dir=$1 local type=$2 local items=() if [ ! -d "$dir" ]; then echo "⚠️ $dir not found — skipping $type." >&2 return fi if [ "$type" = "agents" ]; then while IFS= read -r item; do items+=("$item") done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -name '*.md' -exec basename {} \; | sort) else while IFS= read -r item; do items+=("$item") done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort) fi if [ ${#items[@]} -eq 0 ]; then echo "No $type found." >&2 return fi echo "Available $type (${#items[@]}):" >&2 for i in "${!items[@]}"; do printf " %2d) %s\n" $((i+1)) "${items[i]}" >&2 done read -r -p "Select (all / 1,3,5): " sel if [[ "$sel" =~ ^[Aa][Ll][Ll]$ ]] || [ -z "$sel" ]; then printf '%s\n' "${items[@]}" else local selected=() IFS=',' read -ra nums <<< "$sel" for n in "${nums[@]}"; do idx=$((n-1)) if (( idx >= 0 && idx < ${#items[@]} )); then selected+=("${items[idx]}") fi done printf '%s\n' "${selected[@]}" fi } # ================== USER CHOICES ================== INSTALL_TYPE="${INSTALL_TYPE:-}" INSTALL_MODE="${INSTALL_MODE:-}" if [ -z "$INSTALL_TYPE" ]; then echo "=== Step 1: What do you want to install? ===" echo "1) Agents" echo "2) Skills" read -r -p "Choose (1/2): " install_choice case "$install_choice" in 1) INSTALL_TYPE="agents" ;; 2) INSTALL_TYPE="skills" ;; *) echo "Invalid selection. Use 1 or 2."; exit 1 ;; esac fi echo -e "\n=== Step 2: Install Mode ===" if [ -z "$INSTALL_MODE" ]; then echo "1) Direct copy into product folders (~/.cursor, ~/.claude, ...)" echo "2) Global install only ($GLOBAL_ASSETS_DIR)" echo "3) Global install + symlink into product folders" read -r -p "Choose (1/2/3): " mode else mode="$INSTALL_MODE" fi GLOBAL_INSTALL=false SYMLINK_TO_PRODUCTS=false case "$mode" in 1|direct) GLOBAL_INSTALL=false SYMLINK_TO_PRODUCTS=false ;; 2|global) GLOBAL_INSTALL=true SYMLINK_TO_PRODUCTS=false ;; 3|symlink|global+symlink|global_symlink|products) GLOBAL_INSTALL=true SYMLINK_TO_PRODUCTS=true ;; *) echo "Invalid install mode. Use 1, 2, or 3." exit 1 ;; esac if [ "$INSTALL_TYPE" = "agents" ]; then echo -e "\n=== Step 3: Agents ===" selected_agents=($(select_items "$AGENTS_DIR" "agents")) selected_skills=() elif [ "$INSTALL_TYPE" = "skills" ]; then echo -e "\n=== Step 3: Skills ===" selected_skills=($(select_items "$SKILLS_DIR" "skills")) selected_agents=() else echo "Invalid INSTALL_TYPE: $INSTALL_TYPE (expected: agents or skills)" exit 1 fi echo -e "\n=== Step 4: Target Products ===" for i in "${!PRODUCTS[@]}"; do name="${PRODUCTS[i]%%:*}" printf " %2d) %s\n" $((i+1)) "$name" done read -r -p "Select products (all / 1,2,4): " prod_sel selected_products=() if [[ "$prod_sel" =~ ^[Aa][Ll][Ll]$ ]] || [ -z "$prod_sel" ]; then selected_products=("${PRODUCTS[@]}") else IFS=',' read -ra pnums <<< "$prod_sel" for n in "${pnums[@]}"; do idx=$((n-1)) if (( idx >= 0 && idx < ${#PRODUCTS[@]} )); then selected_products+=("${PRODUCTS[idx]}") fi done fi # ================== CONFIRMATION ================== echo -e "\n=== SUMMARY ===" echo "Component : $INSTALL_TYPE" if [ "$INSTALL_TYPE" = "agents" ]; then echo "Agents : ${selected_agents[*]:-none}" else echo "Skills : ${selected_skills[*]:-none}" fi echo "Products : $(printf '%s ' "${selected_products[@]%%:*}")" if [ "$GLOBAL_INSTALL" = false ]; then echo "Mode : Direct to product folders" elif [ "$SYMLINK_TO_PRODUCTS" = true ]; then echo "Mode : Global install + product symlink" else echo "Mode : Global install only" fi echo "Global Dir : $GLOBAL_ASSETS_DIR" echo read -r -p "Install now? (y/N) " confirm [[ "$confirm" =~ ^[Yy]$ ]] || { echo "Cancelled."; exit 0; } # ================== DO THE INSTALL ================== echo -e "\nInstalling...\n" global_component_copied=false for entry in "${selected_products[@]}"; do name="${entry%%:*}" folder="${entry#*:}" if [ "$GLOBAL_INSTALL" = true ]; then base_dir="$GLOBAL_ASSETS_DIR" product_dir="$HOME/$folder" else base_dir="$HOME/$folder" product_dir="$base_dir" fi mkdir -p "$base_dir" echo "→ $name ($folder)" if [ "$GLOBAL_INSTALL" = false ] || [ "$global_component_copied" = false ]; then if [ "$INSTALL_TYPE" = "agents" ]; then for item in "${selected_agents[@]}"; do src="$AGENTS_DIR/$item" if [ -f "$src" ]; then dest="$base_dir/agents/$item" mkdir -p "$(dirname "$dest")" cp -f "$src" "$dest" echo " ✓ Agent: $item" elif [ -d "$src" ]; then dest="$base_dir/agents/$item" rm -rf "$dest" mkdir -p "$(dirname "$dest")" cp -r "$src" "$dest" echo " ✓ Agent: $item" fi done fi if [ "$INSTALL_TYPE" = "skills" ]; then for item in "${selected_skills[@]}"; do src="$SKILLS_DIR/$item" if [ -d "$src" ]; then dest="$base_dir/skills/$item" rm -rf "$dest" mkdir -p "$(dirname "$dest")" cp -r "$src" "$dest" echo " ✓ Skill: $item" fi done fi if [ "$GLOBAL_INSTALL" = true ]; then global_component_copied=true fi fi # Symlink global content into selected product folder if requested if [ "$SYMLINK_TO_PRODUCTS" = true ]; then mkdir -p "$product_dir" if [ -d "$base_dir/$INSTALL_TYPE" ]; then ln -sfn "$base_dir/$INSTALL_TYPE" "$product_dir/$INSTALL_TYPE" echo " → Symlinked $INSTALL_TYPE into $product_dir" fi fi done echo -e "\n✅ Done! Your $INSTALL_TYPE are now available in the selected tools." echo " Tip: git add the .cursor/, .windsurf/, etc. folders if you want to version them."