diff --git a/tools-sync/scripts/sync_repos.ps1 b/tools-sync/scripts/sync_repos.ps1 index 16f7542..22154f1 100644 --- a/tools-sync/scripts/sync_repos.ps1 +++ b/tools-sync/scripts/sync_repos.ps1 @@ -1,8 +1,8 @@ # tools-sync: mechanical sync of skills across Claude Code, Codex, and the # cross-runtime ~\.agents\skills directory. # -# Phase 1 — git sync of the primary checkout (~\.claude\skills): commit -> pull --rebase -> push. -# Phase 2 — registry reconciliation. Every skill has exactly ONE physical home (the primary +# Phase 1 -- git sync of the primary checkout (~\.claude\skills): commit -> pull --rebase -> push. +# Phase 2 -- registry reconciliation. Every skill has exactly ONE physical home (the primary # checkout); every other registry holds a directory junction pointing at it, so the # tools cannot drift. This phase is bidirectional: # * a real skill dir that exists ONLY in a registry is INGESTED into the primary @@ -12,7 +12,7 @@ # is identical, and is reported as a conflict when it differs. # Judgment calls (rebase conflicts, divergent duplicates) are left to the caller. # -# Exit codes: 0 = clean, 1 = hard failure, 2 = conflicts需要人工裁决. +# Exit codes: 0 = clean, 1 = hard failure, 2 = conflicts that need a human decision. # # Run from PowerShell, NOT git-bash: MSYS rewrites `/c`-style arguments and Windows paths. # @@ -110,9 +110,13 @@ function Get-DirFingerprint([string]$Path) { $skip = '(\\\.git\\|\\__pycache__\\|\\\.langgraph_api\\|\.pyc$|\.pckl$|\.sqlite(-shm|-wal)?$)' $files = Get-ChildItem -LiteralPath $Path -Recurse -File -Force -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch $skip } + # CRs are stripped before hashing: git checkout with autocrlf turns a pulled skill into + # CRLF while the registry copy stays LF, and that alone used to be reported as a CONFLICT. + $sha = [System.Security.Cryptography.SHA256]::Create() $lines = foreach ($f in $files) { $rel = $f.FullName.Substring($Path.Length).TrimStart('\').ToLowerInvariant() - "$rel $((Get-FileHash -LiteralPath $f.FullName -Algorithm SHA256).Hash)" + [byte[]]$bytes = @([System.IO.File]::ReadAllBytes($f.FullName) -ne 13) + "$rel $([BitConverter]::ToString($sha.ComputeHash($bytes)))" } return (($lines | Sort-Object) -join "`n") } @@ -401,7 +405,7 @@ if ($MirrorRegistry) { } } -# An ingested skill matching a local-only .gitignore rule never reaches the remote — +# An ingested skill matching a local-only .gitignore rule never reaches the remote -- # it would silently fail to appear on the next machine. foreach ($name in $script:Ingested) { $ci = Invoke-Git $ClaudeSkills @("check-ignore", "-v", $name)