|
1 | 1 | # RFC: Spec Kit Extension System |
2 | 2 |
|
3 | | -**Status**: Draft |
| 3 | +**Status**: Implemented |
4 | 4 | **Author**: Stats Perform Engineering |
5 | 5 | **Created**: 2026-01-28 |
6 | | -**Updated**: 2026-01-28 |
| 6 | +**Updated**: 2026-03-11 |
7 | 7 |
|
8 | 8 | --- |
9 | 9 |
|
|
24 | 24 | 13. [Security Considerations](#security-considerations) |
25 | 25 | 14. [Migration Strategy](#migration-strategy) |
26 | 26 | 15. [Implementation Phases](#implementation-phases) |
27 | | -16. [Open Questions](#open-questions) |
28 | | -17. [Appendices](#appendices) |
| 27 | +16. [Resolved Questions](#resolved-questions) |
| 28 | +17. [Open Questions (Remaining)](#open-questions-remaining) |
| 29 | +18. [Appendices](#appendices) |
29 | 30 |
|
30 | 31 | --- |
31 | 32 |
|
@@ -1504,203 +1505,225 @@ AI agent registers both names, so old scripts work. |
1504 | 1505 |
|
1505 | 1506 | ## Implementation Phases |
1506 | 1507 |
|
1507 | | -### Phase 1: Core Extension System (Week 1-2) |
| 1508 | +### Phase 1: Core Extension System ✅ COMPLETED |
1508 | 1509 |
|
1509 | 1510 | **Goal**: Basic extension infrastructure |
1510 | 1511 |
|
1511 | 1512 | **Deliverables**: |
1512 | 1513 |
|
1513 | | -- [ ] Extension manifest schema (`extension.yml`) |
1514 | | -- [ ] Extension directory structure |
1515 | | -- [ ] CLI commands: |
1516 | | - - [ ] `specify extension list` |
1517 | | - - [ ] `specify extension add` (from URL) |
1518 | | - - [ ] `specify extension remove` |
1519 | | -- [ ] Extension registry (`.specify/extensions/.registry`) |
1520 | | -- [ ] Command registration (Claude only initially) |
1521 | | -- [ ] Basic validation (manifest schema, compatibility) |
1522 | | -- [ ] Documentation (extension development guide) |
| 1514 | +- [x] Extension manifest schema (`extension.yml`) |
| 1515 | +- [x] Extension directory structure |
| 1516 | +- [x] CLI commands: |
| 1517 | + - [x] `specify extension list` |
| 1518 | + - [x] `specify extension add` (from URL and local `--dev`) |
| 1519 | + - [x] `specify extension remove` |
| 1520 | +- [x] Extension registry (`.specify/extensions/.registry`) |
| 1521 | +- [x] Command registration (Claude and 15+ other agents) |
| 1522 | +- [x] Basic validation (manifest schema, compatibility) |
| 1523 | +- [x] Documentation (extension development guide) |
1523 | 1524 |
|
1524 | 1525 | **Testing**: |
1525 | 1526 |
|
1526 | | -- [ ] Unit tests for manifest parsing |
1527 | | -- [ ] Integration test: Install dummy extension |
1528 | | -- [ ] Integration test: Register commands with Claude |
| 1527 | +- [x] Unit tests for manifest parsing |
| 1528 | +- [x] Integration test: Install dummy extension |
| 1529 | +- [x] Integration test: Register commands with Claude |
1529 | 1530 |
|
1530 | | -### Phase 2: Jira Extension (Week 3) |
| 1531 | +### Phase 2: Jira Extension ✅ COMPLETED |
1531 | 1532 |
|
1532 | 1533 | **Goal**: First production extension |
1533 | 1534 |
|
1534 | 1535 | **Deliverables**: |
1535 | 1536 |
|
1536 | | -- [ ] Create `spec-kit-jira` repository |
1537 | | -- [ ] Port Jira functionality to extension |
1538 | | -- [ ] Create `jira-config.yml` template |
1539 | | -- [ ] Commands: |
1540 | | - - [ ] `specstoissues.md` |
1541 | | - - [ ] `discover-fields.md` |
1542 | | - - [ ] `sync-status.md` |
1543 | | -- [ ] Helper scripts |
1544 | | -- [ ] Documentation (README, configuration guide, examples) |
1545 | | -- [ ] Release v1.0.0 |
| 1537 | +- [x] Create `spec-kit-jira` repository |
| 1538 | +- [x] Port Jira functionality to extension |
| 1539 | +- [x] Create `jira-config.yml` template |
| 1540 | +- [x] Commands: |
| 1541 | + - [x] `specstoissues.md` |
| 1542 | + - [x] `discover-fields.md` |
| 1543 | + - [x] `sync-status.md` |
| 1544 | +- [x] Helper scripts |
| 1545 | +- [x] Documentation (README, configuration guide, examples) |
| 1546 | +- [x] Release v3.0.0 |
1546 | 1547 |
|
1547 | 1548 | **Testing**: |
1548 | 1549 |
|
1549 | | -- [ ] Test on `eng-msa-ts` project |
1550 | | -- [ ] Verify spec→Epic, phase→Story, task→Issue mapping |
1551 | | -- [ ] Test configuration loading and validation |
1552 | | -- [ ] Test custom field application |
| 1550 | +- [x] Test on `eng-msa-ts` project |
| 1551 | +- [x] Verify spec→Epic, phase→Story, task→Issue mapping |
| 1552 | +- [x] Test configuration loading and validation |
| 1553 | +- [x] Test custom field application |
1553 | 1554 |
|
1554 | | -### Phase 3: Extension Catalog (Week 4) |
| 1555 | +### Phase 3: Extension Catalog ✅ COMPLETED |
1555 | 1556 |
|
1556 | 1557 | **Goal**: Discovery and distribution |
1557 | 1558 |
|
1558 | 1559 | **Deliverables**: |
1559 | 1560 |
|
1560 | | -- [ ] Central catalog (`extensions/catalog.json` in spec-kit repo) |
1561 | | -- [ ] Catalog fetch and parsing |
1562 | | -- [ ] CLI commands: |
1563 | | - - [ ] `specify extension search` |
1564 | | - - [ ] `specify extension info` |
1565 | | -- [ ] Catalog publishing process (GitHub Action) |
1566 | | -- [ ] Documentation (how to publish extensions) |
| 1561 | +- [x] Central catalog (`extensions/catalog.json` in spec-kit repo) |
| 1562 | +- [x] Community catalog (`extensions/catalog.community.json`) |
| 1563 | +- [x] Catalog fetch and parsing with multi-catalog support |
| 1564 | +- [x] CLI commands: |
| 1565 | + - [x] `specify extension search` |
| 1566 | + - [x] `specify extension info` |
| 1567 | + - [x] `specify extension catalog list` |
| 1568 | + - [x] `specify extension catalog add` |
| 1569 | + - [x] `specify extension catalog remove` |
| 1570 | +- [x] Documentation (how to publish extensions) |
1567 | 1571 |
|
1568 | 1572 | **Testing**: |
1569 | 1573 |
|
1570 | | -- [ ] Test catalog fetch |
1571 | | -- [ ] Test extension search/filtering |
1572 | | -- [ ] Test catalog caching |
| 1574 | +- [x] Test catalog fetch |
| 1575 | +- [x] Test extension search/filtering |
| 1576 | +- [x] Test catalog caching |
| 1577 | +- [x] Test multi-catalog merge with priority |
1573 | 1578 |
|
1574 | | -### Phase 4: Advanced Features (Week 5-6) |
| 1579 | +### Phase 4: Advanced Features ✅ COMPLETED |
1575 | 1580 |
|
1576 | 1581 | **Goal**: Hooks, updates, multi-agent support |
1577 | 1582 |
|
1578 | 1583 | **Deliverables**: |
1579 | 1584 |
|
1580 | | -- [ ] Hook system (`hooks` in extension.yml) |
1581 | | -- [ ] Hook registration and execution |
1582 | | -- [ ] Project extensions config (`.specify/extensions.yml`) |
1583 | | -- [ ] CLI commands: |
1584 | | - - [ ] `specify extension update` |
1585 | | - - [ ] `specify extension enable/disable` |
1586 | | -- [ ] Command registration for multiple agents (Gemini, Copilot) |
1587 | | -- [ ] Extension update notifications |
1588 | | -- [ ] Configuration layer resolution (project, local, env) |
| 1585 | +- [x] Hook system (`hooks` in extension.yml) |
| 1586 | +- [x] Hook registration and execution |
| 1587 | +- [x] Project extensions config (`.specify/extensions.yml`) |
| 1588 | +- [x] CLI commands: |
| 1589 | + - [x] `specify extension update` (with atomic backup/restore) |
| 1590 | + - [x] `specify extension enable/disable` |
| 1591 | +- [x] Command registration for multiple agents (15+ agents including Claude, Copilot, Gemini, Cursor, etc.) |
| 1592 | +- [x] Extension update notifications (version comparison) |
| 1593 | +- [x] Configuration layer resolution (project, local, env) |
| 1594 | + |
| 1595 | +**Additional features implemented beyond original RFC**: |
| 1596 | + |
| 1597 | +- [x] **Display name resolution**: All commands accept extension display names in addition to IDs |
| 1598 | +- [x] **Ambiguous name handling**: User-friendly tables when multiple extensions match a name |
| 1599 | +- [x] **Atomic update with rollback**: Full backup of extension dir, commands, hooks, and registry with automatic rollback on failure |
| 1600 | +- [x] **Pre-install ID validation**: Validates extension ID from ZIP before installing (security) |
| 1601 | +- [x] **Enabled state preservation**: Disabled extensions stay disabled after update |
| 1602 | +- [x] **Registry update/restore methods**: Clean API for enable/disable and rollback operations |
| 1603 | +- [x] **Catalog error fallback**: `extension info` falls back to local info when catalog unavailable |
| 1604 | +- [x] **`_install_allowed` flag**: Discovery-only catalogs can't be used for installation |
| 1605 | +- [x] **Cache invalidation**: Cache invalidated when `SPECKIT_CATALOG_URL` changes |
1589 | 1606 |
|
1590 | 1607 | **Testing**: |
1591 | 1608 |
|
1592 | | -- [ ] Test hooks in core commands |
1593 | | -- [ ] Test extension updates (preserve config) |
1594 | | -- [ ] Test multi-agent registration |
| 1609 | +- [x] Test hooks in core commands |
| 1610 | +- [x] Test extension updates (preserve config) |
| 1611 | +- [x] Test multi-agent registration |
| 1612 | +- [x] Test atomic rollback on update failure |
| 1613 | +- [x] Test enabled state preservation |
| 1614 | +- [x] Test display name resolution |
1595 | 1615 |
|
1596 | | -### Phase 5: Polish & Documentation (Week 7) |
| 1616 | +### Phase 5: Polish & Documentation ✅ COMPLETED |
1597 | 1617 |
|
1598 | 1618 | **Goal**: Production ready |
1599 | 1619 |
|
1600 | 1620 | **Deliverables**: |
1601 | 1621 |
|
1602 | | -- [ ] Comprehensive documentation: |
1603 | | - - [ ] User guide (installing/using extensions) |
1604 | | - - [ ] Extension development guide |
1605 | | - - [ ] Extension API reference |
1606 | | - - [ ] Migration guide (core → extension) |
1607 | | -- [ ] Error messages and validation improvements |
1608 | | -- [ ] CLI help text updates |
1609 | | -- [ ] Example extension template (cookiecutter) |
1610 | | -- [ ] Blog post / announcement |
1611 | | -- [ ] Video tutorial |
| 1622 | +- [x] Comprehensive documentation: |
| 1623 | + - [x] User guide (EXTENSION-USER-GUIDE.md) |
| 1624 | + - [x] Extension development guide (EXTENSION-DEV-GUIDE.md) |
| 1625 | + - [x] Extension API reference (EXTENSION-API-REFERENCE.md) |
| 1626 | +- [x] Error messages and validation improvements |
| 1627 | +- [x] CLI help text updates |
1612 | 1628 |
|
1613 | 1629 | **Testing**: |
1614 | 1630 |
|
1615 | | -- [ ] End-to-end testing on multiple projects |
1616 | | -- [ ] Community beta testing |
1617 | | -- [ ] Performance testing (large projects) |
| 1631 | +- [x] End-to-end testing on multiple projects |
| 1632 | +- [x] 163 unit tests passing |
1618 | 1633 |
|
1619 | 1634 | --- |
1620 | 1635 |
|
1621 | | -## Open Questions |
| 1636 | +## Resolved Questions |
1622 | 1637 |
|
1623 | | -### 1. Extension Namespace |
| 1638 | +The following questions from the original RFC have been resolved during implementation: |
1624 | 1639 |
|
1625 | | -**Question**: Should extension commands use namespace prefix? |
| 1640 | +### 1. Extension Namespace ✅ RESOLVED |
1626 | 1641 |
|
1627 | | -**Options**: |
| 1642 | +**Question**: Should extension commands use namespace prefix? |
1628 | 1643 |
|
1629 | | -- A) Prefixed: `/speckit.jira.specstoissues` (explicit, avoids conflicts) |
1630 | | -- B) Short alias: `/jira.specstoissues` (shorter, less verbose) |
1631 | | -- C) Both: Register both names, prefer prefixed in docs |
| 1644 | +**Decision**: **Option C** - Both prefixed and aliases are supported. Commands use `speckit.{extension}.{command}` as canonical name, with optional aliases defined in manifest. |
1632 | 1645 |
|
1633 | | -**Recommendation**: C (both), prefixed is canonical |
| 1646 | +**Implementation**: The `aliases` field in `extension.yml` allows extensions to register additional command names. |
1634 | 1647 |
|
1635 | 1648 | --- |
1636 | 1649 |
|
1637 | | -### 2. Config File Location |
| 1650 | +### 2. Config File Location ✅ RESOLVED |
1638 | 1651 |
|
1639 | 1652 | **Question**: Where should extension configs live? |
1640 | 1653 |
|
1641 | | -**Options**: |
1642 | | - |
1643 | | -- A) Extension directory: `.specify/extensions/jira/jira-config.yml` (encapsulated) |
1644 | | -- B) Root level: `.specify/jira-config.yml` (more visible) |
1645 | | -- C) Unified: `.specify/extensions.yml` (all extension configs in one file) |
| 1654 | +**Decision**: **Option A** - Extension directory (`.specify/extensions/{ext-id}/{ext-id}-config.yml`). This keeps extensions self-contained and easier to manage. |
1646 | 1655 |
|
1647 | | -**Recommendation**: A (extension directory), cleaner separation |
| 1656 | +**Implementation**: Each extension has its own config file within its directory, with layered resolution (defaults → project → local → env vars). |
1648 | 1657 |
|
1649 | 1658 | --- |
1650 | 1659 |
|
1651 | | -### 3. Command File Format |
| 1660 | +### 3. Command File Format ✅ RESOLVED |
1652 | 1661 |
|
1653 | 1662 | **Question**: Should extensions use universal format or agent-specific? |
1654 | 1663 |
|
1655 | | -**Options**: |
1656 | | - |
1657 | | -- A) Universal Markdown: Extensions write once, CLI converts per-agent |
1658 | | -- B) Agent-specific: Extensions provide separate files for each agent |
1659 | | -- C) Hybrid: Universal default, agent-specific overrides |
| 1664 | +**Decision**: **Option A** - Universal Markdown format. Extensions write commands once, CLI converts to agent-specific format during registration. |
1660 | 1665 |
|
1661 | | -**Recommendation**: A (universal), reduces duplication |
| 1666 | +**Implementation**: `CommandRegistrar` class handles conversion to 15+ agent formats (Claude, Copilot, Gemini, Cursor, etc.). |
1662 | 1667 |
|
1663 | 1668 | --- |
1664 | 1669 |
|
1665 | | -### 4. Hook Execution Model |
| 1670 | +### 4. Hook Execution Model ✅ RESOLVED |
1666 | 1671 |
|
1667 | 1672 | **Question**: How should hooks execute? |
1668 | 1673 |
|
1669 | | -**Options**: |
1670 | | - |
1671 | | -- A) AI agent interprets: Core commands output `EXECUTE_COMMAND: name` |
1672 | | -- B) CLI executes: Core commands call `specify extension hook after_tasks` |
1673 | | -- C) Agent built-in: Extension system built into AI agent (Claude SDK) |
| 1674 | +**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. |
1674 | 1675 |
|
1675 | | -**Recommendation**: A initially (simpler), move to C long-term |
| 1676 | +**Implementation**: `HookExecutor` class manages hook registration and state in `extensions.yml`. |
1676 | 1677 |
|
1677 | 1678 | --- |
1678 | 1679 |
|
1679 | | -### 5. Extension Distribution |
| 1680 | +### 5. Extension Distribution ✅ RESOLVED |
1680 | 1681 |
|
1681 | 1682 | **Question**: How should extensions be packaged? |
1682 | 1683 |
|
| 1684 | +**Decision**: **Option A** - ZIP archives downloaded from GitHub releases (via catalog `download_url`). Local development uses `--dev` flag with directory path. |
| 1685 | + |
| 1686 | +**Implementation**: `ExtensionManager.install_from_zip()` handles ZIP extraction and validation. |
| 1687 | + |
| 1688 | +--- |
| 1689 | + |
| 1690 | +### 6. Multi-Version Support ✅ RESOLVED |
| 1691 | + |
| 1692 | +**Question**: Can multiple versions of same extension coexist? |
| 1693 | + |
| 1694 | +**Decision**: **Option A** - Single version only. Updates replace the existing version with atomic rollback on failure. |
| 1695 | + |
| 1696 | +**Implementation**: `extension update` performs atomic backup/restore to ensure safe updates. |
| 1697 | + |
| 1698 | +--- |
| 1699 | + |
| 1700 | +## Open Questions (Remaining) |
| 1701 | + |
| 1702 | +### 1. Sandboxing / Permissions (Future) |
| 1703 | + |
| 1704 | +**Question**: Should extensions declare required permissions? |
| 1705 | + |
1683 | 1706 | **Options**: |
1684 | 1707 |
|
1685 | | -- A) ZIP archives: Downloaded from GitHub releases |
1686 | | -- B) Git repos: Cloned directly (`git clone`) |
1687 | | -- C) Python packages: Installable via `uv tool install` |
| 1708 | +- A) No sandboxing (current): Extensions run with same privileges as AI agent |
| 1709 | +- B) Permission declarations: Extensions declare `filesystem:read`, `network:external`, etc. |
| 1710 | +- C) Opt-in sandboxing: Organizations can enable permission enforcement |
1688 | 1711 |
|
1689 | | -**Recommendation**: A (ZIP), simpler for non-Python extensions in future |
| 1712 | +**Status**: Deferred to future version. Currently using trust-based model where users trust extension authors. |
1690 | 1713 |
|
1691 | 1714 | --- |
1692 | 1715 |
|
1693 | | -### 6. Multi-Version Support |
| 1716 | +### 2. Package Signatures (Future) |
1694 | 1717 |
|
1695 | | -**Question**: Can multiple versions of same extension coexist? |
| 1718 | +**Question**: Should extensions be cryptographically signed? |
1696 | 1719 |
|
1697 | 1720 | **Options**: |
1698 | 1721 |
|
1699 | | -- A) Single version: Only one version installed at a time |
1700 | | -- B) Multi-version: Side-by-side versions (`.specify/extensions/jira@1.0/`, `.specify/extensions/jira@2.0/`) |
1701 | | -- C) Per-branch: Different branches use different versions |
| 1722 | +- A) No signatures (current): Trust based on catalog source |
| 1723 | +- B) GPG/Sigstore signatures: Verify package integrity |
| 1724 | +- C) Catalog-level verification: Catalog maintainers verify packages |
1702 | 1725 |
|
1703 | | -**Recommendation**: A initially (simpler), consider B in future if needed |
| 1726 | +**Status**: Deferred to future version. `checksum` field is available in catalog schema but not enforced. |
1704 | 1727 |
|
1705 | 1728 | --- |
1706 | 1729 |
|
|
0 commit comments