# Hooks في Claude Code 2026: إعداد خطوة بخطوة و7 أمثلة جاهزة للاستخدام

> 7 hooks جاهزة للاستخدام في .claude/settings.json، انسخها والصقها مباشرة: حماية .env، وprettier، ومنع force-push إلى main، والشرطة الطويلة، وخادم تطوير. 4 أنواع من المعالجات و31 حدثًا.

Source: https://ailoot.ai/blog/ar/claude-code-hooks/
Canonical: https://ailoot.ai/blog/ar/claude-code-hooks/
Language: ar
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-121
Revision: sha256-9904c0af546322c9fe73b42122b245bf94fcfaa27d4b62c4d86abdaaa723372c


Hooks في Claude Code هي أوامر shell أو استدعاءات HTTP أو فحوصات LLM قصيرة تُنفَّذ تلقائيًا قبل إجراء Claude أو بعده. تضيف فحوصًا لأحداث أدوات محددة، وتنسق الشيفرة بعد التعديل، وتدخل سياقًا عند بدء الجلسة. قد يرفض PreToolUse المطابق ذلك الاستدعاء؛ وأمثلة regex هذه حواجز مساعدة وليست حد أمان لكل مسار وصول للملفات أو Git. فيما يلي شرح لأنواع المعالجات الأربعة، وأحداث دورة الحياة الـ31، واختبار لمعرفة «متى ينبغي أن تكون القاعدة hook»، و7 hooks جاهزة للنسخ إلى `.claude/settings.json`، و3 مشكلات واجهتها بنفسي.



