Skip to content
Back to skills

Cc Hooks Creator

ASecurity

Create effective Claude Code hooks -- shell commands that execute automatically at specific lifecycle points, enabling deterministic control over Claude's behavior.

  • 31 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added June 6, 2026
ai-agentspythonshellbashtestingdebugginggitsecuritydocumentation

Works with

  • claude code

Security analysis

A100/100

Scanned June 6, 2026

npx -y skills add frank-luongt/faos-skills-marketplace --skill cc-hooks-creator --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cc Hooks Creator?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cc Hooks Creator
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/frank-luongt-cc-hooks-creator/badge)](https://www.skillsdirectory.com/skills/frank-luongt-cc-hooks-creator)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
<!-- AUTO-GENERATED by export-skills.py — DO NOT EDIT -->
---
name: cc-hooks-creator
description: Create, configure, and debug Claude Code hooks -- shell commands that execute at lifecycle events (PreToolUse, PostToolUse, Stop, SessionStart, etc.). Use when building file protection, code formatting, notifications, context loading, or any automation triggered by Claude Code events. Covers inline commands, Python scripts, JSON output, state management, and security.
tags: [claude-code, hooks, automation, lifecycle]
---

> **Platform Note:** This skill was designed for multi-agent execution. Perplexity Computer handles orchestration automatically — treat sub-agent instructions as sequential steps to complete thoroughly.

# Claude Code Hooks Creator

Create effective Claude Code hooks -- shell commands that execute automatically at specific lifecycle points, enabling deterministic control over Claude's behavior.

## When to Use

- Creating automation before/after tool execution
- File protection (block edits to sensitive files)
- Code formatting after writes/edits
- Loading context at session start
- Notifications and logging
- Debugging or fixing existing hooks

## Hook Events

| Event | When It Runs | Common Use Cases |
|---|---|---|
| `PreToolUse` | Before tool executes | Block operations, validate inputs, auto-approve |
| `PostToolUse` | After tool completes | Format files, log operations, validate output |
| `Stop` | When Claude finishes | Remind to store learnings, validate completion |
| `SubagentStop` | When subagent completes | Validate subagent output |
| `UserPromptSubmit` | When user submits prompt | Add context, validate prompts |
| `SessionStart` | Session begins | Load context, set environment |
| `SessionEnd` | Session ends | Cleanup, logging |
| `PreCompact` | Before context compact | Save important context |

## Hook Creation Workflow

### 1. Understand Requirements

- **What action?** (log, format, block, notify, validate)
- **When?** (before/after tool, on stop, session start)
- **Which tools?** (Bash, Write, Edit, all)
- **What conditions?** (file types, patterns, always)

### 2. Input/Output Format

**Input** (JSON via stdin):
```json
{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/directory",
  "hook_event_name": "EventName",
  "tool_name": "Write",
  "tool_input": {"file_path": "/path/to/file"}
}
```

**Output** (exit codes):
- Exit 0: Success
- Exit 2: Blocking error (stderr shown to Claude)
- Other: Non-blocking error (stderr logged)

**Advanced JSON output** (exit 0):
```json
{
  "decision": "block",
  "reason": "Explanation for Claude",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask"
  }
}
```

### 3. Implementation

**Inline command (simple):**
```json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.command' >> ~/.claude/bash-log.txt"
      }]
    }]
  }
}
```

**Python script (complex):**
```python
#!/usr/bin/env python3
import json, sys

def main():
    input_data = json.load(sys.stdin)
    tool_name = input_data.get("tool_name", "")
    tool_input = input_data.get("tool_input", {})

    # Your logic here...
    sys.exit(0)  # Allow
    # print("Error message", file=sys.stderr); sys.exit(2)  # Block

if __name__ == "__main__":
    main()
```

### 4. Configure in Settings

Add to `~/.claude/settings.json` (user) or `.claude/settings.json` (project):

```json
{
  "hooks": {
    "EventName": [{
      "matcher": "ToolPattern",
      "hooks": [{
        "type": "command",
        "command": "/path/to/hook-script.py",
        "timeout": 30
      }]
    }]
  }
}
```

**Matcher patterns:** Exact (`"Write"`), Regex (`"Edit|Write"`), All (`"*"` or `""`)

## Common Hook Patterns

### File Protection (PreToolUse)

```python
#!/usr/bin/env python3
import json, sys

PROTECTED = ['.env', 'package-lock.json', '.git/', 'credentials']
input_data = json.load(sys.stdin)
file_path = input_data.get('tool_input', {}).get('file_path', '')

if any(p in file_path for p in PROTECTED):
    print(f"Protected file: {file_path}", file=sys.stderr)
    sys.exit(2)
sys.exit(0)
```

### Code Formatter (PostToolUse)

```python
#!/usr/bin/env python3
import json, sys, subprocess

input_data = json.load(sys.stdin)
file_path = input_data.get('tool_input', {}).get('file_path', '')

if file_path.endswith('.py'):
    subprocess.run(['black', file_path], capture_output=True)
elif file_path.endswith(('.ts', '.tsx', '.js', '.jsx')):
    subprocess.run(['npx', 'prettier', '--write', file_path], capture_output=True)
sys.exit(0)
```

### Context Loader (SessionStart)

```python
#!/usr/bin/env python3
import json, sys, os

result = os.popen('git log --oneline -5 2>/dev/null').read()
output = {
    "hookSpecificOutput": {
        "hookEventName": "SessionStart",
        "additionalContext": f"Recent commits:\n{result}"
    }
}
print(json.dumps(output))
sys.exit(0)
```

## State Management

For hooks tracking state across invocations:

```python
import json, os
from datetime import datetime

STATE_FILE = os.path.expanduser("~/.claude/hook_state.json")

def load_state():
    if os.path.exists(STATE_FILE):
        with open(STATE_FILE) as f:
            return json.load(f)
    return {"invocations": 0, "last_run": None}

def save_state(state):
    with open(STATE_FILE, 'w') as f:
        json.dump(state, f)
```

## Testing and Debugging

1. Make script executable: `chmod +x /path/to/hook.py`
2. Test manually: `echo '{"tool_name":"Write"}' | /path/to/hook.py`
3. Run with debug: `claude --debug`
4. Check verbose output: `Ctrl+O` in Claude Code

## Security

- Hooks run with your user permissions
- Validate and sanitize input data
- Quote shell variables: `"$VAR"` not `$VAR`
- Block path traversal (check for `..`)
- Skip sensitive files (.env, credentials, keys)
- Use absolute paths for scripts

## Anti-Patterns

| Avoid | Why | Instead |
|---|---|---|
| Inline commands for complex logic | Hard to debug, no error handling | Use Python/Bash scripts |
| Missing timeout | Hook can hang indefinitely | Set `"timeout": 30` |
| No exit code handling | Unclear success/failure | Use 0 (allow), 2 (block) |
| Modifying tool input without reason | Confuses Claude | Only modify when necessary, document why |
| Missing `try/except` | JSON parse errors crash hook | Wrap in try/except |

## References

- Source: claude-skills/cc-hooks-creator (MIT License)
- [Claude Code Hooks Documentation](https://docs.anthropic.com/en/docs/claude-code/hooks)

<!-- Source: .faos/custom/skills/tools/cc-hooks-creator/SKILL.md -->

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.