[CmdletBinding()] param( [string]$Source, [string]$Dest, [string]$SkillsDir, [string]$Agent = $(if ($env:HTSY_SKILL_AGENT) { $env:HTSY_SKILL_AGENT } else { "" }), [string]$Repo = $(if ($env:HTSY_SKILL_REPO_URL) { $env:HTSY_SKILL_REPO_URL } else { "https://git.ikuban.com/server/htsy-skill.git" }) ) $ErrorActionPreference = "Stop" function Resolve-Dest { param( [string]$ExactDest, [string]$ParentSkillsDir, [string]$TargetAgent ) if ($ExactDest) { return $ExactDest } if ($env:HTSY_SKILL_DEST) { return $env:HTSY_SKILL_DEST } if (-not $ParentSkillsDir -and $env:AGENT_SKILLS_DIR) { $ParentSkillsDir = $env:AGENT_SKILLS_DIR } if ($ParentSkillsDir) { return Join-Path $ParentSkillsDir "htsy" } switch ($TargetAgent) { "claude" { $claudeHome = $env:CLAUDE_HOME if (-not $claudeHome) { $claudeHome = Join-Path $HOME ".claude" } return Join-Path (Join-Path $claudeHome "skills") "htsy" } "claude-user" { $claudeHome = $env:CLAUDE_HOME if (-not $claudeHome) { $claudeHome = Join-Path $HOME ".claude" } return Join-Path (Join-Path $claudeHome "skills") "htsy" } "claude-project" { return Join-Path (Join-Path (Join-Path (Get-Location).Path ".claude") "skills") "htsy" } "codex" { $codexHome = $env:CODEX_HOME if (-not $codexHome) { $codexHome = Join-Path $HOME ".codex" } return Join-Path (Join-Path $codexHome "skills") "htsy" } "" { throw "Specify -Agent claude|claude-project|codex, -SkillsDir, or -Dest." } default { throw "Unknown agent: $TargetAgent" } } } function Copy-Skill { param( [Parameter(Mandatory = $true)][string]$From, [Parameter(Mandatory = $true)][string]$To ) if (-not (Test-Path (Join-Path $From "SKILL.md"))) { throw "Invalid source: $From does not contain SKILL.md" } $destParent = Split-Path -Parent $To if (-not (Test-Path $destParent)) { New-Item -ItemType Directory -Force $destParent | Out-Null } $tmpDest = Join-Path $destParent (".htsy-install-{0}" -f ([guid]::NewGuid().ToString("N"))) New-Item -ItemType Directory -Force $tmpDest | Out-Null try { Get-ChildItem -LiteralPath $From -Force | Copy-Item -Destination $tmpDest -Recurse -Force if (Test-Path $To) { Remove-Item -Path $To -Recurse -Force } Move-Item -Path $tmpDest -Destination $To } catch { if (Test-Path $tmpDest) { Remove-Item -Path $tmpDest -Recurse -Force } throw } } $Dest = Resolve-Dest -ExactDest $Dest -ParentSkillsDir $SkillsDir -TargetAgent $Agent $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $repoRoot = (Resolve-Path (Join-Path $scriptDir "..")).Path $tempRoot = $null try { if (-not $Source) { $localSkill = Join-Path $repoRoot "htsy" if (Test-Path (Join-Path $localSkill "SKILL.md")) { $Source = $localSkill } else { if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw "git is required when the local htsy/ directory is unavailable." } $tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("htsy-skill-{0}" -f ([guid]::NewGuid().ToString("N"))) git clone --depth 1 $Repo $tempRoot $Source = Join-Path $tempRoot "htsy" } } Copy-Skill -From $Source -To $Dest Write-Host "Installed htsy skill to: $Dest" Write-Host "Verify: $(Join-Path $Dest 'SKILL.md')" } finally { if ($tempRoot -and (Test-Path $tempRoot)) { Remove-Item -Path $tempRoot -Recurse -Force } }