Last active
May 28, 2026 06:58
-
-
Save junetech/c8b976c04cf7233d1222183da48f2078 to your computer and use it in GitHub Desktop.
AGENTS.md ↔ CLAUDE.md symlink 동기화 - Windows 11
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
| #Requires -Version 7 | |
| # sync-agent-rules.ps1 | |
| # AGENTS.md(정본)와 CLAUDE.md(symlink) 간 상태를 진단하고 정리합니다. | |
| param( | |
| [string]$Path = ".", | |
| [switch]$Force | |
| ) | |
| Set-StrictMode -Version Latest | |
| $ErrorActionPreference = "Stop" | |
| # --------------------------------------------------------------------------- | |
| # Symlink 권한 확인 | |
| # --------------------------------------------------------------------------- | |
| . "$PSScriptRoot\test-symlink.ps1" | |
| if (-not $script:CanSymlink) { | |
| Write-Host "ERROR: Symlink를 생성할 수 없습니다." -ForegroundColor Red | |
| Write-Host " enable-git-symlinks.ps1 또는 gist/<gist-id>를 먼저 실행하세요." -ForegroundColor Yellow | |
| exit 1 | |
| } | |
| # --------------------------------------------------------------------------- | |
| # 헬퍼 | |
| # --------------------------------------------------------------------------- | |
| function Write-Status { | |
| param([string]$Status, [string]$Message, [string]$Color) | |
| Write-Host "[$Status] " -ForegroundColor $Color -NoNewline | |
| Write-Host $Message | |
| } | |
| function Write-OK { param([string]$Message) Write-Status "OK" $Message "Green" } | |
| function Write-Warn { param([string]$Message) Write-Status "WARN" $Message "Yellow" } | |
| function Write-Fail { param([string]$Message) Write-Status "FAIL" $Message "Red" } | |
| function Write-Info { param([string]$Message) Write-Status "INFO" $Message "Cyan" } | |
| # --------------------------------------------------------------------------- | |
| # 글로벌 core.symlinks 확인 | |
| # --------------------------------------------------------------------------- | |
| function Test-GlobalSymlinks { | |
| $globalVal = git config --global --get core.symlinks 2>$null | |
| if ($globalVal -ne "true") { | |
| if ($globalVal) { | |
| Write-Warn "글로벌 core.symlinks = $globalVal (false)" | |
| } else { | |
| Write-Warn "글로벌 core.symlinks가 설정되어 있지 않음" | |
| } | |
| Write-Host " → enable-git-symlinks.ps1 또는 다음 명령어로 설정하세요:" -ForegroundColor Gray | |
| Write-Host " git config --global core.symlinks true" -ForegroundColor Cyan | |
| } else { | |
| Write-OK "글로벌 core.symlinks = true" | |
| } | |
| } | |
| # --------------------------------------------------------------------------- | |
| # 저장소 단위 처리 | |
| # --------------------------------------------------------------------------- | |
| function Invoke-Repair { | |
| param([string]$RepoPath) | |
| $agents = Join-Path $RepoPath "AGENTS.md" | |
| $claude = Join-Path $RepoPath "CLAUDE.md" | |
| $hasAgents = Test-Path $agents | |
| $hasClaude = Test-Path $claude | |
| $repoName = (Get-Item $RepoPath).Name | |
| # ── case 분류 ── | |
| if (-not $hasAgents -and -not $hasClaude) { | |
| $case = "none" | |
| } elseif ($hasAgents -and -not $hasClaude) { | |
| $case = "agents-only" | |
| } elseif (-not $hasAgents -and $hasClaude) { | |
| $case = "claude-only" | |
| } else { | |
| $clItem = Get-Item $claude -Force | |
| if ($clItem.LinkType -eq "SymbolicLink") { | |
| $targetFull = if ($clItem.Target) { | |
| [System.IO.Path]::GetFullPath((Join-Path $RepoPath $clItem.Target)) | |
| } | |
| $agentsFull = [System.IO.Path]::GetFullPath($agents) | |
| if ($targetFull -and $targetFull -eq $agentsFull) { | |
| $case = "already-correct" | |
| } else { | |
| $case = "symlink-elsewhere" | |
| } | |
| } else { | |
| $clContent = Get-Content $claude -Raw | |
| $agContent = Get-Content $agents -Raw | |
| $clTrimmed = $clContent.Trim() | |
| if ($clTrimmed -eq "AGENTS.md") { | |
| $case = "redirect" | |
| } elseif ($clContent -eq $agContent) { | |
| $case = "identical" | |
| } else { | |
| $case = "diverged" | |
| } | |
| } | |
| } | |
| Write-Host " $repoName" -ForegroundColor White | |
| # ── case별 처리 ── | |
| switch ($case) { | |
| "none" { | |
| Write-Info " AGENTS.md, CLAUDE.md 모두 없음 → SKIP" | |
| } | |
| "agents-only" { | |
| if ($Force) { | |
| try { | |
| New-Item -ItemType SymbolicLink -Path $claude -Target $agents | Out-Null | |
| Write-OK " CLAUDE.md symlink 생성 → AGENTS.md" | |
| } catch { | |
| Write-Fail " Symlink 생성 실패" | |
| } | |
| } else { | |
| Write-Warn " CLAUDE.md symlink 생성 가능 (적용: -Force 사용)" | |
| } | |
| } | |
| "claude-only" { | |
| $tracked = git -C $RepoPath ls-files CLAUDE.md 2>$null | |
| if ($Force) { | |
| if ($tracked) { | |
| git -C $RepoPath mv CLAUDE.md AGENTS.md 2>$null | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Fail " git mv 실패 (unstaged changes 또는 merge conflict)" | |
| throw "git mv failed for $RepoPath\CLAUDE.md" | |
| } | |
| Write-OK " git mv CLAUDE.md → AGENTS.md" | |
| } else { | |
| Rename-Item $claude "AGENTS.md" | |
| Write-OK " rename CLAUDE.md → AGENTS.md" | |
| } | |
| try { | |
| New-Item -ItemType SymbolicLink -Path $claude -Target $agents | Out-Null | |
| Write-OK " CLAUDE.md symlink 생성 → AGENTS.md" | |
| } catch { | |
| Write-Fail " Symlink 생성 실패" | |
| } | |
| } else { | |
| $action = if ($tracked) { "git mv" } else { "rename" } | |
| Write-Warn " AGENTS.md로 ${action} 후 symlink 생성 가능 (적용: -Force 사용)" | |
| } | |
| } | |
| "redirect" { | |
| Write-OK " CLAUDE.md (redirect) → symlink (자동 적용)" | |
| Remove-Item $claude -Force | |
| try { | |
| New-Item -ItemType SymbolicLink -Path $claude -Target $agents | Out-Null | |
| } catch { | |
| Write-Fail " Symlink 생성 실패" | |
| } | |
| } | |
| "identical" { | |
| Write-OK " CLAUDE.md (내용 동일) → symlink (자동 적용)" | |
| Remove-Item $claude -Force | |
| try { | |
| New-Item -ItemType SymbolicLink -Path $claude -Target $agents | Out-Null | |
| } catch { | |
| Write-Fail " Symlink 생성 실패" | |
| } | |
| } | |
| "diverged" { | |
| Write-Warn " CLAUDE.md와 AGENTS.md 내용이 다릅니다" | |
| $diff = git -C $RepoPath diff --no-index --stat AGENTS.md CLAUDE.md 2>$null | |
| if ($diff) { Write-Host " $diff" -ForegroundColor DarkGray } | |
| if ($Force) { | |
| $timestamp = Get-Date -Format "yyyy-MM-dd_HHmmss" | |
| $backup = "CLAUDE.md.$timestamp.bak" | |
| Copy-Item $claude (Join-Path $RepoPath $backup) | |
| Write-Info " 백업: $backup" | |
| Remove-Item $claude -Force | |
| try { | |
| New-Item -ItemType SymbolicLink -Path $claude -Target $agents | Out-Null | |
| Write-OK " CLAUDE.md → symlink 교체 완료" | |
| } catch { | |
| Write-Fail " Symlink 생성 실패" | |
| } | |
| } else { | |
| Write-Host " → -Force로 백업 후 symlink 교체 가능" -ForegroundColor Gray | |
| } | |
| } | |
| "already-correct" { | |
| Write-OK " 이미 올바른 symlink → SKIP" | |
| } | |
| "symlink-elsewhere" { | |
| Write-Warn " CLAUDE.md가 다른 곳을 가리키는 symlink입니다" | |
| if ($Force) { | |
| Remove-Item $claude -Force | |
| try { | |
| New-Item -ItemType SymbolicLink -Path $claude -Target $agents | Out-Null | |
| Write-OK " CLAUDE.md → AGENTS.md로 symlink 재설정" | |
| } catch { | |
| Write-Fail " Symlink 생성 실패" | |
| } | |
| } else { | |
| Write-Host " → 수동 확인 후 -Force로 재설정 가능" -ForegroundColor Gray | |
| } | |
| } | |
| } | |
| # ── core.symlinks 로컬 설정 제거 ── | |
| $localVal = git -C $RepoPath config --local --get core.symlinks 2>$null | |
| if ($localVal) { | |
| git -C $RepoPath config --local --unset core.symlinks 2>$null | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Info " core.symlinks 로컬 설정 제거됨 (was: $localVal)" | |
| } | |
| } | |
| } | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Main | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| Write-Host "" | |
| Write-Host "AGENTS.md ↔ CLAUDE.md symlink sync" -ForegroundColor Cyan | |
| Write-Host ("─" * 50) -ForegroundColor Cyan | |
| Write-Host @" | |
| AGENTS CLAUDE │ 기본 동작 │ -Force | |
| ─────── ─────── │ ──────────────────────── │ ──────────────────────── | |
| ✗ ✗ │ SKIP │ - | |
| ✓ ✗ │ 권고만 출력 │ symlink 생성 | |
| ✗ ✓ │ 권고만 출력 │ rename / git mv → symlink | |
| ✓ ✓(ref) │ 자동: symlink 교체 │ - | |
| ✓ ✓(id) │ 자동: symlink 교체 │ - | |
| ✓ ✓(≠) │ WARN (diff 출력) │ 백업 → symlink | |
| ✓ ✓(✓) │ SKIP (이미 정상) │ - | |
| ref = redirect (내용이 "AGENTS.md") | |
| id = AGENTS.md와 내용 완전 동일 | |
| ≠ = AGENTS.md와 내용 다름 | |
| "@ -ForegroundColor Gray | |
| Test-GlobalSymlinks | |
| $resolvedPath = Resolve-Path $Path | |
| Write-Host "`n검색 경로: $resolvedPath" -ForegroundColor Cyan | |
| $repos = Get-ChildItem -Path $resolvedPath -Directory | Where-Object { | |
| Test-Path (Join-Path $_.FullName ".git") | |
| } | |
| if ($repos.Count -eq 0) { | |
| Write-Warn "Git 저장소를 찾을 수 없습니다." | |
| exit 0 | |
| } | |
| Write-Info "$($repos.Count)개 저장소 발견" | |
| Write-Host "" | |
| foreach ($repo in $repos) { | |
| Invoke-Repair -RepoPath $repo.FullName | |
| Write-Host "" | |
| } | |
| Write-Host "완료." -ForegroundColor Green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment