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

תיעוד 49

שורת מצב

שורה אופציונלית בתחתית ה-pager, מעל סרגל קיצורי המקשים במסך מלא, מתחת לשורת המידע של ה-prompt במצב מינימלי, ומנוטרלת כברירת מחדל. היא מציגה הקשר הפעלה חי, כגון הדגם, ניצול חלון ההקשר, עלות, ספרייה ו-worktree של git, או את הפלט של כל סקריפט שתגדיר. מצטרפים באמצעות [ui.status_line] ב-~/.grok/config.toml.

#הגדרה

#מובנה

[ui.status_line]
type = "builtin"
items = ["cwd", "model", "context"]   
# default when omitted

זה מוצג, לדוגמה, כ-grok-shell-status-line │ Grok 4.5 │ 12% ctx. פריטים מופיעים בסדר שבו ציינת אותם, ופריטים ארוכים מקוצרים באמצעות : הספרייה ושם ההפעלה ב-40 עמודות, הדגם ב-30.

פריטמציג
cwdספרייה נוכחית (basename)
modelשם תצוגה של הדגם
contextאחוז מחלון ההקשר, בצבע ענבר בסף הדחיסה האוטומטית (auto-compaction) או ב-80% כשהסוכן אינו מדווח על סף כזה
costעלות ההפעלה, מוסתרת מתחת ל-$0.005 כדי שלעולם לא תוצג עלות מטעה של $0.00
turn-timerהזמן שחלף בתור שרץ, החל משנייה אחת ואילך
session-nameשם ההפעלה, כאשר הוא מוגדר

#פקודה

כוון את command לסקריפט. Grok מזרים JSON אליו ב-stdin ומציג את ה-stdout שלו. קידומת ~/ נפתחת לספריית הבית שלך.

[ui.status_line]
type = "command"
command = "~/.grok/statusline.sh"

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

#מנוטרל

הערך type = "disabled", שהוא ברירת המחדל, אינו מציג דבר; הערכים off, none ו-hidden מתקבלים כאיותים נרדפים של disabled.

#אפשרויות

מפתחסוגברירת מחדלתיאור
typestringdisabledbuiltin, command, או disabled.
itemsarray["cwd", "model", "context"]מקטעים מובנים, לפי הסדר.
commandstringללאסקריפט עבור type = "command".
paddinginteger0ריווח אופקי, בתווים לכל צד, מוגבל ל-16 לכל היותר. ריווח רחב מספיק שלא משאיר עמודות שומר את מקום השורה אך אינו מציג בה דבר.
refresh_intervalintegerלא מוגדרשורות command בלבד, בשניות, מ-1 עד 86,400. מריץ מחדש את הסקריפט בתדירות זו גם כאשר שום דבר לא השתנה, כך שהפעלה ללא פעילות יכולה עדיין להציג שינוי: קריאת תקלה, סטטוס CI. כאשר אינו מוגדר, השורה נשארת מונעת מאירועים בלבד. הריצה המתוזמנת נושאת את הערך "trigger": "refresh_interval", וכשלים שלה שומרים על הפלט האחרון במקום לצייר שגיאה (ראה הפעלות רענון). סקריפט שפונה לרשת צריך להעדיף מרווח זמן ארוך יותר ולקרוא ממטמון בריצות של state.

