آموزش تنظیم statusline در Claude Code روی VPS
با افزودن تنظیم statusLine در فایل settings.json، خروجی اسکریپت دلخواه خود را زیر prompt نمایش دهید. این روش برای نمایش hostname و جلوگیری از خطای انسانی عالی است.
نحوه نمایش statusline در Claude Code
یک statusline در Claude Code ردیفی در زیر prompt است که خروجی اسکریپتی که مینویسید را نمایش میدهد. شما یک بلوک statusLine به settings.json اضافه میکنید و آن را به یک دستور ارجاع میدهید. Claude Code آن دستور را اجرا میکند، وضعیت نشست (session state) را به صورت JSON در ورودی استاندارد (stdin) به آن میفرستد و هر آنچه که دستور در خروجی استاندارد (stdout) مینویسد را چاپ میکند.
قرارداد کار به همین سادگی است. اسکریپت شما JSON را از stdin میخواند و متن را در stdout چاپ میکند. این اسکریپت روی ماشین شما اجرا میشود و هیچچیز از آنچه چاپ میکند به مدل فرستاده نمیشود، بنابراین هیچ توکنی مصرف نمیکند.
روی لپتاپی که تنها یک پروژه دارد، این قابلیت صرفاً جنبه تزئینی دارد. اما روی سه سرور، این یک ابزار ایمنی است. هر نشست Claude Code در هر ترمینال یکسان به نظر میرسد، بنابراین چهار پنجره SSH بدون برچسب، همان چیزی است که باعث میشود یک عملیات مهاجرت روی سرور اشتباه انجام شود. یک statusline که با نام میزبان (hostname) شروع میشود، این نوع خطاها را به کلی از بین میبرد.
محل قرارگیری تنظیمات statusLine در settings.json
این تنظیم را در تنظیمات کاربری خود در ~/.claude/settings.json قرار دهید که برای تمام پروژهها در آن ماشین اعمال میشود. تنظیمات پروژه در .claude/settings.json داخل یک مخزن نیز کار میکنند و برای آن دایرکتوری خاص، اولویت دارند.
{
"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 دستور را علاوه بر triggerهای معمول، هر N ثانیه دوباره اجرا میکند، با حداقل مقدار 1؛ این گزینه را تنها زمانی استفاده کنید که خط وضعیت شامل ساعت یا چیزی است که در زمان بیکاری نشست (session) تغییر میکند. hideVimModeIndicator متن داخلی -- INSERT -- را زمانی که اسکریپت خودتان حالت vim را رندر میکند، سرکوب (suppress) میکند.
اسکریپت 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) را شروع کنید و یک پیام بفرستید. نوار وضعیت captured را میخواند. اکنون نگاه کنید چه چیزی دریافت شده است:
jq . /tmp/statusline-input.jsonشما شکل دقیق دادهها را برای build خود دارید و هر زمان که بهروزرسانی چیزی را تغییر داد، میتوانید این کار را تکرار کنید.
بخشهای پایدار، طبق مستندات اوت 2026، اشیاء تو در تو هستند و نه کلیدهای تخت. model شامل id و display_name است. workspace شامل current_dir و project_dir است: current_dir جایی است که نشست اکنون در آن قرار دارد، project_dir جایی است که نشست از آنجا راهاندازی شده است، و این دو پس از تغییر دایرکتوری کاری در میانه نشست با هم تفاوت دارند. سطح بالای cwd همان مقداری را حمل میکند که workspace.current_dir دارد. context_window شامل تعداد توکنها به اضافه یک used_percentage از پیش محاسبهشده است. cost شامل total_cost_usd و شمارندههای مدت زمان است. session_id برای طول عمر نشست پایدار است و در بین نشستها منحصر به فرد است، که برای کش کردن در مراحل بعدی اهمیت دارد.
سه قانون برای زنده نگه داشتن اسکریپت در تغییرات schema وجود دارد.
برخی کلیدها غایب هستند، نه null. فیلدهای vim، agent، pr، worktree و effort تنها زمانی ظاهر میشوند که ویژگی مربوطه فعال باشد. خواندن .vim.mode با jq -r در حالی که حالت vim خاموش است، رشته متنی null را چاپ میکند و نوار وضعیت شما null را به کاربر نشان میدهد. عبارت // empty را به انتهای هر selector اضافه کنید تا در صورت نبود کلید، چیزی چاپ نشود.
برخی مقادیر در ابتدا null هستند. context_window.used_percentage و context_window.current_usage قبل از اولین پاسخ API مقدار null دارند و current_usage پس از /compact تا زمانی که فراخوانی بعدی آن را دوباره پر کند، به null برمیگردد. بنابراین، درصد context روی نوار وضعیت به // 0 نیاز دارد، در غیر این صورت برای چند ثانیه اول هر نشست، null را نمایش میدهد. پیش از آنکه آن عدد را روی نوار قرار دهید، دانستن نحوه پر شدن واقعی پنجره context مفید است.
شاخه git در JSON وجود ندارد. هیچ فیلدی آن را گزارش نمیکند. هر شاخهای که روی نوار وضعیت خود میبینید، ناشی از اجرای دستور git توسط خود اسکریپت شماست.
اسکریپتی برای statusline که بهجای از کار افتادن، تنزل مییابد
این نسخه آماده برای کپی و پیست است. این اسکریپت نام میزبان، دایرکتوری کاری، شاخه 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 را دریافت میکنید. هیچ شاخهای نمایش داده نمیشود، زیرا /srv/api روی دستگاه شما احتمالاً یک مخزن git نیست.
دوم، تست تخریب (degradation test) که همان تستی است که افراد از آن صرفنظر میکنند:
echo '{}' | ~/.claude/statusline.shیک شیء خالی، بدترین حالتی است که یک تغییر در schema میتواند برای شما ایجاد کند. این خط همچنان چاپ میشود: نام میزبان، دایرکتوری فعلی از $PWD، و کلمه claude در جایی که نام مدل قرار میگیرد. هیچچیز کرش نمیکند و هیچچیز null را چاپ نمیکند. اسکریپتی که این تست را با موفقیت پشت سر بگذارد، در برابر تغییر نام یک فیلد مقاوم است، زیرا برای اسکریپت شما، تغییر نام یک فیلد و نبودن آن فیلد، یک رویداد مشابه محسوب میشود.
آنچه باید مشاهده کنید
خط وضعیت در ردیف اختصاصی خود بالای نشانهای (badges) پیشفرض فوتر رندر میشود و جایگزین آنها نمیشود. در یک تنظیمات صحیح، این خط شامل یک ردیف است: نام کوتاه میزبان به رنگ فیروزهای، سپس دایرکتوری کاری که دایرکتوری home شما در آن به ~ خلاصه شده است، سپس نام شاخه (branch) به رنگ زرد در صورتی که دایرکتوری یک مخزن git باشد، و در نهایت نام مدل که کمرنگ نمایش داده میشود. چیزی شبیه به web-01 ~/api main Opus، با آن چهار بخش رنگی.
این ردیف اسکریپت شما را هنگام شروع نشست (session)، از جمله resume، هنگام دریافت پیام جدید از دستیار، پس از پایان /compact، هنگام تغییر حالت مجوزها، هنگام تغییر وضعیت vim mode و در صورت تنظیم، در هر تیک refreshInterval دوباره اجرا میکند. بهروزرسانیها با تأخیر 300 میلیثانیه (debounced) انجام میشوند، بنابراین در صورت وقوع چندین تغییر پشتسرهم، اسکریپت فقط یکبار اجرا میشود. این نوار هنگام تکمیل خودکار (autocomplete)، منوی راهنما و درخواستهای مجوز مخفی شده و سپس بازمیگردد.
چرا نام میزبان باید در اولویت باشد
هنگامی که عاملها (agents) را روی بیش از یک سرور اجرا میکنید، ترمینال تنها چیزی است که به شما میگوید کجا هستید، و ترمینالها گاهی فریب میدهند. اگر یک اتصال ssh دوم از داخل یک pane در tmux باز کنید، عنوان پنجره اغلب نام قبلی را حفظ میکند، زیرا عنوان توسط پوستهای (shell) تنظیم شده که متوجه جابهجایی نشده است. اگر Claude Code را در یک session جداشده tmux روی یک VPS رها کنید و یک روز بعد دوباره به آن متصل شوید، هیچ چیزی روی صفحه، سرور build را از سرور production متمایز نمیکند.
خط وضعیت (statusline) متفاوت است زیرا توسط خود Claude Code و برای هر session، بر اساس دادههایی که همان session در اختیار دارد، رندر میشود. این خط نمیتواند از یک pane اشتباه به ارث برسد یا توسط یک shell prompt که بهروزرسانی نشده، قدیمی بماند. آنچه در این خط نمایش داده میشود، همان سیستمی است که عامل در حال نوشتن فایلها روی آن است.
برای هر سرور یک رنگ اختصاصی در نظر بگیرید تا پیش از خواندن متن، آن را تشخیص دهید. دو خط زیر را پیش از انتساب LINE= اضافه کنید:
CODE=$(printf '%s' "$HOST" | cksum | cut -d' ' -f1)
HOST_COLOR=$(printf '\033[%dm' "$((31 + CODE % 6))")سپس از ${HOST_COLOR} به جای ${CYAN} استفاده کنید. cksum یک checksum از نام میزبان (hostname) چاپ میکند، بنابراین یک نام مشخص همیشه به همان رنگ در بازه 31 تا 36 (از قرمز تا فیروزهای) نگاشت میشود. این اسکریپت را در تمام سیستمها کپی کنید تا هر کدام خودشان را برچسبگذاری کنند.
دایرکتوری نیز به همان دلیل جایگاه خود را در این خط دارد. /srv/api و /srv/api-staging در یک دستور ssh تنها یک کلید با هم فاصله دارند، اما در عمل، تفاوت آنها میتواند به اندازه یک حادثه بزرگ باشد. مدل و شاخه (branch) دو مورد دیگری هستند که ارزش اشغال فضای صفحه را دارند: مدل به شما میگوید کدام session را از سر گرفتهاید و شاخه به شما میگوید که آیا عامل در شرف commit کردن روی main است یا خیر.
صفحه نمایش کوچک، همه این موارد را شفافتر میکند، زیرا عنوان پنجرهای وجود ندارد که به آن تکیه کنید. اگر تنظیمات شما به این صورت است، هدایت Claude Code از طریق گوشی را ببینید.
اسکریپت را سریع نگه دارید
اسکریپت شما با هر پیام دستیار اجرا میشود و Claude Code در صورت دریافت بهروزرسانی جدید، اجرای در حال انجام را لغو میکند. بنابراین یک اسکریپت کند، متن قدیمی یا هیچ متنی را نمایش نمیدهد.
هر فراخوانی jq چند میلیثانیه هزینه دارد. git بخشی است که کند میشود: git status در یک مخزن بزرگ با کش سرد، صدها میلیثانیه زمان میبرد. اسکریپت بالا عمداً از git status اجتناب کرده و git branch --show-current را فراخوانی میکند که .git/HEAD را میخواند و بلافاصله بازمیگردد.
اگر مورد سنگینتری اضافه میکنید، آن را در یک فایل کش کنید و هر چند ثانیه یکبار بهروزرسانی نمایید. فایل را بر اساس نشست (session) کلیدگذاری کنید:
CACHE="/tmp/statusline-$(field '.session_id')"از session_id استفاده کنید، نه $$. $$ شناسه پردازش (PID) اسکریپت شماست که در هر بار اجرا متفاوت است، بنابراین کشی که بر اساس آن کلیدگذاری شده باشد هرگز hit نمیشود و شما هر بار هزینه کامل را میپردازید. session_id برای کل نشست ثابت است و بین نشستها متفاوت است، بنابراین دو نشست Claude Code در دو مخزن مختلف نمیتوانند نام شاخه کششده یکدیگر را بخوانند.
یک محدودیت دیگر که دانستن آن ارزشمند است: tput cols داخل اسکریپت statusline کار نمیکند. Claude Code خروجی را ضبط میکند و اسکریپت شما را به ترمینال متصل نمیکند، بنابراین تشخیص عرض (width) چیزی برای اندازهگیری ندارد. Claude Code متغیرهای محیطی COLUMNS و LINES را پیش از اجرای دستور در نسخه 2.1.153 و بالاتر تنظیم میکند، بنابراین هر زمان نیاز دارید تصمیم بگیرید چه مقدار چاپ کنید، $COLUMNS را بخوانید.
چرا خط وضعیت خالی میماند
هیچ چیزی نمایش داده نمیشود. مجوز اجرای فایل را با ls -l ~/.claude/statusline.sh بررسی کنید، سپس اسکریپت را بهصورت دستی با ورودی نمونه بالا اجرا کنید. اگر اسکریپت در شل خروجی چاپ میکند اما در Claude Code خیر، با claude --debug شروع کنید که کد خروج و stderr اولین اجرای خط وضعیت در نشست را لاگ میکند.
لاگ دیباگ عبارت Status line command skipped: workspace trust not accepted را نشان میدهد. خط وضعیت یک دستور شل را اجرا میکند، بنابراین پشت همان دروازه اعتماد workspace (مشابه hookها) قرار دارد. تا زمانی که کادر محاورهای اعتماد را برای آن دایرکتوری نپذیرید، دستور هرگز اجرا نمیشود. این مورد در VPS رایج است، جایی که هر clone جدید، دایرکتوریای است که Claude Code قبلاً ندیده است. Claude Code را در آن دایرکتوری مجدداً راهاندازی کنید و کادر محاورهای را بپذیرید.
همه چیز خالی است و disableAllHooks تنظیم شده است. گزینه "disableAllHooks": true در فایل settings.json نیز خط وضعیت را غیرفعال میکند، زیرا این گزینه هم از همان دروازه اجرای شل استفاده میکند. آن را حذف کنید یا روی false تنظیم نمایید.
ردیف عبارت null را چاپ میکند. یک انتخابگر jq به کلیدی رسیده که وجود ندارد یا null است و jq -r مقدار null را به صورت چهار کاراکتر null چاپ میکند. برای متن از // empty و برای اعداد از // 0 استفاده کنید.
ردیف بلافاصله پس از ویرایش اسکریپت خالی میشود. دستوری که با کد غیر صفر خارج شود یا چیزی چاپ نکند، ردیف را خالی میکند. علت معمول، وجود خطی مانند [ -n "$BRANCH" ] && LINE="..." در انتهاست که وقتی branch خالی باشد، با کد 1 خارج میشود و کد خروج کل اسکریپت را با خود میبرد. دستور printf را در انتها نگه دارید یا exit 0 را اضافه کنید.
کدهای گریز (Escape codes) به صورت متن ساده مانند \e]8;; روی نوار نمایش داده میشوند. بهجای echo -e از printf '%b' استفاده کنید. لینکهای قابل کلیک OSC 8 نیز به ترمینالی نیاز دارند که از آنها پشتیبانی کند؛ tmux یا SSH ممکن است این توالیها را حذف کنند، بنابراین رنگ ساده در محیطهای ریموت انتخاب امنتری است.
سمت راست ردیف بریده میشود. اعلانهای سیستم و شمارنده توکن در حالت verbose از سمت راست با آن ردیف مشترک هستند و در ترمینالهای باریک، این بخشها با هم تداخل پیدا میکنند. خروجی را کوتاه نگه دارید. برای مشاهده آمار دقیق مصرف بهجای یک عدد روی نوار، به نحوه محاسبه توکنها توسط Claude Code مراجعه کنید.
FAQ
تنظیمات statusline در Claude Code کجا قرار دارد؟
در settings.json، به عنوان یک بلوک statusLine که در آن type روی "command" و command روی مسیر یک اسکریپت یا یک دستور shell تنظیم شده است. تنظیمات کاربر در ~/.claude/settings.json قرار دارند و برای تمام پروژههای موجود در آن ماشین اعمال میشوند. تنظیمات پروژه در .claude/settings.json داخل مخزن (repository) قرار دارند و برای آن دایرکتوری خاص اولویت دارند. تنظیمات بهطور خودکار بارگذاری مجدد میشوند، اما تغییرات تنها با تریگر بهروزرسانی بعدی، مانند ارسال پیام بعدی شما، قابل مشاهده خواهند بود.
چرا statusline در Claude Code من خالی است؟
چهار دلیل تقریباً تمام موارد را پوشش میدهد. اسکریپت فاقد مجوز اجرا (execute bit) است، بنابراین shell مقدار Permission denied را برمیگرداند و چیزی به stdout نمیرسد. دیالوگ اعتماد به فضای کاری (workspace trust) هرگز تایید نشده است و claude --debug در لاگها Status line command skipped: workspace trust not accepted را ثبت میکند. disableAllHooks روی true تنظیم شده است که statusline را تحت همان محدودیت غیرفعال میکند. یا اینکه اسکریپت با کد خروجی غیر صفر پایان مییابد که باعث خالی شدن ردیف میشود. ابتدا آن را بهصورت دستی تست کنید: echo '{}' | ~/.claude/statusline.sh باید حتماً خروجی چاپ کند.
آیا JSON مربوط به statusline شامل شاخه git (git branch) میشود؟
خیر. این JSON وضعیت نشست (session) مانند مدل، دایرکتوریهای فضای کاری، اعداد پنجره کانتکست و هزینه را حمل میکند. هیچ بخشی از آن وضعیت git را گزارش نمیکند. نمایش شاخه در نوار وضعیت شما ناشی از اسکریپت شخصی خودتان است که git branch --show-current را فراخوانی میکند. دایرکتوری را از طریق JSON با git -C "$DIR" ارسال کنید تا شاخه همیشه با دایرکتوری که نوار وضعیت نشان میدهد، مطابقت داشته باشد.
آیا statusline باعث مصرف توکن یا کندی نشست میشود؟
هیچ توکنی مصرف نمیکند، زیرا اسکریپت بهصورت محلی اجرا میشود و خروجی آن هرگز برای مدل ارسال نمیشود. سرعت اجرای آن به عهده شماست. این دستور با هر پیام دستیار و با یک debounce به مدت 300 میلیثانیه اجرا میشود و Claude Code اجرای در حال انجام را هنگام رسیدن بهروزرسانی جدید لغو میکند، بنابراین اسکریپتی که یک ثانیه کامل طول میکشد، متن قدیمی را نمایش میدهد. از اجرای git status در مخازن بزرگ خودداری کنید و هر عملیات کندی را در فایلی که با session_id کلیدگذاری شده، کش (cache) کنید.
چگونه میتوانم در هر سرور یک statusline متفاوت نمایش دهم؟
یک اسکریپت واحد نگه دارید و اجازه دهید ماشین را شناسایی کند. اسکریپت بالا $HOSTNAME را با hostname -s به عنوان جایگزین چاپ میکند، بنابراین همان فایلی که در هر سرور کپی میشود، هر کدام را بهدرستی برچسبگذاری میکند و ترفند رنگآمیزی بر اساس checksum، به هر hostname رنگ خاص خودش را میدهد. اگر سروری به چیدمان متفاوتی نیاز دارد، یک بلوک statusLine در تنظیمات پروژه مخزنی که روی آن ماشین کار میکنید قرار دهید، زیرا تنظیمات پروژه برای آن دایرکتوری بر تنظیمات کاربر اولویت دارد.