Khám phá Learn Stream About Jokes
INSIDER Tony's Friends — Insider — ~2 playbook/tuần, Discord riêng, tài nguyên dựng sẵn Tham gia →
Bài viết

Status line trong Claude Code: một dải màn hình nhỏ giúp mình không lạc hướng

Một mảnh màn hình rất nhỏ ở đáy khung chat, nếu cấu hình tốt, giúp mình giữ định hướng giữa một session dài mà không phải dừng lại để kiểm tra.

Status line trong Claude Code: một dải màn hình nhỏ giúp mình không lạc hướng

Status line trong Claude Code: một dải màn hình nhỏ giúp mình không lạc hướng

Khi làm việc lâu với một AI coding agent, mình nhận ra thứ làm mình mệt không chỉ là code khó. Có những thông tin rất nhỏ nhưng lại gánh quyết định khá nặng: mình đang dùng model nào, effort đang ở mức nào, context window còn bao nhiêu, rate limit đã dùng tới đâu. Nếu phải mở settings, hỏi lại agent, hoặc đoán bằng cảm giác thì dòng suy nghĩ bị đứt. Mình không cần một dashboard lớn, mình chỉ cần một chỗ luôn nhìn thấy nhưng không chen ngang.

Status line là đúng kiểu chỗ đó: một mảnh màn hình rất nhỏ, nếu cấu hình tốt, nó giữ mình có định hướng mà không phá flow. Nó không thay mình suy nghĩ, cũng không làm session “thông minh hơn”, nhưng nó giảm số lần mình phải tự hỏi “ủa mình đang ở đâu trong phiên này?”. Với mình, điểm hay nằm ở sự khiêm tốn của nó. Nó chỉ hiện ra ở cuối khung chat, nhưng những gì nó nhắc lại thường là những thứ mình hay quên nhất.


Status line hóa ra chỉ là một command mình kiểm soát

Status line trong Claude Code là dải thông tin ở đáy khung chat. Cách hoạt động của nó khá thẳng: trong settings.json, mình khai báo một statusLinetypecommand, rồi trỏ tới một script hoặc command nào đó. Mỗi lần Claude Code render lại giao diện, nó gọi command đó, đưa thông tin của session vào qua stdin dưới dạng JSON, và bất cứ gì command in ra stdout sẽ trở thành status line. Nói cách khác, status line không phải một template cứng; nó là một cái hook nhỏ để mình tự quyết định mình muốn thấy gì.

Cách nghĩ này làm mình dễ chịu hơn, vì mình không phải chờ một UI option đúng ý. Nếu mình muốn hiện cwd, branch, model, context window, rate limit, hoặc tên session, mình parse JSON rồi in ra. Nếu mình muốn status line thật tối giản, mình chỉ giữ lại hai ba segment có ích nhất. Và nếu một segment bắt đầu làm mình nhiễu, mình xóa nó khỏi script, không cần tranh luận với một hệ thống cấu hình phức tạp.

Điểm quan trọng là command này nên chạy nhanh và chịu lỗi tốt. Status line được gọi thường xuyên, nên script không nên làm những việc nặng như gọi API, scan cả repo, hay chạy command có thể treo. Mình thường chỉ đọc JSON, gọi git rất nhẹ để lấy branch, rồi in một dòng có màu bằng ANSI. Nếu có lỗi, tốt nhất là in một thông báo ngắn hoặc bỏ qua segment đó, vì status line hư không nên kéo cả phiên làm việc xuống.


Mình chỉ muốn hiện những gì làm đổi quyết định

Một status line đã cấu hình: cwd, branch, model, context window, rate limit

Claude Code pipe vào script một JSON object khá giàu thông tin. Những field mình thấy hữu dụng nhất là .cwd, .model.display_name, .effort.level, .context_window.remaining_percentage, .rate_limits.five_hour.used_percentage, và .rate_limits.seven_day.used_percentage. .cwd giúp mình biết agent đang đứng ở repo hoặc thư mục nào, nhất là khi mình mở nhiều project. .model.display_name.effort.level giúp mình hiểu chất lượng và kiểu suy luận mình đang yêu cầu, vì cùng một prompt nhưng model và effort khác nhau có thể cho ra hành vi khác nhau.

Với context window, mình thích nhìn .context_window.remaining_percentage hơn là đợi tới lúc agent bắt đầu quên chi tiết. Nếu còn nhiều context, mình có thể tiếp tục đào sâu trong cùng session. Nếu còn ít, mình sẽ cân nhắc dừng lại, yêu cầu tóm tắt, hoặc chuyển sang một bước có phạm vi hẹp hơn. Rate limit cũng tương tự: .rate_limits.five_hour.used_percentage.rate_limits.seven_day.used_percentage không phải để mình lo lắng liên tục, mà để mình biết khi nào nên bớt thử nghiệm lan man. Một glance đủ tốt hơn là một bất ngờ giữa lúc đang cần xử lý việc chính.

Có vài field không phải lúc nào cũng có, nhưng đáng biết. .workspace.git_worktree cho biết mình đang ở worktree nào, hữu ích nếu bạn dùng nhiều worktree cho nhiều nhánh song song. .session_name giúp định danh phiên, nhất là khi bạn đặt tên session theo task. .cost.total_cost_usd có thể hữu dụng nếu bạn đang tự theo dõi chi phí, còn .pr.number.pr.review_state có ý nghĩa khi session gắn với một pull request. Mình không nhất thiết hiện tất cả, vì nguyên tắc của mình là: một segment chỉ xứng đáng chiếm chỗ nếu nó làm thay đổi một quyết định mà lẽ ra mình phải đoán.


Cài đặt nhanh: để Claude Code tự làm cho bạn

Cách nhẹ nhất, và cũng đúng tinh thần của công cụ này, là nhờ chính Claude Code dựng status line giúp bạn. Bạn không cần nhớ cú pháp settings.json hay tự viết script; chỉ cần mở Claude Code rồi dán nguyên đoạn prompt dưới đây vào. Mình viết prompt đủ chi tiết để agent biết cần hiện gì, tô màu ra sao, và tránh sẵn hai cái bẫy mình sẽ kể ở phần sau.

Set up a custom status line for me in Claude Code.

Create the script at ~/.claude/statusline-command.sh and wire it into
~/.claude/settings.json under the "statusLine" key (type: "command"),
WITHOUT removing any of my existing settings.

The script reads the session JSON from stdin and prints one colored line
with these segments, separated by a dim " | ":
- cwd (.cwd) in dim grey
- current git branch (from `git -C "$cwd"`) in bright cyan + bold
- model (.model.display_name) in dim; append [effort] (.effort.level)
  only when effort is not "medium"
- ctx: N% (.context_window.remaining_percentage) — green if >80,
  yellow if >70, red otherwise
- 5h: N% (.rate_limits.five_hour.used_percentage) — bright yellow if >=70, else dim
- 7d: N% (.rate_limits.seven_day.used_percentage) — bright yellow if >=70, else dim
Skip any segment whose field is empty.

Two things to get right:
1. Print with `printf '%b\n' "$line"`, NOT `printf "$line\n"` — the "%" in
   "ctx: 62%" would be read as a format specifier and blank the line.
2. Resolve jq with a PATH fallback (/opt/homebrew/bin, /usr/local/bin,
   /usr/bin) because GUI launches don't inherit the shell PATH.

Then show me the final script and the settings.json change.

Sau khi agent chạy xong, bạn mở lại Claude Code một lần là status line xuất hiện. Nếu muốn đổi sau này, bạn cứ nói tiếp với agent, kiểu “thêm session_name vào status line” hoặc “đổi ngưỡng màu của ctx”, thay vì sửa tay.


Cài đặt nhanh: một dòng dán vào terminal

