mirror of
https://github.com/github/spec-kit.git
synced 2026-03-17 02:43:08 +00:00
b9c1a1c7bb8f3ff5cd2c575b199e0e676a437e6b
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
58ce653908 |
feat(extensions): Quality of life improvements for RFC-aligned catalog integration (#1776)
* 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> |
||
|
|
ee922cbde9 |
feat(extensions): support multiple active catalogs simultaneously (#1720)
* Initial plan * feat(extensions): implement multi-catalog stack support - Add CatalogEntry dataclass to represent catalog entries - Add get_active_catalogs() reading SPECKIT_CATALOG_URL, project config, user config, or built-in default stack (org-approved + community) - Add _load_catalog_config() to parse .specify/extension-catalogs.yml - Add _validate_catalog_url() HTTPS validation helper - Add _fetch_single_catalog() with per-URL caching, backward-compat for DEFAULT_CATALOG_URL - Add _get_merged_extensions() that merges all catalogs (priority wins on conflict) - Update search() and get_extension_info() to use merged results annotated with _catalog_name and _install_allowed - Update clear_cache() to also remove per-URL hash cache files - Add extension_catalogs CLI command to list active catalogs - Add catalog add/remove sub-commands for .specify/extension-catalogs.yml - Update extension_add to enforce install_allowed=false policy - Update extension_search to show source catalog per result - Update extension_info to show source catalog with install_allowed status - Add 13 new tests covering catalog stack, merge conflict resolution, install_allowed enforcement, and catalog metadata Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * docs: update RFC, user guide, and API reference for multi-catalog support - RFC: replace FUTURE FEATURE section with full implementation docs, add catalog stack resolution order, config file examples, merge conflict resolution, and install_allowed behavior - EXTENSION-USER-GUIDE.md: add multi-catalog section with CLI examples for catalogs/catalog-add/catalog-remove, update catalog config docs - EXTENSION-API-REFERENCE.md: add CatalogEntry class docs, update ExtensionCatalog docs with new methods and result annotations, add catalog CLI commands (catalogs, catalog add, catalog remove) Also fix extension_catalogs command to correctly show "Using built-in default catalog stack" when config file exists but has empty catalogs Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * Potential fix for pull request finding 'Empty except' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: remove extraneous f-string prefixes (ruff F541) Remove f-prefix from strings with no placeholders in catalog_remove and extension_search commands. Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: address PR review feedback for multi-catalog support - Rename 'org-approved' catalog to 'default' - Move 'catalogs' command to 'catalog list' for consistency - Add 'description' field to CatalogEntry dataclass - Add --description option to 'catalog add' CLI command - Align install_allowed default to False in _load_catalog_config - Add user-level config detection in catalog list footer - Fix _load_catalog_config docstring (document ValidationError) - Fix test isolation for test_search_by_tag, test_search_by_query, test_search_verified_only, test_get_extension_info - Update version to 0.1.14 and CHANGELOG - Update all docs (RFC, User Guide, API Reference) * fix: wrap _load_catalog_config() calls in catalog_list with try/except - Check SPECKIT_CATALOG_URL first (matching get_active_catalogs() resolution order) - Wrap both _load_catalog_config() calls in try/except ValidationError so a malformed config file cannot crash `specify extension catalog list` after the active catalogs have already been printed successfully Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> |
||
|
|
56deda7be3 |
docs: Document dual-catalog system for extensions (#1689)
* docs: Document dual-catalog system for extensions - Clarify distinction between catalog.json (curated) and catalog.community.json (reference) - Update EXTENSION-DEVELOPMENT-GUIDE.md to explain community catalog submission - Update EXTENSION-PUBLISHING-GUIDE.md with dual-catalog workflow - Update EXTENSION-USER-GUIDE.md with catalog selection guidance - Expand README.md with comprehensive catalog explanation - Update RFC-EXTENSION-SYSTEM.md with dual-catalog design and current implementation - Change GitHub references from statsperform to github - Add SPECKIT_CATALOG_URL environment variable documentation This clarifies how organizations can curate their own catalog while browsing community-contributed extensions for discovery. * Update extensions/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update extensions/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update extensions/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
f14a47ea7d |
Add modular extension system (#1551)
* Add modular extension system for Spec Kit Implement a complete extension system that allows third-party developers to extend Spec Kit functionality through plugins. ## Core Features - Extension discovery and loading from local and global directories - YAML-based extension manifest (extension.yml) with metadata and capabilities - Command extensions: custom slash commands with markdown templates - Hook system: pre/post hooks for generate, task, and sync operations - Extension catalog for discovering and installing community extensions - SPECKIT_CATALOG_URL environment variable for catalog URL override ## Installation Methods - Catalog install: `specify extension add <name>` - URL install: `specify extension add <name> --from <url>` - Dev install: `specify extension add --dev <path>` ## Implementation - ExtensionManager class for lifecycle management (load, enable, disable) - Support for extension dependencies and version constraints - Configuration layering (global → project → extension) - Hook conditions for conditional execution ## Documentation - RFC with design rationale and architecture decisions - API reference for extension developers - Development guide with examples - User guide for installing and managing extensions - Publishing guide for the extension catalog ## Included - Extension template for bootstrapping new extensions - Comprehensive test suite - Example catalog.json structure Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update Jira extension to v2.1.0 in catalog Adds 2-level mode support (Epic → Stories only). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Address PR review feedback - Fix Zip Slip vulnerability in ZIP extraction with path validation - Fix keep_config option to actually preserve config files on removal - Add URL validation for SPECKIT_CATALOG_URL (HTTPS required, localhost exception) - Add security warning when installing from custom URLs (--from flag) - Empty catalog.json so organizations can ship their own catalogs - Fix markdown linter errors (MD040: add language to code blocks) - Remove redundant import and fix unused variables in tests - Add comment explaining empty except clause for backwards compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add comprehensive organization catalog customization docs - Explain why default catalog is empty (org control) - Document how to create and host custom catalogs - Add catalog JSON schema reference - Include use cases: private extensions, curated catalogs, air-gapped environments - Add examples for combining catalog with direct installation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix test assertions for extension system data structures - Update test_config_backup_on_remove to use new subdirectory structure (.backup/test-ext/file.yml instead of .backup/test-ext-file.yml) - Update test_full_install_and_remove_workflow to handle registered_commands being a dict keyed by agent name instead of a flat list Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Address Copilot review feedback - Fix localhost URL check to use parsed.hostname instead of netloc.startswith() This correctly handles URLs with ports like localhost:8080 - Fix YAML indentation error in config-template.yml (line 57) - Fix double space typo in example.md (line 172) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add catalog.example.json as reference for organizations The main catalog.json is intentionally empty so organizations can ship their own curated catalogs. This example file shows the expected schema and structure for creating organization-specific catalogs. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Address remaining Copilot security and logic review feedback - Fix Zip Slip vulnerability by using relative_to() for safe path validation - Add HTTPS validation for extension download URLs - Backup both *-config.yml and *-config.local.yml files on remove - Normalize boolean values to lowercase for hook condition comparisons - Show non-default catalog warning only once per instance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Ignoring linter for extensions directory --------- Co-authored-by: iamaeroplane <michal.bachorik@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Manfred Riem <manfred.riem@microsoft.com> |