إنتقل إلى المحتوى الرئيسي

Hooks: الأتمتة الحتمية

متقدّم

الـ Hooks هي أوامر shell ينفّذها Claude Code تلقائيًا عند نقاط محددة في دورة حياته. وبينما تقرر الأذونات ما إذا كان الإجراء مسموحًا، تتيح لك الـ hooks تشغيل منطق حتمي حوله — تنسيق، تحقق، تسجيل، بوابات، حجب بيانات. بها تجعل السلوك مضمونًا بدلًا من "رجاءً تذكّر".

What you'll learn
  • متى تلجأ إلى hook بدلًا من تعليمة أو إذن أو skill
  • دورة الحياة الكاملة: أحداث الجلسة، والـ prompt، والأداة، والوكيل الفرعي، والمهمة، والضغط، ونهاية الدور
  • كيف يُربط الـ hook: الحدث، والـ matcher، وحمولة JSON على stdin
  • الطريقتان اللتان يحجب بهما الـ hook إجراءً — رمز الخروج 2 مقابل JSON على stdout
  • حقول مخرجات JSON العامة المشتركة بين الأحداث: continue وadditionalContext وsystemMessage وreloadSkills وsessionTitle وsuppressOutput وupdatedInput
  • الممارسات الجيدة والأخطاء الشائعة التي تفرّق بين hooks سريعة وآمنة وأخرى بطيئة وصامتة

متى تلجأ إلى hook​

استخدم الـ hook عندما تريد سلوكًا مضمونًا لا مجرد مطلوب. كل مهمة شائعة تقابل حدثًا من دورة الحياة:

  • تنسيق / lint تلقائي بعد كل تعديل لملف (PostToolUse على Edit|Write).
  • حجب إجراء يخالف قاعدة قبل أن يعمل (PreToolUse).
  • حجب بيانات من رسالة المساعد قبل وصولها إلى الطرفية (MessageDisplay).
  • إعادة تحميل الـ skills أو ضبط عنوان الجلسة بعد SessionStart.
  • الإشعار أو التسجيل عند انتهاء جلسة أو إكمال مهمة أو فشل دور (Stop وStopFailure وSessionEnd).
  • حقن السياق عند بدء الجلسة أو عند تحميل التعليمات.

كيف تعمل​

تسجّل الـ hooks في settings.json بمطابقة حدث (وغالبًا matcher — عادةً اسم أداة، وأحيانًا نوع وكيل فرعي أو مُشغّل ضغط أو نوع إشعار). عند إطلاق الحدث، يشغّل Claude أمرك ويمرّر حمولة JSON عبر stdin (اسم الحدث والجلسة وحقولًا خاصة بالحدث مثل اسم الأداة ومدخلاتها). رمز خروج أمرك وstdout يحددان ما يحدث بعد ذلك.

Guided walkthrough1 of 4
  1. سجّل الـ hook في settings.json تحت حدث دورة الحياة الذي يهمك — مثلًا PostToolUse لأتمتة ما بعد الأداة، أو MessageDisplay لحجب البيانات.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
]
}
]
}
}

يقرأ الـ hook أعلاه مسار الملف المعدَّل من JSON القادم عبر stdin (.tool_input.file_path) وينسّقه. لا تفترض أن متغير بيئة يحمل المسار — اقرأه من stdin. أما عناصر المسار الجاهزة مثل ${CLAUDE_PROJECT_DIR} فهي متاحة لتحديد موقع السكربتات.

دورة الحياة في لمحة​

تمتد hooks في Claude Code على الجلسة كلها، لا على استدعاءات الأدوات فقط. جمّعها حسب المرحلة لتعرف أين تعلّق منطقك.

