Skip to content

Commit c3fabb0

Browse files
mvanhornclaude
andcommitted
feat(commands): wire before/after hook events into specify and plan templates
Replicates the hook evaluation pattern from tasks.md and implement.md (introduced in PR #1702) into the specify and plan command templates. This completes the hook lifecycle across all SDD phases. Changes: - specify.md: Add before_specify/after_specify hook blocks - plan.md: Add before_plan/after_plan hook blocks - EXTENSION-API-REFERENCE.md: Document new hook events - EXTENSION-USER-GUIDE.md: List all available hook events Fixes #1788 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1a21bde commit c3fabb0

4 files changed

Lines changed: 137 additions & 2 deletions

File tree

extensions/EXTENSION-API-REFERENCE.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ provides:
5353
required: boolean # Default: false
5454

5555
hooks: # Optional, event hooks
56-
event_name: # e.g., "after_tasks", "after_implement"
56+
event_name: # e.g., "after_specify", "after_plan", "after_tasks", "after_implement"
5757
command: string # Command to execute
5858
optional: boolean # Default: true
5959
prompt: string # Prompt text for optional hooks
@@ -108,7 +108,7 @@ defaults: # Optional, default configuration values
108108
#### `hooks`
109109

110110
- **Type**: object
111-
- **Keys**: Event names (e.g., `after_tasks`, `after_implement`, `before_commit`)
111+
- **Keys**: Event names (e.g., `after_specify`, `after_plan`, `after_tasks`, `after_implement`, `before_commit`)
112112
- **Description**: Hooks that execute at lifecycle events
113113
- **Events**: Defined by core spec-kit commands
114114

@@ -551,7 +551,13 @@ hooks:
551551

552552
Standard events (defined by core):
553553

554+
- `before_specify` - Before specification generation
555+
- `after_specify` - After specification generation
556+
- `before_plan` - Before implementation planning
557+
- `after_plan` - After implementation planning
558+
- `before_tasks` - Before task generation
554559
- `after_tasks` - After task generation
560+
- `before_implement` - Before implementation
555561
- `after_implement` - After implementation
556562
- `before_commit` - Before git commit
557563
- `after_commit` - After git commit

extensions/EXTENSION-USER-GUIDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ settings:
387387
auto_execute_hooks: true
388388
389389
# Hook configuration
390+
# Available events: before_specify, after_specify, before_plan, after_plan,
391+
# before_tasks, after_tasks, before_implement, after_implement,
392+
# before_commit, after_commit
390393
hooks:
391394
after_tasks:
392395
- extension: jira

templates/commands/plan.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,40 @@ $ARGUMENTS
2424

2525
You **MUST** consider the user input before proceeding (if not empty).
2626

27+
## Pre-Execution Checks
28+
29+
**Check for extension hooks (before planning)**:
30+
- Check if `.specify/extensions.yml` exists in the project root.
31+
- If it exists, read it and look for entries under the `hooks.before_plan` key
32+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
33+
- Filter to only hooks where `enabled: true`
34+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
35+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
36+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
37+
- For each executable hook, output the following based on its `optional` flag:
38+
- **Optional hook** (`optional: true`):
39+
```
40+
## Extension Hooks
41+
42+
**Optional Pre-Hook**: {extension}
43+
Command: `/{command}`
44+
Description: {description}
45+
46+
Prompt: {prompt}
47+
To execute: `/{command}`
48+
```
49+
- **Mandatory hook** (`optional: false`):
50+
```
51+
## Extension Hooks
52+
53+
**Automatic Pre-Hook**: {extension}
54+
Executing: `/{command}`
55+
EXECUTE_COMMAND: {command}
56+
57+
Wait for the result of the hook command before proceeding to the Outline.
58+
```
59+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
60+
2761
## Outline
2862
2963
1. **Setup**: Run `{SCRIPT}` from repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot").
@@ -41,6 +75,35 @@ You **MUST** consider the user input before proceeding (if not empty).
4175
4276
4. **Stop and report**: Command ends after Phase 2 planning. Report branch, IMPL_PLAN path, and generated artifacts.
4377
78+
5. **Check for extension hooks**: After reporting, check if `.specify/extensions.yml` exists in the project root.
79+
- If it exists, read it and look for entries under the `hooks.after_plan` key
80+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
81+
- Filter to only hooks where `enabled: true`
82+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
83+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
84+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
85+
- For each executable hook, output the following based on its `optional` flag:
86+
- **Optional hook** (`optional: true`):
87+
```
88+
## Extension Hooks
89+
90+
**Optional Hook**: {extension}
91+
Command: `/{command}`
92+
Description: {description}
93+
94+
Prompt: {prompt}
95+
To execute: `/{command}`
96+
```
97+
- **Mandatory hook** (`optional: false`):
98+
```
99+
## Extension Hooks
100+
101+
**Automatic Hook**: {extension}
102+
Executing: `/{command}`
103+
EXECUTE_COMMAND: {command}
104+
```
105+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
106+
44107
## Phases
45108
46109
### Phase 0: Outline & Research

templates/commands/specify.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,40 @@ $ARGUMENTS
2121

2222
You **MUST** consider the user input before proceeding (if not empty).
2323

24+
## Pre-Execution Checks
25+
26+
**Check for extension hooks (before specification)**:
27+
- Check if `.specify/extensions.yml` exists in the project root.
28+
- If it exists, read it and look for entries under the `hooks.before_specify` key
29+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
30+
- Filter to only hooks where `enabled: true`
31+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
32+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
33+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
34+
- For each executable hook, output the following based on its `optional` flag:
35+
- **Optional hook** (`optional: true`):
36+
```
37+
## Extension Hooks
38+
39+
**Optional Pre-Hook**: {extension}
40+
Command: `/{command}`
41+
Description: {description}
42+
43+
Prompt: {prompt}
44+
To execute: `/{command}`
45+
```
46+
- **Mandatory hook** (`optional: false`):
47+
```
48+
## Extension Hooks
49+
50+
**Automatic Pre-Hook**: {extension}
51+
Executing: `/{command}`
52+
EXECUTE_COMMAND: {command}
53+
54+
Wait for the result of the hook command before proceeding to the Outline.
55+
```
56+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
57+
2458
## Outline
2559
2660
The text the user typed after `/speckit.specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `{ARGS}` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
@@ -176,6 +210,35 @@ Given that feature description, do this:
176210
177211
7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
178212
213+
8. **Check for extension hooks**: After reporting completion, check if `.specify/extensions.yml` exists in the project root.
214+
- If it exists, read it and look for entries under the `hooks.after_specify` key
215+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
216+
- Filter to only hooks where `enabled: true`
217+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
218+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
219+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
220+
- For each executable hook, output the following based on its `optional` flag:
221+
- **Optional hook** (`optional: true`):
222+
```
223+
## Extension Hooks
224+
225+
**Optional Hook**: {extension}
226+
Command: `/{command}`
227+
Description: {description}
228+
229+
Prompt: {prompt}
230+
To execute: `/{command}`
231+
```
232+
- **Mandatory hook** (`optional: false`):
233+
```
234+
## Extension Hooks
235+
236+
**Automatic Hook**: {extension}
237+
Executing: `/{command}`
238+
EXECUTE_COMMAND: {command}
239+
```
240+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
241+
179242
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
180243
181244
## Quick Guidelines

0 commit comments

Comments
 (0)