mirror of
https://github.com/github/spec-kit.git
synced 2026-03-17 02:43:08 +00:00
- Rename 'org-approved' catalog to 'default' - Move 'catalogs' command to 'catalog list' for consistency - Add 'description' field to CatalogEntry dataclass - Add --description option to 'catalog add' CLI command - Align install_allowed default to False in _load_catalog_config - Add user-level config detection in catalog list footer - Fix _load_catalog_config docstring (document ValidationError) - Fix test isolation for test_search_by_tag, test_search_by_query, test_search_verified_only, test_get_extension_info - Update version to 0.1.14 and CHANGELOG - Update all docs (RFC, User Guide, API Reference)
817 lines
18 KiB
Markdown
817 lines
18 KiB
Markdown
# Extension API Reference
|
|
|
|
Technical reference for Spec Kit extension system APIs and manifest schema.
|
|
|
|
## Table of Contents
|
|
|
|
1. [Extension Manifest](#extension-manifest)
|
|
2. [Python API](#python-api)
|
|
3. [Command File Format](#command-file-format)
|
|
4. [Configuration Schema](#configuration-schema)
|
|
5. [Hook System](#hook-system)
|
|
6. [CLI Commands](#cli-commands)
|
|
|
|
---
|
|
|
|
## Extension Manifest
|
|
|
|
### Schema Version 1.0
|
|
|
|
File: `extension.yml`
|
|
|
|
```yaml
|
|
schema_version: "1.0" # Required
|
|
|
|
extension:
|
|
id: string # Required, pattern: ^[a-z0-9-]+$
|
|
name: string # Required, human-readable name
|
|
version: string # Required, semantic version (X.Y.Z)
|
|
description: string # Required, brief description (<200 chars)
|
|
author: string # Required
|
|
repository: string # Required, valid URL
|
|
license: string # Required (e.g., "MIT", "Apache-2.0")
|
|
homepage: string # Optional, valid URL
|
|
|
|
requires:
|
|
speckit_version: string # Required, version specifier (>=X.Y.Z)
|
|
tools: # Optional, array of tool requirements
|
|
- name: string # Tool name
|
|
version: string # Optional, version specifier
|
|
required: boolean # Optional, default: false
|
|
|
|
provides:
|
|
commands: # Required, at least one command
|
|
- name: string # Required, pattern: ^speckit\.[a-z0-9-]+\.[a-z0-9-]+$
|
|
file: string # Required, relative path to command file
|
|
description: string # Required
|
|
aliases: [string] # Optional, array of alternate names
|
|
|
|
config: # Optional, array of config files
|
|
- name: string # Config file name
|
|
template: string # Template file path
|
|
description: string
|
|
required: boolean # Default: false
|
|
|
|
hooks: # Optional, event hooks
|
|
event_name: # e.g., "after_tasks", "after_implement"
|
|
command: string # Command to execute
|
|
optional: boolean # Default: true
|
|
prompt: string # Prompt text for optional hooks
|
|
description: string # Hook description
|
|
condition: string # Optional, condition expression
|
|
|
|
tags: # Optional, array of tags (2-10 recommended)
|
|
- string
|
|
|
|
defaults: # Optional, default configuration values
|
|
key: value # Any YAML structure
|
|
```
|
|
|
|
### Field Specifications
|
|
|
|
#### `extension.id`
|
|
|
|
- **Type**: string
|
|
- **Pattern**: `^[a-z0-9-]+$`
|
|
- **Description**: Unique extension identifier
|
|
- **Examples**: `jira`, `linear`, `azure-devops`
|
|
- **Invalid**: `Jira`, `my_extension`, `extension.id`
|
|
|
|
#### `extension.version`
|
|
|
|
- **Type**: string
|
|
- **Format**: Semantic versioning (X.Y.Z)
|
|
- **Description**: Extension version
|
|
- **Examples**: `1.0.0`, `0.9.5`, `2.1.3`
|
|
- **Invalid**: `v1.0`, `1.0`, `1.0.0-beta`
|
|
|
|
#### `requires.speckit_version`
|
|
|
|
- **Type**: string
|
|
- **Format**: Version specifier
|
|
- **Description**: Required spec-kit version range
|
|
- **Examples**:
|
|
- `>=0.1.0` - Any version 0.1.0 or higher
|
|
- `>=0.1.0,<2.0.0` - Version 0.1.x or 1.x
|
|
- `==0.1.0` - Exactly 0.1.0
|
|
- **Invalid**: `0.1.0`, `>= 0.1.0` (space), `latest`
|
|
|
|
#### `provides.commands[].name`
|
|
|
|
- **Type**: string
|
|
- **Pattern**: `^speckit\.[a-z0-9-]+\.[a-z0-9-]+$`
|
|
- **Description**: Namespaced command name
|
|
- **Format**: `speckit.{extension-id}.{command-name}`
|
|
- **Examples**: `speckit.jira.specstoissues`, `speckit.linear.sync`
|
|
- **Invalid**: `jira.specstoissues`, `speckit.command`, `speckit.jira.CreateIssues`
|
|
|
|
#### `hooks`
|
|
|
|
- **Type**: object
|
|
- **Keys**: Event names (e.g., `after_tasks`, `after_implement`, `before_commit`)
|
|
- **Description**: Hooks that execute at lifecycle events
|
|
- **Events**: Defined by core spec-kit commands
|
|
|
|
---
|
|
|
|
## Python API
|
|
|
|
### ExtensionManifest
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
```python
|
|
from specify_cli.extensions import ExtensionManifest
|
|
|
|
manifest = ExtensionManifest(Path("extension.yml"))
|
|
```
|
|
|
|
**Properties**:
|
|
|
|
```python
|
|
manifest.id # str: Extension ID
|
|
manifest.name # str: Extension name
|
|
manifest.version # str: Version
|
|
manifest.description # str: Description
|
|
manifest.requires_speckit_version # str: Required spec-kit version
|
|
manifest.commands # List[Dict]: Command definitions
|
|
manifest.hooks # Dict: Hook definitions
|
|
```
|
|
|
|
**Methods**:
|
|
|
|
```python
|
|
manifest.get_hash() # str: SHA256 hash of manifest file
|
|
```
|
|
|
|
**Exceptions**:
|
|
|
|
```python
|
|
ValidationError # Invalid manifest structure
|
|
CompatibilityError # Incompatible with current spec-kit version
|
|
```
|
|
|
|
### ExtensionRegistry
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
```python
|
|
from specify_cli.extensions import ExtensionRegistry
|
|
|
|
registry = ExtensionRegistry(extensions_dir)
|
|
```
|
|
|
|
**Methods**:
|
|
|
|
```python
|
|
# Add extension to registry
|
|
registry.add(extension_id: str, metadata: dict)
|
|
|
|
# Remove extension from registry
|
|
registry.remove(extension_id: str)
|
|
|
|
# Get extension metadata
|
|
metadata = registry.get(extension_id: str) # Optional[dict]
|
|
|
|
# List all extensions
|
|
extensions = registry.list() # Dict[str, dict]
|
|
|
|
# Check if installed
|
|
is_installed = registry.is_installed(extension_id: str) # bool
|
|
```
|
|
|
|
**Registry Format**:
|
|
|
|
```json
|
|
{
|
|
"schema_version": "1.0",
|
|
"extensions": {
|
|
"jira": {
|
|
"version": "1.0.0",
|
|
"source": "catalog",
|
|
"manifest_hash": "sha256...",
|
|
"enabled": true,
|
|
"registered_commands": ["speckit.jira.specstoissues", ...],
|
|
"installed_at": "2026-01-28T..."
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### ExtensionManager
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
```python
|
|
from specify_cli.extensions import ExtensionManager
|
|
|
|
manager = ExtensionManager(project_root)
|
|
```
|
|
|
|
**Methods**:
|
|
|
|
```python
|
|
# Install from directory
|
|
manifest = manager.install_from_directory(
|
|
source_dir: Path,
|
|
speckit_version: str,
|
|
register_commands: bool = True
|
|
) # Returns: ExtensionManifest
|
|
|
|
# Install from ZIP
|
|
manifest = manager.install_from_zip(
|
|
zip_path: Path,
|
|
speckit_version: str
|
|
) # Returns: ExtensionManifest
|
|
|
|
# Remove extension
|
|
success = manager.remove(
|
|
extension_id: str,
|
|
keep_config: bool = False
|
|
) # Returns: bool
|
|
|
|
# List installed extensions
|
|
extensions = manager.list_installed() # List[Dict]
|
|
|
|
# Get extension manifest
|
|
manifest = manager.get_extension(extension_id: str) # Optional[ExtensionManifest]
|
|
|
|
# Check compatibility
|
|
manager.check_compatibility(
|
|
manifest: ExtensionManifest,
|
|
speckit_version: str
|
|
) # Raises: CompatibilityError if incompatible
|
|
```
|
|
|
|
### CatalogEntry
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
Represents a single catalog in the active catalog stack.
|
|
|
|
```python
|
|
from specify_cli.extensions import CatalogEntry
|
|
|
|
entry = CatalogEntry(
|
|
url="https://example.com/catalog.json",
|
|
name="default",
|
|
priority=1,
|
|
install_allowed=True,
|
|
description="Built-in catalog of installable extensions",
|
|
)
|
|
```
|
|
|
|
**Fields**:
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `url` | `str` | Catalog URL (must use HTTPS, or HTTP for localhost) |
|
|
| `name` | `str` | Human-readable catalog name |
|
|
| `priority` | `int` | Sort order (lower = higher priority, wins on conflicts) |
|
|
| `install_allowed` | `bool` | Whether extensions from this catalog can be installed |
|
|
| `description` | `str` | Optional human-readable description of the catalog (default: empty) |
|
|
|
|
### ExtensionCatalog
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
```python
|
|
from specify_cli.extensions import ExtensionCatalog
|
|
|
|
catalog = ExtensionCatalog(project_root)
|
|
```
|
|
|
|
**Class attributes**:
|
|
|
|
```python
|
|
ExtensionCatalog.DEFAULT_CATALOG_URL # default catalog URL
|
|
ExtensionCatalog.COMMUNITY_CATALOG_URL # community catalog URL
|
|
```
|
|
|
|
**Methods**:
|
|
|
|
```python
|
|
# Get the ordered list of active catalogs
|
|
entries = catalog.get_active_catalogs() # List[CatalogEntry]
|
|
|
|
# Fetch catalog (primary catalog, backward compat)
|
|
catalog_data = catalog.fetch_catalog(force_refresh: bool = False) # Dict
|
|
|
|
# Search extensions across all active catalogs
|
|
# Each result includes _catalog_name and _install_allowed
|
|
results = catalog.search(
|
|
query: Optional[str] = None,
|
|
tag: Optional[str] = None,
|
|
author: Optional[str] = None,
|
|
verified_only: bool = False
|
|
) # Returns: List[Dict] — each dict includes _catalog_name, _install_allowed
|
|
|
|
# Get extension info (searches all active catalogs)
|
|
# Returns None if not found; includes _catalog_name and _install_allowed
|
|
ext_info = catalog.get_extension_info(extension_id: str) # Optional[Dict]
|
|
|
|
# Check cache validity (primary catalog)
|
|
is_valid = catalog.is_cache_valid() # bool
|
|
|
|
# Clear all catalog caches
|
|
catalog.clear_cache()
|
|
```
|
|
|
|
**Result annotation fields**:
|
|
|
|
Each extension dict returned by `search()` and `get_extension_info()` includes:
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `_catalog_name` | `str` | Name of the source catalog |
|
|
| `_install_allowed` | `bool` | Whether installation is allowed from this catalog |
|
|
|
|
**Catalog config file** (`.specify/extension-catalogs.yml`):
|
|
|
|
```yaml
|
|
catalogs:
|
|
- name: "default"
|
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json"
|
|
priority: 1
|
|
install_allowed: true
|
|
description: "Built-in catalog of installable extensions"
|
|
- name: "community"
|
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json"
|
|
priority: 2
|
|
install_allowed: false
|
|
description: "Community-contributed extensions (discovery only)"
|
|
```
|
|
|
|
### HookExecutor
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
```python
|
|
from specify_cli.extensions import HookExecutor
|
|
|
|
hook_executor = HookExecutor(project_root)
|
|
```
|
|
|
|
**Methods**:
|
|
|
|
```python
|
|
# Get project config
|
|
config = hook_executor.get_project_config() # Dict
|
|
|
|
# Save project config
|
|
hook_executor.save_project_config(config: Dict)
|
|
|
|
# Register hooks
|
|
hook_executor.register_hooks(manifest: ExtensionManifest)
|
|
|
|
# Unregister hooks
|
|
hook_executor.unregister_hooks(extension_id: str)
|
|
|
|
# Get hooks for event
|
|
hooks = hook_executor.get_hooks_for_event(event_name: str) # List[Dict]
|
|
|
|
# Check if hook should execute
|
|
should_run = hook_executor.should_execute_hook(hook: Dict) # bool
|
|
|
|
# Format hook message
|
|
message = hook_executor.format_hook_message(
|
|
event_name: str,
|
|
hooks: List[Dict]
|
|
) # str
|
|
```
|
|
|
|
### CommandRegistrar
|
|
|
|
**Module**: `specify_cli.extensions`
|
|
|
|
```python
|
|
from specify_cli.extensions import CommandRegistrar
|
|
|
|
registrar = CommandRegistrar()
|
|
```
|
|
|
|
**Methods**:
|
|
|
|
```python
|
|
# Register commands for Claude Code
|
|
registered = registrar.register_commands_for_claude(
|
|
manifest: ExtensionManifest,
|
|
extension_dir: Path,
|
|
project_root: Path
|
|
) # Returns: List[str] (command names)
|
|
|
|
# Parse frontmatter
|
|
frontmatter, body = registrar.parse_frontmatter(content: str)
|
|
|
|
# Render frontmatter
|
|
yaml_text = registrar.render_frontmatter(frontmatter: Dict) # str
|
|
```
|
|
|
|
---
|
|
|
|
## Command File Format
|
|
|
|
### Universal Command Format
|
|
|
|
**File**: `commands/{command-name}.md`
|
|
|
|
```markdown
|
|
---
|
|
description: "Command description"
|
|
tools:
|
|
- 'mcp-server/tool_name'
|
|
- 'other-mcp-server/other_tool'
|
|
---
|
|
|
|
# Command Title
|
|
|
|
Command documentation in Markdown.
|
|
|
|
## Prerequisites
|
|
|
|
1. Requirement 1
|
|
2. Requirement 2
|
|
|
|
## User Input
|
|
|
|
$ARGUMENTS
|
|
|
|
## Steps
|
|
|
|
### Step 1: Description
|
|
|
|
Instruction text...
|
|
|
|
\`\`\`bash
|
|
# Shell commands
|
|
\`\`\`
|
|
|
|
### Step 2: Another Step
|
|
|
|
More instructions...
|
|
|
|
## Configuration Reference
|
|
|
|
Information about configuration options.
|
|
|
|
## Notes
|
|
|
|
Additional notes and tips.
|
|
```
|
|
|
|
### Frontmatter Fields
|
|
|
|
```yaml
|
|
description: string # Required, brief command description
|
|
tools: [string] # Optional, MCP tools required
|
|
```
|
|
|
|
### Special Variables
|
|
|
|
- `$ARGUMENTS` - Placeholder for user-provided arguments
|
|
- Extension context automatically injected:
|
|
|
|
```markdown
|
|
<!-- Extension: {extension-id} -->
|
|
<!-- Config: .specify/extensions/{extension-id}/ -->
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Schema
|
|
|
|
### Extension Config File
|
|
|
|
**File**: `.specify/extensions/{extension-id}/{extension-id}-config.yml`
|
|
|
|
Extensions define their own config schema. Common patterns:
|
|
|
|
```yaml
|
|
# Connection settings
|
|
connection:
|
|
url: string
|
|
api_key: string
|
|
|
|
# Project settings
|
|
project:
|
|
key: string
|
|
workspace: string
|
|
|
|
# Feature flags
|
|
features:
|
|
enabled: boolean
|
|
auto_sync: boolean
|
|
|
|
# Defaults
|
|
defaults:
|
|
labels: [string]
|
|
assignee: string
|
|
|
|
# Custom fields
|
|
field_mappings:
|
|
internal_name: "external_field_id"
|
|
```
|
|
|
|
### Config Layers
|
|
|
|
1. **Extension Defaults** (from `extension.yml` `defaults` section)
|
|
2. **Project Config** (`{extension-id}-config.yml`)
|
|
3. **Local Override** (`{extension-id}-config.local.yml`, gitignored)
|
|
4. **Environment Variables** (`SPECKIT_{EXTENSION}_*`)
|
|
|
|
### Environment Variable Pattern
|
|
|
|
Format: `SPECKIT_{EXTENSION}_{KEY}`
|
|
|
|
Examples:
|
|
|
|
- `SPECKIT_JIRA_PROJECT_KEY`
|
|
- `SPECKIT_LINEAR_API_KEY`
|
|
- `SPECKIT_GITHUB_TOKEN`
|
|
|
|
---
|
|
|
|
## 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: null
|
|
```
|
|
|
|
### Hook Events
|
|
|
|
Standard events (defined by core):
|
|
|
|
- `after_tasks` - After task generation
|
|
- `after_implement` - After implementation
|
|
- `before_commit` - Before git commit
|
|
- `after_commit` - After git commit
|
|
|
|
### Hook Configuration
|
|
|
|
**In `.specify/extensions.yml`**:
|
|
|
|
```yaml
|
|
hooks:
|
|
after_tasks:
|
|
- extension: jira
|
|
command: speckit.jira.specstoissues
|
|
enabled: true
|
|
optional: true
|
|
prompt: "Create Jira issues from tasks?"
|
|
description: "..."
|
|
condition: null
|
|
```
|
|
|
|
### Hook Message Format
|
|
|
|
```markdown
|
|
## Extension Hooks
|
|
|
|
**Optional Hook**: {extension}
|
|
Command: `/{command}`
|
|
Description: {description}
|
|
|
|
Prompt: {prompt}
|
|
To execute: `/{command}`
|
|
```
|
|
|
|
Or for mandatory hooks:
|
|
|
|
```markdown
|
|
**Automatic Hook**: {extension}
|
|
Executing: `/{command}`
|
|
EXECUTE_COMMAND: {command}
|
|
```
|
|
|
|
---
|
|
|
|
## CLI Commands
|
|
|
|
### extension list
|
|
|
|
**Usage**: `specify extension list [OPTIONS]`
|
|
|
|
**Options**:
|
|
|
|
- `--available` - Show available extensions from catalog
|
|
- `--all` - Show both installed and available
|
|
|
|
**Output**: List of installed extensions with metadata
|
|
|
|
### extension catalog list
|
|
|
|
**Usage**: `specify extension catalog list`
|
|
|
|
Lists all active catalogs in the current catalog stack, showing name, description, URL, priority, and `install_allowed` status.
|
|
|
|
### extension catalog add
|
|
|
|
**Usage**: `specify extension catalog add URL [OPTIONS]`
|
|
|
|
**Options**:
|
|
|
|
- `--name NAME` - Catalog name (required)
|
|
- `--priority INT` - Priority (lower = higher priority, default: 10)
|
|
- `--install-allowed / --no-install-allowed` - Allow installs from this catalog (default: false)
|
|
- `--description TEXT` - Optional description of the catalog
|
|
|
|
**Arguments**:
|
|
|
|
- `URL` - Catalog URL (must use HTTPS)
|
|
|
|
Adds a catalog entry to `.specify/extension-catalogs.yml`.
|
|
|
|
### extension catalog remove
|
|
|
|
**Usage**: `specify extension catalog remove NAME`
|
|
|
|
**Arguments**:
|
|
|
|
- `NAME` - Catalog name to remove
|
|
|
|
Removes a catalog entry from `.specify/extension-catalogs.yml`.
|
|
|
|
### extension add
|
|
|
|
**Usage**: `specify extension add EXTENSION [OPTIONS]`
|
|
|
|
**Options**:
|
|
|
|
- `--from URL` - Install from custom URL
|
|
- `--dev PATH` - Install from local directory
|
|
|
|
**Arguments**:
|
|
|
|
- `EXTENSION` - Extension name or URL
|
|
|
|
**Note**: Extensions from catalogs with `install_allowed: false` cannot be installed via this command.
|
|
|
|
### extension remove
|
|
|
|
**Usage**: `specify extension remove EXTENSION [OPTIONS]`
|
|
|
|
**Options**:
|
|
|
|
- `--keep-config` - Preserve config files
|
|
- `--force` - Skip confirmation
|
|
|
|
**Arguments**:
|
|
|
|
- `EXTENSION` - Extension ID
|
|
|
|
### extension search
|
|
|
|
**Usage**: `specify extension search [QUERY] [OPTIONS]`
|
|
|
|
Searches all active catalogs simultaneously. Results include source catalog name and install_allowed status.
|
|
|
|
**Options**:
|
|
|
|
- `--tag TAG` - Filter by tag
|
|
- `--author AUTHOR` - Filter by author
|
|
- `--verified` - Show only verified extensions
|
|
|
|
**Arguments**:
|
|
|
|
- `QUERY` - Optional search query
|
|
|
|
### extension info
|
|
|
|
**Usage**: `specify extension info EXTENSION`
|
|
|
|
Shows source catalog and install_allowed status.
|
|
|
|
**Arguments**:
|
|
|
|
- `EXTENSION` - Extension ID
|
|
|
|
### extension update
|
|
|
|
**Usage**: `specify extension update [EXTENSION]`
|
|
|
|
**Arguments**:
|
|
|
|
- `EXTENSION` - Optional, extension ID (default: all)
|
|
|
|
### extension enable
|
|
|
|
**Usage**: `specify extension enable EXTENSION`
|
|
|
|
**Arguments**:
|
|
|
|
- `EXTENSION` - Extension ID
|
|
|
|
### extension disable
|
|
|
|
**Usage**: `specify extension disable EXTENSION`
|
|
|
|
**Arguments**:
|
|
|
|
- `EXTENSION` - Extension ID
|
|
|
|
---
|
|
|
|
## Exceptions
|
|
|
|
### ValidationError
|
|
|
|
Raised when extension manifest validation fails.
|
|
|
|
```python
|
|
from specify_cli.extensions import ValidationError
|
|
|
|
try:
|
|
manifest = ExtensionManifest(path)
|
|
except ValidationError as e:
|
|
print(f"Invalid manifest: {e}")
|
|
```
|
|
|
|
### CompatibilityError
|
|
|
|
Raised when extension is incompatible with current spec-kit version.
|
|
|
|
```python
|
|
from specify_cli.extensions import CompatibilityError
|
|
|
|
try:
|
|
manager.check_compatibility(manifest, "0.1.0")
|
|
except CompatibilityError as e:
|
|
print(f"Incompatible: {e}")
|
|
```
|
|
|
|
### ExtensionError
|
|
|
|
Base exception for all extension-related errors.
|
|
|
|
```python
|
|
from specify_cli.extensions import ExtensionError
|
|
|
|
try:
|
|
manager.install_from_directory(path, "0.1.0")
|
|
except ExtensionError as e:
|
|
print(f"Extension error: {e}")
|
|
```
|
|
|
|
---
|
|
|
|
## Version Functions
|
|
|
|
### version_satisfies
|
|
|
|
Check if a version satisfies a specifier.
|
|
|
|
```python
|
|
from specify_cli.extensions import version_satisfies
|
|
|
|
# True if 1.2.3 satisfies >=1.0.0,<2.0.0
|
|
satisfied = version_satisfies("1.2.3", ">=1.0.0,<2.0.0") # bool
|
|
```
|
|
|
|
---
|
|
|
|
## File System Layout
|
|
|
|
```text
|
|
.specify/
|
|
├── extensions/
|
|
│ ├── .registry # Extension registry (JSON)
|
|
│ ├── .cache/ # Catalog cache
|
|
│ │ ├── catalog.json
|
|
│ │ └── catalog-metadata.json
|
|
│ ├── .backup/ # Config backups
|
|
│ │ └── {ext}-{config}.yml
|
|
│ ├── {extension-id}/ # Extension directory
|
|
│ │ ├── extension.yml # Manifest
|
|
│ │ ├── {ext}-config.yml # User config
|
|
│ │ ├── {ext}-config.local.yml # Local overrides (gitignored)
|
|
│ │ ├── {ext}-config.template.yml # Template
|
|
│ │ ├── commands/ # Command files
|
|
│ │ │ └── *.md
|
|
│ │ ├── scripts/ # Helper scripts
|
|
│ │ │ └── *.sh
|
|
│ │ ├── docs/ # Documentation
|
|
│ │ └── README.md
|
|
│ └── extensions.yml # Project extension config
|
|
└── scripts/ # (existing spec-kit)
|
|
|
|
.claude/
|
|
└── commands/
|
|
└── speckit.{ext}.{cmd}.md # Registered commands
|
|
```
|
|
|
|
---
|
|
|
|
*Last Updated: 2026-01-28*
|
|
*API Version: 1.0*
|
|
*Spec Kit Version: 0.1.0*
|