מדריך קלוד קוד בעברית

תיעוד 161

מדריך עזר ל-Hooks

מדריך עזר לאירועי hook של Claude Code, לסכמת ההגדרות, לפורמטים של קלט ופלט JSON, לקודי יציאה, ל-hooks אסינכרוניים, ל-hooks של HTTP, ל-hooks של prompt, ול-hooks של כלי MCP.

[!TIP]

למדריך התחלה מהירה עם דוגמאות, ראה אוטומציה של פעולות עם hooks.

Hooks הם פקודות מעטפת, נקודות קצה של HTTP, קריאות לכלי MCP, prompts של LLM, או סוכני משנה שהמשתמש מגדיר, והם רצים אוטומטית בנקודות מסוימות במחזור החיים של Claude Code. Claude Code מפעיל את אותם אירועי hook בכל מקום שבו הוא רץ: סשנים בטרמינל, הרחבות IDE, אפליקציית Desktop, וסשנים בענן. השתמש במדריך העזר הזה כדי לחפש סכמות של אירועים, אפשרויות הגדרה, פורמטים של קלט ופלט JSON, ותכונות מתקדמות כמו hooks אסינכרוניים, hooks של HTTP, ו-hooks של כלי MCP.

תוסף יכול גם לרשום hooks כפונקציות JavaScript ש-Claude Code קורא להן בתהליך שלו, והן יכולות גם לצייר בממשק וגם לפעול על אירועים. תוסף שעושה זאת הוא mod, וה-hooks האלה כפונקציות מכוסים בתגובה לאירועים ולא כאן. ה-hooks בדף הזה ממשיכים לעבוד לצד mods.

#מחזור החיים של hook

Claude Code מריץ hooks בנקודות מסוימות במהלך סשן. כשאירוע מופעל ו-matcher תואם, Claude Code מעביר למטפל ה-hook הקשר JSON על האירוע. ב-hooks של פקודה, הקלט מגיע ב-stdin. ב-hooks של HTTP, הוא מגיע כגוף בקשת POST. המטפל יכול לבדוק את הקלט, לפעול, ואם רוצים גם להחזיר החלטה.

האירועים מתחלקים לשלושה קצבים:

  • לכל סשן: SessionStart ו-SessionEnd
  • לכל תור: UserPromptSubmit, Stop, ו-StopFailure
  • בכל קריאת כלי בתוך הלולאה הסוכנית: PreToolUse ו-PostToolUse, למעט קריאות EndConversation, שמדלגות על שניהם

תרשים מחזור חיים של hooks שמראה Setup אופציונלי שנכנס ל-SessionStart, ואז לולאה לכל תור שמכילה UserPromptSubmit, UserPromptExpansion לפקודות לוכסן, הלולאה הסוכנית המקוננת (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), ו-Stop או StopFailure, ואחריהם TeammateIdle, PreCompact, PostCompact, ו-SessionEnd, עם Elicitation ו-ElicitationResult מקוננים בתוך הרצת כלי MCP, PermissionDenied כהסתעפות צד מ-PermissionRequest עבור דחיות במצב אוטומטי, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, ו-DirectoryAdded כאירועים אסינכרוניים עצמאיים, PreModelSwitch כאירוע סדרתי עצמאי שרץ לפני החלפת מודל שהתבקשה, PostModelSwitch כאירוע אסינכרוני עצמאי שרץ אחרי שהמודל של הסשן משתנה, ו-MessageDisplay כאירוע לתצוגה בלבד שרץ בזמן שהטקסט של הודעת העוזר מוזרם

הטבלה הבאה מסכמת מתי כל אירוע מופעל. הסעיף אירועי hook מתעד את סכמת הקלט המלאה ואת אפשרויות בקרת ההחלטה של כל אחד.

אירועמתי הוא מופעל
SessionStartכשסשן מתחיל או ממשיך
Setupכשמתחילים את Claude Code עם --init-only, או עם --init או --maintenance במצב -p. להכנה חד פעמית ב-CI או בסקריפטים
UserPromptSubmitכש-prompt נשלח, לפני ש-Claude מעבד אותו. מופעל גם בתורות ש-Claude Code מתחיל בעצמו
UserPromptExpansionכשפקודה שהמשתמש הקליד מתרחבת ל-prompt, לפני שהיא מגיעה ל-Claude. יכול לחסום את ההרחבה
PreToolUseלפני שקריאת כלי רצה. יכול לחסום אותה
PermissionRequestכשקריאת כלי צריכה החלטת הרשאה
PermissionDeniedכשמצב אוטומטי דוחה קריאת כלי, כולל דחיות בלי פסק דין של מסווג. השתמש ב-JSON hookSpecificOutput.retry: true כדי לומר למודל שהוא רשאי לנסות שוב את קריאת הכלי שנדחתה. Claude Code מתעלם מ-retry כשהמסווג לא הפיק פסק דין
PostToolUseאחרי שקריאת כלי מצליחה
PostToolUseFailureאחרי שקריאת כלי נכשלת
PostToolBatchאחרי שכל אצווה של קריאות כלי מקבילות מסתיימת, לפני קריאת המודל הבאה
Notificationכש-Claude Code שולח התראה
MessageDisplayבזמן שטקסט הודעת העוזר מוצג
SubagentStartכשסוכן משנה נוצר
SubagentStopכשסוכן משנה מסיים
TaskCreatedכשמשימה נוצרת דרך TaskCreate
TaskCompletedכשמשימה מסומנת כהושלמה
Stopכש-Claude מסיים להגיב
StopFailureכשהתור נגמר בגלל שגיאת API
TeammateIdleכשחבר בצוות סוכנים עומד לעבור למצב idle
InstructionsLoadedכשקובץ CLAUDE.md או .claude/rules/*.md נטען להקשר. מופעל בתחילת סשן וכשקבצים נטענים בעצלתיים במהלך סשן
ConfigChangeכשקובץ הגדרות משתנה במהלך סשן
CwdChangedכשספריית העבודה משתנה, לדוגמה כש-Claude מריץ פקודת cd. שימושי לניהול סביבה תגובתי עם כלים כמו direnv
DirectoryAddedכשספריית עבודה מתווספת באמצע סשן דרך /add-dir או בקשת הבקרה register_repo_root של ה-SDK
FileChangedכשקובץ במעקב משתנה בדיסק. השדה matcher מציין אילו שמות קבצים לעקוב
WorktreeCreateכש-worktree נוצר דרך --worktree, isolation: "worktree", או עבור סשן ברקע. מחליף את התנהגות ברירת המחדל של git
WorktreeRemoveכש-worktree ש-hook של WorktreeCreate יצר מוסר
PreCompactלפני דחיסת הקשר
PostCompactאחרי שדחיסת הקשר מסתיימת
PreModelSwitchלפני ש-Claude Code מיישם החלפת מודל שאתה או לקוח ביקשתם. יכול לחסום את ההחלפה
PostModelSwitchאחרי שהמודל של הסשן משתנה, כולל שינויים ש-Claude Code עושה בעצמו, כמו שחזור המודל כשממשיכים סשן
Elicitationכששרת MCP מבקש קלט משתמש במהלך קריאת כלי
ElicitationResultאחרי שמשתמש מגיב ל-elicitation של MCP, לפני שהתשובה נשלחת חזרה לשרת
SessionEndכשסשן מסתיים

#איך hook מוכרע

כדי לראות איך האירוע, ה-matcher והמטפל משתלבים, הבא בחשבון את ה-hook הזה של PreToolUse, שחוסם פקודות מעטפת הרסניות.

#macOS/Linux

ה-matcher מצמצם לקריאות לכלי Bash, ותנאי ה-if מצמצם עוד לפקודות משנה של Bash שתואמות ל-rm *, כך ש-block-rm.sh נוצר רק כששני המסננים תואמים:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

הסקריפט קורא את קלט ה-JSON מ-stdin, מחלץ את הפקודה, ומחזיר permissionDecision של "deny" אם היא מכילה rm -rf. שמור אותו ב-.claude/hooks/block-rm.sh בפרויקט, והפוך אותו לבר הרצה עם chmod +x .claude/hooks/block-rm.sh כדי ש-Claude Code יוכל להריץ אותו:

#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0  # no decision; normal permission flow applies
fi

הסקריפט הזה, כמו שאר דוגמאות Bash בדף הזה שמפענחות קלט JSON, משתמש ב-jq, אז התקן את jq וודא שהוא ב-PATH לפני שמנסים אותן.

#Windows (PowerShell)

ה-matcher Bash|PowerShell מכסה את כלי PowerShell וגם את Bash. כלל if יחיד תואם רק לקריאות של כלי אחד, ולכן לכל כלי יש מטפל משלו: הראשון מצמצם לפקודות משנה של Bash שתואמות ל-rm *, והשני לפקודות PowerShell שתואמות ל-Remove-Item *. שניהם מריצים את אותו סקריפט דרך powershell.exe:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
            ]
          },
          {
            "type": "command",
            "if": "PowerShell(Remove-Item *)",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
            ]
          }
        ]
      }
    ]
  }
}

הדגל -NoProfile מדלג על טעינת פרופיל PowerShell כדי שה-hook יתחיל מהר, ו--ExecutionPolicy Bypass מאפשר ל-PowerShell להריץ את קובץ הסקריפט המקומי.

הסקריפט קורא את קלט ה-JSON מ-stdin, מחלץ את הפקודה, ומחזיר permissionDecision של "deny" אם היא מכילה rm -rf או Remove-Item ואחריו -Recurse. שמור אותו ב-.claude/hooks/block-rm.ps1 בפרויקט:

# .claude/hooks/block-rm.ps1
$callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
$command = $callInput.tool_input.command

if ($command -match 'rm -rf|Remove-Item.*-Recurse') {
  @{
    hookSpecificOutput = @{
      hookEventName = "PreToolUse"
      permissionDecision = "deny"
      permissionDecisionReason = "Destructive command blocked by hook"
    }
  } | ConvertTo-Json
} else {
  exit 0  # no decision; normal permission flow applies
}

עכשיו נניח ש-Claude Code מחליט להריץ Bash "rm -rf /tmp/build" מול הגדרת macOS/Linux. זה מה שקורה:

תרשים של הכרעת hook: PreToolUse מופעל, ה-matcher בודק התאמה ל-Bash, ואז תנאי ה-if בודק התאמה ל-Bash(rm *). אם שניהם תואמים, פקודת ה-hook רצה ומחזירה permissionDecision deny, ולכן קריאת הכלי נחסמת ו-Claude Code ממשיך. אם אחת הבדיקות לא תואמת, ה-hook מדולג וקריאת הכלי מורשית להמשיך.

1. האירוע מופעל

האירוע PreToolUse מופעל. Claude Code שולח את קלט הכלי כ-JSON ב-stdin אל ה-hook:

{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }

2. בדיקות ה-matcher

ה-matcher "Bash" תואם לשם הכלי, ולכן קבוצת ה-hook הזו מופעלת. אם משמיטים את ה-matcher או משתמשים ב-"*", הקבוצה מופעלת בכל מופע של האירוע.

3. בדיקות תנאי ה-if

תנאי ה-if "Bash(rm *)" תואם כי rm -rf /tmp/build היא פקודת משנה שתואמת ל-rm *, ולכן המטפל הזה נוצר. אם הפקודה הייתה npm test, בדיקת ה-if הייתה נכשלת ו-block-rm.sh לא היה רץ אף פעם, וכך נמנעת התקורה של יצירת תהליך. השדה if אופציונלי. בלעדיו, כל מטפל בקבוצה שתואמה רץ.

4. מטפל ה-hook רץ

הסקריפט בודק את הפקודה המלאה ומוצא rm -rf, ולכן הוא מדפיס החלטה ל-stdout:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook"
  }
}

אם הפקודה הייתה גרסת rm בטוחה יותר כמו rm file.txt, הסקריפט היה מגיע ל-exit 0 במקום זאת. קוד יציאה 0 בלי פלט אומר של-hook אין החלטה לדווח, ולכן קריאת הכלי ממשיכה דרך זרימת ההרשאות הרגילה. ה-hook יכול לדחות את הקריאה, אבל שתיקה לא מאשרת אותה.

5. Claude Code פועל לפי התוצאה

Claude Code קורא את החלטת ה-JSON, חוסם את קריאת הכלי, ומראה ל-Claude את הסיבה.

הסעיף הגדרות למטה מתעד את הסכמה המלאה, וכל סעיף אירוע hook מתעד איזה קלט הפקודה מקבלת ואיזה פלט היא יכולה להחזיר.

#הגדרות

Hooks מוגדרים בקובצי הגדרות JSON. להגדרה יש שלוש רמות קינון:

  1. בוחרים אירוע hook להגיב לו, כמו PreToolUse או Stop
  2. מוסיפים קבוצת matcher כדי לסנן מתי הוא מופעל, כמו "רק עבור כלי Bash"
  3. מגדירים מטפל hook אחד או יותר שירוצו כשיש התאמה

ראה איך hook מוכרע למעלה להסבר מלא עם דוגמה מוערת.

[!NOTE]

הדף הזה משתמש במונחים מסוימים לכל רמה: אירוע hook לנקודה במחזור החיים, קבוצת matcher למסנן, ומטפל hook לפקודת המעטפת, לנקודת הקצה של HTTP, לכלי MCP, ל-prompt, או לסוכן שרץ. "Hook" לבדו מתייחס לתכונה הכללית.

#מיקומי הוקים

המקום שבו מגדירים הוק קובע את ההיקף שלו:

מיקוםהיקףניתן לשיתוף
~/.claude/settings.jsonכל הפרויקטים שלךלא, מקומי למחשב שלך
.claude/settings.jsonפרויקט יחידכן, אפשר לבצע commit למאגר
.claude/settings.local.jsonפרויקט יחידלא, ב-gitignore כש-Claude Code שומר אליו הגדרה
הגדרות מדיניות מנוהלותברמת הארגוןכן, בשליטת מנהל
תוסף hooks/hooks.jsonכשהתוסף מופעלכן, ארוז עם התוסף
frontmatter של Skillשאר הסשן מרגע שה-skill הופעל. ראו הוקים ב-skills ובסוכניםכן, מוגדר בקובץ ה-skill
frontmatter של Subagentבזמן שאותו subagent רץכן, מוגדר בקובץ ה-subagent

סשנים בענן לא קוראים את ~/.claude/settings.json המקומי שלך. בסביבה באירוח עצמי, Claude Code גם מריץ את ההוקים שהמפעיל זרע מתוך ~/.claude/ של מארח ה-runner, והוא מריץ את ההוקים בקובץ ההגדרות המנוהלות של תמונת ה-runner כשהקובץ הזה נמצא בין המקורות המנוהלים ש-Claude Code מחיל, מה שבברירת המחדל אומר רק כשאף הגדרות מנוהלות שרת ואף מדיניות Claude Code שנמסרה ב-MDM לא מספקות את שכבת הניהול. ראו מה עובר מההגדרה שלך לגבי אילו קבצי הגדרות ותוספים, ולכן אילו הוקים, מגיעים לסשן ענן.

לפרטים על פתרון קבצי הגדרות, ראו הגדרות.

הוקים מקבצי הגדרות, מהגדרות מדיניות מנוהלות ומתוספים רצים גם בתוך subagents. כש-subagent קורא לכלי, אירועי כלים כמו PreToolUse ו-PostToolUse מפעילים את אותם הוקים מוגדרים כמו בשיחה הראשית, והקלט נושא את agent_id ו-agent_type שדות הקלט המשותפים שמזהים את ה-subagent.

מנהלים יכולים להשתמש ב-allowManagedHooksOnly בהגדרות מנוהלות כדי להגביל אילו הוקים רצים:

  • הוקים של המשתמש, הפרויקט, המקומי והתוסף שלך נחסמים. הוקים מתוספים שהופעלו בכפייה בהגדרות המנוהלות enabledPlugins פטורים
  • Claude Code גם מצמצם את ההגדרות statusLine, fileSuggestion ו-subagentStatusLine שלך להגדרות מנוהלות
  • Claude Code גם משבית תוספים עם מקור command, כולל תוספים שהופעלו בכפייה בהגדרות המנוהלות enabledPlugins, אלא אם disableCommandPluginSources מוגדר במפורש ל-false. מקורות command דורשים Claude Code מגרסה v2.1.229 ואילך
  • Claude Code גם חוסם פקודות headersHelper של marketplace אלא אם disableCommandPluginSources מוגדר במפורש ל-false, למעט marketplace שההגדרות המנוהלות עצמן מצהירות עליו

ראו מה רץ תחת allowManagedHooksOnly.

רשומות הוקים מתמזגות בין רמות ההגדרות במקום להחליף זו את זו: הגדרות משתמש, פרויקט ומקומיות מוסיפות את ההוקים שלהן בלי להסיר הוקים מנוהלים, וההגדרה disableAllHooks לא יכולה להשבית הוקים מנוהלים מחוץ להגדרות מנוהלות.

רשימות ההיתר של HTTP hook חלות על הוקים מכל מקור, כולל הגדרות מדיניות מנוהלות:

  • allowedHttpHookUrls: כשמוגדר בכל רמת הגדרות, Claude Code מריץ מטפל HTTP hook רק אם ה-URL שלו תואם לרשימת ההיתר הממוזגת
  • httpHookAllowedEnvVars: כשמוגדר, Claude Code מבצע אינטרפולציה רק של משתני הסביבה שברשימה הזו לתוך כותרות ה-hook

#תבניות matcher

שדה matcher מסנן מתי הוקים מופעלים. אופן הערכת matcher תלוי בתווים שהוא מכיל:

ערך matcherמוערך כדוגמה
"*", "", או מושמטהתאמה להכלמופעל בכל מופע של האירוע
רק אותיות, ספרות, _, -, רווחים, , ו-|מחרוזת מדויקת, או רשימת מחרוזות מדויקות מופרדות ב-| או , עם רווח אופציונלי מסביבBash תואם רק לכלי Bash; Edit|Write ו-Edit, Write כל אחד תואם לאחד משני הכלים במדויק; code-reviewer תואם רק לסוג הסוכן הזה
מכיל כל תו אחרביטוי רגולרי של JavaScript, בלי עיגון^Notebook תואם לכל כלי ששמו מתחיל ב-Notebook; mcp__memory__.* תואם לכל כלי משרת memory

matcher במסלול הביטוי הרגולרי נבדק עם RegExp.prototype.test של JavaScript, שמצליח על התאמה בכל מקום בערך. Edit.* תואם גם ל-Edit וגם ל-NotebookEdit. עטפו את התבנית ב-^ וב-$, כמו ב-^Edit$, כשצריך התאמה למחרוזת שלמה.

FileChanged ו-StopFailure משתמשים בקבוצת התאמה מדויקת צרה יותר של אותיות, ספרות, _ ו-| בלבד. מקף, רווח או פסיק ב-matcher של שני האירועים האלה משאירים אותו במסלול הביטוי הרגולרי, ורק | מפריד חלופות. כל אירוע אחר עם תמיכה ב-matcher בטבלה שאחרי זה מקבל | או ,.

אירוע FileChanged לא פועל לפי הכללים האלה כשהוא בונה את רשימת המעקב שלו. ראו FileChanged.

כל סוג אירוע מתאים על שדה אחר:

אירועמה ה-matcher מסנןערכי matcher לדוגמה
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDeniedשם הכליBash, Edit|Write, mcp__.*
SessionStartאיך הסשן התחילstartup, resume, clear, compact, fork
Setupאיזה דגל CLI הפעיל את ה-setupinit, maintenance
SessionEndלמה הסשן הסתייםclear, resume, logout, prompt_input_exit, other
Notificationסוג ההתראהpermission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_url_dialog, elicitation_complete, elicitation_response, agent_needs_input, agent_completed, quota_auto_resume_fired, quota_auto_resume_stale, quota_auto_resume_disabled, plugin_notification
SubagentStartסוג הסוכןgeneral-purpose, Explore, Plan, שמות סוכנים מותאמים, או שמות בהיקף תוסף כמו ^my-plugin:reviewer$
PreCompact, PostCompactמה הפעיל את ה-compactionmanual, auto
PreModelSwitch, PostModelSwitchהשם הקנוני של המודל שאליו הסשן עובר, כפי שמתואר תחת PreModelSwitchclaude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.*
SubagentStopסוג הסוכןאותם ערכים כמו SubagentStart
ConfigChangeמקור התצורהuser_settings, project_settings, local_settings, policy_settings, skills
CwdChangedאין תמיכה ב-matcherתמיד מופעל בכל מופע
DirectoryAddedאיך הספרייה נוספהslash_command, register_repo_root
FileChangedשמות קבצים מילוליים למעקב (ראו FileChanged).envrc|.env
StopFailureסוג השגיאהrate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error, unknown
InstructionsLoadedסיבת הטעינהsession_start, nested_traversal, path_glob_match, include, compact
UserPromptExpansionשם הפקודהשמות ה-skill או הפקודה שלך
Elicitationשם שרת MCPשמות שרתי ה-MCP שהוגדרו
ElicitationResultשם שרת MCPאותם ערכים כמו Elicitation
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplayאין תמיכה ב-matcherתמיד מופעל בכל מופע

התאמת StopFailure ל-cloud_credential_error דורשת Claude Code מגרסה v2.1.267 ואילך, הגרסה הראשונה שמדווחת על כשלי טעינת credentials תחת הערך הזה במקום server_error או unknown.

ברוב האירועים, Claude Code מעריך את ה-matcher מול שדה מקלט ה-JSON שהוא שולח להוק שלך ב-stdin. באירועי כלים, השדה הזה הוא tool_name. ב-PreModelSwitch וב-PostModelSwitch, Claude Code מעריך את ה-matcher מול השם הקנוני שהוא גוזר מ-to_model, כפי שמתואר תחת PreModelSwitch. כל מקטע אירוע הוק מפרט את קבוצת ערכי ה-matcher המלאה ואת סכמת הקלט של האירוע.

הדוגמה הזו מריצה סקריפט linting רק כש-Claude כותב או עורך קובץ:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}

אם מוסיפים שדה matcher לאירוע בלי תמיכה ב-matcher, הוא מושמט בשקט.

באירועי כלים, אפשר לסנן בצורה צרה יותר על ידי הגדרת שדה if על מטפלי הוק בודדים. if משתמש בתחביר כללי הרשאה כדי להתאים לשם הכלי ולפרמטרים יחד, כך ש-"Bash(git *)" רץ כשתת-פקודה כלשהי של קלט Bash תואמת ל-git * ו-"Edit(*.ts)" רץ רק לקובצי TypeScript.

#התאמת כלי MCP

כלי שרת MCP מופיעים ככלים רגילים באירועי כלים (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), כך שאפשר להתאים אותם באותו אופן שבו מתאימים לכל שם כלי אחר.

כלי MCP פועלים לפי תבנית השמות mcp__<server>__<tool>, לדוגמה:

  • mcp__memory__create_entities: כלי יצירת הישויות של שרת Memory
  • mcp__filesystem__read_file: כלי קריאת הקובץ של שרת Filesystem
  • mcp__github__search_repositories: כלי החיפוש של שרת GitHub

כדי להתאים לכל כלי משרת, הוסיפו .* לקידומת השרת. ה-.* נדרש: matcher כמו mcp__memory או mcp__brave-search מכיל רק תווי התאמה מדויקת, ולכן הוא מושווה כמחרוזת מדויקת ולא תואם לאף כלי.

  • mcp__memory__.* תואם לכל הכלים משרת memory
  • mcp__brave-search__.* תואם לכל הכלים משרת ששמו מכיל מקף
  • mcp__.*__write.* תואם לכל כלי ששמו מתחיל ב-write מכל שרת

כלים משרת MCP ארוז בתוסף משתמשים במקטע שרת בהיקף שכולל את שם התוסף: mcp__plugin_<plugin-name>_<server-name>__<tool>. matcher שנכתב מול מפתח השרת החשוף אף פעם לא מופעל עבור הכלים האלה. בתוסף בשם my-plugin שאורז שרת תחת המפתח db, כלי query מופיע כ-mcp__plugin_my-plugin_db__query, כך שה-matcher לכל כלי מהשרת הזה הוא mcp__plugin_my-plugin_db__.*. השתמשו באותו שם כלי בהיקף בשדה if של מטפל. ראו שרתי MCP שמסופקים על ידי תוסף לאופן שבו השם בהיקף נבנה.

הדוגמה הזו רושמת את כל פעולות שרת memory ומאמתת פעולות כתיבה מכל שרת MCP:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

#שדות מטפל hook

כל אובייקט במערך הפנימי hooks הוא מטפל hook: פקודת המעטפת, נקודת הקצה HTTP, כלי ה-MCP, הנחיית ה-LLM, או הסוכן שרץ כשה-matcher תואם. יש חמישה סוגים:

  • hooks מסוג פקודה (type: "command"): מריצים פקודת מעטפת. הסקריפט מקבל את קלט ה-JSON של האירוע ב-stdin ומחזיר תוצאות דרך קודי יציאה ו-stdout.
  • hooks מסוג HTTP (type: "http"): שולחים את קלט ה-JSON של האירוע כבקשת HTTP POST לכתובת URL. נקודת הקצה מחזירה תוצאות דרך גוף התגובה באותו פורמט פלט JSON כמו hooks מסוג פקודה.
  • hooks מסוג כלי MCP (type: "mcp_tool"): קוראים לכלי בשרת MCP מוגדר. פלט הטקסט של הכלי מטופל כמו stdout של hook מסוג פקודה.
  • hooks מסוג הנחיה (type: "prompt"): שולחים הנחיה למודל Claude להערכה של תור אחד. המודל מחזיר את ההחלטה שלו כ-JSON. ראו hooks מבוססי הנחיה.
  • hooks מסוג סוכן (type: "agent"): מפעילים תת-סוכן שיכול להשתמש בכלים כמו Read, Grep ו-Glob כדי לאמת תנאים לפני החזרת החלטה. hooks מסוג סוכן הם ניסיוניים ועשויים להשתנות. ראו hooks מבוססי סוכן.

כשאירוע מופעל, ה-hooks התואמים שלו רצים במקביל. אם יותר מ-12 מהם הם hooks מסוג הנחיה או סוכן, 12 מהם רצים בבת אחת והשאר מתחילים כשהרצים מסתיימים. ה-timeout של כל hook שממתין מתחיל להיספר כשה-hook הזה מתחיל.

אם מגדירים את אותו מטפל ביותר מקובץ הגדרות אחד, הוא רץ פעם אחת. עותק של אותו מטפל בתוסף או ב-skill נשאר נפרד.

המטפלים רצים בתיקייה הנוכחית עם הסביבה של Claude Code. אם התיקייה הנוכחית כבר לא קיימת, למשל worktree או תיקייה זמנית שמעטפת אחרת מחקה באמצע הסשן, Claude Code מריץ hooks מסוג פקודה מהראשונה מבין אלה שעדיין קיימת: התיקייה שבה הסשן התחיל, שורש הפרויקט, תיקיית הבית, או תיקיית temp של המערכת. Claude Code רושם אזהרה שמציינת את תיקיית ה-fallback ביומן הניפוי. לסשן worktree שמתחילים מאפליקציית שולחן העבודה, ראו מה worktrees חולקים עם ה-checkout הראשי.

משתנה הסביבה $CLAUDE_CODE_REMOTE הוא "true" בסביבות web מרוחקות ואינו מוגדר ב-CLI המקומי. Claude Code מגרסה v2.1.199 ואילך מגדיר את $CLAUDE_CODE_BRIDGE_SESSION_ID למזהה הסשן של Remote Control בזמן שלסשן המקומי יש חיבור Remote Control פעיל.

#שדות משותפים

השדות האלה חלים על כל סוגי ה-hook:

שדהחובהתיאור
typeכן"command", "http", "mcp_tool", "prompt", או "agent"
ifלאתחביר כללי הרשאה לסינון מתי ה-hook הזה רץ, למשל "Bash(git *)" או "Edit(*.ts)". פקודת ה-hook רצה רק אם קריאת הכלי תואמת לתבנית. ראו את טבלת ההתאמה של Bash לאופן שבו תבניות Bash מוערכות מול פקודות משנה, $(), וגרשיים הפוכים. מוערך רק באירועי כלי: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, ו-PermissionDenied. באירועים אחרים, hook עם if מוגדר אף פעם לא רץ
timeoutלאשניות לפני ביטול. Claude Code לא אוכף אותו על hook מסוג פקודה שמריצים עם async: true. ברירות מחדל: 600 עבור command, http, ו-mcp_tool; 30 עבור prompt; 60 עבור agent. Claude Code מוריד את ברירת המחדל של command, http, ו-mcp_tool ל-30 ב-UserPromptSubmit, PreModelSwitch, ו-PostModelSwitch, ול-10 ב-MessageDisplay. hooks של SessionEnd חולקים תקציב של 1.5 שניות; אם ההגדרות קובעות timeout ארוך יותר לכל hook, Claude Code מעלה את התקציב כדי להתאים, עד 60 שניות
statusMessageלאהודעת ספינר מותאמת שמוצגת בזמן שה-hook רץ
onceלאאם true, Claude Code מסיר את ה-hook אחרי הריצה המוצלחת הראשונה שלו. ריצה שנכשלת, חוסמת עם קוד יציאה 2, או מסתיימת ב-timeout משאירה את ה-hook במקום, כך שהוא רץ שוב באירוע התואם הבא. נלקח בחשבון רק עבור hooks שמוצהרים ב-frontmatter של skill; מתעלמים ממנו בקובצי הגדרות וב-frontmatter של סוכן

שדה ה-if מכיל בדיוק כלל הרשאה אחד. אין תחביר &&, ||, או רשימה לשילוב כללים; כדי להחיל כמה תנאים, הגדירו מטפל hook נפרד לכל אחד.

בתנאי if לכלי קבצים, תבנית תיקייה של מקטע יחיד כמו "Edit(src/**)" תואמת רק לתיקיית src בתיקיית העבודה ולקבצים שתחתיה. כדי להתאים לתיקייה בשם src בכל עומק, כתבו "Edit(**/src/**)".

#איך תבניות if מתאימות לפקודות Bash

עבור תבניות Bash בשדה ה-if, האם פקודת ה-hook רצה תלוי בצורת התבנית ובפקודת ה-Bash ש-Claude מפעיל. הצבות VAR=value מובילות מוסרות לפני ההתאמה.

תבנית ifפקודת Bashה-hook רץ?למה
Bash(git *)FOO=bar git pushכןהצבות מובילות מוסרות; git push תואם
Bash(git *)npm test && git pushכןכל פקודת משנה נבדקת; git push תואם
Bash(rm *)echo $(rm -rf /)כןפקודות בתוך $() וגרשיים הפוכים נבדקות; rm -rf / תואם
Bash(rm *)echo $(date)לאאין פקודת משנה שתואמת ל-rm *
Bash(git push *)echo $(date)כןתבניות שמציינות יותר משם הפקודה מריצות את ה-hook בכל מקרה על $(), גרשיים הפוכים, או $VAR

כש-Claude Code לא יכול לקבוע אילו פקודות קלט ה-Bash מריץ, הוא מריץ את ה-hook בלי קשר לתבנית. מכיוון שמסנן ה-if פועל כמיטב היכולת, השתמשו במערכת ההרשאות במקום ב-hook כדי לאכוף allow או deny מוחלטים.

#שדות hook מסוג פקודה

בנוסף לשדות המשותפים, hooks מסוג פקודה מקבלים את השדות האלה:

שדהחובהתיאור
commandכןפקודת מעטפת להרצה. עם args, קובץ ההרצה שמופעל ישירות. ראו צורת exec וצורת מעטפת
argsלארשימת ארגומנטים. כשהיא קיימת, command מזוהה כקובץ הרצה ומופעל ישירות עם args כוקטור הארגומנטים, בלי מעטפת. ראו צורת exec וצורת מעטפת
asyncלאאם true, רץ ברקע בלי לחסום. ראו הרצת hooks ברקע
asyncRewakeלאאם true, רץ ברקע ומעיר את Claude בקוד יציאה 2. ה-stderr של ה-hook, או stdout אם stderr ריק, מוצג ל-Claude כתזכורת מערכת כדי שיוכל להגיב לכשל רקע ממושך
shellלאהמעטפת לשימוש עבור ה-hook הזה. מקבל "bash" או "powershell". ברירת המחדל היא "bash", או "powershell" ב-Windows כש-Git Bash לא מותקן. הגדרת "powershell" מריצה את הפקודה דרך PowerShell ב-Windows. לא דורש CLAUDE_CODE_USE_POWERSHELL_TOOL כי hooks מפעילים את PowerShell ישירות. מתעלמים ממנו כש-args מוגדר
onFailureלאהאם הפעולה שהפעילה את ה-hook ממשיכה כשה-hook נכשל: "continue" או "block". ראו חסימת הפעולה כש-hook נכשל עבור "block" ועבור השארת השדה לא מוגדר. דורש Claude Code v2.1.295 או חדש יותר
#צורת exec וצורת מעטפת

hook מסוג פקודה רץ כצורת exec כש-args מוגדר, וכצורת מעטפת כש-args מושמט. הגדירו args בכל פעם שה-hook מפנה למציין מיקום של נתיב, כי כל רכיב מועבר כארגומנט אחד בלי ציטוט. השמיטו את args כשצריך תכונות מעטפת כמו צינורות או &&, או כשאף אחד מהחששות לא חל.

צורת exec רצה כש-args קיים. Claude Code מזהה את command כקובץ הרצה ב-PATH ומפעיל אותו ישירות עם args כוקטור הארגומנטים. אין מעטפת, כך שכל רכיב ב-args הוא ארגומנט אחד בדיוק כפי שנכתב, ומצייני מיקום של נתיב כמו ${CLAUDE_PLUGIN_ROOT} מוחלפים ב-command ובכל רכיב של args כמחרוזות רגילות. תווים מיוחדים כמו אפוסטרופים, $, וגרשיים הפוכים עוברים כלשונם כי אין מעטפת שמפרשת אותם. לא מתבצע פירוק לאסימונים של מעטפת באף פלטפורמה.

צורת מעטפת רצה כש-args חסר. מחרוזת ה-command מועברת למעטפת: sh -c ב-macOS וב-Linux, Git Bash ב-Windows, או PowerShell כש-Git Bash לא מותקן. הגדירו את שדה ה-shell כדי לבחור במפורש. המעטפת מפרקת את המחרוזת לאסימונים, מרחיבה משתנים, ומפרשת צינורות, &&, הפניות, ו-globs.

[!NOTE]

ב-Windows, צורת exec דורשת ש-command יזוהה כקובץ הרצה אמיתי כמו .exe. קובצי ה-shim מסוג .cmd ו-.bat ש-npm, npx, eslint וכלים אחרים מתקינים ב-node_modules/.bin אינם קובצי הרצה ואי אפשר להפעיל אותם בלי מעטפת. כדי להריץ אותם בצורת exec, הפעילו את הסקריפט שמתחת עם node ישירות, למשל "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. התבנית של node יחד עם נתיב הסקריפט עובדת בכל פלטפורמה כי node.exe הוא קובץ בינארי אמיתי. כדי להריץ shim של .cmd או .bat לפי שם, השתמשו בצורת מעטפת.

הדוגמה הזו מריצה סקריפט Node שמצורף לתוסף. צורת exec מעבירה את נתיב הסקריפט המזוהה כארגומנט אחד בלי ציטוט:

{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]
}

צורת המעטפת המקבילה צריכה ציטוט כדי לטפל בנתיבים עם רווחים או תווים מיוחדים:

{
  "type": "command",
  "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"
}

שתי הצורות תומכות באותם מצייני מיקום של נתיב, ושתיהן מייצאות אותם כמשתני הסביבה CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT, ו-CLAUDE_PLUGIN_DATA בתהליך שמופעל, כך שסקריפט יכול לקרוא את process.env.CLAUDE_PLUGIN_ROOT בלי קשר לאופן ההפעלה.

hooks של תוסף מחליפים בנוסף ערכי ${user_config.*}, בצורת exec בלבד: הערך מוחלף ב-command ובכל רכיב של args כמחרוזת רגילה, כך שאף מעטפת לא מנתחת אותו מחדש.

hook של תוסף בצורת מעטפת שה-command שלו מפנה ל-${user_config.*} נכשל עם שגיאה במקום לרוץ. כדי להשתמש בערך אפשרות מ-hook בצורת מעטפת, קראו את משתנה הסביבה $CLAUDE_PLUGIN_OPTION_<KEY>, למשל $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL עבור אפשרות webhook_url, או הגדירו args כדי להעביר את ה-hook לצורת exec. לפני v2.1.207, גם פקודות hook של תוסף בצורת מעטפת החליפו את ${user_config.*}.

[!NOTE]

בצורת exec, command הוא שם קובץ ההרצה או הנתיב בלבד. אם command הוא שם חשוף בלי מפריד נתיב ומכיל רווח לבן לצד args, Claude Code רושם אזהרה כי ההפעלה תיכשל: אין קובץ הרצה בשם node script.js. העבירו את האסימונים הנוספים אל args. נתיבים מוחלטים עם רווחים, כמו C:\Program Files\nodejs\node.exe, הם קובץ הרצה יחיד תקין ולא מפעילים את האזהרה.

#שדות hook מסוג HTTP

בנוסף לשדות המשותפים, hooks מסוג HTTP מקבלים את השדות האלה:

שדהחובהתיאור
urlכןכתובת URL לשליחת בקשת ה-POST
headersלאכותרות HTTP נוספות כזוגות מפתח-ערך. הערכים תומכים באינטרפולציה של משתני סביבה בתחביר $VAR_NAME או ${VAR_NAME}. רק משתנים שרשומים ב-allowedEnvVars מזוהים
allowedEnvVarsלארשימת שמות משתני סביבה שאפשר לבצע להם אינטרפולציה בערכי כותרות. הפניות למשתנים שלא ברשימה מוחלפות במחרוזות ריקות. נדרש כדי שאינטרפולציה של משתנה סביבה כלשהו תעבוד
onFailureלאמה קורה לפעולה כשה-hook נכשל: "continue", ברירת המחדל, או "block". ראו חסימת הפעולה כש-hook נכשל. דורש Claude Code v2.1.295 או חדש יותר

Claude Code שולח את קלט ה-JSON של ה-hook כגוף בקשת ה-POST עם Content-Type: application/json. גוף התגובה משתמש באותו פורמט פלט JSON כמו hooks מסוג פקודה.

טיפול בשגיאות שונה מ-hooks מסוג פקודה; ראו טיפול בתגובת HTTP.

הדוגמה הזו שולחת אירועי PreToolUse לשירות ולידציה מקומי, עם אימות באמצעות אסימון ממשתנה הסביבה MY_TOKEN:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/pre-tool-use",
            "timeout": 30,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

#שדות hook מסוג כלי MCP

בנוסף לשדות המשותפים, hooks מסוג כלי MCP מקבלים את השדות האלה:

שדהחובהתיאור
serverכןשם של שרת MCP מוגדר. עבור שרת שמצורף לתוסף, זה השם המתוחם plugin:<plugin-name>:<server-name>, למשל plugin:my-plugin:db, ולא מפתח השרת החשוף
toolכןשם הכלי לקריאה בשרת הזה
inputלאארגומנטים שמועברים לכלי. ערכי מחרוזת תומכים בהחלפת ${path} מקלט ה-JSON של ה-hook, למשל "${tool_input.file_path}"

הדוגמה הזו קוראת לכלי security_scan בשרת ה-MCP my_server אחרי כל Write או Edit, ומעבירה את הנתיב של הקובץ שנערך:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "my_server",
            "tool": "security_scan",
            "input": { "file_path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}
#איך נקראת התוצאה של הכלי

Claude Code קורא את תוכן הטקסט של הכלי באותו אופן שהוא קורא stdout של hook מסוג פקודה, לפי כלל הניתוח תחת קוד יציאה 0. אם הכלי מחזיר isError: true, ה-hook מייצר שגיאה לא חוסמת וההרצה ממשיכה.

#כשהשרת עדיין מתחבר

באירועים שבהם hook יכול לחסום או לשנות את התוצאה, כמו PreToolUse או Stop, Claude Code ממתין לשרת שמתחבר לפני שהוא קורא לכלי, לכל היותר MCP_TIMEOUT ובתוך ה-timeout של ה-hook עצמו. באירועי תצפית, כמו Notification או SessionEnd, הוא לא ממתין.

שרת שמציג את סטטוס cached מתחבר כשה-hook קורא לכלי שלו. אם השרת לא מחובר בנקודה הזו, ה-hook מייצר שגיאה לא חוסמת וההרצה ממשיכה. ה-hook אף פעם לא מתחיל תהליך OAuth, לכן אמתו את השרת מ-/mcp קודם.

#אירועים שמופעלים לפני ששרתי MCP זמינים

SessionStart בהפעלה, כולל עם --continue או --resume, וכל אירוע Setup מופעלים לפני ששרתי ה-MCP של הסשן זמינים ל-hooks. Claude Code מדלג על ה-hooks מסוג mcp_tool שלהם בלי לקרוא לכלי, ויומן הניפוי רושם mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), או אותה הודעה עם השם Setup. כש-SessionStart מופעל שוב מאוחר יותר בסשן, אחרי /clear או compaction, ה-hooks מסוג mcp_tool שלו רצים. לכל דבר שהסשן צריך בהפעלה, השתמשו ב-hook מסוג type: "command" על SessionStart במקום.

#שדות hook מסוג הנחיה וסוכן

בנוסף לשדות המשותפים, hooks מסוג הנחיה וסוכן מקבלים את השדות האלה:

שדהחובהתיאור
promptכןטקסט ההנחיה לשליחה למודל. השתמשו ב-$ARGUMENTS כמציין מיקום עבור ה-JSON של קלט ה-hook. השתמשו בלוכסן הפוך כדי לכלול טקסט מילולי: \$1.00 מוצג כ-$1.00
modelלאהמודל לשימוש להערכה. ברירת המחדל היא המודל ש-Claude Code משתמש בו עבור פונקציונליות רקע

#הפניה לסקריפטים לפי נתיב

השתמשו במצייני המקום האלה כדי להפנות לסקריפטי hook יחסית לשורש הפרויקט או הפלאגין, בלי קשר לתיקיית העבודה בזמן שה-hook רץ:

  • ${CLAUDE_PROJECT_DIR}: שורש הפרויקט שבו ההפעלה התחילה. Claude Code גם מגדיר את המשתנה הזה בסביבה של שרתי MCP ב-stdio ושל שרתי LSP של פלאגינים.
  • ${CLAUDE_PLUGIN_ROOT}: תיקיית ההתקנה של הפלאגין, לסקריפטים שמגיעים עם פלאגין. ראו משתני סביבה של פלאגינים כדי להבין איך הנתיב מתנהג אחרי עדכונים.
  • ${CLAUDE_PLUGIN_DATA}: תיקיית הנתונים המתמשכת של הפלאגין, לתלויות ולמצב שצריכים לשרוד עדכוני פלאגין.

[!NOTE]

Worktrees שונים. אם Claude נכנס ל-worktree במהלך ההפעלה, Claude Code משאיר את ${CLAUDE_PROJECT_DIR} במקום שבו היה ומעביר את נתיב ה-worktree ל-hooks שלכם בדרך אחרת:

  • ${CLAUDE_PROJECT_DIR} נשאר במקום: הוא עדיין מצביע על שורש הפרויקט שבו ההפעלה התחילה, כך שפקודה כמו ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh עדיין מריצה את הסקריפט ב-checkout הראשי.
  • cwd עוקב אחרי Claude: השדה cwd ב-JSON של הקלט של ה-hook הוא שורש ה-worktree אחרי ש-Claude נכנס ל-worktree, והתיקייה החדשה אחרי ש-Claude מריץ cd. קראו אותו כש-hook צריך לדעת באיזו תיקייה Claude עובד.

להפעלת worktree שמתחילים מאפליקציית שולחן העבודה, ראו What worktrees share with the main checkout כדי לדעת לאן ${CLAUDE_PROJECT_DIR} מצביע.

העדיפו צורת exec לכל hook שמפנה למציין מקום של נתיב. בצורת shell, עטפו כל מציין מקום במירכאות כפולות.

סקריפטי פרויקט

הדוגמה הזו משתמשת ב-${CLAUDE_PROJECT_DIR} כדי להריץ בודק סגנון מתיקיית .claude/hooks/ של הפרויקט אחרי כל קריאת כלי Write או Edit:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

סקריפטי פלאגין

הגדירו hooks של פלאגין ב-hooks/hooks.json עם שדה description אופציונלי ברמה העליונה. כשפלאגין מופעל, ה-hooks שלו מתמזגים עם ה-hooks של המשתמש ושל הפרויקט.

הדוגמה הזו מריצה סקריפט עיצוב שמגיע עם הפלאגין:

{
  "description": "Automatic code formatting",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
            "args": [],
            "timeout": 30
          }
        ]
      }
    ]
  }
}

ראו את הפניה לרכיבי פלאגין לפרטים על יצירת hooks של פלאגין.

#Hooks ב-skills ובסוכנים

בנוסף לקובצי הגדרות ולפלאגינים, אפשר להגדיר hooks ישירות ב-skills וב-תת-סוכנים באמצעות frontmatter, באותו פורמט הגדרה כמו hooks שמבוססים על קובצי הגדרות. כמה זמן Claude Code משאיר אותם רשומים תלוי ברכיב:

  • Hooks של תת-סוכן: Claude Code מריץ אותם רק בזמן שאותו תת-סוכן רץ, ומסיר אותם כשהוא מסיים. Claude Code ממיר כאן hook מסוג Stop ל-SubagentStop, האירוע שנורה כשתת-סוכן מסתיים.
  • Hooks של skill: Claude Code רושם אותם כשאתם או Claude מפעילים את ה-skill, וממשיך להריץ אותם עד סוף ההפעלה, גם בתורות שאחרי התור של ה-skill עצמו. כדי ש-Claude Code יסיר hook אחרי הריצה המוצלחת הראשונה שלו, הגדירו עליו once: true.

ה-skill הזה מגדיר hook מסוג PreToolUse שמריץ סקריפט אימות אבטחה לפני כל פקודת Bash:

---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

תת-סוכנים משתמשים באותו פורמט ב-frontmatter של ה-YAML שלהם.

Hooks ב-frontmatter של skill בפרויקט פועלים לפי אותו כלל אמון בסביבת העבודה כמו hooks בקובצי הגדרות. Claude Code רושם אותם כשאתם או Claude מפעילים את ה-skill, כולל בהרצה עם -p בתיקייה שעדיין לא נתתם בה אמון.

Hooks ב-frontmatter של תת-סוכן בפרויקט רצים רק אחרי שאתם מאשרים את תיבת הדו-שיח של אמון בסביבת העבודה לתיקייה שממנה הגיע קובץ הסוכן. הפעלה עם -p לא נחשבת לאישור. What runs before you trust a folder משווה את זה לכלל של קובץ ההגדרות, ודף תת-הסוכנים מפרט אילו היקפים פטורים.

#מגבלות על hooks של skills

כש-hooks ב-frontmatter של skill לא נורים אחרי שאתם או Claude מפעילים את ה-skill, הריצו /hooks באותה הפעלה. אם הם מופיעים ברשימה, ראו Hook not firing. אם אף אחד מהם לא מופיע, ייתכן שה-skill נמצא תחת נעילה לפלאגינים בלבד או שעבר מגבלת hooks, והוא רץ בלי אף אחד מ-hooks של ה-frontmatter שלו. בדקו כל מקרה:

  • נעילה לפלאגינים בלבד: מנהל הארגון יכול לנעול hooks לפלאגינים ולהגדרות מנוהלות באמצעות ההגדרה המנוהלת strictPluginOnlyCustomization. skill שומר על hooks של ה-frontmatter שלו תחת הנעילה הזו רק אם הוא skill של פלאגין, skill ארוז, או skill ב-תיקיית ההגדרות המנוהלות. גם ה-hooks בקובצי ההגדרות שלכם לא רצים.
    • בדיקה: הקלידו /status בהפעלה של Claude Code. אם שורת Setting sources כוללת Enterprise managed settings, שאלו את המנהל אם strictPluginOnlyCustomization נועל hooks.
    • תיקון: העבירו את ה-skill לתוך פלאגין, כי skills של פלאגין שומרים על hooks של ה-frontmatter שלהם. רשמו את הפלאגין ב-marketplace, ואז התקינו אותו משם. אם Claude Code מסרב ל-marketplace עם is blocked by enterprise policy, בקשו מהמנהל לאפשר את ה-marketplace הזה.
  • מגבלת hooks: ב-Claude Code גרסה v2.1.304 ואילך, לכל מקור של skills יש מגבלה משלו לכל הפעלה. הפרויקט שלכם, תיקיית ה-skills האישית שלכם, וכל פלאגין נחשבים למקורות נפרדים. ה-skills ממקור אחד יכולים לרשום לכל היותר 1,024 hooks. ה-matchers של אותם hooks יכולים להסתכם לכל היותר ב-16,384 תווים. matcher עם כמה hooks תחתיו נספר פעם אחת לכל hook.
    • בדיקה: חזרו על ההפעלה שבה ה-hook לא נורה, עם רישום debug מופעל:
      1. התחילו הפעלה חדשה מה-shell עם claude --debug. אם אתם לא מתחילים הפעלות מ-shell, הקלידו קודם /debug בהפעלה החדשה.
      2. הפעילו את אותם skills באותו סדר, כולל אלה ש-Claude הפעיל, כי שני הסיכומים נבנים skill אחרי skill. בקשו מ-Claude להפעיל כל skill שאי אפשר להקליד כפקודה.
      3. חפשו ב-יומן ה-debug את registers none of its hooks. שורה שמכילה את הטקסט הזה מציינת את ה-skill שעבר את המגבלה.
    • תיקון: הגדירו פחות hooks או matchers קצרים יותר באותו skill או ב-skills האחרים מאותו מקור, ואז התחילו הפעלה חדשה. אם ה-skills האלה לא שלכם לעריכה, בקשו מהמחבר את השינוי. עד אז, התחילו הפעלה חדשה והפעילו את ה-skill שאתם צריכים לפני כל skill אחר מאותו מקור.

אם אף אחד מהמקרים אינו שלכם, בדקו frontmatter שלא נותח.

#תפריט /hooks

הקלידו /hooks ב-Claude Code כדי לפתוח דפדפן לקריאה בלבד של ה-hooks שהוגדרו. הרשימה מתייגת כל hook לפי המקור שלו, למשל הגדרות משתמש, הגדרות פרויקט, הגדרות מקומיות, פלאגין, או ההפעלה הנוכחית.

בחרו hook כדי לראות את הטקסט המלא של מה שהוא מריץ והיכן הוא מוגדר, למשל הנתיב של קובץ ההגדרות שלו או שם הפלאגין שלו.

כדי לעיין בכל אירועי ה-hook, כולל כאלה שאין להם hooks מוגדרים, בחרו All events בסוף הרשימה.

#השבתה או הסרה של hooks

כדי להסיר hook שמוגדר בקובץ הגדרות, מחקו את הרשומה שלו מאותו קובץ.

כדי להשבית זמנית את כל ה-hooks בלי להסיר אותם, הגדירו "disableAllHooks": true בקובץ ההגדרות. Claude Code קורא את הערך שנשאר אחרי שקדימות ההגדרות חלה, כך ש-"disableAllHooks": false ב-.claude/settings.json של פרויקט גובר על true בהגדרות המשתמש. כדי לכבות hooks להרצה אחת בלי קשר למה שכתוב בהגדרות הפרויקט, העבירו --settings '{"disableAllHooks": true}', וזה גובר על הגדרות פרויקט והגדרות מקומיות. אין דרך להשבית hook בודד תוך כדי השארתו בהגדרה.

ההגדרה disableAllHooks מכבדת את היררכיית ההגדרות המנוהלות. אם מנהל הגדיר hooks דרך הגדרות מדיניות מנוהלות, disableAllHooks שמוגדר בהגדרות משתמש, פרויקט או מקומיות לא יכול להשבית את ה-hooks המנוהלים האלה. רק disableAllHooks שמוגדר ברמת ההגדרות המנוהלות יכול להשבית hooks מנוהלים. לטווח המלא של כל רמה, ראו disableAllHooks.

עריכות ישירות של hooks בקובצי הגדרות נקלטות בדרך כלל אוטומטית על ידי צופה הקבצים.

#קלט ופלט של Hook

Hooks מסוג פקודה מקבלים נתוני JSON דרך stdin ומעבירים תוצאות דרך קודי יציאה, stdout ו-stderr. Hooks מסוג HTTP מקבלים את אותו JSON כגוף בקשת POST ומעבירים תוצאות דרך גוף תגובת ה-HTTP. הסעיף הזה מכסה שדות והתנהגות משותפים לכל האירועים. הסעיף של כל אירוע תחת אירועי Hook כולל את סכמת הקלט הספציפית שלו ואת אפשרויות בקרת ההחלטה.

ב-macOS וב-Linux, hooks מסוג פקודה רצים בהפעלה משלהם בלי מסוף שולט. תהליך ה-hook וכל תהליכי הבן לא יכולים לפתוח את /dev/tty או לשלוח רצפי escape ישירות לממשק של Claude Code. ב-Windows אין /dev/tty.

כדי להציג הודעה למשתמש בכל פלטפורמה, החזירו systemMessage בפלט JSON. חלק מהאירועים משליכים אותו או מעבירים אותו למקום אחר, והסעיף של כל אירוע מציין זאת. כדי להפעיל התראת שולחן עבודה, להגדיר כותרת חלון, או לצלצל בפעמון, החזירו terminalSequence במקום זאת.

#שדות קלט משותפים

אירועי hook מקבלים את השדות האלה כ-JSON, בנוסף לשדות הספציפיים לאירוע שמתועדים בסעיף של כל אירוע hook. ב-hooks מסוג פקודה, ה-JSON מגיע דרך stdin. ב-hooks מסוג HTTP, הוא מגיע כגוף בקשת POST.

שדהתיאור
session_idמזהה ההפעלה הנוכחית
prompt_idUUID שמזהה את הנחיית המשתמש שמעובדת כרגע. תואם ל-מאפיין prompt.id באירועי OpenTelemetry, כך שאפשר לקשר פלט של hook לטלמטריה של הנחיה אחת. חסר עד הקלט הראשון של המשתמש
transcript_pathנתיב ל-JSON של השיחה. קובץ התמליל נכתב באופן אסינכרוני ועשוי לפגר אחרי השיחה שבזיכרון, ולכן ייתכן שעדיין לא יכלול את ההודעות האחרונות של התור הנוכחי כש-hook נורה. Hooks שצריכים את הטקסט הסופי של העוזר בתור הנוכחי צריכים להשתמש ב-last_assistant_message ב-Stop וב-SubagentStop במקום לקרוא את התמליל
cwdתיקיית העבודה הנוכחית בזמן שה-hook מופעל
scratchpad_dirנתיב ל-תיקיית ה-scratchpad של ההפעלה, שבה Claude שומר קבצי עבודה זמניים. חסר כשאין להפעלה scratchpad או כשתיקיית ה-temp לא זמינה. דורש Claude Code גרסה v2.1.257 ואילך
permission_modeמצב ההרשאות הנוכחי: "default", "plan", "acceptEdits", "auto", "dontAsk", או "bypassPermissions". המצב שמסומן Manual מגיע כ-"default", אף פעם לא כ-"manual", כך שסקריפטים שמתאימים ל-"default" ממשיכים לעבוד. לא כל האירועים מקבלים את השדה הזה. בדקו את דוגמת ה-JSON בסעיף של כל אירוע hook
effortאובייקט שה-level שלו הוא הגדרת רמת המאמץ בזמן שה-hook רץ, כולל רמה של skill, של תת-סוכן, או של ultrathink: "low", "medium", "high", "xhigh", או "max". קיים ב-PreToolUse, PostToolUse, Stop, SubagentStop, ובאירועים אחרים עם הקשר של שימוש בכלי, במודלים עם effort. אם המודל לא תומך ברמה שלכם, level הוא הרמה שרצה. רמה ששיניתם יכולה להופיע כאן לפני שהיא מתחילה. $CLAUDE_EFFORT מחזיק את אותה רמה.
hook_event_nameשם האירוע שנורה

agent_id ו-agent_type אומרים לסקריפט באיזה סוכן ה-hook נורה, למשל תת-סוכן, חבר צוות בתוך התהליך, או הסוכן שבחרתם עם --agent:

שדהתיאור
agent_idמזהה ייחודי לתת-הסוכן או לחבר הצוות בתוך התהליך שבו ה-hook נורה.
agent_typeשם הסוכן (למשל, "Explore" או "security-reviewer"). קיים כשההפעלה משתמשת ב---agent או כשה-hook נורה בתוך תת-סוכן. בתת-סוכנים, סוג תת-הסוכן גובר על ערך --agent של ההפעלה. ראו SubagentStart לערכים שתת-סוכנים מותאמים אישית ושל פלאגין מדווחים, ולאופן כתיבת matcher מול שם בהיקף פלאגין.

רק hooks של SessionStart יכולים לקבל שדה model, ו-Claude Code לא תמיד כולל אותו. Hooks של PreModelSwitch ו-PostModelSwitch מקבלים במקום זאת את from_model ואת to_model, אז השתמשו ב-hook מסוג PostModelSwitch כדי לעקוב אחרי המודל כשהוא משתנה במהלך הפעלה.

אין משתנה סביבה $CLAUDE_MODEL. ה-hook יכול לקרוא את $ANTHROPIC_MODEL אם הגדרתם אותו ב-shell, אבל הערך הזה לא משתנה כשעוברים בין מודלים עם /model במהלך הפעלה.

תהליך hook יורש את סביבת האב, מלבד משתני הייצוא OTEL_* ש-Claude Code מסיר מכל תהליך משנה שהוא מפעיל, וכש-CLAUDE_CODE_SUBPROCESS_ENV_SCRUB מוגדר ל-1, גם את המשתנים שהוא מסיר. בהפעלה שמקבלת את הגדרת HIPAA, Claude Code גם מסיר אישורי Anthropic מסביבת ה-hook.

לדוגמה, hook מסוג PreToolUse לפקודת Bash מקבל את זה ב-stdin:

{
  "session_id": "abc123",
  "prompt_id": "550e8400-e29b-41d4-a716-446655440000",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "scratchpad_dir": "/tmp/claude-1000/-home-user-my-project/abc123/scratchpad",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite",
    "timeout": 120000,
    "run_in_background": false
  },
  "tool_use_id": "toolu_01ABC123..."
}

השדות tool_name, tool_input ו-tool_use_id ספציפיים לאירוע. הסעיף של כל אירוע hook מתעד את השדות הנוספים של אותו אירוע.

#פלט קוד היציאה

כשההרצה של ה-hook מסתיימת, Claude Code משתמש בקוד היציאה כדי להחליט אם להמשיך בפעולה שהפעילה אותו, כמו קריאת כלי או פרומפט:

  • הצלחה: ה-hook יוצא עם 0. Claude Code מחיל את שדות פלט JSON שה-hook הדפיס, והפעולה ממשיכה אלא אם השדות האלה חוסמים או דוחים אותה. Hooks שמתחילים תהליך רקע מכסה את החריג.
  • שגיאה חוסמת: ה-hook יוצא עם 2. באירועים שיכולים לחסום, Claude Code עוצר את הפעולה.
  • שגיאה לא חוסמת: ה-hook יוצא עם כל קוד אחר, או נכשל בדרך אחרת, כמו אי התחלה או הדפסת JSON לא תקין. הפעולה ממשיכה, למעט hooks שכשל שלהם חוסם את הפעולה, למשל hook עם onFailure שמוגדר ל-"block". באירועים כמו PreToolUse, רואים הודעת <hook name> hook error בתמליל.

מה שה-hook מדפיס ל-stdout יכול לשנות את התוצאה. למשל, כש-hook של PreToolUse יוצא עם 1 אבל מדפיס JSON שעובר ולידציה, ה-JSON הזה עדיין יכול להחליט מה קורה. כדי למצוא את התוצאה של ה-hook באירוע כמו PreToolUse, התאימו את מה שהוא הדפיס ל-stdout בעמודה הראשונה לקוד היציאה לאורך השורה העליונה:

Stdoutיציאה 0יציאה 2כל קוד יציאה אחר
אובייקט JSON שעובר ולידציית סכמההצלחה. השדות חליםשגיאה חוסמת. Claude Code עדיין קורא את השדות, אבל הם לא יכולים לבטל את החסימההצלחה. Claude Code מתעלם מקוד היציאה, והשדות לבדם מחליטים. עבור hooks שכשל שלהם חוסם את הפעולה, ההרצה נחשבת לכשל במקום זאת
JSON שלא ניתן לפרסור או נכשל בולידציית סכמהשגיאה לא חוסמת. ההודעה נושאת את הודעת הפרסור או הוולידציהשגיאה חוסמת. ה-stderr שלכם הוא הסיבהשגיאה לא חוסמת. ההודעה נושאת את הודעת הפרסור או הוולידציה
טקסט רגיל, או כלוםהצלחהשגיאה חוסמת. ה-stderr שלכם הוא הסיבהשגיאה לא חוסמת. ההודעה נושאת את השורה הראשונה של ה-stderr שלכם

לחלק מהאירועים יש כללים משלהם:

  • WorktreeCreate: כל קוד יציאה שאינו אפס גורם ליצירת worktree להיכשל, בלי קשר למה שה-JSON אומר.
  • WorktreeRemove: כל קוד יציאה שאינו אפס גורם להסרת worktree להיכשל אם הספרייה עדיין קיימת אחר כך.
  • hooks של Stop, SubagentStop, TaskCompleted, ו-UserPromptSubmit של תוסף: כשה-hook יוצא עם 2 בלי כלום ב-stdout וה-stderr שלו אומר שקובץ חסר, כמו No such file or directory, Claude Code מתייחס להרצה כשגיאה לא חוסמת.
  • Elicitation ו-ElicitationResult: Claude Code מחיל את hookSpecificOutput כשה-hook יוצא עם 0, ומתעלם ממנו בכל קוד יציאה אחר.
  • אירועים שמשליכים את פלט ה-hook, כמו StopFailure: Claude Code מתעלם מה-JSON בכל קוד יציאה, חוץ משדות תופעת לוואי כמו terminalSequence, שעדיין מופעלים.

כדי לבדוק מה קוד יציאה 2 עושה באירוע שלכם, ראו התנהגות קוד יציאה 2 לפי אירוע. כדי לבדוק אילו שדות החלטה הוא מכבד, ראו בקרת החלטה.

#קוד יציאה 0

יציאה 0 פירושה הצלחה, והיא קוד היציאה המיועד כשמדפיסים JSON לבקרה מובנית. כשה-hook יוצא עם 0 אבל מדפיס JSON שלא ניתן לפרסור או נכשל בולידציית סכמה, הפעולה ממשיכה אלא אם ה-hook הוא אחד שכשל שלו חוסם את הפעולה. במקום הצלחה עם השדות שלכם מוחלים, ההרצה היא שגיאה לא חוסמת.

כדי לתקן את הפלט, השתמשו בהודעת הפרסור או הוולידציה שהודעת <hook name> hook error נושאת. עבור hook שאינו async, ה-stdout נחשב לפלט JSON רק כשמדפיסים אובייקט JSON אחד, בשורה אחת או בכמה, בלי כלום סביבו מלבד רווח לבן. Claude Code קורא כל stdout אחר כטקסט רגיל או ככשל פרסור:

  • פלט שלא מתחיל ב-{, או שמתחיל ב-{ ולא נגמר ב-}: טקסט רגיל. מערך JSON ומחרוזת JSON במרכאות הם טקסט רגיל לפי הכלל הזה.
  • שתי שורות או יותר שכל אחת מהן מפורסרת כ-JSON בפני עצמה, הראשונה מתחילה ב-{ והאחרונה נגמרת ב-}: טקסט רגיל כשאף שורה אינה אובייקט פלט JSON שמגדיר שדה. כשאחת מהשורות היא כזו, הפלט הוא כשל פרסור, אז הדפיסו את האובייקט הזה בלי שום דבר אחר ב-stdout.
  • כל דבר אחר שמתחיל ב-{ ונגמר ב-} אבל אינו JSON תקין: כשל פרסור.

ברוב האירועים, Claude Code כותב את stdout ללוג הדיבוג ולא מציג אותו בתמליל. החריגים הם UserPromptSubmit, UserPromptExpansion, SessionStart ו-PostModelSwitch, שבהם Claude Code מוסיף stdout בטקסט רגיל כהקשר ש-Claude יכול לראות ולפעול לפיו. באירועים האלה, Claude Code לא מוסיף stdout שהוא נכשל לפרסר.

Claude אף פעם לא רואה stderr מ-hook שיוצא עם 0. כדי לקרוא אותו בעצמכם באירועים כמו PreToolUse, הפעילו רישום דיבוג. כדי להציף אזהרה ל-Claude מ-hook של PostToolUse או PostToolUseFailure, צאו עם 2 במקום כדי שClaude יראה את ה-stderr למרות שהכלי כבר רץ.

#קוד יציאה 2

צאו עם קוד 2 כדי לחסום את הפעולה. באירועים שיכולים לחסום, Claude Code עוצר את הפעולה: hook של PreToolUse חוסם את קריאת הכלי, למשל, ו-hook של UserPromptSubmit דוחה את הפרומפט.

ההודעה שמגיעה עם החסימה היא ה-stderr של ה-hook. אם ה-hook גם הדפיס JSON שמקבל החלטה חוסמת, Claude Code משתמש בסיבה של ההחלטה הזו במקום.

יציאה 2 חוסמת גם כשה-hook מדפיס JSON:

  • JSON שעובר ולידציית סכמה: Claude Code עדיין קורא את שדות פלט JSON, אבל הם לא יכולים לבטל את החסימה. אפילו permissionDecision של "allow" לא מעביר את הפעולה. ב-Elicitation וב-ElicitationResult, מתעלמים מ-hookSpecificOutput של hook עם יציאה 2.
  • JSON שנכשל בולידציית סכמה: ה-hook עדיין חוסם. Claude Code משתמש ב-stderr שלכם כסיבת החסימה ורושם את כשל הוולידציה בלוג הדיבוג.

הסקריפט הזה חוסם פקודות rm על ידי יציאה עם 2 ומשאיר כל פקודה אחרת לזרימת ההרשאות הרגילה:

#!/bin/bash
# Reads JSON input from stdin, checks the command
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")

if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2  # Blocking error: tool call is prevented
fi

exit 0  # No decision: the normal permission flow applies

עם הסקריפט הזה רשום כ-hook של PreToolUse על Bash, פקודה שמתחילה ב-rm נחסמת, ו-Claude מקבל את ה-stderr של ה-hook כשגיאת הכלי, עם קידומת של שם האירוע, שם הכלי, ופקודת ה-hook:

PreToolUse:Bash hook error: [${CLAUDE_PROJECT_DIR}/.claude/hooks/no-rm.sh]: Blocked: rm commands are not allowed

#קודי יציאה אחרים

הודעת <hook name> hook error עם Failed with non-blocking status code: פירושה שקוד היציאה של ה-hook לא היה 0 או 2 וה-stdout שלו היה טקסט רגיל או ריק. אלא אם ה-hook הוא אחד שכשל שלו חוסם את הפעולה, זו שגיאה לא חוסמת, כך שה-hook לא עצר את הפעולה. כדי ללכוד את ה-stderr המלא של ה-hook במקום השורה הראשונה שההודעה מציגה, הפעילו רישום דיבוג.

למשל, כש-hook של PreToolUse על Bash מדפיס something broke ל-stderr ויוצא עם 1, הודעת PreToolUse:Bash hook error נושאת את השורה הזו:

Failed with non-blocking status code: something broke

אם ההודעה נושאת את הודעת המעטפת No such file or directory, כמו בשורה הבאה, ה-hook אף פעם לא רץ, למשל כי נתיב הסקריפט שלו לא קיים:

Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory

אם הנתיב שגוי, תקנו אותו במקום שבו ה-hook מוגדר. בצורת מעטפת, כשנתיב הסקריפט לא קיים או אינו ניתן להרצה, המעטפת יוצאת עם קוד כמו 127 וההודעה נושאת את הודעת המפרש. אלא אם ה-hook הוא אחד שכשל שלו חוסם את הפעולה, hook שלא יכול להתחיל הוא גם שגיאה לא חוסמת.

כשמגדירים hook מדיניות, שימו לב להודעה הזו בהרצה הראשונה שלו, כי נתיב עם שגיאת הקלדה ב-settings.json אומר שה-hook אף פעם לא רץ. כדי לחסום את הפעולה כשה-hook לא יכול להתחיל, הגדירו onFailure: "block".

[!WARNING]

קוד יציאה 1 בלי פלט JSON תקין הוא שגיאה לא חוסמת אלא אם ה-hook הוא אחד שכשל שלו חוסם את הפעולה, למרות ש-1 הוא קוד הכשל המקובל ב-Unix. אם ה-hook נועד לאכוף מדיניות, השתמשו ב-exit 2.

#פסקי זמן

מלבד hook של command שרצים עם async: true, Claude Code מבטל hook מסוג command, http או mcp_tool שמגיע ל-timeout שלו, משליך את פלט ה-hook, כך שברוב האירועים hook שפג זמנו לא מפיק החלטה. ב-PreModelSwitch, hook שבוטל בפסק הזמן שלו חוסם את החלפת המודל.

ב-PreToolUse, hook מסוג command, http או mcp_tool שפג זמנו לא חוסם את קריאת הכלי, למעט hooks שכשל שלהם חוסם את הפעולה. הקריאה ממשיכה דרך זרימת ההרשאות הרגילה, אז אל תסמכו על hook שנתקע שישמש כשער. כדי לחסום את הקריאה כש-hook של command או http מגיע לפסק זמן, הגדירו onFailure: "block". hook של callback ב-Agent SDK שחורג מפסק הזמן שלו חוסם את קריאת הכלי.

#Hooks שמתחילים תהליך רקע

אם ה-hook של command מקבל את ההחלטה שלו בתהליך רקע ויוצא, דחייה שהתהליך הזה מדפיס אחרי ש-Claude Code הפסיק לקרוא לא חוסמת את קריאת הכלי. עבור hook שאינו async, Claude Code מסיים לקרוא את פלט ה-hook זמן קצר אחרי שתהליך ה-hook עצמו יוצא.

הדפיסו את ההחלטה לפני שמתחילים תהליך רקע, והפנו את stdout ו-stderr של התהליך הזה. אם הסקריפט מדפיס allow, ואז מתחיל תהליך רקע בלי להפנות את stdout ו-stderr של התהליך הזה, Claude Code חוסם את קריאת הכלי. שגיאת ה-hook כוללת refusing to honor the suspect capture.

סקריפט ה-hook הזה של PreToolUse מדפיס את ההחלטה עם policy-check.sh, ואז מעלה מדדים ברקע:

#!/bin/bash
"$CLAUDE_PROJECT_DIR"/.claude/hooks/policy-check.sh || exit $?
"$CLAUDE_PROJECT_DIR"/.claude/hooks/upload-metrics.sh >/dev/null 2>&1 &

שאר הסקריפט עושה את זה:

  • || exit $?: הופך את קוד היציאה של הבדיקה לקוד של ה-hook עצמו, כך שבדיקה שיוצאת עם 2 כדי לדחות עדיין חוסמת. אחרת ה-hook יוצא עם הקוד של השורה האחרונה, שהוא 0.
  • >/dev/null 2>&1 &: מריץ את upload-metrics.sh ברקע עם הפלט שלו מופנה, כך שהוא לא משאיר את פלט ה-hook פתוח.

כדי לבדוק את הסקריפט, גרמו ל-policy-check.sh לצאת עם 2, ואז הפעילו קריאת כלי שה-hook תואם לה. קריאת הכלי נחסמת.

#חסימת הפעולה כש-hook נכשל

ברוב האירועים, כש-hook נכשל או מגיע לפסק זמן, Claude Code עדיין מבצע את הפעולה. hook מדיניות עם נתיב שגוי או סקריפט שקורס אז מעביר הכל. כדי לחסום את הפעולה במקום זאת, הגדירו "onFailure": "block" על hook של command או http. נדרש Claude Code v2.1.295 או מאוחר יותר. עבור hooks של callback ב-Agent SDK, ראו חסימת הפעולה כש-callback נכשל.

[!NOTE]

ב-Windows בלי Git Bash, hook של command בצורת מעטפת מסוג PreToolUse או PermissionRequest שנכשל כש-onFailure לא מוגדר יכול במקום זאת לחסום את קריאת הכלי.

ה-hook הזה של PreToolUse ב-.claude/settings.json מריץ סקריפט פרויקט לפני כל פקודת Bash, וחוסם את הפקודה אם הסקריפט נכשל:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node",
            "args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js"],
            "onFailure": "block"
          }
        ]
      }
    ]
  }
}

כדי לבדוק אותו, השאירו את check-command.js חסר ובקשו מ-Claude להריץ פקודת Bash כמו ls. Claude Code חוסם את הקריאה, והשגיאה כוללת failed; blocking because onFailure is "block" ואחריו פלט השגיאה של node עצמו, שקוצץ כאן לשורה אחת:

PreToolUse:Bash hook error: [node ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js]: failed; blocking because onFailure is "block"
Error: Cannot find module '/path/to/project/.claude/hooks/check-command.js'

אחרי פסק זמן, ההודעה אומרת timed out במקום failed. בלי onFailure מוגדר, אותו סקריפט חסר הוא שגיאה לא חוסמת ו-ls רץ.

כל אחד מאלה נחשב לכשל:

  • לא יכול להתחיל: hook של command נכשל להתחיל, למשל כי הסקריפט או קובץ ההרצה לא קיים
  • קוד יציאה שאינו 0 או 2: נחשב עבור hook של command גם אם הוא הדפיס JSON שמתיר את הפעולה, כמו permissionDecision: "allow". כדי להחזיר החלטת JSON, צאו עם 0
  • שגיאת HTTP: החיבור של hook מסוג HTTP נכשל, או שסטטוס התגובה אינו 2xx
  • פסק זמן: ה-hook מגיע ל-timeout שלו
  • פלט לא תקין: פלט ה-JSON לא ניתן לפרסור או נכשל בולידציית סכמה. עבור hook מסוג HTTP, גוף 2xx שאינו ריק ואינו אובייקט JSON גם נחשב. stdout בטקסט רגיל מ-hook של command אינו כשל

עם "block" מוגדר, כשל עושה את מה שקוד יציאה 2 עושה באירוע הזה, למעט ב-PermissionRequest, שם הוא דוחה את הבקשה. למשל, כשל של PreToolUse חוסם את קריאת הכלי וכשל של UserPromptSubmit חוסם את הפרומפט.

לשדה אין השפעה על ה-hooks האלה:

  • hooks של Stop, SubagentStop, TaskCompleted ו-TeammateIdle: קוד יציאה 2 באירועים האלה שולח את Claude בחזרה להמשיך לעבוד, ו-Claude לא יכול לתקן hook שלא רץ
  • hooks של command ברקע: hooks של command שמגדירים async או asyncRewake

#Hooks שנכשלו ב-Windows בלי Git Bash

אם השגיאה של קריאת כלי שנחסמה כוללת את הטקסט הזה, hook של command מסוג PreToolUse או PermissionRequest נכשל או הגיע לפסק זמן וחסם את הקריאה:

blocking because Git Bash wasn't found, so only exit code 0 or 2 counts as this hook's verdict.

אם השגיאה גם אומרת PowerShell started no program, התחילו בתיקונים ל-command שהוא רק מחרוזות במרכאות, ש-PowerShell מדפיס במקום להריץ.

בלי Git Bash, כל קוד יציאה שאינו 0 או 2 מה-hook הזה נחשב לכשל, גם כשהוא מדפיס JSON שלא דוחה את הקריאה. כדי לגלות למה ה-hook נכשל, בדקו את לוג הדיבוג. אחר כך בחרו את התיקון שמתאים ל-hook שלכם:

  • הקריאה צריכה להמשיך כשה-hook נכשל: הוסיפו "onFailure": "continue" ל-hook
  • ה-hook יוצא עם 1 כדי להזהיר אבל חייב לחסום כשהוא קורס: עם "onFailure": "continue", הקריאה ממשיכה בקוד יציאה 1, באזהרה ובקריסה כאחד. הוסיפו "onFailure": "block" במקום, וגרמו לסקריפט לצאת עם 0 במקום שבו הוא מזהיר, כך שהקריאות האלה ממשיכות. כדי עדיין להציג את האזהרה ב-PreToolUse, הדפיסו אותה ל-stdout כשדה systemMessage של אובייקט JSON אחד.
  • ה-hook נכתב עבור Bash: בלי Git Bash, hook בצורת מעטפת בלי shell מוגדר רץ ב-PowerShell, שם הוא יכול להיכשל. התקינו Git for Windows, או הגדירו CLAUDE_CODE_GIT_BASH_PATH אם Git Bash מותקן במקום ש-Claude Code לא מוצא אותו. אחר כך התחילו סשן חדש.
  • ה-hook מריץ קובץ .exe אחד, לא קובץ .cmd או .bat: שימו את התוכנית ב-command ואת הארגומנטים שלה ב-args כדי להשתמש בצורת exec, והוסיפו "onFailure": "block". Claude Code אז מתחיל את התוכנית בלי מעטפת. בלי "onFailure": "block", הקריאה יכולה להמשיך כשהתוכנית נכשלת, חסרה, או מגיעה לפסק זמן
  • hook שמירה של PowerShell שחייב לחסום בכשל: עם onFailure לא מוגדר, הוא מפסיק לעשות זאת ברגע ש-Git for Windows מותקן ו-Claude Code מוצא את Git Bash. הוסיפו "onFailure": "block" כך שכשל או פסק זמן חוסם את הקריאה עם Git Bash או בלעדיו. גם הגדירו "shell": "powershell" כך שה-hook ממשיך לרוץ ב-PowerShell מכאן והלאה במקום ב-Git Bash

לפני v2.1.309, ההודעה הייתה blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.

#PowerShell לא הפעילה תוכנית

הוק עם השגיאה הזו רץ ב-PowerShell, שמדפיסה command שהוא רק מחרוזות במרכאות, כמו "command": "\"C:\\Tools\\guard.exe\"", במקום להריץ אותו.

  • כדי להדפיס טקסט, כמו JSON: הגדירו "shell": "powershell", וההוק יוצא עם קוד 0
  • כדי להריץ קובץ .ps1, .cmd או .bat, או קובץ .exe שהנתיב שלו כולל משתנה: הגדירו "shell": "powershell" ושימו & לפני הנתיב המצוטט. השאירו את $HOME כפי שהוא כתוב, וכתבו משתני סביבה כפי שדוגמת check.ps1 כותבת את $env:CLAUDE_PROJECT_DIR
  • כדי להריץ קובץ .exe אחד בנתיב בלי $: הסירו את המרכאות הפנימיות מ-command, ואז החילו את התיקון להוק שמריץ קובץ .exe אחד, וכתבו "args": [] אם התוכנית לא מקבלת ארגומנטים
  • כדי להריץ סקריפט שנכתב ל-Bash: החילו את התיקון להוק שנכתב ל-Bash

#התנהגות קוד יציאה 2 לפי אירוע

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

אירוע הוקאפשר לחסום?מה קורה בקוד יציאה 2
PreToolUseכןחוסם את קריאת הכלי
PermissionRequestלאקוד יציאה 2 לא נאכף לאירוע הזה וזרימת ההרשאה ממשיכה ללא שינוי. סרבו דרך אובייקט decision
UserPromptSubmitכןחוסם את הפרומפט, כך שהוא לא מגיע ל-Claude. ראו מה פרומפט חסום משאיר אחריו
UserPromptExpansionכןחוסם את ההרחבה
Stopכןמונע מ-Claude לעצור, וממשיך את השיחה
SubagentStopכןמונע מהסוכן המשנה לעצור
TeammateIdleכןמונע מחבר הצוות לעבור למצב idle, כך שהוא ממשיך לעבוד
TaskCreatedכןמחזיר לאחור את יצירת המשימה
TaskCompletedכןמונע סימון של המשימה כהושלמה
ConfigChangeכןחוסם את כניסת שינוי התצורה לתוקף (מלבד policy_settings)
StopFailureלאמתעלמים מהפלט ומקוד היציאה, מלבד terminalSequence
PostToolUseלאמציג את stderr ל-Claude. הכלי כבר רץ
PostToolUseFailureלאמציג את stderr ל-Claude. הכלי כבר נכשל
PostToolBatchכןעוצר את הלולאה הסוכנית לפני קריאת המודל הבאה
PermissionDeniedלאמתעלמים מקוד היציאה ומ-stderr כי הסירוב כבר התרחש. השתמשו ב-JSON hookSpecificOutput.retry: true כדי לומר למודל שהוא רשאי לנסות שוב. Claude Code מתעלם מ-retry: true עבור סירובים ללא הכרעה
Notificationלאמתעלמים מקוד היציאה ומ-stderr
SubagentStartלאמציג את stderr למשתמש בלבד
SessionStartלאמציג את stderr למשתמש בלבד
Setupלאמתעלמים מקוד היציאה ומ-stderr
SessionEndלאמציג את stderr למשתמש בלבד
CwdChangedלאמציג את stderr למשתמש בלבד
DirectoryAddedלאstderr עובר ליומן הדיבוג. הספרייה כבר נוספה
FileChangedלאמציג את stderr למשתמש בלבד
PreCompactכןחוסם את הדחיסה
PostCompactלאמציג את stderr למשתמש בלבד
PreModelSwitchכןחוסם את החלפת המודל ומציג את stderr למשתמש
PostModelSwitchלאמציג את stderr למשתמש בלבד. המודל כבר הוחלף
Elicitationכןדוחה את הבקשה, ולא מופיעה תיבת דו שיח
ElicitationResultכןחוסם את התגובה (הפעולה הופכת ל-decline)
WorktreeCreateכןכל קוד יציאה שאינו אפס גורם ליצירת worktree להיכשל
WorktreeRemoveכןכל קוד יציאה שאינו אפס גורם להסרת worktree להיכשל אם הספרייה עדיין קיימת לאחר מכן. ראו WorktreeRemove לגבי מה שקורה לספרייה
InstructionsLoadedלאמתעלמים מקוד היציאה
MessageDisplayלאהטקסט המקורי מוצג

עבור SessionStart, SubagentStart ו-PostModelSwitch, Claude Code מציג את stderr של קוד יציאה 2 בתמליל כהודעת <hook name> hook error, באותו אופן שבו הוא מציג שגיאה לא חוסמת. Claude לא רואה אותה, והסשן או הסוכן המשנה ממשיכים. עבור SubagentStart, ההודעה מופיעה בתמליל של הסוכן המשנה עצמו, ולא בשיחת האב.

#טיפול בתגובת HTTP

הוקי HTTP משתמשים בקודי סטטוס של HTTP ובגופי תגובה במקום בקודי יציאה וב-stdout. כדי לחסום את הפעולה כאשר הבקשה נכשלת או מחזירה סטטוס שאינו 2xx, הגדירו onFailure: "block". כדי לחסום קריאת כלי או לסרב להרשאה, החזירו תגובת 2xx עם גוף JSON שמכיל את שדות ההחלטה המתאימים.

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

  • 2xx עם גוף ריק: הצלחה, שווה ערך לקוד יציאה 0 בלי פלט
  • 2xx עם גוף שהוא אובייקט JSON: מנותח לפי אותה סכמת פלט JSON כמו הוקי פקודה. גוף שנכשל באימות הסכמה הוא שגיאה לא חוסמת
  • 2xx עם כל גוף אחר, כמו טקסט רגיל: שגיאה לא חוסמת, שמטופלת כמו סטטוס שאינו 2xx. Claude Code לא מוסיף את הטקסט להקשר של Claude
  • סטטוס שאינו 2xx: שגיאה לא חוסמת
  • כשל בחיבור: שגיאה לא חוסמת
  • Timeout: ההוק מבוטל, כמתואר תחת Timeouts

#פלט JSON

קודי יציאה מאפשרים רק לחסום או להישאר בשתיקה, אבל פלט JSON נותן שליטה מפורטת יותר. במקום לצאת עם קוד 2 כדי לחסום, צא עם 0 והדפס אובייקט JSON ל-stdout. Claude Code קורא שדות מסוימים מאותו JSON כדי לשלוט בהתנהגות, כולל בקרת החלטה לחסימה, לאישור, או להסלמה למשתמש.

[!NOTE]

בחר גישה אחת לכל hook: או להשתמש בקודי יציאה בלבד לאיתות, או לצאת עם 0 ולהדפיס JSON לשליטה מובנית. אם מערבבים ביניהן, יציאה 2 שומרת על אפקט החסימה שלה, ו-Claude Code עדיין קורא את שדות ה-JSON, עם חריג ה-elicitation היחיד שמצוין תחת קוד יציאה 2.

הדפס ל-stdout רק את אובייקט ה-JSON. ב-hook שאינו async, טקסט אחר שם, למשל שורה שפרופיל המעטפת מהדהד בהפעלה, מונע מ-Claude Code לקרוא את האובייקט כ-JSON, ו-ל-JSON של hook אין השפעה מראה איך למצוא ולהשתיק את הטקסט הזה.

המחרוזות additionalContext, systemMessage ו-initialUserMessage של hook, וגם ה-stdout הרגיל שלו, מוגבלות ל-10,000 תווים:

  • היקף: Claude Code מודד כל מחרוזת בפני עצמה, גם כשכמה hooks רצים לאותו אירוע. בפלט JSON כל שדה נמדד בנפרד. stdout רגיל נמדד בשלמותו.
  • מעל המגבלה: Claude Code שומר את הפלט בקובץ בתיקיית הסשן ומחליף אותו בנתיב הקובץ ובתצוגה מקדימה של עד 2,000 התווים הראשונים. תוצאת Bash גדולה ותקינה מטופלת באותו אופן, כמתואר תחת מגבלות פלט. בניגוד לתקרת ה-Bash הזו, למגבלה הזו אין הגדרה או משתנה סביבה כדי להעלות אותה.
  • קריאת הקובץ: Claude Code לא מבקש מ-Claude לקרוא את הקובץ, לכן שמור בתוך המגבלה כל דבר ש-Claude חייב תמיד לראות.

אובייקט ה-JSON תומך בשלושה סוגי שדות:

  • שדות אוניברסליים כמו continue מופיעים בטבלה למטה. כל אירוע מקבל אותם, אבל חלק מהאירועים משליכים אותם או מעבירים את systemMessage למקום שאינו התמליל. הסעיף של כל אירוע מציין זאת. terminalSequence עובד גם באירועים האלה, עם החריגים שמפורטים תחת שידור התראות טרמינל.
  • decision ו-reason ברמה העליונה משמשים חלק מהאירועים לחסימה או למתן משוב.
  • hookSpecificOutput הוא אובייקט מקונן לאירועים שצריכים שליטה עשירה יותר. הוא דורש שדה hookEventName שמוגדר לשם האירוע.
שדהברירת מחדלתיאור
continuetrueאם false, Claude מפסיק את העיבוד לגמרי אחרי שה-hook רץ. גובר על כל שדות החלטה שספציפיים לאירוע
stopReasonnoneהודעה שמוצגת למשתמש כש-continue הוא false. היא נשארת בשיחה, ולכן Claude רואה אותה אם השיחה ממשיכה
suppressOutputfalseאין לו השפעה: Claude Code מקבל את השדה אבל לא פועל לפיו. stdout של hook שהצליח אף פעם לא מוצג בתמליל, והוא נרשם ביומן הדיבוג
systemMessagenoneהודעת אזהרה שמוצגת למשתמש. ב-Agent SDK ובפלט של --output-format stream-json, היא יכולה להגיע כ-SDKInformationalMessage
terminalSequencenoneרצף escape של טרמינל ש-Claude Code ישדר בשמך, למשל התראת שולחן עבודה, כותרת חלון, או פעמון. מוגבל ל-OSC 0/1/2/9/99/777 ול-BEL. אם הערך מכיל משהו מחוץ לרשימת ההיתר, מתעלמים מהשדה. השתמש בזה במקום לכתוב ל-/dev/tty, שאינו זמין ל-hooks

כדי לעצור את Claude לגמרי:

{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

ב-hooks של PreToolUse ו-PostToolUse, העצירה חלה גם כשקריאת הכלי נכשלת או מסתיימת בזמן ש-Claude עדיין מזרים תשובה.

#שידור התראות טרמינל

Hooks רצים בלי טרמינל שולט, ולכן כתיבת רצפי escape ישירות ל-/dev/tty נכשלת. במקום זאת, החזר את רצף ה-escape בשדה terminalSequence, ו-Claude Code משדר אותו עבורך דרך נתיב הכתיבה לטרמינל שלו. זה חופשי מתנאי מרוץ, עובד בתוך tmux ו-GNU screen, ועובד ב-Windows שבה אין /dev/tty.

השדה מקבל מחרוזת של רצף escape אחד או יותר מהרשימה המותרת:

  • OSC 0, 1, 2: כותרות חלון ואייקון
  • OSC 9: התראות של iTerm2, ConEmu, Windows Terminal ו-WezTerm, כולל התקדמות בשורת המשימות 9;4
  • OSC 99: התראות Kitty
  • OSC 777: התראות urxvt, Ghostty ו-Warp
  • BEL בלבד

רצפים יכולים להסתיים ב-BEL או ב-ST. כל דבר מחוץ לרשימת ההיתר, כולל רצפי CSI של סמן וצבע, רצפי לוח צבעים OSC, היפר-קישורים OSC 8, כתיבות ללוח OSC 52, ו-OSC 1337, נדחה, ומתעלמים מהשדה.

Claude Code כותב את הרצף בעצמו כשהוא מעבד את פלט ה-hook, ולכן השדה עובד באירועים שמשליכים את systemMessage ואת continue, כמו Notification ו-StopFailure. יש לו שתי מגבלות:

  • Claude Code כותב את הרצף רק בסשן אינטראקטיבי, ורק כשהממשק שלו על המסך. במצב לא אינטראקטיבי עם הדגל -p וב-Agent SDK, הוא מתעלם מהשדה.
  • hook פקודה של WorktreeCreate לא יכול להחזיר JSON, כי Claude Code קורא את ה-stdout שלו כנתיב ה-worktree. hook HTTP של WorktreeCreate מחזיר JSON ויכול לכלול את השדה.

הדוגמה למטה משגרת התראת שולחן עבודה מ-hook של Notification. רצף ה-escape נבנה עם escape אוקטלי של printf כדי שבתי הבקרה לעולם לא יופיעו בשורת הפקודה של המעטפת, ו-jq -n --arg בונה את פלט ה-JSON כדי שמרכאות, לוכסנים הפוכים ושורות חדשות בהודעת ההתראה יוברחו נכון:

#!/bin/bash
# Notification hook: ping the desktop when Claude Code needs attention.
input=$(cat)
title="Claude Code"
body=$(jq -r '.message // "Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'

הצורה { "terminalSequence": "..." } זהה מכל מעטפת או שפה.

#הוספת הקשר ל-Claude

השדה additionalContext מעביר מחרוזת מה-hook לחלון ההקשר של Claude. Claude Code עוטף את המחרוזת ב-תזכורת מערכת ומכניס אותה לשיחה בנקודה שבה ה-hook נורה. Claude קורא את התזכורת בבקשת המודל הבאה, אבל היא לא מופיעה כהודעת צ'אט בממשק.

החזר additionalContext בתוך hookSpecificOutput לצד שם האירוע:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
  }
}

המקום שבו התזכורת מופיעה תלוי באירוע:

כשכמה hooks מחזירים additionalContext לאותו אירוע, Claude מקבל את כל הערכים.

אם המחרוזת שלך מכילה תג <system-reminder> או </system-reminder>, Claude מקבל את המחרוזת כשה-< של התג הזה מוחלף ב-<.

אם ערך עובר את 10,000 התווים, Claude Code כותב את הטקסט לקובץ בתיקיית הסשן ומעביר ל-Claude את נתיב הקובץ עם תצוגה מקדימה של עד 2,000 התווים הראשונים במקום. Claude יכול לקרוא את הקובץ, אבל Claude Code לא מבקש ממנו לעשות זאת.

השתמש ב-additionalContext למידע ש-Claude צריך לדעת על המצב הנוכחי של הסביבה שלך או על הפעולה שרצה עכשיו:

  • מצב הסביבה: הענף הנוכחי, יעד הפריסה, או דגלי תכונה פעילים
  • כללי פרויקט מותנים: איזו פקודת בדיקה חלה על הקובץ שנערך עכשיו, אילו תיקיות הן לקריאה בלבד ב-worktree הזה
  • נתונים חיצוניים: בעיות פתוחות שמוקצות אליך, תוצאות CI אחרונות, תוכן שנשלף משירות פנימי

להוראות שלעולם לא משתנות, העדף את CLAUDE.md. הוא נטען בלי להריץ סקריפט, והוא המקום הסטנדרטי למוסכמות פרויקט סטטיות.

כתוב את הטקסט כהצהרות עובדתיות ולא כהוראות מערכת בציווי. ניסוח כמו "The deployment target is production" או "This repo uses bun test" נקרא כמידע על הפרויקט. טקסט שממוסגר כפקודות מערכת מחוץ לערוץ יכול להפעיל את הגנות הזרקת ההנחיות של Claude, וזה גורם ל-Claude להציג לך את הטקסט במקום להתייחס אליו כהקשר.

Claude Code שומר את הטקסט שהוזרק בתמליל הסשן. לאירועי אמצע סשן כמו PostToolUse או UserPromptSubmit, כשממשיכים עם --continue או --resume, Claude Code מנגן מחדש את הטקסט השמור במקום להריץ שוב את ה-hook לתורות קודמים, ולכן ערכים כמו חותמות זמן או SHA של commit מתיישנים. hooks של SessionStart רצים שוב בהמשך, עם source שמוגדר ל-"resume", או ל-"fork" אם הוספת --fork-session, כדי שיוכלו לרענן את ההקשר שלהם.

#בקרת החלטה

לא כל אירוע תומך בחסימה או בשליטה בהתנהגות דרך JSON. האירועים שכן תומכים משתמשים כל אחד בסט שדות שונה כדי לבטא את ההחלטה. השתמש בטבלה הזו כהפניה מהירה לפני כתיבת hook:

אירועיםתבנית החלטהשדות מרכזיים
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompactdecision ברמה העליונהdecision: "block", reason. Stop ו-SubagentStop מקבלים גם hookSpecificOutput.additionalContext למשוב שאינו שגיאה שממשיך את השיחה
TeammateIdle, TaskCompletedקוד יציאה או continue: falseקוד יציאה 2 חוסם את הפעולה עם משוב ב-stderr. JSON {"continue": false, "stopReason": "..."} גם עוצר את חבר הצוות לגמרי, בהתאמה להתנהגות hook של Stop. TaskCompleted מתעלם מזה כשהכלי TaskUpdate הפעיל את האירוע
TaskCreatedקוד יציאה או decision ברמה העליונהקוד יציאה 2 או decision: "block" מבטל את המשימה ומחזיר את ההודעה ל-Claude. מתעלמים מ-continue: false
PreToolUsehookSpecificOutputpermissionDecision (allow/deny/ask/defer), permissionDecisionReason
PreModelSwitchhookSpecificOutput או decision ברמה העליונהpermissionDecision (allow/deny/ask), permissionDecisionReason. decision: "block" גם מבטל את המעבר
PermissionRequesthookSpecificOutputdecision.behavior (allow/deny)
PermissionDeniedhookSpecificOutputretry: true אומר למודל שהוא רשאי לנסות שוב את קריאת הכלי שנדחתה. Claude Code מתעלם מזה בדחיות בלי פסק
WorktreeCreateהחזרת נתיבhook פקודה מדפיס נתיב ב-stdout. hook HTTP מחזיר hookSpecificOutput.worktreePath. כשל של hook או נתיב חסר מכשילים את היצירה
WorktreeRemoveקוד יציאהכל קוד יציאה שאינו אפס גורם להסרה להיכשל אם התיקייה עדיין קיימת אחר כך. פלט JSON מושלך
Elicitation, ElicitationResulthookSpecificOutput או decision ברמה העליונהaction (accept/decline/cancel), content (ערכי שדות טופס). decision: "block" גם דוחה
MessageDisplayhookSpecificOutputdisplayContent מחליף את הטקסט המוצג על המסך. תצוגה בלבד: התמליל ומה ש-Claude רואה שומרים את המקור
SessionStart, SubagentStart, PostModelSwitchהקשר בלבדhookSpecificOutput.additionalContext מוסיף הקשר ל-Claude. SessionStart מקבל גם initialUserMessage, watchPaths, sessionTitle ו-reloadSkills. אין חסימה או בקרת החלטה
Setup, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChangedאיןאין בקרת החלטה. משמש לתופעות לוואי כמו רישום או ניקוי

כמה אירועים יכולים גם לשכתב תוכן, ולא רק לאפשר או לחסום אותו:

למקרי שימוש של השחרה או המרה, יירט ב-PreToolUse לקלטי כלים יוצאים וב-PostToolUse לתוצאות כלים נכנסות.

הנה דוגמאות של כל תבנית בפעולה:

החלטה ברמה העליונה

הערך היחיד של decision הוא "block". כדי לאפשר לפעולה להמשיך, השמט את decision מה-JSON, או צא עם 0 בלי JSON בכלל:

{
  "decision": "block",
  "reason": "Test suite must pass before proceeding"
}

PreToolUse

משתמש ב-hookSpecificOutput לשליטה עשירה יותר: לאפשר, לדחות, או להסלים למשתמש. אפשר גם לשנות קלט של כלי לפני שהוא רץ, או להזריק הקשר נוסף ל-Claude. ראה בקרת החלטה של PreToolUse לסט האפשרויות המלא.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database writes are not allowed"
  }
}

PermissionRequest

משתמש ב-hookSpecificOutput כדי לאפשר או לדחות בקשת הרשאה בשם המשתמש. כשמאפשרים, אפשר גם לשנות את הקלט של הכלי או להחיל כללי הרשאה כדי שהמשתמש לא יתבקש שוב. ראה בקרת החלטה של PermissionRequest לסט האפשרויות המלא.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

לדוגמאות מורחבות שכוללות אימות פקודות Bash, סינון הנחיות, וסקריפטים לאישור אוטומטי, ראה מה אפשר לאוטמט במדריך, ואת מימוש הייחוס למאמת פקודות Bash.

#אירועי hook

כל אירוע מתאים לנקודה במחזור החיים של Claude Code שבה hooks יכולים לרוץ. הסעיפים למטה מסודרים כך שיתאימו למחזור החיים: מהגדרת הסשן, דרך לולאת הסוכן, ועד סוף הסשן. כל סעיף מתאר מתי האירוע נורה, אילו matchers הוא תומך בהם, את קלט ה-JSON שהוא מקבל, ואיך לשלוט בהתנהגות דרך הפלט.

#SessionStart

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

SessionStart רץ בכל סשן, לכן שמרו על ההוקים האלה מהירים. נתמכים רק הוקים מסוג type: "command" ו-type: "mcp_tool". ראו שדות הוק של כלי MCP לגבי מתי הוקים מסוג mcp_tool רצים.

ערך ה-matcher תואם לאופן שבו הסשן הופעל:

Matcherמתי הוא נורה
startupסשן חדש
resume--resume, --continue, או /resume
clear/clear
compactדחיסה אוטומטית או ידנית
forkסשן חדש שפוצל מסשן קיים: --fork-session עם --resume או --continue, העותק ברקע של /fork, /branch, או שיחה שמעבירים לרקע

כאשר מתחילים סשן אינטראקטיבי, ממשיכים שיחה בהפעלה עם --continue או --resume, או מריצים /clear, הוקים של SessionStart רצים ברקע. אפשר להקליד מיד, ושיחה שהמשכתם מופיעה בלי להמתין להוקים. התשובה הראשונה של Claude עדיין ממתינה שההוקים יסתיימו, כדי שההקשר שלהם יגיע אל Claude.

כאשר מחליפים שיחות עם /resume בתוך סשן, ההחלפה ממתינה שההוקים יסתיימו. אם מריצים /clear או עוברים לשיחה אחרת בזמן שהוקים ברקע עדיין רצים, שום דבר שהם מחזירים לא חל על הסשן.

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

במהלך כל אחת מההמתנות, לחצו Esc כדי להחזיר את הפרומפט לקלט בלי לשלוח אותו. ההוקים ממשיכים לרוץ.

#קלט של SessionStart

בנוסף לשדות הקלט המשותפים, הוקים של SessionStart מקבלים את source ואופציונלית את model, agent_type, ו-session_title:

שדהתיאור
sourceאיך הסשן התחיל: "startup" לסשנים חדשים, "resume" לסשנים שהומשכו, "clear" אחרי /clear, "compact" אחרי דחיסה, או "fork" לסשן חדש שפוצל מסשן קיים
modelמזהה המודל הפעיל. הוא יכול להיעדר, למשל אחרי /clear או כאשר סשן משוחזר דרך שחזור שיחה, לכן בדקו שהשדה קיים לפני שקוראים אותו
agent_typeשם הסוכן, נוכח כאשר מתחילים את Claude Code עם claude --agent <name>
session_titleהכותרת המותאמת של הסשן, נוכחת כאשר הוגדרה אחת, למשל עם --name, /rename, פלט sessionTitle של הוק, או renameSession() של Agent SDK. הוק שפולט sessionTitle יכול לבדוק את השדה הזה קודם כדי להימנע מדריסת כותרת מותאמת קיימת

סשן שלא נתתם לו שם עדיין יכול לכלול כותרת שנוצרה. הכותרת הזו אינה כותרת מותאמת ואינה מופיעה ב-session_title.

כאשר source הוא "resume" או "fork" והתמליל מכיל לפחות תשובה אחת מ-Claude, הוקים של SessionStart מקבלים גם את ארבעת השדות למטה. ההוק יכול להשתמש בהם כדי לדווח מה עולה המשך של שיחה ישנה לפני הבקשה הראשונה, למשל ב-systemMessage. השדות האלה דורשים Claude Code גרסה v2.1.251 או מאוחרת יותר.

שדהתיאור
seconds_since_last_responseשניות שעון קיר מאז התשובה האחרונה בתמליל שהומשך
context_tokensטוקנים שהבקשה הראשונה של הסשן שהומשך שולחת מחדש כפרומפט שלה
prompt_cache_likely_expiredtrue כאשר התשובה האחרונה ישנה יותר ממשך החיים של מטמון הפרומפט של הסשן, או שדחיסה מאוחרת יותר החליפה את השיחה שנשמרה במטמון
estimated_cache_write_usdעלות משוערת בדולר ארה"ב של כתיבת context_tokens למטמון הפרומפט במודל של הסשן, בלי התשובה

הדוגמה הזו מציגה את הקלט לסשן שהומשך 90 דקות אחרי התשובה האחרונה שלו:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionStart",
  "source": "resume",
  "model": "claude-opus-5",
  "seconds_since_last_response": 5400,
  "context_tokens": 182340,
  "prompt_cache_likely_expired": true,
  "estimated_cache_write_usd": 1.1396
}

#בקרת החלטה של SessionStart

הוק של SessionStart יכול להוסיף הקשר עבור Claude, לספק את הודעת המשתמש הראשונה, להגדיר את כותרת הסשן, לעקוב אחרי קבצים, ולטעון מחדש skills. החזירו את השדה של כל אחד, בנוסף לשדות פלט JSON הזמינים לכל ההוקים:

שדהתיאור
additionalContextמחרוזת שמתווספת להקשר של Claude בתחילת השיחה, לפני הפרומפט הראשון. ראו הוספת הקשר עבור Claude לגבי אופן מסירת הטקסט ומה לשים בו
initialUserMessageמחרוזת שמשמשת כהודעת המשתמש הראשונה של הסשן, במצב לא אינטראקטיבי עם הדגל -p. היא הופכת לתור הראשון גם אם לא מעבירים פרומפט. פרומפט שכן מעבירים בא אחריה כתור הבא
sessionTitleמגדיר את כותרת הסשן, עם אותו אפקט כמו /rename. חל כאשר source הוא "startup", "resume", או "fork"
watchPathsמערך של נתיבים מוחלטים למעקב אחרי אירועי FileChanged במהלך הסשן הזה
reloadSkillsבוליאני. כאשר true, Claude Code סורק מחדש את ספריות ה-skill והפקודות אחרי שהוקים של SessionStart מסתיימים. ראו טעינה מחדש של skills שהוק מתקין

הפלט הזה מוסיף הקשר ונותן שם לסשן:

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
    "sessionTitle": "auth-refactor"
  }
}

הוק שרק מוסיף הקשר יכול להדפיס אותו בלי לבנות JSON, כי Claude Code מוסיף את stdout בטקסט רגיל של הוק SessionStart להקשר של Claude.

אם הוק SessionStart של התוסף מספק initialUserMessage או sessionTitle, התקינו את התוסף לפני שהסשן מתחיל. Claude Code מתעלם משני השדות מתוסף שמסיים להתקין אחרי שהוקים של SessionStart כבר רצו.

#טעינה מחדש של skills שהוק מתקין

כדי ש-skills שהוק SessionStart מתקין יהיו זמינים באותו סשן, החזירו reloadSkills. גילוי skills רץ בדרך כלל לפני שהוקים של SessionStart מסתיימים, ולכן בלעדיו, קבצים שהוק כותב אל ~/.claude/skills/ או .claude/skills/ עלולים להיעדר כאשר הפרומפט הראשון רץ.

הדוגמה הזו מסנכרנת מאגר skills משותף ומבקשת את הסריקה מחדש:

#!/bin/bash

git -C ~/.claude/skills/team-skills pull --quiet 2>/dev/null || \
  git clone --quiet https://git.example.com/your-org/team-skills.git ~/.claude/skills/team-skills

echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'

כתובת ה-URL של המאגר היא מציין מקום. החליפו אותה במאגר ה-skills שלכם.

#שמירת משתני סביבה

להוקים של SessionStart יש גישה למשתנה הסביבה CLAUDE_ENV_FILE, שמספק נתיב קובץ שבו אפשר לשמור משתני סביבה עבור פקודות המעטפת ש-Claude מריץ בהמשך הסשן.

כדי להגדיר משתני סביבה בודדים, כתבו הצהרות export אל CLAUDE_ENV_FILE. השתמשו בצירוף (>>) כדי לשמור משתנים שהוגדרו על ידי הוקים אחרים:

#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0

כדי ללכוד את כל שינויי הסביבה מפקודות ההכנה, השוו את המשתנים המיוצאים לפני ואחרי:

#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0

כל פקודת Bash מריצה את תוכן הקובץ כקוד מעטפת לפני הפקודה עצמה, ולכן שורה שם יכולה להשתמש בכל דבר ש-Bash מעריך, כמו ההפניה ל-$PATH ב-export PATH="$PATH:./node_modules/.bin".

#משתנים שנשמרו בפקודות PowerShell

פקודות PowerShell מקבלות גם הן את המשתנים מ-CLAUDE_ENV_FILE, ב-Claude Code גרסה v2.1.296 או מאוחרת יותר, אבל PowerShell אף פעם לא מריץ את הקובץ. Claude Code קורא מתוכו את ההשמות ומעתיק אותן לסביבה של פקודת PowerShell במקום זאת. הוא עושה זאת רק כאשר כל שורה היא אחת מהבאות, על פני מה שכל הוק בסשן הזה כתב וכל סקריפט שהגדרתם אליו את CLAUDE_ENV_FILE לפני ההפעלה:

  • שורה ריקה או הערת #
  • השמה אחת בתחילת השורה, כתובה export NAME=value, declare -x NAME=value, או NAME=value, עם ערך ש-Bash היה משתמש בו בדיוק כפי שנכתב, המורכב מכל שילוב של החלקים האלה: טקסט בלי מרכאות שמשתמש רק באותיות, ספרות, והתווים _ @ % + = : , . / -, טקסט במרכאות בודדות, וטקסט במרכאות כפולות שבו כל $, backtick, או " בפנים מוברח עם לוכסן הפוך

אם שורה כלשהי היא מסוג אחר, כמו export PATH="$PATH:./node_modules/.bin" עם $PATH הלא מוברח שלו, פקודת source, או מחרוזות $'...' ש-direnv export bash מדפיס, פקודות PowerShell לא מקבלות אף אחד מהמשתנים, ו-claude --debug רושם Session environment is not all plain assignments. פקודות Bash עדיין מקבלות את כולם. ב-Windows, פקודות PowerShell גם לא מקבלות משתנה שהערך שלו מכיל / או \, כי Git Bash ו-Windows כותבים נתיבים אחרת. פקודת PowerShell בארגז חול לא מקבלת אף אחד מהמשתנים.

[!NOTE]

CLAUDE_ENV_FILE זמין להוקים של SessionStart, Setup, CwdChanged, ו-FileChanged. לאירועי הוק אחרים אין גישה למשתנה הזה, וגם לא להוק שרץ ב-PowerShell, בין אם דרך "shell": "powershell" או כברירת מחדל ב-Windows בלי Git Bash.

#Setup

נורה רק כאשר מפעילים את Claude Code עם --init-only, או עם --init או --maintenance במצב לא אינטראקטיבי עם הדגל -p. הוא לא נורה בהפעלה רגילה. השתמשו בו להתקנת תלות חד פעמית או לניקוי מתוזמן שמפעילים במפורש מ-CI או מסקריפטים, בנפרד מהפעלת סשן רגילה. לאתחול לכל סשן, השתמשו ב-SessionStart במקום.

ערך ה-matcher תואם לדגל ה-CLI שהפעיל את ההוק:

Matcherמתי הוא נורה
initclaude --init-only או claude -p --init
maintenanceclaude -p --maintenance

כאשר מריצים claude --init-only, Claude Code מריץ הוקים של Setup והוקים של SessionStart עם ה-matcher startup, ואז יוצא בלי להתחיל שיחה.

כאשר מתחילים או ממשיכים שיחה עם -p, צריך גם לספק פרומפט, כארגומנט או מוזרם ב-stdin. אפשר לדלג על הפרומפט כאשר הוק SessionStart מספק initialUserMessage או כאשר ממשיכים סשן עם קריאת כלי דחויה.

בהצלחה, --init-only לא מדפיס כלום לטרמינל. כדי לאשר שההוקים רצו, התחילו עם claude --debug-file <path> --init-only, כשמחליפים את <path> במיקום קובץ לוג, ובדקו בלוג את רשומות ההוק של Setup ושל SessionStart.

מכיוון ש-Setup לא נורה בכל הפעלה, תוסף שזקוק לתלות מותקנת לא יכול להסתמך על Setup לבד. הדפוס המעשי הוא לבדוק את התלות בשימוש הראשון ולהתקין כשהיא חסרה, למשל הוק או skill שבודק את ${CLAUDE_PLUGIN_DATA}/node_modules ומריץ npm install אם הוא נעדר. ראו את ספריית הנתונים המתמידה לגבי איפה לאחסן תלויות מותקנות. אם מפיצים את התוסף דרך marketplace, ייתכן שלא תצטרכו את הדפוס הזה: Claude Code מתקין אוטומטית תלויות חבילות Node.js זכאיות כאשר הוא שומר את התוסף במטמון.

#קלט של Setup

בנוסף לשדות הקלט המשותפים, הוקים של Setup מקבלים שדה trigger שמוגדר ל-"init" או ל-"maintenance":

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Setup",
  "trigger": "init"
}

#בקרת החלטה של Setup

הוקים של Setup לא יכולים לחסום. הביצוע ממשיך בכל קוד יציאה. בכל קוד יציאה, Claude Code משליך את שדות פלט ה-JSON של הוק Setup, כמו systemMessage, continue, ו-hookSpecificOutput.additionalContext. עם -p, stdout, stderr, וקוד היציאה של הוק Setup מופיעים בפלט הריצה רק כאירועי hook_response כאשר מפעילים עם --output-format stream-json --verbose.

להוקים של Setup יש גישה ל-CLAUDE_ENV_FILE. אם ההוק כותב משתנים לקובץ הזה, הם נשמרים לפקודות Bash הבאות של הסשן, כמו ב-הוקים של SessionStart. רק הוקים מסוג type: "command" רצים ב-Setup. הוק מסוג type: "mcp_tool" על Setup תמיד מדולג, כמתואר תחת שדות הוק של כלי MCP.

#InstructionsLoaded

נורה כאשר קובץ CLAUDE.md או .claude/rules/*.md נטען להקשר. האירוע הזה נורה בתחילת הסשן עבור קבצים שנטענים מיד, ושוב מאוחר יותר כאשר קבצים נטענים בעצלתיים, למשל כאשר Claude ניגש לתת ספרייה שמכילה CLAUDE.md מקונן או כאשר כללים מותנים עם frontmatter של paths: תואמים. ההוק לא תומך בחסימה או בבקרת החלטה. הוא רץ באופן אסינכרוני למטרות תצפית.

האירוע הזה לא נורה כאשר Claude קורא את AGENTS.md ישירות דרך ההגדרה הוראות הפרויקט. הוא כן נורה כאשר CLAUDE.md מייבא את AGENTS.md, עם load_reason שמוגדר ל-include כמו לכל קובץ מיובא אחר, וכאשר CLAUDE.md הוא symlink אליו, כטעינה רגילה של CLAUDE.md.

ה-matcher רץ מול load_reason. למשל, השתמשו ב-"matcher": "session_start" כדי להיירות רק עבור קבצים שנטענו בתחילת הסשן, או ב-"matcher": "path_glob_match|nested_traversal" כדי להיירות רק עבור טעינות עצלות.

#קלט של InstructionsLoaded

בנוסף לשדות הקלט המשותפים, הוקים של InstructionsLoaded מקבלים את השדות האלה:

שדהתיאור
file_pathנתיב מוחלט לקובץ ההוראות שנטען
memory_typeהיקף הקובץ: "User", "Project", "Local", או "Managed"
load_reasonלמה הקובץ נטען: "session_start", "nested_traversal", "path_glob_match", "include", או "compact". הערך "compact" נורה כאשר קובצי הוראות נטענים מחדש אחרי אירוע דחיסה
globsתבניות glob של נתיבים מ-frontmatter של paths: בקובץ, אם יש. נוכח רק עבור טעינות path_glob_match
trigger_file_pathנתיב לקובץ שהגישה אליו הפעילה את הטעינה הזו, עבור טעינות עצלות
parent_file_pathנתיב לקובץ ההוראות ההורה שכלל את זה, עבור טעינות include
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "InstructionsLoaded",
  "file_path": "/Users/my-project/CLAUDE.md",
  "memory_type": "Project",
  "load_reason": "session_start"
}

#בקרת החלטה של InstructionsLoaded

להוקים של InstructionsLoaded אין בקרת החלטה. הם לא יכולים לחסום או לשנות טעינת הוראות. Claude Code משליך את שדות פלט ה-JSON שלהם, כמו systemMessage ו-continue. השתמשו באירוע הזה לרישום ביקורת, מעקב תאימות, או תצפית.

#UserPromptSubmit

רץ כאשר נשלח פרומפט, לפני ש-Claude מעבד אותו. זה מאפשר להוסיף הקשר נוסף לפי הפרומפט או השיחה, לאמת פרומפטים, או לחסום סוגים מסוימים של פרומפטים.

הוקי UserPromptSubmit לא מופעלים רק על פרומפטים שאתה מקליד. Claude Code מריץ אותם גם על:

להוקי UserPromptSubmit יש timeout ברירת מחדל של 30 שניות לסוגים command, http ו-mcp_tool, קצר יותר מברירת המחדל של 600 שניות לסוגים האלה ברוב האירועים האחרים. מכיוון שההוק הזה רץ לפני כל פרומפט וחוסם את עיבוד המודל עד שהוא מסתיים, הוק תקוע עוצר את הסשן. אם ההוק שלך צריך יותר זמן, הגדר את השדה timeout ברשומת ההוק.

מלבד הוק פקודה שאתה מריץ עם async: true, הוק UserPromptSubmit מסוג command, HTTP או כלי MCP שמגיע ל-timeout שלו מבוטל, והפלט שלו, כולל כל additionalContext, נזרק. הפרומפט עדיין מגיע ל-Claude בלי ההקשר הזה. כדי לחסום את הפרומפט במקום זה, הגדר onFailure: "block" על הוק command או HTTP. התמליל מציג הודעה שמציינת את ההוק, את ה-timeout שהופעל, ושהפלט נזרק.

הוק callback של Agent SDK על UserPromptSubmit שמגיע ל-timeout שלו חוסם את הפרומפט עם הודעה שמציינת את ההוק ואת ה-timeout, כי callback שם יכול לפעול כשער מדיניות שאסור שייכשל פתוח. הסשן ממשיך.

#Esc במהלך בדיקת פרומפט

אם אתה לוחץ על Esc או קוטע את הסשן בזמן שהוקים עדיין בודקים פרומפט, הבדיקה נעצרת והפרומפט לא מגיע ל-Claude. טקסט שהקלדת בטרמינל חוזר לשדה קלט הפרומפט, והוקים בודקים אותו שוב כשאתה שולח אותו מחדש.

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

#קלט UserPromptSubmit

בנוסף לשדות הקלט המשותפים, הוקי UserPromptSubmit מקבלים את השדה prompt שמכיל את הטקסט שנשלח. תוכן שהודבק וכווץ למציין מיקום [Pasted text #N] מגיע מורחב במקום. בסשנים שבהם Claude Code מסמן טקסט שהודבק עבור Claude, התוכן המורחב יושב בין שורה <pasted_content id="…"> לשורה </pasted_content id="…">, אז התחשב בשורות האלה אם ההוק שלך מנתח את הפרומפט.

הוקי UserPromptSubmit מקבלים גם session_title כאשר לסשן יש כותרת מותאמת, עם אותה משמעות כמו שדה session_title של SessionStart.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

כאשר הפרומפט הוא skill או פקודה מותאמת שהקלדת, כמו /deploy staging, ההוק מקבל את השורה כפי שהקלדת אותה ולא את תוכן ה-skill המורחב. אם ההוק חוסם את הפרומפט, הפקודה לא רצה, ולכן גם פקודות מעטפת שה-skill מזריק עם !`<command>` לא רצות. כדי להתאים לפי שם הפקודה או הארגומנטים במקום זה, השתמש ב-UserPromptExpansion.

#בקרת החלטה של UserPromptSubmit

הוקי UserPromptSubmit יכולים לשלוט אם פרומפט שנשלח מעובד ולהוסיף הקשר. כל שדות הפלט ב-JSON זמינים.

יש שתי דרכים להוסיף הקשר לשיחה בקוד יציאה 0:

  • stdout בטקסט רגיל: Claude Code מוסיף stdout שהוא מתייחס אליו כטקסט רגיל להקשר של Claude
  • JSON עם additionalContext: השתמש בפורמט ה-JSON למטה לשליטה רבה יותר. השדה additionalContext מתווסף כהקשר

אף אחד מהערוצים לא מייצר רשומת תמליל גלויה. stdout רגיל וערך additionalContext מוזרקים כל אחד כתזכורת מערכת שמתחילה בשם ההוק. Claude קורא את שניהם. כדי לאשר מסירה, בדוק את יומן הדיבוג.

כדי לחסום פרומפט, החזר אובייקט JSON עם decision שמוגדר ל-"block":

שדהתיאור
decision"block" עוצר את הפרומפט לפני שהוא מגיע ל-Claude. השמט כדי לאפשר לפרומפט להמשיך
reasonמוצג למשתמש כאשר decision הוא "block". לא מתווסף להקשר
additionalContextמחרוזת שמתווספת להקשר של Claude לצד הפרומפט שנשלח. ראה הוספת הקשר עבור Claude
sessionTitleמגדיר את כותרת הסשן. השתמש כדי לתת שם לסשנים אוטומטית לפי תוכן הפרומפט
suppressOriginalPromptאם true כאשר ההוק חוסם את הפרומפט, משאיר את טקסט הפרומפט מחוץ להודעת החסימה. ראה מה פרומפט חסום משאיר אחריו

הוק שחוסם על ידי יציאה 2 מנותב באותה דרך כמו reason: הודעת החסימה מציגה למשתמש את טקסט ה-stderr, והוא לא מתווסף להקשר.

{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here",
    "sessionTitle": "My session title",
    "suppressOriginalPrompt": true
  }
}

#מה פרומפט חסום משאיר אחריו

פרומפט חסום אף פעם לא מגיע ל-Claude, אבל הטקסט שלו לא מוסר מכל מקום. כברירת מחדל הודעת החסימה שמוצגת למשתמש מסתיימת ב-Original prompt: ואחריו הטקסט שנשלח, ו-Claude Code כותב את ההודעה הזו לקובץ התמליל של הסשן בדיסק. כדי להשאיר את הטקסט מחוץ להודעה, הדפס JSON עם "suppressOriginalPrompt": true בתוך hookSpecificOutput. זה עובד בין אם ההוק חוסם עם decision: "block" ובין אם על ידי יציאה 2.

suppressOriginalPrompt משנה רק את הודעת החסימה. הטקסט שנשלח עדיין יכול להופיע בקבצים מקומיים כמו תמליל הסשן והיסטוריית הפרומפטים שלך, אז הוק חוסם אינו דרך לשמור סוד מחוץ לדיסק. כדי להגביל או להסיר את הקבצים האלה, ראה אחסון בטקסט גלוי ו-ניקוי נתונים מקומיים.

#UserPromptExpansion

רץ כאשר פקודה שהמשתמש הקליד מתרחבת לפרומפט לפני שהיא מגיעה ל-Claude. השתמש בזה כדי לחסום פקודות מסוימות מהפעלה ישירה, להזריק הקשר ל-skill מסוים, או לרשום אילו פקודות משתמשים מפעילים. לדוגמה, הוק שמתאים ל-deploy יכול לחסום את /deploy אלא אם קיים קובץ אישור, או הוק שמתאים ל-skill של סקירה יכול לצרף את רשימת הבדיקה של הצוות כ-additionalContext.

האירוע הזה מכסה את הנתיב ש-PreToolUse לא מכסה: הוק PreToolUse שמתאים לכלי Skill מופעל רק כאשר Claude קורא לכלי, אבל הקלדת /skillname ישירות עוקפת את PreToolUse. UserPromptExpansion מופעל בנתיב הישיר הזה.

מתאים לפי command_name. השאר את ה-matcher ריק כדי להפעיל על כל פקודה מסוג פרומפט.

#קלט UserPromptExpansion

בנוסף לשדות הקלט המשותפים, הוקי UserPromptExpansion מקבלים את expansion_type, command_name, command_args, command_source, ואת מחרוזת ה-prompt המקורית. השדה expansion_type הוא slash_command עבור skill ופקודות מותאמות, או mcp_prompt עבור פרומפטים של שרת MCP.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../00893aaf.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptExpansion",
  "expansion_type": "slash_command",
  "command_name": "example-skill",
  "command_args": "arg1 arg2",
  "command_source": "plugin",
  "prompt": "/example-skill arg1 arg2"
}

#בקרת החלטה של UserPromptExpansion

הוקי UserPromptExpansion יכולים לחסום את ההרחבה או להוסיף הקשר. כל שדות הפלט ב-JSON זמינים.

שדהתיאור
decision"block" מונע מהפקודה להתרחב. השמט כדי לאפשר לה להמשיך
reasonמוצג למשתמש כאשר decision הוא "block"
additionalContextמחרוזת שמתווספת להקשר של Claude לצד הפרומפט המורחב. ראה הוספת הקשר עבור Claude

הוק שחוסם על ידי יציאה 2 מנותב באותה דרך כמו reason: הודעת החסימה מציגה למשתמש את טקסט ה-stderr.

{
  "decision": "block",
  "reason": "This slash command is not available",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptExpansion",
    "additionalContext": "Additional context for this expansion"
  }
}

#MessageDisplay

רץ בזמן שהודעת assistant מוזרמת למסך. Claude Code מציג את ההודעה במנות: בכל פעם שאצווה של שורות שהושלמו מוכנה לעיבוד, ההוק רץ פעם אחת עם השורות האלה ו-Claude Code מציג במקומן את טקסט ההחלפה של ההוק. הודעה ארוכה מייצרת כמה קריאות. הודעה קצרה עשויה לייצר רק אחת.

השתמש ב-MessageDisplay כדי:

  • להסיר markdown לתצוגה מינימלית
  • לשנות את הטקסט שאפליקציית Agent SDK מציגה למשתמשים שלה
  • להסתיר מפתחות API או שמות מארחים פנימיים מתשובות של Claude

Claude Code מחזיק כל אצווה עד שההוק שלך חוזר, אז שמור על ההוק מהיר. אם ההוק נכשל או מגיע ל-timeout, Claude Code מציג את הטקסט המקורי. ה-timeout ברירת המחדל לאירוע הזה הוא 10 שניות. אם ההוק שלך צריך יותר זמן, הגדר את השדה timeout ברשומת ההוק.

MessageDisplay הוא לתצוגה בלבד: טקסט ההחלפה משנה רק את מה שמוצג על המסך. התמליל ומה ש-Claude רואה שומרים את הטקסט המקורי, כך ש-Claude אף פעם לא רואה את ההחלפה, ומצב verbose מציג את המקור. ההוק מקבל רק טקסט של הודעת assistant, כך שתוצאות כלים והטקסט שאתה מקליד מוצגים ללא שינוי.

MessageDisplay לא תומך ב-matchers ומופעל על כל הודעת assistant שמזרימה טקסט. הודעות בלי טקסט, כמו תשובות של קריאת כלי בלבד, לא מפעילות אותו.

בהרצות לא אינטראקטיביות, כולל שאילתות Agent SDK ו-claude -p, MessageDisplay רץ פעם אחת לכל הודעת assistant במקום פעם אחת לכל אצוות שורות. הקריאה היחידה מגיעה אחרי שההודעה הושלמה ונושאת את טקסט ההודעה המלא: index הוא 0, final הוא true, ו-delta מחזיק את כל ההודעה. הוק שאוסף את טקסט ה-delta לכל הודעה מקבל את אותו סך טקסט בשני המצבים.

#קלט MessageDisplay

בנוסף לשדות הקלט המשותפים, הוקי MessageDisplay מקבלים מזהים לתור ולהודעה, את מיקום הקריאה הזו בתוך ההודעה, ואת הטקסט החדש ב-delta. גבולות האצווה תלויים באופן שבו הטקסט מוזרם, אז השתמש ב-index וב-final כדי לעקוב אחרי ההתקדמות בהודעה במקום לצפות שהשורות יקובצו בצורה מסוימת.

שדהתיאור
turn_idUUID של התור הנוכחי
message_idUUID של הודעת ה-assistant שמוצגת. יציב בכל אצווה של אותה הודעה. זה אינו מזהה ה-API msg_…, ולכן אי אפשר לקשר אותו למזהי הודעות בתמליל
indexאינדקס מבוסס אפס של האצווה הזו בתוך ההודעה
finaltrue באצווה האחרונה של ההודעה. לכל הודעה יש בדיוק אצווה סופית אחת
deltaהשורות שהושלמו מאז האצווה הקודמת, כולל ירידות שורה מסיימות. תמיד שורות שלמות, מלבד האצווה הסופית שעשויה להסתיים באמצע שורה. בהרצות אינטראקטיביות, ה-delta של האצווה הסופית ריק כאשר ההודעה מסתיימת בירידת שורה, אז התייחס ל-final, ולא ל-delta לא ריק, כאות לסיום ההודעה. בהרצות Agent SDK ו-claude -p, הקריאה היחידה נושאת את כל ההודעה
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "MessageDisplay",
  "turn_id": "0c9e6a2f-7d41-4f4e-9a15-3f4f7c2b8d10",
  "message_id": "5b2a9c8e-1f63-4d8a-b7c4-9e0d2a6f1c3b",
  "index": 0,
  "final": false,
  "delta": "Here is the plan:\n"
}

#פלט MessageDisplay

בנוסף לשדות הפלט ב-JSON הזמינים לכל ההוקים, הוקי MessageDisplay יכולים להחזיר displayContent כדי להחליף את ה-delta על המסך:

שדהתיאור
displayContentטקסט שמוצג במקום ה-delta. השמט אותו כדי להציג את המקור

להוקי MessageDisplay אין בקרת החלטה. הם לא יכולים לחסום את ההודעה או לשנות את מה שנשמר בתמליל או נשלח ל-Claude. Claude Code פועל לפי displayContent מפלט ה-JSON שלהם וזורק את systemMessage ואת continue.

הדוגמה הזו מסירה עיצוב markdown מתשובות של Claude לתצוגת טקסט רגיל. הסקריפט קורא כל אצווה מ-stdin, מסיר סמני הדגשה וגרשיים הפוכים של קוד בתוך השורה מ-delta, ומחזיר את התוצאה כ-displayContent.

macOS/Linux

רשום הוק פקודה לאירוע בקובץ ההגדרות שלך:

{
  "hooks": {
    "MessageDisplay": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

שמור את הסקריפט הזה ב-.claude/hooks/plain-display.sh בפרויקט שלך והפוך אותו לניתן להרצה עם chmod +x:

#!/bin/bash
jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'

Windows (PowerShell)

רשום הוק פקודה שמריץ את הסקריפט דרך PowerShell:

{
  "hooks": {
    "MessageDisplay": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.ps1"
            ]
          }
        ]
      }
    ]
  }
}

הדגל -NoProfile מדלג על טעינת פרופיל PowerShell שלך כדי שההוק יתחיל מהר, ו--ExecutionPolicy Bypass מאפשר ל-PowerShell להריץ את קובץ הסקריפט המקומי.

שמור את הסקריפט הזה ב-.claude/hooks/plain-display.ps1 בפרויקט שלך:

$batch = [Console]::In.ReadToEnd() | ConvertFrom-Json
$text = $batch.delta -replace '\*\*', '' -replace '`', ''
@{
  hookSpecificOutput = @{
    hookEventName = "MessageDisplay"
    displayContent = $text
  }
} | ConvertTo-Json

אצוות בלי markdown עוברות ללא שינוי. אם הסקריפט נכשל, למשל כי jq חסר, Claude Code מציג את הטקסט המקורי ומציין את הכשל רק בפלט דיבוג, לא בסשן.

#PreToolUse

רץ אחרי ש-Claude יוצר פרמטרים של כלי ולפני עיבוד קריאת הכלי. מתאים לכל שם כלי מלבד EndConversation: כלים מובנים כמו Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion ו-ExitPlanMode, וכל שמות כלי MCP.

כדי להריץ הוק כאשר קובץ מסוים משתנה בדיסק, לא משנה מי כתב אותו, השתמש ב-FileChanged במקום להתאים כלי עריכת קבצים לפי שם. בניגוד ל-PreToolUse, Claude Code מריץ הוקי FileChanged אחרי השינוי, ואין להם בקרת החלטה, אז הם לא יכולים לחסום את הכתיבה.

[!WARNING]

PreToolUse רץ רק כאשר Claude קורא לכלי. כברירת מחדל, קבצים שאתה מפנה אליהם עם @ בפרומפט מתווספים בלי שום קריאת כלי. Claude Code מכניס את התוכן שלהם בזמן בניית הפרומפט, אז שום הוק PreToolUse לא מופעל עבורם, כולל הוקים שמתאימים ל-Read. PreToolUse גם לא מופעל עבור EndConversation.

אתה יכול לחסום נתיבים מסוימים מהפניות @, או לכבות את ההכנסה:

  • חסימת נתיבים מסוימים: הוסף עבורם כלל דחייה של Read
  • כיבוי ההכנסה: הגדר CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 במעטפת, בהגדרות המשתמש, או בהגדרות מנוהלות. אז Claude מקבל את הנתיבים שצוינו עם הערה לקרוא אותם באמצעות כלי הקבצים שלו. אם הוא עושה זאת, הוקי PreToolUse המתאימים שלך רצים. המשתנה דורש Claude Code v2.1.299 או חדש יותר, ו-Claude Code מתעלם ממנו בהגדרות פרויקט והגדרות מקומיות.

השתמש ב-בקרת החלטה של PreToolUse כדי לאפשר, לדחות, לשאול, או לדחות לזמן מאוחר יותר את קריאת הכלי.

הוק callback של Agent SDK על PreToolUse שחורג מה-timeout שלו חוסם את קריאת הכלי.

#קלט של PreToolUse

בנוסף לשדות הקלט המשותפים, הוקים של PreToolUse מקבלים את tool_name, tool_input ואת tool_use_id.

עבור כלי MCP, הקלט נושא גם את mcp_server, אובייקט עם name של השרת וsource שאומר מאיפה הגיעה הגדרת השרת. ערכי source כוללים את plugin, sdk והיקפי תצורה כגון user וproject. McpServerProvenance במדריך Agent SDK מפרט את כולם ואומר איך להתייחס לערך שלא מזהים. בססו החלטות אמון על source ולא על name או על קידומת שם הכלי mcp__<server>__. השדה mcp_server דורש Claude Code בגרסה v2.1.274 או חדשה יותר.

עבור כלי הקבצים Write, Edit וRead, tool_input.file_path תמיד מוחלט:

  • Claude Code מרחיב את ~ ונתיבים יחסיים לפני שההוקים רצים, כך שהוק שמתאים לפי נתיבים לא ניתן לעקיפה דרך ~ או איות יחסי של אותו נתיב
  • ב-Windows הנתיב מגיע עם מפרידי לוכסן הפוך, גם כשההוק רץ תחת Git Bash שבו $PWD נראה כמו /c/project
  • השוואה שנכתבה עם לוכסנים קדמיים, כגון בדיקת /src/, לעולם לא מתאימה לנתיב עם לוכסן הפוך, וקריאת הכלי ממשיכה כאילו להוק לא היה מה לחסום
  • נרמלו מפרידים לפני ההשוואה: FILE_PATH="${FILE_PATH//\\//}" ב-Bash, או file_path.replace("\\", "/") ב-Python, ואז התאימו מקטע נתיב כגון /src/ ולא עגינה עם ^, כי הנתיב מוחלט

קריאת Write ב-Windows מעבירה:

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "C:\\project\\src\\index.ts",
    "content": "..."
  },
  ...
}

שדות tool_input תלויים בכלי:

#Bash

מריץ פקודות מעטפת.

שדהסוגדוגמהתיאור
commandstring"npm test"פקודת המעטפת להרצה
descriptionstring"Run test suite"תיאור אופציונלי של מה שהפקודה עושה
timeoutnumber120000פסק זמן אופציונלי באלפיות שנייה. ערכים מעל המקסימום מופחתים למקסימום ולא נדחים
run_in_backgroundbooleanfalseהאם להריץ את הפקודה ברקע

כשפקודת Bash משנה קבצים במאגר Git, Claude Code יכול לתעד מה השתנה. הוא מתעד את השינויים בכל מצב הרשאה כשההגדרה bashEditDiffEnabled מפעילה את התיעוד. הרשומה של ההגדרה אומרת אילו קבצים יכולים להגדיר אותה. אחרת הוא מתעד אותם רק במצב auto ובמצב bypassPermissions, ורק כש-Claude Code מכוון את Claude לערוך קבצים דרך Bash. הגדירו את bashEditDiffEnabled לfalse כדי לכבות את התיעוד. פקודות רקע ופקודות לקריאה בלבד לא נושאות diff.

הוק PostToolUse שלכם מקבל אז את הקבצים שהשתנו בtool_response.bashEditDiff. הרשימה מכסה מה השתנה תחת המאגר בזמן שהפקודה רצה. קבצים ש-Git מתעלם מהם וקבצים בתת מודולים לא מופיעים ברשימה. דורש Claude Code בגרסה v2.1.269 או חדשה יותר.

[!NOTE]

הרשימה היא best effort ובבטא ציבורית. Claude Code יכול לפספס שינוי, לכלול קובץ שתהליך אחר שינה באותו זמן, או לעצור במגבלות הגודל שלו. צורת השדה עשויה להשתנות. השתמשו ברשימה כדי למצוא מה לבדוק, לא כדי לאכוף מדיניות.

changedFiles וfiles מפרטים מה הפקודה שינתה. שאר השדות אומרים עד כמה הרשימה מלאה ועד כמה היא אמינה.

שדהסוגדוגמהתיאור
changedFilesarray["/path/to/src/app.ts"]נתיבים מוחלטים של הקבצים שהפקודה שינתה, לכל היותר 200. נוכח בכל פעם שfiles מחזיק diff או שmoreFiles מעל אפס
filesarray[{"filePath": "/path/to/src/app.ts", "hunks": [...]}]diffs של עד 5 קבצים שהשתנו, להצגה. created או deleted הוא true עבור קובץ שהפקודה הוסיפה או הסירה
moreFilesnumber2מספר הקבצים שהשתנו בלי diff בfiles
unavailablebooleantrueמוגדר כשה-diff לא שלם או שלא ניתן היה לקחת אותו
skippedbooleantrueמוגדר עבור פקודת Git שמזיזה את עץ העבודה, כגון git checkout או git stash, כך ש-Claude Code לא לוקח diff
sharedbooleantrueמוגדר כשקריאת כלי Bash אחרת, כגון של subagent, רצה באותו מאגר באותו זמן, כך שחלק מהשינויים הרשומים עשויים להיות של הפקודה ההיא
#PowerShell

מריץ פקודות PowerShell. ראו את כלי PowerShell לזמינות לפי פלטפורמה.

השדות תואמים לכלי Bash, עם מחרוזת הפקודה בcommand:

שדהסוגדוגמהתיאור
commandstring"Get-ChildItem -Recurse"פקודת PowerShell להרצה
descriptionstring"List files recursively"תיאור אופציונלי של מה שהפקודה עושה
timeoutnumber120000פסק זמן אופציונלי באלפיות שנייה
run_in_backgroundbooleanfalseהאם להריץ את הפקודה ברקע

התאימו את Bash|PowerShell בהוקים שבודקים פקודות מעטפת, כדי שיכסו את שני הכלים:

  • ב-Windows, בכל מקום שבו כלי PowerShell מופעל, Claude מתייחס ל-PowerShell כמעטפת הראשית ומנתב פקודות מעטפת דרכו.
  • ב-Windows בלי Git Bash, הכלי מופעל אוטומטית ו-Claude Code לא רושם את כלי Bash בכלל.
  • הוק שמתאים רק לBash לעולם לא מופעל שם.
#Write

יוצר קובץ או דורס אותו.

שדהסוגדוגמהתיאור
file_pathstring"/path/to/file.txt"נתיב מוחלט לקובץ לכתיבה
contentstring"file content"תוכן לכתיבה לקובץ
#Edit

מחליף מחרוזת בקובץ קיים.

שדהסוגדוגמהתיאור
file_pathstring"/path/to/file.txt"נתיב מוחלט לקובץ לעריכה
old_stringstring"original text"טקסט לאיתור ולהחלפה
new_stringstring"replacement text"טקסט חלופי
replace_allbooleanfalseהאם להחליף את כל המופעים
#Read

קורא תוכן קובץ.

שדהסוגדוגמהתיאור
file_pathstring"/path/to/file.txt"נתיב מוחלט לקובץ לקריאה
offsetnumber10מספר שורה אופציונלי שממנו להתחיל לקרוא
limitnumber50מספר שורות אופציונלי לקריאה
#Glob

מוצא קבצים שתואמים לתבנית glob.

שדהסוגדוגמהתיאור
patternstring"**/*.ts"תבנית glob להתאמת קבצים
pathstring"/path/to/dir"תיקייה אופציונלית לחיפוש בתוכה. ברירת המחדל היא תיקיית העבודה הנוכחית
#Grep

