Skip to content

feat(templates): Pluggable Template System with Template Catalog #1708

@mnriem

Description

@mnriem

Summary

Introduce a pluggable template system that decouples Spec Kit's artifact templates (spec, plan, tasks, checklist, constitution, etc.) and command templates from the core release, making them discoverable, customizable, and distributable — similar to the extension system. This includes a template catalog that mirrors the multi-catalog pattern proposed in #1707 for extensions.

Problem Statement

Today, templates are hardcoded into the release ZIP and copied verbatim during specify init. This creates several problems:

Problem Impact
No customization without forking Organizations must fork the entire repo to modify spec-template.md or plan-template.md — even if they just want to add a "Compliance" section
One-size-fits-all artifacts Healthcare projects, fintech compliance, game development, and microservices all get the same spec/plan/tasks structure
Templates can't evolve independently A community-contributed "SAFe tasks template" or "ADR-based plan template" requires a core PR + release cycle
Extensions can't provide templates The extension system supports commands and hooks but has no way to provide custom artifact templates (e.g., a Jira extension wanting a Jira-aware tasks template)
No template composition Users can't layer modifications (org base + team overrides + project-specific) — it's all-or-nothing replacement
Scripts hardcode template paths create-new-feature.sh and setup-plan.sh reference .specify/templates/spec-template.md and .specify/templates/plan-template.md directly with no resolution mechanism

Proposed Solution

1. Template Pack Manifest (template-pack.yml)

A template pack is a self-contained, versioned collection of templates with a declarative manifest — mirroring the extension extension.yml pattern:

schema_version: "1.0"

template_pack:
  id: "healthcare-compliance"          # Unique identifier (lowercase, alphanumeric, hyphens)
  name: "Healthcare Compliance Templates"
  version: "1.0.0"
  description: "HIPAA/SOX-aware spec and plan templates with compliance sections"
  author: "healthcare-org"
  repository: "https://github.com/healthcare-org/spec-kit-templates-healthcare"
  license: "MIT"

requires:
  speckit_version: ">=0.1.0"

provides:
  templates:
    # Artifact templates (scaffolds for spec.md, plan.md, etc.)
    - type: "artifact"
      name: "spec-template"             # Overrides core spec-template.md
      file: "templates/spec-template.md"
      description: "HIPAA-compliant feature specification with PHI sections"
      replaces: "spec-template"          # Which core template this overrides (optional)

    - type: "artifact"
      name: "plan-template"
      file: "templates/plan-template.md"
      description: "Plan template with compliance review gates"
      replaces: "plan-template"

    - type: "artifact"
      name: "compliance-checklist-template"
      file: "templates/compliance-checklist-template.md"
      description: "HIPAA compliance checklist (new, does not replace core)"
      # No 'replaces' — this is additive

    # Command templates (prompts that drive AI agents)
    - type: "command"
      name: "specify"
      file: "commands/specify.md"
      description: "Specification command with compliance guidance"
      replaces: "specify"

tags: ["healthcare", "hipaa", "compliance", "regulated"]

Template types:

  • artifact — Document scaffolds materialized as files (spec.md, plan.md, tasks.md, checklist.md, constitution.md). These are copied into the specs/<feature>/ directory when creating new features.
  • command — AI agent prompts (the files in .claude/commands/, .github/agents/, etc.). These control how the AI agent produces artifacts.

2. Template Resolution Order

When a script or command needs a template (e.g., create-new-feature.sh needs spec-template.md), the system resolves it through a layered stack:

Priority 1: .specify/templates/overrides/          ← Project-local overrides (manual edits)
Priority 2: .specify/templates/packs/<pack-id>/    ← Installed template packs
Priority 3: .specify/extensions/<ext-id>/templates/ ← Extension-provided templates
Priority 4: .specify/templates/                    ← Core templates (shipped with Spec Kit)

A new helper function (resolve_template()) replaces hardcoded paths:

# Before (hardcoded):
TEMPLATE="$REPO_ROOT/.specify/templates/spec-template.md"

# After (resolved):
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT")

This function walks the priority stack and returns the first match, enabling non-destructive customization.

3. Template Catalog (Dual-Catalog, Multi-Catalog Ready)

Mirrors the extension catalog structure and is designed to integrate with the multi-catalog stack from #1707:

templates/catalog.json (org-curated, empty by default):

