# Hooks ב-Claude Code 2026: הגדרה שלב אחר שלב ו-7 דוגמאות מוכנות לשימוש

> 7 hooks מוכנים לשימוש עבור .claude/settings.json, מוכנים להעתקה ולהדבקה: הגנה על .env, prettier, force-push אל main, em dashes ושרת פיתוח. 4 סוגי handlers ו-31 אירועים.

Source: https://ailoot.ai/blog/he/claude-code-hooks/
Canonical: https://ailoot.ai/blog/he/claude-code-hooks/
Language: he
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-121
Revision: sha256-16cfd155c9e5093d84c3d9d22293c81844c5fd31b69ec8bd9e2ced22e841005d


Hooks ב-Claude Code הם פקודות shell, קריאות HTTP או בדיקות LLM קצרות שרצות אוטומטית לפני פעולה של Claude או אחריה. הן מוסיפות בדיקות לאירועי כלים מסוימים, מעצבות קוד אחרי עריכה ומזריקות הקשר עדכני בתחילת סשן. hook תואם של PreToolUse יכול לדחות את הקריאה הזאת; דוגמאות ה-regex הן אמצעי הגנה נוחים, ולא גבול אבטחה בכל נתיב גישה לקבצים או ל-Git. בהמשך מפורטים 4 סוגי handlers, 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="Key takeaways">

הנקודה המרכזית

Hook הוא פקודת shell, קריאת HTTP, הנחיית LLM קצרה או subagent ש-Claude Code מריץ בתגובה לאירוע ספציפי במחזור החיים. בשונה מהוראות ב-`CLAUDE.md`, שהמודל עשוי להתעלם מהן או לשכוח אותן, hook רץ באותו אופן בכל פעם: הוא קוד, לא הצעה.

</div>

במשך חצי שנה השארתי את הכלל ״לעולם לא לערוך את `.env` ישירות״ ב-`CLAUDE.md`. Claude הפר אותו פעם בחודש. לא בזדון; הוא פשוט שכח בסוף סשן ארוך. פעם אחת התווסף טוקן נוסף ל-`.env`; בפעם אחרת משתנה עם שם כפול דרס את המשתנה שהיה בשימוש.

העברתי את הכלל ל-hook, וכך נעשה בלתי אפשרי לשכוח אותו. כאשר כלי עריכה ישיר תואם מספק נתיב של `.env`, ה-hook המוגדר דוחה את הקריאה הזאת. הפניה מחדש ב-Bash, סקריפטים, כלי MCP ונתיבי גישה אחרים דורשים בקרות נפרדות; הדוגמה אינה הופכת את הקובץ לבלתי ניתן לשינוי.

<figure>
<blockquote>
<p>Hooks הם פקודות shell, נקודות קצה של HTTP או הנחיות LLM שאתם מגדירים, והן רצות אוטומטית בנקודות מסוימות במחזור החיים של Claude Code.</p>
</blockquote>
<figcaption>— Anthropic, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

במילים אחרות, hook הוא פקודה שלכם ש-Claude Code מריץ באירוע מסוים. כל עריכת קובץ, הפעלה של Bash, התחלת סשן או השלמה של subagent הם אירועים שאפשר להצמיד אליהם 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="Key takeaways">

הנקודה המרכזית

`CLAUDE.md` הוא טקסט שהמודל קורא בתחילת סשן ומנסה לפעול לפיו. Hook הוא פקודת shell שתהליך Claude Code מריץ בתגובה לאירוע, בלי תלות במודל. Skill הוא חבילת הוראות בעלת שם, ש-Claude יכול להפעיל באמצעות כלי Skill, כמו פונקציה. כל השלושה מיועדים לסוגים שונים של משימות.

</div>

התיעוד של Anthropic מסביר במפורש את ההבדל בין `CLAUDE.md` ל-hooks:

<figure>
<blockquote>
<p>התוכן של CLAUDE.md נמסר כהודעת משתמש אחרי הנחיית המערכת, ולא כחלק מהנחיית המערכת. 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` ואחרים. ה-hook פועל באירוע שהוגדר: PreToolUse תואם יכול לדחות קריאה באמצעות יציאה 2; PostToolUse פועל אחרי הפעולה ואינו מבטל אותה. שינוי קובץ אחרי עריכה הוא פעולה נפרדת.

השוואה:

| מאפיין | CLAUDE.md | Hook | Skill |
|----|----|----|----|
| היכן הוא נמצא | `CLAUDE.md` בשורש הפרויקט | תיקיית `.claude/settings.json` | תיקיית `.claude/skills/` |
| מי מריץ אותו | המודל קורא אותו כחלק מהודעת משתמש | תהליך Claude Code בעת אירוע | המודל באמצעות כלי Skill |
| ערובה להרצה | לא (מאמץ מיטבי) | hook פקודה שהוגדר ותואם לאירוע מריץ קוד; הכשלים והתנהגות קוד היציאה תלויים באירוע | באמצעות קריאת מודל (עם הרשאה, אך לא ברמת shell) |
| למה הוא מיועד | הקשר לפרויקט, טון, סגנון, ניתוב | הגבלות, עיצוב, בדיקות על קבצים מסוימים | נהלים מורכבים מרובי שלבים עם פרמטרים |
| מתי הוא רץ | בכל הודעת מודל בסשן | באירוע מחזור חיים | כשהמודל מפעיל אותו |
| האם הוא יכול לחסום פעולה? | לא | ב-PreToolUse תואם: יציאה 2; ב-PostToolUse: משוב בלבד | לא |

התבנית המלאה של `CLAUDE.md` ו-6 כללים לקובץ שמתפתח עם הפרויקט מופיעים במדריך [איך להגדיר CLAUDE.md ב-2026: תבנית מוכנה לשימוש ו-6 כללים](https://ailoot.ai/blog/he/claude-md-template/). למדו על skills כיכולות בעלות שם במדריך [Claude Code Skills: איך לבנות ספרייה משלכם](https://ailoot.ai/blog/he/claude-code-skills-library/).

Hook הוא הרמה הנמוכה ביותר: בלי מודל, בלי פרשנות, רק shell.

Hooks הם אחד הכלים של [הנדסת הקשר](https://ailoot.ai/blog/he/context-engineering/), לצד Plan Mode ו-<bdi>/compact</bdi>.



## 4 סוגי hooks: Command, HTTP, Prompt, Agent — במה לבחור?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

הנקודה המרכזית

ל-Claude Code יש 4 סוגי handlers. Command הוא סקריפט shell, הסוג הנפוץ ביותר. HTTP שולח בקשת POST לכתובת ה-URL שלכם, עבור צוותים ואינטגרציות. Prompt מפעיל קריאת Haiku קצרה לבדיקה סמנטית. Agent יוצר subagent עם גישה לכלים לצורך אימות מעמיק. מתחילים זקוקים ל-Command ול-Prompt; הם מכסים 90% מהמשימות.

</div>

| סוג | מה הוא עושה | מתי לבחור בו | מורכבות |
|----|----|----|----|
| **Command** | מריץ פקודת shell, מקבל JSON דרך stdin ומחזיר קוד יציאה ו-stdout | 90% מהמקרים: בדיקת נתיבים, עיצוב, grep בתוכן, lsof, פעולות git | נמוכה |
| **HTTP** | שולח בקשת POST לכתובת ה-URL שלכם עם גוף JSON ומקבל JSON בתגובה | כשהכלל שייך לצוות<bdi>/CI</bdi>: אימות מרכזי, לוח מצב משותף, ביקורת מרחוק | בינונית (נדרש שרת) |
| **Prompt** | שולח הנחיה קצרה ל-Haiku (כברירת מחדל) ומקבל כן/לא להערכה סמנטית | כש-grep אינו מספיק: ״בדקו אם הטקסט נשמע תאגידי״ | נמוכה |
| **Agent** | יוצר subagent עם גישה ל-Read/Grep/Glob לצורך בדיקות מרובות שלבים | אימות כבד: ״מצאו את כל הקבצים שהשתנו ובדקו שקיימות בדיקות מתאימות״ | גבוהה |

הטעות הנפוצה ביותר היא לנסות לעשות באמצעות Command משהו שמצריך Prompt. למשל, ״אל תשתמשו במקפים ארוכים בקובצי `.md`״ היא משימת grep, ולכן Command מתאים לה. אבל ״אל תכתבו בטון תאגידי״ היא משימה סמנטית, ולכן היא דורשת Prompt.

אם רק התחלתם, השתמשו ב-Command. כל 7 ה-hooks המוכנים לשימוש בהמשך הם מסוג Command. HTTP/Prompt/Agent יגיעו בהמשך, כשתהיו מוכנים.

## היכן נמצא settings.json, ומהם 4 רמות ההגדרה?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

הנקודה המרכזית

Claude Code מחפש הגדרות ב-4 מקומות: ארגוני (נפרס באמצעות MDM), משתמש (`~/.claude/settings.json`), פרויקט (`.claude/settings.json` בשורש המאגר) ומקומי (`.claude/settings.local.json`, שאינו ב-git). כל 4 הקבצים מתמזגים בתחילת הסשן, כאשר הרמה הנמוכה יותר גוברת.

</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`) |

