شرح Hooks في Claude Code: الأحداث وحالة الخروج 2
تعرّف إلى مكان إعدادات Hooks في Claude Code، والأحداث التي تشغّلها، وكيف تلغي حالة الخروج 2 استدعاء الأداة، ولماذا تهمك كلفة الأمان.
ما هو hook في Claude Code
إن hooks في Claude Code هي أوامر shell يشغّلها Claude Code تلقائياً عند نقاط محددة من دورة حياته. وهذا هو الفرق الأساسي بين hook وملف القواعد. التعليمات الموجودة في CLAUDE.md هي إرشادات، ويوازنها النموذج مقابل كل ما يوجد في سياقه. أما hook فهو تعليمة برمجية، ويُشغَّل سواء وافق النموذج أم لا. إذا استمر agent في تجاهل formatter الذي أخبرته عنه مرتين، فلا تحتاج إلى تعليمات أكثر صرامة. بل تحتاج إلى hook.
الآلية بسيطة. تسجّل أمراً في ملف إعدادات تحت اسم حدث. عند وقوع الحدث، يشغّل Claude Code أمرك ويكتب بيانات الحدث إلى الإدخال القياسي (stdin) بصيغة JSON (ترميز كائنات JavaScript). يقرأ أمرك هذه البيانات، وينفّذ عمله، ثم يعيد حالة خروج. تؤدي حالة الخروج 2 من hook من نوع PreToolUse إلى إلغاء استدعاء الأداة قبل تشغيلها، وتُمرَّر أي بيانات كتبها البرنامج النصي إلى الخطأ القياسي (stderr) إلى النموذج باعتبارها السبب.
تأتي أسماء الأحداث وأسماء الحقول الواردة هنا من مرجع hooks في Claude Code، وقد جرى التحقق منها في August 2026 مقابل الإصدار 2.1.232. تتغير هذه الواجهة بسرعة، لذلك تحقّق من المرجع الخاص بإصدارك قبل نسخ JSON من أي تدوينة، بما في ذلك هذه التدوينة. اطبع مرجعك باستخدام claude --version.
مكان وجود إعدادات الخطافات
الخطاف هو كتلة JSON في ملف إعدادات. يمكن أن تحتوي عليه ستة مواضع، ويحدّد نطاق الملف نطاق الخطاف.
~/.claude/settings.json: كل مشروع على جهازك، ولا يمتد إلى أجهزة الآخرين..claude/settings.json: مشروع واحد، ويُضمَّن في المستودع، لذلك يحصل عليه كل من يستنسخ المشروع..claude/settings.local.json: مشروع واحد، وعلى جهازك فقط.- إعدادات السياسة المُدارة: على مستوى المؤسسة، ويحدّدها المسؤول.
hooks/hooks.jsonداخل مكوّن إضافي، ويظل فعالاً ما دام ذلك المكوّن الإضافي مفعّلاً.- البيانات الوصفية الأولية للمهارة أو الوكيل الفرعي، وتظل فعالة ما دام ذلك المكوّن نشطاً.
تُدمج إدخالات الخطافات من هذه الملفات بدلاً من أن يستبدل بعضها بعضاً. يضيف ملف إعدادات المشروع خطافاته إلى الخطافات الموجودة في إعدادات المستخدم، ولا يستبدلها؛ لذلك يمكن أن يحتوي حدث واحد على عدة خطافات من ملفات متعددة. يؤدي ضبط "disableAllHooks": true إلى تعطيلها، باستثناء واحد: تستمر الخطافات القادمة من إعدادات السياسة المُدارة في العمل، ما لم يُطبَّق ذلك الإعداد أيضاً في الإعدادات المُدارة.
شغّل /hooks داخل جلسة لعرض كل خطاف مسجّل حالياً، مجمّعةً حسب الحدث، مع عرض ملف المصدر والمطابق الخاص بكل خطاف. القائمة للقراءة فقط، لذلك تغيّر الخطاف بتحرير ملف الإعدادات. تلتقط مراقبة الملفات التعديل عادةً من دون إعادة التشغيل.
أحداث خطافات Claude Code المتاحة
يسرد الإصدار 2.1.232 واحداً وثلاثين حدثاً، بدءاً من SessionStart وحتى SessionEnd، وتشمل الضغط، والوكلاء الفرعيين، وأشجار العمل، وملفات الإعداد. تُستخدم مجموعة صغيرة منها في أعمال إدارة الخوادم.
PreToolUse: قبل تنفيذ استدعاء أداة. هذا هو الحدث الذي يمكنه منع التنفيذ.PostToolUse: بعد نجاح استدعاء أداة. يُطلَقPostToolUseFailureعند فشل الاستدعاء بدلاً من ذلك، لذلك يحتاج الخطاف الذي يجب أن يرى كل نتيجة إلى كليهما.PermissionRequest: عندما يحتاج استدعاء أداة إلى قرار صلاحية، أي في اللحظة التي سيظهر فيها طلب الموافقة.UserPromptSubmit: عند إرسال مطالبة، وقبل أن يعالجها Claude. يُضاف كل ما يطبعه هذا الخطاف إلى stdout إلى سياق النموذج.SessionStartوSessionEnd: عند كل نهاية لجلسة. يُطلَقSessionStartأيضاً بعد الضغط، مع قيمة المطابقةcompact.Stop: عندما ينتهي Claude من الرد. يحدث ذلك مرة واحدة لكل دور، وليس مرة واحدة لكل مهمة مكتملة.
تتضمن كل مجموعة matcher يحدد الحالات التي تشغّل الخطاف. في أحداث الأدوات، تتم المطابقة وفق اسم الأداة، لذلك يُطلَق "Edit|Write" عند تعديل الملفات ولا يُطلَق في أي حالة أخرى. تكون قيم المطابقة حساسة لحالة الأحرف. تؤدي قيمة المطابقة الفارغة إلى تشغيل الخطاف عند كل حالة. تُسمّى الأدوات من خادم MCP (بروتوكول سياق النموذج) وفق الصيغة mcp__<server>__<tool>، لذلك تلتقط قيمة المطابقة "mcp__github__.*" أدوات خادم واحد وتترك الأدوات الأخرى دون تغيير.
تتضمن خطافات Stop نقطة مهمة يجب معرفتها قبل كتابة أي خطاف. يعيد خطاف Stop الذي يمنع التنفيذ النموذج إلى العمل، ويتجاوز Claude Code الخطاف بعد ثماني عمليات منع متتالية. اقرأ الحقل stop_hook_active من إدخال الخطاف، واخرج بالرمز 0 عندما تكون قيمته true، وإلا فسيستمر الخطاف في التكرار حتى يبلغ هذا الحد.
ما يتلقّاه hook عبر الإدخال القياسي
عندما يستعد Claude لتشغيل npm test، يقرأ hook من نوع PreToolUse على Bash هذه البيانات من الإدخال القياسي:
{
"session_id": "abc123",
"cwd": "/home/deploy/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}يحتوي كل حدث على session_id وcwd وpermission_mode وtranscript_path وhook_event_name. وتضيف أحداث الأدوات tool_name وtool_input وtool_use_id. أما الأحداث الأخرى، فتحمل حقولها الخاصة: يحصل UserPromptSubmit على نص prompt، ويحصل SessionStart على source من startup أو resume أو clear أو compact أو fork.
يُعد jq الطريقة المعتادة لقراءة هذه البيانات داخل shell script، لكنه لا يكون متوفراً في صورة خادم مصغّرة. ثبّته أولاً باستخدام sudo apt install -y jq على Ubuntu وDebian.
ما يفعله رمز الخروج باستدعاء الأداة الجاري تنفيذه
هناك ثلاث نتائج.
- Exit 0 يعني أن الخطاف لم يُبدِ أي اعتراض. في
PreToolUseلا يعادل ذلك الموافقة، ويستمر مسار الأذونات المعتاد. وفيUserPromptSubmitوSessionStart، تُضاف المخرجات القياسية إلى سياق النموذج. - Exit 2 يمنع الإجراء في الأحداث التي يمكن منعها، ومنها
PreToolUse، وتصبح المخرجات المعيارية سبب المنع الذي يُعرض على النموذج. أما في الأحداث التي لا يمكن منعها، مثلPostToolUse، فيُتجاهل المنع، مع استمرار وصول المخرجات المعيارية إلى النموذج بوصفها ملاحظات. - أي رمز خروج آخر يُعد خطأً غير مانع. يستمر الإجراء. ويعرض السجل إشعاراً بخطأ الخطاف، يتضمن السطر الأول من المخرجات المعيارية بعد النص
Failed with non-blocking status code:.
إذا أردت أي سلوك يتجاوز المنع أو عدم إصدار أي نتيجة، فاستخدم Exit 0 واطبع كائن JSON إلى المخرجات القياسية بدلاً من ذلك. يحدد خطاف PreToolUse القرار باستخدام permissionDecision:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database drops go through a migration, not through the agent."
}
}يتجاوز "allow" المطالبة التفاعلية، ويلغي "deny" الاستدعاء ويرسل السبب إلى النموذج، ويعرض "ask" المطالبة كالمعتاد. اختر أسلوباً واحداً لكل خطاف. يؤدي الجمع بين Exit 2 وقرار JSON في المخرجات القياسية إلى نتيجة يتعين عليك البحث عن معناها.
عندما تطابق عدة خطافات حدثاً واحداً، فإنها تعمل بالتوازي ويستمر كل منها حتى الاكتمال. لا يوقف deny الصادر عن أحد الخطافات الخطافات الشقيقة، لذلك يكتب خطاف التسجيل سطره، بينما يمنع خطاف الحماية الاستدعاء نفسه. يدمج Claude Code الإجابات بعد ذلك ويحتفظ بالأكثر تقييداً، بالترتيب التالي: deny، ثم defer، ثم ask، ثم allow.
المثال 1: حظر أمر تدميري قبل تنفيذه
احفظ هذا الملف باسم .claude/hooks/block-destructive.sh في مشروعك:
#!/bin/bash
# Deny a Bash tool call whose command matches a banned pattern.
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
for pattern in 'rm -rf /' 'mkfs' 'dd if=' 'DROP TABLE'; do
if printf '%s' "$COMMAND" | grep -qiF -- "$pattern"; then
echo "Blocked by policy: the command matches '$pattern'. A human runs this one." >&2
exit 2
fi
done
exit 0اجعله قابلاً للتنفيذ، ثم سجّله في PreToolUse ضمن .claude/settings.json:
chmod +x .claude/hooks/block-destructive.sh{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
"timeout": 10,
"statusMessage": "Checking the command against policy"
}
]
}
]
}
}اختبر البرنامج النصي يدوياً قبل الوثوق به، لأن hook يتعطل عند معالجة مدخله يفشل بطريقة تسمح بالمرور:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /var/lib/postgresql"}}' \
| .claude/hooks/block-destructive.sh
echo $?يجب أن ترى السطر Blocked by policy: في stderr، وأن يكون رمز الخروج 2. مرّر إليه أمراً غير ضار، مثل ls -la، ويجب ألا ترى أي مخرجات، وأن يكون رمز الخروج 0. أثناء الجلسة، يظهر الاستدعاء المحظور في السجل مع رسالتك سبباً للحظر، ويقرأ النموذج هذه الرسالة ويتكيف معها.
هناك خاصية واحدة تجعل تنفيذ ذلك مفيداً: تعمل hooks الخاصة بـPreToolUse قبل التحقق من وضع الأذونات، في كل أوضاع الأذونات، لذلك يظل الحظر سارياً حتى مع bypassPermissions. وهذا ما يجعل hook مفيداً إلى جانب الوضع التلقائي في Claude Code وإعدادات أذوناته، حيث تُخفَّض طلبات التأكيد، بينما يستمر hook في العمل.
كن واضحاً بشأن حدود ذلك. إن مطابقة الأنماط في سلسلة أمر توفر حاجزاً ضد إهمال agent، لكنها لا تشكل حدوداً تمنع agent من التحايل، لأنّه يمكن كتابة الأمر نفسه بصيغة لا يراها grep. يجب وضع القواعد الصارمة في نظام الأذونات وفي الحساب الذي تعمل تحته العملية.
مثال 2: التنسيق والتحقق بعد كل تعديل
يعمل المطابق PostToolUse مع Edit|Write بعد أي أداة لتحرير الملفات. احفظ هذا في .claude/hooks/after-edit.sh:
#!/bin/bash
# Format the edited file, then report lint failures back to the model.
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
case "$FILE" in
*.py)
ruff format "$FILE" >/dev/null 2>&1
if ! ruff check "$FILE" >&2; then
exit 2
fi
;;
*.sh)
if ! shellcheck "$FILE" >&2; then
exit 2
fi
;;
esac
exit 0{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/after-edit.sh",
"timeout": 60
}
]
}
]
}
}اطلب من Claude إضافة دالة ذات مسافات بادئة سيئة إلى ملف Python، ثم افتح الملف. سيظهر منسقاً. هذا يؤكد تشغيل الـhook، لأن الـhook الناجح لا يعرض شيئاً في المحادثة.
لا يؤدي exit 2 هنا إلى التراجع عن أي شيء. يعمل PostToolUse بعد تنفيذ الأداة بالفعل، لذلك يبقى التعديل على القرص في كلتا الحالتين. ما يتيحه exit 2 هو وصول مخرجات ruff check إلى النموذج كملاحظات، فيُصلح الخطأ الذي أدخله للتو بدلاً من المتابعة. هذا هو الفرق بين فشل lint تكتشفه عند وقت commit وفشل يُصلحه الوكيل في الجولة نفسها.
هناك حدّان للمطابق مهمان هنا. لا يرى Edit|Write الملفات التي غيّرها أمر shell، ويكتب Claude الملفات عبر Bash بما يكفي لجعل هذه الفجوة مهمة. للحصول على تغطية لكل استدعاء، طابق Bash أيضاً، واجعل البرنامج النصي يسرد الملفات التي تغيّرت باستخدام git status --porcelain. وللحصول على تغطية مرة واحدة في كل جولة، ضع الفحص في hook من النوع Stop بدلاً من ذلك.
مثال 3: تسجيل كل استدعاء لأداة لأغراض التدقيق
يطابق الشرط الفارغ في PostToolUse كل أداة. يؤدي إرسال السجل إلى سجل النظام بدلاً من ملف في الدليل الرئيسي إلى إبقائه بعيداً عن وصول صدفة الوكيل نفسه:
{
"hooks": {
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{time: now|todate, session: .session_id, cwd: .cwd, tool: .tool_name, input: .tool_input}' | logger -t claude-code -p local0.info"
}
]
}
]
}
}اقرأ السجل مجدداً باستخدام journalctl -t claude-code -o cat | tail -n 5. يجب أن ترى سطراً واحداً بتنسيق JSON لكل استدعاء لأداة، مع ظهور الأحدث أخيراً. إذا لم يظهر شيء، فهذا يعني أن الـhook لم يعمل. يوضح قسم استكشاف الأخطاء وإصلاحها أدناه كيفية التعامل مع ذلك.
أضف الكتلة نفسها ضمن PostToolUseFailure لالتقاط الاستدعاءات التي فشلت، لأن PostToolUse يعمل عند النجاح فقط، بينما يكون الأمر الفاشل هو الأكثر أهمية عادةً. سبب استخدام logger بدلاً من الإلحاق بملف في دليلك الرئيسي هو الملكية: يعمل الـhook بصفته المستخدم نفسه الذي تعمل به صدفة الوكيل، لذلك فإن أي شيء يمكن لهذا المستخدم الإلحاق به يمكنه أيضاً تفريغه. يكتب systemd-journald السجل باستخدام حسابه الخاص.
المدة التي يمكن أن يعمل فيها hook
The data behind this chart
[
{
"label": "command, http or mcp_tool hook",
"default_timeout_seconds": 600
},
{
"label": "agent hook",
"default_timeout_seconds": 60
},
{
"label": "prompt hook",
"default_timeout_seconds": 30
},
{
"label": "command hook on UserPromptSubmit",
"default_timeout_seconds": 30
},
{
"label": "command hook on MessageDisplay",
"default_timeout_seconds": 10
},
{
"label": "any hook on SessionEnd",
"default_timeout_seconds": 1.5
}
]يحصل hook للأوامر افتراضياً على 600 ثانية، أي عشر دقائق. وتفرض بعض الأحداث حداً أقصر بكثير. تتشارك hooks من SessionEnd ميزانية قدرها 1.5 ثانية فيما بينها، لذلك يجب أن تكون عملية التنظيف في نهاية الجلسة سريعة. لكن ضبط timeout لمدة أطول في hook يرفع تلك الميزانية المشتركة لتطابقها، بحد أقصى قدره 60 ثانية.
يُلغى hook الذي يتجاوز مهلة الانتظار، ولا يصدر عنه قرار. وبالنسبة إلى حاجز PreToolUse، يعني ذلك أنه لا يمنع التنفيذ؛ إذ تتابع استدعاءات الأداة مسار الصلاحيات العادي. لذلك اجعل نصوص guardrail صغيرة. أما الأعمال البطيئة التي لا ينتظر أحد اكتمالها، مثل إرسال سجل إلى مكان آخر، فاضبط "async": true، وعندها يعمل hook في الخلفية من دون تعطيل استدعاء الأداة.
Hooks وملفات القواعد والمهارات وخوادم MCP
تختلط أربعة أشياء معاً لأنّها جميعاً تغيّر سلوك الوكيل. واحد منها فقط يتوقف عن كونه اقتراحاً.
ملف القواعد (CLAUDE.md، أو ملفاً ضمن .claude/rules/) هو نص يُحمَّل إلى سياق النموذج. وهو يوجّه السلوك ولا يفرض شيئاً. في محادثة طويلة، أو مع فرق كبير، أو عند وصول طلب مستخدم جديد، قد يفقد سطر واحد منه تأثيره. هذه هي الآلية المعتادة وراء تجاهل الوكلاء للتعليمات التي كتبتها.
المهارة هي مجلد يضم تعليمات ونصوصاً برمجية يحمّلها النموذج عندما يرى أنّ المهارة ذات صلة. هذا الحكم هو جوهر المهارة، وهو أيضاً حدّها: فالنموذج هو من يقرر. يمكنك رؤية الجانبين في مهارة مثل Ponytail، التي تدفع الوكيل نحو أصغر تغيير ينجح، لأنّها توجّه طريقة التعامل مع المهمة كاملة بأسلوب لا يستطيع أي hook توفيره، ولكن فقط عندما يختار النموذج تحميلها.
يوفّر خادم MCP (بروتوكول سياق النموذج) أدوات جديدة يمكن للنموذج استدعاؤها. وهو يوسّع نطاق الموارد التي يستطيع الوكيل الوصول إليها. لكنه لا يجعله يستخدم أيّاً منها، كما أنّه عملية منفصلة يجب عليك تشغيلها وإدارتها، وهذا عمل مستقل بحد ذاته: راجع تشغيل خوادم MCP على VPS.
الـhook هو الوحيد من بين هذه العناصر الأربعة الذي يعمل من دون أن يختاره النموذج. استخدم ملف قواعد لتحديد تفضيل، ومهارة لإجراء ينبغي للنموذج اتباعه عندما ينطبق. استخدم hook للخطوة التي يجب تنفيذها في كل مرة، أو للشيء الذي يجب ألا يحدث مطلقاً. ترد المقارنة الأعمق، بما في ذلك الحالات التي تتفوق فيها المهارة على ملف القواعد، في مقارنة المهارات وMCP وملفات القواعد.
الإضافة هي أسلوب تغليف وليست آلية خامسة. فهي تجمع hooks مع المهارات في وحدة واحدة قابلة للتثبيت. وهكذا يوزّع الفريق وسيلة الحماية نفسها على كل جهاز: راجع كيفية عمل إضافات Claude Code.
قرار الأمان على VPS مشترك
الـhook هو كود يشغّله الوكيل، ويعمل باعتباره المستخدم الذي بدأ Claude Code. ويرث بيئة ذلك المستخدم وصلاحياته على الملفات. على جهاز محمول، هذه مسألة تتعلق بسير العمل. أما على VPS يعمل فيه وكيل دون مراقبة، فهي مسألة أمنية تتكون من أربعة جوانب عملية.
الـhook الموجود في المستودع هو كود لم تكتبه أنت. يُلتزم بـ .claude/settings.json، لذلك يمكن لاستنساخ مستودع وبدء جلسة داخله تسجيل hooks جاءت مع المستودع. يضع Claude Code hooks المشروع خلف مربع حوار الثقة بمساحة العمل الخاصة بذلك المجلد، ما يعني أن قبول الثقة هو اللحظة التي تقرر فيها تشغيلها. اقرأ كتلة hooks أولاً.
يرى الـhook كامل إدخال الأداة. يسجل hook التدقيق الذي يسجل tool_input كل وسيطة لكل أمر في ملف، بما في ذلك أي token صادف وجوده في سطر الأوامر. يحتاج ذلك السجل إلى الحماية نفسها التي يحتاج إليها السر، وهذا جزء من المشكلة الأوسع المتمثلة في إبعاد الأسرار عن متناول وكيل الذكاء الاصطناعي.
يمكن للـhook الكتابة في سياق النموذج. كل ما يطبعه hook من نوع SessionStart أو UserPromptSubmit إلى stdout يُضاف إلى المحادثة. عندما يمرر hook نصاً من مصدر خارجي أو من متتبع مسائل أو من ملف سجل، فإنه يسلّم النص غير الموثوق إلى النموذج كما لو أنك كتبته بنفسك. تعامل مع stdout هذا باعتباره إدخالاً لا إخراجاً.
الامتيازات هي عنصر التحكم الحقيقي. شغّل الوكيل باعتباره مستخدماً مخصصاً غير مميز، ولا تمنحه إلا قواعد sudo التي يحتاج إليها. من المفيد وجود رفض PreToolUse، وهو مصمم ليكون إجراءً بأفضل جهد: تشير الوثائق المرجعية إلى الأمر نفسه بشأن مرشح if، وتطلب منك استخدام نظام الصلاحيات عندما تحتاج إلى رفض قاطع. قواعد الصلاحيات والحساب الذي تعمل تحته العملية هما العنصران اللذان يصمدان عند الضغط.
هناك خاصية واحدة تنطبق في كل إعداد. تعمل hooks من نوع PreToolUse قبل فحص وضع الصلاحيات في كل وضع من أوضاع الصلاحيات، لذلك يمنع hook يعيد deny الأداة حتى مع bypassPermissions. يمكن للـhooks تشديد ما تسمح به قواعد الصلاحيات. لكنها لا تستطيع تخفيفه.
لماذا لا يعمل hook لدي؟
نفّذ خطوات استكشاف الأخطاء بالترتيب. توضّح كل خطوة العَرَض الذي ستراه فعلياً.
- شغّل
/hooksوتحقق من ظهور hook ضمن الحدث المتوقع. يعني غياب hook من القائمة عادةً وجود خطأ في صياغة JSON داخل ملف الإعدادات، لأن الفواصل اللاحقة والتعليقات غير مسموح بها، أو يعني أن الملف ليس في أحد المواقع الستة المذكورة أعلاه. - طابق matcher مع اسم الأداة حرفياً. المطابقات حساسة لحالة الأحرف، لذلك لا يطابق
"bash"الأداةBashمطلقاً. - شغّل البرنامج النصي يدوياً باستخدام إدخال نموذجي، كما في المثال 1 أعلاه. يُعد رمز خروج غير متوقع خطأً في البرنامج النصي، ويبلّغ Claude Code عنه باعتباره خطأً في hook، لا باعتباره قراراً.
- يعني الإشعار
jq: command not foundأنjqمفقود على ذلك الجهاز. ويعني ظهورcommand not foundللبرنامج النصي الخاص بك أن المسار تعذّر حله، لذا استخدم${CLAUDE_PROJECT_DIR}أو مساراً مطلقاً. إذا لم يعمل البرنامج النصي إطلاقاً، فمن المحتمل أنه غير قابل للتنفيذ. - يطبع hook JSON صالحاً، لكن لا يحدث شيء. يعمل hook بصيغة shell عبر
sh -c، وإذا كان ملف تعريف shell يطبع banner، فستُضاف تلك الرسالة قبل JSON. لن يبدأ stdout بعد ذلك بـ{، لذلك يقرأ Claude Code المحتوى كله كنص عادي ويتجاهل القرار. عند الخروج بالرمز 0، لا يُبلَّغ عن أي شيء في أي مكان باستثناء سجل التصحيح. ضع أيechoفي ملف تعريفك داخل شرط بحيث يعمل فقط في جلسات shell التفاعلية. - إذا بقيت المشكلة، فابدأ الجلسة باستخدام
claude --debug-file /tmp/claude.logوشغّلtail -f /tmp/claude.logفي طرفية ثانية. يسجل سجل التصحيح أي hooks تطابقت، ورمز الخروج الذي أعاده كل منها، وكل ما كتبته إلى stdout وstderr.
FAQ
ما الفرق بين hook في Claude Code وتعليمة CLAUDE.md؟
تعليمة CLAUDE.md هي نص في سياق النموذج، ولذلك تنافس المحادثة والطلب الحالي على انتباهه، ويمكن للنموذج موازنتها معهما. أما hook فهو أمر shell يشغّله Claude Code في نقطة ثابتة من دورة حياته، ولذلك يُنفَّذ عند كل وقوع للحدث المعني، بغض النظر عما قرره النموذج. استخدم التعليمة للتفضيلات. واستخدم hook لخطوة يجب أن تحدث دائماً أو لإجراء يجب ألا يحدث مطلقاً.
كيف أوقف Claude Code عن تشغيل أمر shell محدد؟
سجّل hook من نوع PreToolUse مع matcher من نوع Bash يقرأ الأمر من .tool_input.command، ويكتب سبب الرفض إلى stderr، ثم يخرج بالرمز 2. يلغي Claude Code الاستدعاء ويعرض للنموذج سببك. ويحدث ذلك قبل فحص permission mode، لذلك يستمر الرفض حتى في وضع bypassPermissions. يُعدّ مطابقة نمط في سلسلة الأمر وسيلة حماية، وليس حداً أمنياً، لأن كتابة الأمر نفسه بصيغة أخرى قد تتجاوز النمط. لذلك ادعمها بقواعد الصلاحيات وبحساب غير مميّز.
يطبع hook لدي JSON صالحاً، لكن لا يحدث شيء. لماذا؟
السبب الأكثر شيوعاً هو ملف تعريف shell. يعمل hook الذي لا يحتوي على حقل args عبر sh -c، وتطبع بعض ملفات التعريف banner عند كل shell، فيظهر ذلك في stdout قبل JSON. وبما أن الناتج لم يعد يبدأ بـ {، يتعامل Claude Code مع الناتج كله كنص عادي ويتجاهل القرار. وعند الخروج بالرمز 0، لا يُسجَّل أي شيء في المحادثة. ضع أي echo في ملف التعريف خلف اختبار للـshell التفاعلي، ثم تأكد من إصلاح المشكلة بقراءة سجل التصحيح من claude --debug-file /tmp/claude.log.
هل تشغيل Claude Code hooks على خادم مشترك آمن؟
تعمل hooks بصلاحيات المستخدم الذي بدأ Claude Code، ولذلك يمكن لـhook تنفيذ أي إجراء يستطيع ذلك الحساب تنفيذه. يغطي إجراءان معظم المخاطر: شغّل الوكيل باستخدام حساب مخصّص غير مميّز مع سياسة sudo مقيّدة، واقرأ كتلة hooks في أي مستودع قبل قبول مربع حوار الثقة بمساحة العمل، لأن hooks الخاصة بالمشروع تُضمَّن داخل .claude/settings.json. اضبط "disableAllHooks": true في ملف الإعدادات عندما تريد منع تشغيل أي منها.