-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Description
Summary
The current extension catalog system supports only a single active catalog at a time — selectable via SPECKIT_CATALOG_URL or defaulting to the built-in catalog.json. This creates a fundamental conflict: catalog.json is intentionally empty (organizations curate their own approved extensions), while catalog.community.json exists as a shared discovery resource, and organizations also need to point at their own private/internal catalogs. Users currently have no way to benefit from all three simultaneously.
Problem Statement
The RFC describes a "Dual Catalog System" but the two catalogs serve different purposes and neither is composable with the other today:
| Catalog | Purpose | Current Behavior |
|---|---|---|
catalog.json |
Org-curated approved extensions | Empty by design; sole default search target |
catalog.community.json |
Community discovery resource | Never searched by CLI commands |
| Custom URL | Private/internal org catalog | Replaces default entirely via env var |
Real-world usage requires all three active at once: search the community catalog for discoverability, restrict installs to org-approved entries, and also pull from an internal catalog — with clear precedence rules between them.
Proposed Solution
Introduce a catalog stack — an ordered list of catalogs the CLI merges and searches across. Each catalog entry has a url, an optional name, a priority, and an install_allowed flag.
Default built-in stack (no config required)
When no .specify/extension-catalogs.yml exists, the CLI uses a built-in default stack:
catalog.json(org-curated,install_allowed: true, priority 1)catalog.community.json(community discovery,install_allowed: false, priority 2)
This means specify extension search works out of the box and surfaces community extensions, while specify extension add is still restricted to whatever is in catalog.json — preserving the existing curation/trust model.
New config file: .specify/extension-catalogs.yml (project-scoped)
catalogs:
- name: "org-approved"
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json"
priority: 1 # Highest — only approved entries can be installed
install_allowed: true
- name: "internal"
url: "https://internal.company.com/spec-kit/catalog.json"
priority: 2
install_allowed: true
- name: "community"
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json"
priority: 3 # Lowest — discovery only, not installable
install_allowed: falseAn equivalent user-level config lives at ~/.specify/extension-catalogs.yml for user-wide defaults. When a project-level config is present, it takes full control and the built-in defaults are not applied.
Resolution order
When a user runs specify extension search or specify extension add <name>, the CLI:
- Loads all configured catalogs in priority order
- Merges results, with higher-priority catalogs winning on conflicts (same extension
id) - Respects
install_allowed: false— extensions from discovery-only catalogs are shown in search results but cannot be installed directly
CLI additions
# List active catalogs
specify extension catalogs
# Add a catalog (project-scoped)
specify extension catalog add --name "internal" https://internal.company.com/spec-kit/catalog.json
# Remove a catalog
specify extension catalog remove internal
# Show which catalog an extension came from
specify extension info jira
# → Source catalog: org-approvedBackward compatibility
- Built-in default stack includes
catalog.community.jsonasinstall_allowed: false— no config needed to get community discoverability SPECKIT_CATALOG_URLenv var still works: treated as a singleinstall_allowed: truecatalog, replacing both defaults for full backward compat- Explicit
.specify/extension-catalogs.ymloverrides all defaults entirely
Acceptance Criteria
-
specify extension catalogslists all active catalogs with name, URL, priority, andinstall_allowed - When no catalog config exists,
catalog.community.jsonis included in the default stack asinstall_allowed: falseat priority 2 -
specify extension searchaggregates results across all active catalogs, annotating each result with its source catalog -
specify extension addrespectsinstall_allowed: falseand rejects installs from discovery-only catalogs with a clear message: "'linear' is available in the community catalog but not in your approved catalog. Add it to.specify/extension-catalogs.ymlwithinstall_allowed: trueto enable installation." -
.specify/extension-catalogs.ymland~/.specify/extension-catalogs.ymlare both read, with project-level taking precedence -
SPECKIT_CATALOG_URLenv var still overrides the full stack (backward compat) - All catalog URLs must use HTTPS (localhost HTTP allowed for development, consistent with current policy)
- Unit tests cover merge conflict resolution (same extension
idin two catalogs) - Docs updated: RFC,
EXTENSION-USER-GUIDE.md,EXTENSION-API-REFERENCE.md
Out of Scope
- Catalog authentication / private registry tokens (separate issue)
- Automatic synchronization / caching of catalog contents (separate issue)
- Signature verification for catalog entries (tracked in RFC under future security work)
References
- RFC:
extensions/RFC-EXTENSION-SYSTEM.md— "Custom Catalogs" section (currently marked⚠️ FUTURE FEATURE) extensions/catalog.json— empty by designextensions/catalog.community.jsonextensions/EXTENSION-USER-GUIDE.md