אני שומר את 7 ה-hooks שמתוארים בהמשך ב-`.claude/settings.json` של **הפרויקט**, כך שהם נכללים אוטומטית כשמשכפלים את המאגר ו-Claude פועל לפי אותם כללים בחלון חדש. Hooks אישיים לניסויים נשמרים ב-`.claude/settings.local.json`, כדי שלא יפריעו לצוות.

כל 4 הקבצים מתמזגים בתחילת הסשן. אם יש סתירה בין כללים, הרמה הנמוכה יותר גוברת (מקומית על פני פרויקט, פרויקט על פני משתמש).

**הדרך המהירה ביותר ליצור את ה-hook הראשון** היא באמצעות התפריט האינטראקטיבי `/hooks` בתוך Claude Code. בחרו אירוע מהרשימה, בחרו matcher (למשל, `Bash` או `Edit`) והדביקו פקודה. Claude Code יכתוב עבורכם JSON תקין אל `settings.json`.

## 31 אירועי מחזור חיים: לאילו מהם מתחילים זקוקים?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

הנקודה המרכזית

ב-Claude Code יש כרגע 31 אירועי מחזור חיים, המחולקים ל-3 תדירויות: פעם אחת לסשן (`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="Key takeaways">

הנקודה המרכזית

אם כלל עוסק בפעולה (ולא בהקשר), נוגע לבטיחות/כסף/נתונים, אפשר לבדוק אותו באמצעות 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 coders, לא developers; כתבו בלי סלנג.״** פעולה? יותר הקשר. בטיחות? לא. אפשר לבדוק באמצעות regex? קשה מדי. קשה לתקן? לא. ראיתם הפרה? כן. **1 מתוך 5: השאירו אותו ב-`CLAUDE.md`.**

חמישה מהכללים שלי עברו את המבחן. העברתי אותם ל-hooks. 75% הנותרים נשארו בקובץ הטקסט.

## 7 hooks מוכנים להעתקה

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

הנקודה המרכזית

להלן 7 hooks מוכנים לשימוש עבור `.claude/settings.json`: חסימת עריכות `.env`, הרצת prettier אוטומטית, מניעת force-push אל main, בדיקת מקפים ארוכים, פינוי פורט לפני `npm run dev`, ביצוע commit אוטומטי אחרי בדיקות מוצלחות והזרקת הקשר עדכני בתחילת סשן. העתיקו כל אחד לקובץ, הפעילו מחדש את Claude Code והוא יעבוד.

</div>

כל 7 ה-hooks נשמרים בקובץ `.claude/settings.json` אחד בשורש הפרויקט. בכל רשומה יש סוג אירוע, מסנן כלי (matcher) ופקודת shell. קוד יציאה 2 דוחה קריאת PreToolUse תואמת לפני הביצוע. ב-PostToolUse הפעולה כבר התרחשה: ה-hook מדווח משוב ואינו יכול לבטל אותה.

<figure>
<blockquote>
<p>ברוב אירועי ה-hook, רק קוד יציאה 2 חוסם פעולה. Claude Code מתייחס לקוד יציאה 1 כשגיאה שאינה חוסמת וממשיך בפעולה, אף שב-Unix נהוג להשתמש ב-1 לציון כישלון.</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 'Do not edit `.env` directly. Tell me which variable to add, and I’ll open the file myself.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**מה הוא עושה:** לפני כל `Edit`/`Write`/`MultiEdit`, הוא מקבל את נתיב הקובץ, בודק באמצעות grep אם הוא תואם לתבנית `.env`, ואם כן מחזיר יציאה 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 ל-hook 2 מהותי: PreToolUse + יציאה 2 היא חסימה מוחלטת **לפני** הפעולה. PostToolUse + יציאה 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 to main is forbidden. Create a new branch or ask me to do it manually.' >&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 $FILE contains an em dash (U+2014). Replace it with a regular hyphen (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**מה הוא עושה:** PostToolUse עם יציאה 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 'Port 3000 freed before launch' >&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` בקבצים המדויקים שבבעלות המשימה. שינויים שכבר נמצאים ב-staging עוצרים את המתכון כדי לא לכלול עבודה לא קשורה. הוספה שנכשלה עוצרת אותו, diff ריק אינו יוצר commit, ובדיקות commit רגילות נשארות פעילות. בדקו קלטי הצלחה, כישלון ותוצאה חסרה באופן לא מקוון לפני ההפעלה.

אנטי-תבנית: הוספת `git commit --no-verify` לכל Edit. השאירו את בדיקות ה-commit פעילות. אם בדיקה מפעילה שוב את האוטומציה באופן רקורסיבי, צמצמו את הטריגר או שנו את הבדיקה; אל תעקפו אימות כברירת מחדל. בדקו את ה-diff שב-staging ואת בעלות המשימה לפני הפעלת 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 הנוכחי ואת רשימת השינויים שלא בוצע להם commit, ואז מחזיר JSON עם שדה `additionalContext`. Claude Code מזריק את הטקסט הזה להודעת המערכת הראשונה, כך ש-Claude יודע מההתחלה היכן הוא נמצא ומה נותר מהסשן הקודם.

<figure>
<blockquote>
<p>השתמשו ב-additionalContext למידע ש-Claude צריך לדעת על מצב הסביבה או על פעולה שהושלמה זה עתה: מצב הסביבה (הענף הנוכחי, יעד פריסה, דגלי תכונות פעילים), כללי פרויקט מותנים (איזו פקודת בדיקה חלה על הקובץ שנערך זה עתה), נתונים חיצוניים (בעיות פתוחות שהוקצו לכם, תוצאות CI אחרונות).</p>
</blockquote>
<figcaption>— Anthropic, הפניה ל-hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

אפשרויות להרחבת ה-hook הזה: להביא את ה-commit האחרון ואת התיאור שלו, את מצב GitHub Actions, את הספרינט הפעיל ב-Linear/Jira ובעיות פתוחות במאגר הנוכחי.

## 3 מלכודות hooks שכמעט כל אחד נתקל בהן

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

הנקודה המרכזית

המלכודת הראשונה: hook עם יציאה 1 עובר בשקט, ו-Claude מתעלם מהשגיאה. השנייה: עבודה כבדה ב-hook נהרגת אחרי timeout של 60 שניות. השלישית: hook רואה רק את משתני הסביבה מ-`~/.zshrc`, ולא את `.env` של הפרויקט. כל אחת נפתרת בשורה אחת, אבל בלי להכיר את המלכודות האלה אפשר לחשוב ש״hooks לא עובדים״.

</div>

### מלכודת 1. Hook עם יציאה 1 עובר בשקט

המלכודת המסוכנת ביותר. במשך שבוע חשבתי שמנגנון ההגנה על `.env` עובד, אבל התברר שב-hook השתמשתי ב-`jq -r '.tool_input.file_path'` בלי לבדוק ערך null. כש-`tool_input` היה ריק, jq יצא עם קוד 1. Claude התייחס ליציאה 1 כאל ״שגיאת hook, ממשיכים״, ולכן הפעולה בוצעה. לא הבנתי למה עדיין נגעו ב-`.env`. הבנתי את הסיבה רק כשהרצתי את ה-hook ידנית בטרמינל עם קלט ריק.

**הפתרון הוא כלל אחד:** התחילו כל hook בבדיקה לערך null, וסיימו אותו ב-`|| exit 0`. כל יציאה בלתי צפויה תוביל אז ליציאה ״בטוחה״ במקום לעבור בשקט.

```bash
# Safe start and end template:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... your logic ...
exit 0   # or || exit 0 on one line
```

### מלכודת 2. Hooks איטיים נהרגים בגלל timeout

היה לי hook שהריץ `eslint --fix` על כל הקובץ אחרי Edit. עיבוד של קבצים גדולים (<bdi>1000+</bdi> שורות) ארך 8–10 שניות. ברירת המחדל של timeout ל-hook ב-Claude Code היא 60 שניות, ולכן חשבתי שאני בתוך המגבלה. אבל כשמתבצעות כמה פעולות Edit בתוך דקה, ה-hooks מצטברים ונכנסים לתור, וחלקם פשוט מדולגים. הבנתי זאת רק כשראיתי בלוג ״hook timed out״.

**פתרון:** אם hook דורש עבודה כבדה, הריצו אותה ברקע. `eslint --fix "$FILE" &` בלי להמתין לתוצאה. כך ה-hook מחזיר יציאה 0 בתוך אלפיות שנייה, בזמן שהעבודה עצמה מתבצעת ברקע.

```bash
# Heavy operation in background:
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
# Passing project env into hook:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# Or with dotenv:
dotenv -e .env -- node my-validator.js
```

זה כואב במיוחד במונורפו, שבו לכל חבילה יש קובצי `.env` משלה. פתרו זאת באמצעות העברה של `PWD` והרצה מפורשת של `cd "$PWD"` בתחילת הפקודה.

## איך בודקים hooks לפני ביצוע commit?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

הנקודה המרכזית

Hook הוא פקודת shell. אפשר להריץ אותו ידנית בטרמינל עם stdin מזויף, לבדוק את קוד היציאה ואת stderr, ולעשות את כל זה לפני ש-Claude Code מריץ אותו. כך חוסכים שעות של ניפוי שגיאות ומתגוננים מפני hooks שחוסמים את הפעולה של עצמם.

</div>

**תרחיש בדיקה בסיסי.** קחו את פקודת ה-hook מתוך ה-JSON, פתחו טרמינל והעבירו אליו את ה-JSON ש-Claude Code היה שולח:

```bash
# Test hook 1 (protect .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`. אם מתקבל 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 אמור להחזיר יציאה 0 או להיכשל עם שגיאה ברורה, ולא לעבור בשקט.

**היכן לשים hooks ניסיוניים.** לא ב-`.claude/settings.json`, אלא ב-`.claude/settings.local.json` (שאינו ב-git). שימו שם hooks חדשים תחת `PostToolUse` עם `echo` במקום הפעולה האמיתית, בדקו אם הם מופעלים ואז העבירו אותם אל `settings.json` של סביבת הייצור.

```json
// Experiment in settings.local.json - writes to /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="Key takeaways">

הנקודה המרכזית

התחילו ב-hooks של PostToolUse שיוצאים עם 0; הם הבטוחים ביותר ואינם חוסמים דבר. אחרי שבוע, הוסיפו hook ראשון מסוג PreToolUse עם יציאה 2 כדי להגן על `.env` או למנוע force-push. אחרי שבועיים, `CLAUDE.md` יצטמצם מחוזה בן 200 שורות לתיאור הקשר קצר בן 50–80 שורות, ובדיקות אירועים נבחרות יעברו ל-hooks, בעוד שמגבלות גישה קשיחות ייאכפו בנפרד באמצעות הרשאות, sandbox והגנות בשרת המרוחק.

</div>

5 צעדים לשבוע הקרוב:

1.  **חפשו ב-`CLAUDE.md`** מילים כמו ״לעולם לא״, ״אסור״, ״תמיד״ ו״חייבים״. כל כלל כזה מועמד ל-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.  **שמרו hooks ניסיוניים ב-`settings.local.json`.** שימו בהם `echo` במקום הפעולה האמיתית.

לאחר שתגדירו את ה-hooks, השלב הבא באשכול הוא [איך לבטל שינויים ב-Claude Code](https://ailoot.ai/blog/he/claude-code-undo-changes/), למקרה ש-hook יאפשר לפעולה מסוכנת לעבור. אם תרצו ש-Claude יזכור את מבנה הפרויקט בין סשנים, בנו [מוח שני ב-Claude Code](https://ailoot.ai/blog/he/claude-code-second-brain/) (תיקיית `business/` + מאגר ידע מקושר). וכדי לתת שמות ל-skills של הפרויקט ולהשתמש בהם שוב, ראו [Claude Code Skills](https://ailoot.ai/blog/he/claude-code-skills-library/).

<div class="section" role="doc-bibliography" aria-label="Sources">

### מקורות

- [Claude Code Hooks reference - תיעוד רשמי של Anthropic](https://code.claude.com/docs/en/hooks)
- [תיעוד הזיכרון של Claude Code - על CLAUDE.md והנחיית המערכת](https://code.claude.com/docs/en/memory)
- [אוטומציה של תהליכי עבודה באמצעות hooks - מדריך רשמי של Anthropic עם דוגמאות](https://code.claude.com/docs/en/hooks-guide)

</div>





אם אתם זקוקים לגישה לרשתות עצביות דרך Ailoot, בקרו ב-[Ailoot](https://ailoot.ai/).

## שאלות ותשובות: Hooks ב-Claude Code 2026: הגדרה שלב אחר שלב ו-7 דוגמאות מוכנות לשימוש

### למה hook חייב לקרוא את קלט ה-JSON רק פעם אחת?

stdin הוא זרם: תהליך jq הראשון צורך אותו, ולקריאות הבאות נשאר EOF. שמרו את הקלט פעם אחת וחלצו ממנו את שדות הפקודה והתוצאה. תוצאה חסרה או לא ידועה אינה מאומתת, ולכן יש לדלג על commit אוטומטי.

### האם דוגמאות ה-regex של hooks יוצרות גבול אבטחה?

לא. הן בודקות אירועים מסוימים ומחרוזות פקודה מזוהות. הפניה מחדש במעטפת, כינויים, סקריפטים, כלי MCP או נתיב כתיבה אחר עשויים לעקוף אותן; השתמשו בפרטי גישה מוגבלים, בהרשאות מערכת קבצים, ב-sandbox ובענפים מוגנים למגבלות קשיחות.


## Ailoot · החברה ופורטפוליו הפרויקטים שלה

Ailoot נשענת על ניסיון תפעולי מצטבר, בסיס לקוחות רחב, שירות יציב ובקרה שוטפת של KPI.

החברה ופורטפוליו הפרויקטים שלה: מיליוני לקוחות; מכירות בהיקף של מיליארדים מדי חודש.

Ailoot משתמשת בניסיון התפעולי הזה כדי לספק גישת API ליכולות AI במוצרי לקוחות, בסוכנים, באפליקציות ובאוטומציות.
