Skip to content

Instantly share code, notes, and snippets.

@junetech
Last active May 28, 2026 06:58
Show Gist options
  • Select an option

  • Save junetech/c8b976c04cf7233d1222183da48f2078 to your computer and use it in GitHub Desktop.

Select an option

Save junetech/c8b976c04cf7233d1222183da48f2078 to your computer and use it in GitHub Desktop.
AGENTS.md ↔ CLAUDE.md symlink 동기화 - Windows 11
#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