المرحلةالأحداثالاستخدام المعتاد
الجلسةSessionStart وSessionEnd وSetup وInstructionsLoaded وConfigChange وCwdChanged وDirectoryAdded وFileChangedحقن السياق عند البدء، ولقطة عند النهاية، والتفاعل مع إعادة تحميل CLAUDE.md أو تغيّر .env على القرص
الـ PromptUserPromptSubmit وUserPromptExpansionوضع بوابة على الـ prompt أو إعادة كتابته قبل أن يراه Claude
الأداةPreToolUse وPermissionRequest وPermissionDenied وPostToolUse وPostToolUseFailure وPostToolBatchالسماح بإجراء/رفضه، والتصعيد إلى إنسان، والتنسيق بعد التعديلات، وإعادة المحاولة بعد رفض الوضع التلقائي
الوكيل الفرعيSubagentStart وSubagentStopتسجيل التشغيل/الإيقاف، ومنع وكيل فرعي من العودة
المهمةTaskCreated وTaskCompletedالتراجع عن مهمة أُنشئت بالخطأ، ووضع بوابة على الإكمال
الضغطPreCompact وPostCompactأخذ لقطة من السجل قبل الضغط، والتحقق بعده
النموذجPreModelSwitch وPostModelSwitchحجب تبديل النموذج قبل أن يُطبَّق؛ والتفاعل بعد تغيّر النموذج، بما في ذلك عندما تستعيده جلسة مستأنفة
الدورStop وStopFailure وNotification وMessageDisplayالإشعار عند الاكتمال، والتنبيه عند فشل الـ API، وحجب البيانات من النص المعروض
الفريق + MCPTeammateIdle وElicitation وElicitationResult وWorktreeCreate وWorktreeRemoveالتنسيق بين الجلسات، ووضع بوابة على elicitation الخاصة بـ MCP، وتغليف عمليات worktree

حقول stdin الدقيقة لكل حدث، ونحو الـ matcher، وقواعد الحجب موجودة في المرجع الرسمي لـ hooks. القائمة أعلاه هي الخريطة؛ والمرجع هو الأرض.

كيف يحجب الـ hook​

هناك طريقتان بحسب الحدث:

  • رمز الخروج 2 — يُفشل الـ hook الإجراء، وما كتبه إلى stderr يصبح الرسالة التي يراها Claude. بسيط ويعمل مع hooks الأوامر على الأحداث الحاجبة (PreToolUse وUserPromptSubmit وUserPromptExpansion وStop وSubagentStop وTeammateIdle وTaskCreated وTaskCompleted وConfigChange وPostToolBatch وPreCompact وPreModelSwitch وElicitation وElicitationResult وWorktreeCreate وWorktreeRemove). على PermissionRequest يُتجاهل الخروج 2؛ ارفض عبر كائن decision الخاص به بدلًا من ذلك.
  • JSON على stdout (خروج 0) — أعد قرارًا منظّمًا. بالنسبة إلى PreToolUse هو permissionDecision بقيمة "allow" أو "deny" أو "ask" (ضع الاستدعاء أمام المستخدم) أو "defer" (فقط لتشغيلات -p التي يقودها تطبيق يستأنفها)، مع permissionDecisionReason. الأحداث غير الحاجبة (PostToolUse وNotification وMessageDisplay وStopFailure ومعظم أحداث الجلسة) تتجاهل محاولات الحجب — لكن مخرجات JSON الخاصة بها ما زالت قادرة على توجيه ما يراه Claude بعد ذلك.

السكربت أدناه hook من نوع PreToolUse على أداة Bash. اقرأه من الأعلى إلى الأسفل: يستخرج الأمر من stdin، وإن بدا مدمّرًا يكتب سببًا إلى stderr ويخرج بالرمز 2 للحجب.

#!/usr/bin/env bash
# PreToolUse hook on the Bash tool: refuse to delete things.
command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm\ * || "$command" == *"rm -rf"* ]]; then
echo "Blocked: destructive 'rm' is not allowed by policy." >&2
exit 2
fi
exit 0

حقول مخرجات JSON العامة​

تقبل معظم الأحداث كائن JSON منظّمًا على stdout يوجّه ما يحدث بعد ذلك — منفصلًا عن حقول القرار الخاصة بالحدث مثل permissionDecision. أكثرها فائدة:

الحقلماذا يفعلأين يعمل
continueإن كان false يتوقف Claude عن المعالجة كليًا — وله الأسبقية على كل قرار خاص بحدثمعظم الأحداث
systemMessageيضيف رسالة إلى سياق Claude / السجل ليرى سبب تصرفكمعظم الأحداث الحاجبة
additionalContextيحقن سياقًا إضافيًا يستطيع Claude قراءته والعمل به في الدور التاليSessionStart وUserPromptSubmit وغيرها
reloadSkillsيعيد فحص مجلدات الـ skills كي تلتقط الجلسة skill كتبته للتوSessionStart (وأحداث جلسة أخرى)
sessionTitleيضبط عنوان الجلسة في الواجهة — تحت hookSpecificOutput في SessionStartSessionStart
suppressOutputيخفي مخرجات الأداة عن Claude (مفيد للأوامر الكثيرة الضجيج)أحداث الأدوات
updatedInputيعيد كتابة مدخلات الأداة قبل تشغيلها (مثل إضافة علَم --dry-run)PreToolUse
terminalSequenceيُصدر تسلسلات هروب للطرفية — جرس، وعنوان نافذة، وشارة إشعارNotification وStopFailure وغيرها

