cc-hooks — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cc-hooks (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Shell commands that fire at specific points in Claude Code's lifecycle.
<!-- TOC: Quick Start | Events | Blocking | Writing Hooks | Anti-Patterns | References -->
Add to ~/.claude/settings.json (user) or .claude/settings.json (project):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "my-validator.sh" }
]
}
]
}
}| Event | When | Blocks? | Common Use |
|---|---|---|---|
PreToolUse | Before tool runs | Yes | Block/modify commands |
PostToolUse | After tool succeeds | Feedback | Auto-format, lint |
PermissionRequest | Permission dialog | Yes | Auto-approve/deny |
UserPromptSubmit | Prompt submitted | Yes | Add context, validate |
Stop | Claude finishes | Yes | Force continue |
SessionStart | Session begins | No | Load context, set env |
Notification | Notifications | No | Desktop alerts |
Full schemas: HOOK-EVENTS.md
"Bash" → exact match
"Edit|Write" → regex OR
"mcp__.*__write" → MCP tools
"*" or "" → all toolsTools: Bash, Read, Write, Edit, Glob, Grep, Task, WebFetch, WebSearch
| Code | Effect |
|---|---|
| 0 | Success - JSON parsed from stdout |
| 2 | Block - stderr fed to Claude |
| Other | Non-blocking error |
Simple (exit 2):
echo "Blocked: reason" >&2 && exit 2JSON (exit 0):
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Blocked"}}Decisions: "allow" (auto-approve), "deny" (block), "ask" (show dialog)
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow",
"updatedInput":{"command":"modified-command"}}}{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[
{"type":"command","command":"dcg"},
{"type":"command","command":"rch"}
]}]}}git reset --hard, rm -rf, git push --forceDetails: DCG-RCH.md
Minimal Python:
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
cmd = data.get('tool_input', {}).get('command', '')
if 'dangerous' in cmd:
print("Blocked: dangerous", file=sys.stderr)
sys.exit(2)
sys.exit(0) # AllowHook input (stdin):
{"tool_name":"Bash","tool_input":{"command":"npm test"},"session_id":"...","cwd":"..."}| Variable | Scope | Purpose |
|---|---|---|
CLAUDE_PROJECT_DIR | All | Project root |
CLAUDE_ENV_FILE | SessionStart/Setup | Persist env vars |
{"decision":"block","reason":"Tests failing. Fix before stopping."}Critical: Check stop_hook_active to prevent infinite loops.
| Don't | Do |
|---|---|
| Old object format | Array format with matcher |
Unquoted $VAR | "$VAR" |
| Exit 2 with JSON | Exit 2 uses stderr only |
Skip stop_hook_active check | Always check in Stop hooks |
claude --debug # Hook execution details
/hooks # View/edit in REPL~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.