param( [string]$RepoUrl = "https://fun-md.com/Fun_MD/devops-skills.git", [string]$CodexHome = "$HOME\.codex", [switch]$SkipJj, [ValidateSet("auto", "winget", "scoop", "cargo")] [string]$JjInstallMethod = $(if ($env:JJ_INSTALL_METHOD) { $env:JJ_INSTALL_METHOD } else { "auto" }), [ValidateSet("release", "prerelease")] [string]$JjChannel = $(if ($env:JJ_CHANNEL) { $env:JJ_CHANNEL } else { "release" }) ) $ErrorActionPreference = "Stop" $skillName = "gitea-issue-devops-agent" $targetDir = Join-Path $CodexHome "skills\$skillName" $tmpRoot = Join-Path $env:TEMP ("devops-skills-" + [Guid]::NewGuid().ToString("N")) $installJj = $true if ($SkipJj.IsPresent) { $installJj = $false } if ($env:INSTALL_JJ -and $env:INSTALL_JJ -eq "0") { $installJj = $false } function Write-InstallLog { param([string]$Message) Write-Host "[install] $Message" } function Write-InstallWarn { param([string]$Message) Write-Warning "[install] $Message" } function Test-CommandAvailable { param([string]$Name) return $null -ne (Get-Command $Name -ErrorAction SilentlyContinue) } function Show-JjManualHelp { Write-InstallWarn "jj was not installed automatically." Write-InstallWarn "Manual options:" Write-InstallWarn " - winget install jj-vcs.jj" Write-InstallWarn " - scoop install main/jj" if ($JjChannel -eq "prerelease") { Write-InstallWarn " - cargo install --git https://github.com/jj-vcs/jj.git --locked --bin jj jj-cli" } else { Write-InstallWarn " - cargo install --locked --bin jj jj-cli" } Write-InstallWarn "After installation, verify with: jj --version" } function Install-JjWithWinget { if (-not (Test-CommandAvailable winget)) { return $false } if ($JjChannel -ne "release") { Write-InstallWarn "winget path skipped because prerelease jj is requested." return $false } Write-InstallLog "attempting jj installation via winget" & winget install --id jj-vcs.jj -e --accept-source-agreements --accept-package-agreements return $LASTEXITCODE -eq 0 } function Install-JjWithScoop { if (-not (Test-CommandAvailable scoop)) { return $false } if ($JjChannel -ne "release") { Write-InstallWarn "scoop path skipped because prerelease jj is requested." return $false } Write-InstallLog "attempting jj installation via scoop" & scoop install main/jj return $LASTEXITCODE -eq 0 } function Install-JjWithCargo { if (-not (Test-CommandAvailable cargo)) { return $false } Write-InstallLog "attempting jj installation via cargo" if ($JjChannel -eq "prerelease") { & cargo install --git https://github.com/jj-vcs/jj.git --locked --bin jj jj-cli } else { & cargo install --locked --bin jj jj-cli } return $LASTEXITCODE -eq 0 } function Invoke-JjInstall { if (-not $installJj) { Write-InstallLog "skipping jj installation because INSTALL_JJ=0 or -SkipJj was provided" return } if (Test-CommandAvailable jj) { Write-InstallLog "jj already installed: $(& jj --version)" return } $installed = $false switch ($JjInstallMethod) { "auto" { $installed = (Install-JjWithWinget) -or (Install-JjWithScoop) -or (Install-JjWithCargo) break } "winget" { $installed = Install-JjWithWinget break } "scoop" { $installed = Install-JjWithScoop break } "cargo" { $installed = Install-JjWithCargo break } } if (-not $installed) { Show-JjManualHelp return } if (Test-CommandAvailable jj) { Write-InstallLog "jj installation succeeded: $(& jj --version)" } else { Show-JjManualHelp } } try { if (-not (Test-CommandAvailable git)) { throw "[install] git is required but not found." } Write-InstallLog "downloading $skillName from $RepoUrl" git clone --depth 1 $RepoUrl $tmpRoot | Out-Null $sourceDir = Join-Path $tmpRoot "skills\$skillName" if (-not (Test-Path $sourceDir)) { throw "[install] skill directory not found in repository." } New-Item -ItemType Directory -Force (Join-Path $CodexHome "skills") | Out-Null if (Test-Path $targetDir) { Remove-Item -Recurse -Force $targetDir } Copy-Item -Path $sourceDir -Destination $targetDir -Recurse -Force Write-InstallLog "skill installed" Write-InstallLog "installed path: $targetDir" Invoke-JjInstall Write-InstallLog "done" } finally { if (Test-Path $tmpRoot) { Remove-Item -Recurse -Force $tmpRoot } }