#כיצד זה עובד

  • רענון. השורה מתעדכנת כאשר מצב ההפעלה משתנה (תחילת הפעלה, סיום תור, החלפת דגם או רמת מאמץ, הזזת HEAD, דחיסה, התחברות לקוח) ובאופן רציף בזמן שתור רץ, ולא לפי טיימר. הפעלה ללא פעילות אינה מריצה מחדש את הסקריפט שלך, ולכן שעון בתוכו לא יתקתק מעצמו, אלא אם תגדיר את refresh_interval, שמוסיף טיימר מעל לכל האמור לעיל. עדכונים אלה מושהים (debounce) בפרק זמן קבוע של 300 מילישניות, כך שתור עמוס אינו יכול להריץ את הסקריפט שלך בכל פריים. שינוי שחייב להופיע מיד (שינוי גודל, תצלום מצב חדש, החלפת סוכנים) ממתין 100 מילישניות בלבד. ריצה שכבר מתבצעת לעולם אינה מבוטלת: השינוי הבא ממתין לסיומה. Grok קורא את [ui.status_line] בעת ההפעלה, ולכן שינויים בו נכנסים לתוקף בהפעלה הבאה.
  • פלט. כל שורה שאתה מדפיס הופכת לשורה אחת של שורת המצב, עד חמש שורות, וכל שורה נחתכת ב-1024 תווים, כולל רצפי המילוט של ANSI עצמם, כך שלשורה עתירת צבעים יש פחות מקום לטקסט. מסוף נמוך מכיל פחות שורות, ומשמיט את העודף מלמטה. צבעי ANSI נתמכים; כל רצף מילוט אחר (תנועת סמן, מחיקת שורה, דריסה באמצעות מעבר לתחילת שורה) מושמט. קישורי OSC 8 נתמכים עבור יעדי http, https ו-mailto, וכל יעד אחר מוצג כטקסט רגיל. פלט stdout מעבר ל-64 KiB נקטם והסקריפט נעצר. סקריפט שמצליח ואינו מדפיס דבר מעלים את השורה במקום לחזור למקטעים המובנים, ולכן סקריפט שמדפיס רק לעיתים מזיז את התמליל בשורה אחת כשהוא מופיע ונעלם.
  • הגדרת גודל. Grok מגדיר את COLUMNS ואת LINES לגודל השורה שהפלט שלך ממלא, ולא לגודל החלון: הריווח של החלונית וה-padding שלך כבר מנוכים. הפקודה tput מדווחת עליהם גם כן, כיוון שהיא קוראת אותם כאשר stdout אינו מסוף. המשתנה LINES הוא הגודל שהשורה ממלאת כרגע ולא הגודל שאליו היא עשויה לגדול, ולכן הערך שלו הוא 1 עד שתדפיס יותר; התקרה היא חמש שורות ללא קשר לערך המצוין. לפני שהשורה צוירה פעם אחת, ובפריים שאין בו מקום עבורה, הגודל הוא הגודל האחרון שבו השורה צוירה, או 80x1 אם היא מעולם לא צוירה.
  • מעטפת (Shell). הערך command הוא שורת פקודה של מעטפת, כך ש-jq -r '…' וצינורות (pipes) עובדים כפי שנכתבו. נתיב מורץ ישירות כאשר הוא מציין קובץ בר-ביצוע, ובאמצעות sh -c בכל מקרה אחר, שזה מה שמריץ סקריפט ששורת ה-#! שלו חסרה או שגויה. הקף במירכאות נתיב המכיל רווחים כפי שהיית עושה בשורת פקודה. כל ריצה היא תהליך חדש, כך שעריכה של קובץ הסקריפט חלה בריצה הבאה.
  • עבודה ברקע אינה שורדת. כל מה שסקריפט משאיר פועל מחוסל כאשר הריצה מסתיימת, בכל תרחיש: יציאה תקינה, חריגה מזמן ההמתנה (timeout), או פלט רב מדי. הריצה מסתיימת כשהסקריפט שלך יוצא, ולכן כל מה שתהליך רקע מדפיס לאחר מכן אובד.
  • סביבה. סקריפטים רצים בספריית העבודה של ההפעלה, לאחר מכן בשורש המאגר, ולאחר מכן בספרייה של ה-pager עצמו, לפי מה שהוא נתיב מקומי ראשון, עם מגבלת זמן של 10 שניות, שלאחריה השורה מציגה [status line: timed out]. המשתנים COLUMNS ו-LINES מתארים את השורה שהסקריפט ממלא, לא את החלון. קובצי rc של מעטפת אינם רצים (BASH_ENV ו-ENV מנוקים), ומוגדר GIT_OPTIONAL_LOCKS=0. כלי pager ועורכי טקסט מנוטרלים באותו אופן שבו שאר חלקי Grok מנטרלים אותם, כך שקריאה ל-git או ל-gh בתוך הסקריפט שלך לא תיחסם בהמתנה לאחד מהם.
  • קלט. מטען ה-JSON נכתב ל-stdin עם שורה חדשה בסופו, כך שגם read -r line וגם input=$(cat) עובדים.

#הפעלות רענון

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

