# mosaic-link-runtime-assets.ps1 # Syncs Mosaic runtime config files into agent runtime directories. # PowerShell equivalent of mosaic-link-runtime-assets (bash). $ErrorActionPreference = "Stop" $MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" } $BackupStamp = Get-Date -Format "yyyyMMddHHmmss" function Copy-FileManaged { param([string]$Src, [string]$Dst) $parent = Split-Path $Dst -Parent if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } # Remove existing symlink/junction $item = Get-Item $Dst -Force -ErrorAction SilentlyContinue if ($item -and $item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) { Remove-Item $Dst -Force } if (Test-Path $Dst) { $srcHash = (Get-FileHash $Src -Algorithm SHA256).Hash $dstHash = (Get-FileHash $Dst -Algorithm SHA256).Hash if ($srcHash -eq $dstHash) { return } Rename-Item $Dst "$Dst.mosaic-bak-$BackupStamp" } Copy-Item $Src $Dst -Force } function Remove-LegacyPath { param([string]$Path) if (-not (Test-Path $Path)) { return } $item = Get-Item $Path -Force -ErrorAction SilentlyContinue if ($item -and $item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) { Remove-Item $Path -Force return } if (Test-Path $Path -PathType Container) { # Remove symlinks/junctions inside, then empty dirs Get-ChildItem $Path -Recurse -Force | Where-Object { $_.Attributes -band [System.IO.FileAttributes]::ReparsePoint } | Remove-Item -Force Get-ChildItem $Path -Recurse -Directory -Force | Sort-Object { $_.FullName.Length } -Descending | Where-Object { (Get-ChildItem $_.FullName -Force | Measure-Object).Count -eq 0 } | Remove-Item -Force } } # Remove legacy compatibility paths $legacyPaths = @( (Join-Path $env:USERPROFILE ".claude\agent-guides"), (Join-Path $env:USERPROFILE ".claude\scripts\git"), (Join-Path $env:USERPROFILE ".claude\scripts\codex"), (Join-Path $env:USERPROFILE ".claude\scripts\bootstrap"), (Join-Path $env:USERPROFILE ".claude\scripts\cicd"), (Join-Path $env:USERPROFILE ".claude\scripts\portainer"), (Join-Path $env:USERPROFILE ".claude\scripts\debug-hook.sh"), (Join-Path $env:USERPROFILE ".claude\scripts\qa-hook-handler.sh"), (Join-Path $env:USERPROFILE ".claude\scripts\qa-hook-stdin.sh"), (Join-Path $env:USERPROFILE ".claude\scripts\qa-hook-wrapper.sh"), (Join-Path $env:USERPROFILE ".claude\scripts\qa-queue-monitor.sh"), (Join-Path $env:USERPROFILE ".claude\scripts\remediation-hook-handler.sh"), (Join-Path $env:USERPROFILE ".claude\templates"), (Join-Path $env:USERPROFILE ".claude\presets\domains"), (Join-Path $env:USERPROFILE ".claude\presets\tech-stacks"), (Join-Path $env:USERPROFILE ".claude\presets\workflows"), (Join-Path $env:USERPROFILE ".claude\presets\jarvis-loop.json") ) foreach ($p in $legacyPaths) { Remove-LegacyPath $p } # Claude-specific runtime files (settings, hooks — CLAUDE.md is now a thin pointer) $runtimeFiles = @("CLAUDE.md", "settings.json", "hooks-config.json", "context7-integration.md") foreach ($rf in $runtimeFiles) { $src = Join-Path $MosaicHome "runtime\claude\$rf" if (-not (Test-Path $src)) { continue } $dst = Join-Path $env:USERPROFILE ".claude\$rf" Copy-FileManaged $src $dst } # OpenCode runtime adapter $opencodeSrc = Join-Path $MosaicHome "runtime\opencode\AGENTS.md" if (Test-Path $opencodeSrc) { $opencodeDst = Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md" Copy-FileManaged $opencodeSrc $opencodeDst } # Codex runtime adapter $codexSrc = Join-Path $MosaicHome "runtime\codex\instructions.md" if (Test-Path $codexSrc) { $codexDir = Join-Path $env:USERPROFILE ".codex" if (-not (Test-Path $codexDir)) { New-Item -ItemType Directory -Path $codexDir -Force | Out-Null } $codexDst = Join-Path $codexDir "instructions.md" Copy-FileManaged $codexSrc $codexDst } $seqScript = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1" if (Test-Path $seqScript) { & $seqScript } Write-Host "[mosaic-link] Runtime assets synced (non-symlink mode)" Write-Host "[mosaic-link] Canonical source: $MosaicHome"