מדריך גרוק CLI בעברית

תיעוד 34

ווים (Hooks)

ווים (Hooks) מאפשרים לך להריץ סקריפט או לשלוח בקשת HTTP ברגעים מרכזיים במהלך הפעלת Grok. השתמש בהם כדי לבצע אוטומציה של משימות, לאכוף בדיקות בטיחות, לתעד פעילות, לשלוח התראות ולשלב כלים משלך.


#מה הם ווים?

וו (hook) הוא פקודת מעטפת (shell) או נקודת קצה של HTTP ש-Grok מזמן בעת התרחשות אירוע ספציפי במחזור החיים. ווים יכולים:

  • לחסום פעולות: וו מסוג PreToolUse יכול לדחות פקודה מסוכנת לפני שהיא רצה.
  • להשאיר את הסוכן בעבודה: וו מסוג Stop יכול לחסום את הסוכן מלסיים את תורו עד שמתקיים תנאי מסוים (למשל, סוויטת הבדיקות עוברת בהצלחה) ולהזין את הסיבה בחזרה למודל.
  • להגיב לאירועים: וו מסוג PostToolUse יכול לרשום כל הפעלת כלי לקובץ.
  • לתקן קריאה לאחר שרצה: וו מסוג PostToolUse יכול להסביר למודל את המשמעות של תוצאת כלי, או להחליף את הפלט שהמודל קורא: להסתיר סוד, לקצץ שורות יומן (log) רבות, בעוד שהתוצאה האמיתית נשארת מתועדת ברשומה.
  • להגדיר הקשר: וו מסוג SessionStart יכול לייצא משתני סביבה או להריץ סקריפטים של הגדרה ראשונית.

#מקרי שימוש נפוצים

  • מנגנוני בטיחות: לחסום פקודות כגון rm -rf / לפני שהן רצות.
  • רישום ביקורת (Audit logging): לתעד שימוש בכלים והפעלות לקובץ או לשירות חיצוני.
  • התראות: לשלוח הודעה כאשר משימה מסתיימת.
  • עיצוב אוטומטי (Auto-formatting): להריץ cargo fmt או prettier לאחר עריכות.
  • הגדרת סביבה: לייצא משתנים בעת תחילת הפעלה (session).
  • תהליכי עבודה מותאמים אישית: להפעיל תהליכי בנייה, בדיקות או פריסות באירועים ספציפיים.

#התחלה מהירה

  1. צור את ספריית הווים:

    mkdir -p ~/.grok/hooks
  2. צור קובץ וו, לדוגמה ~/.grok/hooks/session-start.json:

    {
      "hooks": {
        "SessionStart": [
          {
            "hooks": [
              { "type": "command", "command": "echo 'Grok session started in '$(pwd)" }
            ]
          }
        ]
      }
    }
  3. הפעל (או הפעל מחדש) הפעלת Grok. הוו רץ אוטומטית באירוע SessionStart.

  4. לחץ על Ctrl+L במסופים שאינם ממשפחת VS Code (או הרץ /hooks בכל מקום, מועדף במסופים ממשפחת VS Code) ובדוק את הלשונית Hooks כדי לוודא שהוא נטען.


#מיקומי ווים

ווים מתגלים מכמה מקומות (כולם ממוזגים יחד):

