תיעוד 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.
#אפשרויות
| מפתח | סוג | ברירת מחדל | תיאור |
|---|---|---|---|
type | string | disabled | builtin, command, או disabled. |
items | array | ["cwd", "model", "context"] | מקטעים מובנים, לפי הסדר. |
command | string | none | סקריפט עבור type = "command". |
padding | integer | 0 | ריווח אופקי, בתווים לכל צד, עם תקרה של 16. padding רחב מספיק כדי שלא יישארו עמודות שומר את השורה אבל לא מצייר בה כלום. |
refresh_interval | integer | unset | רק לשורות 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_id | UUID של הפרומפט שמעובד. קיים רק במהלך תור |
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, ולכן היא מציגה קוד יציאה.