Skip to content

fix(integrations): move extension artifacts on integration uninstall - #4911

Open
Chaoqi31 wants to merge 2 commits into
github:mainfrom
Chaoqi31:fix/uninstall-extension-artifacts
Open

Chaoqi31 wants to merge 2 commits into
github:mainfrom
Chaoqi31:fix/uninstall-extension-artifacts

Conversation

@Chaoqi31

Copy link
Copy Markdown

Description

specify integration uninstall <agent> removes the integration's own files but leaves the extension and preset commands and skills registered for that agent. When the uninstalled integration was the default, it also promotes another integration without registering the enabled extensions and presets for it. use and switch both do these steps; uninstall did not. Reproduction with the bundled git extension:

$ specify init p1 --integration claude --ignore-agent-tools --non-interactive
$ cd p1 && specify integration install gemini
$ specify extension add git
$ specify integration uninstall claude
✓ Integration 'Claude Code' uninstalled
  Removed 10 file(s)
$ find .claude -type f
.claude/skills/speckit-git-commit/SKILL.md          # left behind, no integration owns .claude/ now
.claude/skills/speckit-git-feature/SKILL.md
... (five speckit-git-* skills)
$ find .gemini -type f | grep git
(nothing)                                           # the new default has no extension commands
$ specify integration use gemini && find .gemini -type f | grep git
.gemini/commands/speckit.git.commit.toml            # what uninstall should have done
... (five files)

Presets go the same way: with the bundled lean preset installed, uninstalling claude leaves five of its override skills in .claude/skills/, and gemini keeps the core commands instead of the preset's.

#4862 added these two steps for catalog-installed adapters only, inside the external_lifecycle wrapper. That wrapper returns before its transaction when no external adapter is involved, so built-in integrations never reached them. This PR does them in the uninstall command itself, for every integration and also on the "no manifest" path, using the same helpers (the fallback messages are the wrapper's; the cleanup messages follow switch). It drops the wrapper's copy, which would otherwise run the steps a second time for external adapters. The integrations reference now describes the behaviour under Uninstall.

Behaviour notes: the extensions and presets stay installed; only the files registered for the removed agent go. As with switch, an extension command file the user edited is removed with its agent.

Testing

  • New test_uninstall_unregisters_extension_artifacts: the extension skill and its registry entry are gone after uninstall. On main: Extension skill should be removed with its agent.
  • New test_uninstall_default_registers_extensions_for_fallback, run with and without the integration manifest: the promoted default gets the extension skill. On main: Promoted default should receive enabled extensions (both cases).
  • New negative case test_uninstall_non_default_preserves_default_extension_artifacts: uninstalling a non-default integration leaves the default's extension files and registry entry in place.
  • test_command_uninstall.py: 14 passed.
  • tests/specify_cli/integrations/ without test_installed_adapters.py: 387 passed.
  • tests/specify_cli/integrations/test_installed_adapters.py (external adapters, the path the wrapper steps were removed from): 668 passed.
  • Reran the CLI steps above, and the same steps with the lean preset, against this branch: .claude/ holds no files afterwards, .gemini/commands/speckit.git.*.toml exist, and gemini's commands come from lean.

Equivalent local checks: tests ran with .venv/bin/python -m pytest and the CLI with the venv's specify, on macOS.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (fill in the disclosure below)

AI disclosure: This contribution was produced by Claude Code running autonomously on behalf of @Chaoqi31. An audit agent (Claude Fable 5.1, max reasoning effort) found the bug and wrote a failing reproduction. A fix agent (Claude Opus 5, xhigh effort) wrote the fix, tests and docs sentence. A separate review agent (Claude Fable 5.1, max reasoning effort) checked the branch, and the orchestrating agent (Claude Opus 5) reran the CLI reproductions and wrote the commits and this description. AI generated the code, tests and text. No human reviewed the diff line by line before submission.

Assisted-by: Claude Code (model: Claude Opus 5, autonomous)
`specify integration uninstall` tore down the integration files but left
the extension and preset commands and skills registered for that agent,
and when it promoted another integration to default it did not register
the enabled extensions and presets for it, unlike `use` and `switch`.
github#4862 added both steps for catalog-installed adapters in the
`external_lifecycle` wrapper only. Do them in the uninstall command for
every integration, including the no-manifest path, and drop the wrapper
copy so external adapters do not run them twice.

Assisted-by: Claude Code (model: Claude Opus 5, autonomous)
@Chaoqi31
Chaoqi31 requested a review from mnriem as a code owner October 10, 2026 04:51

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant