Problem Statement
Today an extension manifest can declare at most one command per hook event:
hooks:
after_plan:
command: "speckit.my-ext.verify"
This forces extension authors to choose one of:
- Collapse multiple responsibilities into a single mega-command.
- Hide branching logic behind a single entry point.
- Ship multiple extensions just to register parallel hooks on the same event.
It also leaves no manifest-level way to control execution order when several extensions hook the same event — current order depends on install order, not on the extension author's intent.
Proposed Solution
Allow each hook event in extension.yml to accept either a single mapping (existing form) or a list of mappings, plus an optional integer priority field (default 10, lower runs first; ties preserve insertion order via stable sort).
hooks:
after_plan:
- command: "speckit.my-ext.verify"
priority: 5
optional: false
description: "Verification step"
- command: "speckit.my-ext.report"
priority: 10
optional: true
prompt: "Generate report?"
description: "Reporting step"
CLI side:
ExtensionManifest validates the list form and priority (positive int, bool rejected).
HookExecutor.register_hooks purges and replaces all entries owned by the extension on each event during reinstall, so shape changes (single↔list, list shrinkage) don't leak orphaned entries into the project config.
HookExecutor.get_hooks_for_event sorts entries by priority ascending via normalize_priority, tolerating corrupted on-disk values.
Backward compatibility: existing single-mapping manifests work unchanged.
Alternatives Considered
- Keep one-command-per-hook forever: forces extension authors into mega-commands or extension fragmentation, with no priority control.
- Encode order in command names (e.g.
speckit.my-ext.10-verify): leaks ordering into public command names; brittle to refactors.
- Out-of-band ordering config separate from
extension.yml: splits the source of truth for hook behavior, making manifests harder to read and review.
Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
All agents
Use Cases
- A single extension wants to run verification and reporting on
after_plan as two independently optional / conditional steps, instead of one combined command.
- Two extensions hook the same event and the user wants
verify to run before report regardless of install order.
- A safety-critical extension wants its check to run first via a low
priority, ahead of less-critical extensions on the same event.
Acceptance Criteria
Additional Context
No response
Problem Statement
Today an extension manifest can declare at most one command per hook event:
This forces extension authors to choose one of:
It also leaves no manifest-level way to control execution order when several extensions hook the same event — current order depends on install order, not on the extension author's intent.
Proposed Solution
Allow each hook event in
extension.ymlto accept either a single mapping (existing form) or a list of mappings, plus an optional integerpriorityfield (default10, lower runs first; ties preserve insertion order via stable sort).CLI side:
ExtensionManifestvalidates the list form andpriority(positive int,boolrejected).HookExecutor.register_hookspurges and replaces all entries owned by the extension on each event during reinstall, so shape changes (single↔list, list shrinkage) don't leak orphaned entries into the project config.HookExecutor.get_hooks_for_eventsorts entries bypriorityascending vianormalize_priority, tolerating corrupted on-disk values.Backward compatibility: existing single-mapping manifests work unchanged.
Alternatives Considered
speckit.my-ext.10-verify): leaks ordering into public command names; brittle to refactors.extension.yml: splits the source of truth for hook behavior, making manifests harder to read and review.Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
All agents
Use Cases
after_planas two independentlyoptional/ conditional steps, instead of one combined command.verifyto run beforereportregardless of install order.priority, ahead of less-critical extensions on the same event.Acceptance Criteria
extension.ymlaccepts either a single mapping or a list of mappings under any hook event.priority: int(>= 1, default 10) with validation (rejects non-int,bool, and < 1).priorityascending; ties preserve insertion order via stable sort.extensions/template/extension.ymldocuments the new form.priorityvalues.Additional Context
No response