Loading...
Loading...
Creates and configures Claude Code hooks for event-driven automation. Activates when user wants to automate tasks, create event handlers, add formatting/logging/notifications, or ensure deterministic behaviors. Updates settings.json safely with hook configurations. Use when user mentions "create hook", "automate", "on save", "pre/post tool", "notification", "formatting hook", or wants always-on behaviors.
npx skill4agent add squirrelsoft-dev/claude-builder hook-generator| Purpose | Event | Tool Matcher | Notes |
|---|---|---|---|
| Format after edit | PostToolUse | Edit | Run formatter after file edits |
| Validate before save | PreToolUse | Write, Edit | Block invalid files |
| Log commands | PreToolUse | Bash | Record all commands |
| Desktop notification | Notification | * | Alert when Claude needs input |
| Auto-test after changes | PostToolUse | Edit | Run tests after code changes |
| Session logging | SessionStart/End | N/A | Track session times |
| Backup before changes | PreToolUse | Edit | Create backups |
{
"toolName": "Edit",
"parameters": {...},
"workingDirectory": "/path/to/project"
}# Extract file path from Edit tool
jq -r '.parameters.file_path'
# Extract command from Bash tool
jq -r '.parameters.command'
# Check tool name
jq -r '.toolName'{
"hooks": {
"EventName": [
{
"matcher": "ToolName",
"hooks": [
{
"type": "command",
"command": "shell command here"
}
]
}
]
}
}{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "prettier --write $(jq -r '.parameters.file_path')"
},
{
"type": "command",
"command": "echo \"Formatted $(jq -r '.parameters.file_path')\" >> /tmp/format.log"
}
]
}
]
}
}{
"matcher": "*", // Matches all tools
"hooks": [...]
}~/.claude/settings.json.claude/settings.json// Pseudocode
existing = read settings.json or {}
existing.hooks = existing.hooks or {}
existing.hooks[EventName] = existing.hooks[EventName] or []
// Find matching entry or create new
entry = find by matcher or create new entry
entry.hooks.push(newHook)
write settings.json with proper formatting"Edit a file to trigger PostToolUse/Edit hook"
"Run a bash command to trigger PreToolUse/Bash hook""Check /tmp/hook.log for entries"
"Verify file was formatted"
"Check exit code""Add logging to hook command"
"Test command manually with sample JSON"
"Check Claude Code logs"{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.parameters.file_path'); if [[ $FILE == *.ts ]]; then prettier --write \"$FILE\"; fi"
}
]
}
]
}
}{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.parameters.command' >> ~/.claude/bash-commands.log"
}
]
}
]
}
}{
"hooks": {
"Notification": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.parameters.file_path'); if [[ $FILE == *.lock || $FILE == .env ]]; then echo 'Cannot edit protected file' && exit 1; fi"
}
]
}
]
}
}{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.parameters.file_path'); if [[ $FILE == *.ts && $FILE == *src/* ]]; then npm test -- \"${FILE/src/tests}\" 2>/dev/null || true; fi"
}
]
}
]
}
}{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "echo \"Session started at $(date)\" >> ~/.claude/sessions.log"
}
]
}
],
"SessionEnd": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "echo \"Session ended at $(date)\" >> ~/.claude/sessions.log"
}
]
}
]
}
}{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "CMD=$(jq -r '.parameters.command'); if [[ $CMD == *'rm -rf /'* ]]; then echo 'Dangerous command blocked' && exit 1; fi"
}
]
}
]
}
}prettier --write{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.parameters.file_path'); if [[ $FILE == *.ts ]]; then prettier --write \"$FILE\"; fi"
}
]
}
]
}
}.claude/settings.json# Only run in specific directories
FILE=$(jq -r '.parameters.file_path')
if [[ $FILE == ./src/* ]]; then
# Run command
fi# Chain multiple commands
FILE=$(jq -r '.parameters.file_path')
prettier --write "$FILE" && eslint --fix "$FILE"{
"type": "command",
"command": "/path/to/script.sh"
}# Blocking hook with user-visible message
if [[ condition ]]; then
echo "Error message shown to user" >&2
exit 1
fi# Create sample JSON
echo '{"toolName":"Edit","parameters":{"file_path":"test.ts"}}' | \
jq -r '.parameters.file_path'
# Test your hook command
echo '{"toolName":"Edit","parameters":{"file_path":"test.ts"}}' | \
FILE=$(jq -r '.parameters.file_path'); echo "Would format $FILE"