{
  "schema_version": "1.0",
  "updated_at": "2026-02-26T00:00:00Z",
  "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/templates/catalog.json",
  "template_packs": {}
}

templates/catalog.community.json (community-contributed):

{
  "schema_version": "1.0",
  "updated_at": "2026-02-26T00:00:00Z",
  "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/templates/catalog.community.json",
  "template_packs": {
    "safe-agile": {
      "name": "SAFe Agile Templates",
      "id": "safe-agile",
      "description": "SAFe-aligned spec, plan, and tasks templates with PI planning support",
      "author": "agile-community",
      "version": "1.0.0",
      "download_url": "https://github.com/agile-community/spec-kit-templates-safe/archive/refs/tags/v1.0.0.zip",
      "repository": "https://github.com/agile-community/spec-kit-templates-safe",
      "license": "MIT",
      "requires": { "speckit_version": ">=0.1.0" },
      "provides": { "artifact_templates": 3, "command_templates": 2 },
      "tags": ["safe", "agile", "pi-planning", "enterprise"],
      "verified": false,
      "downloads": 0,
      "created_at": "2026-02-26T00:00:00Z",
      "updated_at": "2026-02-26T00:00:00Z"
    }
  }
}

When #1707 lands, template catalogs slot into the same .specify/extension-catalogs.yml stack (or a parallel .specify/template-catalogs.yml) with the same priority + install_allowed semantics.

4. CLI Commands

# Search for template packs
specify template search [query] [--tag TAG] [--author AUTHOR]

# Install a template pack
specify template add <pack-id>
specify template add --from <url>
specify template add --dev /path/to/local/pack

# List installed template packs
specify template list

# Remove a template pack
specify template remove <pack-id>

# Show which template will be resolved for a given name
specify template resolve <template-name>
# → spec-template: .specify/templates/packs/healthcare-compliance/templates/spec-template.md
#   (from: healthcare-compliance v1.0.0, overrides core)

# Initialize with a template pack
specify init --ai claude --template healthcare-compliance

5. Extension ↔ Template Integration

Extensions can declare template packs as dependencies or bundle their own templates:

# In extension.yml
provides:
  commands:
    - name: "speckit.jira.specstoissues"
      file: "commands/specstoissues.md"
  templates:                               # NEW: Extensions can provide templates
    - type: "artifact"
      name: "tasks-template"
      file: "templates/tasks-template.md"
      description: "Tasks template with Jira issue ID placeholders"
      replaces: "tasks-template"

Resolution priority with extensions:

Priority 1: .specify/templates/overrides/              ← Project-local
Priority 2: .specify/templates/packs/<pack-id>/        ← Template packs
Priority 3: .specify/extensions/<ext-id>/templates/    ← Extension-provided
Priority 4: .specify/templates/                        ← Core defaults

6. Directory Structure

project/
├── .specify/
│   ├── templates/                          # Core templates (existing, unchanged)
│   │   ├── spec-template.md
│   │   ├── plan-template.md
│   │   ├── tasks-template.md
│   │   ├── checklist-template.md
│   │   ├── constitution-template.md
│   │   ├── agent-file-template.md
│   │   ├── vscode-settings.json
│   │   ├── commands/                       # Core command templates
│   │   │   ├── specify.md
│   │   │   ├── plan.md
│   │   │   ├── tasks.md
│   │   │   └── ...
│   │   ├── overrides/                      # (NEW) Project-local overrides
│   │   │   └── spec-template.md            # Custom spec for this project
│   │   └── packs/                          # (NEW) Installed template packs
│   │       ├── .registry                   # Track installed packs (JSON)
│   │       └── healthcare-compliance/      # Installed pack
│   │           ├── template-pack.yml       # Pack manifest
│   │           ├── templates/
│   │           │   ├── spec-template.md
│   │           │   └── plan-template.md
│   │           └── commands/
│   │               └── specify.md
│   ├── extensions/                         # (Existing)
│   └── memory/                             # (Existing)

7. Implementation in Python (src/specify_cli/)

New module: templates.py — mirrors extensions.py architecture:

Class Responsibility
TemplatePackManifest Load & validate template-pack.yml (regex on id/version, validate template types)
TemplatePackRegistry Track installed packs in .specify/templates/packs/.registry
TemplatePackManager Install/remove/update packs with compatibility checking
TemplateCatalog Fetch/cache/search template catalogs (1-hour TTL, same as extensions)
TemplateResolver Walk priority stack to resolve template name → file path

