Created
February 12, 2026 01:16
-
-
Save wangyiyang/90d2734165d7386d6dd0a66571f14624 to your computer and use it in GitHub Desktop.
Sync ~/.codex + ~/.claude to remote
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| usage() { | |
| cat <<'EOF' | |
| sync-code-cli.sh | |
| 用途: | |
| 将本机的 Code CLI 配置(Codex/Claude)同步到远端同路径: | |
| ~/.claude/commands/ -> <remote>:~/.claude/commands/ | |
| ~/.claude/CLAUDE.md -> <remote>:~/.claude/CLAUDE.md | |
| ~/.codex/AGENTS.md -> <remote>:~/.codex/AGENTS.md | |
| ~/.codex/prompts/ -> <remote>:~/.codex/prompts/ | |
| 安全默认: | |
| - 默认 dry-run:只显示将要变更的内容,不会写入远端 | |
| - 只有加 --apply 才会真正覆盖远端 | |
| - 目录同步在 --apply 时默认使用 --delete(镜像覆盖);可用 --no-delete 关闭 | |
| 用法: | |
| ./sync-code-cli.sh --remote <user@host> [options] | |
| 必选参数: | |
| --remote <user@host> 例如:--remote kk@ops.wangyiyang.cc | |
| 可选参数: | |
| --apply 真正执行(否则默认 dry-run) | |
| --no-delete 目录同步不使用 --delete(保留远端多余文件) | |
| --cleanup-misplaced 清理误同步到远端 ~ 根目录的:commands/prompts/CLAUDE.md/AGENTS.md | |
| (不会直接删除,会移动到 ~/.sync-cleanup-backup/<timestamp>/) | |
| --backup-remote 在同步前备份远端目标到 ~/.sync-backup/<timestamp>/ | |
| --ssh-opts "<...>" 透传 ssh 参数(示例:--ssh-opts "-p 2222 -J jump") | |
| --rsync-opts "<...>" 透传 rsync 参数(示例:--rsync-opts "--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r") | |
| -h, --help 显示帮助 | |
| 示例: | |
| # 预览将要变更(dry-run) | |
| ./sync-code-cli.sh --remote kk@ops.wangyiyang.cc | |
| # 真正执行并镜像覆盖(目录包含 --delete) | |
| ./sync-code-cli.sh --remote kk@ops.wangyiyang.cc --apply | |
| # 真正执行但不删除远端多余文件 | |
| ./sync-code-cli.sh --remote kk@ops.wangyiyang.cc --apply --no-delete | |
| # 同步前备份 + 清理误同步路径 | |
| ./sync-code-cli.sh --remote kk@ops.wangyiyang.cc --apply --backup-remote --cleanup-misplaced | |
| 注意: | |
| - macOS 自带 rsync 版本较老,脚本避免使用较新的 rsync 参数(例如 --info=...)。 | |
| - 使用 --delete 有误删风险;建议先 dry-run 再 --apply。 | |
| EOF | |
| } | |
| die() { | |
| echo "错误:$*" >&2 | |
| exit 1 | |
| } | |
| need_cmd() { | |
| command -v "$1" >/dev/null 2>&1 || die "缺少命令:$1" | |
| } | |
| REMOTE="" | |
| APPLY=0 | |
| NO_DELETE=0 | |
| CLEANUP_MISPLACED=0 | |
| BACKUP_REMOTE=0 | |
| SSH_OPTS="" | |
| RSYNC_OPTS_EXTRA="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --remote) | |
| [[ $# -ge 2 ]] || die "--remote 需要参数" | |
| REMOTE="$2" | |
| shift 2 | |
| ;; | |
| --apply) | |
| APPLY=1 | |
| shift | |
| ;; | |
| --no-delete) | |
| NO_DELETE=1 | |
| shift | |
| ;; | |
| --cleanup-misplaced) | |
| CLEANUP_MISPLACED=1 | |
| shift | |
| ;; | |
| --backup-remote) | |
| BACKUP_REMOTE=1 | |
| shift | |
| ;; | |
| --ssh-opts) | |
| [[ $# -ge 2 ]] || die "--ssh-opts 需要参数" | |
| SSH_OPTS="$2" | |
| shift 2 | |
| ;; | |
| --rsync-opts) | |
| [[ $# -ge 2 ]] || die "--rsync-opts 需要参数" | |
| RSYNC_OPTS_EXTRA="$2" | |
| shift 2 | |
| ;; | |
| -h|--help) | |
| usage | |
| exit 0 | |
| ;; | |
| *) | |
| die "未知参数:$1(用 -h 查看帮助)" | |
| ;; | |
| esac | |
| done | |
| [[ -n "$REMOTE" ]] || { usage; exit 2; } | |
| need_cmd ssh | |
| need_cmd rsync | |
| LOCAL_CLAUDE_DIR="${HOME}/.claude" | |
| LOCAL_CODEX_DIR="${HOME}/.codex" | |
| [[ -d "${LOCAL_CLAUDE_DIR}/commands" ]] || die "本地不存在目录:${LOCAL_CLAUDE_DIR}/commands" | |
| [[ -f "${LOCAL_CLAUDE_DIR}/CLAUDE.md" ]] || die "本地不存在文件:${LOCAL_CLAUDE_DIR}/CLAUDE.md" | |
| [[ -f "${LOCAL_CODEX_DIR}/AGENTS.md" ]] || die "本地不存在文件:${LOCAL_CODEX_DIR}/AGENTS.md" | |
| [[ -d "${LOCAL_CODEX_DIR}/prompts" ]] || die "本地不存在目录:${LOCAL_CODEX_DIR}/prompts" | |
| # Common rsync flags; keep compatibility with older rsync (e.g., macOS built-in). | |
| RSYNC_COMMON="-az --no-owner --no-group --stats --progress" | |
| RSYNC_RSH="ssh ${SSH_OPTS}" | |
| DRY_RUN_FLAG="--dry-run" | |
| if [[ "$APPLY" -eq 1 ]]; then | |
| DRY_RUN_FLAG="" | |
| fi | |
| DELETE_FLAG="--delete" | |
| if [[ "$NO_DELETE" -eq 1 ]]; then | |
| DELETE_FLAG="" | |
| fi | |
| echo "远端:${REMOTE}" | |
| echo "模式:$([[ "$APPLY" -eq 1 ]] && echo APPLY || echo DRY-RUN)" | |
| echo "目录同步:$([[ "$APPLY" -eq 1 ]] && [[ "$NO_DELETE" -eq 0 ]] && echo \"镜像(--delete)\" || echo \"不删除\")" | |
| echo "ssh opts:${SSH_OPTS:-<none>}" | |
| echo "rsync extra:${RSYNC_OPTS_EXTRA:-<none>}" | |
| echo | |
| remote_exec() { | |
| # Intentionally allow word splitting in SSH_OPTS to pass multiple flags. | |
| # shellcheck disable=SC2086 | |
| ssh ${SSH_OPTS} "$REMOTE" "$@" | |
| } | |
| if [[ "$APPLY" -eq 1 ]]; then | |
| echo "[1/5] 确保远端目录存在:~/.claude/commands ~/.codex/prompts" | |
| remote_exec "mkdir -p ~/.claude/commands ~/.codex/prompts ~/.claude ~/.codex" | |
| if [[ "$CLEANUP_MISPLACED" -eq 1 ]]; then | |
| echo "[2/5] 清理误同步到远端 ~ 根目录的位置(移动到备份目录)" | |
| remote_exec "set -euo pipefail | |
| TS=\$(date +%Y%m%d-%H%M%S) | |
| BACKUP=\$HOME/.sync-cleanup-backup/\$TS | |
| mkdir -p \"\$BACKUP\" | |
| moved=0 | |
| for p in commands prompts CLAUDE.md AGENTS.md; do | |
| src=\$HOME/\$p | |
| if [ -e \"\$src\" ]; then | |
| mv \"\$src\" \"\$BACKUP/\" | |
| moved=1 | |
| fi | |
| done | |
| if [ \"\$moved\" -eq 1 ]; then | |
| echo \"已移动误同步内容到:\$BACKUP\" | |
| else | |
| echo \"未发现远端 ~ 根目录的误同步内容(无需清理)。\" | |
| fi" | |
| fi | |
| if [[ "$BACKUP_REMOTE" -eq 1 ]]; then | |
| echo "[3/5] 备份远端目标到 ~/.sync-backup/<timestamp>/" | |
| remote_exec "set -euo pipefail | |
| TS=\$(date +%Y%m%d-%H%M%S) | |
| BACKUP=\$HOME/.sync-backup/\$TS | |
| mkdir -p \"\$BACKUP\" | |
| if [ -e \"\$HOME/.claude/CLAUDE.md\" ]; then | |
| mkdir -p \"\$BACKUP/.claude\" | |
| cp -a \"\$HOME/.claude/CLAUDE.md\" \"\$BACKUP/.claude/CLAUDE.md\" | |
| fi | |
| if [ -d \"\$HOME/.claude/commands\" ]; then | |
| mkdir -p \"\$BACKUP/.claude\" | |
| cp -a \"\$HOME/.claude/commands\" \"\$BACKUP/.claude/commands\" | |
| fi | |
| if [ -e \"\$HOME/.codex/AGENTS.md\" ]; then | |
| mkdir -p \"\$BACKUP/.codex\" | |
| cp -a \"\$HOME/.codex/AGENTS.md\" \"\$BACKUP/.codex/AGENTS.md\" | |
| fi | |
| if [ -d \"\$HOME/.codex/prompts\" ]; then | |
| mkdir -p \"\$BACKUP/.codex\" | |
| cp -a \"\$HOME/.codex/prompts\" \"\$BACKUP/.codex/prompts\" | |
| fi | |
| echo \"备份完成:\$BACKUP\"" | |
| fi | |
| else | |
| echo "[提示] 当前为 dry-run,不会创建远端目录、不做备份/清理。" | |
| echo "[提示] 若远端 ~/.claude/ 或 ~/.codex/ 不存在,dry-run 可能会失败;可先加 --apply(或手动 mkdir)。" | |
| fi | |
| echo | |
| echo "[4/5] 执行同步($([[ "$APPLY" -eq 1 ]] && echo apply || echo dry-run))" | |
| echo | |
| # Directory sync: optionally include --delete when applying. | |
| # Intentionally expand RSYNC_OPTS_EXTRA as words. | |
| # shellcheck disable=SC2086 | |
| rsync $RSYNC_COMMON -e "$RSYNC_RSH" $DRY_RUN_FLAG $DELETE_FLAG $RSYNC_OPTS_EXTRA \ | |
| "${LOCAL_CLAUDE_DIR}/commands/" "${REMOTE}:~/.claude/commands/" | |
| # Single files: no --delete needed. | |
| # shellcheck disable=SC2086 | |
| rsync $RSYNC_COMMON -e "$RSYNC_RSH" $DRY_RUN_FLAG $RSYNC_OPTS_EXTRA \ | |
| "${LOCAL_CLAUDE_DIR}/CLAUDE.md" "${REMOTE}:~/.claude/CLAUDE.md" | |
| # shellcheck disable=SC2086 | |
| rsync $RSYNC_COMMON -e "$RSYNC_RSH" $DRY_RUN_FLAG $RSYNC_OPTS_EXTRA \ | |
| "${LOCAL_CODEX_DIR}/AGENTS.md" "${REMOTE}:~/.codex/AGENTS.md" | |
| # shellcheck disable=SC2086 | |
| rsync $RSYNC_COMMON -e "$RSYNC_RSH" $DRY_RUN_FLAG $DELETE_FLAG $RSYNC_OPTS_EXTRA \ | |
| "${LOCAL_CODEX_DIR}/prompts/" "${REMOTE}:~/.codex/prompts/" | |
| echo | |
| if [[ "$APPLY" -eq 1 ]]; then | |
| echo "[5/5] 远端校验(ls)" | |
| remote_exec "ls -la ~/.claude/CLAUDE.md ~/.codex/AGENTS.md && ls -la ~/.claude/commands && ls -la ~/.codex/prompts" | |
| else | |
| echo "[完成] dry-run 结束(未对远端做任何写入)。" | |
| fi | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment