Lifecycle Hooks
Commands for workflow automation with lifecycle hooks.
Overview
Lifecycle hooks allow you to execute custom scripts at specific command events (before or after execution).
Hook Execution Flow:
- Pre-Hook Phase - Runs before command execution (optional)
- If blocking pre-hook fails → Command does not run
- If non-blocking pre-hook fails → Command runs anyway
- Command Execution - The fspec command runs
- Post-Hook Phase - Runs after command execution (optional)
- If blocking post-hook fails → Exit code set to 1
- If non-blocking post-hook fails → Exit code remains 0
Use lifecycle hooks when you need to:
- Execute custom scripts at command lifecycle events
- Add quality gates with blocking pre-hooks
- Automate testing with post-hooks
- Send notifications on workflow events
- Validate hook configurations
- List and manage configured hooks
Lifecycle hooks allow you to integrate validation, testing, notifications, or any custom automation into your ACDD workflow. Hooks execute scripts at specific command events (before or after execution).
Hook Management
List Hooks
List all configured hooks.
fspec list-hooks
Examples:
fspec list-hooks
Output:
Lifecycle Hooks:
pre-update-work-unit-status:
- validate-feature-file (blocking, 30s)
post-implementing:
- run-tests (non-blocking, 60s)
- notify-slack (non-blocking, 10s)
Validate Hooks
Validate hook configuration and script paths.
fspec validate-hooks
This command checks:
- Hook configuration syntax in
spec/fspec-hooks.json
- Script paths exist and are executable
- Event names are valid
- Timeout values are reasonable
Examples:
fspec validate-hooks
Add Hook
Add a new hook via CLI.
fspec add-hook <event> <name> [options]
Options:
--command <path>
- Path to hook script (required)--blocking
- Make hook blocking (failure prevents execution)--timeout <seconds>
- Timeout in seconds (default: 60)
Examples:
# Add blocking pre-hook to validate code before implementing
fspec add-hook pre-implementing lint \
--command spec/hooks/lint.sh \
--blocking \
--timeout 30
# Add non-blocking post-hook to run tests
fspec add-hook post-implementing test \
--command spec/hooks/test.sh
# Add notification hook
fspec add-hook post-validating notify \
--command spec/hooks/notify-slack.js \
--timeout 10
Remove Hook
Remove a hook from configuration.
fspec remove-hook <event> <name>
Examples:
fspec remove-hook pre-implementing lint
fspec remove-hook post-implementing test
Hook Configuration
Hooks are configured in spec/fspec-hooks.json
:
{
"global": {
"timeout": 120,
"shell": "/bin/bash"
},
"hooks": {
"pre-update-work-unit-status": [
{
"name": "validate-feature-file",
"command": "spec/hooks/validate-feature.sh",
"blocking": true,
"timeout": 30
}
],
"post-implementing": [
{
"name": "run-tests",
"command": "spec/hooks/run-tests.sh",
"blocking": false,
"condition": {
"tags": ["@security"],
"prefix": ["AUTH", "SEC"]
}
}
]
}
}
Hook Events
Hooks follow the pattern pre-<command>
and post-<command>
:
Common Events:
pre-update-work-unit-status
- Before updating work unit statuspost-update-work-unit-status
- After updating work unit statuspost-implementing
- After moving work unit to implementing statepost-testing
- After moving work unit to testing statepost-validating
- After moving work unit to validating state- Any fspec command supports pre-/post- hooks
Hook Properties
name
(required) - Unique hook identifiercommand
(required) - Path to hook script (relative to project root)blocking
(optional) - If true, hook failure prevents command execution (pre-hooks) or sets exit code to 1 (post-hooks). Default: falsetimeout
(optional) - Timeout in seconds. Overrides global timeout. Default: 60condition
(optional) - Conditions for when hook should runtags
- Hook runs if work unit has ANY of these tags (OR logic)prefix
- Hook runs if work unit ID starts with ANY of these prefixes (OR logic)epic
- Hook runs if work unit belongs to this epicestimateMin
- Hook runs if work unit estimate is greater than or equal to this valueestimateMax
- Hook runs if work unit estimate is less than or equal to this value
Hook Context
Hooks receive JSON context via stdin:
{
"workUnitId": "AUTH-001",
"event": "pre-update-work-unit-status",
"timestamp": "2025-01-15T10:30:00.000Z"
}
Example Hook Scripts
Bash Hook
Validate feature files before status change (spec/hooks/validate-feature.sh
):
#!/bin/bash
set -e
# Read context from stdin
CONTEXT=$(cat)
WORK_UNIT_ID=$(echo "$CONTEXT" | jq -r '.workUnitId')
echo "Validating feature files for $WORK_UNIT_ID..."
fspec validate
exit 0
Python Hook
Run tests after implementation (spec/hooks/run-tests.py
):
#!/usr/bin/env python3
import sys
import json
import subprocess
# Read context from stdin
context = json.load(sys.stdin)
work_unit_id = context['workUnitId']
print(f"Running tests for {work_unit_id}...")
result = subprocess.run(['npm', 'test'], capture_output=True)
sys.exit(result.returncode)
JavaScript Hook
Send Slack notifications (spec/hooks/notify-slack.js
):
#!/usr/bin/env node
const context = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
console.log(`Notifying Slack about ${context.workUnitId}...`);
// Slack notification logic here
process.exit(0);
Blocking Hooks
Blocking hooks (with blocking: true
) provide quality gates:
- Pre-hooks - Hook failure prevents command from running
- Post-hooks - Hook failure sets command exit code to 1
When a blocking hook fails, stderr output is wrapped in <system-reminder>
tags for AI agents, making failures highly visible in Claude Code.
Example:
# This pre-hook blocks if linting fails
fspec add-hook pre-implementing lint \
--command spec/hooks/lint.sh \
--blocking
Conditional Execution
Hooks can run conditionally based on work unit properties:
{
"name": "security-scan",
"command": "spec/hooks/security-scan.sh",
"blocking": true,
"condition": {
"tags": ["@security", "@critical"],
"prefix": ["AUTH", "SEC"],
"estimateMin": 5
}
}
This hook runs ONLY when:
- Work unit has
@security
OR@critical
tag (OR logic) - AND work unit ID starts with
AUTH
ORSEC
(OR logic) - AND estimate is 5 or greater
Common Use Cases
Quality Gates (Blocking Pre-Hooks)
Validate before advancing work:
fspec add-hook pre-implementing validate-code \
--command spec/hooks/lint.sh \
--blocking
Automated Testing (Post-Hooks)
Run tests after implementation:
fspec add-hook post-implementing test \
--command spec/hooks/test.sh
Notifications (Non-Blocking Post-Hooks)
Send notifications on status changes:
fspec add-hook post-validating notify \
--command spec/hooks/notify.sh
Code Quality Checks
Run linters, formatters, type checkers:
fspec add-hook pre-implementing format \
--command spec/hooks/format.sh \
--blocking
Documentation Generation
Auto-generate docs on completion:
fspec add-hook post-validating generate-docs \
--command spec/hooks/generate-docs.sh
Metrics Recording
Record time, token usage, or custom metrics:
fspec add-hook post-implementing record-metrics \
--command spec/hooks/record-metrics.sh
Benefits
- Quality Assurance - Enforce validation before advancing work
- Automation - Reduce manual steps in ACDD workflow
- Consistency - Same checks run every time
- Visibility - Hook failures are highly visible to AI agents
- Flexibility - Use any language (Bash, Python, JavaScript, etc.)
- Integration - Connect fspec to external tools and services
See Also
- Work Management Commands - Manage work units and workflow
- Discovery Commands - Example Mapping for specification
- ACDD Concepts - Acceptance Criteria Driven Development