حقول القرار الخاصة بالأحداث — permissionDecision ("allow" أو "deny" أو "ask" أو "defer"، إضافة إلى permissionDecisionReason) على PreToolUse، وdecision.behavior ("allow" أو "deny") على PermissionRequest، وretry: true على PermissionDenied ليحاول Claude الاستدعاء المرفوض مجددًا — تُضاف فوق هذه الحقول العامة.

النموذج الذهني​

يعمل hook من نوع PreToolUse قبل الإجراء ويستطيع حجبه؛ ويعمل hook من نوع PostToolUse بعد نجاحه ويتفاعل مع النتيجة.

أحداث الـ Hook في لمحة
اضغط Enter أو مفتاح المسافة لقلب البطاقة. استخدم مفتاحي السهمين الأيسر والأيمن للتنقل بين البطاقات.تم إظهار المصطلح.
1 / 6

الممارسات الجيدة​

  • اجعل الـ hooks سريعة وidempotent — فهي تعمل كثيرًا. تتشارك كل hooks الـ SessionEnd ميزانية واحدة مقدارها 1.5 ثانية، فأبقِ عمل نهاية الجلسة في أدنى حد.
  • افشل بصوت عالٍ عند المشكلات الحقيقية، لكن لا تحجب بسبب أمور شكلية.
  • عامِل مخرجات الـ hook كتغذية راجعة إلى Claude — رسالة واضحة على stderr (خروج 2) أو في systemMessage (JSON) تساعده على تصحيح نفسه.
  • فضّل مخرجات JSON على رموز الخروج حين تريد قول أكثر من "محجوب" — فـ permissionDecision: "ask" على PreToolUse يدفع القرار إلى إنسان دون إغلاق الباب بعنف، وadditionalContext يتيح لك التوجيه لا الرفض فقط.
  • تعمل الـ hooks بصلاحيات الـ shell لديك — راجع أي hook لم تكتبه (مراجعة الشيفرة من أطراف ثالثة).

أخطاء شائعة​

  • قراءة مسار الملف من متغير بيئة. المسار موجود في JSON القادم عبر stdin (.tool_input.file_path)، لا في $CLAUDE_FILE_PATH. مرّر stdin عبر jq.
  • الحجب الصامت. إن خرج hook من نوع PreToolUse بالرمز 2 دون شيء على stderr، فإن Claude محجوب لكنه لا يعرف السبب ولا يستطيع التكيّف. اكتب دائمًا سببًا واضحًا (أو استخدم permissionDecisionReason).
  • محاولة حجب حدث غير حاجب. يُطلق PostToolUse بعد تشغيل الأداة — والخروج 2 مجرد رسالة تُهمس في أذن Claude، ولا يستطيع التراجع عن الإجراء. إن أردت وضع بوابة فعلّق على PreToolUse.
  • Hooks بطيئة. يعمل hook من نوع PostToolUse بعد كل تعديل مطابق. أداة lint تستغرق 3 ثوانٍ تجعل الجلسة كلها تبدو بطيئة — أبقِ الـ hooks سريعة، ويفضّل ألا تعمل إلا على ما تغيّر.
  • matchers واسعة أكثر من اللازم. matcher: ".*" يُطلق على كل أداة. ضيّق باسم دقيق أو قائمة Edit|Write أو حقل if الخاص بكل handler (مثل "if": "Bash(git push *)").
  • نسيان أن للأحداث غير المتعلقة بالأدوات matchers أيضًا. يطابق SessionStart القيم startup|resume|clear|compact|fork؛ ويطابق Notification أنواعًا مثل permission_prompt|idle_prompt|agent_completed؛ ويطابق PreCompact القيمتين manual|auto. أي hook واحد مربوط بمجموعة فرعية خاطئة يُطلق إما أكثر من اللازم أو لا يُطلق أبدًا.
  • الوثوق بـ hooks لم تكتبها. يشغّل الـ hook أي shell بصلاحياتك. راجع أي hook قادم من إضافة أو قالب أولًا — انظر مراجعة الشيفرة من أطراف ثالثة.
