mirror of
https://github.com/github/spec-kit.git
synced 2026-03-16 18:33:07 +00:00
* feat(extensions): implement automatic updates with atomic backup/restore - Implement automatic extension updates with download from catalog - Add comprehensive backup/restore mechanism for failed updates: - Backup registry entry before update - Backup extension directory - Backup command files for all AI agents - Backup hooks from extensions.yml - Add extension ID verification after install - Add KeyboardInterrupt handling to allow clean cancellation - Fix enable/disable to preserve installed_at timestamp by using direct registry manipulation instead of registry.add() - Add rollback on any update failure with command file, hook, and registry restoration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): comprehensive name resolution and error handling improvements - Add shared _resolve_installed_extension helper for ID/display name resolution with proper ambiguous name handling (shows table of matches) - Add _resolve_catalog_extension helper for catalog lookups by ID or display name - Update enable/disable/update/remove commands to use name resolution helpers - Fix extension_info to handle catalog errors gracefully: - Fallback to local installed info when catalog unavailable - Distinguish "catalog unavailable" from "not found in catalog" - Support display name lookup for both installed and catalog extensions - Use resolved display names in all status messages for consistency - Extract _print_extension_info helper for DRY catalog info printing Addresses reviewer feedback: - 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 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): properly detect ambiguous names in extension_info The extension_info command was breaking on the first name match without checking for ambiguity. This fix separates ID matching from name matching and checks for ambiguity before selecting a match, consistent with the _resolve_installed_extension() helper used by other commands. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(extensions): add public update() method to ExtensionRegistry Add a proper public API for updating registry metadata while preserving installed_at timestamp, instead of directly mutating internal registry data and calling private _save() method. Changes: - Add ExtensionRegistry.update() method that preserves installed_at - Update enable/disable commands to use registry.update() - Update rollback logic to use registry.update() This decouples the CLI from registry internals and maintains proper encapsulation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): safely access optional author field in extension_info ExtensionManifest doesn't expose an author property - the author field is optional in extension.yml and stored in data["extension"]["author"]. Use safe dict access to avoid AttributeError. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address multiple reviewer comments - ExtensionRegistry.update() now preserves original installed_at timestamp - Add ExtensionRegistry.restore() for rollback (entry was removed) - Clean up wrongly installed extension on ID mismatch before rollback - Remove unused catalog_error parameter from _print_extension_info() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): check _install_allowed for updates, preserve backup on failed rollback - Skip automatic updates for extensions from catalogs with install_allowed=false - Only delete backup directory on successful rollback, preserve it on failure for manual recovery Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address reviewer feedback on update/rollback logic - Hook rollback: handle empty backup_hooks by checking `is not None` instead of truthiness (falsy empty dict would skip hook cleanup) - extension_info: use resolved_installed_id for catalog lookup when extension was found by display name (prevents wrong catalog match) - Rollback: always remove extension dir first, then restore if backup exists (handles case when no original dir existed before update) - Validate extension ID from ZIP before installing, not after (avoids side effects of installing wrong extension before rollback) - Preserve enabled state during updates: re-apply disabled state and hook enabled flags after successful update - Optimize _resolve_catalog_extension: pass query to catalog.search() instead of fetching all extensions - update() now merges metadata with existing entry instead of replacing (preserves fields like registered_commands when only updating enabled) - Add tests for ExtensionRegistry.update() and restore() methods: - test_update_preserves_installed_at - test_update_merges_with_existing - test_update_raises_for_missing_extension - test_restore_overwrites_completely - test_restore_can_recreate_removed_entry Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(extensions): update RFC to reflect implemented status - Change status from "Draft" to "Implemented" - Update all Implementation Phases to show completed items - Add new features implemented beyond original RFC: - Display name resolution for all commands - Ambiguous name handling with tables - Atomic update with rollback - Pre-install ID validation - Enabled state preservation - Registry update/restore methods - Catalog error fallback - _install_allowed flag - Cache invalidation - Convert Open Questions to Resolved Questions with decisions - Add remaining Open Questions (sandboxing, signatures) as future work - Fix table of contents links Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address third round of PR review comments - Refactor extension_info to use _resolve_installed_extension() helper with new allow_not_found parameter instead of duplicating resolution logic - Fix rollback hook restoration to not create empty hooks: {} in config when original config had no hooks section - Fix ZIP pre-validation to handle nested extension.yml files (GitHub auto-generated ZIPs have structure like repo-name-branch/extension.yml) - Replace unused installed_manifest variable with _ placeholder - Add display name to update status messages for better UX Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address fourth round of PR review comments Rollback fixes: - Preserve installed_at timestamp after successful update (was reset by install_from_zip calling registry.add) - Fix rollback to only delete extension_dir if backup exists (avoids destroying valid installation when failure happens before modification) - Fix rollback to remove NEW command files created by failed install (files that weren't in original backup are now cleaned up) - Fix rollback to delete hooks key entirely when backup_hooks is None (original config had no hooks key, so restore should remove it) Cross-command consistency fix: - Add display name resolution to `extension add` command using _resolve_catalog_extension() helper (was only in `extension info`) - Use resolved extension ID for download_extension() call, not original argument which may be a display name Security fix (fail-closed): - Malformed catalog config (empty/missing URLs) now raises ValidationError instead of silently falling back to built-in catalogs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lint): address ruff linting errors and registry.update() semantics - Remove unused import ExtensionError in extension_info - Remove extraneous f-prefix from strings without placeholders - Use registry.restore() instead of registry.update() for installed_at preservation (update() always preserves existing installed_at, ignoring our override) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: iamaeroplane <michal.bachorik@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>