Files
comun/docs/prompt_recreare_statusline.md
Marius Mutu 8e022d332e #13: formular de facturare unificat - etapa curenta
Squash al branch-ului de lucru plan13-s2.

Clase: ofacturare (nucleul formularului unificat), ofacturare_comun,
ferestre_cere_date, ocomenzi, caut_ora (lista de preturi in combo-urile de
cautare), omodificari.

Programe: ofacturare impartit - ofacturare_antet, ofacturare_rutare_scriere si
ogrid_latimi sunt fisiere noi; oproceduri_facturare primeste discountul pe linie
si cota standard de TVA cand articolul nu are cota pe politica.

Documentatie: capcana SQLExec no_data_found, conventia de encoding, depanarea
testelor VFP si regulile de lucru - conflictele cu modificarile venite din alte
proiecte sunt rezolvate pastrand ambele parti.

Loguri de rulare a testelor scoase din versionare; testele raman.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
2026-09-09 21:38:25 +03:00

10 KiB

Prompt pentru alta sesiune: recreeaza statusline-ul Claude Code

Copiaza tot ce urmeaza si trimite-l ca prompt intr-o sesiune Claude Code noua (pe alta masina/user), ca sa recreeze exact statusline-ul curent (Windows / Git Bash).


Vreau sa configurezi un statusline custom pentru Claude Code, pe Windows cu Git Bash.

  1. Verifica ca exista jq.exe in ~/.claude/bin/jq.exe. Daca nu exista, descarca binarul oficial jq pentru Windows (jq-win64.exe de pe github.com/jqlang/jq/releases) si salveaza-l acolo ca jq.exe.

  2. Creeaza fisierul ~/.claude/statusline.sh (bash, LF, executabil) cu continutul de mai jos.

  3. In ~/.claude/settings.json, adauga/actualizeaza cheia de nivel radacina:

    "statusLine": {
      "type": "command",
      "command": "C:/Users/<USER>/.claude/statusline.sh"
    }
    

    (inlocuieste <USER> cu userul curent; foloseste slash-uri, nu backslash).

Ce face scriptul:

  • Linia 1: model | ctx folosit/total si % din fereastra | proiect (branch git, cu * daca e dirty)
  • Linia 2: trei bare de puncte (10 puncte fiecare) pentru consum curent (5h), saptamanal (7 zile) si sonnet (7 zile) — procent + timp pana la reset, in format ●●●○○○○○○○ 30% ↺ 2hr 15min
  • Cele trei procente vin din /api/oauth/usage (acelasi endpoint ca ecranul /usage), cu cache de 10 minute in ~/.claude/.statusline-usage.json si backoff la 429/eroare (20 min, respectiv 5 min), ca sa nu loveasca rate-limit-ul la fiecare prompt.
  • Pana la primul fetch reusit, current/weekly cad back pe rate_limits din JSON-ul primit pe stdin.
  • Token-ul OAuth se ia din ~/.claude/.credentials.json (claudeAiOauth.accessToken).
  • La inceput reface PATH-ul ($HOME/.claude/bin:/usr/bin:/bin:$PATH), pentru ca Claude Code lanseaza scriptul ca shell non-login/non-interactiv, deci profilul Git Bash nu se incarca si PATH-ul nu are coreutils.

Continutul complet al ~/.claude/statusline.sh:

#!/bin/bash
# Claude Code Statusline (Windows / Git Bash)
# Line 1:  Model  |  ctx 33K/1M 3%  |  project (branch*)
# Line 2:  current <bar> 24% ↺ 3hr  │  weekly <bar> 4% ↺ Thu 7pm  │  sonnet <bar> 2% ↺ Thu 7pm
#
# All three usage figures come from /api/oauth/usage -- the same source as the
# `/usage` screen -- so the numbers match it exactly. That endpoint is rate-
# limited, so the result is cached for 10 min; on failure the last good values
# are kept. Until the first successful fetch, current/weekly fall back to the
# `rate_limits` object on stdin (slightly different snapshot, but never blank).

set -o pipefail

# Claude Code spawns this as a non-login, non-interactive shell, so Git Bash's
# profile is never sourced and PATH has no coreutils. Restore them explicitly,
# plus the bundled jq.exe in ~/.claude/bin.
export PATH="$HOME/.claude/bin:/usr/bin:/bin:$PATH"

input=$(cat)

# Validate JSON
if ! echo "$input" | jq -e . >/dev/null 2>&1; then
  printf '\033[90mLoading...\033[0m'
  exit 0
fi

# --- Context / project (line 1) ---
model=$(echo "$input" | jq -r '.model.display_name // "?"')
workdir=$(echo "$input" | jq -r '.workspace.current_dir // ""')
project=$(basename "$workdir" 2>/dev/null) || project="?"

ctx_pct=$(echo "$input" | jq -r '(.context_window.used_percentage // 0) | floor')
[[ "$ctx_pct" =~ ^[0-9]+$ ]] || ctx_pct=0

ctx_size=$(echo "$input" | jq -r '.context_window.context_window_size // 0')
ctx_input=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // 0')
ctx_output=$(echo "$input" | jq -r '.context_window.current_usage.output_tokens // 0')
ctx_cache_create=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // 0')
ctx_cache_read=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // 0')
ctx_used=$(( ctx_input + ctx_output + ctx_cache_create + ctx_cache_read ))

fmt_tokens() {
  local n=$1
  if [ "$n" -ge 1000000 ]; then printf "%dM" $(( n / 1000000 ))
  elif [ "$n" -ge 1000 ]; then printf "%dK" $(( n / 1000 ))
  else printf "%d" "$n"; fi
}
ctx_used_fmt=$(fmt_tokens "$ctx_used")
ctx_size_fmt=$(fmt_tokens "$ctx_size")

# Git branch with dirty marker
git_branch=""
if [ -n "$workdir" ] && [ -d "$workdir" ]; then
  git_branch=$(cd "$workdir" 2>/dev/null && git -c gc.auto=0 rev-parse --abbrev-ref HEAD 2>/dev/null) || git_branch=""
  if [ -n "$git_branch" ]; then
    git_dirty=$(cd "$workdir" 2>/dev/null && git -c gc.auto=0 status --porcelain 2>/dev/null | head -1)
    [ -n "$git_dirty" ] && git_branch="${git_branch}*"
  fi
fi

# --- Usage: all three figures from /api/oauth/usage, cached ---
USAGE_CACHE="$HOME/.claude/.statusline-usage.json"
USAGE_REFRESH=600        # refresh at most every 10 min
USAGE_BACKOFF_429=1200   # after a rate-limit, wait 20 min before retrying
USAGE_BACKOFF_ERR=300    # after any other error, wait 5 min

now=$(date +%s)
c5p=""; c5r=""; c7p=""; c7r=""; csp=""; csr=""; fetched=0; retry=0

read_usage_cache() {
  [ -f "$USAGE_CACHE" ] || return
  c5p=$(jq -r '.five_hour_pct    // ""' "$USAGE_CACHE" 2>/dev/null)
  c5r=$(jq -r '.five_hour_reset  // ""' "$USAGE_CACHE" 2>/dev/null)
  c7p=$(jq -r '.seven_day_pct    // ""' "$USAGE_CACHE" 2>/dev/null)
  c7r=$(jq -r '.seven_day_reset  // ""' "$USAGE_CACHE" 2>/dev/null)
  csp=$(jq -r '.sonnet_pct       // ""' "$USAGE_CACHE" 2>/dev/null)
  csr=$(jq -r '.sonnet_reset     // ""' "$USAGE_CACHE" 2>/dev/null)
  fetched=$(jq -r '(.fetched_at  // 0) | floor' "$USAGE_CACHE" 2>/dev/null || echo 0)
  retry=$(jq -r '(.retry_until   // 0) | floor' "$USAGE_CACHE" 2>/dev/null || echo 0)
}
read_usage_cache
[[ "$fetched" =~ ^[0-9]+$ ]] || fetched=0
[[ "$retry"   =~ ^[0-9]+$ ]] || retry=0

write_usage_cache() {  # 5p 5r 7p 7r sp sr fetched_at retry_until
  printf '{"five_hour_pct":"%s","five_hour_reset":"%s","seven_day_pct":"%s","seven_day_reset":"%s","sonnet_pct":"%s","sonnet_reset":"%s","fetched_at":%s,"retry_until":%s}\n' \
    "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" > "$USAGE_CACHE"
}

