feat: add category and effect as first-class fields in extension schema (#2899)

* feat: add category and effect as first-class fields in extension schema

Add `category` and `effect` as optional fields in the extension schema
(`extension.yml`) and community catalog (`catalog.community.json`).

Schema changes:
- Valid categories: docs, code, process, integration, visibility
- Valid effects: read-only, read-write
- Both fields are optional (backward-compatible with existing extensions)
- Validation raises ValidationError for invalid values when present

Propagation:
- Added `category` and `effect` to all 108 entries in catalog.community.json
  (populated from the existing docs/community/extensions.md table)
- Updated extension template with commented category/effect fields
- Updated add-community-extension skill with new JSON template fields
- Updated `specify extension info` CLI output to display category/effect
- Added properties to ExtensionManifest class

Tests:
- test_valid_category: all 5 category values pass
- test_valid_effect: both effect values pass
- test_invalid_category: invalid value raises ValidationError
- test_invalid_effect: invalid value raises ValidationError
- test_category_and_effect_optional: omitting fields still works

Closes #2874

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: make category free-form, keep effect validated

Category is a free-form string (only validated as non-empty when present),
while effect remains restricted to 'read-only' or 'read-write'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: address PR review feedback

- Add type guard before 'in' check for effect to prevent TypeError on
  unhashable YAML values (list/dict)
- Comment out category/effect in template so authors must opt in
- Use VALID_EFFECTS constant in test instead of hard-coded values

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: update category docstring to reflect free-form semantics

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: clarify canonical extension effect values

---------

Co-authored-by: Manfred Riem <mnriem@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Manfred Riem
2026-06-10 07:44:27 -05:00
committed by GitHub
parent 45b88f62be
commit 40e48ed22c
7 changed files with 340 additions and 7 deletions

View File

@@ -70,6 +70,8 @@ Use the existing entries as the format template. Required fields:
"documentation": "<documentation>",
"changelog": "<changelog>",
"license": "<license>",
"category": "<category>",
"effect": "<effect>",
"requires": {
"speckit_version": "<speckit_version>"
},
@@ -87,6 +89,9 @@ Use the existing entries as the format template. Required fields:
}
```
**Category** — free-form string; common values: `docs`, `code`, `process`, `integration`, `visibility`
**Effect** — one of: `read-only`, `read-write`
If the extension has optional tool dependencies, add a `"tools"` array inside `"requires"`:
```json
@@ -113,8 +118,8 @@ Determine the category and effect from the extension's behavior:
| <Name> | <Description> | `<category>` | <Effect> | [<repo-name>](<repository-url>) |
```
**Category**one of: `docs`, `code`, `process`, `integration`, `visibility`
**Effect**`Read-only` (produces reports only) or `Read+Write` (modifies project files)
**Category**free-form; common values: `docs`, `code`, `process`, `integration`, `visibility`
**Effect** write canonical values `read-only` or `read-write` in `extension.yml` and `catalog.community.json`; use `Read-only`/`Read+Write` only for the docs table display
### 6. Commit, push, and open PR

View File

@@ -7,7 +7,7 @@
The following community-contributed extensions are available in [`catalog.community.json`](https://github.com/github/spec-kit/blob/main/extensions/catalog.community.json):
**Categories:**
**Categories** (common values, but any string is allowed):
- `docs` — reads, validates, or generates spec artifacts
- `code` — reviews, validates, or modifies source code
@@ -15,10 +15,13 @@ The following community-contributed extensions are available in [`catalog.commun
- `integration` — syncs with external platforms
- `visibility` — reports on project health or progress
**Effect:**
**Effect** (canonical `extension.yml`/catalog values):
- `Read-only` — produces reports without modifying files
- `Read+Write` — modifies files, creates artifacts, or updates specs
- `read-only` — produces reports without modifying files (displayed as `Read-only` in the table)
- `read-write` — modifies files, creates artifacts, or updates specs (displayed as `Read+Write` in the table)
> [!TIP]
> Extension authors can declare `category` and `effect` in their `extension.yml` under the `extension:` block. These fields are also available in `catalog.community.json` for tooling and the CLI (`specify extension info`).
| Extension | Purpose | Category | Effect | URL |
|-----------|---------|----------|--------|-----|

