feat(install): add --prune flag to install.{ps1,sh} [skip-tdd: wrapper]

For each <name>/ dir in $target that has no matching skills/<name>/,
remove it. Catches stale installs after skill rename/retire
(motivating case: using-synology-ops just removed but install dir
lingered until manual rm).

Design choices:
- Combined flag (install + prune in one run), not standalone mode
- Prune always scans full target — does NOT respect -Names/positional
  filter, since stale-cleanup is a global concern
- Prints `pruning: <name>` per removal, no confirmation prompt
- Default off — flag must be passed explicitly

[skip-tdd: wrapper] — shell glue wrapping Remove-Item / rm -rf with
a set-difference; verified via smoke-test on disposable target dir
(fake stale dirs created, full install + prune run via env-overridden
CLAUDE_SKILLS_DIR, post-state confirmed: stale dirs removed, valid
skills installed, using-synology-ops absent as expected).

Closes 1/3 of [install-ps1] acceptance (the --prune flag). The
remaining doc .wiki/concepts/install-cross-platform.md is deferred
to a follow-up commit.
This commit is contained in:
2026-05-25 08:13:26 +03:00
parent ce1e04ea30
commit 7ca5a5ad3c
2 changed files with 43 additions and 5 deletions

View File

@@ -1,11 +1,15 @@
# Install skills/<name>/ into ~\.claude\skills\<name>\ (or $env:CLAUDE_SKILLS_DIR)
# Usage: install.ps1 [-Names <name1>,<name2>] (no args = all)
# Usage: install.ps1 [-Names <name1>,<name2>] [-Prune]
# no args = install all skills/* into target
# -Prune = after install, remove target/<name>/ dirs that are NOT in skills/*
# (prune always scans full target, ignores -Names filter — it's a global cleanup)
#
# PowerShell port of install.sh — same behavior, native cmdlets, no bash dependency.
[CmdletBinding()]
param(
[string[]]$Names = @()
[string[]]$Names = @(),
[switch]$Prune
)
$ErrorActionPreference = 'Stop'
@@ -44,3 +48,14 @@ foreach ($name in $Names) {
Copy-Item -Recurse $srcDir $dstDir
Write-Host "installed: $name -> $dstDir"
}
if ($Prune) {
$sourceNames = @(Get-ChildItem -Path $src -Directory | Select-Object -ExpandProperty Name)
$installedDirs = Get-ChildItem -Path $target -Directory -ErrorAction SilentlyContinue
foreach ($dir in $installedDirs) {
if ($sourceNames -notcontains $dir.Name) {
Write-Host "pruning: $($dir.Name) (not in skills/) -> $($dir.FullName)"
Remove-Item -Recurse -Force $dir.FullName
}
}
}