Files
claude-code-glm-suite/sync-agents.sh
uroma 641ca1588d Add real-time GitHub sync to installers and sync script
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.
2026-01-16 09:13:08 +00:00

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 "$@"