Files
github-spec-kit/extensions/template
Seiya Kojima 4ec4635dd1 feat(extensions): per-event hook lists with priority ordering (#2798)
* feat(extensions): per-event hook lists with priority ordering

The manifest validator restricted each hook event to a single mapping,
even though HookExecutor stores entries as a list per event. This blocked
an extension from running multiple commands on one event (e.g. a
verification step plus a doc-generation step after speckit.plan), and
get_hooks_for_event returned entries in raw insertion order with no way
to influence execution order across or within extensions.

This change:

1. Validator: accept hooks.<event> as either a single mapping or a list
   of mappings. Each entry is validated individually and may carry an
   optional integer `priority` (>= 1, default 10; bool rejected).
2. Command-ref normalization: apply rename / alias->canonical rewriting
   to every entry in the list, not just the head.
3. register_hooks: expand list entries, persist `priority`, and
   purge-and-replace all entries owned by the extension on each event so a
   reinstall whose shape changed (single<->list, or a shorter list) leaves
   no orphaned entries behind.
4. get_hooks_for_event: sort enabled entries by `priority` ascending with
   a stable sort (ties keep insertion order). The existing
   normalize_priority helper is reused as the sort key so corrupted
   on-disk values fall back to the default instead of raising.

Backward compatible: existing single-mapping manifests parse and register
unchanged with priority defaulting to 10. The extension-level `priority`
used by preset/template resolution is independent of the new hook-entry
`priority`.

Implements #2378

* fix(extensions): harden register_hooks per PR review

- Skip non-dict hook entries before .get() so a manifest that bypasses
  validation can't crash register_hooks with AttributeError.
- Normalize `priority` on save via normalize_priority so the on-disk
  config stays clean, mirroring the read-side defense in
  get_hooks_for_event.
- Tests: cover the non-dict-entry skip and add encoding="utf-8" to the
  new tests' manifest writes.

* fix(extensions): purge dropped-event hook orphans on reinstall

register_hooks only purged events the new manifest still declared, so an
extension that dropped an event on reinstall left stale entries for it in
the project config. Purge this extension's entries from undeclared events
(and prune emptied events) before registering; scoped to this extension,
and a no-op for the install/update flow where unregister_hooks runs first.

* fix(extensions): reject boolean priority and complete orphan purge

- normalize_priority falls back to default for bool values
- dedup deletes duplicate commands before re-insert for last-wins ties
- register_hooks purges orphans even when all hooks are dropped

* docs(extensions): document per-event hook lists and priority

- EXTENSION-API-REFERENCE: hook event accepts a mapping or list; add
  priority field reference and last-wins dedup note
- EXTENSION-DEVELOPMENT-GUIDE: add list-form example with priority

* docs(extensions): show both single and list hook forms in schema snippet

* docs(extensions): reference DEFAULT_HOOK_PRIORITY in normalize_priority

normalize_priority hard-coded the default as the literal 10 in both its
signature and docstring, duplicating DEFAULT_HOOK_PRIORITY. Reference the
constant in the signature and drop the literal from the docstring so the
default has a single source of truth.
2026-06-08 08:03:46 -05:00
..
2026-02-10 14:27:20 -06:00
2026-02-10 14:27:20 -06:00

Extension Template

Starter template for creating a Spec Kit extension.

Quick Start

  1. Copy this template:

    cp -r extensions/template my-extension
    cd my-extension
    
  2. Customize extension.yml:

    • Change extension ID, name, description
    • Update author and repository
    • Define your commands
  3. Create commands:

    • Add command files in commands/ directory
    • Use Markdown format with YAML frontmatter
  4. Create config template:

    • Define configuration options
    • Document all settings
  5. Write documentation:

    • Update README.md with usage instructions
    • Add examples
  6. Test locally:

    cd /path/to/spec-kit-project
    specify extension add --dev /path/to/my-extension
    
  7. Publish (optional):

    • Create GitHub repository
    • Create release
    • Submit to catalog (see EXTENSION-PUBLISHING-GUIDE.md)

Files in This Template

  • extension.yml - Extension manifest (CUSTOMIZE THIS)
  • config-template.yml - Configuration template (CUSTOMIZE THIS)
  • commands/example.md - Example command (REPLACE THIS)
  • README.md - Extension documentation (REPLACE THIS)
  • LICENSE - MIT License (REVIEW THIS)
  • CHANGELOG.md - Version history (UPDATE THIS)
  • .gitignore - Git ignore rules

Customization Checklist

  • Update extension.yml with your extension details
  • Change extension ID to your extension name
  • Update author information
  • Define your commands
  • Create command files in commands/
  • Update config template
  • Write README with usage instructions
  • Add examples
  • Update LICENSE if needed
  • Test extension locally
  • Create git repository
  • Create first release

Need Help?

  • Development Guide: See EXTENSION-DEVELOPMENT-GUIDE.md
  • API Reference: See EXTENSION-API-REFERENCE.md
  • Publishing Guide: See EXTENSION-PUBLISHING-GUIDE.md
  • User Guide: See EXTENSION-USER-GUIDE.md

Template Version

  • Version: 1.0.0
  • Last Updated: 2026-01-28
  • Compatible with Spec Kit: >=0.1.0