Hooks Frontmatter Schema for SKILL.md Files
Version: 4.0.0 Last Updated: 2026-01-24
This document defines the hooks frontmatter schema for sf-skills SKILL.md files. Starting with v4.0.0, hooks are defined directly in each skill's SKILL.md frontmatter instead of separate hooks/hooks.json files.
Quick Reference
---
name: sf-apex
description: >
Your skill description here
license: MIT
metadata:
version: "1.0.0"
author: "Author Name"
hooks:
SessionStart:
- type: command
command: "${SHARED_HOOKS}/check-env-weekly.sh"
timeout: 5000
PreToolUse:
- matcher: Bash
hooks:
- type: command
command: "${SHARED_HOOKS}/scripts/guardrails.py"
timeout: 5000
PostToolUse:
- matcher: "Write|Edit"
hooks:
- type: command
command: "${SKILL_HOOKS}/apex-lsp-validate.py"
timeout: 15000
- type: command
command: "${SHARED_HOOKS}/suggest-related-skills.py sf-apex"
timeout: 5000
SubagentStop:
- type: command
command: "${SHARED_HOOKS}/scripts/chain-validator.py sf-apex"
timeout: 5000
---
Hook Event Types
| Event | When It Fires | Use Cases |
|---|---|---|
SessionStart |
Claude Code session begins | Environment validation, org authentication checks |
PreToolUse |
Before a tool executes | Guardrails, input validation, auto-fix |
PostToolUse |
After a tool completes | Validation, skill suggestions, notifications |
SubagentStop |
Subagent finishes work | Chain validation, next-step suggestions |
PermissionRequest |
User permission needed | Auto-approval for safe operations |
UserPromptSubmit |
User sends a message | Skill activation, keyword detection |
Path Variables
These variables are resolved at runtime:
| Variable | Resolves To | Example |
|---|---|---|
${SHARED_HOOKS} |
~/.claude/plugins/marketplaces/sf-skills/shared/hooks |
Shared hook scripts |
${SKILL_HOOKS} |
~/.claude/plugins/marketplaces/sf-skills/<skill>/hooks/scripts |
Skill-specific scripts |
${CLAUDE_PLUGIN_ROOT} |
Current skill's root directory | Same as SKILL_HOOKS parent |
Hook Structure
Basic Hook (No Matcher)
For events that don't need tool matching (SessionStart, UserPromptSubmit):
hooks:
SessionStart:
- type: command
command: "${SHARED_HOOKS}/check-env-weekly.sh"
timeout: 5000
Matcher-Based Hook
For tool-specific events (PreToolUse, PostToolUse):
hooks:
PostToolUse:
- matcher: "Write|Edit" # Regex pattern for tool names
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/validate.py"
timeout: 15000
- matcher: "Bash"
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/parse-output.py"
timeout: 30000
Prompt-Based Hook (LLM Evaluation)
For semantic analysis using Claude:
hooks:
SubagentStop:
- type: prompt
prompt: |
Evaluate if this subagent completed its task successfully.
Check if:
1. All requested files were created
2. Validation passed
3. No errors occurred
Return {"ok": true} to allow, or {"ok": false, "reason": "..."} to continue.
timeout: 30
Hook Configuration Fields
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | "command" or "prompt" |
command |
string | If type=command | Shell command to execute |
prompt |
string | If type=prompt | LLM prompt for evaluation |
timeout |
number | No | Timeout in milliseconds (default: 5000) |
matcher |
string | For tool hooks | Regex pattern for tool names |
Common Hook Patterns
1. Environment Validation (SessionStart)
hooks:
SessionStart:
- type: command
command: "${SHARED_HOOKS}/check-env-weekly.sh"
timeout: 5000
2. PreToolUse Guardrails
hooks:
PreToolUse:
- matcher: Bash
hooks:
- type: command
command: "python3 ${SHARED_HOOKS}/scripts/guardrails.py"
timeout: 5000
3. Code Validation (PostToolUse)
hooks:
PostToolUse:
- matcher: "Write|Edit"
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/apex-lsp-validate.py"
timeout: 15000
- type: command
command: "python3 ${SKILL_HOOKS}/post-tool-validate.py"
timeout: 120000
- type: command
command: "python3 ${SHARED_HOOKS}/suggest-related-skills.py sf-apex"
timeout: 5000
4. Test/Build Output Parsing (PostToolUse on Bash)
hooks:
PostToolUse:
- matcher: Bash
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/parse-test-results.py"
timeout: 30000
5. Chain Validation (SubagentStop)
hooks:
SubagentStop:
- type: command
command: "python3 ${SHARED_HOOKS}/scripts/chain-validator.py sf-apex"
timeout: 5000
Complete Example: sf-apex
---
name: sf-apex
description: >
Generates and reviews Salesforce Apex code with 2025 best practices and 150-point
scoring. Use when writing Apex classes, triggers, test classes, batch jobs, or
reviewing existing Apex code for bulkification, security, and SOLID principles.
license: MIT
metadata:
version: "1.0.0"
author: "Jag Valaiyapathy"
scoring: "150 points across 8 categories"
hooks:
SessionStart:
- type: command
command: "${SHARED_HOOKS}/check-env-weekly.sh"
timeout: 5000
PreToolUse:
- matcher: Bash
hooks:
- type: command
command: "python3 ${SHARED_HOOKS}/scripts/guardrails.py"
timeout: 5000
PostToolUse:
- matcher: "Write|Edit"
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/apex-lsp-validate.py"
timeout: 15000
- type: command
command: "python3 ${SKILL_HOOKS}/post-tool-validate.py"
timeout: 120000
- type: command
command: "python3 ${SHARED_HOOKS}/suggest-related-skills.py sf-apex"
timeout: 5000
SubagentStop:
- type: command
command: "python3 ${SHARED_HOOKS}/scripts/chain-validator.py sf-apex"
timeout: 5000
---
Hook Output Formats
PreToolUse Output (Block/Allow/Modify)
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "DELETE without WHERE detected",
"updatedInput": {
"command": "sf data query --query 'SELECT Id FROM Account LIMIT 200'"
},
"additionalContext": "Warning message for user"
}
}
PostToolUse Output (Context Injection)
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Validation passed. Next: /sf-testing"
}
}
SubagentStop Output (Chain Validation)
{
"hookSpecificOutput": {
"hookEventName": "SubagentStop",
"additionalContext": "Chain progress: Step 3/7 complete. Next: sf-deploy"
}
}
PermissionRequest Output (Auto-Approval)
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"autoApprove": true,
"reason": "Safe read-only operation"
}
}
Migration from hooks/hooks.json
Before (hooks/hooks.json):
{
"hooks": {
"PostToolUse": [{
"matcher": "Write",
"hooks": [{
"type": "command",
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate.py"
}]
}]
}
}
After (SKILL.md frontmatter):
---
name: my-skill
hooks:
PostToolUse:
- matcher: Write
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/validate.py"
timeout: 15000
---
Migration Steps
- Open skill's
hooks/hooks.json - Copy hook configuration
- Convert JSON to YAML format
- Add to SKILL.md frontmatter under
hooks:key - Update path variables (
${CLAUDE_PLUGIN_ROOT}→${SKILL_HOOKS}) - Delete
hooks/hooks.jsonfile (keephooks/scripts/directory)
Troubleshooting
Hook Not Firing
- Check YAML syntax (use a YAML validator)
- Verify path variables resolve correctly
- Check timeout isn't too short
- Ensure script is executable (
chmod +x script.py)
Hook Failing Silently
- Add debug output to script (
print("DEBUG: ...", file=sys.stderr)) - Check
/tmp/sf-skills-*.logfor errors - Test script manually:
echo '{}' | python3 script.py
Path Resolution Issues
- Use absolute paths for debugging
- Check
${SHARED_HOOKS}resolves to:~/.claude/plugins/marketplaces/sf-skills/shared/hooks - Check
${SKILL_HOOKS}resolves to:~/.claude/plugins/marketplaces/sf-skills/<skill>/hooks/scripts
Best Practices
- Keep timeouts reasonable: 5s for simple checks, 15-30s for validation, 120s max for complex operations
- Fail gracefully: Hooks should never break the user experience - exit 0 on errors
- Use shared hooks: For common patterns, use
${SHARED_HOOKS}scripts - Order matters: Hooks execute in order - put fast checks first
- Cache when possible: Registry loading, file parsing - cache to reduce latency
- Provide context: Output helpful messages, not just pass/fail
Related Documentation
- skills-registry.json - Skill orchestration config
- shared/hooks/README.md - Shared hooks documentation
- Claude Code Hooks - Official hooks reference