آموزش تنظیم statusLine در Claude Code روی VPS
با تنظیم statusLine در فایل settings.json، نام میزبان، مسیر و شاخه git را زیر پرامپت نمایش دهید. این کار از اجرای دستورات اشتباه در سرورهای مختلف جلوگیری میکند.
نوار وضعیت Claude Code چه چیزی را نشان میدهد
نوار وضعیت Claude Code ردیفی در زیر پرامپت است که خروجی اسکریپتی که مینویسید را نمایش میدهد. شما یک بلوک statusLine به settings.json اضافه میکنید و آن را به یک دستور ارجاع میدهید. Claude Code آن دستور را اجرا میکند، وضعیت نشست (session state) را به صورت JSON به ورودی استاندارد (stdin) آن میفرستد و هر چیزی که دستور به خروجی استاندارد (stdout) مینویسد را چاپ میکند.
قرارداد کار همین است. اسکریپت شما JSON را از stdin میخواند و متن را در stdout چاپ میکند. این اسکریپت روی ماشین شما اجرا میشود و هیچکدام از خروجیهای آن برای مدل ارسال نمیشود، بنابراین هیچ توکنی مصرف نمیکند.
روی یک لپتاپ با یک پروژه، این کار صرفاً جنبه تزئینی دارد. اما روی سه سرور، این یک ابزار ایمنی است. هر نشست Claude Code در هر ترمینال یکسان به نظر میرسد، بنابراین چهار پنجره SSH بدون برچسب، همان جایی است که یک عملیات مهاجرت ممکن است به اشتباه روی سرور نادرست انجام شود. داشتن یک نوار وضعیت که با نام میزبان (hostname) شروع میشود، این دسته از خطاها را از بین میبرد.
محل قرارگیری تنظیمات 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؛ این گزینه را تنها زمانی استفاده کنید که نوار وضعیت شامل ساعت یا چیزی است که در زمان بیکاری نشست (session) تغییر میکند. 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) را شروع کنید و یک پیام بفرستید. نوار وضعیت captured را میخواند. حالا ببینید چه چیزی دریافت شده است:
jq . /tmp/statusline-input.jsonشما شکل دقیق دادهها را برای بیلد خود در اختیار دارید و هر زمان که بهروزرسانی چیزی را تغییر داد، میتوانید این کار را تکرار کنید.
بخشهای پایدار، طبق مستندات اوت 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 که بهجای از کار افتادن، تنزل کیفیت میدهد
این نسخه آماده برای کپی و استفاده است. این اسکریپت نام میزبان (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یک شیء خالی، بدترین حالتی است که تغییر در طرحواره (schema) میتواند برای شما ایجاد کند. این خط همچنان چاپ میشود: نام میزبان، دایرکتوری فعلی از $PWD و کلمه claude در جایی که نام مدل قرار میگیرد. هیچچیز کرش نمیکند و هیچچیز null را چاپ نمیکند. اسکریپتی که این تست را با موفقیت پشت سر بگذارد، در برابر تغییر نام یک فیلد مقاوم است، زیرا برای اسکریپت شما، تغییر نام یک فیلد و نبودن آن، یک رویداد مشابه محسوب میشود.
آنچه باید مشاهده کنید
خط وضعیت در ردیف مخصوص به خود، بالای نشانهای (badges) پیشفرض فوتر نمایش داده میشود و جایگزین آنها نمیشود. در یک پیکربندی صحیح، این خط شامل یک ردیف است: نام کوتاه میزبان به رنگ فیروزهای، سپس دایرکتوری کاری که دایرکتوری خانگی شما در آن به ~ خلاصه شده است، سپس نام شاخه به رنگ زرد (در صورتی که دایرکتوری یک مخزن git باشد)، و در نهایت نام مدل به صورت کمنور. چیزی شبیه به web-01 ~/api main Opus، با آن چهار بخش رنگی.
این ردیف اسکریپت شما را هنگام شروع نشست (session)، از جمله بازگردانی (resume)، زمان دریافت پیام جدید از دستیار، پس از پایان /compact، هنگام تغییر حالت دسترسی، هنگام تغییر وضعیت حالت vim، و در صورت تنظیم، در هر تیک refreshInterval دوباره اجرا میکند. بهروزرسانیها با تأخیر 300 میلیثانیه (debounce) انجام میشوند، بنابراین در صورت وقوع تغییرات متوالی، اسکریپت فقط یکبار اجرا میشود. این نوار در حین تکمیل خودکار (autocomplete)، منوی راهنما و درخواستهای مجوز پنهان شده و سپس بازمیگردد.
چرا نام میزبان باید در ابتدا قرار گیرد
هنگامی که عاملها (agents) را روی بیش از یک سرور اجرا میکنید، ترمینال تنها چیزی است که موقعیت شما را نشان میدهد و ترمینالها ممکن است گمراهکننده باشند. اگر یک اتصال ssh دوم از داخل یک pane در tmux باز کنید، عنوان پنجره اغلب نام قبلی را حفظ میکند، زیرا عنوان توسط پوستهای (shell) تنظیم شده که متوجه جابهجایی نشده است. اگر Claude Code را در یک session جداشده tmux روی یک VPS رها کنید و یک روز بعد دوباره به آن متصل شوید، هیچ چیزی روی صفحه، سرور build را از سرور production متمایز نمیکند.
خط وضعیت (statusline) متفاوت است زیرا توسط خود Claude Code و برای هر session، بر اساس دادههایی که همان session در اختیار دارد، رندر میشود. این خط نمیتواند از یک pane اشتباه به ارث برسد یا توسط یک 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 (از قرمز تا فیروزهای) نگاشت میشود. این اسکریپت را روی تمام سیستمها کپی کنید تا هر کدام خود را برچسبگذاری کنند.
دایرکتوری نیز به همین دلیل جایگاه خود را دارد. /srv/api و /srv/api-staging در یک دستور ssh تنها یک کلید با هم فاصله دارند، اما در عمل، تفاوت آنها میتواند به اندازه یک حادثه بزرگ باشد. مدل و شاخه (branch) دو مورد دیگری هستند که ارزش فضای اشغالشده را دارند: مدل به شما میگوید کدام session را از سر گرفتهاید و شاخه به شما میگوید که آیا عامل قصد دارد روی main تغییرات را commit کند یا خیر.
یک صفحه نمایش کوچک، تمام این موارد را حیاتیتر میکند، زیرا عنوان پنجرهای وجود ندارد که به آن تکیه کنید. اگر تنظیمات شما به این صورت است، هدایت 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) اسکریپت شماست که در هر بار اجرا متفاوت است، بنابراین کشی که بر اساس آن کلیدگذاری شده باشد هرگز معتبر نخواهد بود و شما هر بار هزینه کامل را پرداخت میکنید. session_id برای کل نشست ثابت است و بین نشستها متفاوت است، بنابراین دو نشست Claude Code در دو مخزن مختلف نمیتوانند نام شاخه کششده یکدیگر را بخوانند. نشستها طبق طراحی ایزوله باقی میمانند، بنابراین انتقال کار از یکی به دیگری نیازمند یک گام عمدی است که هدف از ارسال پیام از یک نشست Claude Code به نشست دیگر همین است.
یک محدودیت دیگر که دانستن آن ارزشمند است: tput cols در داخل اسکریپت statusline کار نمیکند. Claude Code خروجی را ضبط میکند و اسکریپت شما را به ترمینال متصل نمیکند، بنابراین تشخیص عرض (width) چیزی برای اندازهگیری ندارد. Claude Code متغیرهای محیطی COLUMNS و LINES را پیش از اجرای دستور در نسخه v2.1.153 و بعد از آن تنظیم میکند، بنابراین زمانی که نیاز دارید تصمیم بگیرید چه مقدار چاپ کنید، $COLUMNS را بخوانید.
چرا خط وضعیت خالی میماند
هیچ چیزی نمایش داده نمیشود. بیت اجرایی (execute bit) را با ls -l ~/.claude/statusline.sh بررسی کنید، سپس اسکریپت را بهصورت دستی با ورودی نمونه در بالا اجرا کنید. اگر خروجی در شل چاپ میشود اما در Claude Code خیر، با claude --debug شروع کنید که کد خروج و stderr اولین اجرای خط وضعیت در نشست را لاگ میکند.
لاگ دیباگ عبارت Status line command skipped: workspace trust not accepted را نشان میدهد. خط وضعیت یک دستور شل را اجرا میکند، بنابراین پشت همان دروازه اعتماد فضای کاری (workspace trust gate) قرار دارد که هوکها (hooks) قرار دارند. تا زمانی که کادر محاورهای اعتماد را برای آن دایرکتوری نپذیرید، دستور هرگز اجرا نمیشود. این مورد در 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 را تحت همان محدودیت غیرفعال میکند. یا اینکه اسکریپت با کد خروجی غیر صفر (non-zero) پایان مییابد که باعث خالی شدن ردیف میشود. ابتدا آن را بهصورت دستی تست کنید: echo '{}' | ~/.claude/statusline.sh باید حتماً خروجی چاپ کند.
آیا JSON مربوط به statusline شامل شاخه (branch) گیت هست؟
خیر. این JSON وضعیت نشست (session) مانند مدل، دایرکتوریهای فضای کاری، اعداد پنجره کانتکست و هزینه را حمل میکند. هیچ بخشی از آن وضعیت گیت را گزارش نمیدهد. نمایش شاخه در نوار وضعیت شما ناشی از فراخوانی 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 در تنظیمات پروژه مخزنی که روی آن ماشین با آن کار میکنید قرار دهید، زیرا تنظیمات پروژه بر تنظیمات کاربر برای آن دایرکتوری اولویت دارند.