הגדרת statusLine ב-Claude Code על שרת VPS
למדו איך להגדיר שורת סטטוס מותאמת אישית ב-Claude Code באמצעות settings.json. הצגת hostname, נתיב ו-git branch תמנע הרצת פקודות בשרת הלא נכון ותשפר את הבטיחות בעבודה.
מה מציגה שורת הסטטוס של Claude Code
שורת הסטטוס של Claude Code היא שורה המופיעה מתחת להנחיה (prompt) ומציגה את הפלט של סקריפט שכתבת. עליך להוסיף בלוק statusLine לתוך settings.json ולהפנות אותו לפקודה מסוימת. Claude Code מריץ את הפקודה הזו, שולח אליה את מצב הסשן כ־JSON דרך הקלט הסטנדרטי (stdin), ומדפיס את כל מה שהפקודה כותבת לפלט הסטנדרטי (stdout).
זהו כל ההסכם. הסקריפט שלך קורא JSON מ־stdin ומדפיס טקסט ל־stdout. הוא רץ על המכונה שלך, ושום דבר ממה שהוא מדפיס אינו נשלח למודל, לכן הוא אינו צורך tokens.
במחשב נייד עם פרויקט אחד, מדובר בקישוט בלבד. בשלושה שרתים, מדובר באמצעי בטיחות. כל סשן של Claude Code נראה זהה בכל טרמינל, לכן ארבעה חלונות SSH ללא תיוג הם הדרך שבה הגירה עלולה להסתיים בשרת הלא נכון. שורת סטטוס שמתחילה בשם המארח (hostname) מונעת את סוג הטעויות הזה.
היכן מוגדר ה-setting בשם statusLine בתוך settings.json
יש להוסיף אותו להגדרות המשתמש ב-~/.claude/settings.json, הגדרה שתחול על כל פרויקט במכונה זו. ניתן להשתמש גם בהגדרות פרויקט ב-.claude/settings.json בתוך מאגר (repository), והן יקבלו עדיפות עבור אותה ספרייה.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}הערך type הוא תמיד "command". הערך command מורץ דרך shell, לכן הוא יכול להיות נתיב לסקריפט או פקודה פשוטה. ודאו שהחיבור תקין לפני כתיבת סקריפט כלשהו:
{
"statusLine": {
"type": "command",
"command": "hostname -s"
}
}הפעילו את Claude Code ושלחו הודעה אחת. השורה מתחת ל-prompt תציג כעת את שם המארח (hostname) הקצר של השרת. אם היא נשארת ריקה, הבעיה היא בהגדרה או בתיבת הדו-שיח של האמון (trust dialog), ולא בסקריפט שלכם. קראו את "מדוע ה-statusline נשאר ריק" בהמשך.
נכון לאוגוסט 2026 קיימים שלושה מפתחות אופציונליים. padding מוסיף מרווח אופקי בתווים וערך ברירת המחדל שלו הוא 0. refreshInterval מריץ מחדש את הפקודה בכל N שניות בנוסף לטריגרים הרגילים, עם מינימום של 1; השתמשו בזה רק כאשר השורה מציגה שעון או נתון שמשתנה בזמן שהסשן אינו פעיל. hideVimModeIndicator מדכא את הטקסט המובנה -- INSERT -- כאשר הסקריפט שלכם כבר מרנדר את מצב ה-vim.
איזה נתונים מקבל הסקריפט של ה-statusline?
אל תסתמכו על רשימת שדות שקראתם במקום כלשהו, כולל דף זה. תפסו את האובייקט האמיתי שהגרסה שלכם שולחת. כתבו סקריפט זמני ששומר את ה-stdin לקובץ:
cat > ~/.claude/statusline-capture.sh <<'EOF'
#!/bin/bash
cat > /tmp/statusline-input.json
echo "captured"
EOF
chmod +x ~/.claude/statusline-capture.shכוונו את statusLine.command לקובץ הזה, התחילו session, ושלחו הודעה אחת. ה-bar קורא את captured. כעת בדקו מה הגיע:
jq . /tmp/statusline-input.jsonכעת יש לכם את המבנה המדויק עבור ה-build שלכם, ותוכלו לחזור על הפעולה בכל פעם שעדכון משנה משהו.
החלקים היציבים, כפי שתועדו באוגוסט 2026, הם אובייקטים מקוננים ולא מפתחות שטוחים. model מכיל את id ואת display_name. workspace מכיל את current_dir ואת project_dir: current_dir הוא המיקום הנוכחי של ה-session, project_dir הוא המיקום שבו הוא הופעל, והשניים נבדלים ברגע שספריית העבודה משתנה במהלך ה-session. ה-cwd ברמה העליונה נושא את אותו ערך כמו workspace.current_dir. context_window מכיל ספירות token בתוספת used_percentage שחושב מראש. cost מכיל את total_cost_usd ומוני משך זמן. session_id יציב לאורך כל חיי ה-session וייחודי בין sessions שונים, מה שחשוב עבור caching מאוחר יותר.
שלושה כללים שומרים על סקריפט פעיל לאורך שינויי סכימה.
חלק מהמפתחות חסרים, לא null. vim, agent, pr, worktree ו-effort מופיעים רק כאשר התכונה התואמת פעילה. קריאת .vim.mode עם jq -r בזמן ש-vim mode כבוי מדפיסה את המחרוזת המילולית null, וה-bar שלכם מציג null לקורא. הוסיפו // empty לכל selector, כך שמפתח חסר לא ידפיס דבר.
חלק מהערכים הם null בשלבים מוקדמים. context_window.used_percentage ו-context_window.current_usage הם null לפני תגובת ה-API הראשונה, ו-current_usage חוזר להיות null אחרי /compact עד שהקריאה הבאה מאכלסת אותו מחדש. לכן, אחוז context ב-bar זקוק ל-// 0, אחרת הוא יציג null בשניות הראשונות של כל session. לפני שאתם מציבים את המספר הזה על ה-bar, כדאי לדעת כיצד חלון ה-context מתמלא בפועל.
ענף ה-git אינו נמצא ב-JSON. אף שדה לא מדווח עליו. כל ענף שמופיע ב-bar שלכם מגיע מהסקריפט שלכם שמריץ את git בעצמו.
סקריפט לשורת סטטוס שמתדרדר בחן במקום לקרוס
זוהי גרסה להעתקה והדבקה. הסקריפט מדפיס את שם המארח (hostname), ספריית העבודה, ענף ה-git ושם המודל. לכל שדה יש ערך חלופי (fallback), כך שאפילו אובייקט JSON ריק יפיק שורה שמישה.
#!/bin/bash
# ~/.claude/statusline.sh
input=$(cat)
# Read one field. Prints nothing when the key is missing or null.
field() { printf '%s' "$input" | jq -r "$1 // empty" 2>/dev/null; }
HOST=$(hostname -s 2>/dev/null)
[ -z "$HOST" ] && HOST="host"
DIR=$(field '.workspace.current_dir')
[ -z "$DIR" ] && DIR=$(field '.cwd')
[ -z "$DIR" ] && DIR="$PWD"
MODEL=$(field '.model.display_name')
[ -z "$MODEL" ] && MODEL="claude"
SHORT="$DIR"
if [ -n "$HOME" ]; then
case "$DIR" in
"$HOME") SHORT="~" ;;
"$HOME"/*) SHORT="~/${DIR#"$HOME"/}" ;;
esac
fi
BRANCH=""
if git -C "$DIR" rev-parse --git-dir >/dev/null 2>&1; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
[ -z "$BRANCH" ] && BRANCH="detached"
fi
CYAN=$'\033[36m'
YELLOW=$'\033[33m'
DIM=$'\033[2m'
RESET=$'\033[0m'
LINE="${CYAN}${HOST}${RESET} ${SHORT}"
[ -n "$BRANCH" ] && LINE="${LINE} ${YELLOW}${BRANCH}${RESET}"
LINE="${LINE} ${DIM}${MODEL}${RESET}"
printf '%s\n' "$LINE"כל קריאה עוברת דרך field, שמוסיף // empty, כך שמפתח ששונה שמו או הוסר מפיק מחרוזת ריקה והשורה הבאה מספקת ערך ברירת מחדל. הספרייה נסוגה מ-workspace.current_dir ל-cwd ולבסוף ל-$PWD. הענף נלקח מ-git -C "$DIR" במקום מ-git גולמי, כך שהענף תמיד תואם לספרייה שמוצגת בשורת המצב.
שמרו את הקובץ והפכו אותו לניתן להרצה:
chmod +x ~/.claude/statusline.shביט ההרצה (execute bit) אינו אופציונלי. Claude Code מריץ את הפקודה דרך shell, לכן סקריפט ללא +x ייכשל עם Permission denied, לא יפיק פלט (stdout), והשורה תישאר ריקה ללא שגיאה גלויה.
jq מנתח JSON בשורת הפקודה ואינו מותקן בשרת Ubuntu חדש:
sudo apt update && sudo apt install -y jqלאחר מכן, כוונו את ההגדרה אל הסקריפט באמצעות בלוק ה-settings.json הראשון לעיל.
בדקו את הסקריפט לפני שתסמכו עליו
הריצו אותו פעמיים באופן ידני. תחילה עם אובייקט session רגיל:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/srv/api"},"session_id":"t1"}' | ~/.claude/statusline.shתקבלו את שם המארח (hostname), לאחר מכן את /srv/api, ואז את Opus. לא תופיע ענף (branch), כיוון ש-/srv/api במכונה שלכם כנראה אינו מאגר git.
שנית, בצעו את בדיקת ההידרדרות (degradation test), שהיא הבדיקה שאנשים נוטים לדלג עליה:
echo '{}' | ~/.claude/statusline.shאובייקט ריק הוא התרחיש הגרוע ביותר ששינוי סכימה יכול להציב בפניכם. השורה עדיין תדפיס: את שם המארח, את ספריית העבודה הנוכחית מתוך $PWD, ואת המילה claude במקום שבו אמור להופיע שם המודל. דבר אינו קורס ושום דבר לא מדפיס null. סקריפט שעובר את הבדיקה הזו ישרוד שינוי שם של שדה, כיוון שמבחינת הסקריפט שלכם, שדה ששמו שונה ושדה חסר הם אותו אירוע בדיוק.
מה עליך לראות
שורת הסטטוס מוצגת בשורה נפרדת מעל תגיות ה-footer המובנות ואינה מחליפה אותן. בהגדרה תקינה, השורה נראית כך: שם המארח (hostname) המקוצר בצבע ציאן, לאחר מכן ספריית העבודה הנוכחית כאשר ספריית הבית שלך מקוצרת ל-~, לאחר מכן שם הענף (branch) בצהוב כאשר הספרייה היא מאגר git, ולבסוף שם המודל בטשטוש. התוצאה צריכה להיות דומה ל-web-01 ~/api main Opus, עם ארבעת הרכיבים הללו צבועים.
השורה מריצה מחדש את הסקריפט שלך בכל תחילת סשן, כולל חזרה מ-resume, עם הגעת הודעת assistant חדשה, לאחר סיום ה-/compact, בעת שינוי מצב הרשאות, בעת החלפת מצב vim, ובתקתוק של refreshInterval אם הגדרת כזה. עדכונים עוברים debouncing של 300 ms, כך שרצף מהיר של שינויים מריץ את הסקריפט פעם אחת בלבד. השורה מוסתרת בזמן השלמה אוטומטית, תפריט העזרה והנחיות הרשאה, וחוזרת להופיע לאחר מכן.
מדוע שם המארח (hostname) מופיע ראשון
כאשר אתם מריצים סוכנים על יותר משרת אחד, הטרמינל הוא הדבר היחיד שמציין היכן אתם נמצאים, וטרמינלים עלולים להטעות. פתיחת חיבור ssh נוסף מתוך חלונית tmux גורמת לעיתים קרובות לכותרת החלון להישאר עם השם הישן, כיוון שהכותרת נקבעת על ידי shell שלא "ידע" שהוא עבר. אם תעזבו את Claude Code רץ בתוך session מנותק של tmux על גבי VPS ותתחברו אליו מחדש יום לאחר מכן, שום דבר על המסך לא יבדיל בין שרת ה-build לבין שרת ה-production.
שורת הסטטוס שונה מכיוון שהיא מרונדרת על ידי Claude Code עצמו, עבור כל session, מתוך נתונים שה-session מחזיק. היא לא יכולה לעבור בירושה מחלונית שגויה או להישאר לא מעודכנת בגלל shell prompt שלא התרענן. מה שהיא מציגה הוא השרת שעליו הסוכן כותב קבצים בפועל.
תנו לכל שרת צבע משלו כדי שתזהו אותו עוד לפני שתקראו את הטקסט. הוסיפו שתי שורות אלו מעל ההשמה של LINE=:
CODE=$(printf '%s' "$HOST" | cksum | cut -d' ' -f1)
HOST_COLOR=$(printf '\033[%dm' "$((31 + CODE % 6))")לאחר מכן השתמשו ב-${HOST_COLOR} במקום ב-${CYAN}. הפקודה cksum מדפיסה checksum של שם המארח, כך ששם נתון תמיד ימופה לאותו צבע בטווח שבין 31 ל-36 (אדום עד ציאן). העתיקו את אותו סקריפט לכל שרת, וכל אחד מהם יתייג את עצמו.
הספרייה (directory) ראויה למקומה מאותה סיבה. הפקודות /srv/api ו-/srv/api-staging נמצאות במרחק הקשה אחת זו מזו בתוך פקודת ssh, אך ההבדל בתוצאה ביניהן הוא תהומי. המודל וה-branch הם שני הפרמטרים הנוספים ששווים את המקום שהם תופסים: המודל מציין באיזה session המשכתם לעבוד, וה-branch מציין האם הסוכן עומד לבצע commit לתוך main.
מסך קטן הופך את כל זה לברור יותר, כיוון שאין כותרת חלון להסתמך עליה. אם זהו מערך העבודה שלכם, ראו הפעלת Claude Code מטלפון.
שמירה על מהירות הסקריפט
הסקריפט שלך רץ בכל הודעה של ה-assistant, ו-Claude Code מבטל הרצה פעילה כאשר מגיע עדכון חדש. לכן, סקריפט איטי יציג טקסט מיושן או לא יציג טקסט כלל.
כל קריאה ל-jq עולה כמה מילי-שניות. git הוא החלק שנוטה להאט: git status במאגר (repository) גדול עם cache קר לוקח מאות מילי-שניות. הסקריפט שלעיל נמנע מ-git status בכוונה וקורא ל-git branch --show-current, שקורא את .git/HEAD ומחזיר תוצאה באופן מיידי.
אם עליך להוסיף פעולה כבדה יותר, שמור אותה ב-cache בקובץ ורענן אותו בכל כמה שניות. השתמש ב-session כמפתח לקובץ:
CACHE="/tmp/statusline-$(field '.session_id')"השתמש ב-session_id, לא ב-$$. $$ הוא מזהה התהליך (PID) של הסקריפט שלך, והוא שונה בכל הרצה; לכן, cache שמבוסס עליו לעולם לא יצליח (cache miss) ואתה תשלם את מלוא המחיר בכל פעם. session_id הוא יציב לאורך כל ה-session ושונה בין sessions שונים, כך ששני sessions של Claude Code בשני מאגרים שונים לא יכולים לקרוא את שם ה-branch השמור של זה. ה-sessions נשארים מבודדים כחלק מהתכנון, ולכן העברת עבודה מאחד לשני דורשת פעולה מכוונת, וזהו בדיוק הייעוד של שליחת הודעה מ-session אחד של Claude Code לאחר.
מגבלה נוספת שכדאי להכיר: tput cols לא עובד בתוך סקריפט של statusline. Claude Code לוכד את הפלט במקום לחבר את הסקריפט שלך ל-terminal, ולכן אין לזיהוי רוחב מה למדוד. החל מגרסה v2.1.153, Claude Code מגדיר את משתני הסביבה COLUMNS ו-LINES לפני הרצת הפקודה, לכן קרא את $COLUMNS כאשר עליך להחליט כמה טקסט להדפיס.
מדוע שורת הסטטוס נשארת ריקה
לא מופיע דבר. בדקו את הרשאת ההרצה (execute bit) בעזרת ls -l ~/.claude/statusline.sh, ולאחר מכן הריצו את הסקריפט ידנית עם קלט הדמה שלעיל. אם הוא מדפיס שורה ב-shell אך לא ב-Claude Code, התחילו עם claude --debug, שמתעד את קוד היציאה ואת ה-stderr של הרצת שורת הסטטוס הראשונה בסשן.
לוג הניפוי מציג Status line command skipped: workspace trust not accepted. שורת הסטטוס מריצה פקודת shell, ולכן היא כפופה לאותו מנגנון אמון (workspace trust) כמו hooks. עד שלא תאשרו את תיבת הדו-שיח של האמון עבור אותה ספרייה, הפקודה לא תרוץ לעולם. זה נפוץ ב-VPS, שבו כל clone חדש הוא ספרייה ש-Claude Code טרם הכיר. הפעילו מחדש את Claude Code באותה ספרייה ואשרו את הדיאלוג.
הכל ריק ו-disableAllHooks מוגדר. "disableAllHooks": true בתוך settings.json משבית גם את שורת הסטטוס, כיוון שמדובר באותו מנגנון הרצת shell. הסירו הגדרה זו או שנו אותה ל-false.
השורה מדפיסה null. בורר (selector) של jq הגיע למפתח חסר או null, ו-jq -r מדפיס null כארבעת התווים null. הוסיפו // empty עבור טקסט ו-// 0 עבור מספרים.
השורה מתרוקנת מיד לאחר עריכת הסקריפט. פקודה שמחזירה קוד יציאה שאינו אפס, או שאינה מדפיסה דבר, מרוקנת את השורה. הסיבה הנפוצה היא שורה אחרונה כמו [ -n "$BRANCH" ] && LINE="...", שמחזירה 1 כאשר ה-branch ריק וגוררת איתה את קוד היציאה של כל הסקריפט. השאירו את printf אחרון, או הוסיפו exit 0.
קודי Escape מוצגים כטקסט פשוט כמו \e]8;; על הסרגל. השתמשו ב-printf '%b' במקום ב-echo -e. קישורי OSC 8 לחיצים דורשים גם טרמינל שתומך בהם, ו-tmux או SSH עלולים להסיר את הרצפים הללו, לכן צבע פשוט הוא הבחירה הבטוחה יותר במכונה מרוחקת.
הצד הימני של השורה קטוע. התראות מערכת ומונה ה-tokens במצב verbose חולקים את אותה שורה מהצד הימני, וטרמינל צר גורם לאובדן התוכן החופף. שמרו על פלט קצר. לחישוב מדויק של השימוש במקום מספר על סרגל, ראו כיצד Claude Code סופר tokens.
FAQ
Where does the Claude Code statusline setting live?
In settings.json, as a statusLine block with type set to "command" and command set to a script path or a shell command. User settings are at ~/.claude/settings.json and apply to every project on that machine. Project settings are at .claude/settings.json inside the repository and win for that directory. Settings reload on their own, but a change only becomes visible on the next update trigger, such as your next message.
Why is my Claude Code statusline blank?
Four causes cover nearly all of it. The script is missing the execute bit, so the shell returns Permission denied and nothing reaches stdout. The workspace trust dialog was never accepted, and claude --debug logs Status line command skipped: workspace trust not accepted. disableAllHooks is true, which disables the statusline under the same gate. Or the script exits non-zero, which blanks the row. Test it by hand first: echo '{}' | ~/.claude/statusline.sh has to print something.
Does the statusline JSON include the git branch?
No. The JSON carries session state such as the model, the workspace directories, context window numbers and cost. Nothing in it reports git. A branch on your bar comes from your own script calling git branch --show-current. Pass the directory from the JSON with git -C "$DIR", so the branch always matches the directory the bar is showing.
Does a statusline cost tokens or slow the session down?
It costs no tokens, because the script runs locally and its output is never sent to the model. Speed is your responsibility. The command runs on every assistant message with a 300 ms debounce, and Claude Code cancels an in-flight run when a new update arrives, so a script taking a full second shows stale text. Avoid git status in large repositories, and cache anything slow in a file keyed on session_id.
How do I show a different statusline on each server?
Keep one script and let it read the machine. The script above prints $HOSTNAME with hostname -s as a fallback, so the same file copied to every box labels each one correctly, and the checksum colour trick gives each hostname its own colour. If one server needs a different layout, put a statusLine block in the project settings of the repository you work in on that box, since project settings override user settings for that directory.