# Build .skill archives from skills// into dist/.skill # Usage: build.ps1 [-Names ,] [-Prune] # no args = build all skills/* into dist/.skill # -Prune = after build, remove dist/.skill files whose # is NOT in skills/* (analogue of install.ps1 -Prune). # Prune always scans the full dist/, ignores -Names filter. # # Uses .NET System.IO.Compression.ZipArchive directly to produce # spec-compliant ZIPs with forward-slash entry names (Windows PowerShell 5.1's # Compress-Archive writes backslashes, which break extraction on Linux/Mac). [CmdletBinding()] param( [string[]]$Names = @(), [switch]$Prune ) $ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $src = Join-Path $root 'skills' $dist = Join-Path $root 'dist' if (-not (Test-Path $dist)) { New-Item -Type Directory -Path $dist | Out-Null } if ($Names.Count -eq 0) { $Names = Get-ChildItem -Path $src -Directory | Sort-Object Name | Select-Object -ExpandProperty Name } Add-Type -AssemblyName System.IO.Compression Add-Type -AssemblyName System.IO.Compression.FileSystem function New-SkillArchive { param( [string]$SkillName, [string]$SourceDir, [string]$OutPath ) if (Test-Path $OutPath) { Remove-Item $OutPath -Force } $sourceFull = (Resolve-Path $SourceDir).ProviderPath.TrimEnd('\','/') $archive = [System.IO.Compression.ZipFile]::Open( $OutPath, [System.IO.Compression.ZipArchiveMode]::Create ) try { $files = Get-ChildItem -Path $sourceFull -Recurse -File foreach ($file in $files) { $rel = $file.FullName.Substring($sourceFull.Length + 1) -replace '\\','/' $entryName = "$SkillName/$rel" [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile( $archive, $file.FullName, $entryName, [System.IO.Compression.CompressionLevel]::Optimal ) | Out-Null } } finally { $archive.Dispose() } } foreach ($name in $Names) { $srcDir = Join-Path $src $name if (-not (Test-Path $srcDir -PathType Container)) { Write-Warning "skip: $name (not found in skills/)" continue } if (-not (Test-Path (Join-Path $srcDir 'SKILL.md'))) { Write-Warning "skip: $name (missing SKILL.md)" continue } $out = Join-Path $dist "$name.skill" New-SkillArchive -SkillName $name -SourceDir $srcDir -OutPath $out Write-Host "built: dist/$name.skill" } if ($Prune) { $sourceNames = @(Get-ChildItem -Path $src -Directory | Select-Object -ExpandProperty Name) $skillArchives = Get-ChildItem -Path $dist -Filter "*.skill" -File -ErrorAction SilentlyContinue foreach ($archive in $skillArchives) { $archiveName = [System.IO.Path]::GetFileNameWithoutExtension($archive.Name) if ($sourceNames -notcontains $archiveName) { Write-Host "pruning: $archiveName (not in skills/) -> $($archive.FullName)" Remove-Item -Force $archive.FullName } } }