يعمل PermissionRequest hook في Claude Code في اللحظة التي يوشك فيها Claude Code على عرض prompt إذن عليك، ويمكنه الرد عليه نيابةً عنك. للسماح، يخرج الـ hook بـ 0 ويطبع {"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}}؛ وللرفض يستخدم "behavior": "deny" مع message موجهة إلى Claude. يطابق على اسم الأداة، ولا يمكنه تجاوز قواعد الرفض لديك، ورمز الخروج 2 لا يفعل شيئًا في هذا الحدث. إذا لم يطبع الـ hook أي قرار، يظهر الـ prompt المعتاد.
01مقارنة PermissionRequest مع PreToolUse وNotification
ثلاثة أحداث hooks تتعلق بالأذونات، ولكلٍّ منها وظيفة مختلفة:
PreToolUseقبل كل استدعاء أداة، سواء احتاج إلى إذن أم لانعم: allow أو deny أو ask أو deferPermissionRequestفقط عندما يوشك Claude Code على سؤالكنعم: allow أو denyNotification (permission_prompt)بعد انتظار الـ prompt نحو ست ثوانٍ أثناء غيابكلااستخدم PermissionRequest عندما يهمك الـ prompts نفسها: الرد عليها أو تسجيلها أو إرسالها إلى مكان ما. فهو ينطلق فورًا دون تأخير الثواني الست. واستخدم PreToolUse للقواعد التي يجب أن تنطبق على كل استدعاء، مثل "لا تنفّذ rm -rf أبدًا"، لأنه يعمل حتى عندما لا يظهر أي prompt. يشرح دليل Notification hook توقيت ذلك الحدث.
02ما يتلقاه الـ hook
إلى جانب الحقول المشتركة، يتلقى PermissionRequest hook قيمتَي tool_name وtool_input للاستدعاء الذي يريد Claude تنفيذه، ومصفوفة اختيارية permission_suggestions:
{
"session_id": "abc123",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}
تحتوي permission_suggestions على تغييرات القواعد التي يقترحها Claude Code لهذا الطلب. وهي ليست نسخة مطابقة للخيارات في مربع الحوار، لأن كل مربع حوار يبني خياراته الخاصة.
03كائن القرار
أعِد decision داخل hookSpecificOutput:
behavior"allow" أو "deny"updatedInputللسماح فقط. يستبدل مدخلات الأداة بالكامل، لذا أدرج الحقول غير المتغيرة أيضًاupdatedPermissionsللسماح فقط. تغييرات الأذونات المراد تطبيقها، مثل إضافة قاعدة أو تغيير الوضعmessageللرفض فقط. يوضح لـ Claude السببinterruptللرفض فقط. القيمة true توقف Claude أيضًاقاعدتان يجب تذكّرهما:
- قواعد الرفض والسؤال تظل سارية. إرجاع الـ hook للقيمة
"allow"لا يتجاوز قاعدة رفض مطابقة في إعداداتك، كما يُعاد فحص المدخلات المعدّلة وفق تلك القواعد. - رمز الخروج
2يُتجاهل هنا. في معظم الأحداث يؤدي الخروج بالرمز2إلى الحظر، لكن hook من نوعPermissionRequestيخرج بالرمز2دون كائنdecisionيترك الـ prompt كما هو. للرفض، أرجِع القرار.
04مثال: الموافقة التلقائية على prompt واحد
يوافق مثال Anthropic نفسها على ExitPlanMode، وهو الـ prompt الذي يظهر عندما ينتهي Claude من إعداد خطة، كي لا تُسأل في كل مرة:
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}
اجعل الـ matchers ضيقة. فالـ matcher الفارغ أو .* مع موافقة شاملة سيوافق على كل prompt، بما في ذلك كتابة الملفات وأوامر الـ shell.
05مثال: اتخاذ القرار بناءً على الأمر
يرفض هذا السكربت (يتطلب jq) أي شيء يتضمن git push مع سبب يستطيع Claude قراءته، ويترك لك بقية الـ prompts بعدم طباعة أي شيء:
#!/bin/bash
# ~/.claude/hooks/permission-check.sh
input=$(cat)
cmd=$(jq -r '.tool_input.command // empty' <<<"$input")
case "$cmd" in
*"git push"*)
jq -nc '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "deny",
message: "Pushing is not allowed from this session. Commit and stop."}}}'
;;
esac
exit 0
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/permission-check.sh" }]
}
]
}
}
أنشئ الـ JSON باستخدام jq بدلاً من دمج النصوص، حتى لا تُفسده علامات الاقتباس في الرسالة. وتأكد من عدم طباعة أي شيء آخر إلى stdout؛ فإذا كان ملف تعريف الـ shell يطبع سطراً، فلن يقرأ Claude Code القرار.
06مثال: الموافقة وتذكّرها طوال الجلسة
للموافقة على طلب ومنع Claude من السؤال مجدداً حتى نهاية الجلسة، أضف إدخالاً في updatedPermissions مع destination: "session". يمكن للـ hook أن يعيد أحد عناصر permission_suggestions التي استلمها مع تغيير الوجهة فقط:
#!/bin/bash
input=$(cat)
jq -c '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "allow",
updatedPermissions: [(.permission_suggestions // [])[]
| select(.type == "addRules")
| .destination = "session"]}}}' <<<"$input"
بصيغته الحالية، يوافق هذا على كل طلب يراه، لذا استخدمه مع matcher ضيق. ويمكن أن تكون الوجهة أيضاً localSettings أو projectSettings أو userSettings، وهي تكتب القاعدة في ملف إعدادات بدلاً من إبقائها في الذاكرة.
07غالباً ما تكون قاعدة الصلاحيات هي الأداة الأنسب
إذا كانت الإجابة ثابتة دائماً، فلا حاجة إلى hook. ضع القاعدة في settings.json وسيطبّقها Claude Code دون تشغيل أي شيء:
{
"permissions": {
"allow": ["Bash(npm run *)"],
"deny": ["Bash(git push *)"]
}
}
تُقيَّم القواعد بدءاً بالرفض، ثم السؤال، ثم الموافقة. يصبح الـ hook مفيداً حين يعتمد القرار على شيء لا تراه القاعدة، مثل الوقت، أو الفرع الذي تعمل عليه، أو شخص يجيب من مكان آخر. يتناول شرح أوضاع الصلاحيات الخيارات المدمجة الأخرى.
08أمور يقع فيها الكثيرون
- التشغيل غير التفاعلي. مع
claude -pلا يوجد من يُسأل. تظل hooksPermissionRequestتعمل، وإن لم يُرجع أيٌّ منها قراراً يُرفض الاستدعاء. - المهلات. يمكن لـ hook من نوع command أن يعمل حتى 600 ثانية افتراضياً، وClaude ينتظر. اضبط
timeout(بالثواني) إذا كان الـ hook ينتظر شيئاً بطيئاً، كشخص مثلاً. - بعض الـ prompts لا تُفعّله. طلب الشبكة الصادر عن أمر داخل sandbox لا يشغّل
PermissionRequest. استخدم نوع الإشعارpermission_promptلذلك. - الأمان. الـ hook الذي يُرجع
"allow"يوافق على إجراءات باسمك وبصلاحيات مستخدمك. راجعه كأي كود آخر قادر على تشغيل الأوامر.
09أين يأتي دور Claude Code Notifier
السبب الأكثر شيوعاً لكتابة hook من نوع PermissionRequest هو الرد على الـ prompts وأنت بعيد عن لوحة المفاتيح. يتطلب الحل اليدوي أن يرسل الـ hook الطلب إلى مكان ما، وينتظر الرد، ثم يُرجعه قبل انتهاء المهلة. هذا ممكن، وبعض الناس يبنونه باستخدام chat bot.
Claude Code Notifier يتولى ذلك عنك. أداة سطر الأوامر المجانية الخاصة به، ccn، تعمل على Mac وWindows وLinux وتثبّت الـ hooks الخاصة بها في ~/.claude/settings.json. بعد ربط تطبيق iPhone أو Android، يصل طلب الصلاحية إلى هاتفك ويمكنك الموافقة مرة واحدة، أو الموافقة طوال الجلسة، أو الرفض؛ ثم يُرجع ccn هذا القرار من الـ hook PermissionRequest. إذا كتبت شيئاً خلال آخر 30 ثانية، فلا انتظار ويسألك الـ terminal كالمعتاد. أما إن كنت بعيداً، فينتظر الـ terminal الهاتف حتى حدّ أقصى تختاره (hold_ceiling_seconds، والافتراضي 600)، ويتوقف عن الانتظار فور لمسك لوحة المفاتيح. الأداة مستقلة وليست من إنتاج Anthropic. راجع الموافقة على الصلاحيات من هاتفك لسير العمل، وهل الموافقة عن بُعد آمنة؟ للجانب الأمني.
10الأسئلة الشائعة
كيف أوافق تلقائياً على الصلاحيات باستخدام hook؟
أضف hook من نوع PermissionRequest مع matcher ضيق، واجعله يطبع قرار JSON يحتوي على "behavior": "allow" داخل hookSpecificOutput، ثم يخرج بالرمز 0. وإذا كان الأمر نفسه يجب السماح به دائماً، فقاعدة permissions.allow في settings.json أبسط ولا تحتاج إلى hook.
لماذا لا يرفض رمز الخروج 2 الطلب؟
PermissionRequest من الأحداث التي لا يُعتدّ فيها بالخروج بالرمز 2؛ إذ يتجاهله Claude Code ويعرض الـ prompt المعتاد. أرجِع بدلاً من ذلك "behavior": "deny" في كائن القرار.
هل يمكن لـ hook من نوع PermissionRequest تجاوز قواعد الرفض لديّ؟
لا. تظل قواعد الرفض والسؤال تُقيَّم، لذا فإن "allow" الصادرة من hook لا تتخطى قاعدة رفض مطابقة. كما يُفحص أي updatedInput تُرجعه وفق تلك القواعد أيضاً.
ما الفرق بين PermissionRequest وPreToolUse؟
يعمل PreToolUse قبل كل استدعاء لأداة، ويمكنه الموافقة أو الرفض أو السؤال أو التأجيل. أما PermissionRequest فيعمل فقط عندما يوشك Claude Code على عرض prompt، ويمكنه الموافقة عليه أو رفضه. استخدم PreToolUse للسياسات، وPermissionRequest للرد على الـ prompts.
ماذا يحدث إذا لم يطبع الـ hook أي شيء؟
لا يتغير شيء. يعرض Claude Code prompt الصلاحيات المعتاد في الـ terminal، كأن الـ hook غير موجود.