Repository navigation
[Bug]: extension update removes extension skills of non-default integrations #5002
Description
Bug Description
In a project with two integrations installed, specify extension update deletes the extension's skills from the integration that is not the default one. The registry then lists commands for the default integration only, while integration.json still lists both integrations as installed.
The update re-registers commands through _active_command_registration_scope(), which restricts registration to the active agent (only_agent=active_agent in src/specify_cli/extensions/__init__.py). The files previously registered for the other integration are removed and not re-created.
Steps to Reproduce
- Create a project with Spec Kit 0.12.4, Claude as default and Codex as a second integration, then add the git extension:
Both
git init -q p1 cd p1 uvx --from git+https://github.com/github/spec-kit.git@v0.12.4 specify init --here --force --integration claude --script sh --ignore-agent-tools uvx --from git+https://github.com/github/spec-kit.git@v0.12.4 specify integration install codex --script sh uvx --from git+https://github.com/github/spec-kit.git@v0.12.4 specify extension add git.claude/skills/speckit-git-feature/and.agents/skills/speckit-git-feature/exist. - Upgrade both integrations with 1.2.1:
Both skill directories still exist.
uvx --from git+https://github.com/github/spec-kit.git@v1.2.1 specify integration upgrade claude uvx --from git+https://github.com/github/spec-kit.git@v1.2.1 specify integration upgrade codex
- Update the git extension (1.0.0 to 1.0.1) and answer
y:uvx --from git+https://github.com/github/spec-kit.git@v1.2.1 specify extension update git
- List
.agents/skills/speckit-git-*.
Expected Behavior
The extension stays registered for every installed integration, as it was before the update: .agents/skills/speckit-git-* are refreshed, not removed.
Actual Behavior
.claude/skills/speckit-git-* are refreshed and .agents/skills/speckit-git-* are deleted. The registry entry for git keeps registered_commands for claude only. In our own repository, updating agent-context (1.0.0 to 1.0.2) removed .agents/skills/speckit-agent-context-update/ the same way.
Running specify integration use codex and then specify integration use claude brings the Codex skills back, which is the workaround we use.
Specify CLI Version
1.2.1
AI Agent
Claude Code
Operating System
macOS 26 (Darwin 25.6.0, arm64)
Python Version
Python 3.12.7
Error Logs
--- 1.2.1: after integration upgrade claude, then codex
p1/.claude/skills/speckit-git-feature
p1/.agents/skills/speckit-git-feature
• git: 1.0.0 → 1.0.1
✓ Updated to v1.0.1
✓ Successfully updated 1 extension(s)
--- 1.2.1: after extension update git
p1/.claude/skills/speckit-git-feature
(none: p1/.agents/skills/speckit-git-feature)
registry git 1.0.1 registered_commands for ['claude']
installed_integrations ['claude', 'codex']Additional Context
Related but different: #2948 and #3599 are about which format extension skills get for a non-active agent. Here they are removed altogether.
Found while upgrading a repository that commits both .claude/skills and .agents/skills, so the deletion showed up as a diff.
AI Disclosure
Investigated and drafted with Claude Code (Claude Opus 5.5, human-supervised). The reproduction was run as written and the log above is its unedited output. I reviewed the report before filing.
Activity
github-actions commentedon Oct 17, 2026
Bug assessment — multi-integration-update: Valid · severity medium
Bug Assessment: extension update removes non-default integration skills
Report (summarized)
The reporter describes a project with Claude as the default integration and Codex installed as a second integration. After
specify extension update giton Specify CLI 1.2.1, the extension skill files remain under.claude/skills/, but the corresponding.agents/skills/files are deleted. The registry then records only the default integration's commands even though both integrations remain installed. Switching to Codex and back to Claude recreates the missing files. The report includes a successful reproduction and identifies_active_command_registration_scope()as the suspected restriction point.Symptom
Updating an installed extension removes its previously generated skill mirrors for non-active integrations instead of refreshing them. Expected behavior is that all extension artifacts belonging to installed integrations remain available after an update; actual behavior leaves only the active/default integration's skills and can create a repository diff that deletes the other integration's files.
Reproduction
git, confirming its skills exist under both.claude/skills/and.agents/skills/.specify extension update gitand confirm the update.The supplied report states this was reproduced with Specify 1.2.1, Python 3.12.7, and macOS arm64. The same behavior was observed while updating
agent-contextin the reporter's repository.Suspected Code Paths
src/specify_cli/extensions/_command_update_transaction.py:273-319— update backup enumerates command artifacts fromregistered_commands; this registry structure is per-agent, so only agents recorded there are backed up.src/specify_cli/extensions/_command_update_transaction.py:358-364— the update reads the flatregistered_skillslist and backs up matching skill directories, but those backups are used for rollback, not to preserve mirrors across a successful replacement.src/specify_cli/extensions/_command_update_transaction.py:551-554— the update removes the old extension before installing the new archive, invoking normal extension artifact cleanup.src/specify_cli/extensions/__init__.py:2226-2260—_unregister_extension_skills()treats an omittedskills_diras an unscoped cleanup and scans all configured agent skill directories.src/specify_cli/extensions/__init__.py:1440-1480—_active_command_registration_scope()resolves one active agent frominit-options.json; it does not include other installed integrations in the registration scope.src/specify_cli/extensions/__init__.py:1516-1577—_register_commands_for_active_agent()passesonly_agent=active_agent, so a fresh extension install during the update only registers command-backed artifacts for the active integration.src/specify_cli/extensions/__init__.py:4054-4061— the extension skill phase is explicitly guarded byif agent_name == active_agent, so the replacement install does not regenerate skills for inactive integrations.tests/specify_cli/extensions/test_command_update_transaction.py:206-264— existing coverage verifies rollback backup behavior for skill-shaped command files, but not successful updates with multiple installed integrations.tests/specify_cli/extensions/test_command_update.py:652-735— existing skill-update coverage focuses on restoring the active Claude skills after a failed update, not preserving an inactive integration after a successful update.Root Cause Hypothesis
Confidence: high. A successful update is implemented as remove-then-install. The remove path invokes unscoped extension-skill cleanup, which can remove marker-owned skill mirrors in every configured agent directory. The subsequent install path intentionally registers only the active agent (
only_agent=active_agent) and runs skill generation only whenagent_name == active_agent. Therefore, any non-active mirror removed during step 7 is not recreated. The flatregistered_skillsmetadata and per-agentregistered_commandsmetadata do not provide a successful-update path that preserves or rehydrates inactive integration artifacts.Proposed Remediation
Preferred: Make extension update preserve the complete set of artifacts that existed before replacement, including marker-owned skill mirrors for every installed integration. Before removal, capture each owned skill directory together with its owning/configured agent root (and all registered command artifacts). After installing the new extension, re-render or restore the replacement for every previously active integration, while keeping the normal active-integration behavior for ordinary
extension add. Update registry metadata so all surviving per-agent command registrations and flat skill ownership remain accurate. The cleanup should remain ownership-verified and path-safe, and should not overwrite user-customized skills.A less invasive implementation could make the update path invoke the existing integration activation/rescaffold operation for each previously registered integration after the active install, but it must avoid changing the project's selected active integration and must handle integrations whose output directories are global or skills-mode specific.
Files likely to change:
src/specify_cli/extensions/_command_update_transaction.pysrc/specify_cli/extensions/__init__.pytests/specify_cli/extensions/test_command_update.pytests/specify_cli/extensions/test_command_update_transaction.pyTests to add or update:
Risks & Considerations
registered_skillsis flat. A fix should not silently orphan files or drop metadata needed by later removal.Open Questions
integration.jsoneven if its artifacts were previously removed manually?]