- Extract 9 nested skills from website-creator/ to root skills/ - Remove duplicate seo-analyzers, seo-geo, seo-multi-channel from website-creator - Add new ui-ux-pro-max skill with full UI/UX data - Update install-skills.sh to sync properly - Remove .DS_Store artifacts Moved skills: - api-and-interface-design - banner-design - brand - design-system - design - frontend-ui-engineering - slides - spec-driven-development - ui-styling
347 lines
9.7 KiB
Bash
Executable File
347 lines
9.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# OpenCode Skills Installer
|
|
# Simple, bash 3 compatible script
|
|
|
|
set -e
|
|
|
|
# Config
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
SKILLS_DIR="${REPO_ROOT}/skills"
|
|
GLOBAL_DIR="${HOME}/.config/opencode"
|
|
GLOBAL_SKILLS_DIR="${HOME}/.opencode/skills"
|
|
UNIFIED_ENV="${GLOBAL_DIR}/.env"
|
|
REPO_UNIFIED_ENV="${REPO_ROOT}/.env"
|
|
|
|
# Args
|
|
FORCE=0
|
|
AUTO_YES=0
|
|
INSTALL_GLOBAL=1
|
|
|
|
usage() {
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -f, --force Overwrite existing skills without prompting"
|
|
echo " -y, --yes Answer yes to all prompts"
|
|
echo " -g, --global Install to global ~/.config/opencode/skills (default)"
|
|
echo " -p, --project Install to project .opencode/skills"
|
|
echo " -h, --help Show this help message"
|
|
exit 0
|
|
}
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-f|--force) FORCE=1; shift ;;
|
|
-y|--yes) AUTO_YES=1; shift ;;
|
|
-g|--global) INSTALL_GLOBAL=1; shift ;;
|
|
-p|--project) INSTALL_GLOBAL=0; shift ;;
|
|
-h|--help) usage ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
# Colors
|
|
INFO='\033[0;34m'
|
|
SUCCESS='\033[0;32m'
|
|
WARNING='\033[1;33m'
|
|
ERROR='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
print_info() { echo -e "${INFO}[INFO]${NC} $1"; }
|
|
print_success() { echo -e "${SUCCESS}[OK]${NC} $1"; }
|
|
print_warning() { echo -e "${WARNING}[WARN]${NC} $1"; }
|
|
print_error() { echo -e "${ERROR}[ERR]${NC} $1"; }
|
|
line() { echo "=========================================="; }
|
|
|
|
# Get list of skills
|
|
get_skills() {
|
|
local found=""
|
|
for dir in "$SKILLS_DIR"/*/; do
|
|
[ -d "$dir" ] || continue
|
|
name=$(basename "$dir")
|
|
# Skip if it's an embedded git repo (like cloned external skills)
|
|
[ -d "$dir/.git" ] && continue
|
|
[ -f "$dir/SKILL.md" ] && found="$found $name"
|
|
done
|
|
echo $found
|
|
}
|
|
|
|
# Get env vars from .env.example
|
|
get_env_vars() {
|
|
local file="$1"
|
|
[ -f "$file" ] || return
|
|
grep -v '^#' "$file" | grep -v '^$' | grep '=' | cut -d'=' -f1
|
|
}
|
|
|
|
# Setup unified .env
|
|
setup_unified_env() {
|
|
local env_example="${REPO_ROOT}/.env.example"
|
|
local env_file="${REPO_ROOT}/.env"
|
|
|
|
[ -f "$env_example" ] || return
|
|
|
|
# Check if .env already exists in repo - skip interactive setup if it does
|
|
if [ -f "$env_file" ]; then
|
|
line
|
|
print_success "Using existing .env file in project"
|
|
line
|
|
echo ""
|
|
return
|
|
fi
|
|
|
|
line
|
|
print_info "Unified Configuration Setup"
|
|
line
|
|
echo ""
|
|
print_info "Found unified .env.example"
|
|
echo "This file contains credentials for ALL skills."
|
|
echo ""
|
|
|
|
# Get all env vars
|
|
ENV_VARS=$(get_env_vars "$env_example")
|
|
|
|
if [ -n "$ENV_VARS" ]; then
|
|
print_info "Environment variables needed:"
|
|
for v in $ENV_VARS; do
|
|
echo " - $v"
|
|
done
|
|
line
|
|
echo ""
|
|
echo "Please enter values below:"
|
|
echo "(Press Enter to skip optional values)"
|
|
echo ""
|
|
|
|
VALUES_FILE="/tmp/opencode_values_$$"
|
|
> "$VALUES_FILE"
|
|
|
|
for var in $ENV_VARS; do
|
|
val=""
|
|
confirm=""
|
|
matched=0
|
|
|
|
# Check if it's optional or per-website
|
|
is_optional=0
|
|
is_per_website=0
|
|
case "$var" in
|
|
UMAMI_WEBSITE_ID) is_per_website=1 ;;
|
|
CHUTES_*) is_optional=1 ;;
|
|
esac
|
|
|
|
if [ $is_per_website -eq 1 ]; then
|
|
print_info "Skipping $var (configured per-website)"
|
|
continue
|
|
fi
|
|
|
|
while [ $matched -eq 0 ]; do
|
|
val=""
|
|
printf "Enter %s: " "$var"
|
|
read val
|
|
|
|
# Allow empty for optional vars
|
|
if [ -z "$val" ] && [ $is_optional -eq 1 ]; then
|
|
matched=1
|
|
continue
|
|
fi
|
|
|
|
[ -z "$val" ] && print_error "Cannot be empty (or type 'skip' for optional)" && continue
|
|
|
|
# Confirm required values
|
|
if [ $is_optional -eq 0 ]; then
|
|
printf "Confirm %s: " "$var"
|
|
read confirm
|
|
|
|
if [ "$val" = "$confirm" ]; then
|
|
matched=1
|
|
else
|
|
print_error "Values don't match, try again"
|
|
fi
|
|
else
|
|
matched=1
|
|
fi
|
|
done
|
|
|
|
[ -n "$val" ] && [ "$val" != "skip" ] && echo "${var}=${val}" >> "$VALUES_FILE"
|
|
[ -n "$val" ] && [ "$val" != "skip" ] && print_success "Set $var"
|
|
[ "$val" = "skip" ] && print_info "Skipped $var (optional)"
|
|
done
|
|
echo ""
|
|
|
|
# Create unified .env
|
|
print_info "Creating unified .env file..."
|
|
|
|
while IFS= read -r line_content; do
|
|
if echo "$line_content" | grep -q '^#'; then
|
|
echo "$line_content" >> "$env_file"
|
|
elif echo "$line_content" | grep -q '^[A-Z_]*='; then
|
|
varname=$(echo "$line_content" | cut -d'=' -f1)
|
|
val=$(grep "^${varname}=" "$VALUES_FILE" 2>/dev/null | cut -d'=' -f2-)
|
|
[ -n "$val" ] && echo "${varname}=${val}" >> "$env_file" || echo "${varname}=" >> "$env_file"
|
|
elif [ -n "$line_content" ]; then
|
|
echo "$line_content" >> "$env_file"
|
|
fi
|
|
done < "$env_example"
|
|
|
|
chmod 600 "$env_file"
|
|
print_success "Created: ${env_file}"
|
|
|
|
rm -f "$VALUES_FILE"
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# Copy unified .env to global location and into skills/
|
|
copy_unified_env() {
|
|
local source_env="${REPO_ROOT}/.env"
|
|
local target_env="${GLOBAL_DIR}/.env"
|
|
local skills_env="${SKILLS_DIR}/.env"
|
|
|
|
if [ -f "$source_env" ]; then
|
|
print_info "Copying unified .env to global location..."
|
|
mkdir -p "$GLOBAL_DIR"
|
|
cp "$source_env" "$target_env"
|
|
chmod 600 "$target_env"
|
|
print_success "Created: ${target_env}"
|
|
|
|
print_info "Copying .env into skills/ folder..."
|
|
cp "$source_env" "$skills_env"
|
|
chmod 600 "$skills_env"
|
|
print_success "Created: ${skills_env}"
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# Main
|
|
main() {
|
|
line
|
|
echo "OpenCode Skills Installer"
|
|
line
|
|
echo ""
|
|
|
|
# Check skills directory
|
|
if [ ! -d "$SKILLS_DIR" ]; then
|
|
print_error "Skills directory not found: $SKILLS_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Setup unified .env FIRST
|
|
setup_unified_env
|
|
|
|
# Find skills
|
|
print_info "Finding skills..."
|
|
SKILLS=$(get_skills)
|
|
|
|
if [ -z "$SKILLS" ]; then
|
|
print_error "No skills found"
|
|
exit 1
|
|
fi
|
|
|
|
for s in $SKILLS; do
|
|
print_info "Found: $s"
|
|
done
|
|
echo ""
|
|
|
|
# Choose install location
|
|
if [ $INSTALL_GLOBAL -eq 1 ]; then
|
|
TARGET="$GLOBAL_SKILLS_DIR"
|
|
print_info "Installing to global: $TARGET"
|
|
else
|
|
TARGET="${REPO_ROOT}/.opencode/skills"
|
|
print_info "Installing to project: $TARGET"
|
|
fi
|
|
|
|
mkdir -p "$TARGET"
|
|
echo ""
|
|
|
|
# Install skills
|
|
print_info "Installing to $TARGET..."
|
|
|
|
for skill in $SKILLS; do
|
|
dest="${TARGET}/${skill}"
|
|
|
|
if [ -d "$dest" ]; then
|
|
if [ $FORCE -eq 0 ] && [ $AUTO_YES -eq 0 ]; then
|
|
echo -n "$skill exists. Overwrite? [y/N]: "
|
|
read ow
|
|
if [ "$ow" != "y" ] && [ "$ow" != "Y" ]; then
|
|
print_warning "Skipped $skill"
|
|
continue
|
|
fi
|
|
elif [ $AUTO_YES -eq 1 ]; then
|
|
print_info "Skipping $skill (existing)"
|
|
continue
|
|
fi
|
|
rm -rf "$dest"
|
|
fi
|
|
|
|
cp -r "${SKILLS_DIR}/${skill}" "$dest"
|
|
print_success "$skill"
|
|
|
|
# If skill has install.sh, run it
|
|
if [ -f "${SKILLS_DIR}/${skill}/scripts/install.sh" ]; then
|
|
print_info "Running install script for $skill..."
|
|
chmod +x "${SKILLS_DIR}/${skill}/scripts/install.sh"
|
|
"${SKILLS_DIR}/${skill}/scripts/install.sh" 2>/dev/null || print_warning "Install failed for $skill (continuing...)"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Also sync to ~/.config/opencode/skills/ for backward compatibility
|
|
if [ "$TARGET" != "${GLOBAL_DIR}/skills" ]; then
|
|
print_info "Syncing to ~/.config/opencode/skills/ for compatibility..."
|
|
mkdir -p "${GLOBAL_DIR}/skills"
|
|
for skill in $SKILLS; do
|
|
cp -r "${TARGET}/${skill}" "${GLOBAL_DIR}/skills/" 2>/dev/null || true
|
|
done
|
|
print_success "Synced to ~/.config/opencode/skills/"
|
|
fi
|
|
echo ""
|
|
|
|
# Install deps
|
|
print_info "Installing dependencies..."
|
|
|
|
PIP=""
|
|
if command -v pip3 >/dev/null 2>&1; then
|
|
PIP="pip3"
|
|
elif command -v pip >/dev/null 2>&1; then
|
|
PIP="pip"
|
|
fi
|
|
|
|
if [ -n "$PIP" ]; then
|
|
for skill in $SKILLS; do
|
|
req="${SKILLS_DIR}/${skill}/scripts/requirements.txt"
|
|
if [ -f "$req" ]; then
|
|
$PIP install -r "$req" -q 2>/dev/null && print_success "deps for $skill"
|
|
fi
|
|
done
|
|
fi
|
|
echo ""
|
|
|
|
# Copy unified .env to global location
|
|
copy_unified_env
|
|
|
|
# Create skill-specific .env files that reference unified .env
|
|
# (No longer needed - .env is in skills/ folder)
|
|
|
|
print_success "All skills configured"
|
|
echo ""
|
|
|
|
# Done
|
|
line
|
|
print_success "Installation complete!"
|
|
line
|
|
echo ""
|
|
echo "Installed skills:"
|
|
for skill in $SKILLS; do
|
|
echo " - $skill"
|
|
done
|
|
echo ""
|
|
echo "Location: $TARGET"
|
|
echo ""
|
|
echo "Unified .env: ${GLOBAL_DIR}/.env"
|
|
echo ""
|
|
print_info "IMPORTANT: Edit ${GLOBAL_DIR}/.env to update credentials"
|
|
}
|
|
|
|
main "$@"
|