Nếu bạn quen terminal hơn, mình gói nguyên cả script và bước wire vào một dòng paste thẳng vào terminal. Nó tạo file script trong ~/.claude/, rồi thêm khối statusLine vào settings.json của bạn mà không xóa các cấu hình sẵn có (phần merge dùng python3, máy Mac nào cũng có). Chạy xong, mở lại Claude Code là thấy.

D="$HOME/.claude"; mkdir -p "$D"; cat > "$D/statusline-command.sh" <<'SL'
#!/usr/bin/env bash
input=$(cat); JQ=$(command -v jq||true); for c in /opt/homebrew/bin/jq /usr/local/bin/jq /usr/bin/jq; do [ -n "$JQ" ]&&break; [ -x "$c" ]&&JQ="$c"; done; [ -z "$JQ" ]&&{ printf "%b\n" "\033[31mstatusline: jq not found\033[0m"; exit 0; }; g(){ echo "$input"|"$JQ" -r "$1 // empty"; }; cwd=$(g .cwd); model=$(g .model.display_name); effort=$(g .effort.level); rem=$(g .context_window.remaining_percentage); h5=$(g .rate_limits.five_hour.used_percentage); d7=$(g .rate_limits.seven_day.used_percentage); br=""; [ -n "$cwd" ]&&br=$(git -C "$cwd" --no-optional-locks symbolic-ref --short HEAD 2>/dev/null); dim="\033[2m"; b="\033[1m"; y="\033[33m"; r="\033[31m"; gr="\033[32m"; cy="\033[36m"; x="\033[0m"; s="${dim} | ${x}"; L="${dim}${cwd}${x}"; [ -n "$br" ]&&L="${L}${s}${b}${cy}${br}${x}"; if [ -n "$model" ]; then m="${dim}${model}${x}"; [ -n "$effort" ]&&[ "$effort" != medium ]&&m="${m} ${dim}[${effort}]${x}"; L="${L}${s}${m}"; fi; if [ -n "$rem" ]; then n=$(printf "%.0f" "$rem"); if [ "$n" -gt 80 ]; then c=$gr; elif [ "$n" -gt 70 ]; then c=$y; else c=$r; fi; L="${L}${s}${c}ctx: ${n}%${x}"; fi; if [ -n "$h5" ]; then n=$(printf "%.0f" "$h5"); [ "$n" -ge 70 ]&&c="${b}${y}"||c=$dim; L="${L}${s}${c}5h: ${n}%${x}"; fi; if [ -n "$d7" ]; then n=$(printf "%.0f" "$d7"); [ "$n" -ge 70 ]&&c="${b}${y}"||c=$dim; L="${L}${s}${c}7d: ${n}%${x}"; fi; printf "%b\n" "$L"
SL
chmod +x "$D/statusline-command.sh"; F="$D/settings.json"; [ -f "$F" ]||echo "{}">"$F"; python3 -c "import json,sys,os; p=sys.argv[1]; d=json.load(open(p)) if os.path.getsize(p) else {}; d['statusLine']={'type':'command','command':'bash '+os.path.expanduser('~/.claude/statusline-command.sh')}; json.dump(d,open(p,'w'),indent=2)" "$F"; echo "Status line installed. Mở lại Claude Code để thấy."

Hai cách trên đều cho ra cùng một kết quả, nên bạn chọn cái nào thấy thoải mái cũng được. Nói thật là mình vẫn khuyên đọc hết phần dưới một lần, vì lúc muốn sửa segment hoặc đổi màu, hiểu script vẫn dễ hơn là dán lại một đoạn lạ.


Làm thủ công để hiểu từng phần

Nếu muốn tự tay dựng, có hai phần để wire status line. Phần thứ nhất nằm trong settings.json: mình khai báo statusLine là một command, rồi trỏ tới script. Ví dụ dưới đây dùng đường dẫn minh họa /Users/ban/.claude/statusline-command.sh; khi dùng thật, bạn đổi thành đường dẫn trên máy của bạn.

"statusLine": {
  "type": "command",
  "command": "bash /Users/ban/.claude/statusline-command.sh"
}