היקף (Scope)נתיבמהימן?הערות
Global~/.grok/hooks/*.jsonתמידווים אישיים
Global~/.claude/settings.json (וכן settings.local.json)תמידתאימות ל-Claude Code (ניתן להגדרה)
Global~/.cursor/hooks.jsonתמידתאימות ל-Cursor (ניתן להגדרה)
Project<project>/.grok/hooks/*.jsonדורש אמוןאוטומציה לפי מאגר (repo)
Project<project>/.claude/settings.json (וכן settings.local.json)דורש אמוןתאימות ל-Claude (ניתן להגדרה)
Project<project>/.cursor/hooks.jsonדורש אמוןתאימות ל-Cursor (ניתן להגדרה)
Config~/.grok/config.tomlתמידהווים שלך לצד שאר ההגדרות שלך
Configmanaged_config.toml ($GROK_HOME ו-/etc/grok)תמידווים המופצים על ידי הארגון (מסונכרנים מהשרת ועל המכשיר)
Configrequirements.toml (משתמש ומערכת)תמידווים המופצים על ידי הארגון בשכבת הדרישות
Pluginמצורף בתוך תוספים מותקניםלפי תוסףווים משותפים של הצוות

ווי קובצי הגדרה נמצאים באותו קובץ TOML שהארגון שלך כבר מנהל; ראה ווים בקובצי הגדרה לתיאור המבנה. מקורות הווים התואמים של ספקים אחרים נסרקים כברירת מחדל. כדי להשבית סריקה עבור ספק ספציפי, הגדר [compat.<vendor>] hooks = false בתוך ~/.grok/config.toml או במשתנה הסביבה המתאים. ראה Configuration לפרטים נוספים.

מתן אמון בפרויקט (Trusting a project): בפעם הראשונה שתפתח פרויקט הכולל ווים, עליך לתת בו אמון לפני שווי הפרויקט ירוצו; עד אז המערכת מדלגת עליהם בשקט. הענק אמון על ידי הרצת /hooks-trust (או הפעלה עם הדגל --trust); ההחלטה נרשמת במאגר המאוחד של אמון בתיקיות (~/.grok/trusted_folders.toml), אותו שער שמנהל שרתי MCP/LSP מקומיים של המאגר. ווים גלובליים ב-~/.grok/hooks/ הם תמיד מהימנים ואינם זקוקים לרישום. מנגנון זה מונע ממאגרים שאינם מהימנים להריץ קוד שרירותי.

מכיוון שווים מאוחדים תחת מנגנון האמון בתיקיות (folder-trust), מתן אמון באמצעות --trust או /hooks-trust מעניק אמון לתיקייה כולה עבור MCP, LSP, ווים, הוראות פרויקט ומיומנויות פרויקט (project skills) יחד, ומכסה תיקיות משנה של אותו מאגר. עותק git משובץ (nested git checkout) תחת אותה תיקייה נחשב לסביבת עבודה נפרדת ואינו מכוסה. לעומת זאת, השבתת האמון בתיקיות (GROK_FOLDER_TRUST=0 או [folder_trust] enabled = false) פותחת את כל המשטחים הללו יחד ללא שער.


#אירועי ווים

אירועים מופעלים בשלושה מקצבים: פעם אחת בכל הפעלה (SessionStart, SessionEnd), פעם אחת בכל תור (UserPromptSubmit, Stop, StopFailure), ובכל קריאת כלי בתוך התור (PreToolUse, PostToolUse, PostToolUseFailure).

אירועמתי הוא מופעלחוסם?
SessionStartהפעלה מתחילה. אינו מופעל עבור הפעלה עצמאית של תת-סוכן.לא
UserPromptSubmitאתה מגיש בקשה (prompt).כן: יכול לחסום את הבקשה
PreToolUseכלי עומד לרוץ.כן: יכול לדחות
PostToolUseכלי מסיים לרוץ (כולל שגיאה לוגית מובנית כמו יציאה בקוד שאינו אפס של run_terminal_command; כשל בשיגור או תוצאת שגיאה של כלי MCP מפעילים את PostToolUseFailure במקום זאת).לא, אך הוא יכול להזין משוב למודל ולהחליף את הפלט שהמודל רואה
PostToolUseFailureכלי נכשל בשיגור, או שכלי MCP מחזיר תוצאת שגיאה.לא, אך הוא יכול להזין למודל additionalContext
PermissionDeniedמערכת ההרשאות דוחה קריאת כלי.לא
Stopתור של סוכן מסתיים בהשלמה אמיתית (הפרעה מפעילה את StopCancelled במקום זאת).כן: יכול לחסום את העצירה
StopFailureתור מסתיים עקב שגיאת API.לא
StopCancelledרץ במקום Stop כאשר תור מסתיים ללא השלמה: הפרעת משתמש (Ctrl+C או כפתור עצירה בלקוח), דחיית בקשת הרשאה, מגבלת --max-turns, או יציאה מחוסר התקדמות.לא
Notificationאירועי תשומת לב של המשתמש (idle_prompt, permission_prompt, task_complete, ...).לא
SubagentStartתת-סוכן מתחיל.לא
SubagentStopתור של תת-סוכן מסתיים (מופעל פעם אחת, בתוך תת-הסוכן, עם בקרת החלטת עצירה).כן: יכול לחסום את העצירה
PreCompactדחיסת שיחה (conversation compaction) עומדת לרוץ.לא
PostCompactדחיסת שיחה הושלמה.לא
SessionEndההפעלה מסתיימת. נושא את subagentType עבור הפעלת צאצא, כדי שהמארח יוכל להבדיל בין פירוק של צאצא לבין הפירוק שלו עצמו.לא

SubagentEnd מתקבל ככינוי עבור SubagentStop. האירוע PreToolUse יכול לחסום קריאת כלי, UserPromptSubmit יכול לחסום בקשה (ראה להלן), ו-Stop/SubagentStop יכולים לחסום את הסוכן מלעצור (ראה בקרת החלטת עצירה). האירוע PostToolUse רץ מאוחר מכדי לחסום דבר, אך פלט ה-stdout שלו נקרא: הוא יכול להזין משוב למודל ולהחליף את פלט הכלי שהמודל רואה (ראה פלט PostToolUse). כל שאר האירועים הם סבילים.

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

וו מסוג UserPromptSubmit יכול לדחות בקשה: יציאה בקוד 2 חוסמת (stderr הופך להודעה), וכך גם מבנה JSON בצורה {"decision": "block", "reason": "..."} ב-stdout, בכל קוד יציאה. הסיבה מוצגת לך ולעולם אינה מתווספת להקשר של המודל. רק בקשה שהקלדת יכולה להיחסם: תורות של התעוררות אוטומטית (השלמות של משימות ותת-סוכנים, הפעלות מתזמן) והפעלות של תת-סוכנים מריצים את הוו במצב צפייה בלבד (observe-only). פסק הזמן כברירת מחדל לאירוע זה הוא 30 שניות; וו שחרג מפסק הזמן או קרס כושל במצב פתוח (fails open) והבקשה ממשיכה כרגיל.

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

#תאימות לווי Cursor

Grok מקבל את שמות אירועי הווים של Cursor באותיות camelCase, כך ש-~/.cursor/hooks.json נטען ללא שינוי:

אירוע של Cursorממופה אל
sessionStart, sessionEndSessionStart, SessionEnd
preToolUse, postToolUse, postToolUseFailurePreToolUse, PostToolUse, PostToolUseFailure
beforeShellExecution, beforeMCPExecution, beforeReadFilePreToolUse
afterShellExecution, afterMCPExecution, afterFileEditPostToolUse
afterAgentResponse, afterAgentThoughtPostToolUse
beforeSubmitPromptUserPromptSubmit
subagentStart, subagentStopSubagentStart, SubagentStop
preCompact, stopPreCompact, Stop

הווים של Cursor לפי פעולה (beforeShellExecution, afterFileEdit וכדומה) ממופים לאירועים הכלליים PreToolUse/PostToolUse. סקריפט הוו מקבל את שם הכלי בקלט ה-JSON ויכול לסנן לפיו, או להשתמש בשדה matcher.


#מבנה ה-JSON של הוו

כל קובץ .json יכול להגדיר ווים למספר אירועים:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bin/safety-check.sh", "timeout": 10 }
        ]
      }
    ],
    "PostToolUse": [
      {
        "hooks": [
          { "type": "command", "command": "bin/log-activity.sh" }
        ]
      }
    ]
  }
}

#שדות מפתח

  • שם האירוע (Event name) (מפתח ברמה העליונה): כל אירוע המופיע בסעיף אירועי ווים. Grok מדלג על שמות אירועים שאינם מזוהים, כך שקובץ הגדרות משותף של Claude או Cursor עדיין נטען.
  • matcher (אופציונלי): ביטוי רגולרי שבוחר אילו הפעלות יפעילו את הוו. מה שהוא בודק תלוי באירוע: שם הכלי באירועי כלים (PreToolUse, PostToolUse, PostToolUseFailure, PermissionDenied), סוג ההתראה ב-Notification, סוג תת-הסוכן ב-SubagentStart/SubagentStop (למשל explore), מקור ההתחלה ב-SessionStart (startup, resume, ...), סיבת הסיום ב-SessionEnd, גורם הדחיסה ב-PreCompact/PostCompact (manual או auto), סוג השגיאה ב-StopFailure (rate_limit, authentication_failed, invalid_request, server_error, max_output_tokens, או unknown), והסיבה ב-StopCancelled (user_interrupt, permission_rejected, permission_cancelled, max_turns, no_progress, או unknown). שדה matcher על Stop או UserPromptSubmit זוכה להתעלמות בליווי אזהרה (אירועים אלה מופעלים תמיד). שדה matcher ריק או מושמט תואם להכל. צליל סיום חשיבה צריך להגדיר matcher ל-idle_prompt תחת Notification (כל סיום תור, ולאחריו חוסר פעילות ממושך); permission_prompt מופעל רק כאשר ממשק הרשאות ממתין בפועל. ה-matcher בודק את שם הכלי האמיתי; קריאות MCP המנותבות דרך משגרי use_tool הפנימיים או CallMcpTool של Cursor מופיעות כשם מלא בתבנית server__tool (למשל linear__save_issue), לכן יש לבצע התאמה מולו ולא מול שם המשגר.
  • type: "command" (הרצת סקריפט או פקודת מעטפת של שורה אחת) או "http" (שליחת האירוע ב-POST לכתובת URL).
  • command: נתיב לקובץ הפעלה (יחסי לקובץ ה-JSON) או פקודת מעטפת מוטמעת.
  • timeout: שניות לפני סיום מאולץ של הוו (ברירת מחדל: 5, או 600 עבור שערי Stop/SubagentStop/PostToolUse). כל כשלי הווים (חריגות זמן, קריסות, פלט שאינו תקין, חוסר במשתני סביבה נדרשים) פועלים במצב fail-open: הכשל מתועד עבור גלילת ממשק המשתמש (scrollback), אך קריאת הכלי אינה נחסמת. רק החלטת deny מפורשת שמוחזרת על ידי הוו חוסמת קריאת כלי.

#כינויים לשמות כלים

בתוך matcher, Grok ממפה שמות כלים בסגנון Claude לשמות שלו, כך שווים שהועברו מ-Claude יופעלו כהלכה. כינויים נפוצים כוללים:

  • Bash ממופה אל run_terminal_command
  • Read ממופה אל read_file
  • Edit, Write ו-MultiEdit ממופים אל search_replace
  • Grep ממופה אל grep
  • Glob ו-ListDir ממופים אל list_dir
  • WebSearch ממופה אל web_search
  • Task ממופה אל spawn_subagent

ה-matcher שומר גם על שמו המקורי, כך ש-Bash תואם גם ל-Bash וגם ל-run_terminal_command.


#כיצד וו מעובד ומוכרע

בעת הפעלת אירוע, Grok מכריע ומעבד אותו בארבעה שלבים:

  1. בחירת קבוצות תואמות. עבור אותו אירוע, כל קבוצת התאמה ששדה ה-matcher שלה תואם לשדה של האירוע מופעלת. ה-matcher בודק את שם הכלי באירועי כלים, את סוג ההתראה ב-Notification וכך הלאה (ראה שדות מפתח). שדה matcher ריק או מושמט תואם להכל.
  2. הרצת המטפלים (handlers) לפי הסדר. מטפלים בקבוצות שנבחרו רצים לפי סדר ההגדרה, כאשר כל אחד מקבל את האירוע כ-JSON ב-stdin, עד שאחד מהם מחזיר deny (מה שעוצר את השרשרת). מטפלים ממקורות שונים (גלובלי, פרויקט, תוסף, קובץ הגדרה) ממוזגים, ומטפלים זהים מנופים מכפילויות. כל מטפל רואה את קלט הכלי המקורי של המודל; updatedInput של PreToolUse מוחל רק לאחר שכל המטפלים סיימו, כך שמטפל אחד אינו יכול לראות את השכתוב של מטפל אחר (השכתוב האחרון מנצח).
  3. החלת ההחלטה. עבור שער PreToolUse, ה-deny הראשון חוסם את הקריאה והסיבה שלו מוצגת למודל, updatedInput משכתב את קלט הכלי, ואחרת הקריאה ממשיכה. עבור Stop ו-SubagentStop, החלטת block משאירה את הסוכן בעבודה. עבור PostToolUse הכלי כבר רץ, ולכן שום דבר אינו נחסם וכל וו רץ: סיבת block וכל additionalContext נמסרים למודל יחד עם תוצאת הכלי, והחלפת פלט משכתבת את העותק של אותה תוצאה שהמודל רואה. כל אירוע אחר הוא סביל: הפלט שלו מתועד אך אינו משנה את זרימת הבקרה.
  4. כשל פתוח (Fail open). מטפל שחורג מפסק הזמן, קורס או פולט פלט שאינו תקין מתועד בגלילה (scrollback) אך לעולם אינו חוסם את הפעולה. החריג היחיד הוא updatedInput של PreToolUse שנכשל מול הסכמה של הכלי: לא ניתן להריץ את השכתוב בבטחה, ולכן הקריאה נחסמת ומדווחת כשגיאת קלט לא תקין. בכל מקרה אחר, רק deny מפורש חוסם קריאת כלי.

#ווים בקובצי הגדרה

ווים יכולים להתקיים גם ישירות בהגדרות ה-Grok שלך, כך שצוות יכול להפיץ אותם יחד עם שאר ההגדרות שלו במקום לשלוח קובצי JSON נפרדים. אותו אובייקט hooks נקרא משלושה קובצי TOML:

קובץשכבה (Tier)מי מגדיר אותו
~/.grok/config.tomlמשתמש (User)אתה
managed_config.toml ($GROK_HOME, /etc/grok)מנוהל / מערכת (Managed / system)הארגון שלך
requirements.toml (משתמש ומערכת)דרישות (Requirements)הארגון שלך

מבנה ה-TOML זהה מבנית לאובייקט הוו ב-JSON, כך שוו קיים מתורגם ישירות:

[[hooks.PreToolUse]]
matcher = "Bash|Write|Edit"
hooks = [
  { type = "command", command = "/opt/guard/pretooluse.sh", timeout = 10 },
]

כל קבוצת התאמה היא רשומת [[hooks.<Event>]] עם matcher אופציונלי ומערך hooks פנימי של מטפלים. שדות המטפל (type, command, url, timeout, env) ושמות האירועים זהים לחלוטין לאלה שבמבנה ה-JSON של הוו.

TOML מציע שני תחבירים שווי ערך עבור המטפלים הפנימיים, ושניהם מפוענחים לאותו מבנה בדיוק. מערך הטבלאות המוטמעות (inline-table) המוצג לעיל מומלץ: הוא הקריא ביותר למקרה הנפוץ של מטפל יחיד. צורת מערך הטבלאות המקונן מקובלת גם היא:

[[hooks.PreToolUse]]
matcher = "Bash|Write|Edit"
[[hooks.PreToolUse.hooks]]
type = "command"
command = "/opt/guard/pretooluse.sh"
timeout = 10

מומלץ להעדיף את הצורה המוטמעת כדי להימנע מחזרה על הכותרת [[hooks.<Event>.hooks]] עבור כל מטפל.

  • מצטבר בין שכבות. הווים של כל שכבה רצים; שכבה בעדיפות נמוכה יותר מוסיפה ווים אך לעולם אינה מחליפה בלוק של שכבה אחרת. וו המוגדר בצורה זהה ביותר משכבה אחת מנופה מכפילויות, ונשמר העותק בעל הסמכות הגבוהה ביותר.
  • תוויות מקור. ווי הגדרה מופיעים ב-/hooks כשהם מתויגים לפי המקור שלהם (managed:, requirements/user:, user: וכדומה), כך שתוכל לראות איזו שכבה תרמה כל אחד מהם.
  • ללא פריסה בזמן קריאה. ערך מילולי ${VAR} ב-command או ב-url מגיע למריץ הווים ללא שינוי, בהתאם לסמנטיקה של קובצי ווי JSON; מריץ הווים מבצע את הפריסה הבודדת.

#כתיבת סקריפטים של ווים

#קלט

האירוע נשלח כ-JSON ב-stdin (לדוגמה, אירוע PreToolUse; המטען כולל תמיד גם את toolUseId ו-toolInputTruncated):

{
  "hookEventName": "pre_tool_use",
  "hook_event_name": "PreToolUse",
  "sessionId": "abc-123",
  "cwd": "/Users/you/project",
  "workspaceRoot": "/Users/you/project",
  "permissionMode": "default",
  "toolName": "run_terminal_command",
  "toolInput": { "command": "npm test" },
  "timestamp": "2026-04-14T12:00:00Z"
}

כל אירוע נושא את אותם שדות משותפים: hookEventName, sessionId, cwd, workspaceRoot, timestamp, permissionMode (default, auto, plan, או bypassPermissions), וכן promptId (התור שאליו שייך האירוע; אינו קיים עבור אירועים ברמת ההפעלה), בנוסף לשדות ספציפיים לאירוע כמו toolName לעיל. המפתח hook_event_name (מפתח ב-snake_case) נושא את הערך ב-PascalCase של Claude; המפתח hookEventName (מפתח ב-camelCase) נושא את הערך ב-snake_case של grok.

#פלט (ווים חוסמים)

עבור ווי PreToolUse, כתוב JSON ל-stdout:

  • אפשר: {"decision": "allow"}
  • דחה: {"decision": "deny", "reason": "Unsafe command detected"}
  • שאל את המשתמש: {"decision": "ask", "reason": "Confirm this deploy"}
  • ללא הבעת עמדה: {"decision": "defer"}
  • שכתב את קלט הכלי: {"hookSpecificOutput": {"hookEventName": "PreToolUse", "updatedInput": {"command": "npm test"}}}
  • אמור משהו למודל: {"hookSpecificOutput": {"hookEventName": "PreToolUse", "additionalContext": "This repo builds with xb, not cargo"}}

ההחלטה יכולה להיכתב בתור decision ברמה העליונה או בתור hookSpecificOutput.permissionDecision. שניהם מקבלים allow, deny, ask, או defer (האיותים הישנים approve ו-block פועלים גם כן), כל אחד עם שדה סיבה משלו: reason ו-permissionDecisionReason. הערך התקני permissionDecision קובע כאשר הוא קיים; שדה decision ברמה העליונה חל רק כאשר הוא נעדר. הודעת הדחייה או השאלה היא permissionDecisionReason אם קיים, ואחרת reason. הערך allow פירושו רק "לא נחסם", הוא אינו מאשר אוטומטית קריאה שהמשתמש היה נשאל לגביה בכל מקרה. ערך החלטה מחוץ לקבוצה הזו נחשב לכשל של הוו, אשר כושל במצב פתוח, אלא אם הוו גם יוצא בקוד 2, ובמקרה כזה הדחייה נשארת בעינה ונושאת את השגיאה בסיבה שלה.

החלטת ask גורמת לקריאה להגיע לבקשת הרשאה (permission prompt): שום דבר שהיה מאשר אותה ללא שאלה (מצב always-approve, מצב auto, הרשאת "always allow" שמורה, פקודה בטוחה) אינו חל, ובקשת ההרשאה מציינת את שם הוו שלך ומציגה את הסיבה שלך. לעולם אין בקשת הרשאה שנייה: במקום שבו היית נשאל בכל מקרה, ה-ask רק משנה את התווית של אותה בקשה. אישור מריץ אותה; דחייה חוסמת אותה כדחיית הרשאה רגילה. לקוח שרץ במצב מלא של always-approve/YOLO (עונה אוטומטית על כל בקשת הרשאה) עדיין יאשר את הקריאה אוטומטית, בדומה ל-bypassPermissions של Claude Code: ה-ask דורס את נתיבי ה-always-approve, ה-auto, ההרשאה השמורה והפקודה הבטוחה של המנהל, אך לא לקוח שמאשר באופן גורף כל בקשת הרשאה.

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

רק ווים המוגדרים בקובץ הגדרות (ווי פקודה ו-HTTP) יכולים לבצע ask, defer או לשלוח additionalContext: וו מסוג PreToolUse שנרשם דרך grok-agent-sdk יכול לבצע allow או deny, וכל השאר מושלך; החלטת ask או defer שם משאירה את הקריאה לזרימת ההרשאות הרגילה ונרשמת ביומן כהחלטה לא מזוהה, ו-additionalContext לעולם אינו מגיע למודל.

updatedInput מחליף את קלט הכלי לפני שהוא רץ, באופן שקט: המודל אינו מקבל עדכון ושום דבר אינו נכתב לגלילה (scrollback), כך שהסימן היחיד לשכתוב הוא הארגומנטים המשוכתבים עצמם, שהמשתמש רואה אם הקריאה מגיעה לבקשת הרשאה. שער מצב plan, בקשת ההרשאה, הכלי עצמו ומטען ה-PostToolUse המאוחר יותר רואים כולם את הקלט המשוכתב, כך שוו יכול לנרמל או להקשיח קריאה במקום רק לאשר או לדחות אותה. ווים רצים לפני שער מצב plan, כך שוו בעל תופעות לוואי מופעל גם כאשר מצב plan דוחה את הקריאה מאוחר יותר.

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

החלטת deny משליכה כל updatedInput; כאשר מספר ווים מחזירים שכתוב, האחרון מנצח. השמטת decision בעת החזרת updatedInput מאשרת את הקריאה ומחילה את השכתוב.

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

additionalContext הוא הערה עבור המודל. הוא מגיע לאחר שהקריאה רצה, לעולם לא לפניה, יחד עם תוצאות האצווה שהקריאה שייכת אליה, עטוף בתגית התזכורת של המעטפת שלך (<system-reminder> כברירת מחדל) ומציין את שם הוו שכתב אותו, כך שהמודל יכול להבדיל בין הטקסט שלך לבין טקסט המשתמש. כל וו ששולח הקשר כזה נמסר, לפי סדר הרצת הווים (בשונה מ-updatedInput, שבו הכותב האחרון מנצח). החלטת deny משליכה את כולו, מכיוון שהקריאה לעולם אינה רצה, ומציינת את ההשלכה ביומן. טקסט מעל 10,000 תווים נקצץ, אותה תקרה שחלה על משוב של Stop.

#פלט PostToolUse

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

{
  "decision": "block",
  "reason": "The diff still contains a debug print",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "This file is generated; edit the template instead",
    "updatedToolOutput": { "type": "Bash", "command": "…", "exit_code": 0, "output_for_prompt": "[redacted]" }
  }
}
שדההשפעה
decision: "block" + reasonמוסר את reason למודל לצד תוצאת הכלי. הפלט של הכלי עצמו עדיין מגיע; "block" פירושו "אמור למודל שמשהו השתבש", ולא "עצור את הקריאה".
additionalContextמוסיף הערה למודל לצד תוצאת הכלי.
updatedToolOutputמחליף את העותק של התוצאה שהמודל רואה. מפתח אוניברסלי; עובד עבור כל כלי.
updatedMCPToolOutputכינוי ל-MCP בלבד עבור updatedToolOutput. זוכה להתעלמות בכלי מובנה.
  • מסירה (Delivery). סיבת הבלוק ו-additionalContext מגיעים לאחר תוצאת הכלי, עטופים בתגית התזכורת של המעטפת שלך ומציינים את שם הוו שכתב אותם, כך שהמודל יכול לפעול באותו התור. סיבת הבלוק ו-additionalContext של כל וו נמסרים לפי סדר הרצת הווים, כך שממצא של וו אחד אינו יכול להשליך ממצא של וו אחר. רק החלפות פלט פועלות לפי הכלל שהכותב האחרון מנצח: כאשר שני ווים מחזירים החלפה, האחרון שורד וההשלכה מצוינת ביומן.
  • בניית updatedToolOutput. בכלי מובנה עליו לשאת את מבנה הפלט של grok עצמו עבור הכלי שרץ, אובייקט מתויג כדוגמת {"type": "Bash", ...}. קח את toolResult שהאירוע מסר לך, ערוך אותו ושלח אותו חזרה: זה בדיוק המבנה שלפיו הוא מאומת. החלפה שנכשלת בפענוח או מפוענחת כפלט של כלי אחר זוכה להתעלמות והמקורי נשאר בעינו, אך הרצת הוו נרשמת כ-Failed עם הסיבה, כך שוו עם יציאה בקוד 0 שמציג "failed" מדווח על החלפה שהושלכה, ולא על כך שהוא לא רץ כלל. ערך decision שהוקלד שגוי (רק "block" מכובד) מדווח באותו אופן. בדוק תחילה את toolResultTruncated: מטען בגודל חריג מגיע לוו כמחרוזת פשוטה ואינו יכול להישלח בחזרה.
  • כלי MCP. אין מבנה לאכוף, כך שגם updatedToolOutput וגם updatedMCPToolOutput מועברים ללא בדיקה: מחרוזת JSON הופכת לטקסט מול המודל ככתבו וכלשונו, וכל ערך אחר עובר סריאליזציה, והוו האחרון שכותב מנצח בין שני המפתחות.
  • מגבלות גודל (Caps). סיבת הבלוק ו-additionalContext נקצצים ב-10,000 תווים, אותה תקרה שמשותפת למשוב Stop ולהקשר של PreToolUse. החלפת פלט מקבלת 64K תווים. המגבלות נמדדות על גבי הטקסט המוצג מול המודל ומוחלות ברגע שההחלפה עובדה, כך ש-updatedToolOutput ארוך נקצץ כמחרוזת ולא מושלך בגלל גודלו. החלפה מובנית מושלכת רק כאשר היא אינה תואמת למבנה הפלט של הכלי עצמו.
  • וו שבור (Broken hook). יציאה בקוד שאינו אפס, כולל יציאה בקוד 2, שומרת על סיבת הבלוק ומשליכה את כל השאר: additionalContext וההחלפה מושלכים וההשלכה מצוינת ביומן, אותו כלל ש-PreToolUse מחיל על updatedInput. הבלוק הוא כיוון הבטיחות (fail-safe).
  • רשומה לעומת מודל (Record vs. model). החלפה משכתבת רק את העותק של המודל. הגלילה (scrollback), התמליל (transcript) והטלמטריה שומרים על המקור, כך שהסתרת סוד מסתירה אותו מהמודל, לא ממך, וכישלון ששוכצב כהצלחה נשאר אמיתי ברשומה. תמונות אינן נמסרות תחת החלפה, כך שצילום מסך או קריאת PDF שהוחלפו מגיעים למודל כטקסט שלך בלבד. כל מה שוו שולח (הערה, סיבת בלוק, החלפה) עובר escape כדי שלא יוכל לסגור את תגית התזכורת ולהתחזות להוראה שנכתבה על ידי המעטפת או המשתמש.
  • החלפת פלט מוגבלת לקובצי הגדרות בלבד. ווי פקודה ו-HTTP יכולים לבצע את כל זה. וו מסוג PostToolUse שנרשם דרך grok-agent-sdk יכול לתרום סיבת block ו-additionalContext, אך אינו יכול להחליף את פלט הכלי.
  • מתי הוא מופעל. PostToolUse מופעל עבור כל כלי שרץ בפועל, כולל כלי שתוצאתו היא שגיאה לוגית מובנית כמו יציאה בקוד שאינו אפס של run_terminal_command. כלי שנכשל בשיגור, או כלי MCP שהחזיר תוצאת שגיאה, מפעיל את PostToolUseFailure במקום זאת: הקשר בלבד, הוא יכול להזין למודל additionalContext אך אינו יכול לחסום או להחליף את הפלט. הוו יורש את ברירת המחדל של השער בת 600 שניות (הוא מריץ לרוב linter או בדיקה); הגדר timeout במפורש רק כאשר הבדיקה זקוקה ליותר או פחות זמן. וו שחרג מפסק הזמן נרשם ככישלון ואינו תורם דבר.

#קודי יציאה

קוד יציאהמשמעות
0הצלחה / אישור (עבור ווים חוסמים)
2דחייה מפורשת (PreToolUse), חסימת עצירה עם stderr כמשוב (Stop/SubagentStop), או משוב למודל (PostToolUse). עבור PreToolUse, שורת ה-stderr הראשונה (תחת מגבלה) הופכת לסיבת הדחייה כאשר ה-JSON אינו נושא סיבה; Stop/SubagentStop ו-PostToolUse מזינים את מלוא ה-stderr למודל, ושדה reason ב-JSON גובר עליו.
אחרכשל במצב פתוח (Fail-open): הכשל מתועד (בצורה exit code N: <first stderr line>) אך שום דבר אינו נחסם. עבור PreToolUse, החלטת deny ב-JSON שב-stdout מכובדת ללא תלות בקוד היציאה. עבור Stop/SubagentStop, החלטת JSON תקינה ב-stdout גוברת על קוד היציאה; קוד היציאה קובע רק כאשר אין ב-stdout תוכן JSON שמיש, ובמקרה כזה יציאה בקוד 2 חוסמת כאשר stderr משמש כמשוב. עבור PostToolUse, הכלי כבר רץ ולכן שום דבר אינו נחסם בין כה וכה; הכשל עדיין מתועד, והוו שומר על סיבת הבלוק שלו אך מאבד את additionalContext ואת החלפת הפלט שלו.

יציאה בקוד 2 ב-PostToolUse היא שינוי התנהגות. בעבר היא נחשבה לכישלון מתועד רגיל שלא שינה דבר; כעת היא מזינה את ה-stderr של הוו למודל. וו תיעוד שנכתב בצורה run_checker; exit $? מעביר לכן למודל את כל מה שהבודק הדפיס בכל פעם שהבודק יוצא בקוד 2: הכלים mypy, grep, pytest ו-argparse משתמשים כולם בקוד יציאה 2 עבור "אין התאמה" או "שימוש שגוי". סיים וו כזה עם exit 0 מפורש כדי לשמור עליו שקט.

כתוב אבחון קריא לבני אדם ל-stderr: זהו ערוץ המשוב של הוו. במקרי כשל, שורת ה-stderr הראשונה מופיעה ברשומת הגלילה (scrollback) וביומנים במקום קוד יציאה בלבד.

#בקרת החלטת עצירה

ווי Stop ו-SubagentStop רצים כאשר הסוכן עומד לסיים את תורו ויכולים להשאיר אותו בעבודה (תואם Claude Code). כתוב JSON ל-stdout:

  • חסום את העצירה: {"decision": "block", "reason": "The test suite hasn't been run yet"}. הסיבה מוזנת בחזרה למודל כהודעת משתמש והסוכן מריץ סבב נוסף באותו התור.
  • משוב שאינו שגיאה: {"hookSpecificOutput": {"hookEventName": "Stop", "additionalContext": "Run the linter before finishing"}}. משאיר גם כן את הסוכן בעבודה, אך מוצג כמשוב של הוו ולא כשגיאת וו.
  • עצירה כפויה: {"continue": false, "stopReason": "Budget exhausted"}. מסיים את התור, תוך עקיפת כל חסימה.
  • אפשר את העצירה: יציאה בקוד 0 ללא פלט (או כל פלט שאינו JSON).

יציאה בקוד 2 חוסמת גם היא את העצירה, כאשר stderr משמש כמשוב.

קלט הוו כולל את stopHookActive ו-lastAssistantMessage. השדה stopHookActive מקבל ערך true כאשר הסוכן כבר ממשיך בעקבות חסימה קודמת של וו עצירה באותו תור; בדוק אותו, או את התמליל, כדי להימנע מחסימה בתנאי שלעולם לא ייפתר. lastAssistantMessage נושא את הטקסט של תגובתו הסופית של הסוכן בתור זה, כך שווים יכולים לפעול לפיו מבלי לפענח את התמליל. כל אירוע שנושא שדה זה קוצץ אותו ב-32,768 תווים, עם אותו סימון ... [+N chars] כמו שאר שדות הטקסט החופשי. מגבלה זו רחבה בהרבה ממגבלת 1,000 התווים החלה על errorDetails וחבריו מכיוון שהיא נושאת תשובה שלמה ולא תווית, והיא מותאמת לאותו קנה מידה של תקרת מטען הכלים. לאחר 8 המשכות (continuations) (חסימות או משוב שאינו שגיאה) בתור אחד, השער נעקף והתור מסתיים; אין פנייה לווים עבור עצירה סופית וכפויה זו. המונה פועל לפי תור: בקשת המשתמש הבאה מתחילה מחדש, כך שיעד ארוך טווח יכול להשתרע על פני תורות מרובים. כשלי ווים פועלים במצב fail-open: הסוכן עוצר כרגיל.

ווי Stop, SubagentStop ו-PostToolUse מוגדרים כברירת מחדל לפסק זמן של 600 שניות מכיוון ששערים אלה מריצים בדרך כלל תהליכי בנייה או סוויטות בדיקות, וו שחרג מפסק הזמן כושל במצב פתוח, כך שהבדיקה אינה חוסמת בכל מקרה. כל שאר האירועים שומרים על ברירת המחדל של 5 שניות. הגדר timeout במפורש כאשר שער זקוק ליותר מכך: { "type": "command", "command": "bin/verify.sh", "timeout": 1200 }.

השער רץ רק עבור השלמות אמיתיות. תור שהופרע (Ctrl+C), סורב או נקטע במגבלת התורות מדלג על שער ה-Stop, אם כי Ctrl+C שמתרחש בזמן שוו Stop כבר רץ מחסל אותו באמצע הריצה (ראה להלן); תורות עם שגיאות API מפעילים את StopFailure, ותורות שבוטלו מפעילים את StopCancelled. המקש Esc לעולם אינו מבטל תור שרץ. אירוע Stop נפרד מופעל גם בסיום ההפעלה (reason: "channel_closed" או "shutdown"); פלט ההחלטה שלו מפוענח אך זוכה להתעלמות, מכיוון שלא נותר תור להמשיך. סקריפט שסופר או מגביל לפי הפעלות Stop צריך לבדוק reason == "end_turn" כדי שהפעלת סיום ההפעלה לא תטה את החישוב.

StopFailure מיועד לצפייה בלבד (השתמש בו כדי לתעד כשלים או לשלוח התראות; הפלט וקוד היציאה זוכים להתעלמות). הקלט שלו נושא את error (הסוג המסווג שה-matcher בודק: rate_limit, authentication_failed, invalid_request, server_error, max_output_tokens, או unknown עבור כל מה שסביבת הריצה אינה מצליחה להבדיל; שגיאות קיבולת מסווגות כ-rate_limit), את errorDetails (פירוט השגיאה הגולמי, כאשר הוא זמין, נקצץ ב-1000 תווים; נעדר בסירוב, שהסברו מופיע ב-lastAssistantMessage בלבד), את lastAssistantMessage (טקסט השגיאה המעובד שמוצג בשיחה; באירוע זה מדובר במחרוזת השגיאה ולא בפלט העוזר), ואת subagentType (סוג תת-הסוכן כאשר התור רץ בתוך תת-סוכן).

StopCancelled מיועד גם כן לצפייה בלבד. הוא רץ במקום Stop כאשר התור מסתיים ללא השלמה, באותו אופן שבו StopFailure רץ במקום Stop בשגיאת API.

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

אם על המארח שלך חל איסור מוחלט לפספס מעבר למצב חוסר פעילות (idle), האזן גם להתראת Notification מסוג idle_prompt. היא מכסה כל חריג שבו ההפעלה עדיין פעילה, עם פער אחד: הפעלה שפעילותה היחידה הייתה פקודה במצב bash שרצה עד תומה אינה מקבלת את הדיווח וגם לא את הפינג, אם כי הפרעה לפקודה כזו מזכה בשניהם. SessionEnd מכסה את סגירת ההפעלה (teardown). הפינג של idle_prompt מופעל כדקה לאחר שההפעלה מתייצבת, דורש שלפחות תור אחד הסתיים, ומבוטל אם אתה שולח הודעה נוספת קודם לכן.

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

סקריפט שעוקב אחר מצבי עבודה (busy) וחוסר פעילות (idle) צריך להתבסס על promptId, בהתאם לכללים שלהלן. grok מנפיק מזהה אחד לכל תור, אך לקוח שמספק מזהה משלו ב-_meta אחראי על ייחודיותו, לכן התייחס למזהה כאל ערך אטום והגבל את הטווח שלו להפעלה.

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

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

Stop הוא שער, ולכן כאשר וו עצירה חוסם הוא מופעל שוב עבור כל סבב המשך; רק ההפעלה שמאפשרת לתור להסתיים נחשבת לדיווח, ותור שמסתיים במצב מבוטל או נכשל לאחר Stop חסום מדווח על כך במקום זאת. צופה סביל אינו יכול להבדיל בין הפעלת המשך לבין ההפעלה הסופית (stopHookActive הוא true בשתיהן), כך שממשק משתמש המבוסס על Stop בלבד יציג מצב חוסר פעילות שגוי החל מהפעלת ההמשך הראשונה ועד לבקשת המשתמש הבאה, מכיוון ששום UserPromptSubmit אינו מסמן סבב המשך. השמט את Stop מסקריפט המצב כאשר אתה מריץ גם שער חוסם, והסתמך על התראת Notification מסוג idle_prompt במקום זאת.

חלק מהתורות אינם מדווחים על אף אחד משלושת האירועים:

  • מצב bash (!) ופקודות לוכסן מובנות (builtin slash commands) שרצות עד תומן. הפרעה לפקודה כזו עדיין מדווחת על user_interrupt, ללא UserPromptSubmit מקדים.
  • פעולת ביטול ושליחה (cancel-and-send), הרצה לאחור (rewind), או בקשה בתור שהוסרה לפני שרצה.
  • סגירת הפעלה (teardown), המדווחת על ידי Stop של סיום הפעלה ו-SessionEnd.
  • תור שווי העצירה שלו השאירו את הסוכן בעבודה עד שמגבלת ההמשכות לתור כפתה את העצירה.
  • תור שבו אף וו עצירה לא רץ עד תומו, מכיוון שכולם היו מושבתים, לא מהימנים, נכשלו, או, בתת-סוכן, מכיוון שכל ה-matchers שלהם החטיאו. אי הדיווח על התור הוא מכוון: ביטול או כישלון מאוחרים יותר יוכלו לדווח עליו.
  • תור שהוחלף על ידי התור הבא בזמן שהדיווח שלו עדיין נבנה.
  • דיווח שעדיין ממתין בתור בעת יציאה מההפעלה: סגירת ההפעלה ממתינה חצי שנייה לווי סיום תור שממתינים בתור, ולאחר מכן משליכה את מה שנותר ומפסיקה כל וו שעדיין רץ.
  • תור שהושלם, הריץ את ה-Stop שלו, ורק אז נכשל בכתיבה לדיסק: הוא דיווח על Stop, ולכן לא מגיע אחריו StopFailure. הכישלון עדיין מוצג בשיחה.

הקלט של StopCancelled נושא:

  • reason: הסיבה המסווגת, והערך שה-matcher בודק. user_interrupt (Ctrl+C, כפתור עצירה בלקוח, או session/cancel מהלקוח), permission_rejected (דחית קריאת כלי), permission_cancelled (סגרת את בקשת ההרשאה), max_turns, no_progress (הסוכן יצא לאחר סבבים חוזרים ללא פעולה), או unknown (ביטול שסביבת הריצה לא הצליחה לסווג, ומשמש כברירת מחדל לתאימות קדימה). ה-matcher בודק שדה זה בלבד, כך שוו שמעוניין בכל עצירה שמקורה במשתמש מתאים את הסיבות שחשובות לו וקורא את cancelledBy מתוך המטען. סיבות חדשות עשויות להתווסף בעתיד, לכן התייחס לערך לא מזוהה כפי שאתה מתייחס ל-unknown.
  • cancelledBy: user עבור הפרעה, קריאת כלי שנדחתה, או בקשת הרשאה שנסגרה; runtime עבור כל מה שהסוכן החליט בעצמו, כגון max_turns ו-no_progress; unknown כאשר reason הוא unknown, משום שביטול שסביבת הריצה לא הצליחה לסווג אינו יכול לטעון שהמשתמש לא היה מעורב. נגזר מתוך reason, כך שסיבה חדשה מסווגת אוטומטית. ערכים עשויים להתווסף גם כאן: התייחס לערך שאינך מזהה כפי שאתה מתייחס ל-unknown, במקום להניח שכל מה שאינו user מקורו בסביבת הריצה.
  • cancelTrigger: המחווה (gesture), כאשר הלקוח ציין אחת, נקצץ ב-64 תווים, מכיוון ששם מחווה הוא אסימון (token). המציג המובנה שולח אחד משלושה: ctrl_c, mouse (כפתור העצירה שעל המסך), או dashboard_stop. הוא לעולם אינו שולח esc (המקש Esc אינו מבטל תור). לקוח אחר עשוי לשלוח כל מחרוזת, כולל esc, והיא מועברת ללא שינוי. כל ערך כאן מסווג כ-user_interrupt, כולל ערך שבמקרה נושא שם פנימי כמו shutdown, מכיוון שלקוח שמבקש לבטל מייצג בקשה של המשתמש; קרא את cancelledBy מהמטען במקום לנתח מחרוזת זו. מושמט עבור קריאת session/cancel פשוטה ועבור כל סיבה שמקורה בסביבת הריצה.
  • reasonDetails: אותו סוג פירוט ש-StopFailure מציב ב-errorDetails, כאשר קיים כזה בסביבת הריצה. עבור קריאת כלי שנדחתה, זהו <tool>: <why>. נקצץ ב-1000 תווים, בדומה ל-errorDetails של StopFailure.
  • lastAssistantMessage: כל מה שהתור הספיק לרשום לשיחה בעת ההפרעה, אם בכלל. Ctrl+C במהלך התשובה הסופית משאיר את הטקסט האחרון שנרשם, או כלום אם התור לא רשם דבר. נקצץ באותו אופן כמו אותו שדה ב-Stop וב-StopFailure.
  • subagentType: סוג תת-הסוכן כאשר התור רץ בתוך תת-סוכן, כדי שוו יוכל להבדיל בין עצירה של סוכן מקונן לבין עצירה של ההפעלה כולה. אינו קיים בהפעלה הראשית.

ה-timestamp של המעטפת מוטבע כאשר הוו משוגר, ולא כאשר התור הסתיים. שלושת אירועי סיום התור ממתינים בתור מאחורי עובד (worker) יחיד, כך שדיווח הממתין לוו איטי שלפניו נושא חותמת זמן מאוחרת יותר מהרגע שהוא מתאר. השתמש ב-promptId להצלבה, ולא בשעון.

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

פעולת "בטל ושלח" (cancel-and-send, הקלדת הודעה חדשה בזמן שתור רץ) אינה מפעילה את StopCancelled, משום שהתור מוחלף ולא נעצר, והסוכן נשאר עסוק. בתוך תת-סוכן, user_interrupt אינו מופעל גם כן: הוא מגיע בעקבות הביטול של ההורה, והאות ברמת ההפעלה הוא האות השימושי. אירועי max_turns, no_progress או הרשאה שנדחתה בתוך תת-הסוכן עצמו כן מופעלים. ה-matcher בודק את reason בלבד, כך שסקריפט המדווח אם ההפעלה במצב חוסר פעילות צריך לצאת מוקדם כאשר subagentType קיים.

מחוון מלא של מצב עבודה וחוסר פעילות דורש חמישה רישומים. UserPromptSubmit מסמן את ההפעלה כעסוקה; Stop, StopFailure ו-StopCancelled מייצבים אותה ללא קשר לאופן שבו התור הסתיים; התראת Notification מסוג idle_prompt היא רשת הביטחון עבור התורות שאינם מדווחים על אף אחד משלושת האירועים. רישום של StopCancelled בלבד ישאיר את המארח במצב עסוק לאחר כל תור רגיל.

{
  "hooks": {
    "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "bin/turn-started.sh" }] }],
    "Stop": [{ "hooks": [{ "type": "command", "command": "bin/turn-ended.sh", "timeout": 10 }] }],
    "StopFailure": [{ "hooks": [{ "type": "command", "command": "bin/turn-ended.sh" }] }],
    "StopCancelled": [{ "hooks": [{ "type": "command", "command": "bin/turn-ended.sh" }] }],
    "Notification": [
      { "matcher": "idle_prompt", "hooks": [{ "type": "command", "command": "bin/turn-ended.sh" }] }
    ]
  }
}

מה ששני הסקריפטים חייבים לבצע נכון:

  • לעקוב אחר ה-promptId החדש ביותר ולהתעלם מדיווחים על תורות ישנים יותר. דיווח על תור שבוטל משוגר מחוץ ללולאת הפקודות, ולכן הוא יכול להגיע לאחר ה-UserPromptSubmit של התור הבא.
  • להתייצב ללא תנאי כאשר אין promptId. זהו דיווח של grok על ההפעלה ולא על תור: הפינג של idle_prompt ואירוע Stop של סיום ההפעלה. זה מה שמאפשר לרשת הביטחון לפעול עבור הרצה לאחור (rewind) או תור שהוחלף, שאינם מדווחים דבר.
  • להתייחס ל-promptId שמעולם לא ראית את תחילתו כמצב חוסר פעילות. תור שהופרע במצב bash מדווח ללא UserPromptSubmit מקדים.
  • לצאת מוקדם כאשר subagentType קיים. עצירה של תת-סוכן אינה עצירה של ההפעלה כולה.
  • לייצב את המארח לפני שאתה מתעד את התור כמטופל, כך שוו שחוסל באמצע הריצה ישאיר את התור בר-תיקון. קרא מחדש את הרשומה תחילה, כך שתנקה רק תור שתיעדת בעצמך.
  • להגביל את הפעולה לכתיבה מקומית בלבד. סגירת ההפעלה מעניקה לכל תור דיווחי סיום התור חצי שנייה, וכל וו SessionEnd מוגבל לאחר מכן בפסק זמן משלו (ברירת מחדל של 1.5 שניות; הגדר את GROK_SESSION_END_HOOKS_TIMEOUT_MS, במילישניות, כדי לשנות ברירת מחדל זו, עם תקרה של 60 שניות).

Stop הוא שער, ולכן רשומה זו רצה בנתיב הקריטי של התור: שמור עליה מהירה, הגדר לה timeout, וצא בקוד 0, מכיוון שיציאה בקוד 2 חוסמת את העצירה ומשאירה את הסוכן בעבודה. השמט את Stop אם אתה מריץ גם שער Stop חוסם, שכן הפעלת המשך תייצב את המארח בזמן שהסוכן עדיין פועל; רשום את SessionEnd במקום זאת, שהוא הדבר היחיד שמייצב הפעלה שנסגרת לפני הפינג.

שני הסקריפטים רצים גם בתוך הפעלה עצמאית של תת-סוכן. כל אירוע שכל אחד מהסקריפטים קורא נושא שם את subagentType ומשמיט אותו בהפעלה הראשית, כך ש-[ -n "$subagentType" ] && exit 0 מסנן צאצא משני החצאים. הדבר חשוב במיוחד עבור תת-סוכן ברקע, ששורד מעבר לתור ההורה ועלול היה להחזיק את המארח במצב עסוק לאחר שההורה כבר עבר למצב חוסר פעילות.

קלט של Stop נושא גם את backgroundTasks ו-sessionCrons, כך שוו יכול להבדיל בין "ההפעלה הסתיימה" לבין "ההפעלה מושהית וממתינה לעבודה ברקע שתעיר אותה בחזרה". שני המערכים ריקים כאשר אין פעולות בריצה או מתוזמנות. כל רשומה ב-backgroundTasks מתארת משימה אחת שרצה ברקע: id, type (shell, monitor, או subagent), status, וכן (בהתאם לסוג) command (משימות מעטפת בלבד), description (שורת הפקודה הנצפית של מוניטור, או תיאור המשימה של תת-סוכן), ו-agentType (תת-סוכנים). כל רשומה ב-sessionCrons מתארת התעוררות מתוזמנת אחת (scheduler_create או /loop): id, schedule, recurring, ו-prompt. הערך schedule הוא מרווח זמן קריא לבני אדם כגון every 5 minutes; תזמונים ב-grok הם מרווחי זמן, ולא ביטויי cron. שדות טקסט חופשי ברשומה מוגבלים לתקרה של 1000 תווים עם סימון ... [+N chars] בתוך המחרוזת.

בתוך תת-סוכן, השער מופעל כ-SubagentStop (ווי Stop ב-frontmatter של הסוכן ממופים מחדש אוטומטית). וו Stop מבקר רק את הסוכן הראשי.

SubagentStop מופעל פעם אחת עבור כל תת-סוכן, בסיום התור של תת-הסוכן עצמו, בדומה ל-Claude Code. הקלט שלו נושא שדה phase (כרגע תמיד "gate") השמור לתאימות קדימה.

הסבת ווי עצירה מ-Claude Code: אוצר המילים של הפלט (decision, reason, continue, stopReason, additionalContext) פועל ללא שינוי. בדוק רשימה זו עבור מה שאינו תואם ל-Claude:

  • קלט ב-camelCase: מעטפת ה-stdin של grok משתמשת במפתחות ב-camelCase לכל אורכה, בעוד ש-Claude משתמש ב-snake_case. סקריפט הקורא את .stop_hook_active או .background_tasks[].agent_type חייב לעבור ל-.stopHookActive ו-.backgroundTasks[].agentType (המפתח hook_event_name ב-snake_case נושא את ערך ה-PascalCase של Claude, למשל "Stop"; המפתח hookEventName ב-camelCase נושא את ערך ה-snake_case של grok, למשל "stop"). ווים שנרשמו דרך grok-agent-sdk ממירים הן את המפתחות ברמה העליונה והן את מפתחות הרשומות של backgroundTasks/sessionCrons ל-snake_case, כך ש-.backgroundTasks[].agentType שנשלח בחיבור נקרא כ-.background_tasks[].agent_type ב-SDK.
  • שדה toolResult: פלט הכלי ב-PostToolUse הוא toolResult (ב-SDK: tool_result); grok מפיק גם כינוי ב-snake_case בשם tool_response שמעתיק את toolResult, כך שוו הקורא את .tool_response של Claude עובד ללא שינוי.
  • updatedToolOutput נושא את מבנה הפלט של grok עצמו בכלים מובנים: החלפת PostToolUse עבור כלי מובנה מאומתת מול פלט הכלי כפי ש-grok מסדר אותו בסריאליזציה: האובייקט המתויג ב-toolResult של אותו אירוע, כך שהחלפה שנכתבה לפי שמות שדות של סביבת ריצה אחרת מפוענחת כמבנה שגוי וזוכה להתעלמות. בכלי MCP אין מבנה לאכוף, כך ש-updatedToolOutput עובר ישירות בדומה לכינוי שלו updatedMCPToolOutput. ראה פלט PostToolUse.
  • הפעלת סיום הפעלה: אירוע Stop נוסף לצפייה בלבד מופעל בסיום ההפעלה; סנן לפי reason == "end_turn" (ראה לעיל).
  • תזמוני מרווחי זמן: sessionCrons[].schedule הוא מרווח זמן קריא לבני אדם, לעולם לא ביטוי cron.
  • סוגי משימות: backgroundTasks[].type יכול להיות רק shell, monitor, או subagent; התוויות האחרות של Claude (workflow, teammate, ...) אינן מופקות.
  • מחלקות StopFailure: grok מפיק שש מחלקות (rate_limit, authentication_failed, invalid_request, server_error, max_output_tokens, unknown). שגיאות קיבולת (503/529) מסווגות כ-rate_limit. שדה matcher על מחלקת שגיאה ש-grok אינו מפיק לעולם לא יופעל.
  • פסק זמן ברירת מחדל: grok קובע כברירת מחדל לווי צפייה 5 שניות, שהוא זמן קצר יותר מרוב הסביבות. הגדר timeout במפורש בוו מיובא שמבצע עבודה אמיתית.
  • UserPromptSubmit חוסם, עם פער אחד: יציאה בקוד 2 ו-decision: "block" דוחים את הבקשה כמו ב-Claude, ובקשה שנחסמה לעולם אינה נכנסת להיסטוריית השיחה, אך stdout / additionalContext של וו מאשר מושלכים במקום להתווסף כהקשר.
  • StopCancelled הוא ייחודי ל-grok: הגדרה שמשתמשת בו אינה ניתנת להעברה לסביבת ריצה ללא וו הפרעה.
  • idle_prompt מופעל בכל סיום תור: grok מפעיל אותו גם לאחר תור שהופרע או נכשל, ולא רק לאחר תור שהושלם, מכיוון שהוא מדווח על מצב ולא על תוצאה. שדה ה-message שלו הוא טקסט תצוגה ועשוי להשתנות בין גרסאות, לכן בצע התאמה לפי notificationType במקום זאת.
  • זהות תת-סוכן היא subagentType, לא agent_type: grok ממקם אותה במטען של האירועים שיכולים לפעול בתוך תת-סוכן, בהתאמה לאירועי SubagentStart/SubagentStop שלו, ולא בשדות המשותפים.
  • ערכי permission_mode: grok מפיק default, auto, plan, או bypassPermissions. לערכים acceptEdits/dontAsk של Claude אין מקבילה ב-grok (מצב auto של grok הוא הקרוב ביותר), ולכן בדיקה כמו permission_mode === "acceptEdits" לעולם אינה מתאימה.
  • פסקי זמן של שער לקוח (SDK): שערי Stop/SubagentStop ב-SDK מוגדרים כברירת מחדל ל-600 שניות כמו ווי קבצים; שערי לקוח של PreToolUse מוגדרים כברירת מחדל ל-30 שניות (הנתיב החם האינטראקטיבי). ניתן לדרוס כל אחד מהם לפי קבוצת matcher באמצעות timeoutS, עד תקרה של 600.
  • /goal: לולאת היעד של grok היא תכונה נפרדת שרצה לפני שער העצירה; היא אינה וו Stop מסוג בקשה.

מדיניות שמירה על עבודה מלאה בסקריפט אחד:

#!/bin/bash
input=$(cat)
# Gate only genuine turn ends, not the session-end observe fire.
if [ "$(echo "$input" | jq -r '.reason')" != "end_turn" ]; then exit 0; fi
if ! bin/verify.sh >/dev/null 2>&1; then
  echo '{"decision": "block", "reason": "verify.sh failed; fix the failures before finishing"}'
fi

רשום בתור { "type": "command", "command": "bin/stop-gate.sh", "timeout": 300 } עם timeout המותאם לשלב האימות. הוו מופעל שוב לאחר כל המשך, והמגבלה המובנית מסיימת את התור לאחר 8; בדוק את stopHookActive כדי לוותר מוקדם יותר על משוב שהסוכן ככל הנראה אינו יכול לפעול לפיו.

#ווים סבילים

עבור אירועים כמו SessionStart או Notification, פלט ה-stdout זוכה להתעלמות. פשוט צא בקוד 0 בעת הצלחה. החריגים הם PreToolUse (ראה פלט (ווים חוסמים)), Stop/SubagentStop (ראה בקרת החלטת עצירה), ו-PostToolUse, שפלט ה-stdout שלו נקרא למרות שאינו חוסם דבר (ראה פלט PostToolUse).

#משתני סביבה

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

#משתנים המוזרקים על ידי המריץ (זמינים תמיד)

משתנים אלה מוגדרים על ידי מריץ הווים עבור כל וו:

משתנהתיאור
GROK_HOOK_EVENTשם האירוע שהפעיל את הוו (למשל pre_tool_use, session_start, post_tool_use, session_end, stop, notification).
GROK_HOOK_NAMEהשם המוגדר של וו ספציפי זה (כולל את קידומת התוסף עבור ווים שסופקו על ידי תוסף).
GROK_SESSION_IDהמזהה הייחודי של הפעלת Grok הנוכחית.
GROK_WORKSPACE_ROOTנתיב מוחלט לשורש סביבת העבודה הנוכחית.
CLAUDE_PROJECT_DIRנתיב מוחלט לשורש סביבת העבודה. כינוי תואם Claude Code עבור GROK_WORKSPACE_ROOT, המוגדר עבור כל וו.

משתנים אלה הם שמורים. כל ערך שתנסה להגדיר עבורם באמצעות השדה env בקובץ ה-JSON של הוו יוסר בזמן הטעינה (אזהרה תירשם ביומן), והמריץ תמיד מזריק את הערכים האמיתיים בזמן יצירת התהליך (spawn time).

#משתני ווי תוספים

כאשר וו מגיע מתוסף, Grok מזריק בנוסף את המשתנים הבאים:

משתנהתיאור
GROK_PLUGIN_ROOTנתיב מוחלט לספריית ההתקנה של התוסף.
GROK_PLUGIN_DATAנתיב מוחלט לספריית הנתונים הניתנת לכתיבה של התוסף (לאחסון מצב התוסף, מטמונים וכדומה).

ערכים אלה מסופקים על ידי מערכת התוספים. עבור ארבעת המפתחות הקשורים לתוספים (GROK_PLUGIN_ROOT, GROK_PLUGIN_DATA והכינויים שלהם ב-Claude), מתאם התוספים מבטיח שהערכים הרשמיים של התוסף יגברו תמיד על כל ערך שהוגדר על ידי המשתמש במפת ה-env של הוו.

#משתני סביבה מוגדרים על ידי המשתמש

ניתן לספק משתני סביבה נוספים עבור מטפל וו בודד באמצעות השדה env:

{
  "type": "command",
  "command": "bin/my-hook.sh",
  "env": {
    "MY_SECRET": "value",
    "LOG_LEVEL": "debug"
  }
}

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

#שימוש במשתנים בשדות command ו-url

השדות command ו-url תומכים שניהם בפריסה של ${VAR} ו-$VAR. ב-PowerShell ב-Windows, הפניות מוכרות של $VAR משוכתבות ל-$env:VAR כדי שיקראו את סביבת הצאצא. ראה את המדריך לווי משתמש מותאמים (custom-hooks reference) לפרטים על פריסה בזמן טעינה לעומת זמן ריצה, סדר החיפוש במפת env, ומשני פריסת פרמטרים (למשל ${VAR:-default}).


#ווי HTTP

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

{ "type": "http", "url": "https://hooks.example.com/grok-event", "timeout": 15 }

מעטפת האירוע המלאה נשלחת בבקשת POST כ-JSON.


#ניהול ווים בממשק הטקסטואלי (TUI)

#הלשונית Hooks

לחץ על Ctrl+L במסופים שאינם ממשפחת VS Code כדי לפתוח את חלון ההרחבות (הלשונית Plugins), או הרץ /hooks (בכל מסוף; נדרש במסופים ממשפחת VS Code שבהם Ctrl+L משמש להתערבות) כדי לפתוח אותו בלשונית Hooks. בלשונית Hooks:

מקשפעולה
rטעינה מחדש של כל הווים מהדיסק
aהוספת וו מותאם אישית לפי נתיב
xהסרת מקור הוו שנבחר (מבקש אישור; לחץ על y קטנה לאישור)
Spaceהפעלה או השבתה של הוו שנבחר
fהחלפת מסנן המצב במעגל (All / Enabled / Disabled)

הווים מקובצים לפי מקור: גלובלי (Global), פרויקט (Project), תוסף (Plugin), ומותאם אישית (Custom).

כל וו מציג:

  • אירוע (Event) שעליו הוא מופעל
  • פקודה (Command) או כתובת URL שרצה
  • משך פסק זמן (Timeout)
  • מצב (Status): מופעל או [disabled]

#פקודות לוכסן

/hooks-list           
# Show hooks loaded in this session
/hooks-trust          
# Trust this project for hook execution
/hooks-add <path>     
# Add a custom hook file or directory
/hooks-remove <path>  
# Remove a custom hook
/hooks-untrust        
# Revoke trust for this project

במציג של ה-TUI, פקודות ה-/hooks-* הנפרדות אינן מופיעות ברשימת פקודות הלוכסן. החלון של /hooks מכסה הצגה ברשימה, הוספה, הסרה והפעלה או השבתה של ווים; אמון בפרויקט מנוהל באמצעות /hooks-trust (או פעולת Trust בחלון), אשר כותבת למאגר המאוחד של אמון בתיקיות שתואר לעיל.

#הפעלה או השבתה ברמת הוו הבודד

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

#טעינה מחדש באמצע הפעלה

לחץ על r בלשונית Hooks כדי לטעון מחדש את כל הווים מהדיסק. Grok קורא מחדש כל מקור וו, כך שהפעולה קולטת שינויים שביצעת בקובצי הווים במהלך ההפעלה.


#ווים בשורת המצב ובגלילה (Scrollback)

ווים שקטים אלא אם הם מעכבים את התור או משנים את מהלכו:

  • בזמן שהתור חסום בהמתנה לאצוות ווים (שער PreToolUse לפני כלי, שער UserPromptSubmit, שער Stop), שורת המצב מציגה Running pre_tool_use hook… (או Running 3 stop hooks…) לאחר שהאצווה רצה במשך כ-300 מילישניות. הטיימר סופר מהרגע שבו האצווה החלה, כך שוו איטי מציג את זמן ההמתנה המלא שלו; וו מהיר אינו מוצג כלל.
  • וו שרץ ואישר אינו משאיר עקבות. פלט ה-stdout שלו אינו מוצג.
  • וו שדוחה קריאת כלי, חוסם בקשה, או עוצר או ממשיך את הסוכן מקבל שורת הערה אחת עם הסיבה. ווים מקובצי ~/.grok, פרויקט ותוספים מצוינים בשמם; ווים מהגדרה מנוהלת מופיעים בתור "a managed policy hook".
  • וו שנכשל (יציאה בקוד שאינו אפס, חריגת זמן, קריסה, פלט שאינו תקין) מקבל שורה אחת: <event> hook (<name>) failed, ignored: <reason>, כאשר הסיבה היא קוד היציאה יחד עם שורת ה-stderr הראשונה, או חריגת הזמן. המילה "ignored" היא מילולית: כשלים פועלים במצב fail-open, כך שקריאת הכלי או התור ממשיכים כאילו הוו אישר אותם.

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

שורות אלה מופיעות רק כאשר ממשק התוספים מופעל (ברירת המחדל).


#דוגמה: משמר מעטפת בטוח

חסימת פקודות מעטפת מסוכנות:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bin/safe-shell.sh", "timeout": 5 }
        ]
      }
    ]
  }
}

כאשר bin/safe-shell.sh:

#!/bin/sh
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.toolInput.command // empty')

# Block destructive patterns
if echo "$CMD" | grep -qE '(rm -rf /|mkfs|dd if=|:(){ :|& };:)'; then
  echo '{"decision": "deny", "reason": "Blocked potentially destructive command"}' 
  exit 2
fi

echo '{"decision": "allow"}'

#הערות אבטחה

  • ווים גלובליים (~/.grok/hooks/) רצים עם הרשאות המשתמש שלך; התייחס אליהם כמו אל סקריפטים של מעטפת.
  • ווי פרויקט דורשים אמון בתיקייה (/hooks-trust או --trust, אותו שער כמו שרתי MCP/LSP מקומיים של המאגר) כדי למנוע מתקפות שרשרת אספקה ממאגרים זדוניים.
  • ווי HTTP שולחים נתוני הפעלה; השתמש רק בנקודות קצה מהימנות.
  • וו מסוג PostToolUse קובע מה המודל קורא עבור אותה קריאת כלי: הוא יכול להוסיף הוראות או להחליף את הפלט לחלוטין, לכן תן בו אמון כפי שאתה נותן אמון בשער PreToolUse. הגלילה (scrollback) והתמליל (transcript) שומרים על הפלט האמיתי, כך שהחלפה גלויה בפניך תמיד.

#שיטות מומלצות

  1. שמור על ווים מהירים: ווים שרצים זמן רב חוסמים את ממשק המשתמש. השתמש בתהליכי רקע (&) או בפעולות אסינכרוניות במידת האפשר.
  2. השתמש ב-deny מפורש כדי לחסום: ווים כושלים במצב פתוח (fail-open) בכל שגיאה, כך שוו שקורס לא יחסום את הכלי. כדי לאכוף מדיניות, הוו שלך חייב לרוץ עד תומו ולפלוט {"decision":"deny","reason":"..."} ב-stdout. טפל תמיד בשגיאות בתוך הסקריפט שלך כדי שיוכל להחזיר החלטה מפורשת.
  3. השתמש בנתיבים מוחלטים או יחסיים לקובץ הוו: סקריפטים בספריית bin/ לצד קובץ ה-JSON הם ניידים (portable).
  4. בדוק באמצעות החלון (modal): לחץ על Ctrl+L (במסופים שאינם ממשפחת VS Code) או הרץ /hooks כדי לוודא שווים נטענים ומתאימים לפני שאתה מסתמך עליהם.
  5. נהל בקרת גרסאות לווי פרויקט: בצע commit לספריית .grok/hooks/ (אך לעולם אל תכלול סודות).

#פתרון בעיות

  • הוו אינו רץ? לחץ על Ctrl+L במסופים שאינם ממשפחת VS Code (או הרץ /hooks בכל מקום) כדי לבדוק אם הוא נטען והותאם.
  • המערכת מתעלמת מווי פרויקט? ייתכן שהתיקייה אינה מהימנה. הרץ /hooks-trust (או הפעל מחדש עם הדגל --trust).
  • הסקריפט לא נמצא? ודא שהנתיב יחסי לקובץ ה-.json ושהקובץ בעל הרשאות הפעלה (chmod +x).
  • רואה שגיאות? לכוד יומנים על ידי הפעלה עם RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok, ולאחר מכן בדוק את /tmp/grok.log.