מחפש בתוכן קבצים עם ביטויים רגולריים.

שדהסוגדוגמהתיאור
patternstring"TODO.*fix"תבנית ביטוי רגולרי לחיפוש
pathstring"/path/to/dir"קובץ או תיקייה אופציונליים לחיפוש בתוכם
globstring"*.ts"תבנית glob אופציונלית לסינון קבצים
output_modestring"content""content", "files_with_matches" או "count". ברירת המחדל היא "files_with_matches"
-ibooleantrueחיפוש בלי תלות ברישיות
multilinebooleanfalseהפעלת התאמה מרובת שורות
#WebFetch

מביא ומעבד תוכן מהאינטרנט.

שדהסוגדוגמהתיאור
urlstring"https://example.com/api"כתובת URL להבאת תוכן ממנה
promptstring"Extract the API endpoints"הנחיה להרצה על התוכן שהובא
offsetnumber100000מספר תווים אופציונלי לדילוג מתחילת העמוד. Claude מגדיר אותו כדי להמשיך לקרוא עמוד ארוך. דורש Claude Code בגרסה v2.1.290 או חדשה יותר
#WebSearch

מחפש באינטרנט.

שדהסוגדוגמהתיאור
querystring"react hooks best practices"שאילתת חיפוש
allowed_domainsarray["docs.example.com"]אופציונלי: לכלול תוצאות רק מהדומיינים האלה
blocked_domainsarray["spam.example.com"]אופציונלי: להחריג תוצאות מהדומיינים האלה
modestring"extended"אופציונלי: סוג החיפוש. "standard" מבקש חיפוש קל יותר ו"extended" את החיפוש המלא. חלק מההפעלות לא שולחות mode, וקריאה בלעדיו מריצה את החיפוש המלא. דורש Claude Code בגרסה v2.1.282 או חדשה יותר
#Agent