Phần thứ hai là script. Script này đọc toàn bộ stdin, dùng jq để lấy từng field, lấy branch hiện tại bằng git -C "$cwd", rồi ghép thành một dòng có màu. Mình giữ cách in khá dè dặt: cwd dim, branch cyan và bold, model dim, context đổi màu theo ngưỡng, còn rate limit chỉ nổi bật khi đã dùng từ 70% trở lên. Effort cũng chỉ hiện khi khác medium, vì nếu lúc nào cũng hiện default thì nó chiếm chỗ mà không giúp mình quyết định gì thêm.

#!/usr/bin/env bash
# Claude Code status line
# Segments: cwd | branch | model [effort] | ctx X% | 5h: X% | 7d: X%

input=$(cat)

# jq with PATH fallback (GUI launches don't inherit shell PATH)
JQ=$(command -v jq || true)
for cand in /opt/homebrew/bin/jq /usr/local/bin/jq /usr/bin/jq; do
  [ -n "$JQ" ] && break
  [ -x "$cand" ] && JQ="$cand"
done
[ -z "$JQ" ] && { printf '%b\n' '\033[31mstatusline: jq not found\033[0m'; exit 0; }

cwd=$(echo "$input"    | "$JQ" -r '.cwd // empty')
model=$(echo "$input"  | "$JQ" -r '.model.display_name // empty')
effort=$(echo "$input" | "$JQ" -r '.effort.level // empty')
remaining=$(echo "$input" | "$JQ" -r '.context_window.remaining_percentage // empty')
five_pct=$(echo "$input"  | "$JQ" -r '.rate_limits.five_hour.used_percentage // empty')
week_pct=$(echo "$input"  | "$JQ" -r '.rate_limits.seven_day.used_percentage // empty')

branch=""
[ -n "$cwd" ] && branch=$(git -C "$cwd" --no-optional-locks symbolic-ref --short HEAD 2>/dev/null)

dim='\033[2m'; bright='\033[1m'; yellow='\033[33m'; red='\033[31m'
green='\033[32m'; cyan='\033[36m'; reset='\033[0m'
sep="${dim} | ${reset}"

line="${dim}${cwd}${reset}"
[ -n "$branch" ] && line="${line}${sep}${bright}${cyan}${branch}${reset}"

if [ -n "$model" ]; then
  seg="${dim}${model}${reset}"
  [ -n "$effort" ] && [ "$effort" != "medium" ] && seg="${seg} ${dim}[${effort}]${reset}"
  line="${line}${sep}${seg}"
fi

if [ -n "$remaining" ]; then
  r=$(printf '%.0f' "$remaining")
  if   [ "$r" -gt 80 ]; then c="${green}"
  elif [ "$r" -gt 70 ]; then c="${yellow}"
  else c="${red}"; fi
  line="${line}${sep}${c}ctx: ${r}%${reset}"
fi

if [ -n "$five_pct" ]; then
  u=$(printf '%.0f' "$five_pct")
  [ "$u" -ge 70 ] && c="${bright}${yellow}" || c="${dim}"
  line="${line}${sep}${c}5h: ${u}%${reset}"
fi

if [ -n "$week_pct" ]; then
  w=$(printf '%.0f' "$week_pct")
  [ "$w" -ge 70 ] && c="${bright}${yellow}" || c="${dim}"
  line="${line}${sep}${c}7d: ${w}%${reset}"
fi

printf '%b\n' "$line"

Có vài quyết định nhỏ trong script này mình thấy đáng nói. Context còn trên 80% thì xanh, trên 70% thì vàng, còn thấp hơn thì đỏ; ngưỡng này không phải chân lý, nhưng nó phản ánh cách mình dùng session: trên 80% thì còn thoải mái, quanh 70% thì bắt đầu để ý, dưới đó thì nên cẩn thận với task dài. Rate limit thì mình không tô màu mạnh khi còn thấp, vì nếu nó lúc nào cũng nổi bật thì mắt mình sẽ học cách bỏ qua. Còn branch được lấy từ git thay vì JSON vì mình muốn nó đúng theo cwd, kể cả khi metadata workspace không có field riêng phù hợp.


