#!/usr/bin/env bash set -euo pipefail # ───────────────────────────────────────────────────────────────────── # Mobile AI Assets Installer # # One script to install skills, agents, and instructions. # Works both locally (cloned repo) and remotely (curl, no clone). # # Local: ./assets/setup.sh agents # Remote: export ASSETS_BASE_URL="https://..." # bash <(curl -fsSL "$ASSETS_BASE_URL/setup.sh") agents # ───────────────────────────────────────────────────────────────────── VERSION="1.0.0" # ── Configuration (override with env vars) ─────────────────────────── ASSETS_BASE_URL="${ASSETS_BASE_URL:-}" AGENTS_DIR="${AGENTS_DIR:-$HOME/.copilot/agents}" INSTRUCTIONS_DIR="${INSTRUCTIONS_DIR:-./instructions}" # ── Colors ─────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' BOLD='\033[1m' NC='\033[0m' # ── Helpers ────────────────────────────────────────────────────────── info() { printf "${BLUE}▸${NC} %s\n" "$*"; } ok() { printf "${GREEN}✓${NC} %s\n" "$*"; } warn() { printf "${YELLOW}!${NC} %s\n" "$*"; } fail() { printf "${RED}✗${NC} %s\n" "$*" >&2; exit 1; } heading() { printf "\n${BOLD}── %s ──${NC}\n" "$*"; } # ── Mode Detection ─────────────────────────────────────────────────── # Detect if running from a cloned repo or remotely via curl. MODE="" ASSETS_DIR="" detect_mode() { local script_dir script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)" || script_dir="" if [[ -n "$script_dir" && -f "$script_dir/agents.list" ]]; then MODE="local" ASSETS_DIR="$script_dir" elif [[ -n "$ASSETS_BASE_URL" ]]; then MODE="remote" else fail "Set ASSETS_BASE_URL to run without a clone. Example: export ASSETS_BASE_URL=\"https://gitlab.com/org/repo/-/raw/main/assets\" bash <(curl -fsSL \"\$ASSETS_BASE_URL/setup.sh\") agents" fi } # ── Fetch / Download ──────────────────────────────────────────────── # Read a file from the local clone or download it from the remote URL. fetch() { local path="$1" if [[ "$MODE" == "local" ]]; then cat "$ASSETS_DIR/$path" else curl -fsSL "$ASSETS_BASE_URL/$path" fi } # Download a file to a destination path. download_to() { local src="$1" dest="$2" mkdir -p "$(dirname "$dest")" if [[ "$MODE" == "local" ]]; then cp "$ASSETS_DIR/$src" "$dest" else curl -fsSL "$ASSETS_BASE_URL/$src" -o "$dest" fi } # ── Commands ───────────────────────────────────────────────────────── # -- skills [platform] ------------------------------------------------ cmd_skills() { local platform="${1:-shared}" local manifest="${platform}-skills.yaml" heading "Skills ($platform)" local yaml yaml="$(fetch "$manifest")" || fail "Could not fetch $manifest" local installs installs=$(echo "$yaml" | sed -n 's/^[[:space:]]*install:[[:space:]]*//p' | sed 's/^"//;s/"$//') if [[ -z "$installs" ]]; then warn "No install entries in $manifest — nothing to do." return 0 fi local count=0 while IFS= read -r entry; do [[ -z "$entry" ]] && continue read -r -a args <<< "$entry" info "npx skills add ${args[*]}" npx skills add "${args[@]}" count=$((count + 1)) done <<< "$installs" ok "$count skill(s) installed. Restart your editor if they don't appear." } # -- agents ------------------------------------------------------------ cmd_agents() { heading "Agents → $AGENTS_DIR" local list list="$(fetch "agents.list")" || fail "Could not fetch agents.list" mkdir -p "$AGENTS_DIR" local count=0 while IFS= read -r file; do [[ -z "$file" || "$file" == \#* ]] && continue info "$file" download_to "agents/$file" "$AGENTS_DIR/$file" count=$((count + 1)) done <<< "$list" ok "$count agent(s) installed." } # -- instructions ------------------------------------------------------ cmd_instructions() { heading "Instructions → $INSTRUCTIONS_DIR" local list list="$(fetch "instructions.list")" || fail "Could not fetch instructions.list" mkdir -p "$INSTRUCTIONS_DIR" local count=0 while IFS= read -r file; do [[ -z "$file" || "$file" == \#* ]] && continue info "$file" download_to "instructions/$file" "$INSTRUCTIONS_DIR/$file" count=$((count + 1)) done <<< "$list" ok "$count instruction(s) installed." } # -- all [platform] ---------------------------------------------------- cmd_all() { local platform="${1:-shared}" cmd_skills "$platform" cmd_agents cmd_instructions printf "\n" ok "All done." } # -- update-lists (local only) ----------------------------------------- # Regenerate agents.list and instructions.list from directory contents. cmd_update_lists() { if [[ "$MODE" != "local" ]]; then fail "update-lists only works from a cloned repo." fi heading "Updating manifests" # agents.list local agents_dir="$ASSETS_DIR/agents" if [[ -d "$agents_dir" ]]; then find "$agents_dir" -maxdepth 1 -type f -name '*.agent.md' \ | xargs -I{} basename {} \ | sort > "$ASSETS_DIR/agents.list" ok "agents.list → $(wc -l < "$ASSETS_DIR/agents.list" | tr -d ' ') file(s)" else warn "No agents/ directory found — skipping." fi # instructions.list local instr_dir="$ASSETS_DIR/instructions" if [[ -d "$instr_dir" ]]; then find "$instr_dir" -maxdepth 1 -type f -name '*.instructions.md' \ | xargs -I{} basename {} \ | sort > "$ASSETS_DIR/instructions.list" ok "instructions.list → $(wc -l < "$ASSETS_DIR/instructions.list" | tr -d ' ') file(s)" else warn "No instructions/ directory found — skipping." fi } # -- help -------------------------------------------------------------- cmd_help() { cat < [platform] ${BOLD}COMMANDS${NC} skills [platform] Install skills from a curated list agents Install all agent prompt files instructions Install all instruction files all [platform] Install everything at once update-lists Regenerate agents.list / instructions.list (local only) help Show this message ${BOLD}PLATFORMS${NC} (for skills) ios iOS-specific skills android Android-specific skills shared Cross-platform skills (default) ${BOLD}EXAMPLES${NC} ${GREEN}# Cloned repo${NC} ./assets/setup.sh skills ios ./assets/setup.sh agents ./assets/setup.sh all ios ${GREEN}# No clone — run directly from the remote repo${NC} export ASSETS_BASE_URL="https://gitlab.com/org/repo/-/raw/main/assets" bash <(curl -fsSL "\$ASSETS_BASE_URL/setup.sh") skills ios bash <(curl -fsSL "\$ASSETS_BASE_URL/setup.sh") agents bash <(curl -fsSL "\$ASSETS_BASE_URL/setup.sh") all ios ${BOLD}ENVIRONMENT VARIABLES${NC} ASSETS_BASE_URL Base URL for remote downloads (required without clone) AGENTS_DIR Install location for agents (default: ~/.copilot/agents) INSTRUCTIONS_DIR Install location for instructions (default: ./instructions) SKILLS_FILE Override the skills YAML path (local mode only) EOF } # ── Main ───────────────────────────────────────────────────────────── detect_mode case "${1:-help}" in skills) cmd_skills "${2:-shared}" ;; agents) cmd_agents ;; instructions) cmd_instructions ;; all) cmd_all "${2:-shared}" ;; update-lists) cmd_update_lists ;; help|--help|-h) cmd_help ;; *) fail "Unknown command: $1. Run 'setup.sh help' for usage." ;; esac