register-hook
Create and register hook scripts with proper error handling and settings
What this skill does
# Register Hook Skill
**Purpose**: Create hook scripts with mandatory error handling patterns and register them in settings.json with proper matcher syntax.
**Performance**: Ensures hooks work correctly, prevents registration errors, enforces restart requirement
## When to Use This Skill
### ✅ Use register-hook When:
- Creating new hook script from scratch
- Need to register hook in settings.json
- Want to ensure proper error handling pattern
- Setting up hook with specific trigger event
### ❌ Do NOT Use When:
- Modifying existing hook (use Edit tool)
- Hook already registered (verify first)
- Testing hook behavior (use manual execution)
## What This Skill Does
### 1. Creates Hook Script
```bash
# Creates script with mandatory pattern:
#!/bin/bash
set -euo pipefail
# Error handler - output helpful message to stderr on failure
trap 'echo "ERROR in <script-name>.sh at line $LINENO: Command failed: $BASH_COMMAND" >&2; exit 1' ERR
# Hook logic...
```
### 2. Sets Permissions
```bash
chmod +x ~/.claude/hooks/{hook-name}.sh
```
### 3. Registers in settings.json
```json
{
"hooks": {
"{TriggerEvent}": [
{
"matcher": "{tool-pattern}",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/{hook-name}.sh"
}
]
}
]
}
}
```
### 4. Warns About Restart
```markdown
⚠️ Please restart Claude Code for hook changes to take effect
```
### 5. Provides Test Instructions
```markdown
After restart, test hook with:
[specific command to trigger hook]
```
## Trigger Events
### Available Events
**SessionStart**: Runs when session starts or resumes after compaction
```json
"SessionStart": [
{
"hooks": [{"type": "command", "command": "~/.claude/hooks/my-hook.sh"}]
}
]
```
**UserPromptSubmit**: Runs when user submits a prompt
```json
"UserPromptSubmit": [
{
"hooks": [{"type": "command", "command": "~/.claude/hooks/my-hook.sh"}]
}
]
```
**PreToolUse**: Runs before tool execution (supports matchers)
```json
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "~/.claude/hooks/my-hook.sh"}]
}
]
```
**PostToolUse**: Runs after tool execution (supports matchers)
```json
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "~/.claude/hooks/my-hook.sh"}]
}
]
```
**PreCompact**: Runs before context compaction
```json
"PreCompact": [
{
"hooks": [{"type": "command", "command": "~/.claude/hooks/my-hook.sh"}]
}
]
```
## Matcher Syntax
### Tool-Level Filtering
**Matcher patterns filter by tool name only**. Command/path filtering must be done inside hook scripts.
**✅ CORRECT Matcher Syntax**:
```json
"matcher": "Bash" // Single tool
"matcher": "Write|Edit" // Multiple tools (regex)
"matcher": "Notebook.*" // Pattern matching
"matcher": "*" // All tools
// No matcher field = all tools
```
**❌ WRONG Matcher Syntax**:
```json
"matcher": "tool:Bash && command:*git*" // ❌ 'tool:' prefix not supported
"matcher": "command:*echo*" // ❌ command filtering not in matcher
"matcher": "path:**/*.java" // ❌ path filtering not in matcher
```
### Command/Path Filtering in Hook
```bash
# Inside hook script - parse JSON input
JSON_INPUT=$(cat)
COMMAND=$(echo "$JSON_INPUT" | jq -r '.tool_input.command // empty')
# Now filter based on command content
if [[ "$COMMAND" == *"git filter-branch"* ]]; then
echo "Blocking dangerous command" >&2
exit 2
fi
```
## Usage
### Create Simple Hook
```bash
# Create hook that logs all bash commands
HOOK_NAME="log-bash-commands"
TRIGGER="PreToolUse"
MATCHER="Bash"
~/.claude/scripts/register-hook.sh \
--name "$HOOK_NAME" \
--trigger "$TRIGGER" \
--matcher "$MATCHER" \
--script-content "$(cat <<'SCRIPT'
#!/bin/bash
set -euo pipefail
trap 'echo "ERROR in log-bash-commands.sh at line $LINENO: Command failed: $BASH_COMMAND" >&2; exit 1' ERR
# Log bash command to file
JSON_INPUT=$(cat)
COMMAND=$(echo "$JSON_INPUT" | jq -r '.tool_input.command // "unknown"')
echo "[$(date -Iseconds)] $COMMAND" >> /tmp/bash-log.txt
SCRIPT
)"
```
### Create Blocking Hook
```bash
# Create hook that blocks dangerous git commands
HOOK_NAME="block-dangerous-git"
TRIGGER="PreToolUse"
MATCHER="Bash"
~/.claude/scripts/register-hook.sh \
--name "$HOOK_NAME" \
--trigger "$TRIGGER" \
--matcher "$MATCHER" \
--can-block true \
--script-content "$(cat <<'SCRIPT'
#!/bin/bash
set -euo pipefail
trap 'echo "ERROR in block-dangerous-git.sh at line $LINENO: Command failed: $BASH_COMMAND" >&2; exit 1' ERR
JSON_INPUT=$(cat)
COMMAND=$(echo "$JSON_INPUT" | jq -r '.tool_input.command // empty')
# Block dangerous commands
if [[ "$COMMAND" == *"git filter-branch"* ]] || [[ "$COMMAND" == *"--all"* ]]; then
echo '{"permissionDecision": "deny"}'
echo "❌ BLOCKED: Dangerous git command detected" >&2
exit 2
fi
SCRIPT
)"
```
### Create Session Start Hook
```bash
# Create hook that runs on session start
HOOK_NAME="inject-session-context"
TRIGGER="SessionStart"
~/.claude/scripts/register-hook.sh \
--name "$HOOK_NAME" \
--trigger "$TRIGGER" \
--script-content "$(cat <<'SCRIPT'
#!/bin/bash
set -euo pipefail
trap 'echo "ERROR in inject-session-context.sh at line $LINENO: Command failed: $BASH_COMMAND" >&2; exit 1' ERR
# Inject context at session start
echo "## Session Context"
echo "Working directory: $(pwd)"
echo "Git branch: $(git branch --show-current)"
echo "Planning state: $(cat .planning/STATE.md 2>/dev/null | head -5 || echo 'No planning context')"
SCRIPT
)"
```
## Safety Features
### Mandatory Error Handling
- ✅ Enforces `set -euo pipefail`
- ✅ Requires trap with ERR handler
- ✅ Stderr output for errors
- ✅ Helpful diagnostic information
### Registration Validation
- ✅ Checks hook doesn't already exist
- ✅ Validates trigger event is valid
- ✅ Confirms matcher syntax correct
- ✅ Verifies settings.json is valid JSON
### Permission Management
- ✅ Makes script executable
- ✅ Validates script is readable
- ✅ Confirms script at correct path
## Workflow Integration
### Complete Hook Creation Workflow
```markdown
1. ✅ Identify hook need (what to trigger on)
2. ✅ Invoke register-hook skill
3. ✅ Skill creates script with error handling
4. ✅ Skill registers in settings.json
5. ✅ Skill warns about restart requirement
6. ✅ User restarts Claude Code
7. ✅ Test hook triggers correctly
8. ✅ Commit hook and settings.json together
```
## Output Format
Script returns JSON:
```json
{
"status": "success",
"message": "Hook registered successfully",
"hook_name": "log-bash-commands",
"hook_path": "~/.claude/hooks/log-bash-commands.sh",
"trigger_event": "PreToolUse",
"matcher": "Bash",
"executable": true,
"registered": true,
"restart_required": true,
"test_command": "Bash tool with any command",
"timestamp": "2025-11-11T12:34:56-05:00"
}
```
## Related
- **CLAUDE.md § Hook Script Standards**: Mandatory requirements
- **CLAUDE.md § Hook Registration**: Registration checklist
- **settings.json**: Hook configuration file
## Troubleshooting
### Hook Not Triggering After Registration
```bash
# Most common cause: Forgot to restart
# Fix: Restart Claude Code
# Verify hook registered:
cat ~/.claude/settings.json | jq '.hooks.PreToolUse'
# Verify hook executable:
test -x ~/.claude/hooks/my-hook.sh
echo $? # Should be 0
```
### Hook Failing on Execution
```bash
# Check hook error output (stderr)
# Look for trap error message with line number
# Test hook manually:
echo '{"tool_name": "Bash", "tool_input": {"command": "echo test"}}' | \
~/.claude/hooks/my-hook.sh
```
### Matcher Not Working
```bash
# Remember: Matcher is tool-level only
# ✅ CORRECT: "matcher": "Bash"
# ❌ WRONG: "matcher": "Bash && command:git"
# For command filtering, parse JSON inside hook:
JSON_INPUT=$(cat)
COMMAND=$(echo "$JSON_INPUT" | jq -r '.tool_input.command')
```
### Settings.jsonRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.