رفتن به محتوا

سفارشی‌سازیِ نوارِ وضعیت

نوارِ وضعیت (status line) یک نوارِ سفارشی در پایینِ Claude Code است که هر اسکریپتِ شِلی که پیکربندی کنی را اجرا می‌کند. این نوار داده‌های JSON نشست را روی stdin دریافت می‌کند و هر چیزی که اسکریپتت چاپ کند را نمایش می‌دهد؛ یعنی یک نمای دائمی و یک‌نگاهی از مصرفِ کانتکست، هزینه‌ها، وضعیت git یا هر چیز دیگری که بخواهی پیگیری کنی به تو می‌دهد.

نوارهای وضعیت وقتی به کارت می‌آیند که:

  • بخواهی هنگام کار، مصرفِ پنجره‌ی کانتکست را زیرِ نظر داشته باشی
  • لازم باشد هزینه‌های نشست را دنبال کنی
  • روی چند نشست هم‌زمان کار می‌کنی و باید بتوانی آن‌ها را از هم تشخیص بدهی
  • بخواهی شاخه‌ی git و وضعیتش همیشه پیدا باشد

این هم نمونه‌ای از یک نوارِ وضعیتِ چندخطی که اطلاعاتِ git را در خطِ اول و یک نوارِ کانتکستِ رنگی را در خطِ دوم نشان می‌دهد.

A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line

این صفحه راه‌اندازیِ یک نوارِ وضعیتِ پایه را مرحله‌به‌مرحله نشان می‌دهد، توضیح می‌دهد داده‌ها چطور جریان پیدا می‌کنند از Claude Code به اسکریپتت، همه‌ی فیلدهایی که می‌توانی نمایش بدهی را فهرست می‌کند، و نمونه‌های آماده‌ی استفاده برای الگوهای رایج مثل وضعیت git، پیگیریِ هزینه و نوارهای پیشرفت ارائه می‌دهد.

راه‌اندازیِ یک نوارِ وضعیت

Section titled “راه‌اندازیِ یک نوارِ وضعیت”

از دستورِ /statusline استفاده کن تا Claude Code خودش برایت یک اسکریپت بسازد، یا دستی یک اسکریپت بساز و به تنظیماتت اضافه کن.

استفاده از دستورِ /statusline

Section titled “استفاده از دستورِ /statusline”

دستورِ /statusline دستورالعمل‌های زبانِ طبیعی را می‌پذیرد که توصیف می‌کنند چه چیزی می‌خواهی نمایش داده شود. Claude Code یک فایلِ اسکریپت در ~/.claude/ می‌سازد و تنظیماتت را خودکار به‌روزرسانی می‌کند:

/statusline show model name and context percentage with a progress bar

پیکربندیِ دستیِ یک نوارِ وضعیت

Section titled “پیکربندیِ دستیِ یک نوارِ وضعیت”

یک فیلدِ statusLine به تنظیماتِ کاربری‌ات (~/.claude/settings.json، که در آن ~ پوشه‌ی خانگیِ توست) یا تنظیماتِ پروژه اضافه کن. type را روی "command" بگذار و command را به مسیرِ یک اسکریپت یا یک دستورِ شِلِ خطی اشاره بده. برای یک راهنمای کامل از ساختنِ اسکریپت، ساختِ یک نوارِ وضعیت قدم‌به‌قدم را ببین.

{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}

فیلدِ command در یک شِل اجرا می‌شود، پس می‌توانی به‌جای فایلِ اسکریپت از دستورهای خطی هم استفاده کنی. این نمونه از jq برای تجزیه‌ی ورودیِ JSON و نمایشِ نامِ مدل و درصدِ کانتکست استفاده می‌کند:

{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}

فیلدِ اختیاریِ padding فاصله‌ی افقیِ اضافی (به تعدادِ کاراکتر) به محتوای نوارِ وضعیت اضافه می‌کند. پیش‌فرضش 0 است. این فاصله علاوه بر فاصله‌ی توکارِ رابطِ کاربری است، پس تورفتگیِ نسبی را کنترل می‌کند نه فاصله‌ی مطلق از لبه‌ی ترمینال.

فیلدِ اختیاریِ refreshInterval دستورت را علاوه بر به‌روزرسانی‌های رویدادمحور هر N ثانیه یک‌بار دوباره اجرا می‌کند. کمینه‌اش 1 است. این را وقتی تنظیم کن که نوارِ وضعیتت داده‌ی زمان‌محور مثل ساعت نشان می‌دهد، یا وقتی ساب‌ایجنت‌های پس‌زمینه وضعیتِ git را تغییر می‌دهند در حالی که نشستِ اصلی بی‌کار است. اگر بخواهی فقط روی رویدادها اجرا شود، آن را تنظیم‌نشده بگذار.

فیلدِ اختیاریِ hideVimModeIndicator متنِ توکارِ -- INSERT -- زیرِ پرامپت را پنهان می‌کند. این را روی true بگذار وقتی اسکریپتت خودش vim.mode را رندر می‌کند، تا حالت دو بار نشان داده نشود.

غیرفعال‌کردنِ نوارِ وضعیت

Section titled “غیرفعال‌کردنِ نوارِ وضعیت”

/statusline را اجرا کن و از آن بخواه نوارِ وضعیتت را حذف یا پاک کند (مثلاً /statusline delete، /statusline clear، /statusline remove it). می‌توانی فیلدِ statusLine را هم به‌صورت دستی از settings.json حذف کنی.

ساختِ یک نوارِ وضعیت قدم‌به‌قدم

Section titled “ساختِ یک نوارِ وضعیت قدم‌به‌قدم”

این راهنما با ساختنِ دستیِ یک نوارِ وضعیت که مدلِ فعلی، پوشه‌ی کاری و درصدِ مصرفِ پنجره‌ی کانتکست را نمایش می‌دهد، نشان می‌دهد پشتِ پرده چه می‌گذرد.

این نمونه‌ها از اسکریپت‌های Bash استفاده می‌کنند که روی macOS و Linux کار می‌کنند. روی Windows، برای نمونه‌های PowerShell و Git Bash به پیکربندیِ Windows مراجعه کن.

A status line showing model name, directory, and context percentage

یک اسکریپت بساز که JSON بخواند و خروجی چاپ کند

Claude Code داده‌های JSON را از طریقِ stdin به اسکریپتت می‌فرستد. این اسکریپت از jq — یک تجزیه‌گرِ JSON خطِ فرمانی که شاید لازم باشد نصبش کنی — برای استخراجِ نامِ مدل، پوشه و درصدِ کانتکست استفاده می‌کند، و بعد یک خطِ قالب‌بندی‌شده چاپ می‌کند.

این را در ~/.claude/statusline.sh ذخیره کن (که در آن ~ پوشه‌ی خانگیِ توست، مثلاً /Users/username روی macOS یا /home/username روی Linux):

#!/bin/bash
# Read JSON data that Claude Code sends to stdin
input=$(cat)
# Extract fields using jq
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
# The "// 0" provides a fallback if the field is null
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
# Output the status line - ${DIR##*/} extracts just the folder name
echo "[$MODEL] 📁 ${DIR##*/} | ${PCT}% context"

آن را اجراپذیر کن

اسکریپت را اجراپذیر علامت بزن تا شِلت بتواند آن را اجرا کند:

Terminal window
chmod +x ~/.claude/statusline.sh

به تنظیمات اضافه کن

به Claude Code بگو اسکریپتت را به‌عنوانِ نوارِ وضعیت اجرا کند. این پیکربندی را به ~/.claude/settings.json اضافه کن، که type را روی "command" می‌گذارد (یعنی «این دستورِ شِل را اجرا کن») و command را به اسکریپتت اشاره می‌دهد:

{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}

نوارِ وضعیتت در پایینِ رابطِ کاربری ظاهر می‌شود. تنظیمات خودکار دوباره بارگذاری می‌شوند، اما تغییرات تا تعاملِ بعدی‌ات با Claude Code نمایان نمی‌شوند.

نوارهای وضعیت چطور کار می‌کنند

Section titled “نوارهای وضعیت چطور کار می‌کنند”

Claude Code اسکریپتت را اجرا می‌کند و داده‌های JSON نشست را از طریقِ stdin به آن می‌فرستد (pipe). اسکریپتت JSON را می‌خواند، آنچه را لازم دارد استخراج می‌کند، و متن را روی stdout چاپ می‌کند. Claude Code هر چیزی که اسکریپتت چاپ کند را نمایش می‌دهد.

کِی به‌روز می‌شود

اسکریپتت بعد از هر پیامِ تازه‌ی دستیار، پس از پایانِ /compact، هنگامِ تغییرِ حالتِ دسترسی، یا هنگامِ تغییرِ حالتِ vim اجرا می‌شود. به‌روزرسانی‌ها با تأخیرِ ۳۰۰ میلی‌ثانیه‌ای debounce می‌شوند، یعنی تغییراتِ سریع با هم دسته می‌شوند و اسکریپتت وقتی اوضاع آرام شد یک‌بار اجرا می‌شود. اگر در حینِ اجرای اسکریپت یک به‌روزرسانیِ جدید رخ بدهد، اجرای در جریان لغو می‌شود. اگر اسکریپتت را ویرایش کنی، تغییرات تا وقتی تعاملِ بعدی‌ات با Claude Code یک به‌روزرسانی را تحریک کند، نمایان نمی‌شوند.

این محرک‌ها می‌توانند وقتی نشستِ اصلی بی‌کار است ساکت بمانند، مثلاً وقتی یک هماهنگ‌کننده منتظرِ ساب‌ایجنت‌های پس‌زمینه است. برای این‌که بخش‌های زمان‌محور یا منبع‌گرفته از بیرون در دوره‌های بی‌کاری به‌روز بمانند، refreshInterval را تنظیم کن تا دستور را روی یک تایمرِ ثابت هم دوباره اجرا کند.

اسکریپتت چه چیزی می‌تواند خروجی بدهد

اندازه‌کردنِ خروجی با ترمینال

Claude Code خروجیِ اسکریپتت را می‌گیرد به‌جای آن‌که آن را مستقیم به ترمینال وصل کند، پس tput cols و تشخیصِ عرض در سطحِ زبان نمی‌توانند اندازه‌ی ترمینال را از داخلِ اسکریپت بخوانند. {/* min-version: 2.1.153 */}به‌جایش متغیرهای محیطیِ COLUMNS و LINES را بخوان. Claude Code این‌ها را پیش از اجرای اسکریپتت روی ابعادِ فعلیِ ترمینال تنظیم می‌کند. نیاز به Claude Code v2.1.153 یا بالاتر دارد.

Claude Code فیلدهای JSON زیر را از طریقِ stdin به اسکریپتت می‌فرستد:

فیلدتوضیح
model.id, model.display_nameشناسه‌ی مدلِ فعلی و نامِ نمایشی
cwd, workspace.current_dirپوشه‌ی کاریِ فعلی. هر دو فیلد یک مقدار دارند؛ workspace.current_dir برای هماهنگی با workspace.project_dir ترجیح داده می‌شود.
workspace.project_dirپوشه‌ای که Claude Code از آن راه‌اندازی شده، که اگر پوشه‌ی کاری در حینِ نشست تغییر کند ممکن است با cwd فرق کند
workspace.added_dirsپوشه‌های اضافی که با /add-dir یا --add-dir افزوده شده‌اند. اگر هیچ‌کدام افزوده نشده باشد، آرایه‌ی خالی است
workspace.git_worktreeنامِ git worktree وقتی پوشه‌ی فعلی داخلِ یک worktreeِ پیوندی است که با git worktree add ساخته شده. در درختِ کاریِ اصلی غایب است. برای هر git worktree پر می‌شود، برخلافِ worktree.* که فقط برای نشست‌های --worktree کاربرد دارد
workspace.repo.host, workspace.repo.owner, workspace.repo.nameهویتِ مخزن که از remoteِ origin تجزیه می‌شود، مثلاً "github.com", "anthropics", "claude-code". خارج از یک مخزنِ git یا وقتی هیچ remoteِ origin پیکربندی نشده، غایب است
cost.total_cost_usdهزینه‌ی تخمینیِ نشست به دلار، که سمتِ کلاینت محاسبه می‌شود. ممکن است با صورتحسابِ واقعی‌ات فرق کند
cost.total_duration_msزمانِ کلِ سپری‌شده از شروعِ نشست، به میلی‌ثانیه
cost.total_api_duration_msزمانِ کلِ صرف‌شده برای انتظارِ پاسخ‌های API به میلی‌ثانیه
cost.total_lines_added, cost.total_lines_removedخطوطِ کدِ تغییریافته
context_window.total_input_tokens, context_window.total_output_tokensتعدادِ توکن‌هایی که اکنون در پنجره‌ی کانتکست هستند، از آخرین پاسخِ API. ورودی شاملِ خواندن و نوشتنِ کش است. {/* min-version: 2.1.132 */}پیش از v2.1.132 این‌ها مجموعِ انباشتیِ نشست بودند
context_window.context_window_sizeبیشینه‌ی اندازه‌ی پنجره‌ی کانتکست به توکن. پیش‌فرض ۲۰۰۰۰۰، یا ۱۰۰۰۰۰۰ برای مدل‌های با کانتکستِ گسترده.
context_window.used_percentageدرصدِ ازپیش‌محاسبه‌شده‌ی پنجره‌ی کانتکستِ مصرف‌شده
context_window.remaining_percentageدرصدِ ازپیش‌محاسبه‌شده‌ی پنجره‌ی کانتکستِ باقی‌مانده
context_window.current_usageتعدادِ توکن‌ها از آخرین فراخوانیِ API، که در فیلدهای پنجره‌ی کانتکست توضیح داده شده
exceeds_200k_tokensاین‌که آیا مجموعِ تعدادِ توکن‌ها (ورودی، کش و خروجی با هم) از آخرین پاسخِ API از ۲۰۰هزار فراتر می‌رود یا نه. این یک آستانه‌ی ثابت است صرف‌نظر از اندازه‌ی واقعیِ پنجره‌ی کانتکست.
effort.levelتلاشِ استدلالیِ فعلی (low, medium, high, xhigh یا max). مقدارِ زنده‌ی نشست را بازتاب می‌دهد، از جمله تغییراتِ /effort در میانه‌ی نشست. Ultracode یک سطحِ مجزا نیست و به‌صورتِ xhigh گزارش می‌شود. وقتی مدلِ فعلی پارامترِ effort را پشتیبانی نکند، غایب است
thinking.enabledاین‌که آیا تفکرِ گسترده (extended thinking) برای نشست فعال است یا نه
rate_limits.five_hour.used_percentage, rate_limits.seven_day.used_percentageدرصدِ مصرف‌شده‌ی محدودیتِ نرخِ ۵ساعته یا ۷روزه، از ۰ تا ۱۰۰
rate_limits.five_hour.resets_at, rate_limits.seven_day.resets_atثانیه‌های Unix epoch وقتی پنجره‌ی محدودیتِ نرخِ ۵ساعته یا ۷روزه بازنشانی می‌شود
session_idشناسه‌ی یکتای نشست
session_nameنامِ سفارشیِ نشست که با پرچمِ --name یا /rename تنظیم شده. اگر هیچ نامِ سفارشی‌ای تنظیم نشده باشد، غایب است
transcript_pathمسیرِ فایلِ رونوشتِ گفت‌وگو
versionنسخه‌ی Claude Code
output_style.nameنامِ سبکِ خروجیِ فعلی
vim.modeحالتِ فعلیِ vim (NORMAL, INSERT, VISUAL یا VISUAL LINE) وقتی حالتِ vim فعال است
agent.nameنامِ ایجنت وقتی با پرچمِ --agent یا با تنظیماتِ ایجنتِ پیکربندی‌شده اجرا می‌شود
pr.number, pr.urlpull requestِ بازِ شاخه‌ی فعلی. نشانِ PR در نوارِ وضعیتِ پایین را آینه می‌کند. تا وقتی یک PR پیدا نشود، یا وقتی در مخزنِ git نباشی، یا پس از merge یا بسته‌شدنِ PR، غایب است
pr.review_stateوضعیتِ بازبینیِ PRِ باز: approved, pending, changes_requested یا draft. ممکن است حتی وقتی pr حاضر است، مستقلاً غایب باشد
worktree.nameنامِ worktreeِ فعال. فقط در حینِ نشست‌های --worktree حاضر است
worktree.pathمسیرِ مطلقِ پوشه‌ی worktree
worktree.branchنامِ شاخه‌ی git برای worktree (مثلاً "worktree-my-feature"). برای worktreeهای مبتنی‌بر‌hook غایب است
worktree.original_cwdپوشه‌ای که Claude پیش از واردشدن به worktree در آن بود
worktree.original_branchشاخه‌ی git که پیش از واردشدن به worktree checkout شده بود. برای worktreeهای مبتنی‌بر‌hook غایب است
اسکیمای کاملِ JSON

دستورِ نوارِ وضعیتت این ساختارِ JSON را از طریقِ stdin دریافت می‌کند:

{
"cwd": "/current/working/directory",
"session_id": "abc123...",
"session_name": "my-session",
"transcript_path": "/path/to/transcript.jsonl",
"model": {
"id": "claude-opus-4-8",
"display_name": "Opus"
},
"workspace": {
"current_dir": "/current/working/directory",
"project_dir": "/original/project/directory",
"added_dirs": [],
"git_worktree": "feature-xyz",
"repo": {
"host": "github.com",
"owner": "anthropics",
"name": "claude-code"
}
},
"version": "2.1.90",
"output_style": {
"name": "default"
},
"cost": {
"total_cost_usd": 0.01234,
"total_duration_ms": 45000,
"total_api_duration_ms": 2300,
"total_lines_added": 156,
"total_lines_removed": 23
},
"context_window": {
"total_input_tokens": 15500,
"total_output_tokens": 1200,
"context_window_size": 200000,
"used_percentage": 8,
"remaining_percentage": 92,
"current_usage": {
"input_tokens": 8500,
"output_tokens": 1200,
"cache_creation_input_tokens": 5000,
"cache_read_input_tokens": 2000
}
},
"exceeds_200k_tokens": false,
"effort": {
"level": "high"
},
"thinking": {
"enabled": true
},
"rate_limits": {
"five_hour": {
"used_percentage": 23.5,
"resets_at": 1738425600
},
"seven_day": {
"used_percentage": 41.2,
"resets_at": 1738857600
}
},
"vim": {
"mode": "NORMAL"
},
"agent": {
"name": "security-reviewer"
},
"pr": {
"number": 1234,
"url": "https://github.com/anthropics/claude-code/pull/1234",
"review_state": "pending"
},
"worktree": {
"name": "my-feature",
"path": "/path/to/.claude/worktrees/my-feature",
"branch": "worktree-my-feature",
"original_cwd": "/path/to/project",
"original_branch": "main"
}
}

فیلدهایی که ممکن است غایب باشند (در JSON حاضر نباشند):

  • session_name: فقط وقتی ظاهر می‌شود که یک نامِ سفارشی با --name یا /rename تنظیم شده باشد
  • workspace.git_worktree: فقط وقتی ظاهر می‌شود که پوشه‌ی فعلی داخلِ یک git worktreeِ پیوندی باشد
  • workspace.repo: فقط داخلِ یک مخزنِ git با remoteِ originِ پیکربندی‌شده ظاهر می‌شود
  • effort: فقط وقتی ظاهر می‌شود که مدلِ فعلی پارامترِ تلاشِ استدلالی را پشتیبانی کند
  • vim: فقط وقتی حالتِ vim فعال باشد ظاهر می‌شود
  • agent: فقط وقتی با پرچمِ --agent یا با تنظیماتِ ایجنتِ پیکربندی‌شده اجرا شود ظاهر می‌شود
  • pr: فقط تا وقتی یک PRِ باز برای شاخه‌ی فعلی پیدا شود ظاهر می‌شود، و پس از merge یا بسته‌شدنِ PR حذف می‌شود. pr.review_state ممکن است مستقلاً غایب باشد
  • worktree: فقط در حینِ نشست‌های --worktree ظاهر می‌شود. وقتی حاضر است، branch و original_branch هم ممکن است برای worktreeهای مبتنی‌بر‌hook غایب باشند
  • rate_limits: فقط برای مشترکانِ Claude.ai (Pro/Max) پس از اولین پاسخِ API در نشست ظاهر می‌شود. هر پنجره (five_hour, seven_day) ممکن است مستقلاً غایب باشد. برای مدیریتِ روانِ غیبت از jq -r '.rate_limits.five_hour.used_percentage // empty' استفاده کن.

فیلدهایی که ممکن است null باشند:

  • context_window.current_usage: پیش از اولین فراخوانیِ API در یک نشست null است، و دوباره پس از /compact تا وقتی فراخوانیِ API بعدی آن را دوباره پر کند
  • context_window.used_percentage, context_window.remaining_percentage: ممکن است اوایلِ نشست null باشند

فیلدهای ناموجود را با دسترسیِ شرطی و مقادیرِ null را با پیش‌فرض‌های جایگزین در اسکریپت‌هایت مدیریت کن.

فیلدهای پنجره‌ی کانتکست

Section titled “فیلدهای پنجره‌ی کانتکست”

شیءِ context_window پنجره‌ی کانتکستِ زنده را از آخرین پاسخِ API توصیف می‌کند. از v2.1.132 به بعد، total_input_tokens و total_output_tokens مصرفِ کانتکستِ فعلی را بازتاب می‌دهند، نه مجموعِ انباشتیِ نشست را.

  • مجموع‌های ترکیبی (total_input_tokens, total_output_tokens): توکن‌هایی که اکنون در پنجره‌ی کانتکست هستند. total_input_tokens مجموعِ input_tokens، cache_creation_input_tokens و cache_read_input_tokens است؛ total_output_tokens توکن‌های خروجیِ آخرین پاسخ است. هر دو پیش از اولین پاسخِ API برابرِ 0اند.
  • مصرفِ تفکیکی به‌ازای هر مؤلفه (current_usage): همان تعدادِ توکن‌ها که بر اساسِ دسته تفکیک شده‌اند. این را وقتی استفاده کن که لازم داری hitهای کش را جدا از ورودیِ تازه داشته باشی.

شیءِ current_usage شامل این‌هاست:

  • input_tokens: توکن‌های ورودی در کانتکستِ فعلی
  • output_tokens: توکن‌های خروجیِ تولیدشده
  • cache_creation_input_tokens: توکن‌های نوشته‌شده در کش
  • cache_read_input_tokens: توکن‌های خوانده‌شده از کش

برای این‌که فیلدهای کش چه معنایی دارند و چطور محاسبه‌ی هزینه می‌شوند، بررسیِ کاراییِ کش را ببین.

فیلدِ used_percentage فقط از توکن‌های ورودی محاسبه می‌شود: input_tokens + cache_creation_input_tokens + cache_read_input_tokens. شاملِ output_tokens نمی‌شود.

اگر درصدِ کانتکست را به‌صورتِ دستی از current_usage محاسبه می‌کنی، از همان فرمولِ فقط-ورودی استفاده کن تا با used_percentage بخواند.

شیءِ current_usage پیش از اولین فراخوانیِ API در یک نشست null است، و دوباره بلافاصله پس از /compact تا وقتی فراخوانیِ API بعدی آن را دوباره پر کند.

این نمونه‌ها الگوهای رایجِ نوارِ وضعیت را نشان می‌دهند. برای استفاده از هر نمونه:

  1. اسکریپت را در فایلی مثل ~/.claude/statusline.sh (یا .py/.js) ذخیره کن
  2. اجراپذیرش کن: chmod +x ~/.claude/statusline.sh
  3. مسیر را به تنظیماتت اضافه کن

نمونه‌های Bash از jq برای تجزیه‌ی JSON استفاده می‌کنند. Python و Node.js تجزیه‌ی JSON توکار دارند.

مدلِ فعلی و مصرفِ پنجره‌ی کانتکست را با یک نوارِ پیشرفتِ بصری نمایش بده. هر اسکریپت JSON را از stdin می‌خواند، فیلدِ used_percentage را استخراج می‌کند، و یک نوارِ ۱۰کاراکتری می‌سازد که در آن بلوک‌های پُر (▓) نشان‌دهنده‌ی مصرف‌اند:

A status line showing model name and a progress bar with percentage
#!/bin/bash
# Read all of stdin into a variable
input=$(cat)
# Extract fields with jq, "// 0" provides fallback for null
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
# Build progress bar: printf -v creates a run of spaces, then
# ${var// /▓} replaces each space with a block character
BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
echo "[$MODEL] $BAR $PCT%"
#!/usr/bin/env python3
import json, sys
# json.load reads and parses stdin in one step
data = json.load(sys.stdin)
model = data['model']['display_name']
# "or 0" handles null values
pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
# String multiplication builds the bar
filled = pct * 10 // 100
bar = '▓' * filled + '░' * (10 - filled)
print(f"[{model}] {bar} {pct}%")
#!/usr/bin/env node
// Node.js reads stdin asynchronously with events
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
// Optional chaining (?.) safely handles null fields
const pct = Math.floor(data.context_window?.used_percentage || 0);
// String.repeat() builds the bar
const filled = Math.floor(pct * 10 / 100);
const bar = '▓'.repeat(filled) + '░'.repeat(10 - filled);
console.log(`[${model}] ${bar} ${pct}%`);
});

شاخه‌ی git را با نشانگرهای رنگیِ فایل‌های stage‌شده و تغییریافته نشان بده. این اسکریپت از کدهای فرارِ ANSI برای رنگ‌های ترمینال استفاده می‌کند: \033[32m سبز است، \033[33m زرد، و \033[0m به پیش‌فرض بازنشانی می‌کند.

A status line showing model, directory, git branch, and colored indicators for staged and modified files

هر اسکریپت بررسی می‌کند که آیا پوشه‌ی فعلی یک مخزنِ git است، فایل‌های stage‌شده و تغییریافته را می‌شمارد، و نشانگرهای رنگی نمایش می‌دهد:

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
GREEN='\033[32m'
YELLOW='\033[33m'
RESET='\033[0m'
if git rev-parse --git-dir > /dev/null 2>&1; then
BRANCH=$(git branch --show-current 2>/dev/null)
STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
GIT_STATUS=""
[ "$STAGED" -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
[ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS}${YELLOW}~${MODIFIED}${RESET}"
echo -e "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH $GIT_STATUS"
else
echo "[$MODEL] 📁 ${DIR##*/}"
fi
#!/usr/bin/env python3
import json, sys, subprocess, os
data = json.load(sys.stdin)
model = data['model']['display_name']
directory = os.path.basename(data['workspace']['current_dir'])
GREEN, YELLOW, RESET = '\033[32m', '\033[33m', '\033[0m'
try:
subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
staged_output = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
modified_output = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
staged = len(staged_output.split('\n')) if staged_output else 0
modified = len(modified_output.split('\n')) if modified_output else 0
git_status = f"{GREEN}+{staged}{RESET}" if staged else ""
git_status += f"{YELLOW}~{modified}{RESET}" if modified else ""
print(f"[{model}] 📁 {directory} | 🌿 {branch} {git_status}")
except:
print(f"[{model}] 📁 {directory}")
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const dir = path.basename(data.workspace.current_dir);
const GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RESET = '\x1b[0m';
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
let gitStatus = staged ? `${GREEN}+${staged}${RESET}` : '';
gitStatus += modified ? `${YELLOW}~${modified}${RESET}` : '';
console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} ${gitStatus}`);
} catch {
console.log(`[${model}] 📁 ${dir}`);
}
});

پیگیریِ هزینه و مدت‌زمان

Section titled “پیگیریِ هزینه و مدت‌زمان”

هزینه‌های API و زمانِ سپری‌شده‌ی نشستت را دنبال کن. فیلدِ cost.total_cost_usd هزینه‌ی تخمینیِ همه‌ی فراخوانی‌های API در نشستِ فعلی را انباشته می‌کند. فیلدِ cost.total_duration_ms کلِ زمانِ سپری‌شده از شروعِ نشست را اندازه می‌گیرد، در حالی که cost.total_api_duration_ms فقط زمانِ صرف‌شده برای انتظارِ پاسخ‌های API را دنبال می‌کند.

هر اسکریپت هزینه را به‌صورتِ واحدِ پول قالب‌بندی می‌کند و میلی‌ثانیه را به دقیقه و ثانیه تبدیل می‌کند:

A status line showing model name, session cost, and duration
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
COST_FMT=$(printf '$%.2f' "$COST")
DURATION_SEC=$((DURATION_MS / 1000))
MINS=$((DURATION_SEC / 60))
SECS=$((DURATION_SEC % 60))
echo "[$MODEL] 💰 $COST_FMT | ⏱️ ${MINS}m ${SECS}s"
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
model = data['model']['display_name']
cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
duration_sec = duration_ms // 1000
mins, secs = duration_sec // 60, duration_sec % 60
print(f"[{model}] 💰 ${cost:.2f} | ⏱️ {mins}m {secs}s")
#!/usr/bin/env node
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const cost = data.cost?.total_cost_usd || 0;
const durationMs = data.cost?.total_duration_ms || 0;
const durationSec = Math.floor(durationMs / 1000);
const mins = Math.floor(durationSec / 60);
const secs = durationSec % 60;
console.log(`[${model}] 💰 $${cost.toFixed(2)} | ⏱️ ${mins}m ${secs}s`);
});

اسکریپتت می‌تواند چند خط خروجی بدهد تا یک نمایشِ غنی‌تر بسازد. هر دستورِ echo یک ردیفِ جداگانه در ناحیه‌ی وضعیت تولید می‌کند.

A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line

این نمونه چند تکنیک را با هم ترکیب می‌کند: رنگ‌های آستانه‌محور (سبز زیرِ ۷۰٪، زرد ۷۰ تا ۸۹٪، قرمز ۹۰٪ به بالا)، یک نوارِ پیشرفت، و اطلاعاتِ شاخه‌ی git. هر دستورِ print یا echo یک ردیفِ جداگانه می‌سازد:

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
# Pick bar color based on context usage
if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
else BAR_COLOR="$GREEN"; fi
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
printf -v FILL "%${FILLED}s"; printf -v PAD "%${EMPTY}s"
BAR="${FILL// /█}${PAD// /░}"
MINS=$((DURATION_MS / 60000)); SECS=$(((DURATION_MS % 60000) / 1000))
BRANCH=""
git rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | 🌿 $(git branch --show-current 2>/dev/null)"
echo -e "${CYAN}[$MODEL]${RESET} 📁 ${DIR##*/}$BRANCH"
COST_FMT=$(printf '$%.2f' "$COST")
echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${YELLOW}${COST_FMT}${RESET} | ⏱️ ${MINS}m ${SECS}s"
#!/usr/bin/env python3
import json, sys, subprocess, os
data = json.load(sys.stdin)
model = data['model']['display_name']
directory = os.path.basename(data['workspace']['current_dir'])
cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
CYAN, GREEN, YELLOW, RED, RESET = '\033[36m', '\033[32m', '\033[33m', '\033[31m', '\033[0m'
bar_color = RED if pct >= 90 else YELLOW if pct >= 70 else GREEN
filled = pct // 10
bar = '█' * filled + '░' * (10 - filled)
mins, secs = duration_ms // 60000, (duration_ms % 60000) // 1000
try:
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True, stderr=subprocess.DEVNULL).strip()
branch = f" | 🌿 {branch}" if branch else ""
except:
branch = ""
print(f"{CYAN}[{model}]{RESET} 📁 {directory}{branch}")
print(f"{bar_color}{bar}{RESET} {pct}% | {YELLOW}${cost:.2f}{RESET} | ⏱️ {mins}m {secs}s")
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const dir = path.basename(data.workspace.current_dir);
const cost = data.cost?.total_cost_usd || 0;
const pct = Math.floor(data.context_window?.used_percentage || 0);
const durationMs = data.cost?.total_duration_ms || 0;
const CYAN = '\x1b[36m', GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RED = '\x1b[31m', RESET = '\x1b[0m';
const barColor = pct >= 90 ? RED : pct >= 70 ? YELLOW : GREEN;
const filled = Math.floor(pct / 10);
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
const mins = Math.floor(durationMs / 60000);
const secs = Math.floor((durationMs % 60000) / 1000);
let branch = '';
try {
branch = execSync('git branch --show-current', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
branch = branch ? ` | 🌿 ${branch}` : '';
} catch {}
console.log(`${CYAN}[${model}]${RESET} 📁 ${dir}${branch}`);
console.log(`${barColor}${bar}${RESET} ${pct}% | ${YELLOW}$${cost.toFixed(2)}${RESET} | ⏱️ ${mins}m ${secs}s`);
});

این نمونه یک لینکِ قابلِ کلیک به مخزنِ GitHubت می‌سازد. URLِ remoteِ git را می‌خواند، فرمتِ SSH را با sed به HTTPS تبدیل می‌کند، و نامِ مخزن را در کدهای فرارِ OSC 8 می‌پیچد. Cmd (روی macOS) یا Ctrl (روی Windows/Linux) را نگه دار و کلیک کن تا لینک در مرورگرت باز شود.

A status line showing a clickable link to a GitHub repository

هر اسکریپت URLِ remoteِ git را می‌گیرد، فرمتِ SSH را به HTTPS تبدیل می‌کند، و نامِ مخزن را در کدهای فرارِ OSC 8 می‌پیچد. نسخه‌ی Bash از printf '%b' استفاده می‌کند که فرارهای backslash را در شِل‌های مختلف مطمئن‌تر از echo -e تفسیر می‌کند:

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
# Convert git SSH URL to HTTPS
REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
if [ -n "$REMOTE" ]; then
REPO_NAME=$(basename "$REMOTE")
# OSC 8 format: \e]8;;URL\a then TEXT then \e]8;;\a
# printf %b interprets escape sequences reliably across shells
printf '%b' "[$MODEL] 🔗 \e]8;;${REMOTE}\a${REPO_NAME}\e]8;;\a\n"
else
echo "[$MODEL]"
fi
#!/usr/bin/env python3
import json, sys, subprocess, re, os
data = json.load(sys.stdin)
model = data['model']['display_name']
# Get git remote URL
try:
remote = subprocess.check_output(
['git', 'remote', 'get-url', 'origin'],
stderr=subprocess.DEVNULL, text=True
).strip()
# Convert SSH to HTTPS format
remote = re.sub(r'^git@github\.com:', 'https://github.com/', remote)
remote = re.sub(r'\.git$', '', remote)
repo_name = os.path.basename(remote)
# OSC 8 escape sequences
link = f"\033]8;;{remote}\a{repo_name}\033]8;;\a"
print(f"[{model}] 🔗 {link}")
except:
print(f"[{model}]")
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
try {
let remote = execSync('git remote get-url origin', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
// Convert SSH to HTTPS format
remote = remote.replace(/^git@github\.com:/, 'https://github.com/').replace(/\.git$/, '');
const repoName = path.basename(remote);
// OSC 8 escape sequences
const link = `\x1b]8;;${remote}\x07${repoName}\x1b]8;;\x07`;
console.log(`[${model}] 🔗 ${link}`);
} catch {
console.log(`[${model}]`);
}
});

مصرفِ محدودیتِ نرخِ اشتراکِ Claude.ai را در نوارِ وضعیت نمایش بده. شیءِ rate_limits شاملِ پنجره‌های five_hour (پنجره‌ی غلتانِ ۵ساعته) و seven_day (هفتگی) است. هر پنجره used_percentage (۰ تا ۱۰۰) و resets_at (ثانیه‌های Unix epoch وقتی پنجره بازنشانی می‌شود) را فراهم می‌کند.

این فیلد فقط برای مشترکانِ Claude.ai (Pro/Max) پس از اولین پاسخِ API حاضر است. هر اسکریپت فیلدِ غایب را به‌نرمی مدیریت می‌کند:

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
# "// empty" produces no output when rate_limits is absent
FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
LIMITS=""
[ -n "$FIVE_H" ] && LIMITS="5h: $(printf '%.0f' "$FIVE_H")%"
[ -n "$WEEK" ] && LIMITS="${LIMITS:+$LIMITS }7d: $(printf '%.0f' "$WEEK")%"
[ -n "$LIMITS" ] && echo "[$MODEL] | $LIMITS" || echo "[$MODEL]"
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
model = data['model']['display_name']
parts = []
rate = data.get('rate_limits', {})
five_h = rate.get('five_hour', {}).get('used_percentage')
week = rate.get('seven_day', {}).get('used_percentage')
if five_h is not None:
parts.append(f"5h: {five_h:.0f}%")
if week is not None:
parts.append(f"7d: {week:.0f}%")
if parts:
print(f"[{model}] | {' '.join(parts)}")
else:
print(f"[{model}]")
#!/usr/bin/env node
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const parts = [];
const fiveH = data.rate_limits?.five_hour?.used_percentage;
const week = data.rate_limits?.seven_day?.used_percentage;
if (fiveH != null) parts.push(`5h: ${Math.round(fiveH)}%`);
if (week != null) parts.push(`7d: ${Math.round(week)}%`);
console.log(parts.length ? `[${model}] | ${parts.join(' ')}` : `[${model}]`);
});

کش‌کردنِ عملیاتِ پرهزینه

Section titled “کش‌کردنِ عملیاتِ پرهزینه”

اسکریپتِ نوارِ وضعیتت در نشست‌های فعال مکرراً اجرا می‌شود. دستورهایی مثل git status یا git diff می‌توانند کند باشند، به‌خصوص در مخزن‌های بزرگ. این نمونه اطلاعاتِ git را در یک فایلِ موقت کش می‌کند و فقط هر ۵ ثانیه یک‌بار تازه‌اش می‌کند.

نامِ فایلِ کش باید در طولِ فراخوانی‌های نوارِ وضعیت در یک نشست پایدار باشد، اما بین نشست‌ها یکتا باشد تا نشست‌های هم‌زمان در مخزن‌های مختلف وضعیتِ git کش‌شده‌ی یکدیگر را نخوانند. شناسه‌های مبتنی‌بر‌فرایند مثل $$، os.getpid() یا process.pid در هر فراخوانی تغییر می‌کنند و کش را بی‌اثر می‌کنند. به‌جایش از session_id در ورودیِ JSON استفاده کن: در طولِ عمرِ یک نشست پایدار و به‌ازای هر نشست یکتاست.

هر اسکریپت پیش از اجرای دستورهای git بررسی می‌کند که آیا فایلِ کش ناموجود یا قدیمی‌تر از ۵ ثانیه است:

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
SESSION_ID=$(echo "$input" | jq -r '.session_id')
CACHE_FILE="/tmp/statusline-git-cache-$SESSION_ID"
CACHE_MAX_AGE=5 # seconds
cache_is_stale() {
[ ! -f "$CACHE_FILE" ] || \
# stat -f %m is macOS, stat -c %Y is Linux
[ $(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
}
if cache_is_stale; then
if git rev-parse --git-dir > /dev/null 2>&1; then
BRANCH=$(git branch --show-current 2>/dev/null)
STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
echo "$BRANCH|$STAGED|$MODIFIED" > "$CACHE_FILE"
else
echo "||" > "$CACHE_FILE"
fi
fi
IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"
if [ -n "$BRANCH" ]; then
echo "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH +$STAGED ~$MODIFIED"
else
echo "[$MODEL] 📁 ${DIR##*/}"
fi
#!/usr/bin/env python3
import json, sys, subprocess, os, time
data = json.load(sys.stdin)
model = data['model']['display_name']
directory = os.path.basename(data['workspace']['current_dir'])
session_id = data['session_id']
CACHE_FILE = f"/tmp/statusline-git-cache-{session_id}"
CACHE_MAX_AGE = 5 # seconds
def cache_is_stale():
if not os.path.exists(CACHE_FILE):
return True
return time.time() - os.path.getmtime(CACHE_FILE) > CACHE_MAX_AGE
if cache_is_stale():
try:
subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
staged = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
modified = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
staged_count = len(staged.split('\n')) if staged else 0
modified_count = len(modified.split('\n')) if modified else 0
with open(CACHE_FILE, 'w') as f:
f.write(f"{branch}|{staged_count}|{modified_count}")
except:
with open(CACHE_FILE, 'w') as f:
f.write("||")
with open(CACHE_FILE) as f:
branch, staged, modified = f.read().strip().split('|')
if branch:
print(f"[{model}] 📁 {directory} | 🌿 {branch} +{staged} ~{modified}")
else:
print(f"[{model}] 📁 {directory}")
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const dir = path.basename(data.workspace.current_dir);
const sessionId = data.session_id;
const CACHE_FILE = `/tmp/statusline-git-cache-${sessionId}`;
const CACHE_MAX_AGE = 5; // seconds
const cacheIsStale = () => {
if (!fs.existsSync(CACHE_FILE)) return true;
return (Date.now() / 1000) - fs.statSync(CACHE_FILE).mtimeMs / 1000 > CACHE_MAX_AGE;
};
if (cacheIsStale()) {
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
fs.writeFileSync(CACHE_FILE, `${branch}|${staged}|${modified}`);
} catch {
fs.writeFileSync(CACHE_FILE, '||');
}
}
const [branch, staged, modified] = fs.readFileSync(CACHE_FILE, 'utf8').trim().split('|');
if (branch) {
console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} +${staged} ~${modified}`);
} else {
console.log(`[${model}] 📁 ${dir}`);
}
});

روی Windows، Claude Code دستورهای نوارِ وضعیت را وقتی Git Bash نصب باشد از طریقِ Git Bash اجرا می‌کند، یا وقتی Git Bash نباشد از طریقِ PowerShell.

Git Bash بک‌اسلش‌های بدونِ کوتیشن را به‌عنوانِ کاراکترهای فرار تلقی می‌کند، پس مسیری به سبکِ Windows مثل C:\Users\username\script.mjs با جداکننده‌های حذف‌شده به اجراکننده‌ی اسکریپت می‌رسد و دستور بدونِ خطای نمایان شکست می‌خورد. مسیرهای فایل را در رشته‌ی command با اسلشِ رو به جلو بنویس، همان‌طور که در نمونه‌های زیر نشان داده شده. کوتاه‌نوشتِ ~ هم کار می‌کند و به پوشه‌ی خانگیِ Windowsت گسترش می‌یابد.

برای اجرای یک اسکریپتِ PowerShell به‌عنوانِ نوارِ وضعیتت، آن را از طریقِ powershell فراخوانی کن. این چه Claude Code دستور را از طریقِ Git Bash مسیریابی کند و چه از طریقِ PowerShell، کار می‌کند:

{
"statusLine": {
"type": "command",
"command": "powershell -NoProfile -File C:/Users/username/.claude/statusline.ps1"
}
}
Terminal window
$input_json = $input | Out-String | ConvertFrom-Json
$cwd = $input_json.cwd
$model = $input_json.model.display_name
$used = $input_json.context_window.used_percentage
$dirname = Split-Path $cwd -Leaf
if ($used) {
Write-Host "$dirname [$model] ctx: $used%"
} else {
Write-Host "$dirname [$model]"
}

یا، وقتی Git Bash نصب باشد، یک اسکریپتِ Bash را مستقیم اجرا کن:

{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
#!/usr/bin/env bash
input=$(cat)
cwd=$(echo "$input" | grep -o '"cwd":"[^"]*"' | cut -d'"' -f4)
model=$(echo "$input" | grep -o '"display_name":"[^"]*"' | cut -d'"' -f4)
dirname="${cwd##*[/\\]}"
echo "$dirname [$model]"

نوارهای وضعیتِ ساب‌ایجنت

Section titled “نوارهای وضعیتِ ساب‌ایجنت”

تنظیمِ subagentStatusLine بدنه‌ی یک ردیفِ سفارشی را برای هر ساب‌ایجنت که در پنلِ ایجنت زیرِ پرامپت نشان داده می‌شود رندر می‌کند. از آن استفاده کن تا ردیفِ پیش‌فرضِ name · description · token count را با قالب‌بندیِ خودت جایگزین کنی.

{
"subagentStatusLine": {
"type": "command",
"command": "~/.claude/subagent-statusline.sh"
}
}

این دستور یک‌بار در هر تیکِ تازه‌سازی اجرا می‌شود، با همه‌ی ردیف‌های ساب‌ایجنتِ قابلِ‌مشاهده که به‌صورتِ یک شیءِ JSON واحد روی stdin منتقل می‌شوند. ورودی شاملِ فیلدهای پایه‌ی hook به‌علاوه‌ی columns (عرضِ قابلِ‌استفاده‌ی ردیف) و یک آرایه‌ی tasks است، که در آن هر task این‌ها را دارد: id، name، type، status، description، label، startTime، tokenCount، tokenSamples و cwd.

برای هر ردیفی که می‌خواهی بازنویسی کنی، یک خطِ JSON روی stdout بنویس، به شکلِ {"id": "<task id>", "content": "<row body>"}. رشته‌ی content همان‌طور که هست رندر می‌شود، از جمله رنگ‌های ANSI و hyperlinkهای OSC 8. idِ یک task را حذف کن تا رندرِ پیش‌فرضِ آن ردیف حفظ شود؛ یک رشته‌ی contentِ خالی منتشر کن تا آن را پنهان کنی.

همان دروازه‌های اعتماد (trust) و disableAllHooks که برای statusLine اعمال می‌شوند، اینجا هم اعمال می‌شوند. پلاگین‌ها می‌توانند یک subagentStatusLineِ پیش‌فرض را در settings.jsonِ خودشان عرضه کنند.

  • با ورودیِ ساختگی تست کن: echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test-session-abc"}' | ./statusline.sh
  • خروجی را کوتاه نگه دار: نوارِ وضعیت عرضِ محدودی دارد، پس خروجیِ طولانی ممکن است بریده شود یا به‌شکلِ ناجوری شکسته شود
  • عملیاتِ کند را کش کن: اسکریپتت در نشست‌های فعال مکرراً اجرا می‌شود، پس دستورهایی مثل git status می‌توانند باعثِ کندی شوند. برای مدیریتِ این موضوع، نمونه‌ی کش‌کردن را ببین.

پروژه‌های جامعه‌ای مثل ccstatusline و starship-claude پیکربندی‌های ازپیش‌ساخته با تم‌ها و قابلیت‌های بیشتر فراهم می‌کنند.

نوارِ وضعیت ظاهر نمی‌شود

  • بررسی کن که اسکریپتت اجراپذیر است: chmod +x ~/.claude/statusline.sh
  • بررسی کن که اسکریپتت روی stdout خروجی می‌دهد، نه stderr
  • اسکریپتت را به‌صورتِ دستی اجرا کن تا مطمئن شوی خروجی تولید می‌کند
  • روی Windows با Git Bashِ نصب‌شده، بک‌اسلش‌های مسیرِ command احتمالاً پیش از اجرای اسکریپت به‌عنوانِ کاراکترهای فرار مصرف می‌شوند. در مسیر از اسلشِ رو به جلو استفاده کن. پیکربندیِ Windows را ببین.
  • اگر disableAllHooks در تنظیماتت روی true باشد، نوارِ وضعیت هم غیرفعال می‌شود. این تنظیم را حذف کن یا روی false بگذار تا دوباره فعال شود.
  • claude --debug را اجرا کن تا کدِ خروج و stderrِ اولین فراخوانیِ نوارِ وضعیت در یک نشست لاگ شود
  • از Claude بخواه فایلِ تنظیماتت را بخواند و دستورِ statusLine را مستقیم اجرا کند تا خطاها رو بیاید

نوارِ وضعیت -- یا مقادیرِ خالی نشان می‌دهد

  • فیلدها ممکن است پیش از تکمیلِ اولین پاسخِ API برابرِ null باشند
  • مقادیرِ null را در اسکریپتت با جایگزین‌هایی مثل // 0 در jq مدیریت کن
  • اگر مقادیر پس از چند پیام همچنان خالی ماندند، Claude Code را دوباره راه‌اندازی کن

درصدِ کانتکست مقادیرِ غیرمنتظره نشان می‌دهد

  • برای ساده‌ترین وضعیتِ دقیقِ کانتکست از used_percentage استفاده کن
  • درصدِ کانتکست ممکن است به‌خاطرِ زمانِ محاسبه‌ی هر کدام، با خروجیِ /context فرق کند

لینک‌های OSC 8 قابلِ کلیک نیستند

  • بررسی کن که ترمینالت از hyperlinkهای OSC 8 پشتیبانی می‌کند (iTerm2، Kitty، WezTerm)

  • Terminal.app از لینک‌های قابلِ کلیک پشتیبانی نمی‌کند

  • اگر متنِ لینک ظاهر می‌شود ولی قابلِ کلیک نیست، شاید Claude Code پشتیبانی از hyperlink را در ترمینالت تشخیص نداده باشد. این معمولاً Windows Terminal و سایرِ شبیه‌سازهایی که در فهرستِ تشخیصِ خودکار نیستند را درگیر می‌کند. متغیرِ محیطیِ FORCE_HYPERLINK را تنظیم کن تا پیش از راه‌اندازیِ Claude Code تشخیص را بازنویسی کند:

    Terminal window
    FORCE_HYPERLINK=1 claude

    در PowerShell، ابتدا متغیر را در نشستِ فعلی تنظیم کن:

    Terminal window
    $env:FORCE_HYPERLINK = "1"; claude
  • نشست‌های SSH و tmux ممکن است بسته به پیکربندی توالی‌های OSC را حذف کنند

  • اگر توالی‌های فرار به‌صورتِ متنِ خام مثل \e]8;; ظاهر می‌شوند، برای مدیریتِ مطمئن‌ترِ فرار به‌جای echo -e از printf '%b' استفاده کن

