-
-
Save yudikarma/199cf63fb4de7d703757476fc972bfb3 to your computer and use it in GitHub Desktop.
Instagram Skills installer for Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -e | |
| # ── Config ─────────────────────────────────────────────────────────── | |
| REPO="https://github.com/AI-Essentials/instagram-skills.git" | |
| # Skills available in this repo (name|description) | |
| SKILLS=( | |
| "instagram-audit|Full account audit - content, performance, voice, competitors" | |
| "instagram-analysis|Analytics dashboards and competitor research via Metricool" | |
| "instagram-thread-carousel|Turn text threads into tweet-style carousel slides" | |
| "instagram-thumbnail|Generate reel covers and post thumbnails with Gemini" | |
| "instagram-reel-script|Script reels with hooks, beats, CTAs, and visual notes" | |
| "instagram-carousel-preview|Stitch carousel slides into a single preview image" | |
| "instagram-sponsor-outreach|Find and pitch AI companies for sponsored posts" | |
| ) | |
| # ── Helpers ────────────────────────────────────────────────────────── | |
| BOLD='\033[1m' | |
| DIM='\033[2m' | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' | |
| info() { echo -e " ${GREEN}>${NC} $1"; } | |
| warn() { echo -e " ${YELLOW}>${NC} $1"; } | |
| fail() { echo -e " ${RED}x${NC} $1"; exit 1; } | |
| step() { echo -e "\n ${BOLD}$1${NC}"; } | |
| cleanup() { | |
| if [ -n "$TMPDIR_CREATED" ] && [ -d "$TMPDIR_CREATED" ]; then | |
| rm -rf "$TMPDIR_CREATED" | |
| fi | |
| } | |
| trap cleanup EXIT | |
| TARGET_DIR="$(pwd)" | |
| echo "" | |
| echo -e " ${CYAN}██╗███╗ ██╗███████╗████████╗ █████╗ ${NC}" | |
| echo -e " ${CYAN}██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗${NC}" | |
| echo -e " ${CYAN}██║██╔██╗ ██║███████╗ ██║ ███████║${NC}" | |
| echo -e " ${CYAN}██║██║╚██╗██║╚════██║ ██║ ██╔══██║${NC}" | |
| echo -e " ${CYAN}██║██║ ╚████║███████║ ██║ ██║ ██║${NC}" | |
| echo -e " ${CYAN}╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝${NC}" | |
| echo "" | |
| echo -e " ${CYAN} ██████╗ ██████╗ █████╗ ███╗ ███╗${NC}" | |
| echo -e " ${CYAN}██╔════╝ ██╔══██╗██╔══██╗████╗ ████║${NC}" | |
| echo -e " ${CYAN}██║ ███╗██████╔╝███████║██╔████╔██║${NC}" | |
| echo -e " ${CYAN}██║ ██║██╔══██╗██╔══██║██║╚██╔╝██║${NC}" | |
| echo -e " ${CYAN}╚██████╔╝██║ ██║██║ ██║██║ ╚═╝ ██║${NC}" | |
| echo -e " ${CYAN} ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝${NC}" | |
| echo "" | |
| # ── Platform selection ─────────────────────────────────────────────── | |
| if [ "$1" = "claude" ] || [ "$1" = "claude-code" ]; then | |
| PLATFORM="claude"; shift | |
| elif [ "$1" = "openclaw" ]; then | |
| PLATFORM="openclaw"; shift | |
| else | |
| echo -e " ${BOLD}Where are you installing?${NC}" | |
| echo "" | |
| echo -e " ${CYAN}1)${NC} Claude Code ${DIM}(default)${NC}" | |
| echo -e " ${CYAN}2)${NC} OpenClaw" | |
| echo "" | |
| printf " Choice [1]: " | |
| read -r CHOICE < /dev/tty | |
| CHOICE="${CHOICE:-1}" | |
| case "$CHOICE" in | |
| 2|openclaw) PLATFORM="openclaw" ;; | |
| *) PLATFORM="claude" ;; | |
| esac | |
| fi | |
| if [ "$PLATFORM" = "openclaw" ]; then | |
| SKILLS_DIR=".openclaw/skills" | |
| PLATFORM_LABEL="OpenClaw" | |
| else | |
| SKILLS_DIR=".claude/skills" | |
| PLATFORM_LABEL="Claude Code" | |
| fi | |
| # Detect install vs update | |
| FIRST_SKILL=$(echo "${SKILLS[0]}" | cut -d'|' -f1) | |
| if [ -d "$TARGET_DIR/$SKILLS_DIR/$FIRST_SKILL" ]; then | |
| MODE="update" | |
| else | |
| MODE="install" | |
| fi | |
| if [ "$MODE" = "update" ]; then | |
| echo -e " ${DIM}Updating for ${PLATFORM_LABEL}${NC}" | |
| else | |
| echo -e " ${DIM}Installing for ${PLATFORM_LABEL}${NC}" | |
| fi | |
| # ── Resolve source ─────────────────────────────────────────────────── | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null || echo ".")" && pwd)" | |
| if [ -f "$SCRIPT_DIR/.claude/skills/$FIRST_SKILL/SKILL.md" ]; then | |
| SOURCE_DIR="$SCRIPT_DIR" | |
| else | |
| step "Fetching" | |
| TMPDIR_CREATED="$(mktemp -d)" | |
| git clone --depth 1 "$REPO" "$TMPDIR_CREATED" 2>/dev/null || fail "Clone failed. Do you have access to the repo?" | |
| SOURCE_DIR="$TMPDIR_CREATED" | |
| info "done" | |
| fi | |
| # ── Skill selection ────────────────────────────────────────────────── | |
| step "Skills" | |
| # Check for command-line argument (e.g., bash -s -- instagram-audit) | |
| if [ -n "$1" ]; then | |
| MATCH_FOUND=0 | |
| for i in "${!SKILLS[@]}"; do | |
| NAME=$(echo "${SKILLS[$i]}" | cut -d'|' -f1) | |
| if [ "$NAME" = "$1" ]; then | |
| SELECTED=("$i") | |
| MATCH_FOUND=1 | |
| info "$NAME" | |
| break | |
| fi | |
| done | |
| if [ "$MATCH_FOUND" -eq 0 ]; then | |
| fail "Unknown skill: $1. Available: $(printf '%s ' "${SKILLS[@]}" | sed 's/|[^ ]* */ /g')" | |
| fi | |
| else | |
| # Initialize all skills as selected | |
| for i in "${!SKILLS[@]}"; do | |
| TOGGLED[$i]=1 | |
| done | |
| CURSOR=0 | |
| TOTAL=${#SKILLS[@]} | |
| draw_menu() { | |
| if [ "$1" = "redraw" ]; then | |
| for ((i = 0; i < TOTAL; i++)); do | |
| echo -ne "\033[2A" | |
| done | |
| fi | |
| for i in "${!SKILLS[@]}"; do | |
| NAME=$(echo "${SKILLS[$i]}" | cut -d'|' -f1) | |
| DESC=$(echo "${SKILLS[$i]}" | cut -d'|' -f2) | |
| if [ "${TOGGLED[$i]}" -eq 1 ]; then | |
| CHECK="${GREEN}*${NC}" | |
| else | |
| CHECK="${DIM}-${NC}" | |
| fi | |
| if [ "$i" -eq "$CURSOR" ]; then | |
| POINTER="${CYAN}>${NC}" | |
| else | |
| POINTER=" " | |
| fi | |
| echo -e " ${POINTER} ${CHECK} ${BOLD}${NAME}${NC} ${DIM}${DESC}${NC}" | |
| echo -e "" | |
| done | |
| } | |
| echo "" | |
| echo -e " ${DIM}arrows = move, space = toggle, enter = confirm${NC}" | |
| echo "" | |
| draw_menu | |
| while true; do | |
| IFS= read -rsn1 KEY < /dev/tty | |
| if [ "$KEY" = "" ]; then | |
| break | |
| elif [ "$KEY" = " " ]; then | |
| if [ "${TOGGLED[$CURSOR]}" -eq 1 ]; then | |
| TOGGLED[$CURSOR]=0 | |
| else | |
| TOGGLED[$CURSOR]=1 | |
| fi | |
| draw_menu redraw | |
| elif [ "$KEY" = $'\x1b' ]; then | |
| read -rsn1 SEQ1 < /dev/tty | |
| read -rsn1 SEQ2 < /dev/tty | |
| if [ "$SEQ1" = "[" ]; then | |
| case "$SEQ2" in | |
| A) CURSOR=$(( (CURSOR - 1 + TOTAL) % TOTAL )); draw_menu redraw ;; | |
| B) CURSOR=$(( (CURSOR + 1) % TOTAL )); draw_menu redraw ;; | |
| esac | |
| fi | |
| fi | |
| done | |
| SELECTED=() | |
| for i in "${!SKILLS[@]}"; do | |
| if [ "${TOGGLED[$i]}" -eq 1 ]; then | |
| SELECTED+=("$i") | |
| fi | |
| done | |
| if [ "${#SELECTED[@]}" -eq 0 ]; then | |
| fail "No skills selected" | |
| fi | |
| echo "" | |
| for IDX in "${SELECTED[@]}"; do | |
| NAME=$(echo "${SKILLS[$IDX]}" | cut -d'|' -f1) | |
| info "$NAME" | |
| done | |
| fi # end of interactive menu | |
| # ── Prerequisites ──────────────────────────────────────────────────── | |
| step "Prerequisites" | |
| # Python check | |
| if command -v python3 >/dev/null 2>&1; then | |
| PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') | |
| PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1) | |
| PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2) | |
| if [ "$PYTHON_MAJOR" -lt 3 ] || { [ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR" -lt 10 ]; }; then | |
| warn "Python 3.10+ recommended (found $PYTHON_VERSION) - carousel and dashboard skills won't work" | |
| else | |
| info "Python ${DIM}$PYTHON_VERSION${NC}" | |
| fi | |
| else | |
| warn "Python 3 not found - carousel and dashboard skills won't work without it" | |
| fi | |
| # ffmpeg check | |
| if command -v ffmpeg >/dev/null 2>&1; then | |
| info "ffmpeg ${DIM}found${NC}" | |
| else | |
| warn "ffmpeg not found - animated carousel slides will fall back to GIF" | |
| echo -e " ${DIM} Install with: brew install ffmpeg${NC}" | |
| fi | |
| # ── Copy skill files ───────────────────────────────────────────────── | |
| if [ "$SOURCE_DIR" != "$TARGET_DIR" ]; then | |
| step "Installing" | |
| mkdir -p "$TARGET_DIR/$SKILLS_DIR" | |
| for IDX in "${SELECTED[@]}"; do | |
| NAME=$(echo "${SKILLS[$IDX]}" | cut -d'|' -f1) | |
| if [ -d "$TARGET_DIR/$SKILLS_DIR/$NAME" ]; then | |
| rm -rf "$TARGET_DIR/$SKILLS_DIR/$NAME" | |
| fi | |
| cp -r "$SOURCE_DIR/.claude/skills/$NAME" "$TARGET_DIR/$SKILLS_DIR/$NAME" | |
| info "$SKILLS_DIR/$NAME/" | |
| done | |
| # Supporting files | |
| mkdir -p "$TARGET_DIR/assets/headshots" | |
| if [ -d "$SOURCE_DIR/assets/headshots" ]; then | |
| cp -n "$SOURCE_DIR/assets/headshots/"* "$TARGET_DIR/assets/headshots/" 2>/dev/null || true | |
| fi | |
| # CLAUDE.md.example | |
| if [ -f "$SOURCE_DIR/CLAUDE.md.example" ] && [ ! -f "$TARGET_DIR/CLAUDE.md" ]; then | |
| cp "$SOURCE_DIR/CLAUDE.md.example" "$TARGET_DIR/CLAUDE.md.example" | |
| info "CLAUDE.md.example" | |
| fi | |
| # Requirements | |
| if [ -f "$SOURCE_DIR/requirements.txt" ]; then | |
| if [ -f "$TARGET_DIR/requirements.txt" ]; then | |
| while IFS= read -r pkg; do | |
| [ -z "$pkg" ] && continue | |
| PKG_NAME=$(echo "$pkg" | sed 's/[>=<].*//') | |
| if ! grep -q "$PKG_NAME" "$TARGET_DIR/requirements.txt" 2>/dev/null; then | |
| echo "$pkg" >> "$TARGET_DIR/requirements.txt" | |
| fi | |
| done < "$SOURCE_DIR/requirements.txt" | |
| else | |
| cp "$SOURCE_DIR/requirements.txt" "$TARGET_DIR/requirements.txt" | |
| fi | |
| info "requirements.txt" | |
| fi | |
| # .env.example | |
| if [ ! -f "$TARGET_DIR/.env.example" ]; then | |
| cp "$SOURCE_DIR/.env.example" "$TARGET_DIR/.env.example" | |
| info ".env.example" | |
| fi | |
| fi | |
| # ── Environment ────────────────────────────────────────────────────── | |
| cd "$TARGET_DIR" | |
| step "Environment" | |
| if [ ! -f .env ]; then | |
| if [ -f .env.example ]; then | |
| cp .env.example .env | |
| info "Created .env from template" | |
| fi | |
| fi | |
| # Walk through each API key | |
| declare -A ENV_KEYS | |
| ENV_KEYS=( | |
| ["GEMINI_API_KEY"]="Gemini API (image generation)|https://aistudio.google.com/apikey" | |
| ["TAVILY_API_KEY"]="Tavily (image search)|https://tavily.com" | |
| ["GIPHY_API_KEY"]="Giphy (GIF search)|https://developers.giphy.com" | |
| ["SCRAPECREATORS_API_KEY"]="ScrapeCreators (reel scraping)|https://scrapecreators.com" | |
| ) | |
| if [ -f .env ]; then | |
| HAS_EMPTY=0 | |
| for KEY in GEMINI_API_KEY TAVILY_API_KEY GIPHY_API_KEY SCRAPECREATORS_API_KEY; do | |
| CURRENT=$(grep "^${KEY}=" .env 2>/dev/null | cut -d'=' -f2) | |
| if [ -z "$CURRENT" ] || echo "$CURRENT" | grep -q "your_"; then | |
| DESC=$(echo "${ENV_KEYS[$KEY]}" | cut -d'|' -f1) | |
| URL=$(echo "${ENV_KEYS[$KEY]}" | cut -d'|' -f2) | |
| echo "" | |
| echo -e " ${BOLD}${KEY}${NC} ${DIM}- ${DESC}${NC}" | |
| echo -e " ${DIM}Sign up: ${URL}${NC}" | |
| printf " Paste key (or press enter to skip): " | |
| read -r VALUE < /dev/tty | |
| if [ -n "$VALUE" ]; then | |
| if grep -q "^${KEY}=" .env 2>/dev/null; then | |
| sed -i.bak "s|^${KEY}=.*|${KEY}=${VALUE}|" .env && rm -f .env.bak | |
| else | |
| echo "${KEY}=${VALUE}" >> .env | |
| fi | |
| info "${KEY} saved" | |
| else | |
| HAS_EMPTY=1 | |
| fi | |
| else | |
| info "${KEY} ${DIM}already set${NC}" | |
| fi | |
| done | |
| if [ "$HAS_EMPTY" -eq 1 ]; then | |
| echo "" | |
| warn "Some keys skipped - add them to .env later" | |
| fi | |
| else | |
| warn "No .env.example found - create .env manually with your API keys" | |
| fi | |
| # ── MCP Server (Metricool) ─────────────────────────────────────────── | |
| step "MCP Server" | |
| echo "" | |
| echo -e " ${BOLD}Metricool${NC} ${DIM}- powers analytics and audit skills${NC}" | |
| echo -e " ${DIM}Sign up: https://metricool.com${NC}" | |
| echo "" | |
| printf " Set up Metricool MCP now? [y/N]: " | |
| read -r SETUP_MCP < /dev/tty | |
| if [ "$SETUP_MCP" = "y" ] || [ "$SETUP_MCP" = "Y" ]; then | |
| printf " Metricool User Token: " | |
| read -r MC_TOKEN < /dev/tty | |
| printf " Metricool User ID: " | |
| read -r MC_ID < /dev/tty | |
| if [ -n "$MC_TOKEN" ] && [ -n "$MC_ID" ]; then | |
| if command -v claude >/dev/null 2>&1; then | |
| claude mcp add-json mcp-metricool "{ | |
| \"command\": \"uvx\", | |
| \"args\": [\"--upgrade\", \"mcp-metricool\"], | |
| \"env\": { | |
| \"METRICOOL_USER_TOKEN\": \"${MC_TOKEN}\", | |
| \"METRICOOL_USER_ID\": \"${MC_ID}\" | |
| } | |
| }" 2>/dev/null && info "Metricool MCP configured" || warn "MCP setup failed - configure manually" | |
| else | |
| warn "claude CLI not found - add Metricool MCP manually (see install.md)" | |
| fi | |
| else | |
| warn "Skipped - add Metricool MCP manually later" | |
| fi | |
| else | |
| info "Skipped ${DIM}- add later with: claude mcp add-json mcp-metricool ...${NC}" | |
| fi | |
| # ── Python ─────────────────────────────────────────────────────────── | |
| if command -v python3 >/dev/null 2>&1 && [ -f requirements.txt ]; then | |
| step "Python" | |
| if [ ! -d .venv ]; then | |
| python3 -m venv .venv | |
| fi | |
| source .venv/bin/activate 2>/dev/null || . .venv/bin/activate | |
| pip install -q -r requirements.txt 2>/dev/null | |
| info "Packages installed" | |
| # Install Playwright browsers for website screenshots | |
| if python3 -c "import playwright" 2>/dev/null; then | |
| playwright install chromium 2>/dev/null && info "Playwright chromium installed" || warn "Playwright browser install failed - run: playwright install chromium" | |
| fi | |
| fi | |
| # ── CLAUDE.md setup ────────────────────────────────────────────────── | |
| if [ ! -f "$TARGET_DIR/CLAUDE.md" ] && [ -f "$TARGET_DIR/CLAUDE.md.example" ]; then | |
| step "Config" | |
| cp "$TARGET_DIR/CLAUDE.md.example" "$TARGET_DIR/CLAUDE.md" | |
| info "Created CLAUDE.md from template" | |
| warn "Run /instagram-audit to personalize it" | |
| fi | |
| # ── Done ───────────────────────────────────────────────────────────── | |
| echo "" | |
| echo "" | |
| if [ "$MODE" = "update" ]; then | |
| echo -e " ${GREEN}${BOLD}Updated!${NC}" | |
| echo "" | |
| echo -e " ${DIM}Restart ${PLATFORM_LABEL} to pick up changes.${NC}" | |
| else | |
| echo -e " ${GREEN}${BOLD}Installed!${NC}" | |
| echo "" | |
| echo -e " ${DIM}1.${NC} Add any missing API keys to .env" | |
| echo -e " ${DIM}2.${NC} Open ${PLATFORM_LABEL} in this directory" | |
| echo -e " ${DIM}3.${NC} Run ${CYAN}/instagram-audit${NC} to set up your account" | |
| echo "" | |
| echo -e " ${DIM}All skills:${NC}" | |
| for IDX in "${SELECTED[@]}"; do | |
| NAME=$(echo "${SKILLS[$IDX]}" | cut -d'|' -f1) | |
| echo -e " ${CYAN}/${NAME}${NC}" | |
| done | |
| fi | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment