老K 的工作配備分享

Claude Code 兩行 Powerline Statusline

把模型、目錄、git 狀態、訊息數、上下文使用率、5h/7d 額度、本 session 花費,全部塞進 terminal 底部兩行。 一眼掌握寫到哪、用多少、還剩多少,跑得久不會超載。

分享自 領先時代數位 · 老K
Opus 4.7 · highclaude-workspaceJS⎇ master*+128/-42⊕ 3✉ 47
上下文 ████░░░░░░ 42% | 5小時 ██████░░░░ 63% ⏳1h22m | 7天 ███░░░░░░░ 31%/47% 3d5h | $2.84

1顯示什麼

第一行(Powerline 區塊)

從左到右依序:

第二行(彩色進度條)

顏色閾值:<50% 綠 / 50–79% 黃 / ≥80% 紅。

2三步安裝

  1. 裝 jq

    解析 stdin JSON 用的,沒裝這個整支會壞掉。

    brew install jq

    Linux 用 apt install jqyum install jq

  2. 把腳本存進 ~/.claude/statusline.sh

    下面那塊紅褐色 code block,按右上角「複製」整段貼到檔案裡,然後給執行權限:

    chmod +x ~/.claude/statusline.sh
  3. 編輯 ~/.claude/settings.json,加上設定
    {
      "statusLine": {
        "type": "command",
        "command": "bash /Users/你的帳號/.claude/statusline.sh"
      }
    }

    路徑改自己的,存檔後重開 Claude Code 就有了。

3完整腳本(一鍵複製)

點右上角「複製腳本」,貼到 ~/.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

4模組地圖(可以拆)

腳本是模組化設計。不想看哪段,把 # ══ MODULE: 名稱 區塊跟下面 ASSEMBLY 段對應的 pl_add 那行一起刪掉就好。

模組顯示什麼砍掉後
git-infobranch、髒位、今日 commits非 git 目錄本來就不顯示
work-timesession 開了多久純參考
project-typeJS / PY / RS / GO 偵測看當下目錄
msg-countsession 內訊息數看用量
context-bar上下文使用率建議留,第二行靠它
lines-changed本 session +/- 行數看修改量
session-cost本 session 花了幾塊 USDAPI 用戶才有意義
rate-limits5h / 7d 額度 + 倒數需 Claude Code 2.1.80+
effort思考強度等級用 /effort 切換

5常見問題

第二行沒出現 5h / 7d 額度?

要 Claude Code 2.1.80 以上才會在 stdin 提供 rate_limits。先 claude --version 看版本,舊版升一下。

費用一直 $0.00?

正常。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,查色表挑喜歡的塞進去就好。

已複製到剪貼簿