Adds a deterministic hook that fires automatically on tool events. Use when asked to enforce a rule on every commit, block a dangerous pattern, validate output, or gate any tool call.
How this skill is triggered — by the user, by Claude, or both
Slash command
/project-init-workflow:add_hook <hook-name> <event> <description>When to use
Use when the user wants to automate a check or action that runs every time a specific event occurs — e.g. "block pushes to main", "run linter before every commit", "log every Bash command".
<hook-name> <event> <description>This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Claude Code specific**: this skill manages Claude Code configuration
Claude Code specific: this skill manages Claude Code configuration (settings.json wiring). Other agents do not consume what it produces.
Pick the event that matches when the hook should fire:
Tool execution (most common):
PreToolUse — before a tool runs; output block JSON to block itPostToolUse — after a tool runs; cannot block, can log or validatePostToolBatch — after a batch of parallel tool calls completesPermissionRequest — when Claude asks for permission; can auto-approveSession lifecycle:
SessionStart — when a session beginsStop — when Claude stops generating (end of turn)StopFailure — when a turn failsUser input:
UserPromptSubmit — when the user submits a promptUserPromptExpansion — when slash commands expandAgent/task events:
SubagentStart / SubagentStop — subagent lifecycleTaskCreated / TaskCompleted — task tracking eventsFile/config events:
FileChanged, CwdChanged, ConfigChangeCreate .claude/hooks/<name>.sh:
#!/usr/bin/env bash
# Hook receives JSON on stdin from Claude Code.
INPUT=$(cat)
# exit 0 = allow (or no-op for non-blocking events)
# stdout JSON with {"decision":"block","reason":"..."} = block (PreToolUse)
# stdout JSON with {"additionalContext":"..."} = inject context
# Always exit 0 — exit 1 means hook error, not a block
# Example: block pushes to main
CMD=$(echo "$INPUT" | python3 -c "
import json, sys
try:
data = json.load(sys.stdin)
except Exception:
sys.exit(0)
print((data.get('tool_input', {}) or {}).get('command', '') or '')
" 2>/dev/null || true)
[ -z "$CMD" ] && exit 0
if echo "$CMD" | grep -qE 'git push.*(main|master)'; then
python3 -c "import json,sys; print(json.dumps({'decision':'block','reason':sys.argv[1]}))" \
"Direct push to main is not allowed. Use a branch and PR."
exit 0
fi
exit 0
Make it executable: chmod +x .claude/hooks/<name>.sh
Add to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/<name>.sh", "timeout": 10}]
}
]
}
}
Matchers: tool name (Bash, Write, Edit), pipe-separated (Write|Edit), or * for all tools.
Hooks can also live in a skill's frontmatter and fire only while the skill is active:
---
name: my-skill
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./.claude/hooks/check.sh"
---
Trigger the wired tool and verify the hook fires. Check $CLAUDE_PROJECT_DIR is set correctly in the command path.
npx claudepluginhub vytcepas/project-init --plugin project-init-workflowGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.