الـ hooks هي scripts بتشتغل تلقائيًا لما أحداث معينة تحصل خلال جلسة Claude Code. بتستقبل JSON input عبر stdin وبتتواصل عن طريق exit codes وJSON output. الـ command hooks deterministic، قابلة للاختبار، ومش مرتبطة بلغة معين. أما الـ prompt hooks والـ agent hooks فبيستخدموا Claude model للevaluation، فسلوكهم غير deterministic. في الموديول ده، هنغطي نظام الـ hooks، الأحداث الأساسية، وإزاي تكتب hooks مفيدة.
هيكلة الـ Hooks وإعدادها
الـ hooks بتتحدد في ملفات الإعدادات تحت مفتاح hooks. كل حدث (event) عنده مصفوفة من الـ matchers، وكل matcher عنده مصفوفة من تعريفات الـ hooks. الـ matcher field هو regex pattern بيتطبّق على اسم الأداة — "Bash" بيطابق بالظبط، "Write|Edit" بيطابق أي واحدة فيهم، "*" بيطابق كل الأدوات، "mcp__github__.*" بيطابق كل أدوات GitHub MCP.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\"",
"timeout": 10
}
]
}
]
}
}
الـ matchers كمان بتدعم خاصية if المشروطة (v2.1.85) اللي بتستخدم صيغة permission rules عشان تفلتر أكتر إمتى الـ hook يشتغل. الـ matcher بيختار الأداة بالاسم، لكن if بيحدد استدعاءات معينة من الأداة دي. ده مفيد لما تبقى عايز تعترض أوامر git push بس من غير ما تشغّل الـ hook على كل أمر Bash:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"if": "Bash(git push*)",
"hooks": [
{
"type": "command",
"command": "/path/to/check-push.sh"
}
]
}
]
}
}
الـ if pattern بيتبع نفس صيغة permission rules — Bash(git *) بيطابق أي أمر git، وWrite(src/**/test_*.py) بيطابق كتابة ملفات الاختبار، وهكذا. الـ hook اللي عنده if مبيشتغلش غير لما الـ matcher والـ if الاتنين يتطابقوا.
Claude Code بيدعم أكتر من 30 hook event. أكتر الأحداث فائدة في الشغل اليومي: PreToolUse (تحقق قبل ما الأداة تشتغل، ممكن يمنع)، PostToolUse (راقب أو تفاعل بعد ما تخلص، ممكن يضيف سياق)، UserPromptSubmit (اعترض input المستخدم قبل ما Claude يعالجه)، وStop (شغّل فحوصات لما Claude يخلّص رد). فيه كمان أحداث للتعامل مع الصلاحيات (PermissionRequest)، والإشعارات، ودورة حياة الـ subagent (SubagentStart، SubagentStop)، والأخطاء (PostToolUseFailure، StopFailure)، وتغييرات الإعدادات، ومراقبة الملفات (FileChanged)، وضغط السياق (PreCompact، PostCompact)، وإدارة الـ worktree.
كمان فيه أحداث أحدث بتوسّع ردود فعل الـ hooks. CwdChanged (v2.1.83) بيشتغل لما الـ working directory يتغير — زي direnv مثلًا، ممكن تحمّل environment variables تلقائيًا لما Claude يدخل مجلد مشروع. TaskCreated (v2.1.84) بيشتغل لما الأداة TaskCreate تتستخدم، عشان تقدر تسجّل أو تتحقق من المهام الجديدة. WorktreeCreate (v2.1.84) بيشتغل لما worktree agent يتعمل، وبيدعم type: "http" للإشعارات البعيدة. Elicitation (v2.1.76) بيشتغل لما MCP server يطلب input منظم من المستخدم في نص المهمة عبر dialog تفاعلي، وممكن يعترض ويعدّل الـ elicitation قبل ما يتعرض على المستخدم. ElicitationResult (v2.1.76) بيشتغل بعد ما المستخدم يرد على MCP elicitation، وممكن يعترض ويغيّر الرد قبل ما يترجع للـ MCP server.
سكريبتات الـ hook بتستقبل JSON عبر stdin. hook بـ Python بيقراها كده:
import json, sys
data = json.load(sys.stdin)
tool_name = data.get("tool_name", "")
tool_input = data.get("tool_input", {})
Exit code 0 معناه نجاح (اقرأ JSON stdout للناتج). Exit code 2 معناه خطأ مانع — Claude بيوقف ويعرض رسالة الـ stderr بتاعتك. أي exit code تاني هو تحذير غير مانع بيظهر في verbose mode بس.
أنواع الـ Hooks الشائعة والأنماط
الـ hooks بتشتغل بأربع طرق. hooks من نوع command بتنفّذ أوامر shell محلية. hooks من نوع prompt بتطلب من Claude يقيّم prompt معين، عادةً على أحداث Stop أو SubagentStop. hooks من نوع agent بتشغّل subagent لعمل تحقق متعدد الخطوات. hooks من نوع http بتعمل POST لنفس الـ JSON payload على webhook endpoint — مفيدة للـ logging البعيد أو خدمات السياسات. الـ HTTP hooks بتدعم interpolation لـ environment variables في الـ headers، والـ variables دي لازم تكون في الـ allowlist صراحةً.
أنماط شائعة للـ Hooks
تنسيق الملفات تلقائيًا بعد الحفظ من أكتر الـ hooks فائدة. hook من نوع PostToolUse على Write|Edit بيشغّل الـ formatter بتاعك تلقائيًا، عشان output بتاع Claude يبقى دايمًا نظيف:
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))")
case "$FILE" in
*.ts|*.tsx|*.js) prettier --write "$FILE" 2>/dev/null ;;
*.py) black "$FILE" 2>/dev/null ;;
*.go) gofmt -w "$FILE" 2>/dev/null ;;
esac
exit 0
فحص الأمان على الكتابة بيستخدم PostToolUse مع additionalContext output عشان ينبّه Claude لو كتب secrets محتملة:
SECRET_PATTERNS = [
(r"api[_-]?key\s*=\s*['\"][^'\"]+['\"]", "Potential hardcoded API key"),
(r"password\s*=\s*['\"][^'\"]+['\"]", "Potential hardcoded password"),
]
# ... check content, then:
output = {"hookSpecificOutput": {"hookEventName": "PostToolUse",
"additionalContext": f"Security warnings: {'; '.join(warnings)}"}}
print(json.dumps(output))
منع الأوامر الخطيرة بيستخدم PreToolUse مع regex check و exit code 2:
BLOCKED = [(r"\brm\s+-rf\s+/", "Blocking dangerous rm -rf /")]
for pattern, message in BLOCKED:
if re.search(pattern, command):
print(message, file=sys.stderr)
sys.exit(2)
متقدم: الـ Prompt Hooks ونطاق المكونات
لأحداث Stop وSubagentStop، نوع الـ hook "prompt" بيستخدم LLM عشان يقيّم اكتمال المهمة. الـ LLM بيقرأ المحادثة ويرجّع قرار منظم — يسيب Claude يوقف ولا يكمّل شغل. ده قوي جدًا للمهام اللي عندها معايير اكتمال واضحة:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check: 1) Were all files modified? 2) Do tests pass? 3) Is the PR description updated? If anything is missing, explain what.",
"timeout": 30
}
]
}
]
}
}
نوع الـ hook "agent" بيشغّل subagent يعمل التقييم — على عكس الـ prompt hooks (دور واحد)، الـ agent hooks ممكن تستخدم أدوات وتعمل تحليل متعدد الخطوات. استعمل ده لما الفحص محتاج قراءة ملفات أو تشغيل أوامر.
الـ hooks كمان ممكن تتحدد لـ skills وagents معينة باستخدام الـ hooks frontmatter field. hook من نوع PreToolUse في frontmatter الـ skill مبيشتغلش غير وقت تنفيذ الـ skill دي:
---
name: production-deploy
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/production-safety-check.sh"
once: true
---
الـ flag once: true بيشغّل الـ hook مرة واحدة بس في كل session بدل ما يشتغل كل مرة الأداة تتطابق. ده مفيد لفحوصات الإعداد اللي محتاجة تحصل مرة واحدة بس.