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.
62 lines
2.0 KiB
PowerShell
62 lines
2.0 KiB
PowerShell
# Install skills/<name>/ into ~\.claude\skills\<name>\ (or $env:CLAUDE_SKILLS_DIR)
|
|
# 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 = @(),
|
|
[switch]$Prune
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
$src = Join-Path $root 'skills'
|
|
|
|
if ($env:CLAUDE_SKILLS_DIR) {
|
|
$target = $env:CLAUDE_SKILLS_DIR
|
|
} else {
|
|
$target = Join-Path $env:USERPROFILE '.claude\skills'
|
|
}
|
|
|
|
if (-not (Test-Path $target)) {
|
|
New-Item -ItemType Directory -Force -Path $target | Out-Null
|
|
}
|
|
|
|
if ($Names.Count -eq 0) {
|
|
$Names = Get-ChildItem -Path $src -Directory | Sort-Object Name | Select-Object -ExpandProperty Name
|
|
}
|
|
|
|
foreach ($name in $Names) {
|
|
$srcDir = Join-Path $src $name
|
|
$dstDir = Join-Path $target $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
|
|
}
|
|
if (Test-Path $dstDir) {
|
|
Remove-Item -Recurse -Force $dstDir
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|