mirror of
https://github.com/github/spec-kit.git
synced 2026-03-16 18:33:07 +00:00
* feat(extensions): implement automatic updates with atomic backup/restore - Implement automatic extension updates with download from catalog - Add comprehensive backup/restore mechanism for failed updates: - Backup registry entry before update - Backup extension directory - Backup command files for all AI agents - Backup hooks from extensions.yml - Add extension ID verification after install - Add KeyboardInterrupt handling to allow clean cancellation - Fix enable/disable to preserve installed_at timestamp by using direct registry manipulation instead of registry.add() - Add rollback on any update failure with command file, hook, and registry restoration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): comprehensive name resolution and error handling improvements - Add shared _resolve_installed_extension helper for ID/display name resolution with proper ambiguous name handling (shows table of matches) - Add _resolve_catalog_extension helper for catalog lookups by ID or display name - Update enable/disable/update/remove commands to use name resolution helpers - Fix extension_info to handle catalog errors gracefully: - Fallback to local installed info when catalog unavailable - Distinguish "catalog unavailable" from "not found in catalog" - Support display name lookup for both installed and catalog extensions - Use resolved display names in all status messages for consistency - Extract _print_extension_info helper for DRY catalog info printing Addresses reviewer feedback: - Ambiguous name handling in enable/disable/update - Catalog error fallback for installed extensions - UX message clarity (catalog unavailable vs not found) - Resolved ID in status messages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): properly detect ambiguous names in extension_info The extension_info command was breaking on the first name match without checking for ambiguity. This fix separates ID matching from name matching and checks for ambiguity before selecting a match, consistent with the _resolve_installed_extension() helper used by other commands. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(extensions): add public update() method to ExtensionRegistry Add a proper public API for updating registry metadata while preserving installed_at timestamp, instead of directly mutating internal registry data and calling private _save() method. Changes: - Add ExtensionRegistry.update() method that preserves installed_at - Update enable/disable commands to use registry.update() - Update rollback logic to use registry.update() This decouples the CLI from registry internals and maintains proper encapsulation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): safely access optional author field in extension_info ExtensionManifest doesn't expose an author property - the author field is optional in extension.yml and stored in data["extension"]["author"]. Use safe dict access to avoid AttributeError. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address multiple reviewer comments - ExtensionRegistry.update() now preserves original installed_at timestamp - Add ExtensionRegistry.restore() for rollback (entry was removed) - Clean up wrongly installed extension on ID mismatch before rollback - Remove unused catalog_error parameter from _print_extension_info() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): check _install_allowed for updates, preserve backup on failed rollback - Skip automatic updates for extensions from catalogs with install_allowed=false - Only delete backup directory on successful rollback, preserve it on failure for manual recovery Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address reviewer feedback on update/rollback logic - Hook rollback: handle empty backup_hooks by checking `is not None` instead of truthiness (falsy empty dict would skip hook cleanup) - extension_info: use resolved_installed_id for catalog lookup when extension was found by display name (prevents wrong catalog match) - Rollback: always remove extension dir first, then restore if backup exists (handles case when no original dir existed before update) - Validate extension ID from ZIP before installing, not after (avoids side effects of installing wrong extension before rollback) - Preserve enabled state during updates: re-apply disabled state and hook enabled flags after successful update - Optimize _resolve_catalog_extension: pass query to catalog.search() instead of fetching all extensions - update() now merges metadata with existing entry instead of replacing (preserves fields like registered_commands when only updating enabled) - Add tests for ExtensionRegistry.update() and restore() methods: - test_update_preserves_installed_at - test_update_merges_with_existing - test_update_raises_for_missing_extension - test_restore_overwrites_completely - test_restore_can_recreate_removed_entry Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(extensions): update RFC to reflect implemented status - Change status from "Draft" to "Implemented" - Update all Implementation Phases to show completed items - Add new features implemented beyond original RFC: - Display name resolution for all commands - Ambiguous name handling with tables - Atomic update with rollback - Pre-install ID validation - Enabled state preservation - Registry update/restore methods - Catalog error fallback - _install_allowed flag - Cache invalidation - Convert Open Questions to Resolved Questions with decisions - Add remaining Open Questions (sandboxing, signatures) as future work - Fix table of contents links Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address third round of PR review comments - Refactor extension_info to use _resolve_installed_extension() helper with new allow_not_found parameter instead of duplicating resolution logic - Fix rollback hook restoration to not create empty hooks: {} in config when original config had no hooks section - Fix ZIP pre-validation to handle nested extension.yml files (GitHub auto-generated ZIPs have structure like repo-name-branch/extension.yml) - Replace unused installed_manifest variable with _ placeholder - Add display name to update status messages for better UX Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(extensions): address fourth round of PR review comments Rollback fixes: - Preserve installed_at timestamp after successful update (was reset by install_from_zip calling registry.add) - Fix rollback to only delete extension_dir if backup exists (avoids destroying valid installation when failure happens before modification) - Fix rollback to remove NEW command files created by failed install (files that weren't in original backup are now cleaned up) - Fix rollback to delete hooks key entirely when backup_hooks is None (original config had no hooks key, so restore should remove it) Cross-command consistency fix: - Add display name resolution to `extension add` command using _resolve_catalog_extension() helper (was only in `extension info`) - Use resolved extension ID for download_extension() call, not original argument which may be a display name Security fix (fail-closed): - Malformed catalog config (empty/missing URLs) now raises ValidationError instead of silently falling back to built-in catalogs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lint): address ruff linting errors and registry.update() semantics - Remove unused import ExtensionError in extension_info - Remove extraneous f-prefix from strings without placeholders - Use registry.restore() instead of registry.update() for installed_at preservation (update() always preserves existing installed_at, ignoring our override) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: iamaeroplane <michal.bachorik@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1934 lines
59 KiB
Markdown
1934 lines
59 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
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 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 (v1.0.0) - Jira Integration
|
|
Commands: 3 | Hooks: 2 | Status: Enabled
|
|
|
|
✓ linear (v0.9.0) - Linear Integration
|
|
Commands: 1 | Hooks: 1 | 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 custom URL or Git repo
|
|
- `--version VERSION`: Install specific version
|
|
- `--dev PATH`: Install from local path (development mode)
|
|
- `--no-register`: Skip command registration (manual setup)
|
|
|
|
#### `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
|
|
```
|
|
|
|
---
|
|
|
|
## 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?
|