מפעיל subagent.

שדהסוגדוגמהתיאור
promptstring"Find all API endpoints"המשימה שהסוכן יבצע
descriptionstring"Find API endpoints"תיאור קצר של המשימה
subagent_typestring"Explore"סוג הסוכן המתמחה לשימוש
modelstring"sonnet"כינוי מודל אופציונלי לדריסת ברירת המחדל

כשקריאת Agent בחזית מסתיימת, הוק PostToolUse שלכם מקבל את תוצאת ה-subagent ואת טלמטריית הריצה בtool_response. קראו את השדות האלה כדי לבדוק את הריצה. עבור סיכומי טוקנים ועלות בין subagents, השתמשו במוני הטוקנים והעלות מסוננים לquery_source "subagent", כי totalTokens וusage מכסים רק את הבקשה הסופית:

שדהסוגדוגמהתיאור
statusstring"completed""completed" עבור subagents בחזית, "async_launched" עבור subagents ברקע. עבור אילו subagents רצים בכל דרך, ראו Run subagents in foreground or background
agentIdstring"a4d2c8f1e0b3a297"מזהה לריצת ה-subagent
contentarray[{"type": "text", "text": "Found 12 endpoints..."}]בלוקי הטקסט הסופיים של ה-subagent, או, עבור subagent שהדוח שלו עובר דרך SubagentHandback, הערה קצרה על המסירה הזו במקומם
resolvedModelstring"claude-sonnet-4-5"המודל שה-subagent התחיל עליו, שעשוי להיות שונה מהמודל שהתבקש
modelsUsedarray["claude-sonnet-4-5", "claude-haiku-4-5"]מודלים ששימשו לפי הסדר, כשחזרות רצופות מכווצות. מוגדר רק כשהמודל הוחלף באמצע הריצה
totalTokensnumber12450ספירת טוקנים מבקשת ה-API הסופית של ה-subagent: קלט, פלט וטוקני מטמון יחד. זה לא סך הכל על פני כל הריצה
totalDurationMsnumber48211משך שעון הקיר של ריצת ה-subagent
totalToolUseCountnumber7מספר קריאות הכלים שה-subagent ביצע
usageobject{"input_tokens": 8320, ...}פירוט טוקנים לפי סוג של בקשת ה-API הסופית: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens

ב-Claude Code בגרסה v2.1.271 או חדשה יותר, subagent שרץ עם הכלי SubagentHandback, ש-Claude Code מספק במצב auto, מעביר את הדוח שלו דרך הכלי הזה ולא מחזיר אותו כטקסט. שדה content של תוצאת completed שלו נושא אז הערה קצרה על המסירה הזו ולא את הדוח עצמו. כדי לקרוא את הדוח, התאימו הוק PreToolUse או PostToolUse על SubagentHandback וקראו את tool_input.message.

עבור subagents ברקע, הכלי חוזר כשהמשימה עוברת לרקע, כך שtool_response לא נושא שדות שימוש: הפעלה ברקע חוזרת מיד, ומשימה בחזית ש-Claude Code מעביר לרקע באמצע הריצה חוזרת במעבר הזה. יש לו status: "async_launched", agentId, description, prompt, outputFile וresolvedModel.

בתגובת completed, resolvedModel מציין את המודל שה-subagent התחיל עליו, שעשוי להיות שונה מערך model בtool_input, למשל כשavailableModels או דריסה אחרת חלים. בתגובת async_launched, resolvedModel מציין את המודל שבשימוש כשהסוכן עבר לרקע, כך שהחלפה שקרתה לפני המעבר לרקע משתקפת שם.

#AskUserQuestion

שואל את המשתמש שאלה אחת עד ארבע שאלות רב ברירה.

שדהסוגדוגמהתיאור
questionsarray[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false}]שאלות להצגה, כל אחת עם מחרוזת question, header קצר, מערך options ודגל multiSelect אופציונלי
answersobject{"Which framework?": "React"}אופציונלי. ממפה טקסט שאלה לתווית האפשרות שנבחרה. תשובות רב בחירה מחברות תוויות בפסיקים. Claude לא מגדיר את השדה הזה. ספקו אותו דרך updatedInput כדי לענות באופן תכנותי
#ExitPlanMode

מציג תוכנית ומבקש מהמשתמש לאשר אותה לפני ש-Claude יוצא ממצב plan. Claude כותב את התוכנית לקובץ בדיסק לפני שהוא קורא לכלי, כך שtool_input המילולי מהמודל בדרך כלל ריק. Claude Code מזריק את תוכן התוכנית ואת נתיב הקובץ לפני שהוא מעביר את הקלט להוקים.

שדהסוגדוגמהתיאור
planstring"## Refactor auth\n1. Extract..."תוכן התוכנית ב-Markdown. מוזרק מקובץ התוכנית בדיסק
planFilePathstring"/Users/.../plans/refactor-auth.md"נתיב לקובץ התוכנית. מוזרק
allowedPromptsarray[{"tool": "Bash", "prompt": "run tests"}]Deprecated. Claude Code מקבל את השדה אבל מתעלם ממנו. לפני v2.1.205 הוא נשא הרשאות מבוססות הנחיה ש-Claude ביקש כדי ליישם את התוכנית

בPostToolUse, tool_response הוא אובייקט עם שדות plan וfilePath שמחזיקים את התוכנית שאושרה, ועם דגלי סטטוס פנימיים. קראו את tool_response.plan עבור תוכן התוכנית ולא את הקובץ מחדש מהדיסק.

#בקרת החלטה של PreToolUse

הוקי PreToolUse יכולים לקבוע אם קריאת כלי תמשיך. בניגוד להוקים אחרים שמשתמשים בשדה decision ברמה העליונה, PreToolUse מחזיר את ההחלטה שלו בתוך אובייקט hookSpecificOutput. זה נותן לו שליטה עשירה יותר: ארבע תוצאות (allow, deny, ask או defer) וגם היכולת לשנות את קלט הכלי לפני הביצוע.

שדהתיאור
permissionDecision"allow" מדלג על בקשת ההרשאה, חוץ מפעולות שאף מצב לא מאשר אוטומטית ומקריאות מנתיבי רשת. הוא גם לא מדלג על אישור לקריאות אל כלים שאפליקציית Agent SDK מסמנת always_ask, אלא אם מצב ההרשאה מאשר את הקריאה. AskUserQuestion ו-ExitPlanMode צריכים updatedInput בצמוד ל-"allow". למצב תוכנית, ראו אישורי הוק במצב תוכנית. "deny" מונע את קריאת הכלי. "ask" מבקש מהמשתמש לאשר. "defer" מסיים את התור כדי שאפשר יהיה לחדש את הכלי אחר כך, ודורש מצב לא אינטראקטיבי עם הדגל -p. כללי deny ו-ask עדיין מוערכים בלי תלות במה שההוק מחזיר
permissionDecisionReasonעבור "ask", מוצג למשתמש בבקשת ההרשאה. כש-Claude Code דוחה את הקריאה בהרצה עם -p שבה אף אחד לא יכול לענות לבקשה הזו, Claude קורא את הסיבה בתוצאת הכלי במקום זאת. עבור "deny", מוצג ל-Claude. עבור "allow" ו-"defer", נכתב רק ליומן הניפוי
updatedInputמשנה את פרמטרי הקלט של הכלי לפני הביצוע. מחליף את כל אובייקט הקלט, לכן יש לכלול שדות שלא השתנו לצד השדות ששונו. Claude Code מעריך כללי הרשאה ואת הזכאות להעברה אוטומטית לרקע של פקודת Bash מול הקלט שההוק שלך מחזיר, לא מול הקלט ש-Claude שלח. שלבו עם "allow" לאישור אוטומטי, או עם "ask" כדי להציג את הקלט ששונה למשתמש. עבור "defer", מתעלמים ממנו. בלי permissionDecision, מצב אוטומטי יכול לדחות את הקריאה ששונתה
additionalContextמחרוזת שנוספת להקשר של Claude לצד תוצאת הכלי. מתעלמים ממנה כש-permissionDecision הוא "defer". ראו הוספת הקשר ל-Claude

כמה הוקי PreToolUse מחזירים החלטות שונות, סדר העדיפות הוא deny > defer > ask > allow.

הוק שחוסם על ידי יציאה עם קוד 2 מנותב באותו אופן כמו "deny": Claude רואה את הודעת stderr כסיבת הדחייה.

כשהוק מחזיר "ask", בקשת ההרשאה שמוצגת למשתמש כוללת תווית שמזהה מאיפה ההוק הגיע: [settings] להוק מכל קובץ הגדרות או מ-frontmatter של סוכן, [plugin:<name>] להוק של תוסף, או [skill] להוק מ-frontmatter של skill. זה עוזר למשתמשים להבין איזה מקור הגדרה מבקש אישור.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}

[!NOTE]

PreToolUse השתמש בעבר בשדות decision ו-reason ברמה העליונה, אבל הם deprecated עבור האירוע הזה. השתמשו ב-hookSpecificOutput.permissionDecision וב-hookSpecificOutput.permissionDecisionReason במקום. הערכים שיצאו משימוש "approve" ו-"block" ממופים ל-"allow" ול-"deny" בהתאמה. אירועים אחרים כמו PostToolUse ו-Stop ממשיכים להשתמש ב-decision וב-reason ברמה העליונה כפורמט הנוכחי שלהם.

#updatedInput של PreToolUse במצב אוטומטי

כשההוק שלך מחזיר updatedInput בלי permissionDecision במצב אוטומטי, Claude Code יכול לדחות את הקריאה ששונתה. השרת בדק את הקלט ש-Claude כתב, לא את הקלט שההוק שלך החזיר. בסשנים אינטראקטיביים בטרמינל ובסשנים של VS Code בחיבור ישיר ל-API של Anthropic, הדחייה כוללת את הטקסט הזה:

a hook changed this call's input after auto mode reviewed it

אחרי דחייה עם הטקסט הזה, Claude יכול להוציא את הקריאה ששונתה שוב כדי שהשרת יבדוק אותה. אם Claude חוזר על הקלט ששונה בדיוק בתגובה הבאה שלו, ההוק שלך עדיין רץ:

  • הוא יכול לדחות את הקריאה או לשלוח אותה לבקשת ההרשאה
  • כל updatedInput שהוא מחזיר נזרק, כך שמה שרץ הוא מה שהשרת בדק

אין צורך לשנות את ההוק בגלל הדחייה הזו, אלא אם Claude אומר לך בתשובה שלו שמצב אוטומטי לא הצליח לבדוק את הקריאה.

בסשנים שמציגים את הטקסט הזה, הוק command בהגדרות או בקובץ ההוקים של תוסף יכול לשנות קריאות Bash, PowerShell, Monitor, Edit, Write ו-NotebookEdit בלי הדחייה הזו. הדחייה עדיין יכולה להופיע, למשל כשהוק כלשהו שינה קריאה קודמת באותה תגובה של Claude, או כשפקודת Bash מבקשת לרוץ מחוץ לארגז החול.

#החלטות ask של PreToolUse במצב אוטומטי

במצב אוטומטי, "ask" של הוק PreToolUse בדרך כלל שולח את קריאת הכלי לבקשת ההרשאה, והמסווג לא מחליט אותה. חלק מהקריאות הולכות במסלול אחר:

  • קריאות שגם הבדיקות של Claude Code עצמו מסמנות: אלה כוללות כתיבה לנתיבים מוגנים ופקודות שמבקשות לרוץ מחוץ לארגז החול. המסווג יכול לדחות אחת מהקריאות האלה לפני שמופיעה בקשה כלשהי. אם הוא מאשר, הקריאה בדרך כלל עדיין הולכת לבקשה.
  • קריאות SubagentHandback: הכלי הזה מוסר את הדוח של תת-סוכן. לא מופיעה בקשה עבורו, והקריאה רצה גם אם המסווג מתנגד.

כדי לעצור קריאת SubagentHandback, החזירו "deny". Claude Code אז אומר לתת-הסוכן, עד שלוש פעמים, שהדוח שלו לא נמסר ושיקרא לכלי שוב. אם ההוק שלך ממשיך לדחות, תת-הסוכן מסתיים בלי למסור דוח דרך הכלי הזה, והשיחה שממתינה לו מקבלת הערה שאומרת זאת.

#כלים שדורשים אינטראקציה עם המשתמש

AskUserQuestion ו-ExitPlanMode דורשים אינטראקציה עם המשתמש. במצב לא אינטראקטיבי עם הדגל -p, Claude Code מציע אותם רק כשיש להרצה מארח הרשאות שיקבל את הבקשה, כמו callback canUseTool של Agent SDK.

הוק PreToolUse עומד בדרישה הזו כשהוא עושה את הדברים הבאים:

  1. קורא את קלט הכלי מ-stdin
  2. אוסף את התשובה דרך ממשק המשתמש שלך
  3. מחזיר permissionDecision: "allow" יחד עם updatedInput שמחזיק את התשובה, כדי שהכלי ירוץ בלי בקשה

החזרת "allow" לבד אינה מספיקה לכלים האלה.

עבור AskUserQuestion, החזירו את מערך questions המקורי והוסיפו אובייקט answers שממפה את הטקסט של כל שאלה לתשובה שנבחרה. הפלט הזה עונה על שאלה אחת עם React:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "questions": [
        {
          "question": "Which framework?",
          "header": "Framework",
          "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}],
          "multiSelect": false
        }
      ],
      "answers": {"Which framework?": "React"}
    }
  }
}

כלי MCP שהשרת שלו מסמן אותו עם _meta["anthropic/requiresUserInteraction"] מחמיר יותר: הוק לא יכול לדלג על בקשת האישור שלו עם "allow", עם updatedInput או בלעדיו, כי Claude Code לא יכול לאשר שההוק אסף את האינטראקציה שהכלי צריך.

#דחיית קריאת כלי למועד מאוחר יותר

"defer" מיועד לאינטגרציות שמריצות claude -p כתהליך בן וקוראות את פלט ה-JSON שלו, כמו אפליקציית Agent SDK או ממשק מותאם שנבנה מעל Claude Code. זה מאפשר לתהליך הקורא להשהות את Claude בקריאת כלי, לאסוף קלט דרך הממשק שלו, ולחדש מהמקום שבו הוא עצר. Claude Code מכבד את הערך הזה רק במצב לא אינטראקטיבי עם הדגל -p. בסשנים אינטראקטיביים הוא רושם אזהרה ומתעלם מתוצאת ההוק.

הכלי AskUserQuestion הוא המקרה הטיפוסי: Claude רוצה לשאול את המשתמש משהו, אבל אין טרמינל לענות בו. הרצת -p מציעה את AskUserQuestion רק כשיש לה מארח הרשאות, כמו כלי MCP שמעבירים עם --permission-prompt-tool, לכן התחילו את ההרצה עם אחד כזה. הסבב עובד כך:

  1. Claude קורא ל-AskUserQuestion. הוק PreToolUse מופעל.
  2. ההוק מחזיר permissionDecision: "defer". הכלי לא מבוצע. התור מסתיים עם תוצאה שנושאת stop_reason: "tool_deferred", וקריאת הכלי הממתינה נשארת בתמליל.
  3. התהליך הקורא קורא את deferred_tool_use מתוצאת ה-SDK, מציג את השאלה בממשק שלו, ומחכה לתשובה.
  4. התהליך הקורא מריץ claude -p --resume <session-id> עם אותו מארח הרשאות. אותה קריאת כלי מפעילה שוב את PreToolUse.
  5. ההוק מחזיר permissionDecision: "allow" עם התשובה ב-updatedInput. הכלי מבוצע ו-Claude ממשיך.

השדה deferred_tool_use נושא את id, name ו-input של הכלי. ה-input הוא הפרמטרים ש-Claude יצר לקריאת הכלי, שנלכדו לפני הביצוע:

{
  "type": "result",
  "subtype": "success",
  "stop_reason": "tool_deferred",
  "session_id": "abc123",
  "deferred_tool_use": {
    "id": "toolu_01abc",
    "name": "AskUserQuestion",
    "input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false }] }
  }
}

אין timeout ואין מגבלת ניסיונות חוזרים. הסשן נשאר על הדיסק עד שמחדשים אותו, בכפוף לסריקת השימור של cleanupPeriodDays, שמוחקת קבצי סשן אחרי 30 יום כברירת מחדל, לפי כללי סריקת השימור.

אם הכלי שנדחה כבר לא זמין כשמחדשים, לתוצאה הראשונה של החידוש יש stop_reason: "tool_deferred_unavailable" ו-is_error: true, וההוק לא מופעל. זה קורה כששרת MCP שסיפק את הכלי לא מחובר לסשן המחודש. מטען deferred_tool_use עדיין כלול כדי שאפשר יהיה לזהות איזה כלי נעלם.

  • אם לא שלחתם פרומפט, עם החידוש או אחריו, הקריאה עדיין ממתינה. חדשו שוב כשהכלי זמין והיא רצה ראשונה.
  • אם שלחתם פרומפט, עם החידוש או אחריו, Claude Code עונה עליו בלי תוצאת הכלי וזורק את הקריאה שנדחתה. שלחו פרומפט חדש ברגע שהכלי זמין אם עדיין צריך לקרוא לו.

אם התשובה שמחכים לה לא מוכנה כשמחדשים בלי פרומפט, ההוק יכול להחזיר "defer" שוב. אם שלחתם פרומפט וההוק דוחה שוב, Claude Code מתייחס לפרומפט באותו אופן: הוא עונה בלי תוצאת הכלי וזורק את הקריאה. חדשו בלי פרומפט עד שההוק מחזיר "allow" או "deny", וכך התהליך הקורא שובר את הלולאה.

"defer" עובד רק כש-Claude מבצע קריאת כלי אחת בתור. אם Claude מבצע כמה קריאות כלי בבת אחת, מתעלמים מ-"defer" עם אזהרה והכלי ממשיך בזרימת ההרשאה הרגילה. האילוץ קיים כי חידוש יכול להריץ מחדש רק כלי אחד: אין דרך לדחות קריאה אחת מאצווה בלי להשאיר את האחרות לא פתורות.

[!NOTE]

כדי לחדש סשן שנדחה במצב תוכנית, העבירו --permission-prompt-tool יחד עם --resume כדי ש-Claude Code יוכל להציג את התוכנית לאישור. לשאר התנאים, ראו חידוש במצב תוכנית עם -p. דורש Claude Code v2.1.246 או חדש יותר.

כשמחדשים עם -p, Claude Code לא משחזר שום מצב הרשאה שמור אחר. הוא מתחיל את ההרצה במצב ההרשאה שבו הרצת claude -p חדשה הייתה מתחילה, לכן העבירו שוב --permission-mode או --dangerously-skip-permissions אם הסשן שנדחה השתמש באחד מהם. כשמחדשים עם claude --resume <session-id> בלי -p, Claude Code משחזר את מצב ההרשאה השמור, עם החריגים שמפורטים במצב הרשאה בחידוש.

#PermissionRequest

רץ כאשר Claude Code עומד לבקש ממך הרשאה להשתמש בכלי. בסשנים שאינם יכולים להציג הנחיה, כגון subagents ברקע במצב לא אינטראקטיבי, Claude Code עדיין מריץ את ה-hooks האלה, ואם אף hook לא מחזיר החלטה, הוא דוחה את קריאת הכלי. עבור קריאה שמגיעה ל---permission-prompt-tool או ל-callback canUseTool של Agent SDK, ה-hooks רצים לצד ה-host שלך, וההחלטה שמתקבלת ראשונה היא זו שחלה. השתמש בבקרת החלטה של PermissionRequest כדי לאפשר או לדחות בשם המשתמש.

השתמש באירוע הזה כאשר אתה צריך אות ברגע ש-Claude מבקש הרשאה להשתמש בכלי. Claude Code מריץ hook של Notification עם הסוג permission_prompt רק אחרי שההנחיה המתינה כשש שניות.

Claude Code לא מריץ hooks של PermissionRequest עבור בקשת רשת של פקודה ב-sandbox. כדי לקבל אות עבור ההנחיה הזו, השתמש בסוג ההתראה permission_prompt.

ההתאמה היא לפי שם הכלי, אותם ערכים כמו ב-PreToolUse.

#קלט של PermissionRequest

hooks של PermissionRequest מקבלים את השדות tool_name ו-tool_input כמו hooks של PreToolUse, אבל בלי tool_use_id. עבור כלי MCP, הם מקבלים גם את האובייקט mcp_server. מערך אופציונלי permission_suggestions מכיל את עדכוני ההרשאה ש-Claude Code מציע עבור הבקשה הזו, כגון הוספת כלל allow או שינוי מצב ההרשאה.

המערך permission_suggestions אינו רשימה מדויקת של האפשרויות שאתה רואה, כי כל דיאלוג הרשאה בונה את האפשרויות שלו. חלק מהדיאלוגים, כגון זה של עריכות קבצים, לא קוראים את המערך כלל וגוזרים את האפשרויות שלהם מהבקשה עצמה. דיאלוג שכן קורא אותו עדיין יכול להסתיר אפשרות שההצעה שלה נשארת במערך, לדוגמה כאשר allowManagedPermissionRulesOnly מסתיר אפשרויות שמירת כללים. הוא יכול גם להציע אפשרויות שאין להן רשומת הצעה, כגון כן, ועבור למצב auto, שמשנה את מצב ההרשאה ישירות ולא דרך עדכון הרשאה.

hooks של PreToolUse רצים לפני כל קריאת כלי, בין אם היא צריכה הרשאה ובין אם לא. hooks של PermissionRequest רצים רק כאשר Claude Code עומד לבקש ממך הרשאה, או כאשר הוא היה דוחה אוטומטית קריאה שאינה יכולה להציג הנחיה. אף אחד משני האירועים לא מופעל עבור EndConversation.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "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"
    }
  ]
}

#בקרת החלטה של PermissionRequest

hooks של PermissionRequest יכולים לאפשר או לדחות בקשות הרשאה. בנוסף לשדות פלט JSON הזמינים לכל ה-hooks, סקריפט ה-hook שלך יכול להחזיר אובייקט decision עם השדות הספציפיים לאירוע האלה:

שדהתיאור
behavior"allow" מעניק את ההרשאה, "deny" דוחה אותה. כללי deny ו-ask עדיין מוערכים, כך ש-hook שמחזיר "allow" לא עוקף כלל deny תואם
updatedInputעבור "allow" בלבד: משנה את פרמטרי הקלט של הכלי לפני הביצוע. מחליף את אובייקט הקלט כולו, לכן כלול שדות שלא השתנו לצד אלה ששונו. הקלט ששונה מוערך מחדש מול כללי deny ו-ask
updatedPermissionsעבור "allow" בלבד: מערך של רשומות עדכון הרשאה להחלה, כגון הוספת כלל allow או שינוי מצב ההרשאה של הסשן
messageעבור "deny" בלבד: אומר ל-Claude מדוע ההרשאה נדחתה
interruptעבור "deny" בלבד: אם true, עוצר את Claude

hook שיוצא עם קוד 2 בלי אובייקט decision משאיר את זרימת ההרשאה ללא שינוי, וה-stderr שלו נזרק. כדי להעניק או לדחות את הבקשה, החזר את אובייקט decision.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

#רשומות עדכון הרשאה

שדה הפלט updatedPermissions ושדה הקלט permission_suggestions שניהם משתמשים באותו מערך של אובייקטי רשומה. לכל רשומה יש type שקובע את שאר השדות שלה, ו-destination ששולט לאן השינוי נכתב.

typeשדותהשפעה
addRulesrules, behavior, destinationמוסיף כללי הרשאה. rules הוא מערך של אובייקטים {toolName, ruleContent?}. השמט את ruleContent כדי להתאים לכלי כולו. behavior הוא "allow", "deny", או "ask"
replaceRulesrules, behavior, destinationמחליף את כל הכללים של ה-behavior הנתון ב-destination ב-rules שסופקו
removeRulesrules, behavior, destinationמסיר כללים תואמים של ה-behavior הנתון
setModemode, destinationמשנה את מצב ההרשאה. מצבים תקפים הם default, auto, acceptEdits, dontAsk, bypassPermissions, plan, ו-manual ככינוי ל-default
addDirectoriesdirectories, destinationמוסיף ספריות עבודה. directories הוא מערך של מחרוזות נתיב
removeDirectoriesdirectories, destinationמסיר ספריות עבודה

[!NOTE]

setMode עם bypassPermissions נכנס לתוקף רק אם הפעלת את הסשן כאשר מצב bypass כבר זמין: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, או permissions.defaultMode: "bypassPermissions" בהגדרות משתמש, --settings, או הגדרות מנוהלות. אחרת העדכון הוא no-op. העדכון הוא גם no-op כאשר permissions.disableBypassPermissionsMode משבית את המצב, או כאשר הסשן מתחיל במצב מוגבל.

bypassPermissions לעולם לא נשמר כ-defaultMode ללא קשר ל-destination.

שדה destination בכל רשומה קובע אם השינוי נשאר בזיכרון או נשמר לקובץ הגדרות.

destinationנכתב אל
sessionבזיכרון בלבד, נזרק כאשר הסשן מסתיים
localSettings.claude/settings.local.json
projectSettings.claude/settings.json
userSettings~/.claude/settings.json

hook יכול להדהד אחת מ-permission_suggestions שקיבל כפלט updatedPermissions שלו.

#PostToolUse

רץ מיד אחרי שכלי מסתיים בהצלחה.

ההתאמה היא לפי שם הכלי, אותם ערכים כמו ב-PreToolUse.

התאם באופן רחב יותר כאשר שם הכלי אינו המסנן הנכון:

  • כדי להריץ hook אחרי שכל כלי מסתיים בהצלחה, השמט את matcher או הגדר אותו ל-"*" . ה-hook שלך יכול אז לגלות בעצמו מה השתנה, לדוגמה על ידי הרצת git status --porcelain, שגם מציג קבצים לא במעקב ש-git diff מפספס. עבור קריאות כלי שנכשלות, הוסף את אותו hook תחת PostToolUseFailure.
  • כדי להריץ hook כאשר קובץ מסוים משתנה בדיסק, לא משנה מי כתב אותו, השתמש ב-FileChanged. Claude Code לא מריץ hook של PostToolUse שתואם ל-Edit|Write כאשר פקודת Bash או תהליך מחוץ ל-Claude Code כותב מחדש את אותו קובץ.

#קלט של PostToolUse

hooks של PostToolUse מופעלים אחרי שכלי כבר רץ בהצלחה. הקלט כולל גם את tool_input, הארגומנטים שנשלחו לכלי, וגם את tool_response, התוצאה שהוא החזיר. הסכמה המדויקת של שניהם תלויה בכלי. נתיבי tool_input של כלי קבצים מגיעים באותו פורמט כמו ב-PreToolUse: תמיד מוחלטים, עם המפרידים המקומיים של הפלטפורמה, כך ש-backslashes ב-Windows. עבור כלי MCP, הקלט נושא גם את האובייקט mcp_server.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "type": "create"
  },
  "tool_use_id": "toolu_01ABC123...",
  "duration_ms": 12
}
שדהתיאור
duration_msאופציונלי. זמן ביצוע הכלי באלפיות שנייה. לא כולל זמן שהושקע בהנחיות הרשאה וב-hooks של PreToolUse

#בקרת החלטה של PostToolUse

hooks של PostToolUse יכולים לספק משוב ל-Claude אחרי ביצוע הכלי. בנוסף לשדות פלט JSON הזמינים לכל ה-hooks, סקריפט ה-hook שלך יכול להחזיר את השדות הספציפיים לאירוע האלה:

שדהתיאור
decision"block" מוסיף את reason ליד תוצאת הכלי. Claude עדיין רואה את הפלט המקורי. כדי להחליף אותו, השתמש ב-updatedToolOutput
reasonהסבר שמוצג ל-Claude כאשר decision הוא "block"
additionalContextמחרוזת שמתווספת להקשר של Claude לצד תוצאת הכלי. ראה הוספת הקשר ל-Claude
classifierContextהערה קצרה על התוצאה של הקריאה הזו עבור המסווג של מצב auto ולא עבור Claude. ראה הוספת הערה לתוצאה עבור מסווג מצב auto. דורש Claude Code v2.1.236 או מאוחר יותר
updatedToolOutputמחליף את פלט הכלי בערך שסופק לפני שהוא נשלח ל-Claude. הערך חייב להתאים לצורת הפלט של הכלי
updatedMCPToolOutputמחליף את הפלט עבור כלי MCP בלבד. העדף את updatedToolOutput, שעובד עבור כל הכלים

הדוגמה למטה מחליפה את הפלט של קריאת Bash. ערך ההחלפה תואם לצורת הפלט של כלי Bash:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude",
    "updatedToolOutput": {
      "stdout": "[redacted]",
      "stderr": "",
      "interrupted": false,
      "isImage": false
    }
  }
}

[!WARNING]

updatedToolOutput משנה רק את מה ש-Claude רואה. הכלי כבר רץ עד שה-hook מופעל, כך שכל קבצים שנכתבו, פקודות שבוצעו, או בקשות רשת שנשלחו כבר נכנסו לתוקף. טלמטריה כגון tool spans של OpenTelemetry ואירועי analytics גם לוכדת את הפלט המקורי לפני שה-hook רץ. כדי למנוע או לשנות קריאת כלי לפני שהיא רצה, השתמש ב-hook של PreToolUse.

ערך ההחלפה חייב להתאים לצורת הפלט של הכלי. כלים מובנים מחזירים אובייקטים מובנים ולא מחרוזות פשוטות. לדוגמה, Bash מחזיר אובייקט עם השדות stdout, stderr, interrupted, ו-isImage. עבור כלים מובנים, ערך שלא תואם לסכמת הפלט של הכלי מתעלמים ממנו והפלט המקורי משמש. פלט של כלי MCP מועבר בלי אימות סכמה. הסרת פרטי שגיאה ש-Claude צריך יכולה לגרום לו להמשיך על סמך הנחה שגויה.

#הוספת הערה לתוצאה עבור מסווג מצב auto

החזר classifierContext כדי לשלוח הערה קצרה על התוצאה של קריאת הכלי למסווג של מצב auto ולא ל-Claude. המסווג לעולם לא מקבל את תוצאות הכלים עצמן, כך שהשדה הזה הוא הדרך הנתמכת לספר לו משהו על מה שקריאה החזירה לפני שהוא סוקר פעולות מאוחרות יותר. השדה דורש Claude Code v2.1.236 או מאוחר יותר.

הדוגמה למטה אומרת למסווג מאיפה הגיע הפלט של שאילתה:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "classifierContext": "This query ran against the staging database, not production."
  }
}

כמה משקל המסווג נותן להערה תלוי במקום שבו הגדרת את ה-hook:

  • Hooks שהוגדרו ב-Claude Code: עבור hooks מקובצי הגדרות, plugins, skills, ו-frontmatter של agent, המסווג מתייחס להערה כהקשר לא מאומת שסופק על ידי האפליקציה. ההערה לעולם לא מבססת כוונת משתמש, ואם היא טוענת שאישרת או ביקשת משהו, המסווג בודק את הטענה הזו מול ההודעות שלך בשיחה
  • Callbacks של Agent SDK בתוך התהליך: כאשר אפליקציה שמטמיעה את Claude Code רושמת את ה-hook כ-callback של TypeScript SDK ומחזירה את ההערה במהלך הסשן החי, המסווג עשוי לשקול אמירת משתמש שמועברת בהערה ככוונת משתמש. אמירה כזו יכולה לספק דרישת הסכמה שהמסווג היה מקבל מהודעה שאתה שולח, אבל היא לעולם לא מסירה חסימה שההודעה שלך עצמה לא הייתה יכולה להסיר. אחרי שסשן מתחדש, Claude Code מתייחס להערות ששוחזרו כהקשר לא מאומת. כאשר hooks משתי הקבוצות מוסיפים הערה לאותה קריאה, המסווג מתייחס להערה המשולבת כלא מאומתת

Claude Code מחיל את המגבלות האלה בעת מסירת ההערה:

  • אורך: Claude Code מגביל את ההערות עבור קריאת כלי אחת ל-2,000 תווים וחותך את השאר. התקרה משותפת לכל hook שמגיב לקריאה הזו
  • תגובות סינכרוניות בלבד: Claude Code מתעלם מהשדה בתגובה של hook שרץ ברקע, כי התגובה הזו מגיעה אחרי ש-Claude Code רושם את תוצאת הכלי
  • קריאות שהמסווג לא רושם: התמליל של המסווג משמיט חיפושים לקריאה בלבד כגון קריאות קבצים וחיפושים. Claude Code זורק הערה שמצורפת לאחת מהקריאות האלה
  • אינטראקציה עם שכתובים: כאשר ההערה מתארת פלט שאתה מחליף באמצעות updatedToolOutput, החזר את שני השדות באותה תגובת hook. Claude Code משמיט את ההערה אם השכתוב הזה נדחה או אם שכתוב של hook אחר מחליף אותו. Claude Code מוסר הערה שאתה מחזיר בלי שכתוב גם כאשר hook אחר משכתב את הפלט

[!WARNING]

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

#PostToolUseFailure

רץ כאשר כלי שהתחיל לבצע נכשל: הכלי זרק שגיאה, או שכלי MCP החזיר תוצאת שגיאה. השתמשו בזה כדי לרשום כשלים, לשלוח התראות, או לספק משוב מתקן ל-Claude.