The TemplateResolver is the key new concept — it replaces all hardcoded template path lookups.

8. Script Updates

Both bash and PowerShell scripts need a resolve_template helper:

# scripts/bash/common.sh — new function
resolve_template() {
    local template_name="$1"
    local repo_root="$2"
    local base=".specify/templates"

    # Priority 1: Project overrides
    local override="$repo_root/$base/overrides/${template_name}.md"
    [ -f "$override" ] && echo "$override" && return 0

    # Priority 2: Installed packs (by registry order)
    local packs_dir="$repo_root/$base/packs"
    if [ -d "$packs_dir" ]; then
        for pack in "$packs_dir"/*/; do
            local candidate="$pack/templates/${template_name}.md"
            [ -f "$candidate" ] && echo "$candidate" && return 0
        done
    fi

    # Priority 3: Core templates
    local core="$repo_root/$base/${template_name}.md"
    [ -f "$core" ] && echo "$core" && return 0

    return 1
}

Acceptance Criteria

  • template-pack.yml manifest schema defined and validated (id, version, template types)
  • TemplatePackManifest, TemplatePackRegistry, TemplatePackManager, TemplateCatalog, TemplateResolver classes implemented in src/specify_cli/templates.py
  • specify template search, specify template add, specify template list, specify template remove CLI commands functional
  • specify template resolve <name> shows resolved template path with source attribution
  • specify init --template <pack-id> installs a template pack during project initialization
  • Template resolution follows priority stack: overrides → packs → extension-provided → core
  • templates/catalog.json and templates/catalog.community.json created with same schema patterns as extension catalogs
  • resolve_template() helper added to both scripts/bash/common.sh and scripts/powershell/common.ps1
  • create-new-feature.sh and setup-plan.sh (and PowerShell equivalents) updated to use resolve_template() instead of hardcoded paths
  • Extensions can declare templates in extension.yml under provides.templates
  • Template pack scaffold/template directory created (like extensions/template/)
  • Unit tests covering manifest validation, registry persistence, resolution order, catalog search, extension-provided templates
  • Documentation: Template Development Guide, Template User Guide, README update
  • Compatible with multi-catalog stack design from feat(extensions): support multiple active catalogs simultaneously #1707 (template catalogs can be added to catalog stack)

Out of Scope

  • Template inheritance/merging (e.g., "extend core spec-template, add section X") — start with full replacement, consider inheritance in a follow-up
  • Template variables/interpolation engine (e.g., {{PROJECT_NAME}} substitution) — templates today are static Markdown; keep that for now
  • Visual template editor / web UI — CLI-only for initial release
  • Template versioning with migration (e.g., upgrading from v1 → v2 of a template pack with in-place migration of existing specs) — separate concern
  • Catalog authentication / private registry tokens — tracked separately, same as extensions

Design Decisions & Rationale

  1. Template packs, not individual templates — A pack bundles related templates (spec + plan + tasks) that are designed to work together. Installing individual templates would create inconsistency between artifacts within the same methodology.

  2. Full replacement over inheritance — Template inheritance (inserting sections into a base) is complex and fragile. Full replacement is predictable: you see exactly what you get. Composition can be explored later.

  3. Separate from extensions, but interoperable — Templates and extensions serve different purposes (content structure vs. tool integration). Keeping them as separate systems with clear integration points avoids coupling while enabling extensions to provide domain-specific templates.

  4. Same catalog architecture — Reusing the dual-catalog pattern (org-curated + community) and targeting compatibility with feat(extensions): support multiple active catalogs simultaneously #1707's multi-catalog stack avoids inventing parallel infrastructure and allows a unified specify catalogs experience in the future.

  5. Resolution over configuration — Instead of requiring users to configure which template to use for each artifact, the system resolves templates by walking a priority stack. Install a pack and it "just works" — no additional configuration needed.

References

  • Extension system RFC: extensions/RFC-EXTENSION-SYSTEM.md
  • Extension catalog: extensions/catalog.community.json
  • Multi-catalog proposal: feat(extensions): support multiple active catalogs simultaneously #1707
  • Current template usage in scripts: scripts/bash/create-new-feature.sh (line 283), scripts/bash/setup-plan.sh (line 40)
  • Extension manifest: extensions/template/extension.yml

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions