Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions scripts/install.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -1124,17 +1124,45 @@ set "KIRO_AGENTS_DIR=%USERPROFILE%\.kiro\agents"
set "OPENCODE_COMMANDS_DIR=%USERPROFILE%\.config\opencode\commands"
set "GEMINI_COMMANDS_DIR=%USERPROFILE%\.gemini\commands"
set "SKILLS_TMP=%TEMP%\plannotator-skills-%RANDOM%"
REM git's stderr is captured OUTSIDE SKILLS_TMP (which is removed before the
REM failure message prints) so a failed clone can show the real git error
REM (#1238) instead of only the generic "network or git error" line.
set "GIT_ERR_FILE=%TEMP%\plannotator-git-stderr-%RANDOM%.txt"
mkdir "!SKILLS_TMP!" >nul 2>&1

REM Opt-out: jump past the clone so no network call is made and
REM CHECKOUT_FAILED stays 0 - an opt-out is not a fetch failure and must not
REM trip the guard below. Reported above, next to the git check.
if "!SKIP_SKILLS!"=="1" goto skills_checkout_done

git clone --depth 1 --filter=blob:none --sparse "https://github.com/!REPO!.git" --branch "!TAG!" "!SKILLS_TMP!\repo" >nul 2>&1
if !ERRORLEVEL! equ 0 (
set "CLONE_OK=0"
set "SPARSE_CLONE=1"
git clone --depth 1 --filter=blob:none --sparse "https://github.com/!REPO!.git" --branch "!TAG!" "!SKILLS_TMP!\repo" >nul 2>"!GIT_ERR_FILE!"
if !ERRORLEVEL! equ 0 set "CLONE_OK=1"

REM Capability probe, not a version parse (same philosophy as the GitButler
REM flag probing in packages/shared/gitbutler-core.ts): `git clone --sparse`
REM needs git >= 2.25, and an older git rejects the flag instantly with
REM "error: unknown option `sparse'" before any network call (#1238). Fall
REM back to a plain shallow clone - it costs download size, not correctness:
REM every path the copy steps below read is present in the full checkout, and
REM `git sparse-checkout set` (equally missing on that git) is skipped
REM because there is nothing to narrow.
set "SPARSE_UNSUPPORTED=0"
if "!CLONE_OK!"=="0" (
findstr /i /c:"unknown option" "!GIT_ERR_FILE!" >nul 2>&1 && findstr /i /c:"sparse" "!GIT_ERR_FILE!" >nul 2>&1 && set "SPARSE_UNSUPPORTED=1"
)
if "!SPARSE_UNSUPPORTED!"=="1" (
echo This git does not support "git clone --sparse" ^(needs git ^>= 2.25^) - falling back to a plain shallow clone.
set "SPARSE_CLONE=0"
if exist "!SKILLS_TMP!\repo" rmdir /s /q "!SKILLS_TMP!\repo" >nul 2>&1
git clone --depth 1 "https://github.com/!REPO!.git" --branch "!TAG!" "!SKILLS_TMP!\repo" >nul 2>"!GIT_ERR_FILE!"
if !ERRORLEVEL! equ 0 set "CLONE_OK=1"
)

if "!CLONE_OK!"=="1" (
pushd "!SKILLS_TMP!\repo"
git sparse-checkout set apps/skills apps/kiro-cli apps/opencode-plugin/commands apps/gemini/commands >nul 2>&1
if "!SPARSE_CLONE!"=="1" git sparse-checkout set apps/skills apps/kiro-cli apps/opencode-plugin/commands apps/gemini/commands >nul 2>&1

REM Claude Code reads apps\skills\claude\* (injection `!`plannotator ... $ARGUMENTS``
REM + allowed-tools, so /plannotator-* run with no permission prompt); Codex
Expand Down Expand Up @@ -1218,9 +1246,15 @@ rmdir /s /q "!SKILLS_TMP!" >nul 2>&1

if "!CHECKOUT_FAILED!"=="1" (
echo Error: unable to fetch !REPO! at !TAG! ^(network or git error^). 1>&2
if exist "!GIT_ERR_FILE!" (
echo git reported: 1>&2
type "!GIT_ERR_FILE!" 1>&2
del /q "!GIT_ERR_FILE!" >nul 2>&1
)
echo Something went wrong - run the installer again. 1>&2
exit /b 1
)
del /q "!GIT_ERR_FILE!" >nul 2>&1

REM Claude Code commands are deprecated in favor of skills. Remove a legacy
REM command file only once its replacement skill is actually on disk - running
Expand Down
46 changes: 42 additions & 4 deletions scripts/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1047,16 +1047,46 @@ function Copy-SkillIfPresent {
}
}

# Captured tail of git's stderr from the most recent failed clone attempt,
# surfaced with the "network or git error" message below so the real failure
# self-diagnoses instead of being swallowed (#1238). Read before $skillsTmp
# is removed.
$gitStderrTail = @()
$sparseClone = $true

try {
# Scoped Continue preference: on PowerShell < 7.2 (and profiles that
# restore the old behavior), redirecting a native command's stderr under
# $ErrorActionPreference=Stop turns its FIRST stderr line into a
# terminating error, and git prints its normal "Cloning into ..."
# progress on stderr, so the clone "failed" on the message announcing it
# started (#1162). Real failures stay detectable: the clone is verified
# by Test-Path below, never by a throw.
# by Test-Path below, never by a throw. Stderr goes to a file instead of
# $null (#1238) so failures can be diagnosed.
$gitErrFile = Join-Path $skillsTmp "git-stderr.txt"
if (-not $skipSkillsResolved) {
& { $local:ErrorActionPreference = 'Continue'; git clone --depth 1 --filter=blob:none --sparse "https://github.com/$repo.git" --branch $latestTag "$skillsTmp\repo" 2>$null }
& { $local:ErrorActionPreference = 'Continue'; git clone --depth 1 --filter=blob:none --sparse "https://github.com/$repo.git" --branch $latestTag "$skillsTmp\repo" 2>$gitErrFile }
if (-not (Test-Path "$skillsTmp\repo")) {
$cloneErr = ""
if (Test-Path $gitErrFile) { $cloneErr = [System.IO.File]::ReadAllText($gitErrFile) }
# Capability probe, not a version parse (same philosophy as the
# GitButler flag probing in packages/shared/gitbutler-core.ts):
# `git clone --sparse` needs git >= 2.25, and an older git rejects
# the flag instantly with "error: unknown option `sparse'" before
# any network call (#1238). Fall back to a plain shallow clone -
# it costs download size, not correctness: every path the copy
# steps below read is present in the full checkout, and
# `git sparse-checkout set` (equally missing on that git) is
# skipped because there is nothing to narrow.
if ($cloneErr -match '(?i)unknown option' -and $cloneErr -match '(?i)sparse') {
Write-Host "This git does not support 'git clone --sparse' (needs git >= 2.25) - falling back to a plain shallow clone."
$sparseClone = $false
& { $local:ErrorActionPreference = 'Continue'; git clone --depth 1 "https://github.com/$repo.git" --branch $latestTag "$skillsTmp\repo" 2>$gitErrFile }
}
}
if ((-not (Test-Path "$skillsTmp\repo")) -and (Test-Path $gitErrFile)) {
$gitStderrTail = @(Get-Content $gitErrFile -ErrorAction SilentlyContinue | Select-Object -Last 5)
}
}
# git is a native executable - it does not throw under
# $ErrorActionPreference=Stop on non-zero exit. Guard with
Expand All @@ -1076,8 +1106,12 @@ try {
try {
# Same scoped Continue as the clone above: sparse-checkout may
# write advice to stderr, which must not become a terminating
# error on PowerShell < 7.2 (#1162).
& { $local:ErrorActionPreference = 'Continue'; git sparse-checkout set apps/skills apps/kiro-cli apps/opencode-plugin/commands apps/gemini/commands 2>$null }
# error on PowerShell < 7.2 (#1162). Skipped entirely on the
# plain-clone fallback (#1238): that git has no sparse-checkout
# subcommand, and the full checkout needs no narrowing.
if ($sparseClone) {
& { $local:ErrorActionPreference = 'Continue'; git sparse-checkout set apps/skills apps/kiro-cli apps/opencode-plugin/commands apps/gemini/commands 2>$null }
}

# Claude Code and Codex consume different skill bodies. Claude Code
# reads apps/skills/claude/* (dynamic-context injection
Expand Down Expand Up @@ -1168,6 +1202,10 @@ Remove-Item -Recurse -Force $skillsTmp -ErrorAction SilentlyContinue

if ($checkoutFailed) {
Write-Host "Error: unable to fetch $repo at $latestTag (network or git error)."
if ($gitStderrTail.Count -gt 0) {
Write-Host "git reported:"
foreach ($line in $gitStderrTail) { Write-Host " $line" }
}
Write-Host "Something went wrong - run the installer again."
exit 1
}
Expand Down
45 changes: 41 additions & 4 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1610,7 +1610,7 @@ copy_commands_if_present() {
# command of an AND-OR list except the last, and every shell we tested
# (bash 3.2.57, which is what `curl | bash` gets on macOS, plus bash 5.3,
# dash, zsh, and ksh) carries that suppression into the subshell. Writing
# it as `if ! ( ... ); then` suppresses -e the same way. So the four fetch
# it as `if ! ( ... ); then` suppresses -e the same way. So the fetch
# steps below carry an explicit `|| exit 1`: without them a failed clone
# ran the whole block anyway, the subshell exited 0 on its trailing `if`,
# and the installer printed "YOU'RE ALL SET!" with no skills installed.
Expand All @@ -1629,10 +1629,47 @@ checkout_failed=0
fi

cd "$skills_tmp" || exit 1
git clone --depth 1 --filter=blob:none --sparse \
"https://github.com/${REPO}.git" --branch "$latest_tag" repo 2>/dev/null || exit 1
# Capture git's stderr instead of discarding it (#1238): on failure the
# real error is surfaced below so incompatibilities self-diagnose instead
# of hiding behind the generic "network or git error" message.
git_err="$skills_tmp/git-stderr"
surface_git_error() {
echo "git reported:" >&2
tail -n 5 "$git_err" >&2
}
sparse_clone=1
if ! git clone --depth 1 --filter=blob:none --sparse \
"https://github.com/${REPO}.git" --branch "$latest_tag" repo 2>"$git_err"; then
# Capability probe, not a version parse (same philosophy as the
# GitButler flag probing in packages/shared/gitbutler-core.ts):
# `git clone --sparse` needs git >= 2.25, and an older git (macOS
# with stale Xcode CLT ships 2.23) rejects the flag instantly with
# "error: unknown option `sparse'" before any network call (#1238).
# Fall back to a plain shallow clone — it costs download size, not
# correctness: every path the copy steps below read is present in
# the full checkout, and `git sparse-checkout set` (equally missing
# on that git) is skipped because there is nothing to narrow.
if grep -qi "unknown option" "$git_err" && grep -qi "sparse" "$git_err"; then
echo "This git does not support 'git clone --sparse' (needs git >= 2.25) — falling back to a plain shallow clone."
sparse_clone=0
rm -rf repo
if ! git clone --depth 1 \
"https://github.com/${REPO}.git" --branch "$latest_tag" repo 2>"$git_err"; then
surface_git_error
exit 1
fi
else
surface_git_error
exit 1
fi
fi
cd repo || exit 1
git sparse-checkout set apps/skills apps/kiro-cli apps/opencode-plugin/commands apps/gemini/commands 2>/dev/null || exit 1
if [ "$sparse_clone" -eq 1 ]; then
if ! git sparse-checkout set apps/skills apps/kiro-cli apps/opencode-plugin/commands apps/gemini/commands 2>"$git_err"; then
surface_git_error
exit 1
fi
fi

# Core skills -> Claude Code (also serve as /plannotator-* slash commands)
# and the official OpenAI shared-agent path. SOFT guard: a tag pinned
Expand Down
Loading