خرابیِ نمایش با توالی‌های فرار

  • توالی‌های فرارِ پیچیده (رنگ‌های ANSI، لینک‌های OSC 8) گاهی می‌توانند اگر با سایرِ به‌روزرسانی‌های رابطِ کاربری هم‌پوشانی پیدا کنند، باعثِ خروجیِ درهم شوند
  • اگر متنِ خراب دیدی، اسکریپتت را به خروجیِ متنِ ساده ساده‌سازی کن
  • نوارهای وضعیتِ چندخطی با کدهای فرار بیشتر از متنِ سادهٔ تک‌خطی در معرضِ مشکلاتِ رندر هستند

اعتماد به فضای کاری لازم است

  • دستورِ نوارِ وضعیت فقط در صورتی اجرا می‌شود که پنجره‌ی اعتماد به فضای کاری (workspace trust) را برای پوشه‌ی فعلی پذیرفته باشی. چون statusLine یک دستورِ شِل اجرا می‌کند، به همان پذیرشِ اعتمادی نیاز دارد که hookها و سایرِ تنظیماتِ اجراکننده‌ی شِل لازم دارند.
  • اگر اعتماد پذیرفته نشده باشد، به‌جای خروجیِ نوارِ وضعیتت اعلانِ statusline skipped · restart to fix را می‌بینی. Claude Code را دوباره راه‌اندازی کن و پرامپتِ اعتماد را بپذیر تا فعال شود.

خطا یا گیرکردنِ اسکریپت

  • اسکریپت‌هایی که با کدِ غیرصفر خارج می‌شوند یا خروجی تولید نمی‌کنند باعثِ خالی‌شدنِ نوارِ وضعیت می‌شوند
  • اسکریپت‌های کند تا وقتی کامل شوند به‌روزرسانیِ نوارِ وضعیت را مسدود می‌کنند. اسکریپت‌ها را سریع نگه دار تا از خروجیِ کهنه جلوگیری شود.
  • اگر در حینِ اجرای یک اسکریپتِ کند یک به‌روزرسانیِ جدید رخ بدهد، اسکریپتِ در جریان لغو می‌شود
  • اسکریپتت را پیش از پیکربندی، مستقلاً با ورودیِ ساختگی تست کن

اعلان‌ها ردیفِ نوارِ وضعیت را به اشتراک می‌گذارند

  • اعلان‌های سیستمی مثل خطاهای سرورِ MCP و به‌روزرسانی‌های خودکار در سمتِ راستِ همان ردیفِ نوارِ وضعیتت نمایش داده می‌شوند. اعلان‌های گذرا مثل هشدارِ کم‌بودنِ کانتکست هم در همین ناحیه گردش می‌کنند.
  • فعال‌کردنِ حالتِ verbose یک شمارنده‌ی توکن به این ناحیه اضافه می‌کند
  • روی ترمینال‌های باریک، این اعلان‌ها ممکن است خروجیِ نوارِ وضعیتت را ببُرند