#!/usr/bin/env bash # Install shared Levkin machine config from this repo onto the current Mac. # Safe to re-run. Does not overwrite ~/.zshrc.local or secrets. set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" BACKUP_DIR="${BACKUP_DIR:-$HOME/.dotfiles-backup-$(date +%Y%m%d-%H%M%S)}" usage() { cat <<'EOF' Usage: ./install.sh [--ira] [--no-cursor] [--no-zsh] [--dry-run] --ira Print Irina follow-up steps (clone personal-scripts, Gmail app password) --no-cursor Skip ~/.cursor rules/skills/mcp example --no-zsh Skip ~/.zshrc and ~/.p10k.zsh --dry-run Show actions only EOF } DO_ZSH=1 DO_CURSOR=1 IRA=0 DRY=0 while [[ $# -gt 0 ]]; do case "$1" in --ira) IRA=1 ;; --no-cursor) DO_CURSOR=0 ;; --no-zsh) DO_ZSH=0 ;; --dry-run) DRY=1 ;; -h|--help) usage; exit 0 ;; *) echo "Unknown option: $1" >&2; usage; exit 1 ;; esac shift done run() { if [[ "$DRY" -eq 1 ]]; then echo "+ $*" else "$@" fi } backup_if_exists() { local path="$1" if [[ -e "$path" || -L "$path" ]]; then run mkdir -p "$BACKUP_DIR" run mv "$path" "$BACKUP_DIR/$(basename "$path")" echo "Backed up $path → $BACKUP_DIR/" fi } link_file() { local src="$1" dest="$2" if [[ -L "$dest" ]] && [[ "$(readlink "$dest")" == "$src" ]]; then echo "OK (already linked): $dest" return 0 fi backup_if_exists "$dest" run ln -s "$src" "$dest" echo "Linked $dest → $src" } copy_tree_merge() { local src="$1" dest="$2" run mkdir -p "$dest" # Copy files; do not delete extras already on the machine if [[ "$DRY" -eq 1 ]]; then echo "+ rsync -a $src/ $dest/" else rsync -a "$src/" "$dest/" fi echo "Synced $src → $dest" } echo "dotfiles root: $ROOT" if [[ "$DO_ZSH" -eq 1 ]]; then link_file "$ROOT/.zshrc" "$HOME/.zshrc" link_file "$ROOT/.p10k.zsh" "$HOME/.p10k.zsh" if [[ ! -f "$HOME/.zshrc.local" ]]; then run cp "$ROOT/zsh/zshrc.local.example" "$HOME/.zshrc.local" echo "Created ~/.zshrc.local from example (edit as needed)" fi # Global gitleaks pre-commit (optional) if [[ -x "$ROOT/scripts/git-hooks/install.sh" ]]; then if [[ "$DRY" -eq 1 ]]; then echo "+ $ROOT/scripts/git-hooks/install.sh" else "$ROOT/scripts/git-hooks/install.sh" || echo "git-hooks install skipped/failed (non-fatal)" fi fi fi if [[ "$DO_CURSOR" -eq 1 ]]; then run mkdir -p "$HOME/.cursor/rules" "$HOME/.cursor/skills" copy_tree_merge "$ROOT/cursor/rules" "$HOME/.cursor/rules" copy_tree_merge "$ROOT/cursor/skills" "$HOME/.cursor/skills" if [[ ! -f "$HOME/.cursor/mcp.json" && -f "$ROOT/cursor/mcp.json.example" ]]; then run cp "$ROOT/cursor/mcp.json.example" "$HOME/.cursor/mcp.json" echo "Installed ~/.cursor/mcp.json from example (edit if needed)" elif [[ -f "$HOME/.cursor/mcp.json" ]]; then echo "Left existing ~/.cursor/mcp.json alone (see cursor/mcp.json.example)" fi fi mkdir -p "$HOME/Documents/code" 2>/dev/null || true cat <