feat(build): add --prune flag to build.{sh,ps1} [skip-tdd: wrapper]
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].
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Build .skill archives from skills/<name>/ into dist/<name>.skill
|
||||
# Usage: build.ps1 [-Names <name1>,<name2>] (no args = all)
|
||||
# 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
|
||||
@@ -7,7 +11,8 @@
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string[]]$Names = @()
|
||||
[string[]]$Names = @(),
|
||||
[switch]$Prune
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
@@ -69,3 +74,15 @@ foreach ($name in $Names) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build .skill archives from skills/<name>/ into dist/<name>.skill
|
||||
# Usage: build.sh [name...] (no args = all)
|
||||
# Usage: build.sh [--prune] [name...]
|
||||
# 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.sh --prune).
|
||||
# Prune always scans the full dist/, ignores name filter.
|
||||
#
|
||||
# Uses `zip` if available; otherwise delegates to scripts/build.ps1
|
||||
# (so the script works on Linux, macOS, and Windows-with-git-bash without
|
||||
@@ -12,9 +16,18 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
SRC="$ROOT/skills"
|
||||
DIST="$ROOT/dist"
|
||||
|
||||
prune=0
|
||||
positional=()
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--prune) prune=1 ;;
|
||||
*) positional+=("$arg") ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if command -v zip >/dev/null 2>&1; then
|
||||
mkdir -p "$DIST"
|
||||
if [ "$#" -eq 0 ]; then
|
||||
if [ "${#positional[@]}" -eq 0 ]; then
|
||||
# Portable across bash 3.2 (stock macOS) and bash 4+ (Linux, git-bash):
|
||||
# avoid `mapfile` (bash 4+) and `find -printf` (GNU find only).
|
||||
names=()
|
||||
@@ -25,7 +38,7 @@ if command -v zip >/dev/null 2>&1; then
|
||||
IFS=$'\n' names=($(printf '%s\n' "${names[@]}" | sort))
|
||||
unset IFS
|
||||
else
|
||||
names=("$@")
|
||||
names=("${positional[@]}")
|
||||
fi
|
||||
for name in "${names[@]}"; do
|
||||
src_dir="$SRC/$name"
|
||||
@@ -46,12 +59,14 @@ elif command -v powershell.exe >/dev/null 2>&1; then
|
||||
# Windows fallback: delegate to build.ps1 (proper ZIP via .NET API).
|
||||
# PS array binding via -File is fragile (commas don't always split into [string[]]),
|
||||
# so call build.ps1 once per skill and let the bash loop do the work.
|
||||
# --prune is NOT forwarded — bash handles prune at the end of this script
|
||||
# against the same dist/, regardless of which build path ran.
|
||||
ps1="$SCRIPT_DIR/build.ps1"
|
||||
ps1_win="$(cygpath -w "$ps1" 2>/dev/null || echo "$ps1")"
|
||||
if [ "$#" -eq 0 ]; then
|
||||
if [ "${#positional[@]}" -eq 0 ]; then
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$ps1_win"
|
||||
else
|
||||
for name in "$@"; do
|
||||
for name in "${positional[@]}"; do
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$ps1_win" -Names "$name"
|
||||
done
|
||||
fi
|
||||
@@ -59,3 +74,14 @@ else
|
||||
echo "error: need either 'zip' (Linux/macOS) or PowerShell (Windows) to build .skill archives" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$prune" -eq 1 ]; then
|
||||
for f in "$DIST"/*.skill; do
|
||||
[ -f "$f" ] || continue
|
||||
name="$(basename "$f" .skill)"
|
||||
if [ ! -d "$SRC/$name" ]; then
|
||||
echo "pruning: $name (not in skills/) → $f"
|
||||
rm -f "$f"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user