Files
ai-setup/home-configs/claude/statusline-command.sh
Виталий Никитенко 2079768318 fix: system-prompt через файл, кеш rate_limits при старте, SessionStart хук
- Все лаунчеры (ai-claude, ai-deepseek, ai-kimi, ai-openrouter): промпт
  пишется во временный файл через --system-prompt-file вместо аргумента
  командной строки. Решает E2BIG при промптах > 128KB (MAX_ARG_STRLEN)
  из проектов с большими .md файлами.

- statusline: кешируем rate_limits по model_id (раздельные файлы для
  claude/kimi/openrouter). При старте сессии показываем данные из кеша
  + ctx:0%. Убирает пустую статусную строку до первого запроса.

- settings.json: добавляем SessionStart хук при setup, триггерит
  вызов statusLine при открытии сессии.

- ai-claude: --model sonnet зафиксирован, убрали exec для корректной
  работы trap (cleanup временного файла).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 09:24:03 +03:00

152 lines
5.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
input=$(cat)
cwd=$(echo "$input" | jq -r '.cwd')
model=$(echo "$input" | jq -r '.model.display_name // empty')
model_id=$(echo "$input" | jq -r '.model.id // empty')
five_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
five_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
week_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
week_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
ctx_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
branch=$(git -C "$cwd" --no-optional-locks symbolic-ref --short HEAD 2>/dev/null)
short_cwd="${cwd/#$HOME/\~}"
printf "\033[00;37m%s\033[00m" "$short_cwd"
[ -n "$branch" ] && printf " \033[00;37m[%s]\033[00m" "$branch"
[ -n "$model" ] && printf " \033[38;5;173m%s\033[00m" "$model"
# Форматирует оставшееся время до сброса лимита
fmt_remaining() {
local reset_ts="$1"
local now
now=$(date +%s)
local diff=$(( reset_ts - now ))
[ "$diff" -le 0 ] && echo "скоро" && return
local d=$(( diff / 86400 ))
local h=$(( (diff % 86400) / 3600 ))
local m=$(( (diff % 3600) / 60 ))
if [ "$d" -gt 0 ]; then
echo "${d}д${h}ч"
elif [ "$h" -gt 0 ]; then
echo "${h}ч${m}м"
else
echo "${m}м"
fi
}
# Возвращает ANSI-цвет по проценту: зелёный <40%, жёлтый 40-60%, красный >=60%
pct_color() {
local pct="$1"
if [ "$pct" -lt 40 ]; then
printf '\033[00;32m'
elif [ "$pct" -lt 60 ]; then
printf '\033[00;33m'
else
printf '\033[00;31m'
fi
}
# --- Баланс DeepSeek ---
# Моментально показываем кэшированный баланс, в фоне обновляем через API.
if [[ "$model_id" == *deepseek* ]]; then
cache_file="$HOME/.cache/ai-setup/deepseek_balance"
if [ -f "$cache_file" ]; then
balance=$(head -1 "$cache_file")
[ -n "$balance" ] && printf " \033[00;35m\$%s\033[00m" "$balance"
fi
# Фоновое обновление баланса (не чаще раза в 30 секунд)
refresh_ts="$HOME/.cache/ai-setup/deepseek_balance_refresh_ts"
now=$(date +%s)
last=$(cat "$refresh_ts" 2>/dev/null || echo 0)
if [ $(( now - last )) -gt 30 ]; then
key_file="$HOME/.config/ai-setup/deepseek_key"
if [ -f "$key_file" ]; then
echo "$now" > "$refresh_ts" 2>/dev/null
(
api_key=$(cat "$key_file")
resp=$(curl -s --max-time 10 "https://api.deepseek.com/user/balance" \
-H "Authorization: Bearer $api_key" \
-H "Accept: application/json" 2>/dev/null)
if [ -n "$resp" ]; then
new_balance=$(echo "$resp" | python3 -c "
import sys, json
d = json.load(sys.stdin)
infos = d.get('balance_infos', [])
if infos:
curr = infos[0].get('currency', '')
total = infos[0].get('total_balance', '0')
print(f'{total} {curr}')
" 2>/dev/null)
if [ -n "$new_balance" ]; then
echo "$new_balance" > "$cache_file"
fi
fi
) &
fi
fi
else
# Рейт-лимиты для НЕ-DeepSeek провайдеров
# Кеш специфичен для провайдера (по model_id) чтобы не смешивать claude/kimi/openrouter
_cache_key=$(echo "${model_id:-unknown}" | sed 's/[^a-zA-Z0-9._-]/_/g')
RATE_CACHE="$HOME/.cache/ai-setup/rate_limits_${_cache_key}.cache"
mkdir -p "$HOME/.cache/ai-setup"
# Если есть свежие данные - сохранить в кеш
if [ -n "$five_pct" ] || [ -n "$week_pct" ]; then
{
echo "FIVE_PCT=${five_pct}"
echo "FIVE_RESET=${five_reset}"
echo "WEEK_PCT=${week_pct}"
echo "WEEK_RESET=${week_reset}"
} > "$RATE_CACHE"
fi
# Если нет данных - читать из кеша (старт сессии до первого запроса)
if [ -z "$five_pct" ] && [ -z "$week_pct" ] && [ -f "$RATE_CACHE" ]; then
# shellcheck source=/dev/null
source "$RATE_CACHE" 2>/dev/null
five_pct="${FIVE_PCT:-}"
five_reset="${FIVE_RESET:-}"
week_pct="${WEEK_PCT:-}"
week_reset="${WEEK_RESET:-}"
fi
if [ -n "$five_pct" ] && [ -n "$five_reset" ]; then
five_int=$(printf '%.0f' "$five_pct")
remaining=$(fmt_remaining "$five_reset")
color=$(pct_color "$five_int")
printf " %s%s:%s%%\033[00m" "$color" "$remaining" "$five_int"
fi
if [ -n "$week_pct" ] && [ -n "$week_reset" ]; then
week_int=$(printf '%.0f' "$week_pct")
remaining=$(fmt_remaining "$week_reset")
color=$(pct_color "$week_int")
printf " %s%s:%s%%\033[00m" "$color" "$remaining" "$week_int"
fi
fi
# ctx:0% при старте новой сессии (нет данных от API)
[ -z "$ctx_pct" ] && ctx_pct="0"
if [ -n "$ctx_pct" ]; then
ctx_int=$(printf '%.0f' "$ctx_pct")
color=$(pct_color "$ctx_int")
printf " %sctx:%s%%\033[00m" "$color" "$ctx_int"
# Звуковой сигнал при первом достижении 60%
alert_file="$HOME/.cache/ai-setup/ctx_alert_state"
if [ "$ctx_int" -ge 60 ]; then
if [ ! -f "$alert_file" ] || [ "$(cat "$alert_file")" != "alerted" ]; then
mkdir -p "$HOME/.cache/ai-setup"
echo "alerted" > "$alert_file"
(timeout 1s paplay /usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga 2>/dev/null; true) &
fi
elif [ "$ctx_int" -lt 50 ]; then
rm -f "$alert_file" 2>/dev/null
fi
fi
exit 0