Skip to content

Commit 282dd3d

Browse files
Copilotmnriemgithub-code-quality[bot]
authored
feat: Integration catalog — discovery, versioning, and community distribution (#2130)
* Initial plan * feat: add integration catalog system with catalog files, IntegrationCatalog class, list --catalog flag, upgrade command, integration.yml descriptor, and tests Agent-Logs-Url: https://github.com/github/spec-kit/sessions/bbcd44e8-c69c-4735-adc1-bdf1ce109184 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: address PR review feedback - Replace empty except with cache cleanup in _fetch_single_catalog - Log teardown failure warning instead of silent pass in upgrade - Validate catalog_data and integrations are dicts before use - Catch OSError/UnicodeError in IntegrationDescriptor._load - Add isinstance checks for integration/requires/provides/commands - Enforce semver (X.Y.Z) instead of PEP 440 for descriptor versions - Fix docstring and CONTRIBUTING.md to match actual block-on-modified behavior - Restore old manifest on upgrade failure for transactional safety * refactor: address second round of PR review feedback - Remove dead cache_file/cache_metadata_file attributes from IntegrationCatalog - Deduplicate non-default catalog warning (show once per process) - Anchor version regex to reject partial matches like 1.0.0beta - Fix 'Preserved modified' message to 'Skipped' for accuracy - Make upgrade transactional: install new files first, then remove stale old-only files, so a failed setup leaves old integration intact - Update CONTRIBUTING.md: speckit_version validates presence only * 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: address third round of PR review feedback - Fix CONTRIBUTING.md JSON examples to show full catalog structure with schema_version and integrations wrapper - Wrap cache writes in try/except OSError for read-only project dirs - Validate _load_catalog_config YAML root is a dict - Skip non-dict integ_data entries in merged catalog - Normalize tags to list-of-strings before filtering/searching - Add path traversal containment check for stale file deletion - Clarify docstring: lower numeric priority = higher precedence * fix: address fourth round of PR review feedback - Remove unused _write_catalog helper from test file - Fix comment: tests use monkeypatched urlopen, not file:// URLs - Wrap cache unlink calls in OSError handler - Add explicit encoding='utf-8' to all cache read_text/write_text calls - Restore packaging.version.Version for descriptor version validation to align with extension/preset validators - Add missing goose entry to integrations/catalog.json * fix: remove unused Path import, add comment to empty except * fix: validate descriptor root is dict, add shared infra to upgrade - Add isinstance(self.data, dict) check at start of _validate() so non-mapping YAML roots raise IntegrationDescriptorError - Run _install_shared_infra() and ensure_executable_scripts() in upgrade command to match install/switch behavior * fix: address sixth round of PR review feedback - Validate integration.id/name/version/description are strings - Catch TypeError in pkg_version.Version() for non-string versions - Swap validation order: check catalogs type before emptiness - Isolate TestActiveCatalogs from user ~/.specify/ via monkeypatch * fix: address seventh round of PR review feedback - Update docs: version field uses PEP 440, not semver - Harden search() against non-string author/name/description fields - Validate requires.speckit_version is a non-empty string - Validate command name/file are non-empty strings, file is safe relative path - Handle stale symlinks in upgrade cleanup - Document catalog configuration stack in README.md * fix: validate script entries, remove destructive teardown from upgrade rollback - Validate provides.scripts entries are non-empty strings with safe relative paths - Remove teardown from upgrade rollback since setup overwrites in-place — teardown would delete files that were working before the upgrade * fix: use consistent resolved root for stale-file cleanup paths * fix: validate redirect URL and reject drive-qualified paths - Validate final URL after redirects with _validate_catalog_url() - Reject paths with Path.drive or Path.anchor for Windows safety - Update FakeResponse mocks with geturl() method * fix: fix docstring backticks, assert file modification in upgrade tests * docs: clarify directory naming convention for hyphenated integration keys * fix: correct key type hint, isolate all catalog tests from env - Fix key parameter type to str | None (defaults to None) - Add HOME/USERPROFILE monkeypatch and clear SPECKIT_INTEGRATION_CATALOG_URL in all TestCatalogFetch tests for full environment isolation * fix: neutralize catalog table title, handle non-dict cache metadata * fix: validate requires.tools entries in descriptor * fix: show discovery-only status, clear metadata files in clear_cache * fix: catch OSError/UnicodeError in cache read path * refactor: reuse IntegrationManifest.uninstall for stale-file cleanup * fix: normalize null tools to empty list in descriptor accessor --------- 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>
1 parent e0fd355 commit 282dd3d

File tree

7 files changed

+1979
-1
lines changed

7 files changed

+1979
-1
lines changed

integrations/CONTRIBUTING.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Contributing to the Integration Catalog
2+
3+
This guide covers adding integrations to both the **built-in** and **community** catalogs.
4+
5+
## Adding a Built-In Integration
6+
7+
Built-in integrations are maintained by the Spec Kit core team and ship with the CLI.
8+
9+
### Checklist
10+
11+
1. **Create the integration subpackage** under `src/specify_cli/integrations/<package_dir>/`
12+
`<package_dir>` matches the integration key when it contains no hyphens (e.g., `gemini`), or replaces hyphens with underscores when it does (e.g., key `cursor-agent` → directory `cursor_agent/`, key `kiro-cli` → directory `kiro_cli/`). Python package names cannot use hyphens.
13+
2. **Implement the integration class** extending `MarkdownIntegration`, `TomlIntegration`, or `SkillsIntegration`
14+
3. **Register the integration** in `src/specify_cli/integrations/__init__.py`
15+
4. **Add tests** under `tests/integrations/test_integration_<package_dir>.py`
16+
5. **Add a catalog entry** in `integrations/catalog.json`
17+
6. **Update documentation** in `AGENTS.md` and `README.md`
18+
19+
### Catalog Entry Format
20+
21+
Add your integration under the top-level `integrations` key in `integrations/catalog.json`:
22+
23+
```json
24+
{
25+
"schema_version": "1.0",
26+
"integrations": {
27+
"my-agent": {
28+
"id": "my-agent",
29+
"name": "My Agent",
30+
"version": "1.0.0",
31+
"description": "Integration for My Agent",
32+
"author": "spec-kit-core",
33+
"repository": "https://github.com/github/spec-kit",
34+
"tags": ["cli"]
35+
}
36+
}
37+
}
38+
```
39+
40+
## Adding a Community Integration
41+
42+
Community integrations are contributed by external developers and listed in `integrations/catalog.community.json` for discovery.
43+
44+
### Prerequisites
45+
46+
1. **Working integration** — tested with `specify integration install`
47+
2. **Public repository** — hosted on GitHub or similar
48+
3. **`integration.yml` descriptor** — valid descriptor file (see below)
49+
4. **Documentation** — README with usage instructions
50+
5. **License** — open source license file
51+
52+
### `integration.yml` Descriptor
53+
54+
Every community integration must include an `integration.yml`:
55+
56+
```yaml
57+
schema_version: "1.0"
58+
integration:
59+
id: "my-agent"
60+
name: "My Agent"
61+
version: "1.0.0"
62+
description: "Integration for My Agent"
63+
author: "your-name"
64+
repository: "https://github.com/your-name/speckit-my-agent"
65+
license: "MIT"
66+
requires:
67+
speckit_version: ">=0.6.0"
68+
tools:
69+
- name: "my-agent"
70+
version: ">=1.0.0"
71+
required: true
72+
provides:
73+
commands:
74+
- name: "speckit.specify"
75+
file: "templates/speckit.specify.md"
76+
scripts:
77+
- update-context.sh
78+
```
79+
80+
### Descriptor Validation Rules
81+
82+
| Field | Rule |
83+
|-------|------|
84+
| `schema_version` | Must be `"1.0"` |
85+
| `integration.id` | Lowercase alphanumeric + hyphens (`^[a-z0-9-]+$`) |
86+
| `integration.version` | Valid PEP 440 version (parsed with `packaging.version.Version()`) |
87+
| `requires.speckit_version` | Required field; specify a version constraint such as `>=0.6.0` (current validation checks presence only) |
88+
| `provides` | Must include at least one command or script |
89+
| `provides.commands[].name` | String identifier |
90+
| `provides.commands[].file` | Relative path to template file |
91+
92+
### Submitting to the Community Catalog
93+
94+
1. **Fork** the [spec-kit repository](https://github.com/github/spec-kit)
95+
2. **Add your entry** under the `integrations` key in `integrations/catalog.community.json`:
96+
97+
```json
98+
{
99+
"schema_version": "1.0",
100+
"integrations": {
101+
"my-agent": {
102+
"id": "my-agent",
103+
"name": "My Agent",
104+
"version": "1.0.0",
105+
"description": "Integration for My Agent",
106+
"author": "your-name",
107+
"repository": "https://github.com/your-name/speckit-my-agent",
108+
"tags": ["cli"]
109+
}
110+
}
111+
}
112+
```
113+
114+
3. **Open a pull request** with:
115+
- Your catalog entry
116+
- Link to your integration repository
117+
- Confirmation that `integration.yml` is valid
118+
119+
### Version Updates
120+
121+
To update your integration version in the catalog:
122+
123+
1. Release a new version of your integration
124+
2. Open a PR updating the `version` field in `catalog.community.json`
125+
3. Ensure backward compatibility or document breaking changes
126+
127+
## Upgrade Workflow
128+
129+
The `specify integration upgrade` command supports diff-aware upgrades:
130+
131+
1. **Hash comparison** — the manifest records SHA-256 hashes of all installed files
132+
2. **Modified file detection** — files changed since installation are flagged
133+
3. **Safe default** — the upgrade blocks if any installed files were modified since installation
134+
4. **Forced reinstall** — passing `--force` overwrites modified files with the latest version
135+
136+
```bash
137+
# Upgrade current integration (blocks if files are modified)
138+
specify integration upgrade
139+
140+
# Force upgrade (overwrites modified files)
141+
specify integration upgrade --force
142+
```

integrations/README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Spec Kit Integration Catalog
2+
3+
The integration catalog enables discovery, versioning, and distribution of AI agent integrations for Spec Kit.
4+
5+
## Catalog Files
6+
7+
### Built-In Catalog (`catalog.json`)
8+
9+
Contains integrations that ship with Spec Kit. These are maintained by the core team and always installable.
10+
11+
### Community Catalog (`catalog.community.json`)
12+
13+
Community-contributed integrations. Listed for discovery only — users install from the source repositories.
14+
15+
## Catalog Configuration
16+
17+
The catalog stack is resolved in this order (first match wins):
18+
19+
1. **Environment variable**`SPECKIT_INTEGRATION_CATALOG_URL` overrides all catalogs with a single URL
20+
2. **Project config**`.specify/integration-catalogs.yml` in the project root
21+
3. **User config**`~/.specify/integration-catalogs.yml` in the user home directory
22+
4. **Built-in defaults**`catalog.json` + `catalog.community.json`
23+
24+
Example `integration-catalogs.yml`:
25+
26+
```yaml
27+
catalogs:
28+
- url: "https://example.com/my-catalog.json"
29+
name: "my-catalog"
30+
priority: 1
31+
install_allowed: true
32+
```
33+
34+
## CLI Commands
35+
36+
```bash
37+
# List built-in integrations (default)
38+
specify integration list
39+
40+
# Browse full catalog (built-in + community)
41+
specify integration list --catalog
42+
43+
# Install an integration
44+
specify integration install copilot
45+
46+
# Upgrade the current integration (diff-aware)
47+
specify integration upgrade
48+
49+
# Upgrade with force (overwrite modified files)
50+
specify integration upgrade --force
51+
```
52+
53+
## Integration Descriptor (`integration.yml`)
54+
55+
Each integration can include an `integration.yml` descriptor that documents its metadata, requirements, and provided commands/scripts:
56+
57+
```yaml
58+
schema_version: "1.0"
59+
integration:
60+
id: "my-agent"
61+
name: "My Agent"
62+
version: "1.0.0"
63+
description: "Integration for My Agent"
64+
author: "my-org"
65+
repository: "https://github.com/my-org/speckit-my-agent"
66+
license: "MIT"
67+
requires:
68+
speckit_version: ">=0.6.0"
69+
tools:
70+
- name: "my-agent"
71+
version: ">=1.0.0"
72+
required: true
73+
provides:
74+
commands:
75+
- name: "speckit.specify"
76+
file: "templates/speckit.specify.md"
77+
- name: "speckit.plan"
78+
file: "templates/speckit.plan.md"
79+
scripts:
80+
- update-context.sh
81+
- update-context.ps1
82+
```
83+
84+
## Catalog Schema
85+
86+
Both catalog files follow the same JSON schema:
87+
88+
```json
89+
{
90+
"schema_version": "1.0",
91+
"updated_at": "2026-04-08T00:00:00Z",
92+
"catalog_url": "https://...",
93+
"integrations": {
94+
"my-agent": {
95+
"id": "my-agent",
96+
"name": "My Agent",
97+
"version": "1.0.0",
98+
"description": "Integration for My Agent",
99+
"author": "my-org",
100+
"repository": "https://github.com/my-org/speckit-my-agent",
101+
"tags": ["cli"]
102+
}
103+
}
104+
}
105+
```
106+
107+
### Required Fields
108+
109+
| Field | Type | Description |
110+
|-------|------|-------------|
111+
| `schema_version` | string | Must be `"1.0"` |
112+
| `updated_at` | string | ISO 8601 timestamp |
113+
| `integrations` | object | Map of integration ID → metadata |
114+
115+
### Integration Entry Fields
116+
117+
| Field | Type | Required | Description |
118+
|-------|------|----------|-------------|
119+
| `id` | string | Yes | Unique ID (lowercase alphanumeric + hyphens) |
120+
| `name` | string | Yes | Human-readable display name |
121+
| `version` | string | Yes | PEP 440 version (e.g., `1.0.0`, `1.0.0a1`) |
122+
| `description` | string | Yes | One-line description |
123+
| `author` | string | No | Author name or organization |
124+
| `repository` | string | No | Source repository URL |
125+
| `tags` | array | No | Searchable tags (e.g., `["cli", "ide"]`) |
126+
127+
## Contributing
128+
129+
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to add integrations to the community catalog.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"schema_version": "1.0",
3+
"updated_at": "2026-04-08T00:00:00Z",
4+
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/integrations/catalog.community.json",
5+
"integrations": {}
6+
}

0 commit comments

Comments
 (0)