Create Plugin
ruvnet/ruflo
Scaffold a new Claude Code plugin with proper directory structure, plugin.json, skills, commands, and agents
Expert guidance for writing secure, reliable, and performant Claude Code hooks - validates design decisions, enforces best practices, and prevents common pitfalls
$ npx skills add pr-pm/prpm --skill claude-hook-writer -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install pr-pm/prpm claude-hook-writer --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/claude-hook-writer .claude/skills/claude-hook-writer && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "claude-hook-writer" agent skill from https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writer into .claude/skills/claude-hook-writer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "claude-hook-writer", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writerType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add pr-pm/prpm --skill claude-hook-writer -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install pr-pm/prpm claude-hook-writer --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/claude-hook-writer .agents/skills/claude-hook-writer && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "claude-hook-writer" agent skill from https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writer into .agents/skills/claude-hook-writer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "claude-hook-writer", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pr-pm/prpm --skill claude-hook-writer -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install pr-pm/prpm claude-hook-writer --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/claude-hook-writer .cursor/skills/claude-hook-writer && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "claude-hook-writer" agent skill from https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writer into .cursor/skills/claude-hook-writer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "claude-hook-writer", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/pr-pm/prpm.git --path .claude/skills/claude-hook-writer--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add pr-pm/prpm --skill claude-hook-writer -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install pr-pm/prpm claude-hook-writer --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/claude-hook-writer .gemini/skills/claude-hook-writer && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "claude-hook-writer" agent skill from https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writer into .gemini/skills/claude-hook-writer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "claude-hook-writer", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install pr-pm/prpm claude-hook-writerInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add pr-pm/prpm --skill claude-hook-writer -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/claude-hook-writer .github/skills/claude-hook-writer && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "claude-hook-writer" agent skill from https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writer into .github/skills/claude-hook-writer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "claude-hook-writer", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pr-pm/prpm --skill claude-hook-writer -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install pr-pm/prpm claude-hook-writer --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/claude-hook-writer .opencode/skills/claude-hook-writer && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "claude-hook-writer" agent skill from https://github.com/pr-pm/prpm/tree/main/.claude/skills/claude-hook-writer into .opencode/skills/claude-hook-writer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "claude-hook-writer", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
claude-hook-writerExpert guidance for writing secure, reliable, and performant Claude Code hooks - validates design decisions, enforces best practices, and prevents common pitfalls
Claude Hook Writer is an agent skill from pr-pm/prpm. Expert guidance for writing secure, reliable, and performant Claude Code hooks - validates design decisions, enforces best practices, and prevents common pitfalls
Its SKILL.md is about 5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Agent Workflows, covering Hooks and plugins and Architecture decision records. The repository describes itself as: The universal registry for AI coding tools. The licence is MIT.
4 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 5f993e6. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
jqprettiernpmblackbrewFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
code.claude.comprpm.devgithub.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Claude Hook Writer loads about 5k tokens when it runs. Until then it costs about 45 tokens; SKILL.md has 1,108 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
".env"".env.*"".env"".env.*"test_case "Sensitive .env file" \'{"input":{"file_path":".env"}}' \[[ "$FILE" != *".env"* ]] || exit 2if [[ $FILE == ".env" ]]; thenecho "Don't edit .env" >&2if [[ $FILE == ".env" ]]; thenAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from pr-pm/prpm at commit 5f993e6, republished under its MIT licence (© pr-pm). 1,108 words, ~5,033 tokens.
.claude/skills/claude-hook-writer/SKILL.md (or your agent's skills folder).Use this skill when creating or improving Claude Code hooks. This skill ensures hooks are secure, reliable, performant, and follow best practices.
Hooks execute automatically with user permissions. They can read, modify, or delete any file the user can access.
ALWAYS validate and sanitize all input. Hooks receive JSON via stdin—never trust it blindly.
A hook that works 99% of the time is a broken hook. Edge cases (Unicode filenames, spaces in paths, missing tools) will happen.
Test with edge cases before deploying.
Hooks block operations. A 5-second hook means Claude waits 5 seconds before continuing.
Keep hooks fast. Run heavy operations in background.
Missing dependencies, malformed input, and disk errors will occur.
Handle errors explicitly. Log failures. Return meaningful exit codes.
Before writing code, answer these questions:
PreToolUse - Before tool execution (modify input, validate, block)PostToolUse - After tool completes (format, log, cleanup)UserPromptSubmit - Before user input processes (validate, enhance)SessionStart - When Claude Code starts (setup, env check)SessionEnd - When Claude Code exits (cleanup, persist state)Notification - During alerts (desktop notifications, logging)Stop / SubagentStop - When responses finish (cleanup, summary)PreCompact - Before context compaction (save important context)Common mistake: Using PostToolUse for validation (too late—tool already ran). Use PreToolUse to block operations.
Be specific. matcher: "*" runs on every tool call.
Good matchers:
"Write" - Only file writes"Edit|Write" - File modifications"Bash" - Shell commands"mcp__github__*" - All GitHub MCP toolsBad matchers:
"*" - Everything (use only for logging/metrics)Different tools provide different input. Check what's available:
# PreToolUse / PostToolUse
{
"input": {
"file_path": "/path/to/file.ts", // Read, Write, Edit
"command": "npm test", // Bash
"old_string": "...", // Edit
"new_string": "..." // Edit
}
}Validate fields exist before using them:
FILE=$(echo "$INPUT" | jq -r '.input.file_path // empty')
if [[ -z "$FILE" ]]; then
echo "No file path provided" >&2
exit 1
fiCommand hooks (type: "command"):
Prompt hooks (type: "prompt"):
Rule of thumb: Use command hooks unless you need LLM reasoning.
exit 0 - Success (continue operation)exit 2 - Block operation (show error to Claude)exit 1 or other - Non-blocking error (log but continue)For PreToolUse hooks:
For PostToolUse hooks:
Every hook must implement these:
#!/bin/bash
set -euo pipefail # Exit on errors, undefined vars
INPUT=$(cat)
# Validate JSON parse
if ! FILE=$(echo "$INPUT" | jq -r '.input.file_path // empty' 2>&1); then
echo "JSON parse failed: $FILE" >&2
exit 1
fi
# Validate field exists
if [[ -z "$FILE" ]]; then
echo "No file path in input" >&2
exit 1
fi# Validate file is in project
if [[ "$FILE" != "$CLAUDE_PROJECT_DIR"* ]]; then
echo "File outside project: $FILE" >&2
exit 2 # Block operation
fi
# Validate no directory traversal
if [[ "$FILE" == *".."* ]]; then
echo "Path traversal detected: $FILE" >&2
exit 2
fi# Block list (extend as needed)
BLOCKED_PATTERNS=(
".env"
".env.*"
"*.pem"
"*.key"
"*credentials*"
".git/*"
".ssh/*"
)
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if [[ "$FILE" == $pattern ]]; then
echo "Blocked: $FILE matches sensitive pattern $pattern" >&2
exit 2
fi
doneSpaces and special characters in paths break unquoted variables:
# WRONG
cat $FILE # Breaks on "my file.txt"
prettier --write $FILE # Fails with spaces
# RIGHT
cat "$FILE" # Handles spaces
prettier --write "$FILE" # Safe# WRONG - relative path might not resolve
./my-script.sh
# RIGHT - explicit path
"${CLAUDE_PLUGIN_ROOT}/scripts/my-script.sh"
# ALSO RIGHT - use full path
/Users/username/.claude/scripts/my-script.sh# Check tool exists
if ! command -v prettier &> /dev/null; then
echo "prettier not installed, skipping" >&2
exit 0 # Success exit (just skip)
fi
# Check file exists
if [[ ! -f "$FILE" ]]; then
echo "File not found: $FILE" >&2
exit 1
fiDefault is 60 seconds. For slow operations, set explicit timeout:
{
"hooks": [{
"type": "command",
"command": "./slow-operation.sh",
"timeout": 10000 // 10 seconds
}]
}Or run in background:
# Don't block Claude
(heavy_operation "$FILE" &)
exit 0LOG_FILE=~/.claude-hooks/my-hook.log
# Log to stderr (shown in transcript)
echo "Hook failed: some reason" >&2
# Or log to file (for debugging)
echo "[$(date)] Error: some reason" >> "$LOG_FILE"Don't log to stdout unless you want output in Claude's transcript.
Test files:
"file with spaces.txt""文件.txt" (Unicode)"src/deep/nested/path/file.tsx" (deep paths)"/absolute/path.txt" (absolute paths)"../../../etc/passwd" (traversal attempts)Test input:
null valuesTarget < 100ms for PreToolUse hooks. Longer hooks block Claude visibly.
Slow operations:
// BAD - runs on everything
{"matcher": "*", ...}
// GOOD - only file writes
{"matcher": "Write", ...}
// BETTER - only TypeScript writes
// (check file extension in hook)
{"matcher": "Write", ...}If multiple hooks match, they run in parallel. Dedupe with locks:
LOCK_FILE="/tmp/claude-hook-${SESSION_ID}-${HOOK_NAME}.lock"
if [[ -f "$LOCK_FILE" ]]; then
exit 0 # Already running
fi
touch "$LOCK_FILE"
trap "rm -f '$LOCK_FILE'" EXIT # Clean up on exit
# Do work here
expensive_operation#!/bin/bash
set -euo pipefail
# Parse input
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.input.file_path // empty')
# Validate
[[ -n "$FILE" ]] || exit 0
[[ -f "$FILE" ]] || exit 0
[[ "$FILE" == "$CLAUDE_PROJECT_DIR"* ]] || exit 0
# Check formatter installed
if ! command -v prettier &> /dev/null; then
exit 0
fi
# Format by extension
case "$FILE" in
*.ts|*.tsx|*.js|*.jsx)
prettier --write "$FILE" 2>/dev/null || exit 0
;;
*.py)
black "$FILE" 2>/dev/null || exit 0
;;
*.go)
gofmt -w "$FILE" 2>/dev/null || exit 0
;;
esacJSON config:
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "/path/to/format-on-save.sh",
"timeout": 5000
}]
}]
}
}#!/bin/bash
set -euo pipefail
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.input.file_path // empty')
[[ -n "$FILE" ]] || exit 0
# Sensitive patterns
BLOCKED=(
".env"
".env.*"
"*.pem"
"*.key"
"*secret*"
"*credential*"
".git/*"
)
for pattern in "${BLOCKED[@]}"; do
# Use case for glob matching
case "$FILE" in
$pattern)
echo "🚫 Blocked: $FILE is a sensitive file" >&2
echo " Pattern: $pattern" >&2
exit 2 # Block operation
;;
esac
done
exit 0 # AllowJSON config:
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "/path/to/block-sensitive.sh"
}]
}]
}
}#!/bin/bash
set -euo pipefail
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.input.command // empty')
[[ -n "$COMMAND" ]] || exit 0
LOG_FILE=~/claude-commands.log
mkdir -p "$(dirname "$LOG_FILE")"
# Log with timestamp and context
{
echo "---"
echo "Time: $(date '+%Y-%m-%d %H:%M:%S')"
echo "Directory: $CLAUDE_CURRENT_DIR"
echo "Command: $COMMAND"
} >> "$LOG_FILE"
exit 0JSON config:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "/path/to/command-logger.sh"
}]
}]
}
}{
"hooks": {
"PreToolUse": [{
"matcher": "Write",
"hooks": [{
"type": "prompt",
"prompt": "Analyze the file content being written to ${input.file_path}. Check if it contains: hardcoded API keys, AWS credentials, private keys, passwords, or secrets. Return {\"decision\": \"block\", \"reason\": \"<specific issue>\"} if found, otherwise {\"decision\": \"allow\"}.",
"schema": {
"type": "object",
"properties": {
"decision": {"enum": ["allow", "block"]},
"reason": {"type": "string"}
},
"required": ["decision"]
}
}]
}]
}
}Use sparingly: Prompt hooks take 2-10 seconds. Only use for critical security checks.
Create test input:
# Test with sample JSON
echo '{
"session_id": "test",
"input": {
"file_path": "/tmp/test.ts"
}
}' | ./my-hook.sh
# Check exit code
echo $? # 0 = success, 2 = blocked, 1 = error#!/bin/bash
# test-hook.sh
HOOK=./my-hook.sh
test_case() {
local description="$1"
local input="$2"
local expected_exit="$3"
echo "Testing: $description"
echo "$input" | $HOOK
actual_exit=$?
if [[ $actual_exit -eq $expected_exit ]]; then
echo " ✓ PASS"
else
echo " ✗ FAIL (expected exit $expected_exit, got $actual_exit)"
return 1
fi
}
# Test cases
test_case "Normal file" \
'{"input":{"file_path":"/tmp/test.ts"}}' \
0
test_case "Sensitive .env file" \
'{"input":{"file_path":".env"}}' \
2
test_case "File with spaces" \
'{"input":{"file_path":"/tmp/my file.ts"}}' \
0
test_case "Missing file_path" \
'{"input":{}}' \
1
test_case "Malformed JSON" \
'not json' \
1
echo "All tests passed"my-hook/
├── prpm.json # Package manifest
├── hook.json # Hook configuration
├── scripts/
│ └── my-hook.sh # Hook script
└── README.md # Documentation{
"name": "@yourname/my-hook",
"version": "1.0.0",
"description": "Brief description of what hook does (shown in search)",
"author": "Your Name",
"format": "claude",
"subtype": "hook",
"tags": [
"formatting",
"security",
"automation"
],
"main": "hook.json",
"scripts": {
"test": "./test-hook.sh"
}
}{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/my-hook.sh",
"timeout": 5000
}]
}]
}
}Use ${CLAUDE_PLUGIN_ROOT} to reference scripts—expands to hook installation directory.
All hook types support optional fields for controlling execution behavior:
{
"hooks": {
"PreToolUse": [{
"matcher": "Write",
"hooks": [{
"type": "command",
"command": "./my-hook.sh",
"timeout": 5000,
"continue": true, // Whether Claude continues after hook (default: true)
"stopReason": "string", // Message shown when continue is false
"suppressOutput": false, // Hide stdout from transcript (default: false)
"systemMessage": "string" // Warning message shown to user
}]
}]
}
}continue (boolean, default: true)Controls whether Claude continues after hook execution.
When to use false:
{
"type": "command",
"command": "./validate-security.sh",
"continue": false,
"stopReason": "Security validation failed. Please review the detected issues before proceeding."
}Exit code interaction:
continue is ignored, operation is blockedcontinue field determines behaviorstopReason (string)Message displayed to user when continue: false. Should explain why execution stopped and what action is needed.
{
"continue": false,
"stopReason": "Pre-commit checks failed. Fix linting errors and try again."
}suppressOutput (boolean, default: false)Hides hook stdout from transcript mode (Ctrl-R). Stderr is always shown.
When to use true:
{
"type": "command",
"command": "./sync-to-cloud.sh",
"suppressOutput": true // Don't show sync progress in transcript
}Note: Always show critical errors via stderr, as stderr is never suppressed.
systemMessage (string)Warning or info message shown to user when hook executes. Useful for non-blocking warnings.
{
"type": "command",
"command": "./check-dependencies.sh",
"systemMessage": "⚠️ Some dependencies are outdated. Consider running 'npm update'."
}Difference from stopReason:
systemMessage: Informational, Claude continuesstopReason: Critical, requires continue: false# My Hook
Brief description.
## What It Does
- Clear, specific bullet points
- Mention which events it triggers on
- Mention which tools it matches
## Installation
```bash
prpm install @yourname/my-hooknpm install -g prettier)brew install jq)Optional: How to customize behavior.
Show example output or behavior.
Common issues and fixes.
### Publishing
```bash
# Test locally first
prpm test
# Publish
prpm publish
# Version bumps
prpm publish patch # 1.0.0 -> 1.0.1
prpm publish minor # 1.0.0 -> 1.1.0
prpm publish major # 1.0.0 -> 2.0.0# BREAKS on spaces
prettier --write $FILE
# SAFE
prettier --write "$FILE"# DANGEROUS - no validation
FILE=$(jq -r '.input.file_path')
rm "$FILE"
# SAFE - validate first
FILE=$(jq -r '.input.file_path // empty')
[[ "$FILE" == "$CLAUDE_PROJECT_DIR"* ]] || exit 2
[[ "$FILE" != *".env"* ]] || exit 2
rm "$FILE"# BLOCKS Claude for 30 seconds
npm test
# RUN IN BACKGROUND
(npm test &)
exit 0# PreToolUse hook that should block
if [[ $FILE == ".env" ]]; then
echo "Don't edit .env" >&2
exit 1 # WRONG - doesn't block, just logs error
fi
# RIGHT
if [[ $FILE == ".env" ]]; then
echo "Blocked: .env is protected" >&2
exit 2 # Blocks operation
fi# WRONG - appears in transcript
echo "Hook running..."
# RIGHT - stderr or file
echo "Hook running..." >&2
# or
echo "Hook running..." >> ~/.claude-hooks/debug.log# BREAKS if prettier not installed
prettier --write "$FILE"
# SAFE
if command -v prettier &>/dev/null; then
prettier --write "$FILE"
fi#!/bin/bash
set -x # Print commands as they executeRun Claude Code with Ctrl-R (transcript mode) to see hook execution:
PreToolUse hook: ./my-hook.sh
stdout: Formatted file.ts
stderr:
exit: 0
duration: 47ms# Debug what jq extracts
INPUT=$(cat)
echo "$INPUT" | jq '.' >&2 # Show full JSON
echo "$INPUT" | jq -r '.input.file_path' >&2 # Show fieldecho "PROJECT_DIR: $CLAUDE_PROJECT_DIR" >&2
echo "CURRENT_DIR: $CLAUDE_CURRENT_DIR" >&2
echo "SESSION_ID: $SESSION_ID" >&2
echo "PLUGIN_ROOT: $CLAUDE_PLUGIN_ROOT" >&20 = Success (continue)2 = Block operation (PreToolUse only)1 or other = Non-blocking errorRequired:
type - "command" or "prompt"command or prompt - Script path or prompt textOptional:
timeout - Max execution time in ms (default: 60000)continue - Continue after hook? (default: true)stopReason - Message when continue=falsesuppressOutput - Hide stdout from transcript (default: false)systemMessage - Warning message to user$CLAUDE_PROJECT_DIR - Project root$CLAUDE_CURRENT_DIR - Current directory$SESSION_ID - Session identifier$CLAUDE_PLUGIN_ROOT - Hook installation directory$CLAUDE_ENV_FILE - File for persisting vars{
"session_id": "...",
"transcript_path": "...",
"current_dir": "...",
"input": {
// Tool-specific fields
}
}# Extract with default
$(jq -r '.input.file_path // empty')
# Extract array
$(jq -r '.input.files[]')
# Check field exists
if jq -e '.input.file_path' >/dev/null; then
# Parse entire object
INPUT_OBJ=$(jq '.input')Before publishing:
© pr-pm, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/claude-hook-writer of pr-pm/prpm.
Open the folder on GitHubat commit 5f993e6
Claude Hook Writer next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Claude Hook Writer this skillpr-pm/prpm | 122 | — | ~5k | Automated safety check: Notes | MIT | |
| Create Pluginruvnet/ruflo | 74k | — | ~1.4k | Automated safety check: Notes | MIT | |
| Claude Hook Writersecondsky/claude-skills | 227 | — | ~3.1k | Automated safety check: Notes | MIT | |
| Hook Development for Claude Code Pluginsanthropics/claude-plugins-official | 38k | 10 repos | ~4.1k | Automated safety check: Notes | Apache-2.0 | |
| Claude Code Agent Developmentanthropics/claude-plugins-official | 38k | 7 repos | ~2.8k | Automated safety check: Pass | Apache-2.0 | |
| Claude Code Skill Developer Guidediet103/claude-code-infrastructure-showcase | 10k | 11 repos | ~3.5k | Automated safety check: Pass | MIT |
ruvnet/ruflo
Scaffold a new Claude Code plugin with proper directory structure, plugin.json, skills, commands, and agents
secondsky/claude-skills
Expert guidance for writing secure, reliable, and performant Claude Code hooks - validates design decisions, enforces best practices, and prevents common pitfalls.
anthropics/claude-plugins-official
Explains how to write Claude Code plugin hooks, both prompt-based checks and bash commands, for events such as PreToolUse, Stop and SessionStart.
anthropics/claude-plugins-official
Explains how to write agents for Claude Code plugins: the markdown file with YAML frontmatter, trigger descriptions, model and color settings, and system prompt design.
diet103/claude-code-infrastructure-showcase
A guide to creating and managing Claude Code skills with auto-activation: skill-rules.json triggers, hooks, enforcement levels, YAML frontmatter and progressive disclosure.
anthropics/claude-plugins-official
Shows how Claude Code plugins keep per-project settings and state in .claude/plugin-name.local.md files with YAML frontmatter and a markdown body.
pr-pm/prpm
Reference for writing Claude Code agent files: location, frontmatter fields, validation limits, tool and model choices, and the required content format.
pr-pm/prpm
Covers how to build, configure and publish Claude Code hooks: event types, exit codes, JSON I/O, and PRPM packaging.
pr-pm/prpm
Shows how to write .claude/rules/ files correctly: paths frontmatter instead of globs, quoted glob patterns, global rules and conversion of Cursor rules.
pr-pm/prpm
Reference for writing portable Agent Skills packages, covering SKILL.md frontmatter limits, name rules, directory layout and where Codex CLI, GitHub Copilot and Amp look for skills.
pr-pm/prpm
A skill your agent uses when implementing Stripe webhook endpoints and getting 'Raw body not available' or signature verification errors - provides raw body parsing solutions and subscription period…
pr-pm/prpm
Guides writing an agents.md project-context file: plain markdown with no frontmatter, focused on what an AI coding assistant cannot already know.
Categories
Expert guidance for writing secure, reliable, and performant Claude Code hooks - validates design decisions, enforces best practices, and prevents common pitfalls. Claude Hook Writer is an agent skill from pr-pm/prpm.
Claude Hook Writer fits situations like: tasks that involve Hooks and plugins; tasks that involve Architecture decision records.
Run `npx skills add pr-pm/prpm --skill claude-hook-writer -a claude-code`. Or copy the skill folder (.claude/skills/claude-hook-writer in pr-pm/prpm) into .claude/skills/claude-hook-writer in your project. Claude Code loads it when a task matches its description.
Run `npx skills add pr-pm/prpm --skill claude-hook-writer -a codex`. Or copy the skill folder (.claude/skills/claude-hook-writer in pr-pm/prpm) into .agents/skills/claude-hook-writer in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add pr-pm/prpm --skill claude-hook-writer -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/claude-hook-writer, .gemini/skills/claude-hook-writer, .github/skills/claude-hook-writer and .opencode/skills/claude-hook-writer in your project.
Going by SKILL.md and its folder, Claude Hook Writer needs the command-line tools its instructions call (jq, prettier, npm, black and brew).
SKILL.md names 3 domains. As links in the text: code.claude.com, prpm.dev and github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Claude Hook Writer is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Claude Hook Writer: Create Plugin (ruvnet/ruflo, 74k stars), Claude Hook Writer (secondsky/claude-skills, 227 stars), Hook Development for Claude Code Plugins (anthropics/claude-plugins-official, 38k stars) and Claude Code Agent Development (anthropics/claude-plugins-official, 38k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
pr-pm (a GitHub organization) maintains it in pr-pm/prpm, which has 122 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on October 6, 2026.
Source: pr-pm/prpm on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.