ה-hook Stop של Claude Code מריץ פקודה בכל פעם שהסוכן הראשי של Claude מסיים להגיב, ולכן זה המקום הנכון לשלוח התראת "סיימתי", לתעד את התוצאה או להריץ בדיקות. הוא לא רץ כשאתם קוטעים את Claude, ותור שמסתיים בשגיאת API מפעיל במקומו את StopFailure. ה-hook מקבל את ההודעה האחרונה של Claude ב-last_assistant_message, והוא יכול להחזיר decision: "block" עם reason כדי ש-Claude ימשיך במקום לעצור.
01מתי Stop מופעל ומתי לא
Stop מופעל בסוף כל תור, ולא רק כשמשימה שלמה הושלמה. ל-Claude Code אין מושג של "המשימה"; הוא יודע רק ש-Claude הפסיק להגיב ומחכה לכם. שאלה קצרה מקבלת Stop אחד, וכך גם refactor של שעתיים.
הוא לא מופעל:
- כשלוחצים Esc או קוטעים את התור בדרך אחרת;
- כשהתור מסתיים בגלל שגיאת API, כמו rate limit, עומס יתר או כשל באימות. במקרים האלה Claude Code מפעיל את
StopFailure, עם שדהerrorשאפשר לסנן לפיו (rate_limit,overloaded,server_errorועוד); - כש-subagent מסיים. לשם כך יש את
SubagentStop.
Stop לא תומך ב-matcher, כך שכל matcher שתוסיפו יתעלם וה-hook ירוץ בכל תור.
02ה-Stop hook הפשוט ביותר
הוסיפו את זה ל-~/.claude/settings.json כדי להריץ פקודה בכל פעם ש-Claude מסיים. הדוגמה הזו משמיעה צליל ב-macOS:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Glass.aiff"
}
]
}
]
}
}
לפקודות התראה בשולחן העבודה ב-Mac, ב-Windows וב-Linux, ראו איך לקבל התראה כש-Claude Code מסיים. שאר המדריך עוסק במה שאפשר לעשות עם הקלט והפלט של ה-hook.
03מה ה-hook מקבל
בנוסף לשדות המשותפים (session_id, transcript_path, cwd, permission_mode, hook_event_name), hook של Stop מקבל:
last_assistant_messageהטקסט של התשובה האחרונה של Claude בתור הזהstop_hook_activetrue אם Claude כבר ממשיך כי Stop hook חסםbackground_tasksמשימות שעדיין רצות, כמו shells ברקע או subagentssession_cronsהשכמות מתוזמנות בסשן הזה, למשל מ-/loopהשתמשו ב-last_assistant_message במקום לקרוא את קובץ ה-transcript. ה-transcript נכתב באופן אסינכרוני, וייתכן שעדיין לא יכיל את ההודעה האחרונה כשה-hook רץ.
background_tasks ו-session_crons מראים אם הסשן באמת הסתיים או רק מושהה עד שעבודה ברקע תעיר אותו שוב. סקריפט התראה יכול לוודא ששניהם ריקים לפני שהוא מודיע "סיימתי".
04הצגת ההודעה האחרונה של Claude בהתראה
הסקריפט הזה (macOS ו-Linux, דורש jq) מציג את תחילת התשובה של Claude ולא מתריע אם עדיין רצה עבודה ברקע:
#!/bin/bash
# ~/.claude/hooks/notify-done.sh
input=$(cat)
# Skip if background tasks are still in flight
busy=$(jq '(.background_tasks // []) | length' <<<"$input")
[ "$busy" -gt 0 ] && exit 0
msg=$(jq -r '(.last_assistant_message // "Claude has finished")
| gsub("\\s+"; " ") | .[0:140]' <<<"$input")
if command -v osascript >/dev/null 2>&1; then
osascript -e 'on run argv' \
-e 'display notification (item 1 of argv) with title "Claude has finished"' \
-e 'end run' "$msg"
elif command -v notify-send >/dev/null 2>&1; then
notify-send "Claude has finished" "$msg"
fi
exit 0
שמרו אותו, הריצו chmod +x ~/.claude/hooks/notify-done.sh והפנו אליו את ה-hook:
{
"hooks": {
"Stop": [
{
"hooks": [{ "type": "command", "command": "~/.claude/hooks/notify-done.sh" }]
}
]
}
}
הסקריפט תמיד יוצא עם 0. זה חשוב: ב-Stop, קוד יציאה 2 פירושו "אל תעצור", כך שסקריפט התראה שיוצא בטעות עם 2 ישאיר את Claude בעבודה.
05לגרום ל-Claude להמשיך
Stop הוא אחד האירועים שיכולים לחסום. חסימת עצירה פירושה ש-Claude ממשיך בתור, כשהסיבה שנתתם משמשת לו כהוראה הבאה. יש שתי דרכים לעשות את זה:
- לצאת עם
2, ואז Claude מקבל את ה-stderr שלכם כסיבה; או - לצאת עם
0ולהדפיס JSON:
{
"decision": "block",
"reason": "The test suite is failing. Run npm test, fix the failures, then finish."
}
אם ה-hook נותן הכוונה ולא מדווח על שגיאה, גם hookSpecificOutput.additionalContext ממשיך את השיחה, אבל ב-transcript הוא מסומן כמשוב של Stop hook ולא כשגיאת hook:
{
"hookSpecificOutput": {
"hookEventName": "Stop",
"additionalContext": "Please run the test suite before finishing"
}
}
הימנעות מלולאה אינסופית
Stop hook שתמיד חוסם לא ייתן ל-Claude לעצור אף פעם. שני דברים מגנים עליכם:
stop_hook_activeהואtrueכש-Claude כבר ממשיך בגלל Stop hook. בדקו אותו וצאו מוקדם.- Claude Code מסיים את התור בכל מקרה אחרי ש-Stop hooks חסמו שמונה פעמים ברצף בלי קריאה לכלי ביניהן. משתנה הסביבה
CLAUDE_CODE_STOP_HOOK_BLOCK_CAPמעלה את המגבלה הזו.
הנה hook שגורם ל-Claude לתקן בדיקות שנכשלות פעם אחת, ואז נותן לו לעצור:
#!/bin/bash
# ~/.claude/hooks/tests-must-pass.sh
input=$(cat)
[ "$(jq -r '.stop_hook_active' <<<"$input")" = "true" ] && exit 0
if ! npm test --silent >/dev/null 2>&1; then
jq -nc '{decision: "block", reason: "npm test is failing. Fix the failures, then finish."}'
fi
exit 0
Hooks רצים בתיקיית העבודה של הסשן, כך ש-npm test רץ בפרויקט שלכם. כדאי שהבדיקות יהיו מהירות: כברירת מחדל, command hook יכול לרוץ עד 600 שניות, ו-Claude מחכה לו.
אם כל מה שצריך הוא ש-Claude ימשיך לעבוד לקראת יעד בלי לכתוב סקריפט, הפקודה המובנית /goal של Claude Code מגדירה בשבילכם Stop hook ברמת הסשן.
06התראות גם על כשלים
מכיוון ששגיאות API מדלגות על Stop, התראת "סיים" לא תגיד לכם ש-Claude נעצר בגלל rate limit. כדי לקבל עדכון על זה, הוסיפו את אותו סקריפט תחת StopFailure. הפלט וקוד היציאה שלו מתעלמים מהם, כך שהוא יכול רק להתריע או לרשום ללוג. ב-StopFailure, השדה last_assistant_message מכיל את טקסט השגיאה, למשל "API Error: Rate limit reached".
07איפה Claude Code Notifier נכנס לתמונה
Stop hook שקורא לשירות push הוא דרך מצוינת לדעת ש-Claude סיים בזמן שאתם לא ליד המחשב. אם רוצים לבנות את זה לבד, המדריך ל-ntfy הוא נקודת התחלה טובה.
Claude Code Notifier משתמש באותו hook של Stop, עם הבדל אחד: הוא יכול גם להחזיר תשובה. כלי שורת הפקודה החינמי שלו, ccn, רץ על Mac, Windows ו-Linux ומציג התראת דסקטופ בעצמו. כשאפליקציית iPhone או Android מצומדת, מקבלים push של "Claude סיים" ברגע שתור מסתיים, ואפשר להקליד את ההוראה הבאה בטלפון. ccn מחזיר אותה מה-hook של Stop כהוראה הבאה של Claude, כך שהסשן ממשיך גם כשאתם לא ליד המקלדת. אם הקלדתם ב-30 השניות האחרונות, שום דבר לא מחכה לטלפון. הכלי עצמאי ואינו של Anthropic. ראו להתרחק ממשימות ארוכות על תהליך העבודה הזה ואת הדף של ccn להתקנה.
08שאלות נפוצות
האם ה-Stop hook רץ כש-Claude מסיים את כל המשימה, או אחרי כל תגובה?
אחרי כל תגובה. Stop מופעל בכל פעם שהסוכן הראשי מסיים להגיב, לא משנה מה גודל הבקשה. אם צריך לדעת אם עוד עבודה מתוזמנת, בדקו את background_tasks ו-session_crons בקלט.
למה ה-Stop hook שלי לא הופעל?
ייתכן שקטעתם את התור, מה שמדלג על Stop, או שהוא הסתיים בשגיאת API, שמפעילה במקום זאת את StopFailure. אחרת, ודאו ש-/hooks מציג את ה-hook שלכם ושלסקריפט יש הרשאת הרצה. רשימת פתרון התקלות מכסה את השאר.
למה Claude ממשיך לעבוד אחרי שה-Stop hook שלי רץ?
ה-hook שלכם חוסם את העצירה, או ביציאה עם קוד 2 או בהדפסת decision: "block". ודאו ש-hook שנועד רק להתראות תמיד יוצא עם 0, ושכל hook חוסם בודק קודם את stop_hook_active.
איך מקבלים את התשובה הסופית של Claude בתוך ה-hook?
קראו את last_assistant_message מה-JSON שמגיע ב-stdin, למשל עם jq -r '.last_assistant_message'. אל תסתמכו על transcript_path: ייתכן שהקובץ עדיין לא מכיל את ההודעה האחרונה כש-Stop מופעל.
מה ההבדל בין Stop ל-SubagentStop?
Stop מיועד לסוכן הראשי. SubagentStop מופעל כש-subagent מסיים, מתאים לפי סוג הסוכן ותומך באותם שדות חסימה. התראת "Claude סיים" שייכת ל-Stop.