From 7186bd06caed5b96efbf0350efa062c594dcb4da Mon Sep 17 00:00:00 2001 From: Julian lechner Date: Thu, 24 Sep 2026 16:27:34 +0200 Subject: [PATCH] fix(uninstall): reconcile extension-only installs --- TODO.md | 2 +- scripts/commands/uninstall.ps1 | 2 +- scripts/commands/uninstall.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 1928a96..350931e 100644 --- a/TODO.md +++ b/TODO.md @@ -23,7 +23,7 @@ - [x] Keep terminal newlines in Bash-managed files that need substitution. Command substitution strips trailing newlines before Bash hashes and writes the content, while PowerShell retains them; switching installers causes needless file changes and backups. (`scripts/lib/manifest.sh:70`, `scripts/lib/manifest.ps1:75`) - [x] Parse the Bash plugin inventory as JSON and stop when listing fails. `plugin_installed()` searches for one exact spacing pattern and suppresses CLI errors; a minified response or listing failure marks installed plugins unchecked, allowing an update to remove owned plugins. PowerShell parses JSON and fails on a CLI error. (`scripts/lib/plugins.sh:4`, `scripts/lib/plugins.ps1:36`) - [x] Serialize Bash manifest entries without shell word splitting. `write_managed_manifest()` loops over command substitution; a managed filename containing spaces becomes multiple invalid entries and can no longer be reconciled correctly. (`scripts/lib/manifest.sh:47`) -- [ ] Allow uninstall to reconcile owned extensions when file manifests are missing. Both uninstall commands stop with “Not installed” based only on destination manifests, even if `extensions.json` still owns plugins, skills, or MCP servers. (`scripts/commands/uninstall.ps1:21`, `scripts/commands/uninstall.sh:34`) +- [x] Allow uninstall to reconcile owned extensions when file manifests are missing. Both uninstall commands stop with “Not installed” based only on destination manifests, even if `extensions.json` still owns plugins, skills, or MCP servers. (`scripts/commands/uninstall.ps1:21`, `scripts/commands/uninstall.sh:34`) ## Rules, checks, and documentation diff --git a/scripts/commands/uninstall.ps1 b/scripts/commands/uninstall.ps1 index 497366e..fb84c26 100644 --- a/scripts/commands/uninstall.ps1 +++ b/scripts/commands/uninstall.ps1 @@ -18,7 +18,7 @@ $generated = Join-Path $root 'generated' $targets = @(Get-InstallTargets -Generated $generated -HomePath $homePath -Shell 'bash' -Client $Client) $installedTargets = @($targets | Where-Object { Test-Path -LiteralPath (Get-ManagedManifestPath $_.Destination) }) -if (-not $DryRun -and $installedTargets.Count -eq 0) { throw 'Not installed, nothing to uninstall.' } +if (-not $DryRun -and $installedTargets.Count -eq 0 -and -not (Test-Path -LiteralPath (Join-Path $homePath '.my-ai-configuration/extensions.json'))) { throw 'Not installed, nothing to uninstall.' } if (-not $DryRun -and -not $Force) { if ([Console]::IsInputRedirected -or $env:CI -eq 'true' -or $env:AI_CONFIG_NO_INTERACTIVE -eq '1') { diff --git a/scripts/commands/uninstall.sh b/scripts/commands/uninstall.sh index 517c3d8..26fe5b2 100644 --- a/scripts/commands/uninstall.sh +++ b/scripts/commands/uninstall.sh @@ -30,7 +30,7 @@ installed_count=0 for target_pair in "${targets[@]}"; do [ -f "$(manifest_path "${target_pair#*|}")" ] && installed_count=$((installed_count + 1)) done -if [ "$dry_run" = false ] && [ "$installed_count" -eq 0 ]; then +if [ "$dry_run" = false ] && [ "$installed_count" -eq 0 ] && [ ! -f "$home_path/.my-ai-configuration/extensions.json" ]; then printf 'Not installed, nothing to uninstall.\n' >&2 exit 1 fi