refresh_usage() {
  local creds="$HOME/.claude/.credentials.json" token tmp http
  [ -f "$creds" ] || return
  token=$(jq -r '.claudeAiOauth.accessToken // empty' "$creds" 2>/dev/null)
  [ -n "$token" ] || return
  tmp="$HOME/.claude/.statusline-usage.tmp.$$"
  http=$(curl -s -o "$tmp" -w '%{http_code}' --max-time 3 \
    -H "Authorization: Bearer $token" \
    -H "anthropic-beta: oauth-2025-04-20" \
    -H "Content-Type: application/json" \
    "https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
  if [ "$http" = "200" ]; then
    local p5 r5 p7 r7 ps rs
    p5=$(jq -r 'if .five_hour.utilization!=null        then (.five_hour.utilization|floor)        else "" end' "$tmp" 2>/dev/null)
    r5=$(jq -r '.five_hour.resets_at        // ""' "$tmp" 2>/dev/null)
    p7=$(jq -r 'if .seven_day.utilization!=null        then (.seven_day.utilization|floor)        else "" end' "$tmp" 2>/dev/null)
    r7=$(jq -r '.seven_day.resets_at        // ""' "$tmp" 2>/dev/null)
    ps=$(jq -r 'if .seven_day_sonnet.utilization!=null then (.seven_day_sonnet.utilization|floor) else "" end' "$tmp" 2>/dev/null)
    rs=$(jq -r '.seven_day_sonnet.resets_at // ""' "$tmp" 2>/dev/null)
    write_usage_cache "$p5" "$r5" "$p7" "$r7" "$ps" "$rs" "$now" 0
  elif [ "$http" = "429" ]; then
    write_usage_cache "$c5p" "$c5r" "$c7p" "$c7r" "$csp" "$csr" "$fetched" "$(( now + USAGE_BACKOFF_429 ))"
  else
    write_usage_cache "$c5p" "$c5r" "$c7p" "$c7r" "$csp" "$csr" "$fetched" "$(( now + USAGE_BACKOFF_ERR ))"
  fi
  rm -f "$tmp"
}

if [ "$(( now - fetched ))" -gt "$USAGE_REFRESH" ] && [ "$now" -gt "$retry" ]; then
  refresh_usage
  read_usage_cache
fi

# current / weekly: use API cache; fall back to stdin rate_limits if not yet fetched
if [ -n "$c5p" ]; then
  five_hr_pct=$c5p; five_hr_reset=$c5r
else
  five_hr_pct=$(echo "$input" | jq -r '(.rate_limits.five_hour.used_percentage // 0) | floor')
  five_hr_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // ""')
fi
if [ -n "$c7p" ]; then
  weekly_pct=$c7p; weekly_reset=$c7r
else
  weekly_pct=$(echo "$input" | jq -r '(.rate_limits.seven_day.used_percentage // 0) | floor')
  weekly_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // ""')
fi
# sonnet: only available from the API
sonnet_pct=${csp:-0}; sonnet_reset=$csr

[[ "$five_hr_pct" =~ ^[0-9]+$ ]] || five_hr_pct=0
[[ "$weekly_pct"  =~ ^[0-9]+$ ]] || weekly_pct=0
[[ "$sonnet_pct"  =~ ^[0-9]+$ ]] || sonnet_pct=0

# --- reset-time formatting (accepts epoch seconds or ISO string) ---
to_epoch() {
  local v="${1%%.*}"
  if [ -z "$v" ] || [ "$v" = "null" ]; then return 1; fi
  if [[ "$v" =~ ^[0-9]+$ ]]; then printf '%s' "$v"
  else date -d "$1" +%s 2>/dev/null || return 1; fi
}

fmt_reset() {
  # No TZ= : Git Bash's date has no zoneinfo DB, but with TZ unset it uses the
  # Windows system timezone correctly. Setting TZ=Europe/... falls back to UTC.
  local e
  e=$(to_epoch "$1") || { printf '%s' "--"; return; }
  date -d "@$e" "+%a %-I:%M%P" 2>/dev/null || printf '%s' "--"
}

fmt_remaining() {
  local e now_ts rem rh rm rd
  e=$(to_epoch "$1") || { printf '%s' "--"; return; }
  now_ts=$(date +%s)
  if [ "$e" -gt "$now_ts" ]; then
    rem=$(( e - now_ts )); rh=$(( rem / 3600 )); rm=$(( (rem % 3600) / 60 ))
    if [ "$rh" -gt 24 ]; then
      rd=$(( rh / 24 )); rh=$(( rh % 24 )); printf "%dd %dh" "$rd" "$rh"
    elif [ "$rh" -gt 0 ]; then printf "%dhr %dmin" "$rh" "$rm"
    else printf "%dmin" "$rm"; fi
  else printf "resetting"; fi
}

five_hr_remain=$(fmt_remaining "$five_hr_reset")
weekly_reset_str=$(fmt_reset "$weekly_reset")
sonnet_reset_str=$(fmt_reset "$sonnet_reset")

# --- dot bar ---
DOT_TOTAL=10
make_dots() {
  local pct=$1 filled i dots=""
  filled=$(( pct * DOT_TOTAL / 100 ))
  [ "$filled" -gt "$DOT_TOTAL" ] && filled=$DOT_TOTAL
  [ "$filled" -lt 0 ] && filled=0
  for (( i=0; i<DOT_TOTAL; i++ )); do
    if [ "$i" -lt "$filled" ]; then dots="${dots}●"; else dots="${dots}○"; fi
  done
  printf "%s" "$dots"
}

# --- build & output ---
if [ -n "$git_branch" ]; then
  proj_display="${project} (${git_branch})"
else
  proj_display="${project}"
fi
line1=$(printf "%s  │  ctx %s/%s %d%%  │  %s" "$model" "$ctx_used_fmt" "$ctx_size_fmt" "$ctx_pct" "$proj_display")

line2=$(printf "current %s %d%% ↺ %s  │  weekly %s %d%% ↺ %s  │  sonnet %s %d%% ↺ %s" \
  "$(make_dots "$five_hr_pct")" "$five_hr_pct" "$five_hr_remain" \
  "$(make_dots "$weekly_pct")" "$weekly_pct" "$weekly_reset_str" \
  "$(make_dots "$sonnet_pct")" "$sonnet_pct" "$sonnet_reset_str")

printf "%s\n%s" "$line1" "$line2"

Dupa ce salvezi fisierul si actualizezi settings.json, deschide o sesiune noua Claude Code (sau /statusline refresh) ca sa vezi statusline-ul aplicat.