#!/bin/bash # chezmoi-auto-sync.sh — watches ~/.claude/ and ~/.local/share/chezmoi/docs/ for changes # and syncs to chezmoi source + git. Called by the launchd WatchPaths agent. # # IMPORTANT: Do NOT sync ~/.claude/skills/ here — skills managed by .chezmoiexternal.toml # (ffmpeg-usage, swift-front, etc.) are cloned from their own repos. Adding them here # flattens their .git/ dirs into the dotfiles repo and causes conflicts. set -euo pipefail LOCKFILE="/tmp/chezmoi-sync.lock" LOG="/tmp/chezmoi-sync.log" # Prevent concurrent runs if [ -f "$LOCKFILE" ]; then pid=$(cat "$LOCKFILE" 2>/dev/null) if kill -0 "$pid" 2>/dev/null; then exit 0 fi fi echo $$ > "$LOCKFILE" trap 'rm -f "$LOCKFILE"' EXIT log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG" } # Debounce — wait 2 seconds for batch changes to settle sleep 2 log "Change detected in watched paths, syncing..." # Purge .DS_Store files from ~/.claude/ BEFORE chezmoi sees them # (Finder creates these constantly; they're noise) find ~/.claude -name ".DS_Store" -delete 2>/dev/null || true # Pull first to avoid conflicts (rebase local changes on top) cd "$(chezmoi source-path)" git pull --rebase --quiet origin main 2>> "$LOG" || { log "WARNING: git pull --rebase failed, attempting merge" git rebase --abort 2>/dev/null git pull --quiet origin main 2>> "$LOG" || log "ERROR: git pull failed" } # Remove any .DS_Store from source before adding new files find "$(chezmoi source-path)" -name ".DS_Store" -o -name "dot_DS_Store" | xargs rm -f 2>/dev/null # Only sync files we explicitly manage — NOT skills (managed by .chezmoiexternal.toml) chezmoi add ~/.claude/CLAUDE.md 2>> "$LOG" || true chezmoi add ~/.claude/agents/ 2>> "$LOG" || true chezmoi add ~/.claude/settings.json 2>> "$LOG" || true chezmoi add ~/.claude/commands/ 2>> "$LOG" || true chezmoi add ~/.claude/hooks/ 2>> "$LOG" || true chezmoi add ~/.claude/statusline-command.sh 2>> "$LOG" || true chezmoi add ~/.claude/scripts/task-durations/extract.py 2>> "$LOG" || true chezmoi add ~/.claude/scripts/task-durations/estimate.sh 2>> "$LOG" || true chezmoi add ~/.claude/scripts/task-durations/pull-fleet.sh 2>> "$LOG" || true chezmoi add ~/.local/bin/chezmoi-auto-sync.sh 2>> "$LOG" || true # Clean up any .DS_Store that chezmoi added cd "$(chezmoi source-path)" git rm --cached -r --quiet $(git ls-files --cached | grep -i "DS_Store") 2>/dev/null || true # Catch any uncommitted changes in the source dir that chezmoi's autoCommit missed # (e.g., direct edits to ~/.local/share/chezmoi/docs/, which is the same git repo # but not under chezmoi's "managed" tree). When chezmoi add was a no-op, autoCommit # doesn't fire, so those edits sit uncommitted without this fallback. if ! git diff --quiet || ! git diff --cached --quiet || [ -n "$(git ls-files --others --exclude-standard)" ]; then git add -A 2>> "$LOG" git commit -m "Auto-sync from $(hostname -s)" 2>> "$LOG" || true git push 2>> "$LOG" || true log "Pushed uncommitted source changes (e.g. docs/)" fi log "Sync complete"