mirror of
https://github.com/github/spec-kit.git
synced 2026-03-17 19:03:08 +00:00
* feat(extensions,presets): add priority-based resolution ordering Add priority field to extension and preset registries for deterministic template resolution when multiple sources provide the same template. Extensions: - Add `list_by_priority()` method to ExtensionRegistry - Add `--priority` option to `extension add` command - Add `extension set-priority` command - Show priority in `extension list` and `extension info` - Preserve priority during `extension update` - Update RFC documentation Presets: - Add `preset set-priority` command - Show priority in `preset info` output - Use priority ordering in PresetResolver for extensions Both systems: - Lower priority number = higher precedence (default: 10) - Backwards compatible with legacy entries (missing priority defaults to 10) - Comprehensive test coverage including backwards compatibility Closes #1845 Closes #1854 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address code review feedback - list_by_priority(): add secondary sort by ID for deterministic ordering, return deep copies to prevent mutation - install_from_directory/zip: validate priority >= 1 early - extension add CLI: validate --priority >= 1 before install - PresetRegistry.update(): preserve installed_at timestamp - Test assertions: use exact source string instead of substring match Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address additional review feedback - PresetResolver: add fallback to directory scanning when registry is empty/corrupted for robustness and backwards compatibility - PresetRegistry.update(): add guard to prevent injecting installed_at when absent in existing entry (mirrors ExtensionRegistry behavior) - RFC: update extension list example to match actual CLI output format Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: restore defensive code and RFC descriptions lost in rebase - Restore defensive code in list_by_priority() with .get() and isinstance check - Restore detailed --from URL and --dev option descriptions in RFC Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add defensive code to presets list_by_priority() - Add .get() and isinstance check for corrupted/empty registry - Move copy import to module level (remove local import) - Matches defensive pattern used in extensions.py Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address reviewer feedback on priority resolution - Rename _normalize_priority to normalize_priority (public API) - Add comprehensive tests for normalize_priority function (9 tests) - Filter non-dict metadata entries in list_by_priority() methods - Fix extension priority resolution to merge registered and unregistered extensions into unified sorted list (unregistered get implicit priority 10) - Add tests for extension priority resolution ordering (4 tests) The key fix ensures unregistered extensions with implicit priority 10 correctly beat registered extensions with priority > 10, and vice versa. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: DRY refactor and strengthen test assertions - Extract _get_all_extensions_by_priority() helper in PresetResolver to eliminate duplicated extension list construction - Add priority=10 assertion to test_legacy_extension_without_priority_field - Add priority=10 assertion to test_legacy_preset_without_priority_field Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add isinstance(dict) checks for corrupted registry entries Add defensive checks throughout CLI commands and manager methods to handle cases where registry entries may be corrupted (non-dict values). This prevents AttributeError when calling .get() on non-dict metadata. Locations fixed: - __init__.py: preset/extension info, set-priority, enable/disable, upgrade commands - extensions.py: list_installed() - presets.py: list_installed() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: normalize priority display to match resolution behavior Use normalize_priority() for all priority display in CLI commands to ensure displayed values match actual resolution behavior when registry data is corrupted/hand-edited. Locations fixed: - extensions.py: list_installed() - presets.py: list_installed(), PresetResolver - __init__.py: preset info, extension info, set-priority commands Also added GraphQL query for unresolved PR comments to CLAUDE.md. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: repair corrupted priority values in set-priority commands Changed set-priority commands to check if the raw stored value is already a valid int equal to the requested priority before skipping. This ensures corrupted values (e.g., "high") get repaired even when setting to the default priority (10). Also removed CLAUDE.md that was accidentally added to the repo. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: harden registry update methods against corrupted entries - Normalize priority when restoring during extension update to prevent propagating corrupted values (e.g., "high", 0, negative) - Add isinstance(dict) checks in ExtensionRegistry.update() and PresetRegistry.update() to handle corrupted entries (string/list) that would cause TypeError on merge Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use safe fallback for version in list_installed() When registry entry is corrupted (non-dict), metadata becomes {} after the isinstance check. Use metadata.get("version", manifest.version) instead of metadata["version"] to avoid KeyError. 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>
1963 lines
60 KiB
Markdown
1963 lines
60 KiB
Markdown
# RFC: Spec Kit Extension System
|
|
|
|
**Status**: Implemented
|
|
**Author**: Stats Perform Engineering
|
|
**Created**: 2026-01-28
|
|
**Updated**: 2026-03-11
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Summary](#summary)
|
|
2. [Motivation](#motivation)
|
|
3. [Design Principles](#design-principles)
|
|
4. [Architecture Overview](#architecture-overview)
|
|
5. [Extension Manifest Specification](#extension-manifest-specification)
|
|
6. [Extension Lifecycle](#extension-lifecycle)
|
|
7. [Command Registration](#command-registration)
|
|
8. [Configuration Management](#configuration-management)
|
|
9. [Hook System](#hook-system)
|
|
10. [Extension Discovery & Catalog](#extension-discovery--catalog)
|
|
11. [CLI Commands](#cli-commands)
|
|
12. [Compatibility & Versioning](#compatibility--versioning)
|
|
13. [Security Considerations](#security-considerations)
|
|
14. [Migration Strategy](#migration-strategy)
|
|
15. [Implementation Phases](#implementation-phases)
|
|
16. [Resolved Questions](#resolved-questions)
|
|
17. [Open Questions (Remaining)](#open-questions-remaining)
|
|
18. [Appendices](#appendices)
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
Introduce an extension system to Spec Kit that allows modular integration with external tools (Jira, Linear, Azure DevOps, etc.) without bloating the core framework. Extensions are self-contained packages installed into `.specify/extensions/` with declarative manifests, versioned independently, and discoverable through a central catalog.
|
|
|
|
---
|
|
|
|
## Motivation
|
|
|
|
### Current Problems
|
|
|
|
1. **Monolithic Growth**: Adding Jira integration to core spec-kit creates:
|
|
- Large configuration files affecting all users
|
|
- Dependencies on Jira MCP server for everyone
|
|
- Merge conflicts as features accumulate
|
|
|
|
2. **Limited Flexibility**: Different organizations use different tools:
|
|
- GitHub Issues vs Jira vs Linear vs Azure DevOps
|
|
- Custom internal tools
|
|
- No way to support all without bloat
|
|
|
|
3. **Maintenance Burden**: Every integration adds:
|
|
- Documentation complexity
|
|
- Testing matrix expansion
|
|
- Breaking change surface area
|
|
|
|
4. **Community Friction**: External contributors can't easily add integrations without core repo PR approval and release cycles.
|
|
|
|
### Goals
|
|
|
|
1. **Modularity**: Core spec-kit remains lean, extensions are opt-in
|
|
2. **Extensibility**: Clear API for building new integrations
|
|
3. **Independence**: Extensions version/release separately from core
|
|
4. **Discoverability**: Central catalog for finding extensions
|
|
5. **Safety**: Validation, compatibility checks, sandboxing
|
|
|
|
---
|
|
|
|
## Design Principles
|
|
|
|
### 1. Convention Over Configuration
|
|
|
|
- Standard directory structure (`.specify/extensions/{name}/`)
|
|
- Declarative manifest (`extension.yml`)
|
|
- Predictable command naming (`speckit.{extension}.{command}`)
|
|
|
|
### 2. Fail-Safe Defaults
|
|
|
|
- Missing extensions gracefully degrade (skip hooks)
|
|
- Invalid extensions warn but don't break core functionality
|
|
- Extension failures isolated from core operations
|
|
|
|
### 3. Backward Compatibility
|
|
|
|
- Core commands remain unchanged
|
|
- Extensions additive only (no core modifications)
|
|
- Old projects work without extensions
|
|
|
|
### 4. Developer Experience
|
|
|
|
- Simple installation: `specify extension add jira`
|
|
- Clear error messages for compatibility issues
|
|
- Local development mode for testing extensions
|
|
|
|
### 5. Security First
|
|
|
|
- Extensions run in same context as AI agent (trust boundary)
|
|
- Manifest validation prevents malicious code
|
|
- Verify signatures for official extensions (future)
|
|
|
|
---
|
|
|
|
## Architecture Overview
|
|
|
|
### Directory Structure
|
|
|
|
```text
|
|
project/
|
|
├── .specify/
|
|
│ ├── scripts/ # Core scripts (unchanged)
|
|
│ ├── templates/ # Core templates (unchanged)
|
|
│ ├── memory/ # Session memory
|
|
│ ├── extensions/ # Extensions directory (NEW)
|
|
│ │ ├── .registry # Installed extensions metadata (NEW)
|
|
│ │ ├── jira/ # Jira extension
|
|
│ │ │ ├── extension.yml # Manifest
|
|
│ │ │ ├── jira-config.yml # Extension config
|
|
│ │ │ ├── commands/ # Command files
|
|
│ │ │ ├── scripts/ # Helper scripts
|
|
│ │ │ └── docs/ # Documentation
|
|
│ │ └── linear/ # Linear extension (example)
|
|
│ └── extensions.yml # Project extension configuration (NEW)
|
|
└── .gitignore # Ignore local extension configs
|
|
```
|
|
|
|
### Component Diagram
|
|
|
|
```text
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Spec Kit Core │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ CLI (specify) │ │
|
|
│ │ - init, check │ │
|
|
│ │ - extension add/remove/list/update ← NEW │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ Extension Manager ← NEW │ │
|
|
│ │ - Discovery, Installation, Validation │ │
|
|
│ │ - Command Registration, Hook Execution │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ Core Commands │ │
|
|
│ │ - /speckit.specify │ │
|
|
│ │ - /speckit.tasks │ │
|
|
│ │ - /speckit.implement │ │
|
|
│ └─────────┬────────────────────────────────────────┘ │
|
|
└────────────┼────────────────────────────────────────────┘
|
|
│ Hook Points (after_tasks, after_implement)
|
|
↓
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Extensions │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ Jira Extension │ │
|
|
│ │ - /speckit.jira.specstoissues │ │
|
|
│ │ - /speckit.jira.discover-fields │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ Linear Extension │ │
|
|
│ │ - /speckit.linear.sync │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│ Calls external tools
|
|
↓
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ External Tools │
|
|
│ - Jira MCP Server │
|
|
│ - Linear API │
|
|
│ - GitHub API │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Extension Manifest Specification
|
|
|
|
### Schema: `extension.yml`
|
|
|
|
```yaml
|
|
# Extension Manifest Schema v1.0
|
|
# All extensions MUST include this file at root
|
|
|
|
# Schema version for compatibility
|
|
schema_version: "1.0"
|
|
|
|
# Extension metadata (REQUIRED)
|
|
extension:
|
|
id: "jira" # Unique identifier (lowercase, alphanumeric, hyphens)
|
|
name: "Jira Integration" # Human-readable name
|
|
version: "1.0.0" # Semantic version
|
|
description: "Create Jira Epics, Stories, and Issues from spec-kit artifacts"
|
|
author: "Stats Perform" # Author/organization
|
|
repository: "https://github.com/statsperform/spec-kit-jira"
|
|
license: "MIT" # SPDX license identifier
|
|
homepage: "https://github.com/statsperform/spec-kit-jira/blob/main/README.md"
|
|
|
|
# Compatibility requirements (REQUIRED)
|
|
requires:
|
|
# Spec-kit version (semantic version range)
|
|
speckit_version: ">=0.1.0,<2.0.0"
|
|
|
|
# External tools required by extension
|
|
tools:
|
|
- name: "jira-mcp-server"
|
|
required: true
|
|
version: ">=1.0.0" # Optional: version constraint
|
|
description: "Jira MCP server for API access"
|
|
install_url: "https://github.com/your-org/jira-mcp-server"
|
|
check_command: "jira --version" # Optional: CLI command to verify
|
|
|
|
# Core spec-kit commands this extension depends on
|
|
commands:
|
|
- "speckit.tasks" # Extension needs tasks command
|
|
|
|
# Core scripts required
|
|
scripts:
|
|
- "check-prerequisites.sh"
|
|
|
|
# What this extension provides (REQUIRED)
|
|
provides:
|
|
# Commands added to AI agent
|
|
commands:
|
|
- name: "speckit.jira.specstoissues"
|
|
file: "commands/specstoissues.md"
|
|
description: "Create Jira hierarchy from spec and tasks"
|
|
aliases: ["speckit.specstoissues"] # Alternate names
|
|
|
|
- name: "speckit.jira.discover-fields"
|
|
file: "commands/discover-fields.md"
|
|
description: "Discover Jira custom fields for configuration"
|
|
|
|
- name: "speckit.jira.sync-status"
|
|
file: "commands/sync-status.md"
|
|
description: "Sync task completion status to Jira"
|
|
|
|
# Configuration files
|
|
config:
|
|
- name: "jira-config.yml"
|
|
template: "jira-config.template.yml"
|
|
description: "Jira integration configuration"
|
|
required: true # User must configure before use
|
|
|
|
# Helper scripts
|
|
scripts:
|
|
- name: "parse-jira-config.sh"
|
|
file: "scripts/parse-jira-config.sh"
|
|
description: "Parse jira-config.yml to JSON"
|
|
executable: true # Make executable on install
|
|
|
|
# Extension configuration defaults (OPTIONAL)
|
|
defaults:
|
|
project:
|
|
key: null # No default, user must configure
|
|
hierarchy:
|
|
issue_type: "subtask"
|
|
update_behavior:
|
|
mode: "update"
|
|
sync_completion: true
|
|
|
|
# Configuration schema for validation (OPTIONAL)
|
|
config_schema:
|
|
type: "object"
|
|
required: ["project"]
|
|
properties:
|
|
project:
|
|
type: "object"
|
|
required: ["key"]
|
|
properties:
|
|
key:
|
|
type: "string"
|
|
pattern: "^[A-Z]{2,10}$"
|
|
description: "Jira project key (e.g., MSATS)"
|
|
|
|
# Integration hooks (OPTIONAL)
|
|
hooks:
|
|
# Hook fired after /speckit.tasks completes
|
|
after_tasks:
|
|
command: "speckit.jira.specstoissues"
|
|
optional: true
|
|
prompt: "Create Jira issues from tasks?"
|
|
description: "Automatically create Jira hierarchy after task generation"
|
|
|
|
# Hook fired after /speckit.implement completes
|
|
after_implement:
|
|
command: "speckit.jira.sync-status"
|
|
optional: true
|
|
prompt: "Sync completion status to Jira?"
|
|
|
|
# Tags for discovery (OPTIONAL)
|
|
tags:
|
|
- "issue-tracking"
|
|
- "jira"
|
|
- "atlassian"
|
|
- "project-management"
|
|
|
|
# Changelog URL (OPTIONAL)
|
|
changelog: "https://github.com/statsperform/spec-kit-jira/blob/main/CHANGELOG.md"
|
|
|
|
# Support information (OPTIONAL)
|
|
support:
|
|
documentation: "https://github.com/statsperform/spec-kit-jira/blob/main/docs/"
|
|
issues: "https://github.com/statsperform/spec-kit-jira/issues"
|
|
discussions: "https://github.com/statsperform/spec-kit-jira/discussions"
|
|
email: "support@statsperform.com"
|
|
```
|
|
|
|
### Validation Rules
|
|
|
|
1. **MUST have** `schema_version`, `extension`, `requires`, `provides`
|
|
2. **MUST follow** semantic versioning for `version`
|
|
3. **MUST have** unique `id` (no conflicts with other extensions)
|
|
4. **MUST declare** all external tool dependencies
|
|
5. **SHOULD include** `config_schema` if extension uses config
|
|
6. **SHOULD include** `support` information
|
|
7. Command `file` paths **MUST be** relative to extension root
|
|
8. Hook `command` names **MUST match** a command in `provides.commands`
|
|
|
|
---
|
|
|
|
## Extension Lifecycle
|
|
|
|
### 1. Discovery
|
|
|
|
```bash
|
|
specify extension search jira
|
|
# Searches catalog for extensions matching "jira"
|
|
```
|
|
|
|
**Process:**
|
|
|
|
1. Fetch extension catalog from GitHub
|
|
2. Filter by search term (name, tags, description)
|
|
3. Display results with metadata
|
|
|
|
### 2. Installation
|
|
|
|
```bash
|
|
specify extension add jira
|
|
```
|
|
|
|
**Process:**
|
|
|
|
1. **Resolve**: Look up extension in catalog
|
|
2. **Download**: Fetch extension package (ZIP from GitHub release)
|
|
3. **Validate**: Check manifest schema, compatibility
|
|
4. **Extract**: Unpack to `.specify/extensions/jira/`
|
|
5. **Configure**: Copy config templates
|
|
6. **Register**: Add commands to AI agent config
|
|
7. **Record**: Update `.specify/extensions/.registry`
|
|
|
|
**Registry Format** (`.specify/extensions/.registry`):
|
|
|
|
```json
|
|
{
|
|
"schema_version": "1.0",
|
|
"extensions": {
|
|
"jira": {
|
|
"version": "1.0.0",
|
|
"installed_at": "2026-01-28T14:30:00Z",
|
|
"source": "catalog",
|
|
"manifest_hash": "sha256:abc123...",
|
|
"enabled": true,
|
|
"priority": 10
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Priority Field**: Extensions are ordered by `priority` (lower = higher precedence). Default is 10. Used for template resolution when multiple extensions provide the same template.
|
|
|
|
### 3. Configuration
|
|
|
|
```bash
|
|
# User edits extension config
|
|
vim .specify/extensions/jira/jira-config.yml
|
|
```
|
|
|
|
**Config discovery order:**
|
|
|
|
1. Extension defaults (`extension.yml` → `defaults`)
|
|
2. Project config (`jira-config.yml`)
|
|
3. Local overrides (`jira-config.local.yml` - gitignored)
|
|
4. Environment variables (`SPECKIT_JIRA_*`)
|
|
|
|
### 4. Usage
|
|
|
|
```bash
|
|
claude
|
|
> /speckit.jira.specstoissues
|
|
```
|
|
|
|
**Command resolution:**
|
|
|
|
1. AI agent finds command in `.claude/commands/speckit.jira.specstoissues.md`
|
|
2. Command file references extension scripts/config
|
|
3. Extension executes with full context
|
|
|
|
### 5. Update
|
|
|
|
```bash
|
|
specify extension update jira
|
|
```
|
|
|
|
**Process:**
|
|
|
|
1. Check catalog for newer version
|
|
2. Download new version
|
|
3. Validate compatibility
|
|
4. Back up current config
|
|
5. Extract new version (preserve config)
|
|
6. Re-register commands
|
|
7. Update registry
|
|
|
|
### 6. Removal
|
|
|
|
```bash
|
|
specify extension remove jira
|
|
```
|
|
|
|
**Process:**
|
|
|
|
1. Confirm with user (show what will be removed)
|
|
2. Unregister commands from AI agent
|
|
3. Remove from `.specify/extensions/jira/`
|
|
4. Update registry
|
|
5. Optionally preserve config for reinstall
|
|
|
|
---
|
|
|
|
## Command Registration
|
|
|
|
### Per-Agent Registration
|
|
|
|
Extensions provide **universal command format** (Markdown-based), and CLI converts to agent-specific format during registration.
|
|
|
|
#### Universal Command Format
|
|
|
|
**Location**: Extension's `commands/specstoissues.md`
|
|
|
|
```markdown
|
|
---
|
|
# Universal metadata (parsed by all agents)
|
|
description: "Create Jira hierarchy from spec and tasks"
|
|
tools:
|
|
- 'jira-mcp-server/epic_create'
|
|
- 'jira-mcp-server/story_create'
|
|
scripts:
|
|
sh: ../../scripts/bash/check-prerequisites.sh --json
|
|
ps: ../../scripts/powershell/check-prerequisites.ps1 -Json
|
|
---
|
|
|
|
# Command implementation
|
|
## User Input
|
|
$ARGUMENTS
|
|
|
|
## Steps
|
|
1. Load jira-config.yml
|
|
2. Parse spec.md and tasks.md
|
|
3. Create Jira items
|
|
```
|
|
|
|
#### Claude Code Registration
|
|
|
|
**Output**: `.claude/commands/speckit.jira.specstoissues.md`
|
|
|
|
```markdown
|
|
---
|
|
description: "Create Jira hierarchy from spec and tasks"
|
|
tools:
|
|
- 'jira-mcp-server/epic_create'
|
|
- 'jira-mcp-server/story_create'
|
|
scripts:
|
|
sh: .specify/scripts/bash/check-prerequisites.sh --json
|
|
ps: .specify/scripts/powershell/check-prerequisites.ps1 -Json
|
|
---
|
|
|
|
# Command implementation (copied from extension)
|
|
## User Input
|
|
$ARGUMENTS
|
|
|
|
## Steps
|
|
1. Load jira-config.yml from .specify/extensions/jira/
|
|
2. Parse spec.md and tasks.md
|
|
3. Create Jira items
|
|
```
|
|
|
|
**Transformation:**
|
|
|
|
- Copy frontmatter with adjustments
|
|
- Rewrite script paths (relative to repo root)
|
|
- Add extension context (config location)
|
|
|
|
#### Gemini CLI Registration
|
|
|
|
**Output**: `.gemini/commands/speckit.jira.specstoissues.toml`
|
|
|
|
```toml
|
|
[command]
|
|
name = "speckit.jira.specstoissues"
|
|
description = "Create Jira hierarchy from spec and tasks"
|
|
|
|
[command.tools]
|
|
tools = [
|
|
"jira-mcp-server/epic_create",
|
|
"jira-mcp-server/story_create"
|
|
]
|
|
|
|
[command.script]
|
|
sh = ".specify/scripts/bash/check-prerequisites.sh --json"
|
|
ps = ".specify/scripts/powershell/check-prerequisites.ps1 -Json"
|
|
|
|
[command.template]
|
|
content = """
|
|
# Command implementation
|
|
## User Input
|
|
{{args}}
|
|
|
|
## Steps
|
|
1. Load jira-config.yml from .specify/extensions/jira/
|
|
2. Parse spec.md and tasks.md
|
|
3. Create Jira items
|
|
"""
|
|
```
|
|
|
|
**Transformation:**
|
|
|
|
- Convert Markdown frontmatter to TOML
|
|
- Convert `$ARGUMENTS` to `{{args}}`
|
|
- Rewrite script paths
|
|
|
|
### Registration Code
|
|
|
|
**Location**: `src/specify_cli/extensions.py`
|
|
|
|
```python
|
|
def register_extension_commands(
|
|
project_path: Path,
|
|
ai_assistant: str,
|
|
manifest: dict
|
|
) -> None:
|
|
"""Register extension commands with AI agent."""
|
|
|
|
agent_config = AGENT_CONFIG.get(ai_assistant)
|
|
if not agent_config:
|
|
console.print(f"[yellow]Unknown agent: {ai_assistant}[/yellow]")
|
|
return
|
|
|
|
ext_id = manifest['extension']['id']
|
|
ext_dir = project_path / ".specify" / "extensions" / ext_id
|
|
agent_commands_dir = project_path / agent_config['folder'].rstrip('/') / "commands"
|
|
agent_commands_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
for cmd_info in manifest['provides']['commands']:
|
|
cmd_name = cmd_info['name']
|
|
source_file = ext_dir / cmd_info['file']
|
|
|
|
if not source_file.exists():
|
|
console.print(f"[red]Command file not found:[/red] {cmd_info['file']}")
|
|
continue
|
|
|
|
# Convert to agent-specific format
|
|
if ai_assistant == "claude":
|
|
dest_file = agent_commands_dir / f"{cmd_name}.md"
|
|
convert_to_claude(source_file, dest_file, ext_dir)
|
|
elif ai_assistant == "gemini":
|
|
dest_file = agent_commands_dir / f"{cmd_name}.toml"
|
|
convert_to_gemini(source_file, dest_file, ext_dir)
|
|
elif ai_assistant == "copilot":
|
|
dest_file = agent_commands_dir / f"{cmd_name}.md"
|
|
convert_to_copilot(source_file, dest_file, ext_dir)
|
|
# ... other agents
|
|
|
|
console.print(f" ✓ Registered: {cmd_name}")
|
|
|
|
def convert_to_claude(
|
|
source: Path,
|
|
dest: Path,
|
|
ext_dir: Path
|
|
) -> None:
|
|
"""Convert universal command to Claude format."""
|
|
|
|
# Parse universal command
|
|
content = source.read_text()
|
|
frontmatter, body = parse_frontmatter(content)
|
|
|
|
# Adjust script paths (relative to repo root)
|
|
if 'scripts' in frontmatter:
|
|
for key in frontmatter['scripts']:
|
|
frontmatter['scripts'][key] = adjust_path_for_repo_root(
|
|
frontmatter['scripts'][key]
|
|
)
|
|
|
|
# Inject extension context
|
|
body = inject_extension_context(body, ext_dir)
|
|
|
|
# Write Claude command
|
|
dest.write_text(render_frontmatter(frontmatter) + "\n" + body)
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Management
|
|
|
|
### Configuration File Hierarchy
|
|
|
|
```yaml
|
|
# .specify/extensions/jira/jira-config.yml (Project config)
|
|
project:
|
|
key: "MSATS"
|
|
|
|
hierarchy:
|
|
issue_type: "subtask"
|
|
|
|
defaults:
|
|
epic:
|
|
labels: ["spec-driven", "typescript"]
|
|
```
|
|
|
|
```yaml
|
|
# .specify/extensions/jira/jira-config.local.yml (Local overrides - gitignored)
|
|
project:
|
|
key: "MYTEST" # Override for local testing
|
|
```
|
|
|
|
```bash
|
|
# Environment variables (highest precedence)
|
|
export SPECKIT_JIRA_PROJECT_KEY="DEVTEST"
|
|
```
|
|
|
|
### Config Loading Function
|
|
|
|
**Location**: Extension command (e.g., `commands/specstoissues.md`)
|
|
|
|
````markdown
|
|
## Load Configuration
|
|
|
|
1. Run helper script to load and merge config:
|
|
|
|
```bash
|
|
config_json=$(bash .specify/extensions/jira/scripts/parse-jira-config.sh)
|
|
echo "$config_json"
|
|
```
|
|
|
|
1. Parse JSON and use in subsequent steps
|
|
````
|
|
|
|
**Script**: `.specify/extensions/jira/scripts/parse-jira-config.sh`
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
EXT_DIR=".specify/extensions/jira"
|
|
CONFIG_FILE="$EXT_DIR/jira-config.yml"
|
|
LOCAL_CONFIG="$EXT_DIR/jira-config.local.yml"
|
|
|
|
# Start with defaults from extension.yml
|
|
defaults=$(yq eval '.defaults' "$EXT_DIR/extension.yml" -o=json)
|
|
|
|
# Merge project config
|
|
if [ -f "$CONFIG_FILE" ]; then
|
|
project_config=$(yq eval '.' "$CONFIG_FILE" -o=json)
|
|
defaults=$(echo "$defaults $project_config" | jq -s '.[0] * .[1]')
|
|
fi
|
|
|
|
# Merge local config
|
|
if [ -f "$LOCAL_CONFIG" ]; then
|
|
local_config=$(yq eval '.' "$LOCAL_CONFIG" -o=json)
|
|
defaults=$(echo "$defaults $local_config" | jq -s '.[0] * .[1]')
|
|
fi
|
|
|
|
# Apply environment variable overrides
|
|
if [ -n "${SPECKIT_JIRA_PROJECT_KEY:-}" ]; then
|
|
defaults=$(echo "$defaults" | jq ".project.key = \"$SPECKIT_JIRA_PROJECT_KEY\"")
|
|
fi
|
|
|
|
# Output merged config as JSON
|
|
echo "$defaults"
|
|
```
|
|
|
|
### Config Validation
|
|
|
|
**In command file**:
|
|
|
|
````markdown
|
|
## Validate Configuration
|
|
|
|
1. Load config (from previous step)
|
|
2. Validate against schema from extension.yml:
|
|
|
|
```python
|
|
import jsonschema
|
|
|
|
schema = load_yaml(".specify/extensions/jira/extension.yml")['config_schema']
|
|
config = json.loads(config_json)
|
|
|
|
try:
|
|
jsonschema.validate(config, schema)
|
|
except jsonschema.ValidationError as e:
|
|
print(f"❌ Invalid jira-config.yml: {e.message}")
|
|
print(f" Path: {'.'.join(str(p) for p in e.path)}")
|
|
exit(1)
|
|
```
|
|
|
|
1. Proceed with validated config
|
|
````
|
|
|
|
---
|
|
|
|
## Hook System
|
|
|
|
### Hook Definition
|
|
|
|
**In extension.yml:**
|
|
|
|
```yaml
|
|
hooks:
|
|
after_tasks:
|
|
command: "speckit.jira.specstoissues"
|
|
optional: true
|
|
prompt: "Create Jira issues from tasks?"
|
|
description: "Automatically create Jira hierarchy"
|
|
condition: "config.project.key is set"
|
|
```
|
|
|
|
### Hook Registration
|
|
|
|
**During extension installation**, record hooks in project config:
|
|
|
|
**File**: `.specify/extensions.yml` (project-level extension config)
|
|
|
|
```yaml
|
|
# Extensions installed in this project
|
|
installed:
|
|
- jira
|
|
- linear
|
|
|
|
# Global extension settings
|
|
settings:
|
|
auto_execute_hooks: true # Prompt for optional hooks after commands
|
|
|
|
# Hook configuration
|
|
hooks:
|
|
after_tasks:
|
|
- extension: jira
|
|
command: speckit.jira.specstoissues
|
|
enabled: true
|
|
optional: true
|
|
prompt: "Create Jira issues from tasks?"
|
|
|
|
after_implement:
|
|
- extension: jira
|
|
command: speckit.jira.sync-status
|
|
enabled: true
|
|
optional: true
|
|
prompt: "Sync completion status to Jira?"
|
|
```
|
|
|
|
### Hook Execution
|
|
|
|
**In core command** (e.g., `templates/commands/tasks.md`):
|
|
|
|
Add at end of command:
|
|
|
|
````markdown
|
|
## Extension Hooks
|
|
|
|
After task generation completes, check for registered hooks:
|
|
|
|
```bash
|
|
# Check if extensions.yml exists and has after_tasks hooks
|
|
if [ -f ".specify/extensions.yml" ]; then
|
|
# Parse hooks for after_tasks
|
|
hooks=$(yq eval '.hooks.after_tasks[] | select(.enabled == true)' .specify/extensions.yml -o=json)
|
|
|
|
if [ -n "$hooks" ]; then
|
|
echo ""
|
|
echo "📦 Extension hooks available:"
|
|
|
|
# Iterate hooks
|
|
echo "$hooks" | jq -c '.' | while read -r hook; do
|
|
extension=$(echo "$hook" | jq -r '.extension')
|
|
command=$(echo "$hook" | jq -r '.command')
|
|
optional=$(echo "$hook" | jq -r '.optional')
|
|
prompt_text=$(echo "$hook" | jq -r '.prompt')
|
|
|
|
if [ "$optional" = "true" ]; then
|
|
# Prompt user
|
|
echo ""
|
|
read -p "$prompt_text (y/n) " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
echo "▶ Executing: $command"
|
|
# Let AI agent execute the command
|
|
# (AI agent will see this and execute)
|
|
echo "EXECUTE_COMMAND: $command"
|
|
fi
|
|
else
|
|
# Auto-execute mandatory hooks
|
|
echo "▶ Executing: $command (required)"
|
|
echo "EXECUTE_COMMAND: $command"
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
```
|
|
````
|
|
|
|
**AI Agent Handling:**
|
|
|
|
The AI agent sees `EXECUTE_COMMAND: speckit.jira.specstoissues` in output and automatically invokes that command.
|
|
|
|
**Alternative**: Direct call in agent context (if agent supports it):
|
|
|
|
```python
|
|
# In AI agent's command execution engine
|
|
def execute_command_with_hooks(command_name: str, args: str):
|
|
# Execute main command
|
|
result = execute_command(command_name, args)
|
|
|
|
# Check for hooks
|
|
hooks = load_hooks_for_phase(f"after_{command_name}")
|
|
for hook in hooks:
|
|
if hook.optional:
|
|
if confirm(hook.prompt):
|
|
execute_command(hook.command, args)
|
|
else:
|
|
execute_command(hook.command, args)
|
|
|
|
return result
|
|
```
|
|
|
|
### Hook Conditions
|
|
|
|
Extensions can specify **conditions** for hooks:
|
|
|
|
```yaml
|
|
hooks:
|
|
after_tasks:
|
|
command: "speckit.jira.specstoissues"
|
|
optional: true
|
|
condition: "config.project.key is set and config.enabled == true"
|
|
```
|
|
|
|
**Condition evaluation** (in hook executor):
|
|
|
|
```python
|
|
def should_execute_hook(hook: dict, config: dict) -> bool:
|
|
"""Evaluate hook condition."""
|
|
condition = hook.get('condition')
|
|
if not condition:
|
|
return True # No condition = always eligible
|
|
|
|
# Simple expression evaluator
|
|
# "config.project.key is set" → check if config['project']['key'] exists
|
|
# "config.enabled == true" → check if config['enabled'] is True
|
|
|
|
return eval_condition(condition, config)
|
|
```
|
|
|
|
---
|
|
|
|
## Extension Discovery & Catalog
|
|
|
|
### Dual Catalog System
|
|
|
|
Spec Kit uses two catalog files with different purposes:
|
|
|
|
#### User Catalog (`catalog.json`)
|
|
|
|
**URL**: `https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json`
|
|
|
|
- **Purpose**: Organization's curated catalog of approved extensions
|
|
- **Default State**: Empty by design - users populate with extensions they trust
|
|
- **Usage**: Primary catalog (priority 1, `install_allowed: true`) in the default stack
|
|
- **Control**: Organizations maintain their own fork/version for their teams
|
|
|
|
#### Community Reference Catalog (`catalog.community.json`)
|
|
|
|
**URL**: `https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json`
|
|
|
|
- **Purpose**: Reference catalog of available community-contributed extensions
|
|
- **Verification**: Community extensions may have `verified: false` initially
|
|
- **Status**: Active - open for community contributions
|
|
- **Submission**: Via Pull Request following the Extension Publishing Guide
|
|
- **Usage**: Secondary catalog (priority 2, `install_allowed: false`) in the default stack — discovery only
|
|
|
|
**How It Works (default stack):**
|
|
|
|
1. **Discover**: `specify extension search` searches both catalogs — community extensions appear automatically
|
|
2. **Review**: Evaluate community extensions for security, quality, and organizational fit
|
|
3. **Curate**: Copy approved entries from community catalog to your `catalog.json`, or add to `.specify/extension-catalogs.yml` with `install_allowed: true`
|
|
4. **Install**: Use `specify extension add <name>` — only allowed from `install_allowed: true` catalogs
|
|
|
|
This approach gives organizations full control over which extensions can be installed while still providing community discoverability out of the box.
|
|
|
|
### Catalog Format
|
|
|
|
**Format** (same for both catalogs):
|
|
|
|
```json
|
|
{
|
|
"schema_version": "1.0",
|
|
"updated_at": "2026-01-28T14:30:00Z",
|
|
"extensions": {
|
|
"jira": {
|
|
"name": "Jira Integration",
|
|
"id": "jira",
|
|
"description": "Create Jira Epics, Stories, and Issues from spec-kit artifacts",
|
|
"author": "Stats Perform",
|
|
"version": "1.0.0",
|
|
"download_url": "https://github.com/statsperform/spec-kit-jira/releases/download/v1.0.0/spec-kit-jira-1.0.0.zip",
|
|
"repository": "https://github.com/statsperform/spec-kit-jira",
|
|
"homepage": "https://github.com/statsperform/spec-kit-jira/blob/main/README.md",
|
|
"documentation": "https://github.com/statsperform/spec-kit-jira/blob/main/docs/",
|
|
"changelog": "https://github.com/statsperform/spec-kit-jira/blob/main/CHANGELOG.md",
|
|
"license": "MIT",
|
|
"requires": {
|
|
"speckit_version": ">=0.1.0,<2.0.0",
|
|
"tools": [
|
|
{
|
|
"name": "jira-mcp-server",
|
|
"version": ">=1.0.0"
|
|
}
|
|
]
|
|
},
|
|
"tags": ["issue-tracking", "jira", "atlassian", "project-management"],
|
|
"verified": true,
|
|
"downloads": 1250,
|
|
"stars": 45
|
|
},
|
|
"linear": {
|
|
"name": "Linear Integration",
|
|
"id": "linear",
|
|
"description": "Sync spec-kit tasks with Linear issues",
|
|
"author": "Community",
|
|
"version": "0.9.0",
|
|
"download_url": "https://github.com/example/spec-kit-linear/releases/download/v0.9.0/spec-kit-linear-0.9.0.zip",
|
|
"repository": "https://github.com/example/spec-kit-linear",
|
|
"requires": {
|
|
"speckit_version": ">=0.1.0"
|
|
},
|
|
"tags": ["issue-tracking", "linear"],
|
|
"verified": false
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Catalog Discovery Commands
|
|
|
|
```bash
|
|
# List all available extensions
|
|
specify extension search
|
|
|
|
# Search by keyword
|
|
specify extension search jira
|
|
|
|
# Search by tag
|
|
specify extension search --tag issue-tracking
|
|
|
|
# Show extension details
|
|
specify extension info jira
|
|
```
|
|
|
|
### Custom Catalogs
|
|
|
|
Spec Kit supports a **catalog stack** — an ordered list of catalogs that the CLI merges and searches across. This allows organizations to maintain their own org-approved extensions alongside an internal catalog and community discovery, all at once.
|
|
|
|
#### Catalog Stack Resolution
|
|
|
|
The active catalog stack is resolved in this order (first match wins):
|
|
|
|
1. **`SPECKIT_CATALOG_URL` environment variable** — single catalog replacing all defaults (backward compat)
|
|
2. **Project-level `.specify/extension-catalogs.yml`** — full control for the project
|
|
3. **User-level `~/.specify/extension-catalogs.yml`** — personal defaults
|
|
4. **Built-in default stack** — `catalog.json` (install_allowed: true) + `catalog.community.json` (install_allowed: false)
|
|
|
|
#### Default Built-in Stack
|
|
|
|
When no config file exists, the CLI uses:
|
|
|
|
| Priority | Catalog | install_allowed | Purpose |
|
|
|----------|---------|-----------------|---------|
|
|
| 1 | `catalog.json` (default) | `true` | Curated extensions available for installation |
|
|
| 2 | `catalog.community.json` (community) | `false` | Discovery only — browse but not install |
|
|
|
|
This means `specify extension search` surfaces community extensions out of the box, while `specify extension add` is still restricted to entries from catalogs with `install_allowed: true`.
|
|
|
|
#### `.specify/extension-catalogs.yml` Config File
|
|
|
|
```yaml
|
|
catalogs:
|
|
- name: "default"
|
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json"
|
|
priority: 1 # Highest — only approved entries can be installed
|
|
install_allowed: true
|
|
description: "Built-in catalog of installable extensions"
|
|
|
|
- name: "internal"
|
|
url: "https://internal.company.com/spec-kit/catalog.json"
|
|
priority: 2
|
|
install_allowed: true
|
|
description: "Internal company extensions"
|
|
|
|
- name: "community"
|
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json"
|
|
priority: 3 # Lowest — discovery only, not installable
|
|
install_allowed: false
|
|
description: "Community-contributed extensions (discovery only)"
|
|
```
|
|
|
|
A user-level equivalent lives at `~/.specify/extension-catalogs.yml`. When a project-level config is present with one or more catalog entries, it takes full control and the built-in defaults are not applied. An empty `catalogs: []` list is treated the same as no config file, falling back to defaults.
|
|
|
|
#### Catalog CLI Commands
|
|
|
|
```bash
|
|
# List active catalogs with name, URL, priority, and install_allowed
|
|
specify extension catalog list
|
|
|
|
# Add a catalog (project-scoped)
|
|
specify extension catalog add --name "internal" --install-allowed \
|
|
https://internal.company.com/spec-kit/catalog.json
|
|
|
|
# Add a discovery-only catalog
|
|
specify extension catalog add --name "community" \
|
|
https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json
|
|
|
|
# Remove a catalog
|
|
specify extension catalog remove internal
|
|
|
|
# Show which catalog an extension came from
|
|
specify extension info jira
|
|
# → Source catalog: default
|
|
```
|
|
|
|
#### Merge Conflict Resolution
|
|
|
|
When the same extension `id` appears in multiple catalogs, the higher-priority (lower priority number) catalog wins. Extensions from lower-priority catalogs with the same `id` are ignored.
|
|
|
|
#### `install_allowed: false` Behavior
|
|
|
|
Extensions from discovery-only catalogs are shown in `specify extension search` results but cannot be installed directly:
|
|
|
|
```
|
|
⚠ 'linear' is available in the 'community' catalog but installation is not allowed from that catalog.
|
|
|
|
To enable installation, add 'linear' to an approved catalog (install_allowed: true) in .specify/extension-catalogs.yml.
|
|
```
|
|
|
|
#### `SPECKIT_CATALOG_URL` (Backward Compatibility)
|
|
|
|
The `SPECKIT_CATALOG_URL` environment variable still works — it is treated as a single `install_allowed: true` catalog, **replacing both defaults** for full backward compatibility:
|
|
|
|
```bash
|
|
# Point to your organization's catalog
|
|
export SPECKIT_CATALOG_URL="https://internal.company.com/spec-kit/catalog.json"
|
|
|
|
# All extension commands now use your custom catalog
|
|
specify extension search # Uses custom catalog
|
|
specify extension add jira # Installs from custom catalog
|
|
```
|
|
|
|
**Requirements:**
|
|
- URL must use HTTPS (HTTP only allowed for localhost testing)
|
|
- Catalog must follow the standard catalog.json schema
|
|
- Must be publicly accessible or accessible within your network
|
|
|
|
**Example for testing:**
|
|
```bash
|
|
# Test with localhost during development
|
|
export SPECKIT_CATALOG_URL="http://localhost:8000/catalog.json"
|
|
specify extension search
|
|
```
|
|
|
|
---
|
|
|
|
## CLI Commands
|
|
|
|
### `specify extension` Subcommands
|
|
|
|
#### `specify extension list`
|
|
|
|
List installed extensions in current project.
|
|
|
|
```bash
|
|
$ specify extension list
|
|
|
|
Installed Extensions:
|
|
✓ Jira Integration (v1.0.0)
|
|
jira
|
|
Create Jira issues from spec-kit artifacts
|
|
Commands: 3 | Hooks: 2 | Priority: 10 | Status: Enabled
|
|
|
|
✓ Linear Integration (v0.9.0)
|
|
linear
|
|
Create Linear issues from spec-kit artifacts
|
|
Commands: 1 | Hooks: 1 | Priority: 10 | Status: Enabled
|
|
```
|
|
|
|
**Options:**
|
|
|
|
- `--available`: Show available (not installed) extensions from catalog
|
|
- `--all`: Show both installed and available
|
|
|
|
#### `specify extension search [QUERY]`
|
|
|
|
Search extension catalog.
|
|
|
|
```bash
|
|
$ specify extension search jira
|
|
|
|
Found 1 extension:
|
|
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ jira (v1.0.0) ✓ Verified │
|
|
│ Jira Integration │
|
|
│ │
|
|
│ Create Jira Epics, Stories, and Issues from spec-kit │
|
|
│ artifacts │
|
|
│ │
|
|
│ Author: Stats Perform │
|
|
│ Tags: issue-tracking, jira, atlassian │
|
|
│ Downloads: 1,250 │
|
|
│ │
|
|
│ Repository: github.com/statsperform/spec-kit-jira │
|
|
│ Documentation: github.com/.../docs │
|
|
└─────────────────────────────────────────────────────────┘
|
|
|
|
Install: specify extension add jira
|
|
```
|
|
|
|
**Options:**
|
|
|
|
- `--tag TAG`: Filter by tag
|
|
- `--author AUTHOR`: Filter by author
|
|
- `--verified`: Show only verified extensions
|
|
|
|
#### `specify extension info NAME`
|
|
|
|
Show detailed information about an extension.
|
|
|
|
```bash
|
|
$ specify extension info jira
|
|
|
|
Jira Integration (jira) v1.0.0
|
|
|
|
Description:
|
|
Create Jira Epics, Stories, and Issues from spec-kit artifacts
|
|
|
|
Author: Stats Perform
|
|
License: MIT
|
|
Repository: https://github.com/statsperform/spec-kit-jira
|
|
Documentation: https://github.com/statsperform/spec-kit-jira/blob/main/docs/
|
|
|
|
Requirements:
|
|
• Spec Kit: >=0.1.0,<2.0.0
|
|
• Tools: jira-mcp-server (>=1.0.0)
|
|
|
|
Provides:
|
|
Commands:
|
|
• speckit.jira.specstoissues - Create Jira hierarchy from spec and tasks
|
|
• speckit.jira.discover-fields - Discover Jira custom fields
|
|
• speckit.jira.sync-status - Sync task completion status
|
|
|
|
Hooks:
|
|
• after_tasks - Prompt to create Jira issues
|
|
• after_implement - Prompt to sync status
|
|
|
|
Tags: issue-tracking, jira, atlassian, project-management
|
|
|
|
Downloads: 1,250 | Stars: 45 | Verified: ✓
|
|
|
|
Install: specify extension add jira
|
|
```
|
|
|
|
#### `specify extension add NAME`
|
|
|
|
Install an extension.
|
|
|
|
```bash
|
|
$ specify extension add jira
|
|
|
|
Installing extension: Jira Integration
|
|
|
|
✓ Downloaded spec-kit-jira-1.0.0.zip (245 KB)
|
|
✓ Validated manifest
|
|
✓ Checked compatibility (spec-kit 0.1.0 ≥ 0.1.0)
|
|
✓ Extracted to .specify/extensions/jira/
|
|
✓ Registered 3 commands with claude
|
|
✓ Installed config template (jira-config.yml)
|
|
|
|
⚠ Configuration required:
|
|
Edit .specify/extensions/jira/jira-config.yml to set your Jira project key
|
|
|
|
Extension installed successfully!
|
|
|
|
Next steps:
|
|
1. Configure: vim .specify/extensions/jira/jira-config.yml
|
|
2. Discover fields: /speckit.jira.discover-fields
|
|
3. Use commands: /speckit.jira.specstoissues
|
|
```
|
|
|
|
**Options:**
|
|
|
|
- `--from URL`: Install from a remote URL (archive). Does not accept Git repositories directly.
|
|
- `--dev`: Install from a local path in development mode (the PATH is the positional `extension` argument).
|
|
- `--priority NUMBER`: Set resolution priority (lower = higher precedence, default 10)
|
|
|
|
#### `specify extension remove NAME`
|
|
|
|
Uninstall an extension.
|
|
|
|
```bash
|
|
$ specify extension remove jira
|
|
|
|
⚠ This will remove:
|
|
• 3 commands from AI agent
|
|
• Extension directory: .specify/extensions/jira/
|
|
• Config file: jira-config.yml (will be backed up)
|
|
|
|
Continue? (yes/no): yes
|
|
|
|
✓ Unregistered commands
|
|
✓ Backed up config to .specify/extensions/.backup/jira-config.yml
|
|
✓ Removed extension directory
|
|
✓ Updated registry
|
|
|
|
Extension removed successfully.
|
|
|
|
To reinstall: specify extension add jira
|
|
```
|
|
|
|
**Options:**
|
|
|
|
- `--keep-config`: Don't remove config file
|
|
- `--force`: Skip confirmation
|
|
|
|
#### `specify extension update [NAME]`
|
|
|
|
Update extension(s) to latest version.
|
|
|
|
```bash
|
|
$ specify extension update jira
|
|
|
|
Checking for updates...
|
|
|
|
jira: 1.0.0 → 1.1.0 available
|
|
|
|
Changes in v1.1.0:
|
|
• Added support for custom workflows
|
|
• Fixed issue with parallel tasks
|
|
• Improved error messages
|
|
|
|
Update? (yes/no): yes
|
|
|
|
✓ Downloaded spec-kit-jira-1.1.0.zip
|
|
✓ Validated manifest
|
|
✓ Backed up current version
|
|
✓ Extracted new version
|
|
✓ Preserved config file
|
|
✓ Re-registered commands
|
|
|
|
Extension updated successfully!
|
|
|
|
Changelog: https://github.com/statsperform/spec-kit-jira/blob/main/CHANGELOG.md#v110
|
|
```
|
|
|
|
**Options:**
|
|
|
|
- `--all`: Update all extensions
|
|
- `--check`: Check for updates without installing
|
|
- `--force`: Force update even if already latest
|
|
|
|
#### `specify extension enable/disable NAME`
|
|
|
|
Enable or disable an extension without removing it.
|
|
|
|
```bash
|
|
$ specify extension disable jira
|
|
|
|
✓ Disabled extension: jira
|
|
• Commands unregistered (but files preserved)
|
|
• Hooks will not execute
|
|
|
|
To re-enable: specify extension enable jira
|
|
```
|
|
|
|
#### `specify extension set-priority NAME PRIORITY`
|
|
|
|
Change the resolution priority of an installed extension.
|
|
|
|
```bash
|
|
$ specify extension set-priority jira 5
|
|
|
|
✓ Extension 'Jira Integration' priority changed: 10 → 5
|
|
|
|
Lower priority = higher precedence in template resolution
|
|
```
|
|
|
|
**Priority Values:**
|
|
|
|
- Lower numbers = higher precedence (checked first in resolution)
|
|
- Default priority is 10
|
|
- Must be a positive integer (1 or higher)
|
|
|
|
**Use Cases:**
|
|
|
|
- Ensure a critical extension's templates take precedence
|
|
- Override default resolution order when multiple extensions provide similar templates
|
|
|
|
---
|
|
|
|
## Compatibility & Versioning
|
|
|
|
### Semantic Versioning
|
|
|
|
Extensions follow [SemVer 2.0.0](https://semver.org/):
|
|
|
|
- **MAJOR**: Breaking changes (command API changes, config schema changes)
|
|
- **MINOR**: New features (new commands, new config options)
|
|
- **PATCH**: Bug fixes (no API changes)
|
|
|
|
### Compatibility Checks
|
|
|
|
**At installation:**
|
|
|
|
```python
|
|
def check_compatibility(extension_manifest: dict) -> bool:
|
|
"""Check if extension is compatible with current environment."""
|
|
|
|
requires = extension_manifest['requires']
|
|
|
|
# 1. Check spec-kit version
|
|
current_speckit = get_speckit_version() # e.g., "0.1.5"
|
|
required_speckit = requires['speckit_version'] # e.g., ">=0.1.0,<2.0.0"
|
|
|
|
if not version_satisfies(current_speckit, required_speckit):
|
|
raise IncompatibleVersionError(
|
|
f"Extension requires spec-kit {required_speckit}, "
|
|
f"but {current_speckit} is installed. "
|
|
f"Upgrade spec-kit with: uv tool install specify-cli --force"
|
|
)
|
|
|
|
# 2. Check required tools
|
|
for tool in requires.get('tools', []):
|
|
tool_name = tool['name']
|
|
tool_version = tool.get('version')
|
|
|
|
if tool.get('required', True):
|
|
if not check_tool(tool_name):
|
|
raise MissingToolError(
|
|
f"Extension requires tool: {tool_name}\n"
|
|
f"Install from: {tool.get('install_url', 'N/A')}"
|
|
)
|
|
|
|
if tool_version:
|
|
installed = get_tool_version(tool_name, tool.get('check_command'))
|
|
if not version_satisfies(installed, tool_version):
|
|
raise IncompatibleToolVersionError(
|
|
f"Extension requires {tool_name} {tool_version}, "
|
|
f"but {installed} is installed"
|
|
)
|
|
|
|
# 3. Check required commands
|
|
for cmd in requires.get('commands', []):
|
|
if not command_exists(cmd):
|
|
raise MissingCommandError(
|
|
f"Extension requires core command: {cmd}\n"
|
|
f"Update spec-kit to latest version"
|
|
)
|
|
|
|
return True
|
|
```
|
|
|
|
### Deprecation Policy
|
|
|
|
**Extension manifest can mark features as deprecated:**
|
|
|
|
```yaml
|
|
provides:
|
|
commands:
|
|
- name: "speckit.jira.old-command"
|
|
file: "commands/old-command.md"
|
|
deprecated: true
|
|
deprecated_message: "Use speckit.jira.new-command instead"
|
|
removal_version: "2.0.0"
|
|
```
|
|
|
|
**At runtime, show warning:**
|
|
|
|
```text
|
|
⚠️ Warning: /speckit.jira.old-command is deprecated
|
|
Use /speckit.jira.new-command instead
|
|
This command will be removed in v2.0.0
|
|
```
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
### Trust Model
|
|
|
|
Extensions run with **same privileges as AI agent**:
|
|
|
|
- Can execute shell commands
|
|
- Can read/write files in project
|
|
- Can make network requests
|
|
|
|
**Trust boundary**: User must trust extension author.
|
|
|
|
### Verification
|
|
|
|
**Verified Extensions** (in catalog):
|
|
|
|
- Published by known organizations (GitHub, Stats Perform, etc.)
|
|
- Code reviewed by spec-kit maintainers
|
|
- Marked with ✓ badge in catalog
|
|
|
|
**Community Extensions**:
|
|
|
|
- Not verified, use at own risk
|
|
- Show warning during installation:
|
|
|
|
```text
|
|
⚠️ This extension is not verified.
|
|
Review code before installing: https://github.com/...
|
|
|
|
Continue? (yes/no):
|
|
```
|
|
|
|
### Sandboxing (Future)
|
|
|
|
**Phase 2** (not in initial release):
|
|
|
|
- Extensions declare required permissions in manifest
|
|
- CLI enforces permission boundaries
|
|
- Example permissions: `filesystem:read`, `network:external`, `env:read`
|
|
|
|
```yaml
|
|
# Future extension.yml
|
|
permissions:
|
|
- "filesystem:read:.specify/extensions/jira/" # Can only read own config
|
|
- "filesystem:write:.specify/memory/" # Can write to memory
|
|
- "network:external:*.atlassian.net" # Can call Jira API
|
|
- "env:read:SPECKIT_JIRA_*" # Can read own env vars
|
|
```
|
|
|
|
### Package Integrity
|
|
|
|
**Future**: Sign extension packages with GPG/Sigstore
|
|
|
|
```yaml
|
|
# catalog.json
|
|
"jira": {
|
|
"download_url": "...",
|
|
"checksum": "sha256:abc123...",
|
|
"signature": "https://github.com/.../spec-kit-jira-1.0.0.sig",
|
|
"signing_key": "https://github.com/statsperform.gpg"
|
|
}
|
|
```
|
|
|
|
CLI verifies signature before extraction.
|
|
|
|
---
|
|
|
|
## Migration Strategy
|
|
|
|
### Backward Compatibility
|
|
|
|
**Goal**: Existing spec-kit projects work without changes.
|
|
|
|
**Strategy**:
|
|
|
|
1. **Core commands unchanged**: `/speckit.tasks`, `/speckit.implement`, etc. remain in core
|
|
|
|
2. **Optional extensions**: Users opt-in to extensions
|
|
|
|
3. **Gradual migration**: Existing `taskstoissues` stays in core, Jira extension is alternative
|
|
|
|
4. **Deprecation timeline**:
|
|
- **v0.2.0**: Introduce extension system, keep core `taskstoissues`
|
|
- **v0.3.0**: Mark core `taskstoissues` as "legacy" (still works)
|
|
- **v1.0.0**: Consider removing core `taskstoissues` in favor of extension
|
|
|
|
### Migration Path for Users
|
|
|
|
**Scenario 1**: User has no `taskstoissues` usage
|
|
|
|
- No migration needed, extensions are opt-in
|
|
|
|
**Scenario 2**: User uses core `taskstoissues` (GitHub Issues)
|
|
|
|
- Works as before
|
|
- Optional: Migrate to `github-projects` extension for more features
|
|
|
|
**Scenario 3**: User wants Jira (new requirement)
|
|
|
|
- `specify extension add jira`
|
|
- Configure and use
|
|
|
|
**Scenario 4**: User has custom scripts calling `taskstoissues`
|
|
|
|
- Scripts still work (core command preserved)
|
|
- Migration guide shows how to call extension commands instead
|
|
|
|
### Extension Migration Guide
|
|
|
|
**For extension authors** (if core command becomes extension):
|
|
|
|
```bash
|
|
# Old (core command)
|
|
/speckit.taskstoissues
|
|
|
|
# New (extension command)
|
|
specify extension add github-projects
|
|
/speckit.github.taskstoissues
|
|
```
|
|
|
|
**Compatibility shim** (if needed):
|
|
|
|
```yaml
|
|
# extension.yml
|
|
provides:
|
|
commands:
|
|
- name: "speckit.github.taskstoissues"
|
|
file: "commands/taskstoissues.md"
|
|
aliases: ["speckit.taskstoissues"] # Backward compatibility
|
|
```
|
|
|
|
AI agent registers both names, so old scripts work.
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Core Extension System ✅ COMPLETED
|
|
|
|
**Goal**: Basic extension infrastructure
|
|
|
|
**Deliverables**:
|
|
|
|
- [x] Extension manifest schema (`extension.yml`)
|
|
- [x] Extension directory structure
|
|
- [x] CLI commands:
|
|
- [x] `specify extension list`
|
|
- [x] `specify extension add` (from URL and local `--dev`)
|
|
- [x] `specify extension remove`
|
|
- [x] Extension registry (`.specify/extensions/.registry`)
|
|
- [x] Command registration (Claude and 15+ other agents)
|
|
- [x] Basic validation (manifest schema, compatibility)
|
|
- [x] Documentation (extension development guide)
|
|
|
|
**Testing**:
|
|
|
|
- [x] Unit tests for manifest parsing
|
|
- [x] Integration test: Install dummy extension
|
|
- [x] Integration test: Register commands with Claude
|
|
|
|
### Phase 2: Jira Extension ✅ COMPLETED
|
|
|
|
**Goal**: First production extension
|
|
|
|
**Deliverables**:
|
|
|
|
- [x] Create `spec-kit-jira` repository
|
|
- [x] Port Jira functionality to extension
|
|
- [x] Create `jira-config.yml` template
|
|
- [x] Commands:
|
|
- [x] `specstoissues.md`
|
|
- [x] `discover-fields.md`
|
|
- [x] `sync-status.md`
|
|
- [x] Helper scripts
|
|
- [x] Documentation (README, configuration guide, examples)
|
|
- [x] Release v3.0.0
|
|
|
|
**Testing**:
|
|
|
|
- [x] Test on `eng-msa-ts` project
|
|
- [x] Verify spec→Epic, phase→Story, task→Issue mapping
|
|
- [x] Test configuration loading and validation
|
|
- [x] Test custom field application
|
|
|
|
### Phase 3: Extension Catalog ✅ COMPLETED
|
|
|
|
**Goal**: Discovery and distribution
|
|
|
|
**Deliverables**:
|
|
|
|
- [x] Central catalog (`extensions/catalog.json` in spec-kit repo)
|
|
- [x] Community catalog (`extensions/catalog.community.json`)
|
|
- [x] Catalog fetch and parsing with multi-catalog support
|
|
- [x] CLI commands:
|
|
- [x] `specify extension search`
|
|
- [x] `specify extension info`
|
|
- [x] `specify extension catalog list`
|
|
- [x] `specify extension catalog add`
|
|
- [x] `specify extension catalog remove`
|
|
- [x] Documentation (how to publish extensions)
|
|
|
|
**Testing**:
|
|
|
|
- [x] Test catalog fetch
|
|
- [x] Test extension search/filtering
|
|
- [x] Test catalog caching
|
|
- [x] Test multi-catalog merge with priority
|
|
|
|
### Phase 4: Advanced Features ✅ COMPLETED
|
|
|
|
**Goal**: Hooks, updates, multi-agent support
|
|
|
|
**Deliverables**:
|
|
|
|
- [x] Hook system (`hooks` in extension.yml)
|
|
- [x] Hook registration and execution
|
|
- [x] Project extensions config (`.specify/extensions.yml`)
|
|
- [x] CLI commands:
|
|
- [x] `specify extension update` (with atomic backup/restore)
|
|
- [x] `specify extension enable/disable`
|
|
- [x] Command registration for multiple agents (15+ agents including Claude, Copilot, Gemini, Cursor, etc.)
|
|
- [x] Extension update notifications (version comparison)
|
|
- [x] Configuration layer resolution (project, local, env)
|
|
|
|
**Additional features implemented beyond original RFC**:
|
|
|
|
- [x] **Display name resolution**: All commands accept extension display names in addition to IDs
|
|
- [x] **Ambiguous name handling**: User-friendly tables when multiple extensions match a name
|
|
- [x] **Atomic update with rollback**: Full backup of extension dir, commands, hooks, and registry with automatic rollback on failure
|
|
- [x] **Pre-install ID validation**: Validates extension ID from ZIP before installing (security)
|
|
- [x] **Enabled state preservation**: Disabled extensions stay disabled after update
|
|
- [x] **Registry update/restore methods**: Clean API for enable/disable and rollback operations
|
|
- [x] **Catalog error fallback**: `extension info` falls back to local info when catalog unavailable
|
|
- [x] **`_install_allowed` flag**: Discovery-only catalogs can't be used for installation
|
|
- [x] **Cache invalidation**: Cache invalidated when `SPECKIT_CATALOG_URL` changes
|
|
|
|
**Testing**:
|
|
|
|
- [x] Test hooks in core commands
|
|
- [x] Test extension updates (preserve config)
|
|
- [x] Test multi-agent registration
|
|
- [x] Test atomic rollback on update failure
|
|
- [x] Test enabled state preservation
|
|
- [x] Test display name resolution
|
|
|
|
### Phase 5: Polish & Documentation ✅ COMPLETED
|
|
|
|
**Goal**: Production ready
|
|
|
|
**Deliverables**:
|
|
|
|
- [x] Comprehensive documentation:
|
|
- [x] User guide (EXTENSION-USER-GUIDE.md)
|
|
- [x] Extension development guide (EXTENSION-DEV-GUIDE.md)
|
|
- [x] Extension API reference (EXTENSION-API-REFERENCE.md)
|
|
- [x] Error messages and validation improvements
|
|
- [x] CLI help text updates
|
|
|
|
**Testing**:
|
|
|
|
- [x] End-to-end testing on multiple projects
|
|
- [x] 163 unit tests passing
|
|
|
|
---
|
|
|
|
## Resolved Questions
|
|
|
|
The following questions from the original RFC have been resolved during implementation:
|
|
|
|
### 1. Extension Namespace ✅ RESOLVED
|
|
|
|
**Question**: Should extension commands use namespace prefix?
|
|
|
|
**Decision**: **Option C** - Both prefixed and aliases are supported. Commands use `speckit.{extension}.{command}` as canonical name, with optional aliases defined in manifest.
|
|
|
|
**Implementation**: The `aliases` field in `extension.yml` allows extensions to register additional command names.
|
|
|
|
---
|
|
|
|
### 2. Config File Location ✅ RESOLVED
|
|
|
|
**Question**: Where should extension configs live?
|
|
|
|
**Decision**: **Option A** - Extension directory (`.specify/extensions/{ext-id}/{ext-id}-config.yml`). This keeps extensions self-contained and easier to manage.
|
|
|
|
**Implementation**: Each extension has its own config file within its directory, with layered resolution (defaults → project → local → env vars).
|
|
|
|
---
|
|
|
|
### 3. Command File Format ✅ RESOLVED
|
|
|
|
**Question**: Should extensions use universal format or agent-specific?
|
|
|
|
**Decision**: **Option A** - Universal Markdown format. Extensions write commands once, CLI converts to agent-specific format during registration.
|
|
|
|
**Implementation**: `CommandRegistrar` class handles conversion to 15+ agent formats (Claude, Copilot, Gemini, Cursor, etc.).
|
|
|
|
---
|
|
|
|
### 4. Hook Execution Model ✅ RESOLVED
|
|
|
|
**Question**: How should hooks execute?
|
|
|
|
**Decision**: **Option A** - Hooks are registered in `.specify/extensions.yml` and executed by the AI agent when it sees the hook trigger. Hook state (enabled/disabled) is managed per-extension.
|
|
|
|
**Implementation**: `HookExecutor` class manages hook registration and state in `extensions.yml`.
|
|
|
|
---
|
|
|
|
### 5. Extension Distribution ✅ RESOLVED
|
|
|
|
**Question**: How should extensions be packaged?
|
|
|
|
**Decision**: **Option A** - ZIP archives downloaded from GitHub releases (via catalog `download_url`). Local development uses `--dev` flag with directory path.
|
|
|
|
**Implementation**: `ExtensionManager.install_from_zip()` handles ZIP extraction and validation.
|
|
|
|
---
|
|
|
|
### 6. Multi-Version Support ✅ RESOLVED
|
|
|
|
**Question**: Can multiple versions of same extension coexist?
|
|
|
|
**Decision**: **Option A** - Single version only. Updates replace the existing version with atomic rollback on failure.
|
|
|
|
**Implementation**: `extension update` performs atomic backup/restore to ensure safe updates.
|
|
|
|
---
|
|
|
|
## Open Questions (Remaining)
|
|
|
|
### 1. Sandboxing / Permissions (Future)
|
|
|
|
**Question**: Should extensions declare required permissions?
|
|
|
|
**Options**:
|
|
|
|
- A) No sandboxing (current): Extensions run with same privileges as AI agent
|
|
- B) Permission declarations: Extensions declare `filesystem:read`, `network:external`, etc.
|
|
- C) Opt-in sandboxing: Organizations can enable permission enforcement
|
|
|
|
**Status**: Deferred to future version. Currently using trust-based model where users trust extension authors.
|
|
|
|
---
|
|
|
|
### 2. Package Signatures (Future)
|
|
|
|
**Question**: Should extensions be cryptographically signed?
|
|
|
|
**Options**:
|
|
|
|
- A) No signatures (current): Trust based on catalog source
|
|
- B) GPG/Sigstore signatures: Verify package integrity
|
|
- C) Catalog-level verification: Catalog maintainers verify packages
|
|
|
|
**Status**: Deferred to future version. `checksum` field is available in catalog schema but not enforced.
|
|
|
|
---
|
|
|
|
## Appendices
|
|
|
|
### Appendix A: Example Extension Structure
|
|
|
|
**Complete structure of `spec-kit-jira` extension:**
|
|
|
|
```text
|
|
spec-kit-jira/
|
|
├── README.md # Overview, features, installation
|
|
├── LICENSE # MIT license
|
|
├── CHANGELOG.md # Version history
|
|
├── .gitignore # Ignore local configs
|
|
│
|
|
├── extension.yml # Extension manifest (required)
|
|
├── jira-config.template.yml # Config template
|
|
│
|
|
├── commands/ # Command files
|
|
│ ├── specstoissues.md # Main command
|
|
│ ├── discover-fields.md # Helper: Discover custom fields
|
|
│ └── sync-status.md # Helper: Sync completion status
|
|
│
|
|
├── scripts/ # Helper scripts
|
|
│ ├── parse-jira-config.sh # Config loader (bash)
|
|
│ ├── parse-jira-config.ps1 # Config loader (PowerShell)
|
|
│ └── validate-jira-connection.sh # Connection test
|
|
│
|
|
├── docs/ # Documentation
|
|
│ ├── installation.md # Installation guide
|
|
│ ├── configuration.md # Configuration reference
|
|
│ ├── usage.md # Usage examples
|
|
│ ├── troubleshooting.md # Common issues
|
|
│ └── examples/
|
|
│ ├── eng-msa-ts-config.yml # Real-world config example
|
|
│ └── simple-project.yml # Minimal config example
|
|
│
|
|
├── tests/ # Tests (optional)
|
|
│ ├── test-extension.sh # Extension validation
|
|
│ └── test-commands.sh # Command execution tests
|
|
│
|
|
└── .github/ # GitHub integration
|
|
└── workflows/
|
|
└── release.yml # Automated releases
|
|
```
|
|
|
|
### Appendix B: Extension Development Guide (Outline)
|
|
|
|
**Documentation for creating new extensions:**
|
|
|
|
1. **Getting Started**
|
|
- Prerequisites (tools needed)
|
|
- Extension template (cookiecutter)
|
|
- Directory structure
|
|
|
|
2. **Extension Manifest**
|
|
- Schema reference
|
|
- Required vs optional fields
|
|
- Versioning guidelines
|
|
|
|
3. **Command Development**
|
|
- Universal command format
|
|
- Frontmatter specification
|
|
- Template variables
|
|
- Script references
|
|
|
|
4. **Configuration**
|
|
- Config file structure
|
|
- Schema validation
|
|
- Layered config resolution
|
|
- Environment variable overrides
|
|
|
|
5. **Hooks**
|
|
- Available hook points
|
|
- Hook registration
|
|
- Conditional execution
|
|
- Best practices
|
|
|
|
6. **Testing**
|
|
- Local development setup
|
|
- Testing with `--dev` flag
|
|
- Validation checklist
|
|
- Integration testing
|
|
|
|
7. **Publishing**
|
|
- Packaging (ZIP format)
|
|
- GitHub releases
|
|
- Catalog submission
|
|
- Versioning strategy
|
|
|
|
8. **Examples**
|
|
- Minimal extension
|
|
- Extension with hooks
|
|
- Extension with configuration
|
|
- Extension with multiple commands
|
|
|
|
### Appendix C: Compatibility Matrix
|
|
|
|
**Planned support matrix:**
|
|
|
|
| Extension Feature | Spec Kit Version | AI Agent Support |
|
|
|-------------------|------------------|------------------|
|
|
| Basic commands | 0.2.0+ | Claude, Gemini, Copilot |
|
|
| Hooks (after_tasks) | 0.3.0+ | Claude, Gemini |
|
|
| Config validation | 0.2.0+ | All |
|
|
| Multiple catalogs | 0.4.0+ | All |
|
|
| Permissions (sandboxing) | 1.0.0+ | TBD |
|
|
|
|
### Appendix D: Extension Catalog Schema
|
|
|
|
**Full schema for `catalog.json`:**
|
|
|
|
```json
|
|
{
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"type": "object",
|
|
"required": ["schema_version", "updated_at", "extensions"],
|
|
"properties": {
|
|
"schema_version": {
|
|
"type": "string",
|
|
"pattern": "^\\d+\\.\\d+$"
|
|
},
|
|
"updated_at": {
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
"extensions": {
|
|
"type": "object",
|
|
"patternProperties": {
|
|
"^[a-z0-9-]+$": {
|
|
"type": "object",
|
|
"required": ["name", "id", "version", "download_url", "repository"],
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"id": { "type": "string", "pattern": "^[a-z0-9-]+$" },
|
|
"description": { "type": "string" },
|
|
"author": { "type": "string" },
|
|
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
|
|
"download_url": { "type": "string", "format": "uri" },
|
|
"repository": { "type": "string", "format": "uri" },
|
|
"homepage": { "type": "string", "format": "uri" },
|
|
"documentation": { "type": "string", "format": "uri" },
|
|
"changelog": { "type": "string", "format": "uri" },
|
|
"license": { "type": "string" },
|
|
"requires": {
|
|
"type": "object",
|
|
"properties": {
|
|
"speckit_version": { "type": "string" },
|
|
"tools": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["name"],
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"version": { "type": "string" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"tags": {
|
|
"type": "array",
|
|
"items": { "type": "string" }
|
|
},
|
|
"verified": { "type": "boolean" },
|
|
"downloads": { "type": "integer" },
|
|
"stars": { "type": "integer" },
|
|
"checksum": { "type": "string" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Summary & Next Steps
|
|
|
|
This RFC proposes a comprehensive extension system for Spec Kit that:
|
|
|
|
1. **Keeps core lean** while enabling unlimited integrations
|
|
2. **Supports multiple agents** (Claude, Gemini, Copilot, etc.)
|
|
3. **Provides clear extension API** for community contributions
|
|
4. **Enables independent versioning** of extensions and core
|
|
5. **Includes safety mechanisms** (validation, compatibility checks)
|
|
|
|
### Immediate Next Steps
|
|
|
|
1. **Review this RFC** with stakeholders
|
|
2. **Gather feedback** on open questions
|
|
3. **Refine design** based on feedback
|
|
4. **Proceed to Phase A**: Implement core extension system
|
|
5. **Then Phase B**: Build Jira extension as proof-of-concept
|
|
|
|
---
|
|
|
|
## Questions for Discussion
|
|
|
|
1. Does the extension architecture meet your needs for Jira integration?
|
|
2. Are there additional hook points we should consider?
|
|
3. Should we support extension dependencies (extension A requires extension B)?
|
|
4. How should we handle extension deprecation/removal from catalog?
|
|
5. What level of sandboxing/permissions do we need in v1.0?
|