Skip to content
Open
Show file tree
Hide file tree
Changes from all 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*
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)

### 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*
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 on lines +33 to +38
```
## 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 on lines +218 to +223
```
## 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