# skills-sync.ps1 — Claude Code 스킬 폴더(~/.claude/skills)를 구글 드라이브 «내 드라이브\Claude-Sync\skills» 와 양방향 동기화. # 두 노트북(본체·서버)에 같은 스크립트를 두고 작업 스케줄러가 하루 한 번 실행한다. # 규칙: 파일마다 «더 최신인 쪽이 이긴다»(robocopy /XO). 삭제는 전파하지 않는다. 부품·결과물 폴더는 제외. $ErrorActionPreference = 'Stop' $Local = Join-Path $env:USERPROFILE '.claude\skills' $LogDir = Join-Path $env:USERPROFILE '.claude\logs' $Log = Join-Path $LogDir 'skills-sync.log' New-Item -ItemType Directory -Force $LogDir | Out-Null function Write-Log([string]$msg) { $line = '{0} {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $msg Add-Content -Path $Log -Value $line -Encoding UTF8 } # 구글 드라이브 데스크톱 앱이 만든 «내 드라이브» 위치 찾기 (드라이브 문자는 PC 마다 다를 수 있다) $MyDrive = Get-PSDrive -PSProvider FileSystem | ForEach-Object { foreach ($n in @('내 드라이브', 'My Drive')) { $p = Join-Path $_.Root $n; if (Test-Path $p) { $p } } } | Select-Object -First 1 if (-not $MyDrive) { Write-Log '중단: 구글 드라이브(내 드라이브)를 찾지 못함 — 드라이브 앱이 켜져 있는지 확인'; exit 1 } $Remote = Join-Path $MyDrive 'Claude-Sync\skills' New-Item -ItemType Directory -Force $Remote | Out-Null # 동시에 두 번 돌지 않게 잠금 $Lock = Join-Path $env:TEMP 'claude-skills-sync.lock' if ((Test-Path $Lock) -and ((Get-Item $Lock).LastWriteTime -gt (Get-Date).AddMinutes(-60))) { Write-Log '건너뜀: 이전 동기화가 아직 실행 중'; exit 0 } Set-Content -Path $Lock -Value $PID # synced = Claude 앱이 claude.ai 에서 받아 두는 스킬 캐시(앱이 관리) — PC 마다 따로 둔다 $Exclude = @('synced', 'node_modules', '.git', '__pycache__', '.venv', 'venv', 'out', 'renders', 'motion16-out', 'dist', 'build', '.cache') # remotion-* 12개 = 공식 저장소 remotion-dev/skills 를 «npx skills» 로 PC 마다 따로 설치·업데이트(본체는 Junction, 서버는 복사본) $Exclude += @('remotion-best-practices', 'remotion-captions', 'remotion-create', 'remotion-docs', 'remotion-interactivity', 'remotion-maps', 'remotion-markup', 'remotion-multimedia', 'remotion-render', 'remotion-saas', 'remotion-studio', 'remotion-upgrade') # archify = tt-a1i/archify 를 «npx skills» 로 PC 마다 따로 설치(본체는 복사본, 서버는 Junction) $Exclude += @('archify') $Common = @('/E', '/XO', '/FFT', '/XJ', '/R:1', '/W:2', '/NP', '/NDL', '/NJH', '/NJS', '/XF', 'desktop.ini', '*.tmp', '~$*', '/XD') + $Exclude function Sync([string]$from, [string]$to, [string]$label) { $out = & robocopy $from $to @Common 2>&1 $code = $LASTEXITCODE # 실제로 복사된 파일 줄만 센다(*EXTRA = 대상에만 있는 항목은 복사가 아니므로 제외) $copied = @($out | Where-Object { $_ -match '^\s*(New File|Newer|Older|Changed|Tweaked)\s' }).Count if ($code -ge 8) { Write-Log ("{0}: 오류 (robocopy {1})`n{2}" -f $label, $code, ($out | Select-Object -Last 15 | Out-String)) } else { Write-Log ('{0}: 파일 {1}개 복사 (robocopy {2})' -f $label, $copied, $code) } return $code } try { $up = Sync $Local $Remote '올리기 (이 PC → 드라이브)' $down = Sync $Remote $Local '받기 (드라이브 → 이 PC)' } finally { Remove-Item $Lock -ErrorAction SilentlyContinue } # 로그는 최근 1000줄만 유지 $lines = Get-Content $Log -Encoding UTF8 if ($lines.Count -gt 1000) { $lines | Select-Object -Last 1000 | Set-Content $Log -Encoding UTF8 } if ($up -ge 8 -or $down -ge 8) { exit 2 }