מתאים לפי שם הכלי, אותם ערכים כמו ב-PreToolUse.

[!NOTE]

האירוע הזה לא נורה עבור קריאות כלים שנדחו לפני הביצוע: שם כלי לא ידוע, קלט שנכשל בולידציית סכמה או בולידציה ייחודית לכלי, או דחיית הרשאה. דחיות ולידציה מוחזרות כתוצאות tool_use_error ומתרחשות לפני שה-hooks רצים, ולכן הן לא מפעילות לא את PreToolUse ולא את PostToolUseFailure. דחיות הרשאה מפעילות את PreToolUse אבל לא את האירוע הזה. ראו PermissionDenied.

#קלט PostToolUseFailure

hooks של PostToolUseFailure מקבלים את אותם שדות tool_name ו-tool_input כמו PostToolUse, יחד עם מידע על השגיאה כשדות ברמה העליונה. עבור כלי MCP, הם מקבלים גם את האובייקט mcp_server. לדוגמה, פקודת npm test שנכשלה עשויה לספק:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Exit code 1\nError: Cannot find module 'express'",
  "is_interrupt": false,
  "duration_ms": 4187
}
שדהתיאור
errorמחרוזת שמתארת מה השתבש. הפורמט תלוי בכלי שנכשל
is_interruptבוליאני אופציונלי. True כאשר הכשל הגיע ל-Claude Code כביטול ולא כשגיאה שהכלי דיווח. ביטול של כלי שרץ לא מפעיל את ה-hook הזה. תוצאת הכלי נושאת במקום זאת את הודעת ההפרעה
duration_msאופציונלי. זמן ביצוע הכלי באלפיות שנייה. לא כולל זמן שהושקע בהנחיות הרשאה וב-hooks של PreToolUse

מחרוזת error היא בדרך כלל אותו טקסט ש-Claude מקבל כתוצאת הכלי שנכשל. הפורמט שלה משתנה לפי כלי ולפי כשל. בסיסו את ה-hook על tool_name, על is_interrupt ועל שורת הפתיחה Exit code N. התייחסו לשאר המחרוזת כטקסט תצוגה, לא כפורמט יציב.

  • עבור Bash ו-PowerShell, פקודה שרצה ויצאה מייצרת שורה ראשונה Exit code N, ואז כל פלט שהפקודה ייצרה כבלוק אחד שבו stdout ו-stderr משולבים
  • מטען עשוי לשאת גם הודעת כשל חשופה בלי שורת קוד יציאה, כאשר Claude Code לא הצליח להפעיל את תהליך המעטפת עצמו
  • Claude Code חותך באמצע מחרוזות ארוכות סביב סמן ... [N characters truncated] ..., ויכול להוסיף שורות משלו, כמו Command timed out after 2m 0s

#בקרת החלטה של PostToolUseFailure

hooks של PostToolUseFailure יכולים לספק הקשר ל-Claude אחרי כשל של כלי. בנוסף לשדות פלט JSON הזמינים לכל ה-hooks, סקריפט ה-hook יכול להחזיר את השדות הייחודיים לאירוע האלה:

שדהתיאור
additionalContextמחרוזת שמתווספת להקשר של Claude לצד השגיאה. ראו הוספת הקשר ל-Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}

#PostToolBatch

רץ פעם אחת אחרי שכל קריאת כלי באצווה נפתרה, לפני ש-Claude Code שולח את הבקשה הבאה למודל. PostToolUse נורה פעם אחת לכל כלי, וזה אומר שהוא נורה במקביל כאשר Claude מבצע קריאות כלים מקבילות. PostToolBatch נורה בדיוק פעם אחת עם האצווה המלאה, ולכן זה המקום הנכון להזריק הקשר שתלוי בקבוצת הכלים שרצו ולא בכלי יחיד. אין matcher לאירוע הזה.

#קלט PostToolBatch

בנוסף לשדות הקלט המשותפים, hooks של PostToolBatch מקבלים את tool_calls, מערך שמתאר כל קריאת כלי באצווה:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolBatch",
  "tool_calls": [
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/accounts.py"},
      "tool_use_id": "toolu_01...",
      "tool_response": "1\tfrom __future__ import annotations\n2\t..."
    },
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/transactions.py"},
      "tool_use_id": "toolu_02...",
      "tool_response": "1\tfrom __future__ import annotations\n2\t..."
    }
  ]
}

tool_response מכיל את אותו תוכן שהמודל מקבל בבלוק tool_result המתאים. הערך הוא מחרוזת סריאלית או מערך בלוקי תוכן, בדיוק כפי שהכלי פלט אותו. עבור Read, זה אומר טקסט עם קידומת מספר שורה ולא תוכן קובץ גולמי. תגובות יכולות להיות גדולות, ולכן נתחו רק את השדות שאתם צריכים.

[!NOTE]

צורת tool_response שונה מזו של PostToolUse. PostToolUse מעביר את אובייקט ה-Output המובנה של הכלי, כמו {filePath: "...", type: "create"} עבור Write. PostToolBatch מעביר את תוכן tool_result הסריאלי שהמודל רואה.

#בקרת החלטה של PostToolBatch

hooks של PostToolBatch יכולים להזריק הקשר ל-Claude. בנוסף לשדות פלט JSON הזמינים לכל ה-hooks, סקריפט ה-hook יכול להחזיר את השדות הייחודיים לאירוע האלה:

שדהתיאור
additionalContextמחרוזת הקשר שמוזרקת פעם אחת לפני קריאת המודל הבאה. ראו הוספת הקשר ל-Claude לפרטי המסירה, מה לשים בה, ואיך הפעלות שהתחדשו מטפלות בערכים קודמים
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolBatch",
    "additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete."
  }
}

החזרת decision: "block" או continue: false עוצרת את לולאת הסוכן לפני קריאת המודל הבאה. הודעת החסימה מגיעה מ-reason או מ-stopReason ב-JSON, או מ-stderr ביציאה 2. אתם רואים אותה כאזהרה בתמליל, והיא נשארת בשיחה, כך ש-Claude רואה אותה כשהשיחה ממשיכה.

#PermissionDenied

רץ כאשר מצב אוטומטי דוחה קריאת כלי, כולל כאשר הוא דוחה בלי פסק דין של מסווג כי בדיקת בטיחות נפרדת ממצב אוטומטי סירבה לבקשה של המסווג עצמו או שהתגובה שלו לא נותחה. ה-hook הזה נורה רק במצב אוטומטי: הוא לא רץ כאשר אתם דוחים ידנית תיבת הרשאה, כאשר hook של PreToolUse חוסם קריאה, או כאשר כלל deny תואם. השתמשו בו כדי לרשום דחיות, להתאים תצורה, או לומר למודל שהוא רשאי לנסות שוב את קריאת הכלי.

מתאים לפי שם הכלי, אותם ערכים כמו ב-PreToolUse.

#קלט PermissionDenied

בנוסף לשדות הקלט המשותפים, hooks של PermissionDenied מקבלים את tool_name, את tool_input, את tool_use_id ואת reason. עבור כלי MCP, הם מקבלים גם את האובייקט mcp_server.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "auto",
  "hook_event_name": "PermissionDenied",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build",
    "description": "Clean build directory"
  },
  "tool_use_id": "toolu_01ABC123...",
  "reason": "[Irreversible Local Destruction]"
}
שדהתיאור
reasonסיבת הדחייה. עבור פסק דין של מסווג, ברוב ההפעלות הוא מציין את הכלל שתאם בסוגריים מרובעים, כמו [Data Exfiltration]. ראו סקירת דחיות לצורות האחרות. עבור דחייה בלי פסק דין, הוא מתחיל ב-Auto mode could not evaluate this action and is blocking it for safety. עבור דחייה כי מודל המסווג לא היה זמין, זה הטקסט הקבוע Classifier unavailable

#בקרת החלטה של PermissionDenied

hooks של PermissionDenied יכולים לומר למודל שהוא רשאי לנסות שוב את קריאת הכלי שנדחתה. החזירו אובייקט JSON עם hookSpecificOutput.retry שמוגדר ל-true:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionDenied",
    "retry": true
  }
}

כאשר retry הוא true, Claude Code מוסיף הודעה לשיחה שאומרת למודל שהוא רשאי לנסות שוב את קריאת הכלי. Claude Code לא מבטל את הדחייה בעצמו. אם ה-hook לא מחזיר JSON, או מחזיר retry: false, הדחייה נשארת והמודל מקבל את הודעת הדחייה המקורית.

Claude Code מתעלם מ-retry: true כאשר המסווג הפיק אין פסק דין על הפעולה: התגובה שלו לא נותחה, או שבדיקת בטיחות נפרדת ממצב אוטומטי סירבה לבקשה של המסווג עצמו. עבור הדחיות האלה, Claude Code כבר אומר למודל בהודעת הדחייה אם לנסות שוב מאוחר יותר או להמשיך הלאה.

#Notification

רץ כאשר Claude Code שולח התראות. מתאים לפי סוג ההתראה. השמיטו את ה-matcher כדי להריץ hooks לכל סוגי ההתראות.

אתם מקבלים את אירועי ה-hook האלה גם כשהתראות שולחן העבודה כבויות: ההגדרה preferredNotifChannel, כולל notifications_disabled, משנה רק את אופן ההתראה, לא את השאלה אם ה-hook רץ.

Matcherמתי הוא נורה
permission_promptClaude צריך שתאשרו שימוש בכלי או בקשת רשת של פקודה בארגז חול, וההנחיה המתינה כשש שניות
idle_promptClaude סיים להגיב לפני כ-60 שניות ולא הקלדתם מאז
auth_successהאימות מסתיים
elicitation_dialogשרת MCP פותח טופס elicitation ולא הקלדתם כשש שניות
elicitation_url_dialogשרת MCP מבקש שתפתחו כתובת URL בדפדפן ולא הקלדתם כשש שניות
elicitation_completeשרת MCP מדווח ש-elicitation במצב URL הושלם
elicitation_responseתגובת elicitation של MCP נשלחת בחזרה לשרת
agent_needs_inputהפעלת רקע מתחילה להמתין לקלט שלכם בזמן ש-תצוגת סוכן פתוחה בטרמינל. נורה גם כאשר הפעלת טרמינל מציגה לכם שאלת הגדרת טרמינל של חבר צוות סוכנים או הודעה של מצב אוטומטי על חיובים של בקשות מסווג ולא הקלדתם כשש שניות
agent_completedהפעלת רקע מסתיימת או נכשלת. נורה רק בזמן ש-תצוגת סוכן פתוחה בטרמינל
quota_auto_resume_firedClaude Code ממשיך את המשימה אחרי שמגבלת שימוש של claude.ai עצרה אותה: באיפוס, או מוקדם יותר כאשר משהו שאתם עושים ב-Claude Code במהלך ההמתנה, כמו הוספת קרדיטי שימוש, שדרוג התוכנית, או החלפת מודלים, מחזיר את השימוש לזמין, עם חריגת הגדרת המודל
quota_auto_resume_staleאיפוס של מגבלת שימוש ב-claude.ai התרחש בזמן שהמחשב ישן יותר מכ-30 דקות. Claude Code ממתין שתלחצו על Enter במקום להמשיך. אחרי שינה קצרה יותר הוא ממשיך ומפעיל את quota_auto_resume_fired במקום זאת
quota_auto_resume_disabledClaude Code מסיים את ההמתנה למגבלת שימוש של claude.ai בלי להמשיך את המשימה: autoContinueAtUsageLimit כובה או שהאיפוס זז יותר מ-24 שעות קדימה במהלך המתנה ש-Claude Code התחיל בעצמו, המשימה שהמשיכה המשיכה לפגוע במגבלה, או שההמשך נחסם לפני שהגיע למודל. לא נורה כאשר אתם לוחצים על Esc או על Ctrl+C, או בוחרים Don't continue automatically
plugin_notificationמוד מעלה התראה עם $.ui.notify. ה-title של ה-hook הוא שם המוד אלא אם המוד הגדיר אחד

הסוגים quota_auto_resume_fired, quota_auto_resume_stale ו-quota_auto_resume_disabled דורשים Claude Code בגרסה v2.1.234 או מאוחרת יותר.

בהפעלות טרמינל, permission_prompt עבור בקשת רשת של פקודה בארגז חול דורש Claude Code בגרסה v2.1.246 או מאוחרת יותר.

agent_needs_input עבור שאלת הגדרת טרמינל של חבר צוות דורש Claude Code בגרסה v2.1.248 או מאוחרת יותר.

[!NOTE]

הסוגים permission_prompt, idle_prompt, elicitation_dialog ו-elicitation_url_dialog חולקים את התזמון שלהם עם התראות שולחן העבודה, ולכן בהפעלות טרמינל אתם רואים אותם רק כאשר נראה שאתם רחוקים מהטרמינל:

  • צפו ל-permission_prompt אחרי שלא הקלדתם כשש שניות. הטיימר מתחיל כאשר הנחיית ההרשאה מופיעה, וכל הקשה דוחה אותו. כדי להריץ hook מיד כאשר Claude מבקש הרשאה להשתמש בכלי, השתמשו ב-PermissionRequest במקום זאת.
  • צפו ל-idle_prompt כ-60 שניות אחרי ש-Claude מסיים להגיב, ורק אם לא הקלדתם מאז ואין סוכן רקע, כמו תת-סוכן ברקע, שעדיין רץ. Claude Code לא שולח idle_prompt בזמן שהוא ממתין לאיפוס של מגבלת שימוש ב-claude.ai. כאשר ההמתנה מסתיימת מעצמה, אחד מסוגי quota_auto_resume_* נורה במקום זאת.
  • צפו ל-elicitation_dialog עבור טופס elicitation, או ל-elicitation_url_dialog עבור בקשת כתובת URL בדפדפן, אחרי שלא הקלדתם כשש שניות. שניהם חולקים את אותו שער של שש שניות כמו permission_prompt: הטיימר מתחיל כאשר התיבה מופיעה, וכל הקשה דוחה אותו.

בקשת הרשאה או elicitation שמגיעה בזמן שתיבה אחרת על המסך שומרת על אותו שער של שש שניות, שנמדד מהרגע שהבקשה מגיעה. ההתראה שלה יכולה להגיע אליכם בזמן שהבקשה עדיין ממתינה מאחורי התיבה הפתוחה.

Claude Code מתזמן את permission_prompt אחרת בהפעלות שבהן הוא שולח בקשות הרשאה ל-callback של canUseTool של Agent SDK, וכך Claude Desktop והרחבת VS Code מארחים את Claude Code:

  • צפו ל-permission_prompt כשש שניות אחרי ש-Claude מבקש הרשאה. Claude Code לא דוחה אותו בזמן שאתם מקלידים.
  • אם אתם או hook של PermissionRequest עונים מוקדם יותר, Claude Code לא מריץ את permission_prompt.
  • הגדירו את CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS ל-1 כדי לכבות את permission_prompt בהפעלות האלה.

לפני v2.1.233, permission_prompt לא נורה בהפעלות האלה.

השתמשו ב-matchers נפרדים כדי להריץ מטפלים שונים לפי סוג ההתראה. התצורה הזו מפעילה סקריפט התראה ייחודי להרשאה כאשר Claude צריך אישור הרשאה, והתראה אחרת כאשר Claude היה לא פעיל:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

#קלט Notification

בנוסף לשדות הקלט המשותפים, hooks של Notification מקבלים את message עם טקסט ההתראה, title אופציונלי, ואת notification_type שמציין איזה סוג נורה.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}

hooks של Notification לא יכולים לחסום או לשנות התראות. Claude Code משליך את השדות systemMessage ו-continue שלהם אבל עדיין פולט את terminalSequence, ועל זה נשענת דוגמת התראת שולחן העבודה. hooks של Notification מיועדים לתופעות לוואי כמו העברת ההתראה לשירות חיצוני.

#SubagentStart

רץ כאשר קלוד מפעיל subagent עם כלי ה-Agent, כאשר קלוד מחדש subagent, ובכל פעם שחבר צוות agent team בתוך התהליך מטפל בהודעה חדשה. תומך ב-matchers לסינון לפי שם סוג הסוכן. עבור סוכנים מובנים, זה שם הסוכן כמו general-purpose, Explore או Plan. עבור subagents מותאמים, זה שדה ה-name מה-frontmatter של הסוכן, לא שם הקובץ.

עבור subagents שמגיעים מ-plugin, סוג הסוכן הוא המזהה בטווח התוסף, כגון my-plugin:reviewer, ולא שם ה-frontmatter החשוף. הנקודתיים מציבות שם בטווח תוסף על נתיב הביטוי הרגולרי, לכן עגנו את ה-matcher עם ^ ו-$ להתאמה מדויקת: ^my-plugin:reviewer$.

#קלט SubagentStart

בנוסף ל-שדות הקלט המשותפים, הוקי SubagentStart מקבלים agent_id עם המזהה הייחודי של ה-subagent ו-agent_type עם שם הסוכן שעליו ה-matcher מסנן.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}

הוקי SubagentStart לא יכולים לחסום יצירת subagent, אבל הם יכולים להזריק הקשר לתוך ה-subagent. בנוסף ל-שדות פלט JSON הזמינים לכל ההוקים, אפשר להחזיר:

שדהתיאור
additionalContextמחרוזת שמתווספת להקשר של ה-subagent בתחילת השיחה שלו, לפני הפרומפט הראשון שלו. ראו הוספת הקשר לקלוד
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}

כאשר ההוק רץ שוב עבור אותו subagent, Claude Code מזריק את ההקשר שהוחזר רק כאשר ההקשר של ה-subagent עדיין לא מחזיק את העותק מהרצה קודמת. העותק שהוזרק בהפעלה נשאר במקום, ומשאיר את מטמון הפרומפט של ה-subagent שלם. אחרי ש-auto-compaction משליך את העותק הזה, Claude Code מזריק שוב את ההקשר של ההרצה הבאה.

#SubagentStop

רץ כאשר subagent של Claude Code סיים להגיב. מתאים לפי סוג סוכן, אותם ערכים כמו SubagentStart.

#קלט SubagentStop

בנוסף ל-שדות הקלט המשותפים, הוקי SubagentStop מקבלים stop_hook_active, agent_id, agent_type, agent_transcript_path ו-last_assistant_message. השדה agent_type הוא הערך שמשמש לסינון matcher. ה-transcript_path הוא התמליל של הסשן הראשי, בעוד agent_transcript_path הוא התמליל של ה-subagent עצמו, שמאוחסן בתיקיית subagents/ מקוננת. השדה last_assistant_message מכיל את תוכן הטקסט של התגובה הסופית של ה-subagent, כך שהוקים יכולים לגשת אליו בלי לנתח את קובץ התמליל.

לא כל אירוע SubagentStop מגיע מ-subagent שקלוד הפעיל. Claude Code גם מריץ סוכנים פנימיים עבור חלק מהתכונות שלו, כגון הצעות פרומפט ו-שאלות צד /btw, ו-SubagentStop נורה גם כאשר אחד מאלה מסתיים. באירועים האלה, agent_type הוא שם הסוכן שהסשן עצמו רץ בו, כגון שם שהוגדר עם --agent או הגדרת agent, ומחרוזת ריקה כאשר הסשן רץ בלי אחד.

matcher שמציין סוגי סוכן לא מתאים ל-agent_type ריק. הוק שה-matcher שלו הושמט, "" או "*", או שהוא ביטוי רגולרי שמתאים למחרוזת ריקה, רץ גם עבור אירועים עם agent_type ריק.

ב-Claude Code v2.1.271 ואילך, subagent שרץ עם כלי SubagentHandback מוסר את הדוח שלו דרך הכלי הזה לפני שהוא עוצר. השדה last_assistant_message מחזיק אז את טקסט הסיום של ה-subagent, אם יש, וזה אינו הדוח שנמסר. הדוח הוא קלט ה-message של הקריאה הזו, שהוק PreToolUse או PostToolUse שהותאם על SubagentHandback מקבל כ-tool_input.message.

הוקי SubagentStop מקבלים גם את המערכים background_tasks ו-session_crons שמתוארים תחת קלט Stop. שני המערכים מוגבלים לסשן האב, לא ל-subagent.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues...",
  "background_tasks": [],
  "session_crons": []
}

הוקי SubagentStop משתמשים באותו פורמט בקרת החלטה כמו הוקי Stop, כולל hookSpecificOutput.additionalContext עם hookEventName שמוגדר ל-"SubagentStop", למשוב שאינו שגיאה ומשאיר את ה-subagent רץ. החזרת decision: "block" עם reason משאירה את ה-subagent רץ ומוסרת את reason ל-subagent כהוראה הבאה שלו. הוק שחוסם על ידי יציאה עם קוד 2 מוסר את הודעת stderr שלו באותו אופן. כדי להזריק הקשר לסשן האב אחרי ש-subagent חוזר, השתמשו בהוק PostToolUse על כלי ה-Agent במקום זאת.

#TaskCreated

רץ כאשר משימה נוצרת דרך כלי TaskCreate. השתמשו בזה כדי לאכוף מוסכמות שמות, לדרוש תיאורי משימות, או למנוע יצירת משימות מסוימות. ב-סשן בלי כלי Task, האירוע הזה לא נורה.

הוקי TaskCreated לא תומכים ב-matchers ונורים בכל מופע.

#קלט TaskCreated

בנוסף ל-שדות הקלט המשותפים, הוקי TaskCreated מקבלים task_id, task_subject, ואופציונלית task_description, teammate_name ו-team_name.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "TaskCreated",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "session-a1b2c3d4"
}
שדהתיאור
task_idמזהה המשימה שנוצרת
task_subjectכותרת המשימה
task_descriptionתיאור מפורט של המשימה. עשוי להיעדר
teammate_nameשם חבר הצוות שיוצר את המשימה. עשוי להיעדר
team_nameDeprecated. שם צוות שנגזר מהסשן. יוסר במהדורה עתידית
agent_idבאירוע הזה, שדה הקלט המשותף מזהה את ה-subagent או את חבר הצוות בתוך התהליך שיוצר את המשימה. עשוי להיעדר. דורש Claude Code v2.1.290 ואילך

#בקרת החלטה של TaskCreated

הוק TaskCreated יכול לחסום את היצירה עם קוד יציאה 2 או עם החלטת JSON. בכל אחת מהדרכים, Claude Code מוחק את המשימה ומחזיר את ההודעה שלכם לקלוד כשגיאת הכלי. Claude Code מתעלם מ-continue: false מהאירוע הזה, וקלוד ממשיך לעבוד.

  • קוד יציאה 2: Claude Code מחזיר את טקסט stderr כהודעה.
  • JSON {"decision": "block", "reason": "..."}: Claude Code מחזיר את reason כהודעה.

הדוגמה הזו חוסמת משימות שה-subject שלהן לא עוקב אחרי הפורמט הנדרש:

#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
  echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
  exit 2
fi

exit 0

#TaskCompleted

רץ כאשר משימה מסומנת כמושלמת. זה נורה בשני מצבים: כאשר כל סוכן מסמן במפורש משימה כמושלמת דרך כלי TaskUpdate, או כאשר חבר צוות agent team מסיים את התור שלו עם משימות בסטטוס in-progress. השתמשו בזה כדי לאכוף קריטריוני השלמה כמו מעבר בדיקות או בדיקות lint לפני שמשימה יכולה להיסגר.

הוקי TaskCompleted לא תומכים ב-matchers ונורים בכל מופע.

#קלט TaskCompleted

בנוסף ל-שדות הקלט המשותפים, הוקי TaskCompleted מקבלים task_id, task_subject, ואופציונלית task_description, teammate_name ו-team_name.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "session-a1b2c3d4"
}
שדהתיאור
task_idמזהה המשימה שמושלמת
task_subjectכותרת המשימה
task_descriptionתיאור מפורט של המשימה. עשוי להיעדר
teammate_nameשם חבר הצוות שמשלים את המשימה. עשוי להיעדר
team_nameDeprecated. שם צוות שנגזר מהסשן. יוסר במהדורה עתידית
agent_idבאירוע הזה, שדה הקלט המשותף מזהה את ה-subagent או את חבר הצוות בתוך התהליך שמשלים את המשימה. עשוי להיעדר. דורש Claude Code v2.1.290 ואילך

#בקרת החלטה של TaskCompleted

הוקי TaskCompleted תומכים בשתי דרכים לשלוט בהשלמת משימה:

  • קוד יציאה 2: המשימה לא מסומנת כמושלמת, והודעת stderr מוחזרת למודל כמשוב.
  • JSON {"continue": false, "stopReason": "..."}: כאשר חבר צוות שמסיים את התור שלו הפעיל את האירוע, עוצר את חבר הצוות לגמרי, בהתאמה להתנהגות הוק Stop. ה-stopReason מוצג למשתמש. כאשר כלי TaskUpdate הפעיל את האירוע, Claude Code מתעלם מ-continue: false. קוד יציאה 2 עדיין חוסם את ההשלמה.

הדוגמה הזו מריצה בדיקות וחוסמת השלמת משימה אם הן נכשלות:

#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

# Run the test suite
if ! npm test 2>&1; then
  echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
  exit 2
fi

exit 0

#Stop

רץ כאשר סוכן Claude Code הראשי סיים להגיב. לא רץ אם העצירה קרתה בגלל הפסקת משתמש. שגיאות API מפעילות StopFailure במקום זאת.

[!TIP]

הפקודה /goal היא קיצור מובנה להוק Stop מבוסס פרומפט בטווח הסשן. השתמשו בה כאשר אתם רוצים שקלוד ימשיך לעבוד לקראת תנאי בלי לכתוב הגדרת הוק.

#קלט Stop

בנוסף ל-שדות הקלט המשותפים, הוקי Stop מקבלים stop_hook_active, last_assistant_message, background_tasks ו-session_crons. השדה stop_hook_active הוא true כאשר Claude Code כבר ממשיך כתוצאה מהוק stop. בדקו את הערך הזה או עבדו את התמליל כדי להימנע מחסימה על תנאי שלעולם לא ייפתר.

Claude Code מחיל תקרת 8 המשכים רצופים: אחרי שהוקי stop המשיכו את התור שמונה פעמים ברצף, Claude Code דורס את החסימה הבאה ומסיים את התור. ספירת ההמשכים הרצופים מתאפסת בכל פעם שקלוד קורא לכלי. כדי להעלות את התקרה, הגדירו CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.

השדה last_assistant_message מכיל את תוכן הטקסט של התגובה הסופית של קלוד, כך שהוקים יכולים לגשת אליו בלי לנתח את קובץ התמליל. עבור הוקים שפועלים על התור שזה עתה הושלם, כגון הוקי הקראה בקול או התראה, השתמשו בשדה הזה במקום לקרוא את transcript_path: קובץ התמליל לא מובטח שיכלול את ההודעה הסופית בזמן Stop בכל הגרסאות.

המערכים background_tasks ו-session_crons מאפשרים להוקים להבחין בין "הסשן הסתיים" לבין "הסשן מושהה ומחכה שעבודה ברקע תעיר אותו בחזרה". שני המערכים נוכחים כאשר מרשם המשימות נגיש, והם ריקים כאשר שום דבר לא בביצוע או מתוזמן.

כל רשומה ב-background_tasks מתארת משימה אחת בביצוע ומשתמשת בשדות האלה:

שדהתיאור
idמזהה משימה
typeתווית ידידותית של סוג משימה כגון shell, subagent, monitor, workflow, teammate, cloud session או MCP task. כל תווית מזהה איזו תכונה של Claude Code יצרה את המשימה. נופל חזרה ל-discriminant הגולמי עבור סוגים לא מזוהים
statusסטטוס המשימה הנוכחי
descriptionתיאור טקסט חופשי, מוגבל ל-1000 תווים עם סמן … [+N chars] בתוך המחרוזת כאשר נחתך
commandשורת פקודת shell, מוגבלת ל-1000 תווים. נוכחת רק עבור משימות shell
agent_typeשם סוג subagent. נוכח רק עבור משימות subagent
serverשם שרת MCP. נוכח רק עבור משימות monitor ו-MCP task
toolשם כלי MCP. נוכח רק עבור משימות monitor ו-MCP task
nameשם workflow. נוכח רק עבור משימות workflow

כל רשומה ב-session_crons מתארת התעוררות מתוזמנת אחת בטווח הסשן, שמקורה ב-CronCreate, ScheduleWakeup ו-/loop:

שדהתיאור
idמזהה משימת cron
scheduleביטוי cron, לדוגמה 0 9 * * 1-5
recurringfalse עבור התעוררויות חד פעמיות שה-schedule שלהן מקודד זמן ירייה יחיד, true עבור משימות שנורות מחדש בכל התאמה
promptהפרומפט שנשלח כאשר ה-cron נורה, מוגבל ל-1000 תווים עם אותו סמן … [+N chars]

הדוגמה הזו מציגה קלט Stop עם משימת shell אחת בביצוע ו-cron חוזר אחד:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true,
  "last_assistant_message": "I've completed the refactoring. Here's a summary...",
  "background_tasks": [
    {
      "id": "task-001",
      "type": "shell",
      "status": "running",
      "description": "tail logs",
      "command": "tail -f /var/log/syslog"
    }
  ],
  "session_crons": [
    {
      "id": "cron-001",
      "schedule": "0 9 * * 1-5",
      "recurring": true,
      "prompt": "check the build"
    }
  ]
}

#בקרת החלטה של Stop

הוקי Stop ו-SubagentStop יכולים לשלוט אם קלוד ממשיך. בנוסף ל-שדות פלט JSON הזמינים לכל ההוקים, סקריפט ההוק שלכם יכול להחזיר את השדות הספציפיים לאירוע האלה:

שדהתיאור
decision"block" מונע מקלוד לעצור. השמיטו כדי לאפשר לקלוד לעצור
reasonנדרש כאשר decision הוא "block". אומר לקלוד למה הוא צריך להמשיך
hookSpecificOutput.additionalContextמשוב שאינו שגיאה לקלוד. השיחה ממשיכה כדי שקלוד יוכל לפעול לפיו, אבל בניגוד ל-decision: "block" הוא מוצג בתמליל כמשוב הוק ולא כשגיאת הוק

הוק שחוסם על ידי יציאה עם קוד 2 מנותב באותו אופן כמו reason: קלוד מקבל את הודעת stderr כהסבר למה הוא צריך להמשיך.

{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

השתמשו ב-additionalContext כאשר ההוק עובד כמתוכנן ונותן לקלוד הנחיה, כגון "run the test suite before finishing". זה משאיר את השיחה פעילה דרך אותן הגנות לולאה כמו decision: "block", כלומר קלט stop_hook_active ותקרת 8 ההמשכים הרצופים, אבל התמליל מתייג אותו Stop hook feedback ולא מוצגת התראת שגיאת הוק:

{
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "Please run the test suite before finishing"
  }
}

#StopFailure

רץ במקום Stop כשהתור מסתיים בגלל שגיאת API. Claude Code מתעלם מפלט ה-hook ומקוד היציאה, מלבד terminalSequence. השתמשו בזה כדי לרשום כשלים, לשלוח התראות, או לבצע פעולות התאוששות כש-Claude לא יכול להשלים תגובה בגלל מגבלות קצב, בעיות אימות, או שגיאות API אחרות.

#קלט StopFailure

בנוסף לשדות הקלט המשותפים, הוקים של StopFailure מקבלים את error, את error_details האופציונלי, ואת last_assistant_message האופציונלי. השדה error מזהה את סוג השגיאה ומשמש לסינון matcher.

שדהתיאור
errorסוג השגיאה: rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error, או unknown
error_detailsפרטים נוספים על השגיאה, כשהם זמינים
last_assistant_messageטקסט השגיאה המוצג בשיחה. בניגוד ל-Stop ול-SubagentStop, שבהם השדה מחזיק את הפלט השיחתי של Claude, עבור StopFailure הוא מכיל את מחרוזת שגיאת ה-API עצמה, כמו "API Error: Rate limit reached"
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "StopFailure",
  "error": "rate_limit",
  "error_details": "429 Too Many Requests",
  "last_assistant_message": "API Error: Rate limit reached"
}

להוקים של StopFailure אין בקרת החלטה. הם רצים למטרות התראה ורישום בלבד.

#TeammateIdle

רץ כשחבר צוות בצוות סוכנים עומד לעבור למצב סרק אחרי שסיים את התור שלו. השתמשו בזה כדי לאכוף שערי איכות לפני שחבר צוות מפסיק לעבוד, למשל לדרוש בדיקות lint שעוברות או לוודא שקבצי פלט קיימים.

הוקים של TeammateIdle לא תומכים ב-matchers ורצים בכל התרחשות.

#קלט TeammateIdle

בנוסף לשדות הקלט המשותפים, הוקים של TeammateIdle מקבלים את teammate_name ואת team_name.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "session-a1b2c3d4"
}
שדהתיאור
teammate_nameשם חבר הצוות שעומד לעבור למצב סרק
team_nameDeprecated. שם צוות שנגזר מהסשן. יוסר במהדורה עתידית
agent_idבאירוע הזה, שדה הקלט המשותף מזהה את חבר הצוות בתוך התהליך שעומד לעבור למצב סרק. עשוי להיעדר. דורש Claude Code גרסה v2.1.290 או חדשה יותר

#בקרת החלטה של TeammateIdle

הוקים של TeammateIdle תומכים בשתי דרכים לשלוט בהתנהגות חבר הצוות:

  • קוד יציאה 2: חבר הצוות מקבל את הודעת stderr כמשוב וממשיך לעבוד במקום לעבור למצב סרק.
  • JSON {"continue": false, "stopReason": "..."}: עוצר את חבר הצוות לגמרי, בהתאמה להתנהגות hook של Stop. ה-stopReason מוצג למשתמש.

הדוגמה הזו בודקת שארטיפקט build קיים לפני שמאפשרים לחבר צוות לעבור למצב סרק:

#!/bin/bash

if [ ! -f "./dist/output.js" ]; then
  echo "Build artifact missing. Run the build before stopping." >&2
  exit 2
fi

exit 0

#ConfigChange

רץ כשקובץ תצורה משתנה במהלך סשן. השתמשו בזה כדי לבקר שינויי הגדרות, לאכוף מדיניות אבטחה, או לחסום שינויים לא מורשים בקבצי תצורה.

Claude Code מריץ הוקים של ConfigChange כשקובץ settings, קובץ מדיניות מנוהלת, או קובץ skill משתנים. עבור מדיניות מנוהלת, הוא מריץ אותם רק כש-managed-settings.json או קובץ ב-managed-settings.d/ משתנים. הוא מחיל הגדרות מנוהלות בשרת ושינויים בהעדפות מנוהלות של macOS או במדיניות הרישום של Windows בלי להריץ אותם. ב-WSL עם wslInheritsWindowsSettings, הוא גם מחיל קובץ הגדרות מנוהלות מצד Windows שהשתנה בסקר המדיניות שלו בלי להריץ אותם.

ה-matcher מסנן לפי מקור התצורה:

Matcherמתי הוא רץ
user_settings~/.claude/settings.json משתנה
project_settings.claude/settings.json משתנה
local_settings.claude/settings.local.json משתנה
policy_settingsmanaged-settings.json או קובץ ב-managed-settings.d/ משתנים
skillsקובץ skill ב-.claude/skills/ משתנה

הדוגמה הזו רושמת את כל שינויי התצורה לביקורת אבטחה:

{
  "hooks": {
    "ConfigChange": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

#קלט ConfigChange

בנוסף לשדות הקלט המשותפים, הוקים של ConfigChange מקבלים את source ובאופן אופציונלי את file_path. השדה source מציין איזה סוג תצורה השתנה, ו-file_path מספק את הנתיב לקובץ הספציפי ששונה.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ConfigChange",
  "source": "project_settings",
  "file_path": "/Users/.../my-project/.claude/settings.json"
}

#בקרת החלטה של ConfigChange

הוקים של ConfigChange יכולים לחסום שינויי תצורה כך שלא ייכנסו לתוקף. השתמשו בקוד יציאה 2 או ב-JSON עם decision כדי למנוע את השינוי. כשנחסם, ההגדרות החדשות לא מוחלות על הסשן הרץ.

שדהתיאור
decision"block" מונע את החלת שינוי התצורה. השמיטו כדי לאפשר את השינוי
reasonמתקבל אבל אף פעם לא מוצג
{
  "decision": "block",
  "reason": "Configuration changes to project settings require admin approval"
}

שינויי policy_settings לא ניתנים לחסימה. הוקים עדיין רצים עבור מקורות policy_settings כשקובץ הגדרות מנוהלות במכונה משתנה, כך שאפשר להשתמש בהם כדי לרשום את העריכות האלה, אבל כל החלטת חסימה מתעלמים ממנה. זה מבטיח שהגדרות מנוהלות בארגון תמיד ייכנסו לתוקף. Claude Code לא מריץ הוקים של ConfigChange כשהגדרות מנוהלות בשרת מגיעות או מתרעננות.

Claude Code פועל לפי החלטת החסימה מפלט ה-JSON של hook מסוג ConfigChange ומשליך את systemMessage ואת continue. שינוי שנחסם לא מציג שום הודעה לכם או ל-Claude, בין אם חסמתם עם reason ובין אם עם stderr בקוד יציאה 2. Claude Code כותב רק שורה ללוג הדיבוג.

#CwdChanged

רץ כשפקודת shell בשיחה הראשית משנה את ספריית העבודה, למשל כש-Claude מריץ פקודת cd. השתמשו בזה כדי להגיב לשינויי ספרייה: לטעון מחדש משתני סביבה, להפעיל toolchains ספציפיים לפרויקט, או להריץ סקריפטי setup אוטומטית. משתלב עם FileChanged עבור כלים כמו direnv שמנהלים סביבה לפי ספרייה.

להוקים של CwdChanged יש גישה ל-CLAUDE_ENV_FILE. אם ה-hook כותב משתנים לקובץ הזה, הם נשמרים לפקודות Bash הבאות עד אירוע CwdChanged הבא, ואז Claude Code מנקה אותם.

CwdChanged לא תומך ב-matchers ורץ בכל התרחשות.

#קלט CwdChanged

בנוסף לשדות הקלט המשותפים, הוקים של CwdChanged מקבלים את old_cwd ואת new_cwd.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project/src",
  "hook_event_name": "CwdChanged",
  "old_cwd": "/Users/my-project",
  "new_cwd": "/Users/my-project/src"
}

#פלט CwdChanged

בנוסף לשדות פלט JSON הזמינים לכל ההוקים, הוקים של CwdChanged יכולים להחזיר watchPaths כדי להגדיר באופן דינמי אילו נתיבי קבצים FileChanged צופה בהם:

שדהתיאור
watchPathsמערך של נתיבים מוחלטים. מחליף את רשימת הצפייה הדינמית הנוכחית. נתיבים מתצורת ה-matcher שלכם תמיד נצפים. החזרת מערך ריק מנקה את הרשימה הדינמית, וזה אופייני בכניסה לספרייה חדשה

להוקים של CwdChanged אין בקרת החלטה. הם לא יכולים לחסום את שינוי הספרייה.

Claude Code קורא את watchPaths ואת systemMessage מפלט ה-JSON שלהם ומשליך את continue. בסשנים אינטראקטיביים, הוא מציג את ה-systemMessage כהתראת טרמינל קצרה. ההודעה לא מגיעה לזרם ההודעות של ה-SDK.

#DirectoryAdded

רץ אחרי שמוסיפים ספריית עבודה באמצע הסשן עם הפקודה /add-dir, או אחרי שלקוח SDK מוסיף אחת עם בקשת הבקרה register_repo_root. השתמשו בזה כדי להכין מאגר שנוסף זה עתה, למשל על ידי התקנת התלויות שלו.

Claude Code לא מפעיל את האירוע הזה כש:

  • מעבירים ספרייה עם דגל ההפעלה --add-dir. SessionStart מכסה את הספריות האלה
  • מוסיפים ספרייה בלשונית Workspace של /permissions
  • מוסיפים ספרייה שכבר היא ספריית עבודה או נמצאת בתוך אחת

Claude Code מפעיל את DirectoryAdded אחרי רענון מצב ה-sandbox וההרשאות, כך שכלים בתוך sandbox כבר רואים את הספרייה החדשה כשה-hook רץ. פקודות ה-hook עצמן רצות בלי sandbox.

Claude Code לא מחכה ל-hook: ההוספה מסתיימת מיד, וה-hook רץ ברקע עם פסק הזמן ברירת המחדל של 600 שניות.

ה-matcher מסנן לפי האופן שבו הספרייה נוספה:

Matcherמתי הוא רץ
slash_commandמוסיפים ספרייה עם /add-dir
register_repo_rootלקוח SDK מוסיף ספרייה עם בקשת הבקרה register_repo_root

#קלט DirectoryAdded

בנוסף לשדות הקלט המשותפים, הוקים של DirectoryAdded מקבלים את directory ואת source.

שדהתיאור
directoryנתיב מוחלט של הספרייה שנוספה
sourceאיך הספרייה נוספה, "slash_command" עבור /add-dir או "register_repo_root" עבור בקשת הבקרה של ה-SDK
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "DirectoryAdded",
  "directory": "/Users/my-other-repo",
  "source": "slash_command"
}

להוקים של DirectoryAdded אין בקרת החלטה. הם לא יכולים לחסום את ההוספה, שכבר הושלמה כשה-hook רץ. Claude Code משליך את השדה continue מפלט ה-JSON שלהם ומציג את השאר אחרת לפי המקור:

  • slash_command: Claude Code מעביר את ה-systemMessage של ה-hook ל-Claude כהקשר בתור השיחה הבא, במקום להציג אותו לכם. ספירה של הוקים שנכשלו מופיעה בתמליל. פלט כשל מלא הולך ללוג הדיבוג
  • register_repo_root: Claude Code כותב את פלט systemMessage ואת פלט הכשל ללוג הדיבוג בלבד

#FileChanged

רץ כשקובץ נצפה משתנה בדיסק. Claude Code מזהה שינויים עם filesystem watcher, לא על ידי בדיקת קריאות כלים, כך שהוא מריץ את ה-hook בלי קשר למה ששינה את הקובץ: קריאת כלי Edit או Write, סקריפט ש-Claude מריץ עם Bash, או תהליך מחוץ ל-Claude Code לגמרי. שימוש נפוץ הוא טעינה מחדש של משתני סביבה כשקובצי תצורת פרויקט משתנים.

ה-matcher לאירוע הזה ממלא שני תפקידים:

  • בניית רשימת הצפייה: הערך מפוצל על | וכל מקטע נרשם כשם קובץ מילולי בספריית העבודה, כך ש-".envrc|.env" צופה בדיוק בשני הקבצים האלה. תבניות regex לא שימושיות כאן: ערך כמו ^\.env יצפה בקובץ ששמו המילולי הוא ^\.env.
  • סינון אילו הוקים רצים: כשקובץ נצפה משתנה, אותו ערך מסנן אילו קבוצות הוקים רצות לפי כללי ה-matcher הרגילים מול שם הבסיס של הקובץ שהשתנה.

הדוגמה הזו מנרמלת סיומי שורות ב-data.csv אחרי כל שינוי, כולל פקודת Bash או סקריפט חיצוני שכותב מחדש את הקובץ:

{
  "hooks": {
    "FileChanged": [
      {
        "matcher": "data.csv",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/normalize-line-endings.sh"
          }
        ]
      }
    ]
  }
}

ה-hook קורא את הנתיב המוחלט של הקובץ שהשתנה מהשדה file_path של קלט ה-JSON ב-stdin. מגן ה-grep שלו בודק את אותו דבר ש-perl מסיר, CR בסוף שורה, כך שההרצה אחרי נרמול יוצאת בלי לגעת בקובץ. מגן רופף יותר נכנס ללולאה אינסופית, כי perl -i כותב מחדש את הקובץ גם כשהוא לא מחליף כלום ו-Claude Code מריץ את ה-hook שוב אחרי כל כתיבה מחדש. שמרו את הסקריפט הזה ב-/path/to/normalize-line-endings.sh והפכו אותו לניתן להרצה:

#!/bin/bash
FILE=$(jq -r .file_path)
if grep -q $'\r$' "$FILE"; then
  perl -pi -e 's/\r$//' "$FILE"
fi

כדי לאשר שה-hook עובד, בקשו מ-Claude לצרף שורת CRLF ל-data.csv עם פקודת Bash. Claude Code מריץ את ה-hook והקובץ מסתיים עם סיומי LF.

כדי לצפות בקבצים שאי אפשר לנקוב בשמם מראש, החזירו watchPaths מ-hook כדי לעדכן את רשימת הצפייה באופן דינמי. Claude Code מפעיל את ה-watcher רק כשמשהו מנקב בשם קובץ לצפייה, אז זרעו את הרשימה עם קבוצת FileChanged שה-matcher שלה מנקב בשם של לפחות קובץ אחד, או עם hook של SessionStart או CwdChanged שמחזיר watchPaths. ה-matcher עדיין מסנן אילו קבוצות הוקים רצות כשקובץ נצפה משתנה, אז תנו לקבוצה שמטפלת בנתיבים דינמיים matcher שהושמט, שמתאים לכל קובץ נצפה ולא מוסיף כלום לרשימת הצפייה. matcher של "*" גם מתאים לכל קובץ, אבל Claude Code רושם אותו ברשימת הצפייה כמו כל ערך אחר, כקובץ מילולי בשם *.

להוקים של FileChanged יש גישה ל-CLAUDE_ENV_FILE. משתנים שנכתבים לקובץ הזה נשמרים לפקודות Bash הבאות עד אירוע CwdChanged הבא, ואז Claude Code מנקה אותם.

#קלט FileChanged

בנוסף לשדות הקלט המשותפים, הוקים של FileChanged מקבלים את file_path ואת event.

שדהתיאור
file_pathנתיב מוחלט לקובץ שהשתנה
eventמה קרה: "change" לקובץ ששונה, "add" לקובץ שנוצר, או "unlink" לקובץ שנמחק
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "FileChanged",
  "file_path": "/Users/my-project/.envrc",
  "event": "change"
}

#פלט FileChanged

בנוסף לשדות פלט JSON הזמינים לכל ההוקים, הוקים של FileChanged יכולים להחזיר watchPaths כדי לעדכן באופן דינמי אילו נתיבי קבצים נצפים:

שדהתיאור
watchPathsמערך של נתיבים מוחלטים. מחליף את רשימת הצפייה הדינמית הנוכחית. נתיבים מתצורת ה-matcher שלכם תמיד נצפים. השתמשו בזה כשסקריפט ה-hook מגלה קבצים נוספים לצפייה על סמך הקובץ שהשתנה

להוקים של FileChanged אין בקרת החלטה. הם לא יכולים לחסום את שינוי הקובץ מלהתרחש.

Claude Code קורא את watchPaths ואת systemMessage מפלט ה-JSON שלהם ומשליך את continue. בסשנים אינטראקטיביים, הוא מציג את ה-systemMessage כהתראת טרמינל קצרה. ההודעה לא מגיעה לזרם ההודעות של ה-SDK.

#WorktreeCreate

רץ כאשר Claude Code יוצר worktree במקרים כמו אלה:

הוק WorktreeCreate לא רץ עבור worktree ש-Claude מוסיף דרך Bash, והוק של תוסף לא רץ עבור זה שנוצר כשאתה מפעיל עם --worktree ועם --tmux:

  • git worktree add דרך Bash: כדי לבדוק או לחסום את הפקודה הזו, הוסף הוק PreToolUse עם ה-matcher Bash.
  • --worktree עם --tmux: Claude Code יוצר את ה-worktree לפני שהוא טוען תוספים, ולכן הוק WorktreeCreate מתוסף לא רץ. כדי שההוק שלך ירוץ עם שני הדגלים, הגדר אותו בקובץ הגדרות במקום זאת. לפני v2.1.300, הוק WorktreeCreate של תוסף לא רץ גם עם --worktree לבד.

כברירת מחדל Claude Code יוצר את עותק העבודה המבודד עם git worktree. הגדרת הוק WorktreeCreate מחליפה את התנהגות ה-git ברירת המחדל הזו, ומאפשרת לך להשתמש במערכת בקרת גרסאות אחרת כמו SVN, Perforce, או Mercurial.

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

ההוק חייב להחזיר את הנתיב לתיקיית ה-worktree שנוצרה. Claude Code משתמש בנתיב הזה כתיקיית העבודה של הסשן המבודד. ראה פלט WorktreeCreate לאופן שבו כל סוג הוק מחזיר את הנתיב.

Claude Code פועל לפי הצלחת ההוק ולפי הנתיב שהוחזר, ומשליך את systemMessage ואת continue.

הדוגמה הזו יוצרת עותק עבודה של SVN ומדפיסה את הנתיב כדי ש-Claude Code ישתמש בו. החלף את כתובת ה-URL של המאגר בזו שלך:

{
  "hooks": {
    "WorktreeCreate": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
          }
        ]
      }
    ]
  }
}

ההוק קורא את name של ה-worktree מקלט ה-JSON ב-stdin, מבצע checkout לעותק חדש לתוך תיקייה חדשה, ומדפיס את נתיב התיקייה. ה-echo בשורה האחרונה הוא מה ש-Claude Code קורא כנתיב ה-worktree. הפנה כל פלט אחר אל stderr כדי שהוא לא יפריע לנתיב.

#קלט WorktreeCreate

בנוסף לשדות הקלט המשותפים, הוקי WorktreeCreate מקבלים את השדה name. זהו מזהה slug עבור ה-worktree החדש, שצוין על ידי המשתמש או שנוצר אוטומטית, לדוגמה bold-oak-a3f2.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeCreate",
  "name": "feature-auth"
}

#פלט WorktreeCreate

הוקי WorktreeCreate לא משתמשים במודל ההחלטה הרגיל של allow/block. במקום זאת, הצלחה או כישלון של ההוק קובעים את התוצאה. ההוק חייב להחזיר את הנתיב לתיקיית ה-worktree שנוצרה:

  • הוקי command (type: "command"): הדפס את הנתיב כשורה האחרונה שאינה ריקה ב-stdout. Claude Code מסיר קודי מילוט של ANSI לפני שהוא קורא את השורה הזו, כך שבאנרים של הפעלת מעטפת שהודפסו לפני ה-echo שלך מתעלמים מהם. הפנה כל פלט אחר של ההוק אל stderr.
  • הוקי HTTP (type: "http"): החזר { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } } בגוף התשובה.

אם ההוק נכשל או לא מייצר נתיב, יצירת ה-worktree נכשלת עם שגיאה. Claude Code פותר נתיב יחסי ביחס לתיקייה שבה ההוק רץ, ומצמצם כל מקטעי . או .. בתוכו. אם הנתיב שמתקבל אינו תיקייה ש-Claude Code יכול להיכנס אליה, הסשן מדפיס שגיאה שמציינת את הנתיב ויוצא עם קוד 1.

Claude Code מסרב לנתיב מוחלט שמכיל מקטעי . או .., ולכל נתיב שעובר דרך symlink מתחת לשורש המאגר. השגיאה מציינת את הרכיב שנדחה. symlink ש-commit שלו נמצא במאגר יכול להפנות את ה-worktree אל מחוץ לו. החזר נתיב מנורמל שלא עובר דרך symlink בתוך המאגר.

#WorktreeRemove

רץ כאשר Claude Code מנקה worktree שההוק WorktreeCreate שלך יצר. האירוע נורה כאשר:

  • אתה יוצא מסשן worktree אינטראקטיבי ובוחר להסיר את ה-worktree כאשר Claude Code מבקש ממך
  • אתה יוצא מסשן worktree אינטראקטיבי שלא נתת לו שם, Claude Code לא מוצא קבצים שהשתנו או קבצים שלא במעקב, והוא מסיר את ה-worktree בלי לבקש ממך
  • אתה מוחק סשן רקע שרץ ב-worktree
  • אתה מבקש מ-Claude לצאת ולהסיר את ה-worktree במהלך סשן, ו-Claude מסיר אותו עם הכלי ExitWorktree
  • סשן ש-claude remote-control התחיל במצב worktree מסתיים בלי לקרוס, ו-Claude Code לא מוצא קבצים שהשתנו או קבצים שלא במעקב ואין קומיטים חדשים ב-worktree שלו
  • אתה עוצר את claude remote-control בזמן שסשנים שהוא התחיל במצב worktree עדיין רצים, ו-Claude Code לא מוצא קבצים שהשתנו או קבצים שלא במעקב ואין קומיטים חדשים ב-worktrees שלהם

כאשר סוכן משנה או סוכן workflow מסיים, Claude Code שומר את ה-worktree שההוק WorktreeCreate שלך יצר עבורו ולא מריץ את ההוק WorktreeRemove שלך. הסר את ה-worktrees האלה בעצמך כשהעבודה הסתיימה.

Claude Code לא מבקש ממך לפני שההוק שלך רץ עבור קריאה ל-ExitWorktree או עבור ניקוי של claude remote-control. ExitWorktree מסרב להסיר worktree שנוצר על ידי הוק אלא אם הקריאה מעבירה discard_changes: true, מה שהופך את ההוק שלך לבדיקה האחרונה במסלול הזה.

Claude Code משתמש ב-git כדי לחפש קבצים שהשתנו או קבצים שלא במעקב, ולכן הוא לא מוצא כאלה ב-worktree שאינו checkout של git או בתוך אחד כזה, גם כאשר התיקייה מכילה עבודה שלא עברה commit. בדוק את העבודה הזו בהוק WorktreeRemove שלך לפני שהוא מוחק משהו.

עבור worktrees מבוססי git, Claude Code מטפל בניקוי אוטומטית עם git worktree remove. אם הגדרת הוק WorktreeCreate, צמד אותו עם הוק WorktreeRemove כדי לשלוט בניקוי:

  • אין הוק WorktreeRemove: Claude Code חוזר ל-git עבור הסרה ביציאה או דרך ExitWorktree, ושומר את ה-worktree כאשר claude remote-control מנקה:
    • אתה יוצא מסשן worktree, או Claude קורא ל-ExitWorktree: Claude Code מסיר את הנתיב שההוק WorktreeCreate שלך החזיר כפי ש-git worktree remove --force היה עושה, כך ש-worktree ש-git מזהה מוסר. worktree ש-git לא מזהה, לדוגמה אחד שההוק שלך יצר עם מערכת בקרת גרסאות שאינה git, נשאר על הדיסק.
    • claude remote-control מנקה את ה-worktree של סשן: ה-worktree נשאר על הדיסק, והטרמינל שמריץ את claude remote-control מציג worktree removal failed, kept: <path>.
    • אתה מוחק סשן רקע: ראה מה מחיקת סשן מסירה.
  • ההוק יוצא עם 0: ה-worktree נחשב כמוסר. Claude Code לא קורא שום דבר אחר מההוק, אז ודא שההוק שלך מחק את התיקייה.
  • ההוק יוצא עם קוד שאינו אפס: ההסרה נכשלת אם התיקייה ב-worktree_path עדיין קיימת אחר כך, וה-worktree נשאר על הדיסק בלי חזרה ל-git. הוק שמחק את התיקייה לפני יציאה עם קוד שאינו אפס נחשב כמוסר. לאופן הדיווח על הכישלון, ראה קלט WorktreeRemove.

Claude Code לעולם לא מוחק ענף ששייך ל-worktree שנוצר על ידי הוק, כי הוא יודע רק את הנתיב שההוק WorktreeCreate שלך החזיר. אם ההוק WorktreeCreate שלך יוצר ענף, מחק אותו בהוק WorktreeRemove שלך. Claude Code משליך שדות פלט JSON של הוק WorktreeRemove, כמו systemMessage ו-continue.

עבור מחיקת סשן רקע, Claude Code מאמת את נתיב ה-worktree השמור לפני הרצת ההוק, ומסרב לנתיב שהוא symlink או שעובר דרך אחד מתחת לשורש המאגר. במסלול הזה, ההוק רץ עבור worktree שעדיין מכיל קבצים רק כאשר אתה מאשר את המחיקה בתצוגת הסוכן. עבור worktree כזה, claude rm שומר את הסשן ואת ה-worktree במקום זאת.

Claude Code מעביר את הנתיב שהחזיר WorktreeCreate כ-worktree_path בקלט ההוק. הדוגמה הזו קוראת את הנתיב הזה ומסירה את התיקייה:

{
  "hooks": {
    "WorktreeRemove": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
          }
        ]
      }
    ]
  }
}

#קלט WorktreeRemove

בנוסף לשדות הקלט המשותפים, הוקי WorktreeRemove מקבלים את השדה worktree_path, שהוא הנתיב המוחלט ל-worktree שמוסר.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeRemove",
  "worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}

קוד היציאה של הוק WorktreeRemove קובע את התוצאה. כאשר הוק יוצא עם קוד שאינו אפס והתיקייה ב-worktree_path עדיין קיימת אחר כך, ההסרה נכשלת:

  • ה-worktree נשאר על הדיסק, והפקודה וה-stderr של ההוק הולכים אל יומן הדיבאג.
  • אם מחקת סשן רקע, גם הסשן נשאר. הודעת הסירוב בתצוגת הסוכן מדווחת איך ההוק הסתיים, כמו exited 1, מצטטת את תחילת ה-stderr שלו, ואומרת אם מחיקת הסשן שוב מסירה את התיקייה בכל זאת.
  • אם claude remote-control ניקה את ה-worktree של סשן, הטרמינל שמריץ אותו מציג worktree removal failed, kept: <path>.

#PreCompact

רץ לפני ש-Claude Code עומד להריץ פעולת compact.

ערך ה-matcher מציין אם ה-compaction הופעל ידנית או אוטומטית:

Matcherמתי הוא נורה
manual/compact
autocompact אוטומטי כאשר השיחה מגיעה לחלון ה-auto-compact, ו-idle compaction

צא עם קוד 2 כדי לחסום compaction. עבור /compact ידני, הודעת ה-stderr מוצגת למשתמש. אפשר גם לחסום על ידי החזרת JSON עם "decision": "block".

לחסימת compaction אוטומטי יש השפעות שונות בהתאם למועד שבו הוא נורה. אם ה-compaction הופעל באופן יזום לפני מגבלת ההקשר, Claude Code מדלג עליו והשיחה ממשיכה בלי compaction. אם ה-compaction הופעל כדי להתאושש משגיאת מגבלת הקשר שכבר הוחזרה על ידי ה-API, השגיאה הבסיסית מופיעה והבקשה הנוכחית נכשלת.

Claude Code משליך את השדות systemMessage ו-continue של הוק PreCompact.

#קלט PreCompact

בנוסף לשדות הקלט המשותפים, הוקי PreCompact מקבלים את trigger ואת custom_instructions. עבור manual, custom_instructions מכיל את מה שהמשתמש מעביר אל /compact והוא null כאשר הם לא מעבירים כלום. עבור auto, custom_instructions הוא null.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": null
}

#PostCompact

רץ אחרי ש-Claude Code משלים פעולת compact. השתמש באירוע הזה כדי להגיב למצב ה-compacted החדש, לדוגמה כדי לרשום ביומן את הסיכום שנוצר או לעדכן מצב חיצוני. Claude Code משליך את השדות systemMessage ו-continue של הוק PostCompact.

אותם ערכי matcher חלים כמו עבור PreCompact:

Matcherמתי הוא נורה
manualאחרי /compact
autoאחרי auto-compact כאשר השיחה מגיעה לחלון ה-auto-compact, ואחרי idle compaction

#קלט PostCompact

בנוסף לשדות הקלט המשותפים, הוקי PostCompact מקבלים את trigger ואת compact_summary. השדה compact_summary מכיל את סיכום השיחה שנוצר על ידי פעולת ה-compact.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}

להוקי PostCompact אין שליטה בהחלטה. הם לא יכולים להשפיע על תוצאת ה-compaction אבל יכולים לבצע משימות המשך.

#PreModelSwitch

רץ לפני שקלוד קוד מחיל החלפת מודל שאתם או לקוח ביקשתם. השתמשו בו כדי לחסום החלפה, לדרוש אישור, או להציג כמה ההחלפה תעלה לפני שהיא מתרחשת.

PreModelSwitch דורש את קלוד קוד בגרסה v2.1.251 ומעלה. קלוד קוד מריץ אותו עבור הבקשות האלה:

  • /model <name> ובורר /model
  • בורר המודל של Option+P או Alt+P
  • הגדרת Model בתוך /config
  • הפעלת מצב מהיר כשהיא משנה את מודל הסשן
  • בקשת set_model, או שינוי מודל בבקשת apply_flag_settings, ממארח Agent SDK או מ-Remote Control

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

קלוד קוד משווה את ה-matcher מול השם הקנוני של המודל שאליו הסשן עובר, ומתעלם מסיומת [1m] כלשהי. כינוי כמו opus, מזהה מודל עם תאריך, ומזהה ספציפי לספק כמו מזהה מודל של Amazon Bedrock, כולם תואמים את השם הקנוני האחד שאליו הם נפתרים, ולכן claude-opus-5 מכסה כל כתיב של Opus 5.

כשקלוד קוד לא יכול לקבוע שם קנוני ליעד, למשל מזהה מודל מותאם שרק שער ה-LLM שלכם מכיר, הוא מריץ כל הוק PreModelSwitch בלי תלות ב-matcher. הוק שחוסם צריך לכן לבדוק את to_model מהקלט שלו, ולא להסתמך על ה-matcher לבדו.

כתבו את ה-matcher כשם מדויק, כרשימה מופרדת ב-| כמו claude-opus-4-6|claude-opus-5, או כביטוי רגולרי כמו .*opus.*. הדוגמה הזו משתמשת ב-matcher של שם מדויק וגם בודקת את to_model מקלט ההוק, כך שהיא מסרבת להחלפה אל Opus 4.6 על ידי יציאה עם קוד 2 ומעבירה כל יעד אחר:

[tab] macOS/Linux

הפקודה בודקת את to_model עם jq:

{
  "hooks": {
    "PreModelSwitch": [
      {
        "matcher": "claude-opus-4-6",
        "hooks": [
          {
            "type": "command",
            "command": "jq -e '.to_model | test(\"opus-4-6\")' > /dev/null && { echo 'Opus 4.6 is retired for this project. Use a newer model.' >&2; exit 2; }; exit 0"
          }
        ]
      }
    ]
  }
}

[tab] Windows (PowerShell)

רשמו הוק פקודה שמריץ סקריפט דרך PowerShell:

{
  "hooks": {
    "PreModelSwitch": [
      {
        "matcher": "claude-opus-4-6",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-opus-46.ps1"
            ]
          }
        ]
      }
    ]
  }
}

שמרו את הסקריפט הזה אל .claude/hooks/block-opus-46.ps1 בפרויקט שלכם:

$hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
if ($hookInput.to_model -match 'opus-4-6') {
  [Console]::Error.WriteLine('Opus 4.6 is retired for this project. Use a newer model.')
  exit 2
}
exit 0

כדי לוודא שההוק עובד, הריצו /model claude-opus-4-6 מסשן שרץ על מודל אחר. קלוד קוד שומר את המודל הנוכחי ומדווח שהוק PreModelSwitch חסם את ההחלפה, עם ההודעה שלכם כסיבה.

#קלט PreModelSwitch

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

שדהסוגתיאור
from_modelstringמזהה המודל שממנו ההחלפה משנה
to_modelstringמזהה המודל שאליו ההחלפה משנה. ה-matcher מושווה מול השם הקנוני של המודל הזה
requested_modelstring או nullהמודל שהבקשה נקבה בשמו: כינוי כמו opus, מזהה מודל מלא, או null כשהבקשה הייתה למודל ברירת המחדל
sourcestringמאיפה הבקשה הגיעה: "command" עבור /model <name>, הגדרת Model בתוך /config, או הפעלת מצב מהיר; "picker" עבור בורר מודל; "sdk" עבור בקשת set_model, או שינוי מודל בבקשת apply_flag_settings, ממארח Agent SDK או מ-Remote Control
context_tokensnumberטוקנים שהבקשה הבאה שולחת מחדש כפרומפט שלה: הטוקנים של הקלט, קריאת המטמון, יצירת המטמון והפלט של התשובה האחרונה בשיחה הראשית, יחד. 0 לפני התשובה הראשונה
prompt_cache_warmbooleanהאם מטמון הפרומפט של המודל הנוכחי כנראה עדיין חם, כלומר ההחלפה מוותרת עליו
cache_ttlstringמשך חיי מטמון הפרומפט של השיחה הראשית בזמן ההחלפה הזו: "5m" או "1h"
estimated_cache_write_usdnumberעלות משוערת בדולר אמריקאי של כתיבת context_tokens אל מטמון הפרומפט על to_model בתעריף cache_ttl, בלי התשובה הבאה. השרת אולי לא צריך לשמור מחדש במטמון את כל ההקשר, ולכן התייחסו לזה כהערכה
pricingstringאיך קלוד קוד תמחר את estimated_cache_write_usd: "configured" בתעריפים של הארגון שלכם כשהוא הגדיר אותם, "catalog" במחיר מחירון, או "default" כשל-to_model אין מחיר ידוע וקלוד קוד הניח תעריף ברירת מחדל

הדוגמה הזו מציגה את הקלט עבור /model opus בסשן שרץ על Sonnet 5:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreModelSwitch",
  "from_model": "claude-sonnet-5",
  "to_model": "claude-opus-5",
  "requested_model": "opus",
  "source": "command",
  "context_tokens": 182340,
  "prompt_cache_warm": true,
  "cache_ttl": "5m",
  "estimated_cache_write_usd": 1.1396,
  "pricing": "catalog"
}

#בקרת החלטה של PreModelSwitch

הוקים של PreModelSwitch יכולים לבטל את ההחלפה, לבקש מהמשתמש לאשר אותה, או לתת לה להמשיך. קוד יציאה 2 או decision: "block" ברמה העליונה מבטל את ההחלפה.

לשליטה עדינה יותר, החזירו permissionDecision ו-permissionDecisionReason באובייקט hookSpecificOutput, כמו ב-PreToolUse. PreModelSwitch מקבל "allow", "deny" ו-"ask". הוא לא מקבל "defer", updatedInput או additionalContext. הטבלה למטה מתארת את שני השדות:

שדהתיאור
permissionDecision"allow" ממשיך ומדלג על האישור שקלוד קוד מציג בזמן שמטמון הפרומפט חם. "deny" מבטל את ההחלפה. "ask" מבקש מהמשתמש לאשר אותה
permissionDecisionReasonעבור "deny", מוצג למשתמש כסיבה שההחלפה נחסמה, או מוחזר כשגיאה עבור בקשת set_model. עבור "ask", מוצג בהנחיית האישור. מתעלמים ממנו עבור "allow"

רק /model בסשן אינטראקטיבי יכול להציג את הנחיית "ask". בכל משטח אחר, כולל מצב לא אינטראקטיבי עם הדגל -p, /config ובקשות set_model, קלוד קוד מתייחס אל "ask" כסירוב.

הדוגמה הזו מבקשת מהמשתמש לאשר ומצטטת את ספירת הטוקנים מ-context_tokens:

{
  "hookSpecificOutput": {
    "hookEventName": "PreModelSwitch",
    "permissionDecision": "ask",
    "permissionDecisionReason": "Switching now re-sends about 180k tokens to the new model. Continue?"
  }
}

כשכמה הוקים של PreModelSwitch מחזירים החלטות שונות, סדר העדיפות הוא deny > ask > allow.

קלוד קוד מציג למשתמש כל systemMessage שההוק שלכם מחזיר בלי תלות בהחלטה, כך שהוק לדיווח עלות יכול להחזיר {"systemMessage": "..."} ולצאת עם 0.

#פסקי זמן של PreModelSwitch וקודי יציאה אחרים

הוק PreModelSwitch שלא מגיב לפני פסק הזמן שלו חוסם את ההחלפה. פסק הזמן ברירת המחדל לאירוע הזה הוא 30 שניות.

קוד יציאה שאינו 0 או 2 בלי פלט JSON תקין הוא שגיאה לא חוסמת, כך שההחלפה ממשיכה, חוץ מהוקים שהכשל שלהם חוסם אותה.

#PostModelSwitch

רץ אחרי שמודל הסשן משתנה. השתמשו בו כדי לתת לקלוד הנחיה ספציפית למודל בלי לערוך כל CLAUDE.md, למשל הוראה כלל ארגונית שחלה על מודלים מסוימים.

PostModelSwitch דורש את קלוד קוד בגרסה v2.1.251 ומעלה. הוא לא יכול לחסום, כי המודל כבר השתנה. קלוד קוד מריץ הוקים של PostModelSwitch אחרי כל אחד מהשינויים האלה:

  • החלפה שאתם או לקוח ביקשתם
  • נפילה אוטומטית למודל, שמשנה את מודל הסשן
  • הגדרה כמו opusplan שנכנסת למצב תכנון או יוצאת ממנו
  • קלוד קוד משחזר את המודל כשאתם ממשיכים סשן

קלוד קוד לא מריץ הוקים של PostModelSwitch כשמודל מתוך שרשרת מודלי נפילה משרת תור, כי ההחלפה הזו נמשכת תור אחד ומשאירה את מודל הסשן ללא שינוי.

ה-matcher פועל לפי אותם כללים כמו PreModelSwitch: קלוד קוד משווה אותו מול השם הקנוני של המודל שאליו הסשן עבר.

הדוגמה הזו מוסיפה הנחיה בכל פעם שמודל הסשן משתנה לכל מודל Opus:

{
  "hooks": {
    "PostModelSwitch": [
      {
        "matcher": ".*opus.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'On Opus, delegate implementation work to subagents and keep this conversation for planning and review.'"
          }
        ]
      }
    ]
  }
}

כדי לוודא שההוק עובד, עברו למודל Opus מסשן שרץ על מודל אחר, למשל הריצו /model opus מסשן Sonnet, ואז שאלו את קלוד איזו הנחיה יש לו על המודל הנוכחי.

#קלט PostModelSwitch

