Skip to content
Automation
Skill

/creating-claude-hooks-skill

Use when creating or publishing Claude Code hooks - covers executable format, event types, JSON I/O, exit codes, security requirements, and PRPM package structure

From plugin
relay
79017 skills39 agents
Install
$ npx -y skills add AgentWorkforce/relay --skill creating-claude-hooks-skill --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/creating-claude-hooks-skill

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when creating or publishing Claude Code hooks - covers executable format, event types, JSON I/O, exit codes, security requirements, and PRPM package structure

SKILL.md

creating-claude-hooks-skill.SKILL.md
name: creating-claude-hooks
description: Use when creating or publishing Claude Code hooks - covers executable format, event types, JSON I/O, exit codes, security requirements, and PRPM package structure
skillType: skill
allowed-tools: Read, Write, Edit, Grep, Glob, Bash

Creating Claude Code Hooks

Use this skill when creating, improving, or publishing Claude Code hooks. Provides essential guidance on hook format, event handling, I/O conventions, and package structure.

When to Use This Skill

Activate this skill when:

  • User asks to create a new Claude Code hook
  • User wants to publish a hook as a PRPM package
  • User needs to understand hook format or events
  • User is troubleshooting hook execution
  • User asks about hook vs skill vs command differences

Quick Reference

Hook File Format

| Aspect | Requirement | | --------------- | ------------------------------------------------- | | **Location** | `.claude/hooks/<event-name>` | | **Format** | Executable file (shell, TypeScript, Python, etc.) | | **Permissions** | Must be executable (`chmod +x`) | | **Shebang** | Required (`#!/bin/bash` or `#!/usr/bin/env node`) | | **Input** | JSON via stdin | | **Output** | Text via stdout (shown to user) | | **Exit Codes** | `0` = success, `2` = block, other = error |

Available Events

| Event | When It Fires | Common Use Cases | | -------------------- | --------------------------- | ---------------------------------------- | | `session-start` | New session begins | Environment setup, logging, checks | | `user-prompt-submit` | Before user input processes | Validation, enhancement, filtering | | `tool-call` | Before tool execution | Permission checks, logging, modification | | `assistant-response` | After assistant responds | Formatting, logging, cleanup |

Hook Format Requirements

File Location

**Project hooks:**

.claude/hooks/session-start
.claude/hooks/user-prompt-submit

**User-global hooks:**

~/.claude/hooks/session-start
~/.claude/hooks/tool-call

Executable Requirements

Every hook MUST:

1. **Have a shebang line:**

#!/bin/bash
# or
#!/usr/bin/env node
# or
#!/usr/bin/env python3

2. **Be executable:**

chmod +x .claude/hooks/session-start

3. **Handle JSON input from stdin:**

#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.input.file_path // empty')

4. **Exit with appropriate code:**

exit 0  # Success
exit 2  # Block operation
exit 1  # Error (logs but continues)

Input/Output Format

JSON Input Structure

Hooks receive JSON via stdin with event-specific data:

{
  "event": "tool-call",
  "timestamp": "2025-01-15T10:30:00Z",
  "session_id": "abc123",
  "current_dir": "/path/to/project",
  "input": {
    "file_path": "/path/to/file.ts",
    "command": "npm test",
    "old_string": "...",
    "new_string": "..."
  }
}

Stdout Output

  • Normal output shows in transcript
  • Empty output runs silently
  • Use stderr (`>&2`) for errors

Exit Codes

| Code | Meaning | Behavior | | ------------ | ------- | -------------------------- | | `0` | Success | Continue normally | | `2` | Block | Stop operation, show error | | `1` or other | Error | Log error, continue |

Schema Validation

Hooks should validate against the JSON schema:

**Schema URL:** https://github.com/pr-pm/prpm/blob/main/packages/converters/schemas/claude-hook.schema.json

**Required frontmatter fields:**

  • `name` - Hook identifier (lowercase, hyphens only)
  • `description` - What the hook does
  • `event` - Event type (optional, inferred from filename)
  • `language` - bash, typescript, javascript, python, binary (optional)
  • `hookType: "hook"` - For round-trip conversion

Common Mistakes

| Mistake | Problem | Solution | | ---------------------- | ------------------------- | ------------------------------------ | | Not quoting variables | Breaks on spaces | Always use `"$VAR"` | | Missing shebang | Won't execute | Add `#!/bin/bash` | | Not executable | Permission denied | Run `chmod +x hook-file` | | Logging to stdout | Clutters transcript | Use stderr: `echo "log" >&2` | | Wrong exit code | Doesn't block when needed | Use `exit 2` to block | | No input validation | Security risk | Always validate JSON fields | | Slow operations | Blocks Claude | Run in background or use PostToolUse | | Absolute paths missing | Can't find scripts | Use `$CLAUDE_PLUGIN_ROOT` |

Basic Hook Examples

Shell Script Hook

#!/bin/bash
# .claude/hooks/session-start

# Log session start
echo "Session started at $(date)" >> ~/.claude/session.log

# Check environment
if ! command -v node &> /dev/null; then
  echo "Warning: Node.js not installed" >&2
fi

# Output to user
echo "Development environment ready"
exit 0

TypeScript Hook

#!/usr/bin/env node
// .claude/hooks/user-prompt-submit

import { readFileSync } from 'fs';

// Read JSON from stdin
const input = readFileSync(0, 'utf-8');
const data = JSON.parse(input);

// Validate prompt
if (data.prompt.includes('API_KEY')) {
  console.error('Warning: Prompt may contain secrets');
  process.exit(2); // Block
}

console.log('Prompt validated');
process.exit(0);

Best Practices

1. Keep Hooks Fast

Target < 100ms for PreToolUse hooks:

  • Cache results where possible
  • Run heavy operations in background
  • Use specific matchers, not wildcards
Read more
Ships withrelay

Let Claude Code message Codex. Let your Hyperagent talk to your Hermes agent. Give your custom agents a way to message each other.

Get the whole plugin