Watch out
  • يشغّل الـ hook أي shell بصلاحياتك — لا تربط أبدًا hook قادمًا من إضافة أو قالب دون قراءته أولًا.

وصفات تستحق الاقتباس​

قوالب النسخ واللصق الجاهزة في وصفات Hooks وsettings.json. وهذه بعض الوصفات عالية الأثر لتري شكل الأحداث الأحدث:

تنسيق الملفات المعدَّلة تلقائيًا (PostToolUse على Edit|Write)

{
"hooks": {
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
      ]
    }
  ]
}
}

ضبط عنوان جلسة لكل مشروع عند البدء (SessionStart)

{
"hooks": {
  "SessionStart": [
    {
      "matcher": "startup|resume",
      "hooks": [
        {
          "type": "command",
          "command": "jq -nc --arg t \"$(basename \"$PWD\") — $(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo detached)\" '{hookSpecificOutput:{sessionTitle:$t}}'"
        }
      ]
    }
  ]
}
}

إعادة تحميل الـ skills بعد SessionStart (تلتقط skill كتبتها للتو)

{
"hooks": {
  "SessionStart": [
    {
      "matcher": "startup|resume",
      "hooks": [
        { "type": "command", "command": "printf '{\"reloadSkills\": true}\n'" }
      ]
    }
  ]
}
}

حجب مفاتيح API من نص المساعد المعروض (MessageDisplay)

{
"hooks": {
  "MessageDisplay": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "jq -r '.message.content' | sed -E 's/(sk-[A-Za-z0-9_-]{20,})/sk-***REDACTED***/g'"
        }
      ]
    }
  ]
}
}

الإشعار عند فشل الدور (StopFailure — حدود المعدل، الحمل الزائد، المصادقة)

{
"hooks": {
  "StopFailure": [
    {
      "matcher": "rate_limit|overloaded|authentication_failed|billing_error",
      "hooks": [
        { "type": "command", "command": "osascript -e 'display notification \"Claude Code turn failed — check the terminal\" with title \"Claude Code\"'" }
      ]
    }
  ]
}
}

اختبر نفسك

0/4
  1. أين يجد الـ hook مسار الملف الذي عُدِّل للتو؟
  2. يخرج hook من نوع PreToolUse بالرمز 2. ماذا يحدث؟
  3. تريد أن تلتقط الجلسة skill أضفتها للتو إلى .claude/skills/ دون إعادة تشغيل. أي حقل مخرجات، وعلى أي حدث؟
  4. تريد حجب الأسرار من نص Claude قبل ظهوره على شاشة المستخدم. أي حدث؟
Key takeaways
  • تجعل الـ Hooks السلوك مضمونًا لا مطلوبًا فقط — منطق حتمي حول الإجراءات التي لا تفعل الأذونات معها سوى السماح أو الرفض.
  • تمتد دورة الحياة على أكثر من 30 حدثًا عبر مراحل الجلسة والـ prompt والأداة والوكيل الفرعي والمهمة والضغط والدور — اختر المرحلة الصحيحة قبل كتابة السكربت.
  • سجّل في settings.json مقابل حدث مع matcher؛ يمرّر Claude حمولة JSON على stdin ويقرأ رمز خروجك ومخرجاتك.
  • اقرأ مسار الملف من stdin (.tool_input.file_path) — لا من متغير بيئة.
  • احجب برمز الخروج 2 (يصبح stderr هو الرسالة) أو بـ JSON منظّم على stdout (permissionDecision على أحداث الأدوات، و{continue: false} لإيقاف كل شيء).
  • حقول المخرجات العامة — additionalContext وsystemMessage وreloadSkills وsessionTitle وsuppressOutput وupdatedInput — تتيح لك توجيه Claude لا السماح أو الرفض فقط.
  • أبقِ الـ hooks سريعة وidempotent وذات matcher ضيّق — وراجع أي hook لم تكتبه، لأنه يعمل بصلاحيات الـ shell لديك.

التالي​