View File

@@ -15,6 +15,8 @@
"documentation": "https://github.com/mnriem/spec-kit-extensions/blob/main/aide/README.md",
"changelog": "https://github.com/mnriem/spec-kit-extensions/blob/main/aide/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -48,6 +50,8 @@
"documentation": "https://github.com/xymelon/spec-kit-agent-assign/blob/main/README.md",
"changelog": "https://github.com/xymelon/spec-kit-agent-assign/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -80,6 +84,8 @@
"documentation": "https://github.com/bigsmartben/spec-kit-agent-governance/blob/main/README.md",
"changelog": "https://github.com/bigsmartben/spec-kit-agent-governance/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.0",
"tools": [
@@ -117,6 +123,8 @@
"documentation": "https://github.com/pragya247/spec-kit-orchestrator/blob/main/README.md",
"changelog": "https://github.com/pragya247/spec-kit-orchestrator/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.6.1"
},
@@ -149,6 +157,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-api-evolve/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-api-evolve/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -186,6 +196,8 @@
"documentation": "https://github.com/bigsmartben/spec-kit-arch/blob/main/README.md",
"changelog": "https://github.com/bigsmartben/spec-kit-arch/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.10.dev0"
},
@@ -217,6 +229,8 @@
"documentation": "https://github.com/UmmeHabiba1312/spec-kit-architect-preview/blob/main/README.md",
"changelog": "https://github.com/UmmeHabiba1312/spec-kit-architect-preview/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -249,6 +263,8 @@
"documentation": "https://github.com/DyanGalih/spec-kit-architecture-guard/blob/main/docs/architecture-overview.md",
"changelog": "https://github.com/DyanGalih/spec-kit-architecture-guard/releases",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -283,6 +299,8 @@
"documentation": "https://github.com/stn1slv/spec-kit-archive/blob/main/README.md",
"changelog": "https://github.com/stn1slv/spec-kit-archive/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -314,6 +332,8 @@
"documentation": "https://github.com/pragya247/spec-kit-azure-devops/blob/main/README.md",
"changelog": "https://github.com/pragya247/spec-kit-azure-devops/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -353,6 +373,8 @@
"documentation": "https://github.com/chordpli/spec-kit-blueprint/blob/main/README.md",
"changelog": "https://github.com/chordpli/spec-kit-blueprint/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -385,6 +407,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-branch-convention/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-branch-convention/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -417,6 +441,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-brownfield/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-brownfield/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -449,6 +475,8 @@
"documentation": "https://github.com/MaksimShevtsov/BrownKit/blob/main/README.md",
"changelog": "https://github.com/MaksimShevtsov/BrownKit/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -481,6 +509,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-bugfix/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-bugfix/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -513,6 +543,8 @@
"documentation": "https://github.com/maximiliamus/spec-kit-canon/blob/master/README.md",
"changelog": "https://github.com/maximiliamus/spec-kit-canon/blob/master/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.3"
},
@@ -548,6 +580,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-catalog-ci/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-catalog-ci/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -580,6 +614,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-changelog/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-changelog/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -612,6 +648,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-ci-guard/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-ci-guard/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -645,6 +683,8 @@
"documentation": "https://github.com/aaronrsun/spec-kit-checkpoint/blob/main/README.md",
"changelog": "https://github.com/aaronrsun/spec-kit-checkpoint/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -674,6 +714,8 @@
"documentation": "https://github.com/dsrednicki/spec-kit-cleanup/blob/main/README.md",
"changelog": "https://github.com/dsrednicki/spec-kit-cleanup/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -706,6 +748,8 @@
"documentation": "https://github.com/twbrandon7/spec-kit-conduct-ext/blob/main/README.md",
"changelog": "https://github.com/twbrandon7/spec-kit-conduct-ext/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.1"
},
@@ -736,6 +780,8 @@
"documentation": "https://github.com/arunt14/spec-kit-critique/blob/main/README.md",
"changelog": "https://github.com/arunt14/spec-kit-critique/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -766,6 +812,8 @@
"documentation": "https://github.com/aaronrsun/spec-kit-confluence/blob/main/README.md",
"changelog": "https://github.com/aaronrsun/spec-kit-confluence/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -794,6 +842,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-cost/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-cost/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.0"
},
@@ -826,6 +876,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-diagram-/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-diagram-/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -858,6 +910,8 @@
"documentation": "https://github.com/raccioly/docguard/blob/main/extensions/spec-kit-docguard/README.md",
"changelog": "https://github.com/raccioly/docguard/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -900,6 +954,8 @@
"documentation": "https://github.com/KhawarHabibKhan/spec-kit-doctor/blob/main/README.md",
"changelog": "https://github.com/KhawarHabibKhan/spec-kit-doctor/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -931,6 +987,8 @@
"documentation": "https://github.com/mnriem/spec-kit-extensions/blob/main/extensify/README.md",
"changelog": "https://github.com/mnriem/spec-kit-extensions/blob/main/extensify/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.0"
},
@@ -962,6 +1020,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-fix-findings/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-fix-findings/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -994,6 +1054,8 @@
"documentation": "https://github.com/speckit-community/spec-kit-fixit/blob/main/README.md",
"changelog": "https://github.com/speckit-community/spec-kit-fixit/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1025,6 +1087,8 @@
"documentation": "https://github.com/sharathsatish/spec-kit-fleet/blob/main/README.md",
"changelog": "https://github.com/sharathsatish/spec-kit-fleet/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1055,6 +1119,8 @@
"homepage": "https://github.com/RogerBestMsft/spec-kit-FxToNet",
"documentation": "https://github.com/RogerBestMsft/spec-kit-FxToNet/blob/main/README.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -1094,6 +1160,8 @@
"documentation": "https://github.com/Fatima367/spec-kit-github-issues/blob/main/README.md",
"changelog": "https://github.com/Fatima367/spec-kit-github-issues/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -1134,6 +1202,8 @@
"documentation": "https://github.com/aaronrsun/spec-kit-issue/blob/main/README.md",
"changelog": "https://github.com/aaronrsun/spec-kit-issue/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1166,6 +1236,8 @@
"documentation": "https://github.com/imviancagrace/spec-kit-iterate/blob/main/README.md",
"changelog": "https://github.com/imviancagrace/spec-kit-iterate/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1196,6 +1268,8 @@
"documentation": "https://github.com/mbachorik/spec-kit-jira/blob/main/README.md",
"changelog": "https://github.com/mbachorik/spec-kit-jira/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1260,6 +1334,8 @@
"documentation": "https://github.com/imviancagrace/spec-kit-learn/blob/main/README.md",
"changelog": "https://github.com/imviancagrace/spec-kit-learn/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1291,6 +1367,8 @@
"documentation": "https://github.com/ashbrener/spec-kit-linear-sync/blob/main/README.md",
"changelog": "https://github.com/ashbrener/spec-kit-linear-sync/releases",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1324,6 +1402,8 @@
"documentation": "https://github.com/BenBtg/spec-kit-m365/blob/main/README.md",
"changelog": "https://github.com/BenBtg/spec-kit-m365/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -1362,6 +1442,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-ext/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-ext/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1395,6 +1477,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-azure-devops/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-azure-devops/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1427,6 +1511,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-ci/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-ci/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1460,6 +1546,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-github-projects/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-github-projects/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1492,6 +1580,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-jira/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-jira/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1524,6 +1614,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-linear/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-linear/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1556,6 +1648,8 @@
"documentation": "https://github.com/GenieRobot/spec-kit-maqa-trello/blob/main/README.md",
"changelog": "https://github.com/GenieRobot/spec-kit-maqa-trello/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.3.0"
},
@@ -1588,6 +1682,8 @@
"documentation": "https://github.com/BenBtg/spec-kit-markitdown/blob/main/README.md",
"changelog": "https://github.com/BenBtg/spec-kit-markitdown/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -1625,6 +1721,8 @@
"repository": "https://github.com/AI-MDE/spec-kit-mde",
"homepage": "https://github.com/AI-MDE/spec-kit-mde",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1656,6 +1754,8 @@
"documentation": "https://github.com/KevinBrown5280/spec-kit-memory-loader/blob/main/README.md",
"changelog": "https://github.com/KevinBrown5280/spec-kit-memory-loader/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.6.0"
},
@@ -1687,6 +1787,8 @@
"documentation": "https://github.com/DyanGalih/spec-kit-memory-hub/blob/main/README.md",
"changelog": "https://github.com/DyanGalih/spec-kit-memory-hub/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -1720,6 +1822,8 @@
"documentation": "https://github.com/RbBtSn0w/spec-kit-extensions/blob/main/memorylint/README.md",
"changelog": "https://github.com/RbBtSn0w/spec-kit-extensions/blob/main/memorylint/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.5.1"
},
@@ -1752,6 +1856,8 @@
"documentation": "https://github.com/formin/multi-model-review/blob/main/README.md",
"changelog": "https://github.com/formin/multi-model-review/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0",
"tools": [
@@ -1802,6 +1908,8 @@
"documentation": "https://github.com/teeyo/spec-kit-multi-sites/blob/main/README.md",
"changelog": "https://github.com/teeyo/spec-kit-multi-sites/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1833,6 +1941,8 @@
"documentation": "https://github.com/dmux/spec-kit-onboard/blob/main/README.md",
"changelog": "https://github.com/dmux/spec-kit-onboard/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1866,6 +1976,8 @@
"documentation": "https://github.com/sakitA/spec-kit-optimize/blob/main/README.md",
"changelog": "https://github.com/sakitA/spec-kit-optimize/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1898,6 +2010,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-orchestrator/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-orchestrator/releases",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -1930,6 +2044,8 @@
"documentation": "https://github.com/luno/spec-kit-plan-review-gate/blob/main/README.md",
"changelog": "https://github.com/luno/spec-kit-plan-review-gate/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -1961,6 +2077,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-pr-bridge-/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-pr-bridge-/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -1993,6 +2111,8 @@
"documentation": "https://github.com/mnriem/spec-kit-extensions/blob/main/presetify/README.md",
"changelog": "https://github.com/mnriem/spec-kit-extensions/blob/main/presetify/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -2024,6 +2144,8 @@
"documentation": "https://github.com/bigsmartben/spec-kit-preview/blob/main/README.md",
"changelog": "https://github.com/bigsmartben/spec-kit-preview/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.10.dev0"
},
@@ -2055,6 +2177,8 @@
"documentation": "https://github.com/d0whc3r/spec-kit-product/wiki",
"changelog": "https://github.com/d0whc3r/spec-kit-product/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -2097,6 +2221,8 @@
"documentation": "https://github.com/VaiYav/speckit-product-forge/blob/main/README.md",
"changelog": "https://github.com/VaiYav/speckit-product-forge/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2129,6 +2255,8 @@
"documentation": "https://github.com/arunt14/spec-kit-qa/blob/main/README.md",
"changelog": "https://github.com/arunt14/spec-kit-qa/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2159,6 +2287,8 @@
"documentation": "https://github.com/Sertxito/spec-kit-extension-rag-azure-builder#readme",
"changelog": "https://github.com/Sertxito/spec-kit-extension-rag-azure-builder/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.0"
},
@@ -2191,6 +2321,8 @@
"documentation": "https://github.com/Rubiss-Projects/spec-kit-ralph/blob/main/README.md",
"changelog": "https://github.com/Rubiss-Projects/spec-kit-ralph/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -2232,6 +2364,8 @@
"documentation": "https://github.com/stn1slv/spec-kit-reconcile/blob/main/README.md",
"changelog": "https://github.com/stn1slv/spec-kit-reconcile/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2263,6 +2397,8 @@
"documentation": "https://github.com/ashbrener/spec-kit-red-team/blob/main/README.md",
"changelog": "https://github.com/ashbrener/spec-kit-red-team/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2295,6 +2431,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-refine/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-refine/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -2327,6 +2465,8 @@
"documentation": "https://github.com/liuyiyu/spec-kit-repoindex/tree/main/docs",
"changelog": "https://github.com/liuyiyu/spec-kit-repoindex/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -2364,6 +2504,8 @@
"documentation": "https://github.com/LoogacyStudio/spec-kit-reqnroll-bdd#readme",
"changelog": "https://github.com/LoogacyStudio/spec-kit-reqnroll-bdd/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.0",
"tools": [
@@ -2402,6 +2544,8 @@
"documentation": "https://github.com/arunt14/spec-kit-retro/blob/main/README.md",
"changelog": "https://github.com/arunt14/spec-kit-retro/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2432,6 +2576,8 @@
"documentation": "https://github.com/emi-dm/spec-kit-retrospective/blob/main/README.md",
"changelog": "https://github.com/emi-dm/spec-kit-retrospective/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2464,6 +2610,8 @@
"documentation": "https://github.com/ismaelJimenez/spec-kit-review/blob/main/README.md",
"changelog": "https://github.com/ismaelJimenez/spec-kit-review/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2498,6 +2646,8 @@
"documentation": "https://github.com/chordpli/spec-kit-ripple/blob/main/README.md",
"changelog": "https://github.com/chordpli/spec-kit-ripple/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -2530,6 +2680,8 @@
"documentation": "https://github.com/jfranc38/spec-kit-schedule/blob/main/README.md",
"changelog": "https://github.com/jfranc38/spec-kit-schedule/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -2562,6 +2714,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-scope-/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-scope-/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -2595,6 +2749,8 @@
"documentation": "https://github.com/DyanGalih/spec-kit-security-review/blob/main/README.md",
"changelog": "https://github.com/DyanGalih/spec-kit-security-review/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2627,6 +2783,8 @@
"documentation": "https://ysumanth06.github.io/spec-kit-sf/introduction.html",
"changelog": "https://github.com/ysumanth06/spec-kit-sf/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0",
"tools": [
@@ -2671,6 +2829,8 @@
"documentation": "https://github.com/arunt14/spec-kit-ship/blob/main/README.md",
"changelog": "https://github.com/arunt14/spec-kit-ship/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2701,6 +2861,8 @@
"documentation": "https://github.com/KevinBrown5280/spec-kit-spec-reference-loader/blob/main/README.md",
"changelog": "https://github.com/KevinBrown5280/spec-kit-spec-reference-loader/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.6.0"
},
@@ -2732,6 +2894,8 @@
"documentation": "https://github.com/aeltayeb/spec-kit-spec-validate/blob/main/README.md",
"changelog": "https://github.com/aeltayeb/spec-kit-spec-validate/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.5.0"
},
@@ -2764,6 +2928,8 @@
"documentation": "https://github.com/Azure-Samples/Spec2Cloud/blob/main/spec-kit/README.md",
"changelog": "https://github.com/Azure-Samples/Spec2Cloud/blob/main/spec-kit/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -2796,6 +2962,8 @@
"documentation": "https://github.com/lihan3238/speckit-superpowers-bridge#readme",
"changelog": "https://github.com/lihan3238/speckit-superpowers-bridge/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.10",
"tools": [
@@ -2845,6 +3013,8 @@
"documentation": "https://github.com/mvanhorn/speckit-utils/blob/main/README.md",
"changelog": "https://github.com/mvanhorn/speckit-utils/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2877,6 +3047,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-spectest/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-spectest/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -2910,6 +3082,8 @@
"documentation": "https://github.com/jwill824/spec-kit-squad/blob/main/README.md",
"changelog": "https://github.com/jwill824/spec-kit-squad/blob/main/docs/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.8.11",
"tools": [
@@ -2949,6 +3123,8 @@
"documentation": "https://github.com/arunt14/spec-kit-staff-review/blob/main/README.md",
"changelog": "https://github.com/arunt14/spec-kit-staff-review/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -2979,6 +3155,8 @@
"documentation": "https://github.com/KhawarHabibKhan/spec-kit-status/blob/main/README.md",
"changelog": "https://github.com/KhawarHabibKhan/spec-kit-status/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3011,6 +3189,8 @@
"documentation": "https://github.com/Open-Agent-Tools/spec-kit-status/blob/main/README.md",
"changelog": "https://github.com/Open-Agent-Tools/spec-kit-status/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3041,6 +3221,8 @@
"documentation": "https://github.com/RbBtSn0w/spec-kit-extensions/blob/main/superpowers-bridge/README.md",
"changelog": "https://github.com/RbBtSn0w/spec-kit-extensions/blob/main/superpowers-bridge/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.3",
"tools": [
@@ -3084,6 +3266,8 @@
"documentation": "https://github.com/WangX0111/superspec/blob/main/README.md",
"changelog": "https://github.com/WangX0111/superspec/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3117,6 +3301,8 @@
"documentation": "https://github.com/bgervin/spec-kit-sync/blob/main/README.md",
"changelog": "https://github.com/bgervin/spec-kit-sync/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3149,6 +3335,8 @@
"documentation": "https://github.com/tarunkumarbhati/spec-kit-team-assign/blob/main/README.md",
"changelog": "https://github.com/tarunkumarbhati/spec-kit-team-assign/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3180,6 +3368,8 @@
"documentation": "https://github.com/teeyo/spec-kit-time-machine",
"changelog": "https://github.com/teeyo/spec-kit-time-machine/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -3217,6 +3407,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-tinyspec/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-tinyspec/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -3249,6 +3441,8 @@
"documentation": "https://github.com/NaviaSamal/spec-kit-threatmodel/blob/main/README.md",
"changelog": "https://github.com/NaviaSamal/spec-kit-threatmodel/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.6.0"
},
@@ -3281,6 +3475,8 @@
"documentation": "https://github.com/coderandhiker/spec-kit-token-analyzer/blob/main/README.md",
"changelog": "https://github.com/coderandhiker/spec-kit-token-analyzer/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -3312,6 +3508,8 @@
"documentation": "https://github.com/tinesoft/spec-kit-token-budget/blob/main/README.md",
"changelog": "https://github.com/tinesoft/spec-kit-token-budget/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -3354,6 +3552,8 @@
"documentation": "https://github.com/leocamello/spec-kit-v-model/blob/main/README.md",
"changelog": "https://github.com/leocamello/spec-kit-v-model/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "docs",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3386,6 +3586,8 @@
"documentation": "https://github.com/ismaelJimenez/spec-kit-verify/blob/main/README.md",
"changelog": "https://github.com/ismaelJimenez/spec-kit-verify/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3418,6 +3620,8 @@
"documentation": "https://github.com/datastone-inc/spec-kit-verify-tasks/blob/main/README.md",
"changelog": "https://github.com/datastone-inc/spec-kit-verify-tasks/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "code",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0"
},
@@ -3449,6 +3653,8 @@
"documentation": "https://github.com/KevinBrown5280/spec-kit-version-guard/blob/main/README.md",
"changelog": "https://github.com/KevinBrown5280/spec-kit-version-guard/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.2.0"
},
@@ -3478,6 +3684,8 @@
"homepage": "https://github.com/DevAbdullah90/spec-kit-whatif",
"documentation": "https://github.com/DevAbdullah90/spec-kit-whatif/blob/main/README.md",
"license": "MIT",
"category": "visibility",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.6.0"
},
@@ -3508,6 +3716,8 @@
"documentation": "https://github.com/TortoiseWolfe/spec-kit-extension-wireframe/blob/main/README.md",
"changelog": "https://github.com/TortoiseWolfe/spec-kit-extension-wireframe/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "visibility",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.6.0"
},
@@ -3543,6 +3753,8 @@
"documentation": "https://github.com/sakitA/spec-kit-workiq/blob/main/README.md",
"changelog": "https://github.com/sakitA/spec-kit-workiq/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "integration",
"effect": "read-only",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
@@ -3587,6 +3799,8 @@
"documentation": "https://github.com/Quratulain-bilal/spec-kit-worktree/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-worktree/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},
@@ -3619,6 +3833,8 @@
"documentation": "https://github.com/dango85/spec-kit-worktree-parallel/blob/main/README.md",
"changelog": "https://github.com/dango85/spec-kit-worktree-parallel/blob/main/CHANGELOG.md",
"license": "MIT",
"category": "process",
"effect": "read-write",
"requires": {
"speckit_version": ">=0.4.0"
},

