Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- feat: wire `before_specify`, `after_specify`, `before_plan`, and `after_plan` hook events into command templates (#1788)
- feat(extensions): support `.extensionignore` to exclude files/folders during `specify extension add` (#1781)

## [0.2.0] - 2026-03-09
Expand Down
29 changes: 25 additions & 4 deletions extensions/EXTENSION-API-REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,12 @@ Examples:

```yaml
hooks:
before_specify:
command: "speckit.research.pre-spec"
optional: true
prompt: "Perform pre-specification research?"
description: "Search context before writing spec"

after_tasks:
command: "speckit.jira.specstoissues"
optional: true
Expand All @@ -551,17 +557,30 @@ hooks:

Standard events (defined by core):

- `before_specify` - Before specification generation
- `after_specify` - After specification generation
- `before_plan` - Before implementation planning
- `after_plan` - After implementation planning
- `before_tasks` - Before task generation
- `after_tasks` - After task generation
- `before_implement` - Before implementation
- `after_implement` - After implementation
- `before_commit` - Before git commit
- `after_commit` - After git commit
- `before_commit` - Before git commit (future)
- `after_commit` - After git commit (future)

### Hook Configuration

**In `.specify/extensions.yml`**:

```yaml
hooks:
before_specify:
- extension: research
command: speckit.research.pre-spec
enabled: true
optional: true
prompt: "Perform pre-specification research?"

after_tasks:
- extension: jira
command: speckit.jira.specstoissues
Expand Down Expand Up @@ -591,6 +610,8 @@ Or for mandatory hooks:
**Automatic Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not end the response until the hook has been triggered.
```

---
Expand Down Expand Up @@ -811,6 +832,6 @@ satisfied = version_satisfies("1.2.3", ">=1.0.0,<2.0.0") # bool

---

*Last Updated: 2026-01-28*
*Last Updated: 2026-03-13*
*API Version: 1.0*
*Spec Kit Version: 0.1.0*
*Spec Kit Version: 0.2.1*
Comment thread
davidniu-0914 marked this conversation as resolved.
Outdated
24 changes: 22 additions & 2 deletions extensions/EXTENSION-DEVELOPMENT-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,14 @@ Integration hooks for automatic execution.

Available hook points:

- `before_specify`: Before `/speckit.specify` starts
- `after_specify`: After `/speckit.specify` completes
- `before_plan`: Before `/speckit.plan` starts
- `after_plan`: After `/speckit.plan` completes
- `before_tasks`: Before `/speckit.tasks` starts
- `after_tasks`: After `/speckit.tasks` completes
- `after_implement`: After `/speckit.implement` completes (future)
- `before_implement`: Before `/speckit.implement` starts
- `after_implement`: After `/speckit.implement` completes

Hook object:

Expand Down Expand Up @@ -639,11 +645,25 @@ echo "Using endpoint: $endpoint"

### Extension with Hooks

Extension that runs automatically:
Extension that runs automatically at different lifecycle stages:

```yaml
# extension.yml
hooks:
# Pre-hook: runs before specification starts
before_specify:
command: "speckit.research.pre-spec"
optional: true
prompt: "Perform pre-specification research?"
description: "Gather context from codebase before writing spec"

# Post-hook: runs after planning completes
after_plan:
command: "speckit.architect.validate"
optional: false # Mandatory execution
description: "Validate architecture against project standards"

# Post-hook: runs after task generation
after_tasks:
command: "speckit.auto.analyze"
optional: false # Always run
Expand Down
8 changes: 4 additions & 4 deletions extensions/EXTENSION-USER-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ Extensions are modular packages that add new commands and functionality to Spec

### Prerequisites

- Spec Kit version 0.1.0 or higher
- Spec Kit version 0.2.1 or higher
- A spec-kit project (directory with `.specify/` folder)
Comment on lines 42 to 44

### Check Your Version

```bash
specify version
# Should show 0.1.0 or higher
# Should show 0.2.1 or higher
```

### First Extension
Expand Down Expand Up @@ -966,5 +966,5 @@ After creating tasks, sync to Jira:

---

*Last Updated: 2026-01-28*
*Spec Kit Version: 0.1.0*
*Last Updated: 2026-03-13*
*Spec Kit Version: 0.2.1*
Comment thread
davidniu-0914 marked this conversation as resolved.
Outdated
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "specify-cli"
version = "0.2.0"
version = "0.2.1"
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
requires-python = ">=3.11"
dependencies = [
Expand Down
4 changes: 2 additions & 2 deletions src/specify_cli/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1884,7 +1884,7 @@ def get_hooks_for_event(self, event_name: str) -> List[Dict[str, Any]]:
"""Get all registered hooks for a specific event.

Args:
event_name: Name of the event (e.g., 'after_tasks')
event_name: Name of the event (e.g., 'after_specify', 'after_plan', 'after_tasks')

Returns:
List of hook configurations
Expand Down Expand Up @@ -2036,7 +2036,7 @@ def check_hooks_for_event(self, event_name: str) -> Dict[str, Any]:
This method is designed to be called by AI agents after core commands complete.

Args:
event_name: Name of the event (e.g., 'after_spec', 'after_tasks')
event_name: Name of the event (e.g., 'after_specify', 'after_plan', 'after_tasks')

Returns:
Dictionary with hook information:
Expand Down
4 changes: 4 additions & 0 deletions templates/commands/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ You **MUST** consider the user input before proceeding (if not empty).

Wait for the result of the hook command before proceeding to the Outline.
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not proceed until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

## Outline
Expand Down Expand Up @@ -198,4 +200,6 @@ Note: This command assumes a complete task breakdown exists in tasks.md. If task
Executing: `/{command}`
EXECUTE_COMMAND: {command}
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not end the response until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
67 changes: 67 additions & 0 deletions templates/commands/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,42 @@ $ARGUMENTS

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

## Pre-Execution Checks

**Check for extension hooks (before planning generation)**:
- Check if `.specify/extensions.yml` exists in the project root.
- If it exists, read it and look for entries under the `hooks.before_plan` key
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
- Filter to only hooks where `enabled: true`
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
- For each executable hook, output the following based on its `optional` flag:
- **Optional hook** (`optional: true`):
Comment thread
mnriem marked this conversation as resolved.
Outdated
```
## Extension Hooks

**Optional Pre-Hook**: {extension}
Command: `/{command}`
Description: {description}

Prompt: {prompt}
To execute: `/{command}`
```
- **Mandatory hook** (`optional: false`):
```
## Extension Hooks

**Automatic Pre-Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}

Wait for the result of the hook command before proceeding to the Outline.
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not proceed until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

## Outline

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").
Expand All @@ -41,6 +77,37 @@ You **MUST** consider the user input before proceeding (if not empty).

4. **Stop and report**: Command ends after Phase 2 planning. Report branch, IMPL_PLAN path, and generated artifacts.

5. **Check for extension hooks**: After reporting completion, check if `.specify/extensions.yml` exists in the project root.
- If it exists, read it and look for entries under the `hooks.after_plan` key
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
- Filter to only hooks where `enabled: true`
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
- For each executable hook, output the following based on its `optional` flag:
- **Optional hook** (`optional: true`):
```
## Extension Hooks

**Optional Hook**: {extension}
Command: `/{command}`
Description: {description}

Prompt: {prompt}
To execute: `/{command}`
```
- **Mandatory hook** (`optional: false`):
```
## Extension Hooks

**Automatic Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not end the response until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

## Phases

### Phase 0: Outline & Research
Expand Down
67 changes: 67 additions & 0 deletions templates/commands/specify.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,42 @@ $ARGUMENTS

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

## Pre-Execution Checks

**Check for extension hooks (before specification generation)**:
- Check if `.specify/extensions.yml` exists in the project root.
- If it exists, read it and look for entries under the `hooks.before_specify` key
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
- Filter to only hooks where `enabled: true`
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
- For each executable hook, output the following based on its `optional` flag:
- **Optional hook** (`optional: true`):
```
## Extension Hooks

**Optional Pre-Hook**: {extension}
Command: `/{command}`
Description: {description}

Prompt: {prompt}
To execute: `/{command}`
```
- **Mandatory hook** (`optional: false`):
```
## Extension Hooks

**Automatic Pre-Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}

Wait for the result of the hook command before proceeding to the Outline.
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not proceed until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

## Outline

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.
Expand Down Expand Up @@ -176,6 +212,37 @@ Given that feature description, do this:

7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).

8. **Check for extension hooks**: After reporting completion, check if `.specify/extensions.yml` exists in the project root.
- If it exists, read it and look for entries under the `hooks.after_specify` key
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
- Filter to only hooks where `enabled: true`
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
- For each executable hook, output the following based on its `optional` flag:
- **Optional hook** (`optional: true`):
Comment thread
davidniu-0914 marked this conversation as resolved.
Outdated
```
## Extension Hooks

**Optional Hook**: {extension}
Command: `/{command}`
Description: {description}

Prompt: {prompt}
To execute: `/{command}`
```
- **Mandatory hook** (`optional: false`):
```
## Extension Hooks

**Automatic Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not end the response until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.

## General Guidelines
Expand Down
4 changes: 4 additions & 0 deletions templates/commands/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ You **MUST** consider the user input before proceeding (if not empty).

Wait for the result of the hook command before proceeding to the Outline.
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not proceed until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

## Outline
Expand Down Expand Up @@ -124,6 +126,8 @@ You **MUST** consider the user input before proceeding (if not empty).
Executing: `/{command}`
EXECUTE_COMMAND: {command}
```

**CRITICAL**: You MUST execute the command above immediately after outputting it. Do not end the response until the hook has been triggered.
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently

Context for task generation: {ARGS}
Expand Down
Loading