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

תיעוד 49

שורת סטטוס

שורה אופציונלית בתחתית ה-pager: מעל סרגל הקיצורים במסך מלא, ומתחת לשורת המידע של הפרומפט במצב מינימלי. היא כבויה כברירת מחדל. היא מציגה הקשר חי של הסשן, כמו המודל, שימוש בחלון ההקשר, עלות, ספרייה ו-git worktree, או את הפלט של כל סקריפט שמגדירים. מפעילים אותה עם [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אחוז חלון ההקשר, בצבע ענבר בסף הדחיסה האוטומטית או ב-80% כשהסוכן לא מדווח סף
costעלות הסשן, מוסתרת מתחת ל-$0.005 כדי שלא תוצג $0.00 מטעה
turn-timerהזמן שחלף בתור הרץ, משנייה אחת ומעלה
session-nameשם הסשן, כשהוא מוגדר

#פקודה

מפנים את command לסקריפט. גרוק מעביר אליו 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"]מקטעים מובנים, לפי הסדר.
commandstringnoneסקריפט עבור type = "command".
paddinginteger0ריווח אופקי, בתווים לכל צד, עם תקרה של 16. padding רחב מספיק כדי שלא יישארו עמודות שומר את השורה אבל לא מצייר בה כלום.
refresh_intervalintegerunsetרק לשורות command, בשניות, מ-1 עד 86,400. מריץ מחדש את הסקריפט בתדירות הזו גם כששום דבר לא השתנה, כדי שסשן במנוחה עדיין יוכל להציג שינוי: דף אירוע, סטטוס CI. כשלא מוגדר, השורה נשארת מונעת מאירועים. הריצה שהוא מתזמן נושאת "trigger": "refresh_interval", וכשלים שלה שומרים את הפלט האחרון במקום לצייר שגיאה (ראו ריצות רענון). סקריפט שקורא לרשת עדיף שישתמש במרווח ארוך יותר ויקרא מטמון בריצות state.

#איך זה עובד

  • רענון. השורה מתעדכנת כשמצב הסשן משתנה (התחלת סשן, התחלת תור, סיום תור, החלפת מודל או effort, תזוזת HEAD, דחיסה, חיבור לקוח) וברציפות בזמן שתור רץ, לא לפי טיימר. סשן במנוחה לא מריץ מחדש את הסקריפט, ולכן שעון בתוכו לא מתקדם מעצמו, אלא אם מגדירים refresh_interval, שמוסיף טיימר מעל כל מה שתואר. העדכונים האלה מעוכבים ב-debounce קבוע של 300 ms, כדי שתור עמוס לא יריץ את הסקריפט בכל פריים. שינוי שחייב להופיע מיד (שינוי גודל, snapshot חדש, החלפת סוכנים) ממתין רק 100 ms. ריצה שכבר מתבצעת אף פעם לא מבוטלת: השינוי הבא ממתין שהיא תסתיים. גרוק קורא את [ui.status_line] בהפעלה, ולכן שינויים בו נכנסים לתוקף בהפעלה הבאה.
  • פלט. כל שורה שמדפיסים הופכת לשורה אחת בשורת הסטטוס, עד חמש, וכל אחת נחתכת ב-1024 תווים, כולל ספירת תווי ה-ANSI עצמם, ולכן שורה צבועה בכבדות משאירה פחות מקום לטקסט. מסוף קצר לוקח פחות, ומוריד את העודף מלמטה. צבעי ANSI מכובדים. כל escape אחר (תנועת סמן, מחיקת שורה, דריסה ב-carriage-return) מושמט. קישורי OSC 8 מכובדים ליעדי http, https ו-mailto, וכל יעד אחר מוצג כטקסט רגיל. stdout מעבר ל-64 KiB נחתך והסקריפט נעצר. סקריפט שמצליח ולא מדפיס כלום מסיר את השורה במקום ליפול חזרה למקטעים המובנים, ולכן סקריפט שמדפיס רק לפעמים מזיז את התמליל בשורה כשהוא מופיע ונעלם.
  • גודל. גרוק מגדיר את COLUMNS ו-LINES לשורה שהפלט ממלא, לא לחלון: ה-padding של החלונית וה-padding שלך כבר הופחתו. גם tput מדווח אותם, כי הוא קורא אותם כש-stdout אינו מסוף. LINES הוא מה שהשורה ממלאת כרגע ולא מה שהיא עשויה לגדול אליו, ולכן הוא קורא 1 עד שמדפיסים יותר. התקרה היא חמש, מה שהוא אומר. לפני שהשורה צוירה פעם אחת, ובפריים שאין בו מקום לה, הגודל הוא האחרון שבו השורה צוירה, או 80x1 אם היא מעולם לא צוירה.
  • מעטפת. command הוא שורת פקודת מעטפת, ולכן jq -r '…' וצינורות עובדים כפי שנכתבו. נתיב רץ ישירות כשהוא מציין קובץ הפעלה, ודרך sh -c אחרת, וזה מה שמריץ סקריפט ששורת ה-#! שלו חסרה או שגויה. צטטו נתיב שמכיל רווחים כמו בשורת הפקודה. ב-Windows, שורת פקודת מעטפת רצה תחת המעטפת שזוהתה, ו-pwsh או Windows PowerShell מתחילים עם -ExecutionPolicy Bypass (ראו PowerShell Execution Policy on Windows). כל ריצה היא תהליך חדש, ולכן עריכה בקובץ הסקריפט חלה בריצה הבאה.
  • עבודה ברקע לא שורדת. כל מה שסקריפט משאיר רץ נהרג כשהריצה מסתיימת, בכל מסלול: יציאה נקייה, timeout, או יותר מדי פלט. הריצה מסתיימת כשהסקריפט יוצא, ולכן כל מה ש-job ברקע מדפיס אחרי זה אובד.
  • סביבה. סקריפטים רצים בספריית העבודה של הסשן, אחר כך בשורש המאגר, ואחר כך בספרייה של ה-pager עצמו, הראשונה שהיא נתיב מקומי. טאב שהסשן שלו רץ במכונה אחרת מתחיל את הסקריפט בספרייה של ה-pager עצמו. לכל ריצה יש timeout של 10 שניות, ואחריו השורה מציגה [status line: timed out]. COLUMNS ו-LINES מתארים את השורה שהסקריפט ממלא, לא את החלון. קובצי rc של המעטפת לא רצים (BASH_ENV ו-ENV מנוקים), ו-GIT_OPTIONAL_LOCKS=0. pagers ועורכים מנוטרלים באותה דרך שבה שאר גרוק מנטרל אותם, ולכן קריאה ל-git או ל-gh בתוך הסקריפט לא תיחסם בהמתנה לאחד מהם.
  • קלט. מטען ה-JSON נכתב ל-stdin עם שורה חדשה בסוף, ולכן גם read -r line וגם input=$(cat) עובדים.
  • במסלול ה-daemon. סשן שה-daemon מריץ לא שולח transcript_path. תור שאף פרומפט מה-pager הזה לא התחיל, כמו תור התעוררות או תור של לקוח אחר, נושא את מזהה הפרומפט remote-turn-<turn id>. השורה מתעדכנת כשהסשן נפתח או לקוח מתחבר, כשתור מתחיל או מסתיים, אחרי החלפת מודל או effort, כשרשימת המודלים מתרעננת, וכשההקשר המסוכם של דחיסה מגיע. היא לא מתעדכנת בתזוזת HEAD ולא ברציפות בזמן שתור רץ.

#ריצות רענון

מגדירים 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/logs/unified.jsonl, כדי שנקודת קצה לא יציבה לא תצייר שגיאה על פני לילה שקט. שלושה כשלי רענון רצופים אומרים שהסקריפט עצמו שבור, והשגיאה מוצגת אחרי הכל. כשל רענון לפני שהסקריפט ענה משהו, סשן חדש, או מיד אחרי החלפת סוכנים, גם מצויר מיד, כי אין מה לשמור. ריצה שמופעלת ממצב הסשן עדיין מדווחת על הכשל שלה מיד, כמו תמיד.
  • אש שהוחמצו מתאחדות. בזמן שהשורה מוסתרת (תצוגת subagent במסך מלא, מסך הפתיחה) או שריצה כבר מחזיקה את המשבצת, האש ממתין והשורה חייבת ריצה אחת כשהיא יכולה לקבל אותה, אף פעם לא פרץ עבור האשים שהשהיה או תור ארוך דילגו. הטיימר שומר על הקצב שלו בלי קשר לזמן הריצה של הסקריפט: אש שמגיע למועד בזמן שריצה עדיין מתבצעת נישא לריצה הבאה ולא נערם מאחוריה.
  • הטיימר שייך למצב שמריץ סקריפט. refresh_interval תחת builtin לא מתזמן כלום ומדווח דרך grok inspect. תחת disabled הוא כבוי עם כל השאר.

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

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

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

שדהתיאור
cwd, session_idספריית עבודה ומזהה סשן ייחודי
session_nameשם הטאב של הסשן, שממלא הלקוח. קיים ב-stdin של command, נעדר מהתראת SessionStatus
prompt_idUUID של הפרומפט שמעובד. קיים רק במהלך תור
transcript_pathנתיב אל updates.jsonl של הסשן. הקובץ הוא זרם העדכונים של גרוק עצמו, ולכן סקריפט שמפרסר פורמט תמליל של כלי אחר לא יקרא אותו
model.id, model.display_nameמזהה המודל ושם התצוגה. מושמטים כשהסוכן לא יכול לקרוא את המודל של הסשן
workspace.current_dirהספרייה הנוכחית
workspace.repo_rootשורש המאגר, נעדר מחוץ לאחד. לא project_dir, שם שבמקום אחר משמש לספריית הפעלה
workspace.branchהענף שנבחר, בכל מאגר. נעדר ב-detached HEAD
workspace.git_worktreeשם ה-worktree, בתוך worktree מקושר
workspace.repo.{host,owner,name}מפורסר מה-remote origin, בתוך מאגר git. owner מושמט ל-remote בלי מקטע בעלים
schema_versionגרסת צורת המטען. הוספת שדה אף פעם לא מעלה אותה. הסרה או שינוי סוג של שדה כן. בדקו אותה עם >=, והסתעפו לפיה ולא לפי version
versionגרסת גרוק, לתצוגה
cost.total_duration_msאלפיות שנייה מאז שהתהליך הזה חיבר את הסשן. סשן שחודש נספר מהחידוש, כמו העלות שלו
cost.total_cost_usd, cost.total_api_duration_msעלות הסשן ואלפיות שנייה של המתנה ל-API. העלות נעדרת עד שמשהו בסשן נושא מחיר, וגם כשפנקס השימוש לא קריא, אז התייחסו לעלות נעדרת כלא ידועה ולא כאפס
context_window.context_window_sizeגודל הקשר מרבי, בטוקנים. מושמט עד שחלון המודל ידוע
context_window.context_tokensטוקנים שהשיחה תופסת כרגע, בספירת קלט בלבד, ולכן הוא יורד אחרי דחיסה. מושמט כשהסוכן לא יכול לקרוא את הספירה, ולכן 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רמת מאמץ החשיבה, כשהמודל תומך בה
turn.started_at_msאלפיות שנייה של Unix שבהן התור שבאוויר התחיל, נעדר בין תורות. חסרו אותו מהשעון שלכם לזמן שחלף
worktree.{name,path,branch,main_worktree_root}ה-worktree הפעיל, בתוך worktree מקושר. name מושמט ל-worktree בשורש מערכת קבצים, ו-main_worktree_root הוא המקום שממנו ה-worktree התפצל
triggerלמה הריצה הזו הופעלה: refresh_interval לריצה שהטיימר ביקש, state אחרת. קיים ב-stdin של שורת פקודה, נעדר מהתראת SessionStatus, שמתארת את הסשן ולא ריצה

שדות שגרוק לא יכול למקור מושמטים במקום להישלח כמצייני מקום, כדי שהשורה אף פעם לא תציג ערך מומצא. תמיד הגנו עליהם: 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 ל-escapes אמינים.

#פתרון בעיות

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