# mosaic-migrate-local-skills.ps1 # Migrates runtime-local skill directories to Mosaic-managed junctions. # Uses directory junctions (no elevation required) with fallback to copies. # PowerShell equivalent of mosaic-migrate-local-skills (bash). $ErrorActionPreference = "Stop" param( [switch]$Apply, [switch]$Help ) $MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" } $LocalSkillsDir = Join-Path $MosaicHome "skills-local" if ($Help) { Write-Host @" Usage: mosaic-migrate-local-skills.ps1 [-Apply] [-Help] Migrate runtime-local skill directories (e.g. ~/.claude/skills/jarvis) to Mosaic-managed skills by replacing local directories with junctions to ~/.config/mosaic/skills-local. Default mode is dry-run. "@ exit 0 } if (-not (Test-Path $LocalSkillsDir)) { Write-Host "[mosaic-local-skills] Missing local skills dir: $LocalSkillsDir" -ForegroundColor Red exit 1 } $skillRoots = @( (Join-Path $env:USERPROFILE ".claude\skills"), (Join-Path $env:USERPROFILE ".codex\skills"), (Join-Path $env:USERPROFILE ".config\opencode\skills") ) $count = 0 Get-ChildItem $LocalSkillsDir -Directory | ForEach-Object { $name = $_.Name $src = $_.FullName foreach ($root in $skillRoots) { if (-not (Test-Path $root)) { continue } $target = Join-Path $root $name # Already a junction/symlink — check if it points to the right place $existing = Get-Item $target -Force -ErrorAction SilentlyContinue if ($existing -and ($existing.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) { $currentTarget = $existing.Target if ($currentTarget -and ($currentTarget -eq $src -or (Resolve-Path $currentTarget -ErrorAction SilentlyContinue).Path -eq (Resolve-Path $src -ErrorAction SilentlyContinue).Path)) { continue } } # Only migrate local directories containing SKILL.md if ((Test-Path $target -PathType Container) -and (Test-Path (Join-Path $target "SKILL.md")) -and -not ($existing -and ($existing.Attributes -band [System.IO.FileAttributes]::ReparsePoint))) { $count++ if ($Apply) { $stamp = Get-Date -Format "yyyyMMddHHmmss" Rename-Item $target "$target.mosaic-bak-$stamp" try { New-Item -ItemType Junction -Path $target -Target $src -ErrorAction Stop | Out-Null Write-Host "[mosaic-local-skills] migrated: $target -> $src" } catch { Write-Host "[mosaic-local-skills] Junction failed for $name, falling back to copy" Copy-Item $src $target -Recurse -Force Write-Host "[mosaic-local-skills] copied: $target <- $src" } } else { Write-Host "[mosaic-local-skills] would migrate: $target -> $src" } } } } if ($Apply) { Write-Host "[mosaic-local-skills] complete: migrated=$count" } else { Write-Host "[mosaic-local-skills] dry-run: migratable=$count" Write-Host "[mosaic-local-skills] re-run with -Apply to migrate" }