[ui.status_line]
type = "command"
command = "~/.grok/statusline.sh"
refresh_interval = 300   
# seconds
  • המטען מציין מדוע הסקריפט רץ. ריצה שעונה לטיימר נושאת את הערך "trigger": "refresh_interval", כאשר שינוי מצב שמגיע בזמן שמועדת הפעלת טיימר מצטרף לאותה ריצה, וריצה ללא הפעלת טיימר נושאת את הערך "trigger": "state". פנה לרשת ב-refresh_interval וקרא ממטמון ב-state, אחרת תור עמוס, שמריץ את הסקריפט מחדש ברציפות, יהפוך לסערת בקשות כנגד השירות שהסקריפט קורא לו.
  • המטען הוא האחרון ש-Grok שלח. ריצת טיימר מריצה מחדש את הסקריפט שלך עם המטען משינוי המצב האחרון, כך שמספרי ההפעלה שלו: עלות, הקשר, אסימונים, נכונים לאותו שינוי, ולא למועד הפעלת הטיימר. רק מה שהסקריפט שלך מביא בעצמו הוא עדכני.
  • כשלי רענון שומרים על הפלט האחרון. ברגע שהסקריפט שלך ענה, הדפיס שורה, או בכוונה לא הדפיס דבר, ריצת טיימר שנכשלת או חורגת מזמן ההמתנה משאירה את השורה בדיוק כפי שהייתה, בין אם זה הפלט האחרון ובין אם זה כשל שריצת מצב כבר ציירה, וכותבת את הכשל אל ~/.grok/logs/unified.jsonl, כך שנקודת קצה לא יציבה לא תציג שגיאה לאורך לילה שקט. שלושה כשלי רענון רצופים מעידים שהסקריפט עצמו שבור, והשגיאה מוצגת בכל זאת; כשל רענון לפני שהסקריפט ענה על משהו, בהפעלה חדשה, או מיד לאחר החלפת סוכנים, מוצג גם כן מיד, כיוון שאין מה לשמור. ריצה המופעלת על ידי מצב הפעלה עדיין מדווחת על הכשל שלה מיד, כתמיד.
  • הפעלות שהוחמצו מתמזגות. בזמן שהשורה מוסתרת (תצוגת סוכן משנה במסך מלא, מסך הפתיחה) או כאשר ריצה כבר תופסת את המשבצת, ההפעלה ממתינה ולשורה מגיעה ריצה אחת כאשר הדבר מתאפשר, לעולם לא פרץ של הפעלות עבור אלו שהושעו או דולגו במהלך תור ארוך. הטיימר שומר על הקצב שלו ללא קשר לזמן הריצה של הסקריפט שלך: הפעלה שמגיע זמנה בזמן שריצה עדיין מתבצעת מועברת לריצה הבאה במקום להצטבר מאחוריה.
  • הטיימר שייך למצב שמריץ סקריפט. הערך refresh_interval תחת builtin אינו מתזמן דבר ומדווח באמצעות grok inspect; תחת disabled הוא כבוי יחד עם כל השאר.

#נתונים זמינים

בעת הסבת סקריפט, קרא שדות אלה בעיון. workspace.repo_root הוא שורש המאגר, ואין project_dir, שם המשמש במקומות אחרים עבור ספריית הפעלה. המשתנה context_window.session_usage וספירות האסימונים של session_* הם מצטברים עבור כל ההפעלה, ולא עבור קריאה בודדת, בעוד שהחלון החי הוא context_window.context_tokens. אין רשימה של ספריות הפעלה נוספות, מכיוון של-Grok אין כאלה. transcript_path מציין את זרם העדכונים של Grok עצמו ולא תמליל בפורמט של כלי אחר, ו-prompt_id קיים רק בזמן שתור רץ. בכל אחד מהמקרים, סקריפט מוסב קורא ערך ריק ולא תשובה שגויה, לכן הגן על השדות שבהם אתה משתמש.

שום דבר מחוץ לטבלה להלן אינו נשלח. סקריפט מוסב שקורא ספירת שורות שהסוכן שינה, סיכום מגבלת קצב (rate-limit), מצב עורך, דגל חשיבה (thinking) או מצב מהיר (fast-mode), סגנון פלט, בקשת משיכה (pull request), ספריות הפעלה נוספות, או את הספרייה שממנה נוצר ה-worktree, יגלה שהם חסרים: כל אחד מהם הוא תכונה שאין ל-Grok או מספר שאינו יכול לספק באופן מהימן.

שדהתיאור
cwd, session_idספריית עבודה ומזהה הפעלה ייחודי
session_nameשם הלשונית של ההפעלה, ממולא על ידי הלקוח. קיים ב-stdin של command, חסר בהודעת SessionStatus
prompt_idמזהה UUID של ה-prompt המעובד. קיים רק במהלך תור
transcript_pathנתיב לקובץ updates.jsonl של ההפעלה. הקובץ הוא זרם העדכונים של Grok עצמו, כך שסקריפט המפענח פורמט תמליל של כלי אחר לא יקרא אותו
model.id, model.display_nameמזהה דגם ושם תצוגה. מושמט כאשר הסוכן אינו יכול לקרוא את הדגם של ההפעלה
workspace.current_dirספרייה נוכחית
workspace.repo_rootשורש המאגר, חסר מחוץ למאגר. אינו project_dir, שם המשמש במקומות אחרים עבור ספריית הפעלה
workspace.branchהענף שנבדק (checked-out), בכל מאגר. חסר במצב detached HEAD
workspace.git_worktreeשם ה-worktree, בתוך worktree מקושר
workspace.repo.{host,owner,name}מפוענח מתוך ה-remote בשם origin, בתוך מאגר git. owner מושמט עבור remote ללא מקטע בעלים
schema_versionמהדורת מבנה המטען. הוספת שדה לעולם אינה מקדמת אותה; הסרה או שינוי טיפוס מקדמים אותה. בדוק אותה באמצעות >=, ובצע הסתעפות קוד לפיה ולא לפי version
versionגרסת שחרור של Grok, לתצוגה
cost.total_duration_msמילישניות מאז שתהליך זה התחבר להפעלה. הפעלה שחודשה נספרת מהחידוש, כפי שנספרת העלות שלה
cost.total_cost_usd, cost.total_api_duration_msעלות ההפעלה ומילישניות של המתנה ל-API. העלות חסרה עד שלמשהו בהפעלה יש מחיר, וגם כאשר ספר החשבונות של השימוש אינו קריא, לכן התייחס לעלות חסרה כאל ערך לא ידוע ולא כאל אפס
context_window.context_window_sizeגודל חלון הקשר מרבי, באסימונים. מושמט עד שחלון הדגם ידוע
context_window.context_tokensאסימונים שהשיחה תופסת כרגע, בספירת קלט בלבד, כך שהערך יורד לאחר דחיסה (compaction). מושמט כאשר הסוכן אינו יכול לקרוא את הספירה, כך ש-0 תמיד מציין חלון הקשר ריק
context_window.session_input_tokens, .session_output_tokensמחויבים לאורך כל ההפעלה, כך שהם רק גדלים. נקראים על שם ההפעלה מכיוון שזה מה שהם סופרים: total_* משמש במקומות אחרים עבור מה שנמצא בחלון כרגע, שכאן הוא context_tokens. חלוקה שלהם ב-context_window_size עוברת את 100% וממשיכה לעלות. מושמטים כאשר ספר החשבונות של השימוש אינו קריא
context_window.used_percentage, .remaining_percentageעד כמה החלון מלא כרגע, מספרים שלמים מ-0 עד 100. מושמטים יחד עם context_window_size או context_tokens, שכן אחוז מחלון לא ידוע אינו מספר
context_window.session_usage.{input_tokens,output_tokens,cache_creation_input_tokens,cache_read_input_tokens}input_tokens, cache_creation_input_tokens ו-cache_read_input_tokens, שסכומם שווה ל-session_input_tokens, בתוספת output_tokens. מצטבר עבור כל ההפעלה, לא עבור תור בודד. חסר לפני הקריאה הראשונה
context_window.auto_compact_threshold_percentהסף שבו ההפעלה מבצעת דחיסה אוטומטית. מושמט כאשר הסוכן לא דיווח על סף
effort.levelרמת מאמץ הסקת מסקנות (reasoning effort), כאשר הדגם תומך בכך
turn.started_at_msמילישניות יוניקס שבהן התחיל התור שרץ, חסר בין תורים. החסר זאת מהשעון שלך כדי לחשב את הזמן שחלף
worktree.{name,path,branch,main_worktree_root}ה-worktree הפעיל, בתוך worktree מקושר. name מושמט עבור worktree בשורש מערכת הקבצים, ו-main_worktree_root הוא המקום שממנו ה-worktree התפצל
triggerהסיבה להפעלת ריצה זו: refresh_interval עבור ריצה שהטיימר ביקש, state בכל מקרה אחר. קיים ב-stdin של שורת פקודה, חסר בהודעת SessionStatus, המתארת את ההפעלה ולא ריצה מסוימת

שדות ש-Grok אינו יכול לספק מושמטים במקום להישלח כשומרי מקום, כך שהשורה לעולם אינה מציגה ערך מומצא. הגן עליהם תמיד: jq -r מדפיס את הטקסט המילולי null עבור מפתח חסר, לכן כתוב // 0 או // "?" ב-jq, ו-?. ב-JavaScript.

#דוגמה

שמור סקריפט (לדוגמה ~/.grok/statusline.sh), הפוך אותו לקובץ בר-ביצוע באמצעות chmod +x, והגדר אותו כ-command. דוגמה זו משתמשת ב-jq. Python ו-Node.js מפענחות JSON באופן טבעי. המטען אינו נושא ספירת קבצים שהשתנו, לכן הסקריפט קורא ל-git עבור נתון זה.

