#!/bin/bash ################################################################################ # sync-agents.sh - Agent Synchronization Script ################################################################################ # Synchronizes agents from GitHub/Gitea repositories to local Claude Code # installation. # # Usage: # ./sync-agents.sh [options] # # Options: # --source URL Source repository URL # --branch NAME Branch to sync from (default: main) # --force Force overwrite existing agents # --dry-run Show what would be done without doing it # --verbose Enable verbose output # # Configuration: # Sources can be configured in ~/.claude/agents/sync-sources.json ################################################################################ set -e # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # Directories CLAUDE_DIR="${HOME}/.claude" AGENTS_DIR="${CLAUDE_DIR}/agents" CONFIG_FILE="${AGENTS_DIR}/sync-sources.json" # Flags FORCE=false DRY_RUN=false VERBOSE=false # Default sources DEFAULT_SOURCES=( "https://github.com/anthropics/anthropic-agents" "https://github.com/contains-cafe/studio-agents" ) ################################################################################ # Helper Functions ################################################################################ log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_step() { echo -e "${CYAN}${BOLD}==>${NC} ${BOLD}$1${NC}" } ################################################################################ # Agent Synchronization ################################################################################ sync_from_source() { local source_url="$1" local branch="${2:-main}" local temp_dir log_step "Syncing from $source_url" # Create temp directory for clone temp_dir=$(mktemp -d) if [ "$DRY_RUN" = true ]; then log_info "[DRY RUN] Would clone $source_url (branch: $branch)" rm -rf "$temp_dir" return 0 fi # Clone repository if ! git clone --depth 1 --branch "$branch" "$source_url" "$temp_dir" 2>/dev/null; then log_warn "Failed to clone $source_url" rm -rf "$temp_dir" return 1 fi # Copy agents local agent_count=0 local copied_count=0 local skipped_count=0 # Find agent definitions (look for common patterns) while IFS= read -r -d '' agent_file; do ((agent_count++)) local agent_name agent_name=$(basename "$(dirname "$agent_file")") local dest_dir="${AGENTS_DIR}/${agent_name}" # Check if already exists if [ -d "$dest_dir" ] && [ "$FORCE" = false ]; then ((skipped_count++)) [ "$VERBOSE" = true ] && log_info "Skipped: $agent_name (exists)" continue fi # Create destination and copy mkdir -p "$dest_dir" cp -r "$(dirname "$agent_file")"/* "$dest_dir/" 2>/dev/null || true ((copied_count++)) [ "$VERBOSE" = true ] && log_info "Copied: $agent_name" done < <(find "$temp_dir" -type f \( -name "agent.md" -o -name "skill.md" -o -name "AGENT.md" -o -name "SKILL.md" \) -print0 2>/dev/null) # Cleanup rm -rf "$temp_dir" log_success "Synced from $source_url: $copied_count copied, $skipped_count skipped (found $agent_count total)" } sync_from_config() { local config_file="$1" if [ ! -f "$config_file" ]; then log_warn "Config file not found: $config_file" return 1 fi # Parse JSON config (requires jq) if command -v jq &> /dev/null; then local sources sources=$(jq -r '.sources[].url' "$config_file" 2>/dev/null) if [ -z "$sources" ]; then log_warn "No sources found in config" return 1 fi while IFS= read -r source; do local branch branch=$(jq -r ".sources[] | select(.url==\"$source\") | .branch // \"main\"" "$config_file") sync_from_source "$source" "$branch" done <<< "$sources" else log_warn "jq not found. Cannot parse config file." log_info "Install jq: sudo apt-get install jq" return 1 fi } sync_default_sources() { log_step "Syncing from default sources" for source in "${DEFAULT_SOURCES[@]}"; do sync_from_source "$source" "main" done } ################################################################################ # Main ################################################################################ print_usage() { cat << EOF Usage: $0 [options] Options: --source URL Source repository URL --branch NAME Branch to sync from (default: main) --force Force overwrite existing agents --dry-run Show what would be done without doing it --verbose Enable verbose output -h, --help Show this help message Examples: $0 # Sync from default sources $0 --source https://github.com/user/repo $0 --force --verbose # Force overwrite, verbose output $0 --dry-run # Preview changes Configuration: Sources can be configured in ~/.claude/agents/sync-sources.json Format: { "sources": [ { "url": "https://github.com/user/repo", "branch": "main" } ] } EOF } main() { local custom_source="" local custom_branch="main" # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --source) custom_source="$2" shift 2 ;; --branch) custom_branch="$2" shift 2 ;; --force) FORCE=true shift ;; --dry-run) DRY_RUN=true shift ;; --verbose) VERBOSE=true shift ;; -h|--help) print_usage exit 0 ;; *) log_error "Unknown option: $1" print_usage exit 1 ;; esac done # Ensure agents directory exists mkdir -p "$AGENTS_DIR" # Sync if [ -n "$custom_source" ]; then sync_from_source "$custom_source" "$custom_branch" elif [ -f "$CONFIG_FILE" ]; then sync_from_config "$CONFIG_FILE" else sync_default_sources fi # Summary local total_agents total_agents=$(find "$AGENTS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l) log_success "Agent sync complete!" log_info "Total agents in $AGENTS_DIR: $total_agents" } # Run main main "$@"