#!/usr/bin/env bash set -euo pipefail ############################################################################### # SYNC — Commit and push all ~/content/ repos # # Maintainer: Kris Yotam # License: MIT # Created: 2026-02-15 # Description: Walks each git repo in ~/content/, stages changes, commits, # and pushes. If a commit or push fails, invokes headless Claude # to diagnose and fix the issue. Designed to be called by git.js # before committing the main site repo. # # Usage: # sync # sync all content repos # sync --quiet # suppress per-repo output (for git.js integration) ############################################################################### CONTENT_DIR="$HOME/.corpus/content" CONTENT_TYPES=(blog diary essays fiction news notes ocs papers progymnasmata reviews verse) QUIET=false [[ "${1:-}" == "--quiet" ]] && QUIET=true log() { $QUIET || echo "$@"; } ############################################################################### # sync a single repo ############################################################################### sync_repo() { local type="$1" local repo="$CONTENT_DIR/$type" # Skip if not a git repo [[ -d "$repo/.git" ]] || return 0 cd "$repo" # Check for changes if git diff --quiet HEAD -- 2>/dev/null && [[ -z "$(git ls-files --others --exclude-standard)" ]]; then log " $type: clean" return 0 fi log " $type: changes detected, committing..." # Stage all changes if ! git add -A 2>/dev/null; then log " $type: ERROR staging files" attempt_fix "$type" "git add -A failed in $repo" return $? fi # Commit with date-stamped message local date date="$(date +%Y-%m-%d)" local msg="content update $date" if ! git commit -m "$msg" 2>/dev/null; then log " $type: ERROR committing" attempt_fix "$type" "git commit failed in $repo" return $? fi # Push if ! git push origin HEAD 2>/dev/null; then log " $type: ERROR pushing, retrying..." # Try pull --rebase then push if git pull --rebase origin main 2>/dev/null && git push origin HEAD 2>/dev/null; then log " $type: pushed after rebase" else log " $type: push failed, invoking Claude..." attempt_fix "$type" "git push failed in $repo after rebase attempt" return $? fi fi log " $type: synced" return 0 } ############################################################################### # claude error recovery ############################################################################### attempt_fix() { local type="$1" local error_desc="$2" local repo="$CONTENT_DIR/$type" if ! command -v claude >/dev/null 2>&1; then echo " $type: FAILED — claude CLI not available for auto-fix" return 1 fi local instruction="A git error occurred in the content repo at $repo. Error: $error_desc Current state: $(cd "$repo" && git status 2>&1) Recent log: $(cd "$repo" && git log --oneline -5 2>&1) Fix this git issue so the repo can be committed and pushed to origin. The repo contains .mdx content files — do not delete any content. Common fixes: - If merge conflict: resolve by keeping both changes - If push rejected: pull --rebase then push - If dirty index: stage and commit - If detached HEAD: checkout main and merge Run the necessary git commands in $repo to fix the issue, then commit and push." log " $type: invoking Claude for auto-fix..." if claude -p "$instruction" --allowedTools "Bash(git *)" 2>/dev/null; then log " $type: Claude fix succeeded" return 0 else echo " $type: FAILED — Claude could not fix the issue" return 1 fi } ############################################################################### # main ############################################################################### log "Syncing content repos..." failed=0 synced=0 clean=0 for type in "${CONTENT_TYPES[@]}"; do repo="$CONTENT_DIR/$type" [[ -d "$repo/.git" ]] || continue if sync_repo "$type"; then # Check if it was clean or synced cd "$repo" if git diff --quiet HEAD -- 2>/dev/null && [[ -z "$(git ls-files --others --exclude-standard)" ]]; then clean=$((clean + 1)) else synced=$((synced + 1)) fi else failed=$((failed + 1)) fi done log "" if [[ $failed -gt 0 ]]; then echo "Sync complete: $failed repo(s) failed" exit 1 else log "Sync complete: all repos clean/synced" exit 0 fi