Interactive installer (interactive-install-claude.sh): - Add fetch_latest_agents() function to check GitHub for updates - Clone latest agents from contains-studio/agents before installing - Update local agents directory with upstream changes - Gracefully fallback to local agents if GitHub fetch fails - Call fetch_latest_agents() before install_agents() in main flow Sync script (sync-agents.sh): - Add REPO_AGENTS_DIR environment variable support - Automatically update repository agents directory when syncing - Keep installer agents in sync with Claude Code agents This ensures users always get the latest agents from upstream GitHub while maintaining customizations in the local repository.
266 lines
7.8 KiB
Bash
Executable File
266 lines
7.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Claude Code Agents Sync Script
|
|
# Syncs local agents with GitHub repository and backs up to Gitea
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
AGENTS_DIR="${HOME}/.claude/agents"
|
|
BACKUP_DIR="${AGENTS_DIR}.backup.$(date +%Y%m%d-%H%M%S)"
|
|
GITHUB_REPO="https://github.com/contains-studio/agents"
|
|
TEMP_DIR="/tmp/claude-agents-sync-$RANDOM"
|
|
UPSTREAM_DIR="$TEMP_DIR/upstream"
|
|
LOG_FILE="${AGENTS_DIR}/update.log"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging function
|
|
log() {
|
|
local level=$1
|
|
shift
|
|
local message="$@"
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Print colored message
|
|
print_msg() {
|
|
local color=$1
|
|
shift
|
|
echo -e "${color}$*${NC}"
|
|
}
|
|
|
|
# Create backup
|
|
create_backup() {
|
|
print_msg "$BLUE" "📦 Creating backup..."
|
|
if cp -r "$AGENTS_DIR" "$BACKUP_DIR"; then
|
|
print_msg "$GREEN" "✓ Backup created: $BACKUP_DIR"
|
|
log "INFO" "Backup created at $BACKUP_DIR"
|
|
else
|
|
print_msg "$RED" "✗ Failed to create backup"
|
|
log "ERROR" "Backup creation failed"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Download upstream agents
|
|
download_upstream() {
|
|
print_msg "$BLUE" "📥 Downloading agents from $GITHUB_REPO..."
|
|
mkdir -p "$TEMP_DIR"
|
|
|
|
if command -v git &> /dev/null; then
|
|
# Use git if available (faster)
|
|
git clone --depth 1 "$GITHUB_REPO" "$UPSTREAM_DIR" 2>/dev/null || {
|
|
print_msg "$RED" "✗ Failed to clone repository"
|
|
log "ERROR" "Git clone failed"
|
|
exit 1
|
|
}
|
|
else
|
|
# Fallback to wget/curl
|
|
print_msg "$YELLOW" "⚠ Git not found, downloading archive..."
|
|
local archive="$TEMP_DIR/agents.tar.gz"
|
|
if command -v wget &> /dev/null; then
|
|
wget -q "$GITHUB_REPO/archive/main.tar.gz" -O "$archive"
|
|
elif command -v curl &> /dev/null; then
|
|
curl -sL "$GITHUB_REPO/archive/main.tar.gz" -o "$archive"
|
|
else
|
|
print_msg "$RED" "✗ Need git, wget, or curl"
|
|
exit 1
|
|
fi
|
|
mkdir -p "$UPSTREAM_DIR"
|
|
tar -xzf "$archive" -C "$UPSTREAM_DIR" --strip-components=1
|
|
fi
|
|
|
|
print_msg "$GREEN" "✓ Downloaded upstream agents"
|
|
log "INFO" "Downloaded agents from $GITHUB_REPO"
|
|
}
|
|
|
|
# Compare and sync agents
|
|
sync_agents() {
|
|
print_msg "$BLUE" "🔄 Syncing agents..."
|
|
|
|
local new_agents=()
|
|
local updated_agents=()
|
|
local custom_agents=()
|
|
|
|
# Find all agent files in upstream
|
|
while IFS= read -r upstream_file; do
|
|
local rel_path="${upstream_file#$UPSTREAM_DIR/}"
|
|
local local_file="$AGENTS_DIR/$rel_path"
|
|
|
|
if [[ ! -f "$local_file" ]]; then
|
|
# New agent
|
|
new_agents+=("$rel_path")
|
|
mkdir -p "$(dirname "$local_file")"
|
|
cp "$upstream_file" "$local_file"
|
|
log "INFO" "Added new agent: $rel_path"
|
|
elif ! diff -q "$upstream_file" "$local_file" &>/dev/null; then
|
|
# Updated agent - check if customized
|
|
if grep -q "CUSTOMIZED" "$local_file" 2>/dev/null || \
|
|
[[ -f "${local_file}.local" ]]; then
|
|
custom_agents+=("$rel_path")
|
|
log "WARN" "Skipped customized agent: $rel_path"
|
|
else
|
|
updated_agents+=("$rel_path")
|
|
cp "$upstream_file" "$local_file"
|
|
log "INFO" "Updated agent: $rel_path"
|
|
fi
|
|
fi
|
|
done < <(find "$UPSTREAM_DIR" -name "*.md" -type f)
|
|
|
|
# Report results
|
|
echo ""
|
|
print_msg "$GREEN" "✨ New agents (${#new_agents[@]}):"
|
|
for agent in "${new_agents[@]}"; do
|
|
echo " + $agent"
|
|
done | head -20
|
|
|
|
echo ""
|
|
print_msg "$YELLOW" "📝 Updated agents (${#updated_agents[@]}):"
|
|
for agent in "${updated_agents[@]}"; do
|
|
echo " ~ $agent"
|
|
done | head -20
|
|
|
|
if [[ ${#custom_agents[@]} -gt 0 ]]; then
|
|
echo ""
|
|
print_msg "$YELLOW" "⚠️ Preserved custom agents (${#custom_agents[@]}):"
|
|
for agent in "${custom_agents[@]}"; do
|
|
echo " • $agent"
|
|
done | head -20
|
|
fi
|
|
|
|
# Summary
|
|
local total_changes=$((${#new_agents[@]} + ${#updated_agents[@]}))
|
|
log "INFO" "Sync complete: ${#new_agents[@]} new, ${#updated_agents[@]} updated, ${#custom_agents[@]} preserved"
|
|
|
|
# If REPO_AGENTS_DIR is set, also update repository agents
|
|
if [[ -n "${REPO_AGENTS_DIR:-}" && -d "$REPO_AGENTS_DIR" ]]; then
|
|
print_msg "$BLUE" "📦 Updating repository agents directory..."
|
|
|
|
for category in engineering marketing product studio-operations project-management testing design bonus; do
|
|
if [ -d "$AGENTS_DIR/$category" ]; then
|
|
mkdir -p "$REPO_AGENTS_DIR/$category"
|
|
cp -f "$AGENTS_DIR/$category"/*.md "$REPO_AGENTS_DIR/$category/" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
if [ -f "$AGENTS_DIR/README.md" ]; then
|
|
cp -f "$AGENTS_DIR/README.md" "$REPO_AGENTS_DIR/README.md" 2>/dev/null || true
|
|
fi
|
|
|
|
print_msg "$GREEN" "✓ Repository agents updated"
|
|
log "INFO" "Repository agents updated at $REPO_AGENTS_DIR"
|
|
fi
|
|
}
|
|
|
|
# Commit to git
|
|
commit_to_git() {
|
|
print_msg "$BLUE" "💾 Committing to git..."
|
|
|
|
cd "$AGENTS_DIR"
|
|
|
|
# Check if there are changes
|
|
if git diff --quiet && git diff --cached --quiet; then
|
|
print_msg "$YELLOW" "⚠️ No changes to commit"
|
|
return
|
|
fi
|
|
|
|
# Add all agents
|
|
git add . -- '*.md'
|
|
|
|
# Commit with descriptive message
|
|
local commit_msg="Update agents from upstream
|
|
|
|
$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
Changes:
|
|
- $(git diff --cached --name-only | wc -l) files updated
|
|
- From: $GITHUB_REPO"
|
|
|
|
git commit -m "$commit_msg" 2>/dev/null || {
|
|
print_msg "$YELLOW" "⚠️ Nothing to commit or git not configured"
|
|
log "WARN" "Git commit skipped"
|
|
return
|
|
}
|
|
|
|
print_msg "$GREEN" "✓ Committed to local git"
|
|
log "INFO" "Committed changes to git"
|
|
}
|
|
|
|
# Push to Gitea
|
|
push_to_gitea() {
|
|
if [[ -z "${GITEA_REPO_URL:-}" ]]; then
|
|
print_msg "$YELLOW" "⚠️ GITEA_REPO_URL not set, skipping push"
|
|
print_msg "$YELLOW" " Set it with: export GITEA_REPO_URL='your-gitea-repo-url'"
|
|
log "WARN" "GITEA_REPO_URL not set, push skipped"
|
|
return
|
|
fi
|
|
|
|
print_msg "$BLUE" "📤 Pushing to Gitea..."
|
|
|
|
cd "$AGENTS_DIR"
|
|
|
|
# Ensure remote exists
|
|
if ! git remote get-url origin &>/dev/null; then
|
|
git remote add origin "$GITEA_REPO_URL"
|
|
fi
|
|
|
|
if git push -u origin main 2>/dev/null || git push -u origin master 2>/dev/null; then
|
|
print_msg "$GREEN" "✓ Pushed to Gitea"
|
|
log "INFO" "Pushed to Gitea: $GITEA_REPO_URL"
|
|
else
|
|
print_msg "$YELLOW" "⚠️ Push failed (check credentials/URL)"
|
|
log "ERROR" "Push to Gitea failed"
|
|
fi
|
|
}
|
|
|
|
# Cleanup
|
|
cleanup() {
|
|
rm -rf "$TEMP_DIR"
|
|
}
|
|
|
|
# Rollback function
|
|
rollback() {
|
|
print_msg "$RED" "🔄 Rolling back to backup..."
|
|
if [[ -d "$BACKUP_DIR" ]]; then
|
|
rm -rf "$AGENTS_DIR"
|
|
mv "$BACKUP_DIR" "$AGENTS_DIR"
|
|
print_msg "$GREEN" "✓ Rolled back successfully"
|
|
log "INFO" "Rolled back to $BACKUP_DIR"
|
|
else
|
|
print_msg "$RED" "✗ No backup found!"
|
|
log "ERROR" "Rollback failed - no backup"
|
|
fi
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
print_msg "$BLUE" "🚀 Claude Code Agents Sync"
|
|
print_msg "$BLUE" "════════════════════════════"
|
|
echo ""
|
|
|
|
trap cleanup EXIT
|
|
trap rollback ERR
|
|
|
|
create_backup
|
|
download_upstream
|
|
sync_agents
|
|
commit_to_git
|
|
push_to_gitea
|
|
|
|
echo ""
|
|
print_msg "$GREEN" "✅ Sync complete!"
|
|
print_msg "$BLUE" "💾 Backup: $BACKUP_DIR"
|
|
print_msg "$BLUE" "📋 Log: $LOG_FILE"
|
|
echo ""
|
|
print_msg "$YELLOW" "To rollback: rm -rf $AGENTS_DIR && mv $BACKUP_DIR $AGENTS_DIR"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|