把模型、目錄、git 狀態、訊息數、上下文使用率、5h/7d 額度、本 session 花費,全部塞進 terminal 底部兩行。 一眼掌握寫到哪、用多少、還剩多少,跑得久不會超載。
分享自 領先時代數位 · 老K從左到右依序:
Opus 4.7 · high* 代表有未 commit 的變更+128/-42⊕ 3顏色閾值:<50% 綠 / 50–79% 黃 / ≥80% 紅。
解析 stdin JSON 用的,沒裝這個整支會壞掉。
brew install jq
Linux 用 apt install jq 或 yum install jq
下面那塊紅褐色 code block,按右上角「複製」整段貼到檔案裡,然後給執行權限:
chmod +x ~/.claude/statusline.sh
{
"statusLine": {
"type": "command",
"command": "bash /Users/你的帳號/.claude/statusline.sh"
}
}
路徑改自己的,存檔後重開 Claude Code 就有了。
點右上角「複製腳本」,貼到 ~/.claude/statusline.sh。
#!/bin/bash
# ╔══════════════════════════════════════════════════════════════════╗
# ║ Claude Code Powerline Statusline ║
# ║ A full-featured, two-line Powerline statusline for Claude Code ║
# ║ Requires: bash, jq, git (optional), bc (optional) ║
# ║ Tested on: Claude Code 2.1.80+ (macOS / Linux) ║
# ╚══════════════════════════════════════════════════════════════════╝
JSON=$(cat)
MODEL=$(echo "$JSON" | jq -r '.model.display_name // "Unknown"')
DIR=$(echo "$JSON" | jq -r '.workspace.current_dir // "Unknown"' | xargs basename)
SESSION_ID=$(echo "$JSON" | jq -r '.session_id // "Unknown"')
TRANSCRIPT_PATH=$(echo "$JSON" | jq -r '.transcript_path // ""')
NOW=$(date +%s)
L1_SEGS=()
L2_SEGS=()
pl_add() {
local target=$1 bg=$2 fg=$3 text=$4
if [ "$target" = "1" ]; then
L1_SEGS+=("${bg}:${fg}:${text}")
else
L2_SEGS+=("${bg}:${fg}:${text}")
fi
}
pl_render() {
local arr_name=$1
local out=""
eval "local count=\${#${arr_name}[@]}"
for ((i=0; i<count; i++)); do
eval "local seg=\${${arr_name}[$i]}"
IFS=':' read -r bg fg text <<< "$seg"
out+="\033[38;5;${fg};48;5;${bg}m ${text} "
if [ $((i+1)) -lt "$count" ]; then
eval "local next_seg=\${${arr_name}[$((i+1))]}"
IFS=':' read -r next_bg _ _ <<< "$next_seg"
out+="\033[38;5;${bg};48;5;${next_bg}m"
else
out+="\033[0m\033[38;5;${bg}m\033[0m"
fi
done
printf '%b\n' "$out"
}
mini_bar() {
local pct=$1
local filled=$((pct / 10))
local bar=""
for ((i=0; i<filled; i++)); do bar+="●"; done
for ((i=filled; i<10; i++)); do bar+="○"; done
echo "$bar"
}
BRANCH=$(git branch --show-current 2>/dev/null || echo "no-git")
GIT_STATUS=""
if [ "$BRANCH" != "no-git" ]; then
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
GIT_STATUS="*"
fi
fi
TODAY_COMMITS=""
COMMIT_COUNT=0
if [ "$BRANCH" != "no-git" ]; then
TODAY=$(date '+%Y-%m-%d')
COMMIT_COUNT=$(git log --oneline --since="$TODAY 00:00:00" --until="$TODAY 23:59:59" 2>/dev/null | wc -l | xargs)
fi
WORK_TIME=""
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
TRANSCRIPT_TIME=$(stat -f %m "$TRANSCRIPT_PATH" 2>/dev/null)
else
TRANSCRIPT_TIME=$(stat -c %Y "$TRANSCRIPT_PATH" 2>/dev/null)
fi
if [ -n "$TRANSCRIPT_TIME" ]; then
WORK_SECONDS=$(( NOW - TRANSCRIPT_TIME ))
if [ "$WORK_SECONDS" -gt 0 ]; then
HOURS=$((WORK_SECONDS / 3600))
MINUTES=$(((WORK_SECONDS % 3600) / 60))
if [ "$HOURS" -gt 0 ]; then
WORK_TIME="${HOURS}h${MINUTES}m"
elif [ "$MINUTES" -gt 0 ]; then
WORK_TIME="${MINUTES}m"
else
WORK_TIME="<1m"
fi
fi
fi
fi
PROJECT_TYPE=""
if [ -f "package.json" ]; then
PROJECT_TYPE="JS"
elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
PROJECT_TYPE="PY"
elif [ -f "Cargo.toml" ]; then
PROJECT_TYPE="RS"
elif [ -f "go.mod" ]; then
PROJECT_TYPE="GO"
fi
MSG_COUNT=""
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
COUNT=$(grep -c '"role":' "$TRANSCRIPT_PATH" 2>/dev/null || echo "0")
if [ "$COUNT" -gt 0 ]; then
MSG_COUNT="$COUNT"
fi
fi
LINES_ADDED=$(echo "$JSON" | jq -r '.cost.total_lines_added // 0')
LINES_REMOVED=$(echo "$JSON" | jq -r '.cost.total_lines_removed // 0')
SESSION_COST=$(echo "$JSON" | jq -r '.cost.total_cost_usd // 0')
LIMIT_5H=""
LIMIT_7D=""
UTIL_5H=$(echo "$JSON" | jq -r '.rate_limits.five_hour.used_percentage // empty')
RESET_5H_EPOCH=$(echo "$JSON" | jq -r '.rate_limits.five_hour.resets_at // empty')
UTIL_7D=$(echo "$JSON" | jq -r '.rate_limits.seven_day.used_percentage // empty')
RESET_7D_EPOCH=$(echo "$JSON" | jq -r '.rate_limits.seven_day.resets_at // empty')
UTIL_5H_INT=0
if [ -n "$UTIL_5H" ] && [ "$UTIL_5H" != "null" ]; then
UTIL_5H_INT=${UTIL_5H%.*}
BAR_5H=$(mini_bar "$UTIL_5H_INT")
RESET_5H_TEXT=""
if [ -n "$RESET_5H_EPOCH" ] && [ "$RESET_5H_EPOCH" -gt "$NOW" ] 2>/dev/null; then
REMAIN=$((RESET_5H_EPOCH - NOW))
RH=$((REMAIN / 3600))
RM=$(((REMAIN % 3600) / 60))
RESET_5H_TEXT=" ⏳${RH}h${RM}m"
fi
LIMIT_5H="5h ${BAR_5H} ${UTIL_5H_INT}%${RESET_5H_TEXT}"
fi
UTIL_7D_INT=0
IDEAL_7D=""
if [ -n "$UTIL_7D" ] && [ "$UTIL_7D" != "null" ] && [ "$UTIL_7D" != "0" ]; then
UTIL_7D_INT=${UTIL_7D%.*}
BAR_7D=$(mini_bar "$UTIL_7D_INT")
REMAIN_7D_TEXT=""
if [ -n "$RESET_7D_EPOCH" ] && [ "$RESET_7D_EPOCH" -gt "$NOW" ] 2>/dev/null; then
REMAIN_SEC=$((RESET_7D_EPOCH - NOW))
ELAPSED=$((604800 - REMAIN_SEC))
IDEAL_7D=$(echo "scale=0; $ELAPSED * 100 / 604800" | bc 2>/dev/null || echo "")
R7D=$((REMAIN_SEC / 86400))
R7H=$(((REMAIN_SEC % 86400) / 3600))
REMAIN_7D_TEXT=" ${R7D}d${R7H}h"
fi
if [ -n "$IDEAL_7D" ]; then
LIMIT_7D="7d ${BAR_7D} ${UTIL_7D_INT}%/${IDEAL_7D}%${REMAIN_7D_TEXT}"
else
LIMIT_7D="7d ${BAR_7D} ${UTIL_7D_INT}%${REMAIN_7D_TEXT}"
fi
fi
EFFORT=""
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
EFFORT=$(grep -o 'Set effort level to [a-z]*:' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1 | sed 's/.*to //;s/://')
fi
if [ -z "$EFFORT" ]; then
EFFORT=$(jq -r '.effortLevel // empty' ~/.claude/settings.json 2>/dev/null)
fi
EFFORT=${EFFORT:-high}
MODEL_SHORT=$(echo "$MODEL" | sed 's/ (.*)//')
pl_add 1 24 255 "${MODEL_SHORT} · ${EFFORT}"
pl_add 1 17 255 "$DIR"
if [ -n "$PROJECT_TYPE" ]; then
pl_add 1 60 255 "$PROJECT_TYPE"
fi
if [ "$BRANCH" != "no-git" ]; then
pl_add 1 31 255 " ${BRANCH}${GIT_STATUS}"
fi
if [ "$LINES_ADDED" -gt 0 ] 2>/dev/null || [ "$LINES_REMOVED" -gt 0 ] 2>/dev/null; then
pl_add 1 236 255 " +${LINES_ADDED}/-${LINES_REMOVED}"
fi
LIGHT_BGS=(255 253)
LIGHT_IDX=0
if [ "$BRANCH" != "no-git" ] && [ "$COMMIT_COUNT" -gt 0 ] 2>/dev/null; then
pl_add 1 "${LIGHT_BGS[$((LIGHT_IDX % 2))]}" 232 " $COMMIT_COUNT"
LIGHT_IDX=$((LIGHT_IDX + 1))
fi
if [ -n "$MSG_COUNT" ]; then
pl_add 1 "${LIGHT_BGS[$((LIGHT_IDX % 2))]}" 232 " $MSG_COUNT"
LIGHT_IDX=$((LIGHT_IDX + 1))
fi
if [ -n "$WORK_TIME" ]; then
pl_add 1 "${LIGHT_BGS[$((LIGHT_IDX % 2))]}" 232 " $WORK_TIME"
LIGHT_IDX=$((LIGHT_IDX + 1))
fi
PERCENT_USED=$(echo "$JSON" | jq -r '.context_window.used_percentage // 0')
color_bar() {
local pct=$1
local filled=$((pct / 10))
local empty=$((10 - filled))
local color
if [ "$pct" -ge 80 ] 2>/dev/null; then
color="38;5;196"
elif [ "$pct" -ge 50 ] 2>/dev/null; then
color="38;5;214"
else
color="38;5;82"
fi
local bar="\033[${color}m"
for ((i=0; i<filled; i++)); do bar+="█"; done
bar+="\033[38;5;240m"
for ((i=0; i<empty; i++)); do bar+="░"; done
bar+="\033[0m"
printf '%b' "$bar"
}
pct_color() {
local pct=$1
local color
if [ "$pct" -ge 80 ] 2>/dev/null; then
color="38;5;196"
elif [ "$pct" -ge 50 ] 2>/dev/null; then
color="38;5;214"
else
color="38;5;82"
fi
printf '\033[%sm%s%%\033[0m' "$color" "$pct"
}
LINE2=""
HAS_LINE2=0
CTX_INT=${PERCENT_USED%.*}
CTX_INT=${CTX_INT:-0}
CTX_BAR=$(color_bar "$CTX_INT")
CTX_PCT=$(pct_color "$CTX_INT")
LINE2+="上下文 ${CTX_BAR} ${CTX_PCT}"
HAS_LINE2=1
if [ -n "$UTIL_5H" ] && [ "$UTIL_5H" != "null" ]; then
Q5_BAR=$(color_bar "$UTIL_5H_INT")
Q5_PCT=$(pct_color "$UTIL_5H_INT")
LINE2+=" | 5小時 ${Q5_BAR} ${Q5_PCT}${RESET_5H_TEXT}"
HAS_LINE2=1
fi
if [ -n "$UTIL_7D" ] && [ "$UTIL_7D" != "null" ] && [ "$UTIL_7D" != "0" ]; then
Q7_BAR=$(color_bar "$UTIL_7D_INT")
Q7_PCT=$(pct_color "$UTIL_7D_INT")
Q7_EXTRA=""
if [ -n "$IDEAL_7D" ]; then
Q7_EXTRA+="/${IDEAL_7D}%"
fi
if [ -n "$REMAIN_7D_TEXT" ]; then
Q7_EXTRA+="${REMAIN_7D_TEXT}"
fi
LINE2+=" | 7天 ${Q7_BAR} ${Q7_PCT}${Q7_EXTRA}"
HAS_LINE2=1
fi
COST_VAL=${SESSION_COST:-0}
[ "$COST_VAL" = "null" ] && COST_VAL=0
COST_FORMATTED=$(printf '%.2f' "$COST_VAL" 2>/dev/null || echo "0.00")
LINE2+=" | \033[38;5;214m\$${COST_FORMATTED}\033[0m"
HAS_LINE2=1
pl_render L1_SEGS
if [ "$HAS_LINE2" -eq 1 ]; then
printf '%b\n' "$LINE2"
fi
腳本是模組化設計。不想看哪段,把 # ══ MODULE: 名稱 區塊跟下面 ASSEMBLY 段對應的 pl_add 那行一起刪掉就好。
| 模組 | 顯示什麼 | 砍掉後 |
|---|---|---|
git-info | branch、髒位、今日 commits | 非 git 目錄本來就不顯示 |
work-time | session 開了多久 | 純參考 |
project-type | JS / PY / RS / GO 偵測 | 看當下目錄 |
msg-count | session 內訊息數 | 看用量 |
context-bar | 上下文使用率 | 建議留,第二行靠它 |
lines-changed | 本 session +/- 行數 | 看修改量 |
session-cost | 本 session 花了幾塊 USD | API 用戶才有意義 |
rate-limits | 5h / 7d 額度 + 倒數 | 需 Claude Code 2.1.80+ |
effort | 思考強度等級 | 用 /effort 切換 |
要 Claude Code 2.1.80 以上才會在 stdin 提供 rate_limits。先 claude --version 看版本,舊版升一下。
正常。Subscription(Pro / Max)這欄就是 0,這個數字只對 API 計費用戶有意義。
terminal 字型要支援 Powerline。推薦 MesloLGS NF、Fira Code Nerd Font、JetBrains Mono Nerd Font。iTerm2 / Warp / Ghostty 都可以裝。
三件事:①檢查 chmod +x ~/.claude/statusline.sh 有沒有給執行權限 ②~/.claude/settings.json 的 command 路徑改成你的絕對路徑(不能用 ~) ③重開 Claude Code。
每個模組都有 # ══ MODULE: xxx 起點,後面的 # ══ ASSEMBLY 段控制顯示順序和顏色。BG / FG 用 256 色 code,查色表挑喜歡的塞進去就好。