הוקים של PostModelSwitch מקבלים את אותם שדות כמו PreModelSwitch, עם hook_event_name שמוגדר ל-"PostModelSwitch" ושני ערכי source נוספים: "auto" עבור נפילה אוטומטית או שינוי אחר שקלוד קוד ביצע בעצמו, ו-"resume" עבור המודל ששוחזר כשאתם ממשיכים סשן.

requested_model הוא null כש-source הוא "auto". כש-source הוא "resume", זהו הגדרת המודל השמורה שקלוד קוד שחזר.

#בקרת החלטה של PostModelSwitch

קלוד קוד לוקח את stdout בטקסט רגיל של ההוק שלכם ביציאה 0, או את additionalContext מפלט JSON, ומעביר אותו לקלוד עם הבקשה הבאה אחרי ההחלפה. בנוסף לשדות פלט ה-JSON שזמינים לכל ההוקים, אפשר להחזיר:

שדהתיאור
additionalContextמחרוזת שנוספת להקשר של קלוד עם הבקשה הבאה. ראו הוספת הקשר לקלוד

אם ההוק לא הסתיים תוך חמש שניות אחרי ששלחתם את הפרומפט הבא, קלוד קוד שולח את הבקשה הזו בלי הפלט ומצרף אותו לבקשה שאחריה במקום זאת. אם המודל משתנה כמה פעמים לפני הבקשה הבאה, קלוד קוד מעביר רק את הפלט עבור מודל היעד של ההחלפה האחרונה.

#SessionEnd

רץ כשסשן של קלוד קוד מסתיים. שימושי למשימות ניקוי, לרישום סטטיסטיקות של הסשן, או לשמירת מצב הסשן. תומך ב-matchers כדי לסנן לפי סיבת היציאה.

השדה reason בקלט ההוק מציין למה הסשן הסתיים:

סיבהתיאור
clearהסשן נוקה עם הפקודה /clear
resumeהסשן הוחלף דרך /resume אינטראקטיבי
logoutהמשתמש התנתק
prompt_input_exitהמשתמש יצא בזמן שקלט הפרומפט היה גלוי
otherסיבות יציאה אחרות
bypass_permissions_disabledהוסר ב-v2.1.234; קלוד קוד לא שולח אותו. הסירו אותו מה-matchers של SessionEnd שלכם

#קלט SessionEnd

בנוסף לשדות הקלט המשותפים, הוקים של SessionEnd מקבלים שדה reason שמציין למה הסשן הסתיים. ראו את טבלת הסיבות למעלה לכל הערכים.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

להוקים של SessionEnd אין בקרת החלטה. הם לא יכולים לחסום סיום סשן, אבל יכולים לבצע משימות ניקוי. קלוד קוד משליך את שדות פלט ה-JSON שלהם, כמו systemMessage.

להוקים של SessionEnd יש פסק זמן ברירת מחדל של 1.5 שניות. הוא חל כשאתם יוצאים, מריצים /clear, או מחליפים סשנים עם /resume אינטראקטיבי. אפשר לתת להוק יותר זמן בשתי דרכים:

  • timeout לכל הוק: הגדירו timeout בהגדרות של אותו הוק. התקציב הכולל עולה אוטומטית כדי להתאים ל-timeout הגבוה ביותר לכל הוק בקבצי ההגדרות שלכם, עד 60 שניות. אם מעלים את התקציב כך, הוק בלי timeout משלו עדיין שומר את ברירת המחדל. פסקי זמן שמוגדרים על הוקים שמגיעים מתוסף לא מעלים את התקציב.
  • CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: הגדירו את משתנה הסביבה הזה באלפיות שנייה כדי לדרוס את התקציב במפורש. הערך שאתם מגדירים גם הופך לפסק הזמן של כל הוק בלי timeout משלו.

הדוגמה הזו מגדירה את התקציב ל-5 שניות:

CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

לפני v2.1.268, CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS העלה רק את התקציב הכולל, והוק בלי timeout משלו עדיין בוטל אחרי 1.5 שניות.

#Elicitation

רץ כששרת MCP מבקש קלט מהמשתמש באמצע משימה. כברירת מחדל, Claude Code מציג דיאלוג אינטראקטיבי כדי שהמשתמש יגיב. Hooks יכולים ליירט את הבקשה ולהגיב באופן תכנותי, ולדלג על הדיאלוג לגמרי.

לדוגמה מלאה של hook עם רשומת ההגדרות והסקריפט שלה, ראו מענה לבקשת טופס מתוך סקריפט.

שדה ה-matcher מתאים לשם שרת ה-MCP.

#קלט Elicitation

בנוסף לשדות הקלט המשותפים, hooks של Elicitation מקבלים את השדות mcp_server_name, message, ואופציונלית mode, url, elicitation_id ו-requested_schema.

ל-elicitation במצב טופס, המקרה הנפוץ ביותר:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": {
      "username": { "type": "string", "title": "Username" }
    }
  }
}

ל-elicitation במצב URL, המשמש לאימות מבוסס דפדפן:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please authenticate",
  "mode": "url",
  "url": "https://auth.example.com/login"
}

#פלט Elicitation

hook של Elicitation יכול לענות על הבקשה בשם המשתמש, לדחות אותה או לבטל אותה, או להשאיר אותה לדיאלוג. כדי לענות, לדחות או לבטל, צאו עם קוד 0 והדפיסו אובייקט hookSpecificOutput עם action. השרת מקבל את התשובה שלכם ולא מופיע דיאלוג. כל שורה בטבלה הזו מראה מה להחזיר עבור תוצאה אחת ומה שרת ה-MCP מקבל:

כדיהחזרההשרת מקבל
מענה בשם המשתמש"action": "accept", עם ערכי שדות הטופס ב-contentaccept עם ה-content שלכם
דחיית הבקשה"action": "decline"decline
ביטול הבקשה"action": "cancel"cancel
השארת הבקשה למשתמשבלי פלט, עם קוד יציאה 0תשובת המשתמש מתוך הדיאלוג

הפלט הזה עונה על בקשת מצב הטופס שמוצגת תחת קלט Elicitation. המפתחות ב-content הם שמות המאפיינים מתוך ה-requested_schema של אותה בקשה:

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": {
      "username": "alice"
    }
  }
}

הפלט הזה דוחה בקשה:

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "decline"
  }
}

בדיאלוג, בחירה ב-Decline שולחת decline ולחיצה על Esc שולחת cancel, ולכן החזירו את זה שאתם רוצים שהשרת יראה.

עבור בקשה במצב URL, hook שמחזיר accept מדלג על הדיאלוג, כך שה-URL לא נפתח לעולם.

Claude Code משליך את reason, systemMessage ו-continue מפלט ה-JSON של hook של Elicitation, בכל action שאתם מחזירים.

#דרכים נוספות לדחות elicitation

ה-hook שלכם יכול גם לדחות בדרכים האלה. השרת מקבל את אותו decline כמו עבור "action": "decline":

  • יציאה עם קוד 2: Claude Code מתעלם מ-hookSpecificOutput שהודפס על ידי אותו hook
  • הדפסת "decision": "block" ברמה העליונה: ה-block גובר על action באותו פלט

כשכמה hooks מתאימים לאותה בקשה, decline מאחד מהם גובר על accept או cancel מאחר.

הסקריפט הזה דוחה בקשות במצב URL ומשאיר בקשות טופס לדיאלוג:

#!/bin/bash
if [ "$(jq -r '.mode')" = "url" ]; then
  exit 2
fi

לא המשתמש ולא השרת רואים למה ה-hook שלכם דחה, כי Claude Code לא מציג את ה-stderr שלכם או את ה-reason שלכם.

Claude Code התעלם מ-decision ברמה העליונה מ-hooks של Elicitation ו-ElicitationResult מ-v2.1.105 עד התיקון ב-v2.1.284.

#מענה לבקשת טופס מתוך סקריפט

הדוגמה הזו עונה על שאלה חוזרת אחת בשם המשתמש. שרת MCP בשם issue-tracker מבקש מפתח פרויקט בטופס, וה-hook ממלא DOCS. הסקריפט מקבל כש-project_key הוא השדה היחיד של הטופס. לכל בקשה אחרת הוא לא מדפיס כלום, ולכן הדיאלוג מופיע.

[tab] macOS/Linux

רשמו command hook לאירוע בקובץ ההגדרות שלכם, עם שם השרת כ-matcher:

{
  "hooks": {
    "Elicitation": [
      {
        "matcher": "issue-tracker",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

שמרו את הסקריפט הזה ב-.claude/hooks/answer-project-key.sh בפרויקט שלכם והפכו אותו לבר הרצה עם chmod +x:

#!/bin/bash
input=$(cat)
fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")

if [ "$fields" = '["project_key"]' ]; then
  jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'
fi

[tab] Windows (PowerShell)

רשמו command hook שמריץ את הסקריפט דרך PowerShell, עם שם השרת כ-matcher:

{
  "hooks": {
    "Elicitation": [
      {
        "matcher": "issue-tracker",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
            ]
          }
        ]
      }
    ]
  }
}

שמרו את הסקריפט הזה ב-.claude/hooks/answer-project-key.ps1 בפרויקט שלכם:

$request = [Console]::In.ReadToEnd() | ConvertFrom-Json
$fields = @($request.requested_schema.properties.PSObject.Properties.Name)

if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {
  @{
    hookSpecificOutput = @{
      hookEventName = "Elicitation"
      action = "accept"
      content = @{ project_key = "DOCS" }
    }
  } | ConvertTo-Json -Depth 3
}

כדי לאשר שה-hook עובד, הפעילו את Claude Code עם claude --debug ותנו ל-Claude משימה שגורמת לשרת לבקש את מפתח הפרויקט. לא מופיע דיאלוג, וביומן הדיבוג יש שורה שמסתיימת ב-Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}.

#ElicitationResult

רץ אחרי שמשתמש מגיב ל-elicitation של MCP. Hooks יכולים לצפות בתגובה, לשנות אותה או לחסום אותה לפני שהיא נשלחת בחזרה לשרת ה-MCP.

כש-hook של Elicitation עונה על בקשה, Claude Code שולח את התשובה הזו לשרת בלי להריץ hooks של ElicitationResult.

שדה ה-matcher מתאים לשם שרת ה-MCP.

#קלט ElicitationResult

בנוסף לשדות הקלט המשותפים, hooks של ElicitationResult מקבלים את השדות mcp_server_name, action, ואופציונלית mode, elicitation_id ו-content.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ElicitationResult",
  "mcp_server_name": "my-mcp-server",
  "action": "accept",
  "content": { "username": "alice" },
  "mode": "form"
}

#פלט ElicitationResult

hook של ElicitationResult יכול להעביר את תגובת המשתמש, לשנות את הערכים שלה, או לחסום אותה. כדי לשנות או לחסום את התגובה, צאו עם קוד 0 והדפיסו אובייקט hookSpecificOutput עם action. כל שורה בטבלה הזו מראה מה להחזיר עבור תוצאה אחת ומה שרת ה-MCP מקבל:

כדיהחזרההשרת מקבל
העברת התגובהבלי פלט, עם קוד יציאה 0תגובת המשתמש, בלי שינוי
שינוי הערכים שנשלחו"action": "accept", עם הערכים החדשים ב-contentaccept עם ה-content שלכם במקום ערכי המשתמש
חסימת התגובה"action": "decline"decline, בלי ערכי המשתמש
ביטול הבקשה"action": "cancel"cancel, יחד עם הערכים שהמשתמש שלח. כדי למנוע את שליחתם, החזירו "decline"

הפלט הזה משנה את התגובה שמוצגת תחת קלט ElicitationResult, כך שהשרת מקבל [email protected] במקום שבו המשתמש שלח alice:

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "accept",
    "content": {
      "username": "[email protected]"
    }
  }
}

ה-content שלכם מחליף את כל אובייקט ה-content של המשתמש, ולכן כללו את השדות שאתם לא משנים. החזירו action יחד איתו, כי Claude Code מתעלם מ-hookSpecificOutput שאין בו action.

hooks של ElicitationResult רצים גם כשהמשתמש דוחה או מבטל, וה-action שלכם מחליף את שלהם. בדקו שה-action של הקלט הוא accept לפני שאתם מחזירים accept, אחרת ה-hook שלכם הופך בקשה שנדחתה לבקשה שהתקבלה. הסקריפט הזה מבצע את אותו שינוי כשהמשתמש קיבל, שומר את שאר השדות, ולא מדפיס כלום אחרת:

#!/bin/bash
input=$(cat)

if [ "$(jq -r '.action' <<<"$input")" = "accept" ]; then
  jq '{hookSpecificOutput: {hookEventName: "ElicitationResult", action: "accept", content: (.content + {username: (.content.username + "@example.com")})}}' <<<"$input"
fi

הפלט הזה חוסם את התגובה:

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline"
  }
}

קוד יציאה 2 ו-"decision": "block" ברמה העליונה גם חוסמים את התגובה. דרכים נוספות לדחות elicitation מכסה איזה מהם נכנס לתוקף כש-hook משלב אותם, מה המשתמש רואה, ואילו גרסאות התעלמו מ-decision.

Claude Code משליך את reason, systemMessage ו-continue מפלט ה-JSON של hook של ElicitationResult, בכל action שאתם מחזירים.

#Hooks מבוססי prompt

בנוסף ל-hooks מסוג command, HTTP ו-MCP tool, Claude Code תומך ב-hooks מבוססי prompt (type: "prompt") שמשתמשים ב-LLM כדי להעריך אם לאפשר או לחסום פעולה, וב-agent hooks (type: "agent") שמפעילים מאמת סוכני עם גישה לכלים. לא כל האירועים תומכים בכל סוג hook.

אירועים שתומכים בכל חמשת סוגי ה-hook (command, http, mcp_tool, prompt ו-agent):

  • PermissionDenied
  • PostToolBatch
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • TeammateIdle
  • UserPromptExpansion
  • UserPromptSubmit

PermissionRequest תומך ב-hooks מסוג command, http, mcp_tool ו-prompt אבל לא ב-hooks מסוג agent. אם תגדירו agent hook על האירוע הזה, Claude Code מדלג עליו וזרימת ההרשאה ממשיכה בלי שינוי. כדי לאפשר או למנוע מתוך hook, החזירו את אובייקט ההחלטה מ-hook מסוג command או HTTP.

אירועים שתומכים ב-hooks מסוג command, http ו-mcp_tool אבל לא ב-prompt או agent:

  • ConfigChange
  • CwdChanged
  • DirectoryAdded
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • MessageDisplay
  • Notification
  • PostCompact
  • PostModelSwitch
  • PreCompact
  • PreModelSwitch
  • SessionEnd
  • StopFailure
  • SubagentStart
  • WorktreeCreate
  • WorktreeRemove

SessionStart ו-Setup תומכים ב-hooks מסוג command ו-mcp_tool, ושדות MCP tool hook מתארים מתי ה-hooks מסוג mcp_tool שלהם רצים. הם לא תומכים ב-hooks מסוג http, prompt או agent.

#איך hooks מבוססי prompt עובדים

במקום להריץ פקודת Bash, hooks מבוססי prompt:

  1. שולחים את קלט ה-hook ואת ה-prompt שלכם למודל Claude, כברירת מחדל זה ש-Claude Code משתמש בו עבור פונקציונליות רקע
  2. ה-LLM מגיב ב-JSON מובנה שמכיל החלטה
  3. Claude Code מעבד את ההחלטה אוטומטית

#הגדרת prompt hook

הגדירו את type ל-"prompt" וספקו מחרוזת prompt במקום command. השתמשו במציין המיקום $ARGUMENTS כדי להזריק את נתוני קלט ה-JSON של ה-hook לטקסט ה-prompt שלכם.

ב-prompt או ב-agent hook, אפשר לכתוב את ה-prompt ככלל על מה לחסום או לאפשר, למשל "Block any Bash command that reads .env files", או כתנאי שחייב להתקיים, למשל "All unit tests pass".

ה-hook הזה של Stop מבקש מה-LLM להעריך אם כל המשימות הושלמו לפני שמאפשרים ל-Claude לסיים:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
שדהנדרשתיאור
typeכןחייב להיות "prompt"
promptכןטקסט ה-prompt לשליחה ל-LLM. השתמשו ב-$ARGUMENTS כמציין מיקום ל-JSON של קלט ה-hook. אם $ARGUMENTS לא קיים, JSON הקלט מצורף ל-prompt
modelלאהמודל לשימוש בהערכה. ברירת המחדל היא המודל ש-Claude Code משתמש בו עבור פונקציונליות רקע
timeoutלאזמן קצוב בשניות. ברירת מחדל: 30
continueOnBlockלאבאירועים שהוא חל עליהם, true מחזיר ל-Claude סיבה של ok: false וממשיך במקום לסיים את התור. ברירת מחדל: false. ראו סכמת תגובה להתנהגות לפי אירוע

#סכמת תגובה

ה-LLM חייב להגיב ב-JSON שמכיל:

{
  "ok": true | false,
  "reason": "Explanation for the decision",
  "impossible": true | false
}
שדהתיאור
oktrue כדי לאפשר. עבור false, ראו את ההתנהגות לפי אירוע למטה
reasonנדרש כש-ok הוא false
impossibleאופציונלי. המודל מחזיר אותו עם ok: false כשהוא שופט שהתנאי לא יכול להתקיים לעולם. ב-Stop וב-SubagentStop, Claude Code אז מאפשר לתור להסתיים במקום להחזיר את הסיבה. agent hooks ואירועים אחרים מתעלמים ממנו

מה שקורה ב-ok: false תלוי באירוע:

  • Stop ו-SubagentStop: הסיבה מוחזרת ל-Claude כהוראה הבאה שלו והתור ממשיך, אלא אם התגובה גם מגדירה impossible: true, ובמקרה הזה Claude Code מאפשר את העצירה והתור מסתיים
  • PreToolUse: קריאת הכלי נדחית. כברירת מחדל התור מסתיים וסיבת הדחייה מופיעה בצ'אט כשורת אזהרה. הגדירו continueOnBlock: true כדי במקום זאת להחזיר את הסיבה ל-Claude כשגיאת הכלי כך שיוכל להתאים ולהמשיך, שווה ערך ל-permissionDecision: "deny" של command hook
  • PostToolUse: כברירת מחדל התור מסתיים והסיבה מופיעה בצ'אט כשורת אזהרה. הגדירו continueOnBlock: true כדי להחזיר את הסיבה ל-Claude ולהמשיך את התור
  • PostToolBatch, UserPromptSubmit ו-UserPromptExpansion: התור מסתיים והסיבה מופיעה כשורת אזהרה. האירועים האלה מסיימים את התור ב-decision: "block" בלי קשר ל-continue
  • PostToolUseFailure ו-TaskCreated: הסיבה מוחזרת ל-Claude כשגיאת כלי והתור ממשיך, בלי קשר ל-continueOnBlock
  • TaskCompleted: כשהוא נורה כי משימה סומנה כהושלמה במהלך תור, הסיבה מוחזרת ל-Claude כשגיאת כלי והתור ממשיך, בלי קשר ל-continueOnBlock. כשהוא נורה כי teammate עוצר, הוא מתנהג כמו TeammateIdle ועוצר את ה-teammate כברירת מחדל
  • TeammateIdle: כברירת מחדל ה-teammate עוצר והסיבה מופיעה כשורת אזהרה. הגדירו continueOnBlock: true כדי להחזיר את הסיבה ל-teammate ולהשאיר אותו בעבודה
  • PermissionRequest: ל-ok: false אין השפעה. כדי לדחות אישור מתוך hook, השתמשו ב-command hook שמחזיר hookSpecificOutput.decision.behavior: "deny"
  • PermissionDenied: ל-ok: false אין השפעה כי הדחייה כבר קרתה. הפלט היחיד שהאירוע הזה קורא הוא hookSpecificOutput.retry, ש-prompt hooks ו-agent hooks לא יכולים להגדיר. הם רצים על האירוע הזה, אבל הפלט שלהם מושלך. השתמשו ב-command hook כדי להחזיר retry

אם אתם צריכים שליטה עדינה יותר על אירוע כלשהו, השתמשו ב-command hook עם השדות לפי אירוע שמתוארים בבקרת החלטה.

#בדיקת כמה תנאים לפני עצירה

ה-hook הזה של Stop משתמש ב-prompt מפורט כדי לבדוק שלושה תנאים לפני שמאפשרים ל-Claude לעצור. hooks של SubagentStop משתמשים באותו פורמט כדי להעריך אם subagent צריך לעצור. אם המודל מחזיר "ok": false כי התנאי עדיין לא מתקיים, Claude ממשיך לעבוד עם הסיבה שסופקה כהוראה הבאה שלו:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

#הוקים מבוססי סוכן

[!WARNING]

הוקי סוכן הם ניסיוניים. ההתנהגות וההגדרות עשויות להשתנות במהדורות עתידיות. לתהליכי עבודה בייצור, העדיפו הוקי command.

הוקים מבוססי סוכן (type: "agent") דומים להוקים מבוססי prompt, אבל עם גישה לכלים לאורך כמה תורות. במקום קריאה אחת ל-LLM, הוק סוכן מפעיל תת-סוכן שיכול לקרוא קבצים, לחפש בקוד ולבדוק את בסיס הקוד כדי לאמת תנאים. הוקי סוכן תומכים באותם אירועים כמו הוקים מבוססי prompt, חוץ מ-PermissionRequest.

#איך הוקי סוכן עובדים

כשהוק סוכן מופעל:

  1. Claude Code מפעיל תת-סוכן עם ה-prompt שלכם ועם קלט ה-JSON של ההוק
  2. תת-הסוכן יכול להשתמש בכלים כמו Read, Grep ו-Glob כדי לחקור
  3. אחרי עד 50 תורות, תת-הסוכן מחזיר החלטה מובנית { "ok": true/false }
  4. Claude Code מתיר את הפעולה אם ok הוא true. אם ok הוא false, Claude Code מטפל בחסימה באותו אופן כמו הוק prompt עם continueOnBlock: true באותו אירוע, כפי שמפורט תחת סכמת תגובה

הוקי סוכן שימושיים כשהאימות דורש בדיקה של קבצים בפועל או של פלט בדיקות, ולא רק הערכה של נתוני הקלט של ההוק לבדם.

#הגדרת הוק סוכן

הגדירו את type ל-"agent" וספקו מחרוזת prompt, תוך שימוש ב-$ARGUMENTS כמציין מקום לקלט ה-JSON של ההוק. שדות ההגדרה זהים לאלה של הוקי prompt, חוץ מזה שלהוקי סוכן יש timeout ברירת מחדל ארוך יותר של 60 שניות ואין שדה continueOnBlock.

סכמת התגובה היא { "ok": true } כדי להתיר, או { "ok": false, "reason": "..." } כדי לחסום. כש-ok: false, Claude Code מטפל בהוק סוכן כמו שהוא מטפל ב-הוק prompt עם continueOnBlock: true באותו אירוע. להוקי סוכן אין שדה continueOnBlock, והם לא תומכים בשדה impossible של הוק prompt.

הוק Stop זה מאמת שכל בדיקות היחידה עוברות לפני שהוא מאפשר ל-Claude לסיים:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

#הרצת הוקים ברקע

כברירת מחדל, הוקים חוסמים את הביצוע של Claude עד שהם מסתיימים. למשימות ארוכות כמו פריסות, חבילות בדיקות או קריאות ל-API חיצוני, הגדירו "async": true כדי להריץ את ההוק ברקע בזמן ש-Claude ממשיך לעבוד. הוקים אסינכרוניים לא יכולים לחסום או לשלוט בהתנהגות של Claude: שדות תגובה כמו decision, permissionDecision ו-continue לא משפיעים, כי הפעולה שהם היו אמורים לשלוט בה כבר הושלמה.

#הגדרת הוק אסינכרוני

הוסיפו "async": true להגדרה של הוק command כדי להריץ אותו ברקע בלי לחסום את Claude. השדה הזה זמין רק בהוקים מסוג type: "command".

ההוק הזה מריץ סקריפט בדיקות אחרי כל קריאת כלי Write. Claude ממשיך לעבוד מיד בזמן ש-run-tests.sh רץ. כשהסקריפט מסתיים, הפלט שלו נמסר בתור השיחה הבא:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

אחרי שהוק אסינכרוני רץ ברקע, Claude Code לא אוכף עליו timeout. Claude Code עדיין אוכף timeout על הוק שאתם מריצים עם asyncRewake.

Claude Code מוסר את התוצאות של הוק אסינכרוני רק בזמן שהסשן רץ:

  • במצב לא אינטראקטיבי עם הדגל -p, Claude Code הורג כל הוק אסינכרוני שעדיין רץ בזמן teardown ומסיים אותו עם התוצאה cancelled
  • אם העבודה של ההוק חייבת להמשיך אחרי סשן claude -p, הפעילו ממנו תהליך מנותק לחלוטין

#איך הוקים אסינכרוניים רצים

כשהוק אסינכרוני מופעל, Claude Code מתחיל את תהליך ההוק וממשיך מיד בלי לחכות שהוא יסתיים. ההוק מקבל את אותו קלט JSON דרך stdin כמו הוק סינכרוני.

אחרי שתהליך הרקע יוצא, Claude Code מוסר את השדות additionalContext ו-systemMessage מתגובת ה-JSON של ההוק אל Claude בתור השיחה הבא, חוץ מפלט SessionStart חוזר. בניגוד ל-systemMessage של הוק סינכרוני, אף אחד משני השדות לא מוצג לכם.

הדפיסו את תגובת ה-JSON לבדה ב-stdout או בשורה משלה:

  • לבד ב-stdout: כשהתגובה היא הטקסט היחיד ב-stdout, היא יכולה להשתרע על כמה שורות, למשל פלט jq מודפס בפורמט קריא. פריסה על כמה שורות דורשת Claude Code v2.1.295 או חדש יותר.
  • בשורה אחת משלה: הוק אסינכרוני יכול להדפיס טקסט אחר ל-stdout כשהתגובה נכנסת בשורה אחת בפני עצמה, למשל עם jq -c.

Claude Code מאמת את תגובת ה-JSON מול אותה סכמת פלט כמו הוקים סינכרוניים, ומשמיט כל שדה שהערך שלו מסוג שגוי, למשל systemMessage שאינו מחרוזת, במקום למסור אותו. הריצו עם --debug כדי לראות אזהרה שמציינת כל שדה שהושמט.

התראות על סיום הוק אסינכרוני מוסתרות כברירת מחדל. כדי לראות אותן, הפעילו מצב verbose עם Ctrl+O או הפעילו את Claude Code עם --verbose.

#הרצת בדיקות אחרי שינויי קבצים

ההוק הזה מתחיל חבילת בדיקות ברקע בכל פעם ש-Claude כותב קובץ, ואז מדווח את התוצאות חזרה ל-Claude כשהבדיקות מסתיימות. שמרו את הסקריפט הזה ב-.claude/hooks/run-tests-async.sh בפרויקט שלכם והפכו אותו לבר-הרצה עם chmod +x:

#!/bin/bash
# run-tests-async.sh

# Read hook input from stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Only run tests for source files
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi

# Run tests and report results to Claude via additionalContext
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
  MSG="Tests passed after editing $FILE_PATH"
else
  MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'

ואז הוסיפו את ההגדרה הזו ל-.claude/settings.json בשורש הפרויקט. הדגל async: true מאפשר ל-Claude להמשיך לעבוד בזמן שהבדיקות רצות:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
            "args": [],
            "async": true
          }
        ]
      }
    ]
  }
}

#מגבלות

להוקים אסינכרוניים יש אילוצים נוספים בהשוואה להוקים סינכרוניים:

  • פלט ההוק נמסר בתור השיחה הבא. אם הסשן לא פעיל, התגובה ממתינה עד האינטראקציה הבאה של המשתמש. חריג: הוק asyncRewake שיוצא עם קוד 2 מעיר את Claude מיד גם כשהסשן לא פעיל.
  • כל הרצה יוצרת תהליך רקע נפרד.
  • הוק SessionStart אסינכרוני רץ בכל פעם שהאירוע מופעל. Claude Code מדלג על פלט ההוק כשהטקסט של additionalContext ו-systemMessage תואם למה ש-Claude קיבל לאחרונה מאותו הוק, אלא אם הוק SessionStart סינכרוני הוסיף טקסט אחר מאז. Claude Code מוסר את הטקסט כשהוא משתנה, ושוב בפעם הבאה שההוק רץ אחרי compaction או /clear. לפני v2.1.295, Claude Code הוסיף את אותו טקסט לשיחה שוב בכל resume.

#שיקולי אבטחה

#כתב ויתור

[!WARNING]

הוקי command מריצים פקודות shell עם הרשאות המשתמש המלאות שלכם. הם יכולים לשנות, למחוק או לגשת לכל קובץ שחשבון המשתמש שלכם יכול לגשת אליו. בדקו ובחנו את כל פקודות ההוק לפני שאתם מוסיפים אותן להגדרות.

#אמון בסביבת העבודה

Claude Code בודק אמון בסביבת העבודה לפני שהוא מריץ הוק כלשהו מקובץ הגדרות. מה שנחשב מהימן תלוי בסוג הסשן:

  • סשן אינטראקטיבי: Claude Code מעכב הוקים מכל קובץ הגדרות, כולל ~/.claude/settings.json שלכם, עד שתקבלו את תיבת הדו-שיח של אמון בסביבת העבודה עבור התיקייה, או עבור תיקיית אב שהאמון בה משתרע עליה
  • סשן -p או SDK: Claude Code אף פעם לא מציג את תיבת הדו-שיח ומתייחס לתיקייה כמהימנה, ולכן הוקים שנשמרו ב-.claude/settings.json של מאגר רצים בתיקייה שמעולם לא סימנתם כמהימנה

לפני שאתם מריצים בסקריפט את claude -p על מאגר שלא כתבתם, בדקו את קובצי ההגדרות ב-.claude/, התחילו עם --bare, או כבו הוקים להרצה הזו עם --settings '{"disableAllHooks": true}'. הוקי frontmatter בתת-סוכן של פרויקט פועלים לפי כלל מחמיר יותר מהוקים בקובץ הגדרות. מה רץ לפני שאתם סומכים על תיקייה מפרט כל סוג של תוכן מאגר לפי סוג סשן.

#שיטות עבודה מומלצות לאבטחה

זכרו את השיטות האלה כשאתם כותבים הוקים:

  • אמתו ונקו קלטים: אף פעם אל תסמכו על נתוני קלט בעיוורון
  • תמיד צטטו משתני shell: השתמשו ב-"$VAR" ולא ב-$VAR
  • חסמו path traversal: בדקו אם יש .. בנתיבי קבצים
  • השתמשו בנתיבים מוחלטים: ציינו נתיבים מלאים לסקריפטים. בצורת exec, השתמשו ב-${CLAUDE_PROJECT_DIR} והנתיב לא צריך ציטוט. בצורת shell, עטפו אותו במירכאות כפולות
  • דלגו על קבצים רגישים: הימנעו מ-.env, .git/, מפתחות וכן הלאה.

#כלי Windows PowerShell

ב-Windows אפשר להריץ הוקים בודדים ב-PowerShell על ידי הגדרת "shell": "powershell" בהוק command. Claude Code מזהה אוטומטית את pwsh.exe, קובץ ההרצה של PowerShell 7 ומעלה, ונופל חזרה ל-powershell.exe עבור Windows PowerShell 5.1.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "shell": "powershell",
            "command": "Write-Host 'File written'"
          }
        ]
      }
    ]
  }
}

#הפניה לסקריפטים לפי נתיב בהוקי PowerShell

כדי להפנות לשורש הפרויקט מפקודת צורת shell של PowerShell, כתבו ${CLAUDE_PROJECT_DIR} או $env:CLAUDE_PROJECT_DIR. Claude Code משכתב את מצייני המקום ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} ו-${CLAUDE_PLUGIN_DATA} בפקודת צורת shell של PowerShell לצורה ${env:NAME} של PowerShell, בין אם ההוק מוגדר ב-settings.json, בתוסף או ב-skill. PowerShell אז פותר את הערך מהסביבה המיוצאת אחרי הניתוח, ולכן מציין המקום עובד בתוך מחרוזות במירכאות כפולות אבל לא בתוך מחרוזות במירכאות בודדות, שבהן PowerShell אף פעם לא מרחיב משתנים.

אל תכתבו את האיות החשוף $CLAUDE_PROJECT_DIR בהוק PowerShell. PowerShell מנתח אותו כמשתנה מקומי לא מוגדר ופותר אותו ל-$null, מה שמשאיר את נתיב הסקריפט בלי קידומת שורש הפרויקט. Claude Code לא משכתב את הצורה הזו. הוא רושם אזהרה ב-יומן הדיבוג במקום.

הדוגמה למטה מציגה הוק settings.json שמריץ סקריפט פרויקט עם הצורה $env::

{
  "type": "command",
  "shell": "powershell",
  "command": "& \"$env:CLAUDE_PROJECT_DIR\\.claude\\hooks\\check.ps1\""
}

#דיבוג הוקים

פרטי הרצת הוקים נכתבים לקובץ יומן הדיבוג. הפעילו את Claude Code עם claude --debug-file <path> כדי לכתוב את היומן למיקום ידוע, או הריצו claude --debug וקראו את היומן ב-~/.claude/debug/<session-id>.txt. הדגל --debug לא מדפיס לטרמינל.

לדוגמה, הוק PostToolUse על Write שהפקודה שלו מדפיסה hook-ran מייצר רשומות כמו:

2026-07-19T02:03:24.382Z [DEBUG] Hook output does not start with {, treating as plain text
2026-07-19T02:03:24.382Z [DEBUG] "Hook PostToolUse:Write (PostToolUse) success:\nhook-ran"

כדי למצוא הוק איטי, חפשו ביומן שורות Hooks: שמסתיימות במשך זמן. ב-Claude Code v2.1.296 או חדש יותר, כל הוק command על אירוע כלי, UserPromptSubmit, SessionStart, Stop ועוד כמה אירועים משאיר אחת כשהוא מסתיים, בלי קשר למה שהדפיס. השורה נותנת את שם האירוע, מחובר בנקודתיים לשם הכלי או לערך אחר שההוק התאים אליו, ואז את פקודת ההוק בסוגריים מרובעים, התוסף שממנו הגיע אם יש, איך ההרצה הסתיימה, וכמה זמן היא לקחה, כמו ב-Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms). הרצה יכולה להסתיים גם כ-timed out after <N>ms, cancelled, moved to the background, או failed to start. בחלק מהאירועים, כמו Notification, SessionEnd ו-PreCompact, הוק command משאיר שורת completed with status בלי משך זמן במקום.

לפרטי התאמת הוקים מפורטים יותר, הגדירו CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose כדי לראות שורות יומן נוספות כמו ספירות matcher של הוקים והתאמת שאילתות.

לפתרון בעיות נפוצות כמו הוקים שלא מופעלים, הוקי Stop שממשיכים לחסום, או שגיאות הגדרה, ראו מגבלות ופתרון בעיות במדריך. לסקירת אבחון רחבה יותר שמכסה את /context, /doctor וקדימות הגדרות, ראו דיבוג ההגדרות שלכם.