#!/bin/bash
input=$(cat)
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
BRANCH=$(echo "$input" | jq -r '.workspace.branch // "detached"')
DIRTY=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
printf '%b\n' "${DIR##*/} │ $MODEL │ ${PCT}% ctx │ \033[32m$BRANCH\033[0m ~$DIRTY"

#טיפים

  • בדוק באמצעות קלט דמה: echo '{"session_id":"t","workspace":{"current_dir":"/tmp/demo"},"model":{"display_name":"Grok 4.5"},"context_window":{"used_percentage":25}}' | ./statusline.sh
  • שמור במטמון פקודות איטיות כגון git status לקובץ זמני שמפתח החיפוש שלו מבוסס על session_id, ורענן אותו כל כמה שניות. המזהה session_id יציב לאורך כל ההפעלה וייחודי בין הפעלות שונות.
  • השתמש ב-printf '%b' במקום ב-echo -e עבור רצפי מילוט אמינים.

#פתרון בעיות

  • שום דבר לא מוצג. Grok קורא את [ui.status_line] בעת ההפעלה, לכן הפעל אותו מחדש לאחר עריכת config.toml. הפעלה מחדש מספיקה: כאשר הלקוח החדש מתחבר, הסוכן מפעיל את השורה עבור הפעלה שעדיין רצה. השורה מוצגת רק כאשר תצוגת הסוכן פעילה, כלומר לא במסך הפתיחה ולא כאשר תצוגת סוכן משנה פתוחה במסך מלא. ודא שהערך type אינו disabled, ושסקריפט פקודה הוא בר-ביצוע וכותב ל-stdout.
  • הודעה בשורה. שורה המתחילה ב-[ui.status_line] מציינת ש-Grok לא הצליח להשתמש בסעיף זה כפי שנכתב: היא מציינת את המפתח שהוא לא הצליח לקרוא, או מציינת מה המצב שבחרת עדיין דורש. הפקודה grok inspect מציגה את אותן הבעיות, כולל מפתחות שגרסה זו אינה מכירה, וזה המקום לחפש בו כאשר השורה כבויה. כל מה שהוא הצליח לקרוא עדיין חל, ו-Grok משאיר את הסעיף כפי שכתבת אותו במקום לשכתב סעיף שאינו יכול לקרוא. הגדרת type = "disabled" מסירה את השורה ואת ההודעה.
  • שורה ריקה שלעולם אינה מתמלאת. הסוכן אינו שולח עדכוני מצב, מה שבדרך כלל מעיד על תהליך grok או תהליך leader ישן יותר מלקוח זה. הפעל מחדש את ה-leader או עדכן את Grok.
  • רק התצורה שלך יכולה להגדיר זאת. שורת command מריצה תוכנית, ולכן היא נקראת מ-~/.grok/config.toml שלך ומתצורה שמנהל המערכת שלך מנהל. מאגר אינו יכול להגדיר זאת: קובץ .grok/config.toml מקומי של מאגר נקרא עבור שרתי MCP בלבד, ו-[ui.status_line] אינו בין המפתחות ששכבה ברמת פרויקט יכולה לספק, כך ששכפול מאגר אינו יכול לגרום ל-Grok להריץ את הסקריפט שלו.
  • תצורה שנדחפה לא השפיעה. הסעיף [ui.status_line] מוסר מטלאים של קמפיינים ודריסת גרסאות (version-override), מכיוון ששורת מצב יכולה לציין פקודה שהמכונה שלך תריץ. הגדר אותו בקובץ config.toml שלך.
  • שגיאות. כל מה שהסקריפט שלך מדפיס מוצג, גם כאשר הוא יוצא עם קוד שאינו אפס, כך ש-printf …; [[ -n $dirty ]] מתנהג כפי שאתה מצפה. סקריפט שאינו מדפיס דבר ונכשל מציג [status line: exit N], וזה נשאר עד שהריצה הבאה מצליחה, במקרה של ריצה שהופעלה על ידי מצב הפעלה, המדווחת על הכשל שלה מיד; כשל של ריצת טיימר שומר על הפלט האחרון במקום זאת (ראה הפעלות רענון). הפלט הסטנדרטי לשגיאות (stderr) של הסקריפט שלך לעולם אינו מוצג, כך שפקודת echo לצורכי ניפוי שגיאות לא תשבש את השורה; הרץ את Grok עם --debug כדי לקרוא אותו. סקריפט ש-Grok לא הצליח להפעיל כלל מציג [status line: could not start the script: …], שזה מה שמייצר קובץ ללא הרשאת ביצוע, וסקריפט שהמערכת מחסלת מציג [status line: killed by signal]. שורת #! המציינת מפרש חסר מנוסה מחדש תחת sh במקום זאת, ולכן היא מציגה קוד יציאה.