You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: wire after_tasks and after_implement hook events into command templates (#1702)
* fix: wire after_tasks and after_implement hook events into command templates (#1701)
The HookExecutor backend in extensions.py was fully implemented but
check_hooks_for_event() was never called by anything — the core command
templates had no instructions to check .specify/extensions.yml.
Add a final step to templates/commands/tasks.md (step 6, after_tasks) and
templates/commands/implement.md (step 10, after_implement) that instructs
the AI agent to:
- Read .specify/extensions.yml if it exists
- Filter hooks.{event} to enabled: true entries
- Evaluate any condition fields and skip non-matching hooks
- Output the RFC-specified hook message format, including
EXECUTE_COMMAND: markers for mandatory (optional: false) hooks
Bumps version to 0.1.7.
* fix: clarify hook condition handling and add YAML error guidance in templates
- Replace ambiguous "evaluate any condition value" instruction with explicit
guidance to skip hooks with non-empty conditions, deferring evaluation to
HookExecutor
- Add instruction to skip hook checking silently if extensions.yml cannot
be parsed or is invalid
* Fix/extension hooks not triggered (#1)
* feat(templates): implement before-hooks check as pre-execution phase
* test(hooks): create scenario for LLMs/Agents on hooks
---------
Co-authored-by: Dhilip <s.dhilipkumar@gmail.com>
Copy file name to clipboardExpand all lines: templates/commands/implement.md
+63Lines changed: 63 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,40 @@ $ARGUMENTS
13
13
14
14
You **MUST** consider the user input before proceeding (if not empty).
15
15
16
+
## Pre-Execution Checks
17
+
18
+
**Check for extension hooks (before implementation)**:
19
+
- Check if `.specify/extensions.yml` exists in the project root.
20
+
- If it exists, read it and look for entries under the `hooks.before_implement` key
21
+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
22
+
- Filter to only hooks where `enabled: true`
23
+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
24
+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
25
+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
26
+
- For each executable hook, output the following based on its `optional` flag:
27
+
-**Optional hook** (`optional: true`):
28
+
```
29
+
## Extension Hooks
30
+
31
+
**Optional Pre-Hook**: {extension}
32
+
Command: `/{command}`
33
+
Description: {description}
34
+
35
+
Prompt: {prompt}
36
+
To execute: `/{command}`
37
+
```
38
+
- **Mandatory hook** (`optional: false`):
39
+
```
40
+
## Extension Hooks
41
+
42
+
**Automatic Pre-Hook**: {extension}
43
+
Executing: `/{command}`
44
+
EXECUTE_COMMAND: {command}
45
+
46
+
Wait for the result of the hook command before proceeding to the Outline.
47
+
```
48
+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
49
+
16
50
## Outline
17
51
18
52
1. Run `{SCRIPT}` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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").
@@ -136,3 +170,32 @@ You **MUST** consider the user input before proceeding (if not empty).
136
170
- Report final status with summary of completed work
137
171
138
172
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/speckit.tasks` first to regenerate the task list.
173
+
174
+
10. **Check for extension hooks**: After completion validation, check if `.specify/extensions.yml` exists in the project root.
175
+
- If it exists, read it and look for entries under the `hooks.after_implement` key
176
+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
177
+
- Filter to only hooks where `enabled: true`
178
+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
179
+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
180
+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
181
+
- For each executable hook, output the following based on its `optional` flag:
182
+
- **Optional hook** (`optional: true`):
183
+
```
184
+
## Extension Hooks
185
+
186
+
**Optional Hook**: {extension}
187
+
Command: `/{command}`
188
+
Description: {description}
189
+
190
+
Prompt: {prompt}
191
+
To execute: `/{command}`
192
+
```
193
+
- **Mandatory hook** (`optional: false`):
194
+
```
195
+
## Extension Hooks
196
+
197
+
**Automatic Hook**: {extension}
198
+
Executing: `/{command}`
199
+
EXECUTE_COMMAND: {command}
200
+
```
201
+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
Copy file name to clipboardExpand all lines: templates/commands/tasks.md
+63Lines changed: 63 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,6 +22,40 @@ $ARGUMENTS
22
22
23
23
You **MUST** consider the user input before proceeding (if not empty).
24
24
25
+
## Pre-Execution Checks
26
+
27
+
**Check for extension hooks (before tasks generation)**:
28
+
- Check if `.specify/extensions.yml` exists in the project root.
29
+
- If it exists, read it and look for entries under the `hooks.before_tasks` key
30
+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
31
+
- Filter to only hooks where `enabled: true`
32
+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
33
+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
34
+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
35
+
- For each executable hook, output the following based on its `optional` flag:
36
+
-**Optional hook** (`optional: true`):
37
+
```
38
+
## Extension Hooks
39
+
40
+
**Optional Pre-Hook**: {extension}
41
+
Command: `/{command}`
42
+
Description: {description}
43
+
44
+
Prompt: {prompt}
45
+
To execute: `/{command}`
46
+
```
47
+
- **Mandatory hook** (`optional: false`):
48
+
```
49
+
## Extension Hooks
50
+
51
+
**Automatic Pre-Hook**: {extension}
52
+
Executing: `/{command}`
53
+
EXECUTE_COMMAND: {command}
54
+
55
+
Wait for the result of the hook command before proceeding to the Outline.
56
+
```
57
+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
58
+
25
59
## Outline
26
60
27
61
1. **Setup**: Run `{SCRIPT}` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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").
@@ -63,6 +97,35 @@ You **MUST** consider the user input before proceeding (if not empty).
63
97
- Suggested MVP scope (typically just User Story 1)
64
98
- Format validation: Confirm ALL tasks follow the checklist format (checkbox, ID, labels, file paths)
65
99
100
+
6. **Check for extension hooks**: After tasks.md is generated, check if `.specify/extensions.yml` exists in the project root.
101
+
- If it exists, read it and look for entries under the `hooks.after_tasks` key
102
+
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
103
+
- Filter to only hooks where `enabled: true`
104
+
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
105
+
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
106
+
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
107
+
- For each executable hook, output the following based on its `optional` flag:
108
+
- **Optional hook** (`optional: true`):
109
+
```
110
+
## Extension Hooks
111
+
112
+
**Optional Hook**: {extension}
113
+
Command: `/{command}`
114
+
Description: {description}
115
+
116
+
Prompt: {prompt}
117
+
To execute: `/{command}`
118
+
```
119
+
- **Mandatory hook** (`optional: false`):
120
+
```
121
+
## Extension Hooks
122
+
123
+
**Automatic Hook**: {extension}
124
+
Executing: `/{command}`
125
+
EXECUTE_COMMAND: {command}
126
+
```
127
+
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
128
+
66
129
Context for task generation: {ARGS}
67
130
68
131
The tasks.md should be immediately executable - each task must be specific enough that an LLM can complete it without additional context.
This directory contains a mock project to verify that LLM agents correctly identify and execute hook commands defined in `.specify/extensions.yml`.
4
+
5
+
## Test 1: Testing `before_tasks` and `after_tasks`
6
+
7
+
1. Open a chat with an LLM (like GitHub Copilot) in this project.
8
+
2. Ask it to generate tasks for the current directory:
9
+
> "Please follow `/speckit.tasks` for the `./tests/hooks` directory."
10
+
3.**Expected Behavior**:
11
+
- Before doing any generation, the LLM should notice the `AUTOMATIC Pre-Hook` in `.specify/extensions.yml` under `before_tasks`.
12
+
- It should state it is executing `EXECUTE_COMMAND: pre_tasks_test`.
13
+
- It should then proceed to read the `.md` docs and produce a `tasks.md`.
14
+
- After generation, it should output the optional `after_tasks` hook (`post_tasks_test`) block, asking if you want to run it.
15
+
16
+
## Test 2: Testing `before_implement` and `after_implement`
17
+
18
+
*(Requires `tasks.md` from Test 1 to exist)*
19
+
20
+
1. In the same (or new) chat, ask the LLM to implement the tasks:
21
+
> "Please follow `/speckit.implement` for the `./tests/hooks` directory."
22
+
2.**Expected Behavior**:
23
+
- The LLM should first check for `before_implement` hooks.
24
+
- It should state it is executing `EXECUTE_COMMAND: pre_implement_test` BEFORE doing any actual task execution.
25
+
- It should evaluate the checklists and execute the code writing tasks.
26
+
- Upon completion, it should output the optional `after_implement` hook (`post_implement_test`) block.
27
+
28
+
## How it works
29
+
30
+
The templates for these commands in `templates/commands/tasks.md` and `templates/commands/implement.md` contains strict ordered lists. The new `before_*` hooks are explicitly formulated in a **Pre-Execution Checks** section prior to the outline to ensure they're evaluated first without breaking template step numbers.
0 commit comments