Back to skills
SKILL.md
072 Configuration F2b2cb11
ASecurityComplete reference for configuring hooks in .
- 9 stars
- 0 votes
- 0 copies
- 0 views
- Added October 11, 2026
Works with
Security analysis
100/100npx -y skills add tools-only/X-Skills --skill 072-configuration_f2b2cb11 --agent claude-codeAre you the author of 072 Configuration F2b2cb11?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tools-only-072-configuration-f2b2cb11)# Hook Configuration Reference
Complete reference for configuring hooks in .
## Configuration File Location
Hooks are configured in `.claude/settings.json` or `.claude/settings.local.json`:
```
/your-project/
├── .claude/
│ └── settings.json ← Add hook configuration here
└── hooks/
├── security/ ← secret-detection.py, secret-file-scanner.py, file-protection.py
├── quality/ ← frontmatter-validator.py, tag-taxonomy-enforcer.py, wiki-link-checker.py, filename-convention-checker.py
├── ux/ ← code-formatter.py, context-loader.sh, search-hint.sh
├── safety/ ← bash-safety.py
└── notification/ ← desktop-notify.sh
```
### Project vs Global Settings
- **Project settings:** `.claude/settings.json` (in project root)
- **Local settings:** `.claude/settings.local.json` (gitignored, per-machine overrides)
- **Global settings:** `~/.claude/settings.json` (applies to all projects)
Project settings override global settings for the same event/matcher.
## Basic Structure
```json
{
"hooks": {
"<EventType>": [
{
"matcher": "<ToolNamePattern>",
"hooks": [
{"type": "command", "command": "<command>"}
]
}
]
}
}
```
## Event Types
| Event | When It Fires |
|-------|--------------|
| `PreToolUse` | Before tool executes |
| `PostToolUse` | After tool executes |
| `UserPromptSubmit` | User submits prompt |
| `Stop` | Conversation ends |
| `Start` | Conversation begins |
| `PermissionRequest` | Claude requests permission |
**Most common:** PreToolUse, PostToolUse
## Matcher Syntax
Matchers use **regex patterns** to match tool names:
```json
"matcher": "Edit|Write" // Matches Edit OR Write
"matcher": ".*" // Matches all tools
"matcher": "Bash" // Matches Bash only
"matcher": "Edit|Write|NotebookEdit" // Multiple tools
```
### Common Matchers
| Pattern | Matches |
|---------|---------|
| `Edit|Write` | File editing tools |
| `Bash` | Bash commands |
| `.*` | All tools |
| `Read` | File reading |
| `Grep|Glob` | Search tools |
## Hook Object
Each hook in the `hooks` array:
```json
{
"type": "command",
"command": "python3 hooks/security/file-protection.py"
}
```
### Fields
- `type`: Always `"command"` (only type supported)
- `command`: Shell command to execute
**Command requirements:**
- Must read JSON from stdin
- Must write JSON to stdout
- Must exit with appropriate code (0/1/2)
## Complete Example
```json
{
"hooks": {
"UserPromptSubmit": [
{
"matcher": ".*",
"hooks": [
{"type": "command", "command": "python3 hooks/security/secret-detection.py", "timeout": 10},
{"type": "command", "command": "hooks/ux/context-loader.sh", "timeout": 5}
]
}
],
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "python3 hooks/security/file-protection.py", "timeout": 5},
{"type": "command", "command": "python3 hooks/security/secret-file-scanner.py", "timeout": 10}
]
},
{
"matcher": "Grep",
"hooks": [
{"type": "command", "command": "hooks/ux/search-hint.sh", "timeout": 5}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "python3 hooks/quality/frontmatter-validator.py", "timeout": 10},
{"type": "command", "command": "python3 hooks/quality/tag-taxonomy-enforcer.py", "timeout": 10},
{"type": "command", "command": "python3 hooks/quality/wiki-link-checker.py", "timeout": 15},
{"type": "command", "command": "python3 hooks/quality/filename-convention-checker.py", "timeout": 10},
{"type": "command", "command": "python3 hooks/ux/code-formatter.py", "timeout": 10}
]
}
],
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [
{"type": "command", "command": "python3 hooks/safety/bash-safety.py", "timeout": 5}
]
}
],
"Notification": [
{
"matcher": "Stop",
"hooks": [
{"type": "command", "command": "hooks/notification/desktop-notify.sh"}
]
}
]
}
}
```
## Hook Execution Order
Hooks execute **sequentially** in the order defined:
```json
"hooks": [
{"type": "command", "command": "hook1.py"}, // Runs first
{"type": "command", "command": "hook2.py"}, // Runs second
{"type": "command", "command": "hook3.py"} // Runs third
]
```
**Blocking behaviour:**
- If hook1 exits 2 (block), hook2 and hook3 don't run
- If hook1 exits 1 (warn), hook2 and hook3 still run
- If hook1 exits 0 (allow), hook2 and hook3 run
**Order matters for:**
- Security checks (run first to block early)
- Formatters (run after validation)
- Backups (run last)
## Environment Variables
Hooks receive environment variables from Claude Code:
```bash
CLAUDE_PROJECT_ROOT=/path/to/project
CLAUDE_CONVERSATION_ID=abc123
CLAUDE_USER_NAME=username
```
Access in Python:
```python
import os
project_root = os.getenv("CLAUDE_PROJECT_ROOT")
conversation_id = os.getenv("CLAUDE_CONVERSATION_ID")
```
**Custom environment variables:**
Set in shell before running Claude Code:
```bash
export VAULT_ROOT=/path/to/vault
export BACKUP_DIR=/path/to/backups
```
Hooks can read these for configuration.
## Path Resolution
Hook commands are executed with CWD = project root.
**Relative paths:**
```json
"command": "python3 hooks/security/file-protection.py"
```
Resolves to: `/your-project/hooks/security/file-protection.py`
**Absolute paths:**
```json
"command": "/usr/local/bin/custom-hook"
```
Use absolute paths for hooks outside project directory.
## Conditional Configuration
Use environment variables to conditionally enable hooks:
```bash
# .bashrc or .zshrc
export CLAUDE_ENABLE_BACKUP_HOOKS=true
```
Hook checks variable:
```python
import os
if not os.getenv("CLAUDE_ENABLE_BACKUP_HOOKS"):
sys.exit(0) # Skip backup if not enabled
```
## Testing Configuration
### 1. Validate JSON
```bash
cat .claude/settings.json | jq .
```
### 2. Test individual hook
```bash
echo '{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {"file_path": "test.md"}
}' | python3 hooks/security/file-protection.py
echo "Exit code: 127"
```
### 3. Verify hook loads
Enable debug mode:
```bash
export CLAUDE_DEBUG=1
```
Claude Code will log hook execution in conversation.
## Common Configurations
### Minimal (Security Only)
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "python3 hooks/security/file-protection.py"}
]
}
]
}
}
```
### Standard (Security + Quality)
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "python3 hooks/security/secret-file-scanner.py"},
{"type": "command", "command": "python3 hooks/security/file-protection.py"}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": "python3 hooks/quality/frontmatter-validator.py"}
]
}
]
}
}
```
### Full (All Features)
See [examples/obsidian-vault.json](./examples/obsidian-vault.json)
## Troubleshooting
### Hooks not running
1. Check JSON syntax: `cat .claude/settings.json | jq .`
2. Verify matcher pattern matches tool name
3. Test hook manually: `echo '{}' | python3 hooks/your-hook.py`
4. Check file permissions: `chmod +x hooks/**/*.py`
### Hook exits but doesn't block
- PreToolUse hooks must exit 2 to block
- PostToolUse hooks cannot block (exit 1 for warning)
- Check exit code: `echo 127` after running hook
### Hook crashes
- Add error handling: wrap in try/except
- Check stdin input: ensure it's valid JSON
- Add logging: write to stderr for debugging
### Performance issues
- Check hook execution time
- Use caching for expensive operations
- Consider moving slow operations to PostToolUse
## Further Reading
- [Hook Lifecycle](./hook-lifecycle.md) — Events and exit codes
- [Hook Patterns](./hook-patterns.md) — Implementation patterns
- [README](./README.md) — Hook overview
- [Installation Guide](./installation.md) — Setup instructions
Attribution
Comments
Loading comments…