Hai cái bẫy làm mình tưởng status line bị hư

Cái bẫy đầu tiên là printf. Lúc đầu mình có kiểu in tưởng vô hại như printf "$line\n", và nó chạy ổn cho tới khi trong line có dấu phần trăm, ví dụ ctx: 62%. Với printf, chuỗi đầu tiên là format string, nên % trong dữ liệu bị hiểu như format specifier. Kết quả là terminal báo lỗi, line bị cắt, và trong Claude Code mình chỉ thấy status line trống hoặc thiếu đoạn. Cách sửa đúng là printf '%b\n' "$line": format string được cố định, nội dung được truyền như dữ liệu, trong khi ANSI escapes vẫn được expand.

Cái bẫy thứ hai là jqPATH. Nếu Claude Code được mở từ GUI thay vì từ shell, nó có thể không inherit đúng shell PATH của bạn. Lúc đó jq có cài trên máy nhưng script vẫn không tìm thấy, từng segment parse JSON sẽ rỗng, và nhìn từ UI thì mọi thứ giống như status line không nhận được data. Vì vậy script ở trên không chỉ dùng command -v jq, mà còn thử các đường dẫn phổ biến như /opt/homebrew/bin/jq, /usr/local/bin/jq, và /usr/bin/jq. Nói thật là đây là kiểu lỗi làm mình mất thời gian vì nó không ồn ào; một fallback nhỏ giúp script đỡ phụ thuộc vào cách app được launch.

Mình cũng học được rằng status line nên fail nhẹ. Nếu không có jq, script in statusline: jq not found màu đỏ rồi exit 0, thay vì exit lỗi. Nếu không lấy được branch, nó chỉ bỏ qua branch. Nếu field nào không tồn tại, jq -r '.field // empty' trả về rỗng, và segment đó không được thêm vào line. Cách này làm status line phù hợp với nhiều loại session hơn, vì không phải session nào cũng có PR, cost, worktree, hay đầy đủ rate limit metadata.


Một dải nhỏ, nhưng làm phiên dài bớt mơ hồ

Điều mình thích ở status line là nó không đòi mình đổi cách làm việc. Nó chỉ đặt vài sự thật quan trọng vào đúng chỗ mình đã nhìn sẵn, để khi cần thì mình liếc xuống là thấy, không phải dừng lại hỏi hay đoán. Khác biệt không nằm ở một khoảnh khắc lớn. Nó nằm ở nhiều lần liếc rất nhanh rồi quay lại việc đang làm, cộng dồn qua cả một phiên.

Mình chưa chắc status line của mình là cấu hình tối ưu cho mọi người. Có người sẽ muốn thêm session_name, có người muốn hiện cost.total_cost_usd, có người làm nhiều PR chắc sẽ muốn .pr.number.pr.review_state luôn nằm đó. Với mình, bản tốt là bản đủ ít để không thành nhiễu, nhưng đủ đúng để giúp mình ra quyết định. Nếu bạn tự làm status line cho mình, bạn sẽ đặt segment nào lên đó, và thông tin nào bạn hay mất dấu nhất trong một session dài?

#claude-code #ai-workflows #terminal #productivity #config

Bài viết liên quan

Khi video trở thành perception layer cho auto-edit

idea

Giá trị thật của video-analyzer không phải biến video thành thứ để search, mà thành signal có timestamp để một pipeline khác tự ra quyết định cắt. video-analyzer lo perception, Claude Code lo judgment, ffmpeg lo execution.

Canary: lớp verify cho autonomous coding

idea

Một autonomous harness lo việc build. Nhưng ai kiểm tra cái nó vừa build, và lần sau mình chạy lại bằng gì? Canary là lớp QA biến mỗi lần agent thử thành một Playwright script chạy lại được.

0:00

Chia sẻ ảnh

Bắt đầu gõ để tìm kiếm...