[وثائق Ailoot: واجهة API والمفاتيح والإعداد](https://ailoot.ai/docs/).

## ما هي hooks في Claude Code؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

الـhook هو أمر shell أو استدعاء HTTP أو prompt قصير لـLLM أو وكيل فرعي يشغّله Claude Code استجابةً لحدث محدد من أحداث دورة الحياة. بخلاف التعليمات في `CLAUDE.md`، التي قد يتجاهلها النموذج أو ينساها، يعمل الـhook بالطريقة نفسها في كل مرة: إنه شيفرة، وليس اقتراحًا.

</div>

لمدة نصف عام، أبقيت قاعدة «لا تعدّل `.env` مباشرةً أبدًا» في `CLAUDE.md`. كان Claude يخالفها مرة في الشهر. ليس عن قصد؛ بل كان ينساها ببساطة مع نهاية جلسة طويلة. مرةً، انتهى الأمر بوجود token إضافي في `.env`؛ وفي مرة أخرى، استبدل متغيرٌ باسم مكرر المتغيرَ المستخدم.

نقلت القاعدة إلى hook لفحص مسار التعديل المطابق بدل الاعتماد على تذكر النموذج؛ وما زالت المسارات الأخرى تحتاج إلى ضوابط. عندما تقدم أداة تعديل مباشر مطابقة مسار `.env`، يرفض hook المضبوط ذلك الاستدعاء. تحتاج إعادة توجيه Bash والنصوص وأدوات MCP ومسارات الوصول الأخرى إلى ضوابط مستقلة؛ ولا يجعل المثال الملف غير قابل للتغيير.

<figure>
<blockquote>
<p>Hooks هي أوامر shell أو نقاط نهاية HTTP أو prompts لـLLM تحددها، وتُشغَّل تلقائيًا في نقاط معينة من دورة حياة Claude Code.</p>
</blockquote>
<figcaption>— Anthropic، https://code.claude.com/docs/en/hooks</figcaption>
</figure>

بعبارة أخرى، الـhook هو أمر منك يشغّله Claude Code عند وقوع حدث محدد. كل تعديل لملف أو تشغيل Bash أو بدء جلسة أو اكتمال وكيل فرعي هو حدث يمكنك ربط hook به.

لماذا يهم ذلك لمن يبرمجون بأسلوب vibe coding:

- حماية الملفات التي تحتوي على أسرار من التعديلات غير المقصودة.
- تشغيل prettier/eslint تلقائيًا بعد كل توليد للشيفرة.
- حظر `git push --force` إلى main من دون إذن صريح.
- إدخال سياق حديث عند بدء كل جلسة.
- تسجيل إجراءات Claude لأغراض التدقيق.

يتولى CLAUDE.md السياق. وتتولى hooks **الإجراءات**. هذان أمران مختلفان، والخلط بينهما هو الخطأ الأشيع لدى المبتدئين.

## ما الفرق بين hooks وCLAUDE.md وskills؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

`CLAUDE.md` نص يقرؤه النموذج في بداية الجلسة ويحاول اتباعه. أما الـhook فهو أمر shell تشغّله عملية Claude Code استجابةً لحدث، بمعزل عن النموذج. والـskill حزمة تعليمات مسماة يستطيع Claude استدعاءها باستخدام أداة Skill، مثل دالة. يعالج كل واحد من الثلاثة نوعًا مختلفًا من المهام.

</div>

تشرح وثائق Anthropic صراحةً الفرق بين `CLAUDE.md` وhooks:

<figure>
<blockquote>
<p>يُقدَّم محتوى CLAUDE.md بوصفه رسالة مستخدم بعد system prompt، وليس جزءًا من system prompt. يقرؤه Claude ويحاول اتباعه، لكن لا يوجد ضمان للامتثال الصارم.</p>
</blockquote>
<figcaption>— Anthropic، وثائق الذاكرة، https://code.claude.com/docs/en/memory</figcaption>
</figure>

بعبارة أخرى، يصل محتوى `CLAUDE.md` إلى Claude كأول رسالة من المستخدم، لا كتعليمات نظام. يقرؤه النموذج ويحاول اتباعه، لكن لا توجد ضمانات، ولا سيما قرب نهاية جلسة طويلة حين يتضخم السياق وتُزاح التعليمات الأقدم.

يعمل الـhook بطريقة مختلفة. فهو موجود في `settings.json`، ولا يعتمد على النموذج، وتشغّله عملية Claude Code عند حدث محدد: `PreToolUse` أو `PostToolUse` أو `SessionStart` وغيرها. يعمل الأمر قبل أن يتلقى Claude النتيجة، ويمكنه حظر إجراء فعليًا (exit 2) أو تعديل ملف بعد تحريره (exit 0).

مقارنة:

| الميزة | CLAUDE.md | Hook | Skill |
|----|----|----|----|
| مكانه | `CLAUDE.md` في جذر المشروع | `.claude/settings.json` | مجلد `.claude/skills/` |
| من يشغّله | يقرؤه النموذج بوصفه جزءًا من رسالة مستخدم | عملية Claude Code عند وقوع حدث | النموذج عبر أداة Skill |
| ضمان التنفيذ | لا (بأفضل جهد) | ينفذ command hook مضبوط ومطابق شيفرة؛ ويتوقف الفشل وسلوك الخروج على الحدث | عبر استدعاء للنموذج (مع grant، لكن ليس على مستوى shell) |
| الغرض | سياق المشروع والنبرة والأسلوب والتوجيه | القيود والتنسيق والفحوصات على ملفات محددة | إجراءات معقدة متعددة الخطوات ذات معاملات |
| وقت التشغيل | مع كل رسالة نموذج في الجلسة | عند حدث من دورة الحياة | عندما يستدعيه النموذج |
| هل يمكنه حظر إجراء؟ | لا | نعم (exit 2) | لا |

ستجد القالب الكامل لـ`CLAUDE.md` والقواعد الست لملف حي في الدليل [كيفية إعداد CLAUDE.md في 2026: قالب جاهز للاستخدام و6 قواعد](https://ailoot.ai/blog/ar/claude-md-template/). تعرّف على skills بوصفها إمكانات مسماة في [Claude Code Skills: كيفية إنشاء مكتبتك الخاصة](https://ailoot.ai/blog/ar/claude-code-skills-library/).

الـhook هو المستوى الأدنى: لا نموذج ولا تفسير، بل shell فقط.

Hooks هي إحدى أدوات [هندسة السياق](https://ailoot.ai/blog/ar/context-engineering/)، إلى جانب Plan Mode و<bdi>/compact</bdi>.



## أنواع hooks الأربعة: Command وHTTP وPrompt وAgent — أيها تختار؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

لدى Claude Code أربعة أنواع من المعالجات. Command هو سكربت shell، وهو النوع الأكثر شيوعًا. يرسل HTTP طلب POST إلى عنوان URL خاص بك، للفرق وعمليات التكامل. يجري Prompt استدعاء Haiku قصيرًا لفحص دلالي. وينشئ Agent وكيلًا فرعيًا يملك صلاحية استخدام الأدوات لإجراء تحقق معمق. يحتاج المبتدئون إلى Command وPrompt؛ فهما يغطيان 90% من المهام.

</div>

| النوع | ما الذي يفعله | متى تختاره | التعقيد |
|----|----|----|----|
| **Command** | يشغّل أمر shell، ويتلقى JSON على stdin، ويرد برمز خروج وstdout | في 90% من الحالات: فحص المسارات، والتنسيق، واستخدام grep على المحتوى، وlsof، وعمليات git | منخفض |
| **HTTP** | يرسل طلب POST إلى عنوان URL خاص بك مع جسم JSON ويتلقى استجابة JSON | عندما تنتمي القاعدة إلى فريق<bdi>/CI</bdi>: التحقق المركزي، ولوحة حالة مشتركة، والتدقيق عن بُعد | متوسط (يتطلب خادمًا) |
| **Prompt** | يرسل prompt قصيرًا إلى Haiku (افتراضيًا) ويحصل على إجابة نعم/لا لتقييم دلالي | عندما لا يكفي grep: «قيّم ما إذا كان النص يبدو مؤسسيًا» | منخفض |
| **Agent** | ينشئ وكيلًا فرعيًا بصلاحية Read/Grep/Glob لإجراء فحوصات متعددة الخطوات | التحقق المكثف: «اعثر على جميع الملفات المتغيرة وتأكد من وجود اختبارات مقابلة لها» | مرتفع |

أكثر الأخطاء شيوعًا هو محاولة تنفيذ ما يتطلب Prompt باستخدام Command. مثلًا، «لا تستخدم الشرطة الطويلة في ملفات `.md`» مهمة grep، لذا يكون Command مثاليًا. أما «لا تكتب بنبرة مؤسسية» فمسألة دلالية، ولذلك تحتاج إلى Prompt.

إذا كنت في البداية، فاستخدم Command. كل الـhooks السبعة الجاهزة أدناه من نوع Command. يأتي دور HTTP/Prompt/Agent لاحقًا عندما تكون مستعدًا.

## أين يوجد settings.json، وما مستويات الإعداد الأربعة؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

يبحث Claude Code عن الإعدادات في أربعة مواضع: المؤسسة (تُنشر عبر MDM)، والمستخدم (`~/.claude/settings.json`)، والمشروع (`.claude/settings.json` في جذر المستودع)، والمحلي (`.claude/settings.local.json`، ولا يُضاف إلى git). تُدمج الملفات الأربعة كلها عند بدء الجلسة، وتكون الأولوية للمستوى الأدنى.

</div>

| المستوى | المسار | متى يُطبّق | هل يوجد في git؟ |
|----|----|----|----|
| المؤسسة | `/Library/Application Support/ClaudeCode/settings.json` (Mac)، `/etc/claude-code/settings.json` (Linux) | على مستوى الفريق عبر MDM | لا |
| المستخدم | `~/.claude/settings.json` | جميع مشاريع مستخدم واحد | لا |
| المشروع | `.claude/settings.json` في جذر المستودع | هذا المشروع، للفريق كله | **نعم** |
| المحلي | `.claude/settings.local.json` | هذا المشروع وحده وهذا المستخدم وحده | **لا** (في `.gitignore`) |

أحتفظ بالـhooks السبعة الموضحة أدناه في `.claude/settings.json` الخاص **بالمشروع**، بحيث تُضمَّن تلقائيًا عند استنساخ المستودع ويتبع Claude القواعد نفسها في نافذة جديدة. أما الـhooks التجريبية الشخصية فأضعها في `.claude/settings.local.json` كي لا تعيق الفريق.

تُدمج الملفات الأربعة كلها عند بدء الجلسة. عند تعارض القواعد، تكون الأولوية للمستوى الأدنى (المحلي على المشروع، والمشروع على المستخدم).

**أسرع طريقة لإنشاء أول hook** هي قائمة `/hooks` التفاعلية داخل Claude Code. اختر حدثًا من القائمة، ثم matcher (مثل `Bash` أو `Edit`)، والصق أمرًا. سيكتب Claude Code JSON صالحًا في `settings.json` نيابةً عنك.

## أحداث دورة الحياة الـ31: ما الذي يحتاج إليه المبتدئون؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

لدى Claude Code حاليًا 31 حدثًا لدورة الحياة، موزعة على ثلاثة إيقاعات: مرة لكل جلسة (`SessionStart`/`SessionEnd`)، ومرة لكل دور (`UserPromptSubmit`/`Stop`)، وعند كل استدعاء أداة (`PreToolUse`/`PostToolUse`). يحتاج المبتدئون إلى 4 أحداث من أصل 31: `PreToolUse` للقيود، و`PostToolUse` للتعديلات التلقائية، و`SessionStart` للسياق، و`Stop` للفحوصات النهائية.

</div>

<figure>
<blockquote>
<p>تقع الأحداث وفق ثلاثة تكرارات: مرة لكل جلسة (SessionStart, SessionEnd)، ومرة لكل دور (UserPromptSubmit, Stop, StopFailure)، وعند كل استدعاء أداة داخل الحلقة الوكيلة (PreToolUse, PostToolUse).</p>
</blockquote>
<figcaption>— Anthropic، مرجع hooks، https://code.claude.com/docs/en/hooks</figcaption>
</figure>

من المهم فهم الإيقاعات الثلاثة حتى لا تختلط عليك الأمور:

1.  **مرة لكل جلسة** - `SessionStart` و`SessionEnd` و`Setup`. تعمل هذه الأحداث مرة واحدة عند فتح Claude Code وإغلاقه. استخدمها لتهيئة السياق وتنظيف الملفات المؤقتة.
2.  **مرة لكل دور** - `UserPromptSubmit` و`UserPromptExpansion` و`Stop` و`StopFailure`. تقع مع كل رسالة منك وكل مرة يتوقف فيها النموذج. استخدمها لتسجيل المحادثة وإجراء الفحوصات النهائية للنتيجة.
3.  **عند كل استدعاء أداة** - `PreToolUse` و`PostToolUse` و`PostToolUseFailure` و`PermissionRequest` و`PermissionDenied` و`PostToolBatch` و`SubagentStart` و`SubagentStop`. تقع مع كل تعديل ملف أو تشغيل Bash أو قراءة أو بحث. هذه أكثر الطبقات نشاطًا، وتُنفَّذ فيها القيود والتنسيق والتحققات.

تتضمن قائمة الأحداث الـ31 الكاملة أحداثًا غير متزامنة/تفاعلية: `TeammateIdle` و`PreCompact` و`PostCompact` و`Elicitation` و`ElicitationResult` و`InstructionsLoaded` و`ConfigChange` و`CwdChanged` و`FileChanged` و`WorktreeCreate` و`WorktreeRemove` و`Notification`. هذه للحالات المتقدمة؛ تجاوزها الآن.

## اختبار من خمسة أسئلة: متى توضع القاعدة في hook ومتى تبقى في CLAUDE.md؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

إذا كانت القاعدة تتعلق بإجراء (لا بالسياق)، وتخص السلامة/المال/البيانات، ويمكن فحصها باستخدام grep/regex، ويصعب تدارك خرقها، وسبق أن خالفها Claude مرة واحدة على الأقل، فهي مكانها في hook. إذا كانت إجابة «نعم» أقل من ثلاث مرات من أصل خمسة، فلتبقَ في `CLAUDE.md`. طبّقت الاختبار على 80 سطرًا لدي، ونقلت 5 قواعد إلى hooks.

</div>

بالنسبة إليّ، يعمل هذا كقانون لا كاقتراح. اختزلت الاختيار بين `CLAUDE.md` وhook إلى اختبار بسيط من خمسة أسئلة. إذا كانت ثلاث إجابات على الأقل «نعم»، توضع القاعدة في hook. إذا كانت أقل من ثلاث، تبقى في النص.

**اختبار «متى ينبغي أن تكون القاعدة hook؟»:**

1.  هل تتعلق هذه القاعدة بإجراء لا بسياق؟ («لا تشغّل `rm -rf`» إجراء؛ و«لدي مشروع TypeScript» سياق.)
2.  هل تتعلق هذه القاعدة بالسلامة أو المال أو البيانات؟ («لا تلمس `.env`»، «لا تنشر المفاتيح».)
3.  هل يمكنك فحصها باستخدام grep أو regex أو رمز خروج أمر shell؟ («بعد تعديل `.md`، تحقق من عدم وجود شرطة طويلة».)
4.  إذا خولفت القاعدة، فهل ستكون عواقب ذلك مؤلمة عند التراجع؟ («force-push إلى main» يصعب التراجع عنه؛ و«نسيان نقطة في النهاية» يسهل.)
5.  هل سبق أن رأيت Claude ينسى هذه القاعدة مرة واحدة على الأقل؟

**مثال: «لا تستخدم الشرطة الطويلة؛ استخدم الشرطات القصيرة فقط.»** هل هي إجراء؟ نعم. سلامة؟ لا. يمكن فحصها باستخدام grep؟ نعم. يصعب التراجع عنها؟ لا. هل رأيت مخالفتها؟ نعم. **3 من 5: ضعها في hook.**

**مثال مضاد: «أنا من مستخدمي vibe coding ولست مطورًا؛ اكتب من دون عامية.»** هل هي إجراء؟ أقرب إلى السياق. سلامة؟ لا. هل يمكن فحصها باستخدام regex؟ صعب جدًا. يصعب التراجع عنها؟ لا. هل رأيت مخالفتها؟ نعم. **1 من 5: أبقها في `CLAUDE.md`.**

اجتازت خمس من قواعدي الاختبار. نقلتها إلى hooks. أما نسبة 75% المتبقية فظلت في الملف النصي.

## 7 hooks جاهزة للنسخ

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

فيما يلي 7 hooks جاهزة للاستخدام في `.claude/settings.json`: حظر تعديلات `.env`، وتشغيل prettier تلقائيًا، ومنع force-push إلى main، والتحقق من الشرطة الطويلة، وإخلاء منفذ قبل `npm run dev`، وإجراء commit تلقائي بعد نجاح الاختبارات، وإدخال سياق حديث عند بدء الجلسة. انسخ كل hook إلى الملف، وأعد تشغيل Claude Code، وسيعمل.

</div>

تُخزَّن الـhooks السبعة كلها في ملف `.claude/settings.json` واحد في جذر المشروع. يتضمن كل إدخال نوع حدث، ومرشح أداة (matcher)، وأمر shell. يرفض رمز الخروج 2 استدعاء PreToolUse مطابقًا قبل التنفيذ. في PostToolUse، حدث الإجراء بالفعل؛ يبلغ hook ملاحظات ولا يستطيع التراجع عنه.

<figure>
<blockquote>
<p>في معظم أحداث hook، لا يحظر الإجراء إلا رمز الخروج 2. يتعامل Claude Code مع رمز الخروج 1 بوصفه خطأً غير مانع، ويتابع الإجراء، مع أن 1 يعني الفشل تقليديًا في Unix.</p>
</blockquote>
<figcaption>— Anthropic، مرجع hooks، https://code.claude.com/docs/en/hooks</figcaption>
</figure>

يعني `exit 0` النجاح. ويعني `exit 1` وجود خطأ، لكن Claude يتابع (وهذا هو النمط المضاد الأول أدناه). ولـ `exit 2` سلوك بحسب الحدث: قد يمنع PreToolUse، بينما يبلغ PostToolUse ملاحظات عن إجراء حدث بالفعل.

### Hook 1. حراسة تعديلات `.env` المباشرة (PreToolUse, exit 2)

يحظر محاولات تعديل أي ملف يطابق `.env` أو `.env.local` أو `.env.production` وما إلى ذلك مباشرةً.

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '(^|/)\\.env(\\.|$)' && { echo 'لا تعدّل `.env` مباشرةً. أخبرني بالمتغير الذي تريد إضافته، وسأفتح الملف بنفسي.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** قبل كل `Edit`/`Write`/`MultiEdit`، يحصل على مسار الملف ويفحص باستخدام grep ما إذا كان يطابق نمط `.env`. وإذا طابقه، يعيد exit 2 مع رسالة. يرى Claude stderr ويخبرني: «حاولت تعديل `.env`، لكن hook منعني؛ أخبرني بالمتغير الذي تريد إضافته».

### Hook 2. تشغيل prettier تلقائيًا بعد كل تعديل للشيفرة (PostToolUse, exit 0)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(ts|tsx|js|jsx|json)$' && npx prettier --write \"$FILE\" 2>/dev/null & exit 0"
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** بعد كل `Edit`/`Write`، يحصل على المسار، وإذا كان امتداد الملف `.ts`/`.tsx`/`.js`/`.jsx`/`.json` يشغّل prettier في الخلفية باستخدام `&`. وينتهي دائمًا بـ0 لأن هذه عملية تنسيق لا تقييد. إذا فشل prettier، فلا مشكلة كبيرة؛ يبقى الملف كما كان.

الفرق عن hook 1 جوهري: استخدام PreToolUse + exit 2 يعني حظرًا صارمًا **قبل** الإجراء. واستخدام PostToolUse + exit 0 يعني تعديلًا مرنًا **بعد** الإجراء. هذان وضعان مختلفان لمهمتين مختلفتين.

### Hook 3. حظر `git push --force` إلى الفرع main (PreToolUse, exit 2)

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE 'git[[:space:]]+push' && echo \"$CMD\" | grep -qE '(\\-\\-force|\\-\\-force-with-lease|[[:space:]]-f([[:space:]]|$))' && echo \"$CMD\" | grep -qE '([[:space:]/:])main([[:space:]/:]|$)' && { echo 'يُحظر إجراء force-push إلى main. أنشئ فرعًا جديدًا أو اطلب مني تنفيذ ذلك يدويًا.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** عند كل تشغيل لـBash، يتحقق من أن الأمر يتضمن `push` و`--force`/`--force-with-lease`/`-f` ويذكر `main`. إذا انطبقت الشروط الثلاثة كلها، يحظره برسالة واضحة.

كان هذا القانون مكتوبًا بأحرف كبيرة في `CLAUDE.md`. ومع ذلك، كان Claude يخالفه مرة كل أسبوعين تقريبًا حين أطلب منه «إجراء force-push للتراجع عن commit سيئ». يلتقط hook صيغ الأوامر الحرفية المعروضة، لكن الأسماء البديلة والنصوص وفرع الوجهة الضمني أو عميل Git آخر قد تتجاوز الفحص النصي. افرض حماية main في المستودع البعيد وقيّد بيانات اعتماد الوكيل.

### Hook 4. حظر الشرطة الطويلة في الملفات النصية (PostToolUse, exit 2)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(md|mdx|tsx?)$' && grep -q $'\\xe2\\x80\\x94' \"$FILE\" && { echo \"يحتوي الملف $FILE على شرطة طويلة (U+2014). استبدلها بشرطة عادية (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** يبلغ PostToolUse مع exit 2 فشل فحص لاحق إلى Claude بعد التعديل؛ ولا يعكس الكتابة أو يمنعها. إذا عدّل Claude ملفًا وترك فيه شرطة طويلة، يفشل الـhook ويتلقى Claude ملاحظات ويحاول مجددًا. بعد 2–3 جلسات، يتوقف Claude تمامًا عن إدخال هذا المحرف.

لاحظ أن grep يبحث تحديدًا عن محرف الشرطة الطويلة (U+2014) باستخدام `$'\xe2\x80\x94'`، وهي صيغة Bash لكتابته كبايتات UTF-8. إذا كتبت ببساطة `grep -q '-'`، فسيمسك بالشرطات العادية أيضًا ويعمل على كل ملف. وقد يعطّل محرف غير مرئي المنطق كله.

### Hook 5. إخلاء المنفذ 3000 قبل `npm run dev` (PreToolUse, exit 0)

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE '(npm|pnpm|yarn)[[:space:]]+run[[:space:]]+dev' && { lsof -ti:3000 | xargs -r kill -9 2>/dev/null; echo 'تم إخلاء المنفذ 3000 قبل التشغيل' >&2; }; exit 0"
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** قبل كل `npm run dev` (أو `pnpm`/`yarn`)، ينهي العملية على المنفذ 3000. وينتهي دائمًا بـ0 لأنه إجراء تحضيري لا تقييد. يحمي ذلك من حالات يرى فيها Claude «المنفذ مستخدم»، فيظن أن المشكلة في الشيفرة ويبدأ بإصلاح أشياء، بينما لم يكن الأمر سوى أن الخادم القديم ظل يعمل.

### Hook 6. إجراء commit تلقائيًا بعد نجاح الاختبارات (PostToolUse, exit 0)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat); CMD=$(printf '%s' \"$INPUT\" | jq -r '.tool_input.command // empty') || exit 1; EXIT=$(printf '%s' \"$INPUT\" | jq -r '.tool_response.exit_code // empty') || exit 1; case \"$CMD\" in 'npm test'|'pnpm test'|'yarn test') ;; *) exit 0 ;; esac; [ \"$EXIT\" = \"0\" ] || exit 0; git diff --cached --quiet || { echo 'Existing staged changes: review manually' >&2; exit 0; }; git add -- src/orders.ts tests/orders.test.ts || exit 1; git diff --cached --quiet && exit 0; git commit -m 'tests: checkpoint after successful run'"
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** يقرأ JSON من stdin مرة واحدة ويقبل أوامر الاختبار البسيطة المطابقة حرفيًا فقط. يتابع عندما تكون `tool_response.exit_code` المقدمة صفرًا صراحة؛ وإذا لم يعرض إصدارك الحقل، يتجاوز commit بدل تخمين النجاح. استبدل `src/orders.ts` و`tests/orders.test.ts` بالملفات الدقيقة المملوكة للمهمة. توقف التغييرات المرحلية القائمة الوصفة لمنع إدراج عمل آخر. يوقف فشل الإضافة التنفيذ، ولا ينشئ الفرق الفارغ commit، وتبقى hooks العادية مفعلة. اختبر حالات النجاح والفشل والنتيجة الغائبة دون اتصال قبل تفعيله.

نمط مضاد: إضافة `git commit --no-verify` إلى كل Edit. أبق فحوص commit مفعلة. إذا أعاد فحص تشغيل الأتمتة تكراريًا، فضيّق المحفز أو أعد تصميم الفحص؛ ولا تتجاوز التحقق افتراضيًا. راجع الفرق المرحلي وملكية المهمة قبل تفعيل commits تلقائية.

### Hook 7. إدخال سياق حديث عند بدء الجلسة (SessionStart مع additionalContext)

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo 'no-branch'); STATUS=$(git status --short 2>/dev/null | head -10); echo \"{\\\"additionalContext\\\":\\\"Current branch: $BRANCH. Uncommitted changes:\\n$STATUS\\\"}\""
          }
        ]
      }
    ]
  }
}
```

**ما الذي يفعله:** عند بدء الجلسة، يجمع فرع Git الحالي وقائمة التغييرات غير الملتزم بها، ثم يعيد JSON يتضمن حقل `additionalContext`. يُدخل Claude Code هذا النص في أول رسالة للنظام، فيعرف Claude منذ البداية موضعه وما تبقى من الجلسة السابقة.

<figure>
<blockquote>
<p>استخدم additionalContext للمعلومات التي ينبغي أن يعرفها Claude عن الحالة الحالية للبيئة أو عن عملية أُنجزت للتو: حالة البيئة (الفرع الحالي، هدف النشر، feature flags النشطة)، وقواعد المشروع المشروطة (أمر الاختبار المناسب للملف الذي عُدّل للتو)، والبيانات الخارجية (المشكلات المفتوحة المسندة إليك، ونتائج CI الأخيرة).</p>
</blockquote>
<figcaption>— Anthropic، مرجع hooks، https://code.claude.com/docs/en/hooks</figcaption>
</figure>

توسعات هذا الـhook: جلب أحدث commit ووصفه، وحالة GitHub Actions، والـsprint النشط من Linear/Jira، والمشكلات المفتوحة في المستودع الحالي.

## 3 مشكلات في hooks يقع فيها كل شخص آخر

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

المشكلة الأولى: hook ينتهي بـexit 1 يمر بصمت ويتجاهل Claude الخطأ. الثانية: يُقتل العمل الثقيل في hook بعد مهلة 60 ثانية. الثالثة: لا يرى hook إلا متغيرات البيئة من `~/.zshrc`، لا ملف `.env` الخاص بالمشروع. يمكن إصلاح كل واحدة بسطر واحد، لكن من دون معرفة هذه المشكلات قد يبدو أن «hooks لا تعمل».

</div>

### المشكلة 1. hook ينتهي بـexit 1 يمر بصمت

هذه أخطر مشكلة. لمدة أسبوع، ظننت أن hook حماية `.env` يعمل، لكن اتضح أنني استخدمت `jq -r '.tool_input.file_path'` في الـhook من دون التحقق من null. عندما كان `tool_input` فارغًا، خرج jq بالرمز 1. تعامل Claude مع exit 1 باعتباره «خطأ hook، تابع»، فتم الإجراء. لم أستطع فهم سبب استمرار لمس `.env`. ولم أرَ السبب إلا عندما شغّلت الـhook يدويًا في terminal بمدخل فارغ.

**الحل قاعدة واحدة:** ابدأ كل hook بالتحقق من null، وأنهه بـ`|| exit 0`. عندها يؤدي أي خروج غير متوقع إلى خروج «آمن» بدلًا من المرور بصمت.

```bash
# نموذج بداية ونهاية آمنتين:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... منطقك ...
exit 0   # أو || exit 0 في سطر واحد
```

### المشكلة 2. تُقتل hooks البطيئة بسبب المهلة

كان لدي hook يشغّل `eslint --fix` على الملف كله بعد Edit. كانت الملفات الكبيرة (<bdi>1000+</bdi> سطر) تستغرق 8–10 ثوانٍ للمعالجة. مهلة hook الافتراضية في Claude Code هي 60 ثانية، فظننت أنني ضمن الحد. لكن عندما تحدث عدة تعديلات خلال دقيقة واحدة، تتراكم hooks وتنتظر في طابور، ويُتخطى بعضها ببساطة. لم أدرك ذلك إلا عندما أظهر السجل “hook timed out”.

**الحل:** إذا كان الـhook يتطلب عملًا ثقيلًا، فشغّله في الخلفية. استخدم `eslint --fix "$FILE" &` من دون انتظار النتيجة. عندئذ يعيد الـhook exit 0 خلال أجزاء من الثانية بينما يجري العمل الفعلي في الخلفية.

```bash
# عملية ثقيلة في الخلفية:
npx eslint --fix "$FILE" &
exit 0
```

### المشكلة 3. لا يستطيع hook رؤية متغيرات بيئة المشروع

ظننت أن `process.env` سيكون متاحًا في hook كما هو الحال في تطبيق Node.js. لكنه ليس كذلك. يعمل الـhook في shell أنشأته العملية الأم لـClaude Code، ولا يرى إلا ما في `~/.zshrc` أو `~/.bashrc`، إلى جانب ما يمرره Claude Code صراحةً. إذا اعتمد الـhook على `OPENAI_API_KEY` من `.env`، فسيتعين عليك قراءة `.env` يدويًا.

**الحل:** اقرأ `.env` صراحةً في أمر hook.

```bash
# تمرير بيئة المشروع إلى hook:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# أو باستخدام dotenv:
dotenv -e .env -- node my-validator.js
```

تكون هذه المشكلة مزعجة للغاية في monorepo، حيث تملك الحزم المختلفة ملفات `.env` خاصة بها. عالجها بتمرير `PWD` وتشغيل `cd "$PWD"` صراحةً في بداية الأمر.

## كيف تختبر hooks قبل إجراء commit؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

الـhook أمر shell. يمكنك تشغيله يدويًا في terminal باستخدام stdin وهمي، والتحقق من رمز الخروج وstderr، وفعل ذلك كله قبل أن يشغّله Claude Code. وهذا يوفر ساعات من تصحيح الأخطاء ويحميك من hooks تعرقل عملها بنفسها.

</div>

**سيناريو اختبار أساسي.** خذ أمر hook من JSON، وافتح terminal، ومرّر إليه JSON الذي سيرسله Claude Code:

```bash
# اختبار hook 1 (حماية .env):
echo '{"tool_input":{"file_path":".env"}}' | bash -c '
FILE=$(jq -r ".tool_input.file_path // empty")
[ -z "$FILE" ] && exit 0
echo "$FILE" | grep -qE "(^|/)\.env(\.|$)" && {
  echo "BLOCKED" >&2
  exit 2
} || exit 0
'
echo "Exit code: $?"
```

ينبغي أن ترى `BLOCKED` في stderr و`Exit code: 2`. إذا حصلت على exit 0، فهذا يعني أن regex لا يعمل؛ أصلح grep.

**سيناريوهات للتحقق منها:**

1.  **المسار الناجح** - ينبغي حظر الملف. استخدم `.env` مع hook 1، و`git push --force main` مع hook 3.
2.  **المسار السلبي** - ينبغي ألا يُحظر الملف. استخدم `README.md` مع hook 1، و`git push origin feature-branch` مع hook 3.
3.  **الحالات الحدّية** - إدخال فارغ، وقيم null، ومحارف غير متوقعة. ينبغي أن يعيد الـhook exit 0 أو يفشل بخطأ واضح، لا أن يمر بصمت.

**أين تضع hooks التجريبية.** ليس في `.claude/settings.json`، بل في `.claude/settings.local.json` (غير موجود في git). ضع hooks الجديدة هناك تحت `PostToolUse` واستخدم `echo` بدلًا من الإجراء الفعلي، وتحقق من أنها تعمل، ثم انقلها إلى `settings.json` الإنتاجي.

```json
// تجربة في settings.local.json - تكتب في /tmp/hook-debug.log:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[hook fired] $(date) $(jq -r '.tool_input.file_path // \"?\"')\" >> /tmp/hook-debug.log"
          }
        ]
      }
    ]
  }
}
```

بعد إجراء 5–10 تعديلات في Claude Code، افتح `/tmp/hook-debug.log` لترى الملفات التي أدت إلى تشغيل الـhook. إذا كان الملف فارغًا، فـmatcher غير مناسب؛ أصلح regex.

## ما الخطوة التالية؟

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="أهم النقاط">

أهم النقاط

ابدأ باستخدام hooks من نوع PostToolUse تنتهي بـexit 0؛ فهي الأكثر أمانًا ولا تحظر شيئًا. بعد أسبوع، أضف أول hook من نوع PreToolUse مع exit 2 لحماية `.env` أو منع force-push. وبعد أسبوعين، سيتقلص `CLAUDE.md` من عقد مؤلف من 200 سطر إلى وصف سياق موجز من 50–80 سطرًا، مع نقل فحوص أحداث مختارة إلى hooks وفرض حدود الوصول الصارمة مستقلًا بالأذونات والعزل والحماية البعيدة.

</div>

5 خطوات للأسبوع المقبل:

1.  **ابحث باستخدام Grep في `CLAUDE.md`.** ابحث عن كلمات مثل “never” و“forbidden” و“always” و“must”. كل قاعدة من هذا النوع مرشحة لتصبح hook. إذا كانت القاعدة أمرًا، فلن يفرضها الملف النصي.
2.  **ابدأ بـPostToolUse + exit 0.** هذه الخيارات الأكثر أمانًا: تُجري تغييرًا لكنها لا تحظر شيئًا. prettier (hook 2)، وإنهاء خادم التطوير (hook 5)، وإجراء commit تلقائي بعد الاختبارات (hook 6).
3.  **عندما تصبح واثقًا فقط، أضف أول PreToolUse + exit 2.** مثلًا، احمِ `.env` (hook 1) أو امنع force-push (hook 3). هذا حظر صارم، وقد يؤدي خطأ في أمر grep إلى حظرك أنت أيضًا.
4.  **اختبر hooks يدويًا قبل إجراء commit** (راجع القسم أعلاه). فهذا يوفر ساعات من تصحيح الأخطاء.
5.  **احتفظ بـ`settings.local.json` للتجارب.** ضع hooks الجديدة فيه واستخدم echo بدلًا من الإجراء الفعلي.

بعد إعداد hooks، تكون الخطوة التالية في هذه المجموعة هي [كيفية التراجع عن التغييرات في Claude Code](https://ailoot.ai/blog/ar/claude-code-undo-changes/)، تحسبًا لمرور إجراء خطير من hook. إذا أردت أن يتذكر Claude بنية مشروعك بين الجلسات، فأنشئ [Second Brain في Claude Code](https://ailoot.ai/blog/ar/claude-code-second-brain/) (مجلد `business/` + قاعدة معرفة مترابطة). ولمنح skills الخاصة بمشروعك أسماء قابلة لإعادة الاستخدام، راجع [Claude Code Skills](https://ailoot.ai/blog/ar/claude-code-skills-library/).

<div class="section" role="doc-bibliography" aria-label="المصادر">

### المصادر

- [مرجع Claude Code Hooks - وثائق Anthropic الرسمية](https://code.claude.com/docs/en/hooks)
- [وثائق ذاكرة Claude Code - عن CLAUDE.md وsystem prompt](https://code.claude.com/docs/en/memory)
- [أتمتة سير العمل باستخدام hooks - دليل Anthropic الرسمي مع أمثلة](https://code.claude.com/docs/en/hooks-guide)

</div>





إذا كنت بحاجة إلى الوصول إلى الشبكات العصبية عبر Ailoot، فألقِ نظرة على [Ailoot](https://ailoot.ai/).

## أسئلة شائعة

### لماذا يجب أن يقرأ hook إدخال JSON مرة واحدة فقط؟

stdin تدفق؛ يستهلكه أول jq فيترك القراءات اللاحقة عند EOF. احفظ الإدخال مرة ثم استخرج حقلي الأمر والنتيجة من القيمة المحفوظة. اعتبر النتيجة الغائبة أو المجهولة غير متحققة وتجاوز commit الآلي.

### هل تشكل hooks التعبير النمطي في المثال حدًا أمنيًا؟

لا. تفحص أحداثًا محددة وصيغ أوامر معروفة. قد تتجاوزها إعادة توجيه الصدفة والأسماء البديلة والنصوص وأدوات MCP أو مسار كتابة آخر؛ استخدم اعتمادًا مقيدًا وأذونات ملفات وعزلًا وفروعًا محمية للحدود الصارمة.


## Ailoot · الشركة ومحفظة مشاريعها

تستند Ailoot إلى خبرة تشغيلية متراكمة وقاعدة عملاء واسعة وخدمة مستقرة ومراقبة مستمرة لمؤشرات الأداء KPI.

الشركة ومحفظة مشاريعها: ملايين العملاء؛ مبيعات بالمليارات شهريًا.

توظف Ailoot هذه الخبرة التشغيلية لإتاحة الوصول عبر API إلى قدرات الذكاء الاصطناعي في منتجات العملاء ووكلائهم وتطبيقاتهم وعمليات الأتمتة.
