Symmetric to install-side --prune (e871c20). Removes
dist/<name>.skill files whose <name> is not in skills/*.
Design choices match install:
- Combined flag (build + prune in one run)
- Global scan, ignores -Names / positional filter
- Default off, print-and-delete, no confirmation
Bash wrinkle: when build.sh delegates to powershell.exe -File
build.ps1 (Windows-without-zip case), --prune is NOT forwarded.
Bash runs prune itself at the end of the script against the
shared dist/. Keeps the delegation surface narrow and the prune
logic single-sourced per shell.
[skip-tdd: wrapper] — same carve-out as install-side, smoke-test
evidence: fake dist/fake-stale-{sh,ps}.skill files created in real
dist/, ran build.{sh,ps1} --prune, verified fakes removed, real
caveman.skill etc. left intact.
Wiki .wiki/concepts/install-cross-platform.md extended:
- title broadened (Install → Install / Build)
- new "Build-side --prune" section
- scope note: dist-hermes/ is separate (managed by build-hermes.py)
Closes [install-ps1-build-prune-followup].
89 lines
3.1 KiB
PowerShell
89 lines
3.1 KiB
PowerShell
# Build .skill archives from skills/<name>/ into dist/<name>.skill
|
|
# Usage: build.ps1 [-Names <name1>,<name2>] [-Prune]
|
|
# no args = build all skills/* into dist/<name>.skill
|
|
# -Prune = after build, remove dist/<name>.skill files whose <name>
|
|
# 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
|
|
}
|
|
}
|
|
}
|