246 lines
6.7 KiB
Bash
Executable File
246 lines
6.7 KiB
Bash
Executable File
#!/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."
|