2026-05-24
编程工具
claudecode
3
#!/usr/bin/env bash

# Claude Code 自定义状态栏(单行 + 图标,| 分隔)

set -u

input="$(cat)"

# -----------------------------
# 颜色定义
# -----------------------------
RESET="\033[0m"

C_MUTED="\033[38;5;245m"
C_GREEN="\033[38;5;114m"
C_MINT="\033[38;5;121m"
C_BLUE="\033[38;5;117m"
C_YELLOW="\033[38;5;221m"
C_RED="\033[38;5;203m"
C_CYAN="\033[38;5;81m"
C_ORANGE="\033[38;5;215m"

if [ "${NO_COLOR:-}" != "" ]; then
  RESET=""
  C_MUTED=""; C_GREEN=""; C_MINT=""
  C_BLUE=""; C_YELLOW=""; C_RED=""
  C_CYAN=""; C_ORANGE=""
fi

_expand() { printf -v "$1" "%b" "${!1}"; }
for _v in RESET C_MUTED C_GREEN C_MINT C_BLUE C_YELLOW C_RED C_CYAN C_ORANGE; do
  _expand "$_v"
done

# -----------------------------
# JSON 解析(内联 node 解析器)
# -----------------------------

eval "$(echo "$input" | node -e "
  var chunks = [];
  process.stdin.on('data', function(c) { chunks.push(c); });
  process.stdin.on('end', function() {
    try {
      var d = JSON.parse(Buffer.concat(chunks).toString('utf8'));
      var m = d.model || {};
      var w = d.workspace || {};
      var cw = d.context_window || {};
      var cu = cw.current_usage || {};
      var cost = d.cost || {};
      function num(v) { return Math.floor(Number(v)) || 0; }
      function num1(v) { var n = Number(v); return (isNaN(n) ? 0 : Math.round(n * 10) / 10).toFixed(1); }

      console.log('model=' + JSON.stringify(m.display_name || 'Claude'));
      console.log('cwd=' + JSON.stringify(w.current_dir || d.cwd || ''));
      console.log('ctx_pct=' + num1(cw.used_percentage));
      console.log('duration_ms=' + num(cost.total_duration_ms));
      console.log('req_hit=' + num(cu.cache_read_input_tokens));
      console.log('req_miss=' + num(cu.input_tokens));
      console.log('total_output_tokens=' + num(cw.total_output_tokens));
    } catch(e) {
      console.log('model=\"Claude\"'); console.log('cwd=\"\"'); console.log('ctx_pct=0');
      console.log('duration_ms=0'); console.log('req_hit=0'); console.log('req_miss=0');
      console.log('total_output_tokens=0');
    }
  });
" 2>/dev/null)"

# node 失败时的兜底默认值
: "${model:=Claude}"
: "${cwd:=}"
: "${ctx_pct:=0}"
: "${duration_ms:=0}"
: "${req_hit:=0}"
: "${req_miss:=0}"
: "${total_output_tokens:=0}"

# -----------------------------
# 工具函数
# -----------------------------

fmt_tokens() {
  local n="${1:-0}"
  [ -z "$n" ] || [ "$n" = "null" ] && n=0
  awk -v n="$n" 'BEGIN {
    if (n >= 1000000) printf "%.1fm", n / 1000000
    else if (n >= 1000) { if (n % 1000 == 0) printf "%.0fk", n / 1000; else printf "%.1fk", n / 1000 }
    else printf "%.1f", n
  }'
}

fmt_time() {
  local ms="${1:-0}"
  [ -z "$ms" ] || [ "$ms" = "null" ] && ms=0
  awk -v ms="$ms" 'BEGIN {
    s = ms / 1000
    if (s < 1) printf "0s"
    else if (s < 60) printf "%.0fs", s
    else if (s < 3600) printf "%.0fm", s / 60
    else printf "%.0fh %.0fm", int(s / 3600), (s % 3600) / 60
  }'
}

short_dir() {
  local dir="${1:-$PWD}"
  if [ -n "${HOME:-}" ] && [ "${dir#$HOME}" != "$dir" ]; then
    dir="~${dir#$HOME}"
  fi
  if [[ "$dir" =~ ^/[a-zA-Z]/ ]]; then
    local drive="${dir:1:1}"
    drive="${drive^^}"
    dir="${drive}:${dir:2}"
    dir="${dir//\//\\}"
  fi
  if [ "${#dir}" -gt 36 ]; then
    basename "$dir"
  else
    echo "$dir"
  fi
}

git_branch() {
  local dir="$1"
  git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "无"; return; }
  local b
  b="$(git -C "$dir" branch --show-current 2>/dev/null)"
  [ -z "$b" ] && b="$(git -C "$dir" rev-parse --short HEAD 2>/dev/null)"
  [ -z "$b" ] && b="detached"
  if [ -n "$(git -C "$dir" status --porcelain 2>/dev/null)" ]; then
    b="${b}*"
  fi
  echo "$b"
}

# -----------------------------
# 计算缓存命中率
# -----------------------------

if [ $((req_hit + req_miss)) -gt 0 ]; then
  cache_pct="$(awk -v r="$req_hit" -v i="$req_miss" \
    'BEGIN { printf "%.1f", r / (r + i) * 100 }')"
else
  cache_pct="0.0"
fi

project="$(short_dir "$cwd")"
branch="$(git_branch "$cwd")"
tokens="⇣$(fmt_tokens "$req_hit") ⇡$(fmt_tokens "$req_miss")  ${cache_pct}%  →$(fmt_tokens "$total_output_tokens")"
time_display="$(fmt_time "$duration_ms")"

# -----------------------------
# 输出(单行 + 图标,| 分隔)
# -----------------------------

SEP="  ${C_MUTED}│${RESET}  "

printf "%b" "${C_MINT}🤖 ${model}${RESET}"
printf "%b" "${SEP}"
printf "%b" "${C_YELLOW}📁 ${project}${RESET}"
printf "%b" "${SEP}"
printf "%b" "${C_BLUE}🔀 ${branch}${RESET}"
printf "%b" "${SEP}"
printf "%b" "${C_GREEN}🫧 ${ctx_pct}%${RESET}"
printf "%b" "${SEP}"
printf "%b" "${C_RED}🚀 ${tokens}${RESET}"
printf "%b" "${SEP}"
printf "%b" "${C_MUTED}⏱  ${time_display}${RESET}"
printf "\n"
标签:

claudecode