Skip to content

feat(extensions): Quality of life improvements for RFC-aligned catalog integration#1776

Merged
mnriem merged 12 commits intogithub:mainfrom
mbachorik:feat/extension-catalog-integration
Mar 13, 2026
Merged

feat(extensions): Quality of life improvements for RFC-aligned catalog integration#1776
mnriem merged 12 commits intogithub:mainfrom
mbachorik:feat/extension-catalog-integration

Conversation

@mbachorik
Copy link
Copy Markdown
Contributor

@mbachorik mbachorik commented Mar 7, 2026

Quality of life comprehensive improvements for RFC-aligned catalog integration with version comparison and enhanced verbose output.

Changes

Core Feature: Automatic Extension Updates

  • Automatic extension updates: Implement download → remove → install flow with confirmation
  • Atomic backup/restore: Full rollback on failed updates including:
    • Extension directory backup/restore
    • Command files for all AI agents backup/restore
    • Hooks in extensions.yml backup/restore
    • Registry metadata backup/restore
  • Config preservation: User config files preserved during updates (via keep_config=True)
  • Extension ID verification: Verify downloaded ZIP contains expected extension ID (security) - now validates BEFORE installing
  • Better error handling: Catch all exceptions except KeyboardInterrupt in update loop
  • Enabled state preservation: Disabled extensions stay disabled after update

Name Resolution & UX Improvements

  • Display name support: All extension commands (enable/disable/update/remove/info) accept display names in addition to IDs
  • Ambiguous name handling: When multiple installed extensions share a display name, shows a table of matches and asks user to specify by ID
  • Catalog display name lookup: extension info resolves display names from catalog for uninstalled extensions
  • Resolved names in messages: Status messages use resolved display names for consistency

Error Handling & Fallbacks

  • Catalog error fallback: extension info falls back to local installed info when catalog is unavailable
  • Catalog unavailable vs not found: Clear distinction between "catalog unavailable" (network error) and "not found in catalog"
  • Registry integrity: Preserve installed_at timestamp on rollback and enable/disable (using direct registry manipulation)

Functional Testing Results ✅

All extension operations were tested end-to-end in an isolated temporary environment:

Test Result
Install from local (--dev) ✅ Pass
List extensions ✅ Pass
Info by ID ✅ Pass
Info by display name ✅ Pass
Enable/disable with installed_at preservation ✅ Pass
Remove by ID ✅ Pass
Remove by display name ✅ Pass
Update detection (version comparison) ✅ Pass
Update execution (2.0.0 → 2.1.0) ✅ Pass
Backup cleanup on successful update ✅ Pass
_install_allowed check (skip updates from restricted catalogs) ✅ Pass
Extension list shows installed extension ✅ Pass
Extension info by ID ✅ Pass
Extension info by display name ✅ Pass
Disable preserves installed_at ✅ Pass
Enable preserves installed_at ✅ Pass
Update validates extension ID before installing ✅ Pass
Rollback preserves registry state ✅ Pass
Remove by display name ✅ Pass
Registry update() merges metadata ✅ Pass (via unit tests)

Test Environment

  • Temporary git repository with specify init
  • Local extension (spec-kit-jira) installed via --dev flag
  • Custom catalog config with install_allowed: true for update testing
  • Verified installed_at timestamp preserved across enable/disable cycles
  • Verified backup directory cleanup after successful update

Test Coverage

  • All 163 tests pass (5 new tests for ExtensionRegistry.update()/restore())

Reviewer Feedback Addressed ✅

  • Ambiguous name handling in enable/disable/update
  • Catalog error fallback for installed extensions
  • UX message clarity (catalog unavailable vs not found)
  • Resolved ID in status messages
  • enable/disable preserves installed_at
  • Registry rollback preserves installed_at
  • Rollback completeness (command files, hooks, extensions.yml)
  • Non-ExtensionError handling in update loop
  • Always backup registry (even without directory)
  • Extension ID mismatch detection (security)
  • Catalog display name lookup for uninstalled extensions
  • Safe author access in extension_info fallback
  • Unused parameter cleanup (_print_extension_info)
  • Clean up wrongly installed extension on ID mismatch
  • Preserve backup on failed rollback for manual recovery
  • Use restore() method for rollback (not update())
  • Cache invalidation when SPECKIT_CATALOG_URL changes
  • Hook rollback handles empty backup_hooks (falsy dict issue)
  • extension_info uses resolved ID for catalog lookup
  • Rollback removes new extension dir even if no backup existed
  • Validate extension ID from ZIP BEFORE installing (not after)
  • Preserve enabled state during updates
  • Optimize _resolve_catalog_extension with query parameter
  • update() merges metadata instead of replacing (preserves fields)
  • Add tests for ExtensionRegistry.update() and restore()

Authored with GitHub Copilot and Claude

Loading
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.

3 participants