View File

@@ -13,6 +13,14 @@ extension:
# CUSTOMIZE: Brief description (under 200 characters)
description: "Brief description of what your extension does"
# CUSTOMIZE: Extension category — describes what the extension operates on
# Common values: docs, code, process, integration, visibility
# category: "process"
# CUSTOMIZE: Extension effect — whether it modifies project files
# One of: read-only | read-write
# effect: "read-write"
# CUSTOMIZE: Your name or organization name
author: "Your Name"

View File

@@ -1975,7 +1975,11 @@ def extension_info(
author = ext_manifest.data.get("extension", {}).get("author")
if author:
console.print(f"[dim]Author:[/dim] {author}")
console.print()
if ext_manifest.category:
console.print(f"[dim]Category:[/dim] {ext_manifest.category}")
if ext_manifest.effect:
console.print(f"[dim]Effect:[/dim] {ext_manifest.effect}")
console.print()
if ext_manifest.commands:
console.print("[bold]Commands:[/bold]")
@@ -2025,6 +2029,12 @@ def _print_extension_info(ext_info: dict, manager):
console.print(f"[dim]Author:[/dim] {ext_info.get('author', 'Unknown')}")
console.print(f"[dim]License:[/dim] {ext_info.get('license', 'Unknown')}")
# Category and Effect
if ext_info.get('category'):
console.print(f"[dim]Category:[/dim] {ext_info['category']}")
if ext_info.get('effect'):
console.print(f"[dim]Effect:[/dim] {ext_info['effect']}")
# Source catalog
if ext_info.get("_catalog_name"):
install_allowed = ext_info.get("_install_allowed", True)

View File

@@ -41,6 +41,8 @@ _FALLBACK_CORE_COMMAND_NAMES = frozenset({
})
EXTENSION_COMMAND_NAME_PATTERN = re.compile(r"^speckit\.([a-z0-9-]+)\.([a-z0-9-]+)$")
VALID_EFFECTS = frozenset({"read-only", "read-write"})
DEFAULT_HOOK_PRIORITY = 10
REINSTALL_COMMAND = "uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git"
@@ -201,6 +203,21 @@ class ExtensionManifest:
except pkg_version.InvalidVersion:
raise ValidationError(f"Invalid version: {ext['version']}")
# Validate optional category field (free-form string)
if "category" in ext:
if not isinstance(ext["category"], str) or not ext["category"].strip():
raise ValidationError(
"Invalid extension.category: must be a non-empty string"
)
# Validate optional effect field
if "effect" in ext:
if not isinstance(ext["effect"], str) or ext["effect"] not in VALID_EFFECTS:
raise ValidationError(
f"Invalid extension.effect '{ext.get('effect')}': "
f"must be one of {sorted(VALID_EFFECTS)}"
)
# Validate requires section
requires = self.data["requires"]
if "speckit_version" not in requires:
@@ -374,6 +391,16 @@ class ExtensionManifest:
"""Get extension description."""
return self.data["extension"]["description"]
@property
def category(self) -> Optional[str]:
"""Get extension category (free-form; common values: docs, code, process, integration, visibility)."""
return self.data["extension"].get("category")
@property
def effect(self) -> Optional[str]:
"""Get extension effect (read-only, read-write)."""
return self.data["extension"].get("effect")
@property
def requires_speckit_version(self) -> str:
"""Get required spec-kit version range."""

View File

@@ -24,6 +24,7 @@ from specify_cli.extensions import (
CatalogEntry,
CORE_COMMAND_NAMES,
DEFAULT_HOOK_PRIORITY,
VALID_EFFECTS,
ExtensionManifest,
ExtensionRegistry,
ExtensionManager,
@@ -300,6 +301,69 @@ class TestExtensionManifest:
with pytest.raises(ValidationError, match="Invalid version"):
ExtensionManifest(manifest_path)
def test_valid_category(self, temp_dir, valid_manifest_data):
"""Test manifest with various category values (free-form string)."""
import yaml
for category in ("docs", "code", "process", "integration", "visibility", "custom-category"):
valid_manifest_data["extension"]["category"] = category
manifest_path = temp_dir / "extension.yml"
with open(manifest_path, 'w') as f:
yaml.dump(valid_manifest_data, f)
manifest = ExtensionManifest(manifest_path)
assert manifest.category == category
def test_valid_effect(self, temp_dir, valid_manifest_data):
"""Test manifest with valid effect values."""
import yaml
for effect in sorted(VALID_EFFECTS):
valid_manifest_data["extension"]["effect"] = effect
manifest_path = temp_dir / "extension.yml"
with open(manifest_path, 'w') as f:
yaml.dump(valid_manifest_data, f)
manifest = ExtensionManifest(manifest_path)
assert manifest.effect == effect
def test_invalid_category(self, temp_dir, valid_manifest_data):
"""Test manifest with empty category raises ValidationError."""
import yaml
valid_manifest_data["extension"]["category"] = ""
manifest_path = temp_dir / "extension.yml"
with open(manifest_path, 'w') as f:
yaml.dump(valid_manifest_data, f)
with pytest.raises(ValidationError, match="Invalid extension.category"):
ExtensionManifest(manifest_path)
def test_invalid_effect(self, temp_dir, valid_manifest_data):
"""Test manifest with invalid effect raises ValidationError."""
import yaml
valid_manifest_data["extension"]["effect"] = "write-only"
manifest_path = temp_dir / "extension.yml"
with open(manifest_path, 'w') as f:
yaml.dump(valid_manifest_data, f)
with pytest.raises(ValidationError, match="Invalid extension.effect"):
ExtensionManifest(manifest_path)
def test_category_and_effect_optional(self, temp_dir, valid_manifest_data):
"""Test that omitting category and effect still passes validation."""
import yaml
# Ensure no category/effect in data
valid_manifest_data["extension"].pop("category", None)
valid_manifest_data["extension"].pop("effect", None)
manifest_path = temp_dir / "extension.yml"
with open(manifest_path, 'w') as f:
yaml.dump(valid_manifest_data, f)
manifest = ExtensionManifest(manifest_path)
assert manifest.category is None
assert manifest.effect is None
def test_invalid_command_name(self, temp_dir, valid_manifest_data):
"""Test manifest with command name that cannot be auto-corrected raises ValidationError."""
import yaml