diff --git a/CHANGELOG.md b/CHANGELOG.md index c8edb68..c1dadcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ### Fixed +- Run native Windows skill updates from the caller's project directory instead of the `updates` installation directory, for both direct and `npx` execution. - Make Unix command and capture waits interruptible, preserve interactive stdin and exit statuses, and clean owned children, output helpers, and temporary resources on SIGINT/SIGTERM. - Disable prompts for background pip installations and for discovery/planning under `--non-interactive`. - Honor `--non-interactive` for Homebrew upgrade-table confirmation with command-scoped `HOMEBREW_NO_ASK=1`; explain the confirmation prompt during interactive runs. diff --git a/PLAN.md b/PLAN.md index cecc284..30024a3 100644 --- a/PLAN.md +++ b/PLAN.md @@ -33,12 +33,15 @@ ## Execution checklist -- [ ] Register a default-on `skills` module between `pi` and `mise` on Bash and native Windows. -- [ ] Resolve the backing command as the `skills` CLI first, then `npx --yes skills`; skip gracefully when neither exists and fail under `--only`. -- [ ] Run `skills update --project --global`, appending `--yes` under `--non-interactive`; dry-run prints the resolved command without executing. -- [ ] Cover direct, npx-fallback, non-interactive, dry-run, and missing-dependency behavior in Bash tests; mirror success, fallback, and missing-dependency coverage in native Windows tests. -- [ ] Update README, SPEC (module matrix, execution order, §8.15), CHANGELOG, and this plan. -- [ ] Pass lint and tests before opening the PR. +- [x] Register a default-on `skills` module between `pi` and `mise` on Bash and native Windows. +- [x] Resolve the backing command as the `skills` CLI first, then `npx --yes skills`; skip gracefully when neither exists and fail under `--only`. +- [x] Run `skills update --project --global`, appending `--yes` under `--non-interactive`; dry-run prints the resolved command without executing. +- [x] Cover direct, npx-fallback, non-interactive, dry-run, and missing-dependency behavior in Bash tests; mirror success, fallback, and missing-dependency coverage in native Windows tests. +- [x] Preserve the caller's project directory on native Windows and cover project paths containing spaces on both platforms. +- [x] Update README, SPEC (module matrix, execution order, §8.15), CHANGELOG, and this plan. +- [x] Pass lint and full local tests; record native Windows validation availability. + +Validation: `./scripts/lint.sh`, `./scripts/test.sh`, and the focused skills tests under `/bin/bash` passed on macOS. An isolated PowerShell handler check reproduced the wrong working directory before the fix and passed for direct/npx adapters afterward. Native Windows execution remains unverified on this host; all skill update commands were stubbed. --- diff --git a/README.md b/README.md index 4bc0465..cb48ef2 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ updates --dry-run updates --only brew,node --brew-mode formula updates --only linux -n updates --only winget,node,bun +updates --only skills -n updates --full updates --skip python --log-file ./updates.log updates --json -n --no-self-update --log-level warn @@ -101,7 +102,7 @@ Modules are auto-detected: if the underlying command isn’t installed, the modu - `rustup`: update Rust toolchains via `rustup update` - `claude`: update Claude Code CLI via `claude update` - `pi`: update the pi AI CLI and installed extensions via `pi update --all` -- `skills`: update agent skills in both project and global scopes via `skills update` (falls back to `npx --yes skills update`); passing both scopes matches the interactive "Both" option without prompting +- `skills`: update agent skills in both project and global scopes via `skills update --project --global` (falls back to `npx --yes skills update --project --global`); project scope uses the directory where you run `updates`, not all projects on the machine - `mise`: update mise and upgrade installed tools (`mise self-update`, `mise upgrade`) - `go`: update Go binaries from `GO_BINARIES` in `~/.updatesrc` (entries default to `@latest`) - `macos`: list available macOS software updates via `softwareupdate -l` (disabled by default; enable with `--macos-updates` or `--full`) diff --git a/SPEC.md b/SPEC.md index d2fb704..4a37e8c 100644 --- a/SPEC.md +++ b/SPEC.md @@ -573,12 +573,13 @@ Purpose: update the `pi` AI coding CLI and its installed extensions (pinned sour ### 8.15 `skills` -Purpose: update agent skills installed on the machine in both project and global scopes. +Purpose: update agent skills in the caller's current project directory and global scope. This does not scan other projects on the machine. - Requires: the `skills` CLI (`npx skills`), or `npx` as a fallback adapter. - Resolution order: a direct `skills` command on `PATH` first, then `npx --yes skills`. - - If neither is available, default runs warn and skip; explicit `--only skills` fails. + - If neither is available, default runs log and skip; explicit `--only skills` fails. - Scope: runs `skills update --project --global`, which the skills CLI resolves to its interactive "Both" scope without prompting. +- Working directory: preserve the caller's directory on Bash and native Windows, including when using the `npx` fallback; never substitute the `updates` installation directory. - Non-dry-run: - Bash: ` update --project --global [--yes]` - Native Windows: `skills update --project --global` or `npx --yes skills update --project --global` diff --git a/tests/test_cli.sh b/tests/test_cli.sh index 9210cb2..217d936 100755 --- a/tests/test_cli.sh +++ b/tests/test_cli.sh @@ -1779,6 +1779,15 @@ write_stub npx 'echo "npx $*" >>"$CALL_LOG"' out="$("$SCRIPT" --only skills --no-emoji --no-color)" echo "$out" | grep -q '^==> skills END (OK)' grep -q '^npx --yes skills update --project --global$' "$CALL_LOG" +skills_project="${tmp_dir}/skills project" +mkdir -p "$skills_project" +# shellcheck disable=SC2016 +write_stub npx 'echo "npx $*" >>"$CALL_LOG"; pwd -P >>"$CALL_LOG"' +: >"$CALL_LOG" +out="$(cd "$skills_project" && "$SCRIPT" -n --only skills --no-emoji --no-color)" +echo "$out" | grep -q '^==> skills END (OK)' +grep -q '^npx --yes skills update --project --global --yes$' "$CALL_LOG" +grep -Fxq "$(cd "$skills_project" && pwd -P)" "$CALL_LOG" : >"$CALL_LOG" out="$("$SCRIPT" --dry-run --only skills --no-emoji --no-color)" echo "$out" | grep -q '^DRY RUN: npx --yes skills update --project --global$' diff --git a/tests/test_windows_native.ps1 b/tests/test_windows_native.ps1 index 6c0245c..2c65728 100644 --- a/tests/test_windows_native.ps1 +++ b/tests/test_windows_native.ps1 @@ -2193,12 +2193,19 @@ if (Should-RunTest 'native payload runs Skills update module') { Install-RepoWindowsRuntime -RepoRoot $repoRoot -InstallRoot $installRoot -Version $currentReleaseVersion $stubDir = Join-Path $installRoot 'stubs' $log = Join-Path $installRoot 'skills-module.log' + $projectDir = Join-Path $installRoot 'project with spaces' + $cwdLog = Join-Path $installRoot 'skills-cwd.log' $null = New-Item -ItemType Directory -Path $stubDir -Force - Write-CmdStub -Path (Join-Path $stubDir 'skills.cmd') -Lines @(('echo skills:%*>>"{0}"' -f $log)) - $result = Invoke-Bootstrap -InstallRoot $installRoot -ArgumentList @('--no-self-update', '--only', 'skills', '--no-emoji', '--no-color') -Environment @{ PATH = $stubDir } + $null = New-Item -ItemType Directory -Path $projectDir -Force + Write-CmdStub -Path (Join-Path $stubDir 'skills.cmd') -Lines @( + ('echo skills:%*>>"{0}"' -f $log), + ('cd >"{0}"' -f $cwdLog) + ) + $result = Invoke-ProcessCapture -FilePath (Get-PwshPath) -ArgumentList @('-NoLogo', '-NoProfile', '-File', (Join-Path $installRoot 'updates.ps1'), '--no-self-update', '--only', 'skills', '--no-emoji', '--no-color') -WorkingDirectory $projectDir -Environment @{ PATH = $stubDir } Assert-Equal -Expected 0 -Actual $result.ExitCode -Message "Skills module should succeed`n$($result.Output)" $calls = Get-Content -LiteralPath $log -Raw Assert-Match -Text $calls -Pattern '(?m)^skills:update --project --global\s*$' -Message 'Skills module should update project and global scopes without prompting' + Assert-Equal -Expected $projectDir -Actual (Get-Content -LiteralPath $cwdLog -Raw).Trim() -Message 'Skills should run from the caller project, not the updates installation' $emptyPath = Join-Path $installRoot 'empty-skills-path' $null = New-Item -ItemType Directory -Path $emptyPath -Force @@ -2207,12 +2214,16 @@ if (Should-RunTest 'native payload runs Skills update module') { Assert-Match -Text $missing.Output -Pattern '(?i)skills not found' -Message 'missing Skills dependency should be explicit' $npxLog = Join-Path $installRoot 'skills-npx.log' - Write-CmdStub -Path (Join-Path $stubDir 'npx.cmd') -Lines @(('echo npx:%*>>"{0}"' -f $npxLog)) + Write-CmdStub -Path (Join-Path $stubDir 'npx.cmd') -Lines @( + ('echo npx:%*>>"{0}"' -f $npxLog), + ('cd >"{0}"' -f $cwdLog) + ) Remove-Item -LiteralPath (Join-Path $stubDir 'skills.cmd') -Force - $fallback = Invoke-Bootstrap -InstallRoot $installRoot -ArgumentList @('--no-self-update', '--only', 'skills', '--non-interactive', '--no-emoji', '--no-color') -Environment @{ PATH = $stubDir } + $fallback = Invoke-ProcessCapture -FilePath (Get-PwshPath) -ArgumentList @('-NoLogo', '-NoProfile', '-File', (Join-Path $installRoot 'updates.ps1'), '--no-self-update', '--only', 'skills', '--non-interactive', '--no-emoji', '--no-color') -WorkingDirectory $projectDir -Environment @{ PATH = $stubDir } Assert-Equal -Expected 0 -Actual $fallback.ExitCode -Message "Skills npx fallback should succeed`n$($fallback.Output)" $npxCalls = Get-Content -LiteralPath $npxLog -Raw Assert-Match -Text $npxCalls -Pattern '(?m)^npx:--yes skills update --project --global --yes\s*$' -Message 'npx fallback should update both scopes non-interactively' + Assert-Equal -Expected $projectDir -Actual (Get-Content -LiteralPath $cwdLog -Raw).Trim() -Message 'npx skills should retain the caller project directory' } } } diff --git a/updates-main.ps1 b/updates-main.ps1 index fab44b5..169d8d4 100644 --- a/updates-main.ps1 +++ b/updates-main.ps1 @@ -1398,7 +1398,7 @@ function Invoke-ModuleSkills { $arguments.Add('--yes') } - $result = Invoke-LoggedProcess -FilePath $filePath -ArgumentList $arguments.ToArray() + $result = Invoke-LoggedProcess -FilePath $filePath -ArgumentList $arguments.ToArray() -WorkingDirectory (Get-Location).Path if ($result.ExitCode -ne 0) { Write-ErrorLine 'skills: update failed' return 1