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>
This commit is contained in:
Виталий Никитенко
2026-06-08 09:24:03 +03:00
parent cf34698116
commit 2079768318
2 changed files with 86 additions and 10 deletions

View File

@@ -2,6 +2,7 @@
input=$(cat) input=$(cat)
cwd=$(echo "$input" | jq -r '.cwd') cwd=$(echo "$input" | jq -r '.cwd')
model=$(echo "$input" | jq -r '.model.display_name // empty') 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_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') 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_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
@@ -47,13 +48,72 @@ pct_color() {
fi fi
} }
if [[ "$model" == *[Dd]eep[Ss]eek* ]]; then # --- Баланс DeepSeek ---
# Моментально показываем кэшированный баланс, в фоне обновляем через API.
if [[ "$model_id" == *deepseek* ]]; then
cache_file="$HOME/.cache/ai-setup/deepseek_balance" cache_file="$HOME/.cache/ai-setup/deepseek_balance"
if [ -f "$cache_file" ]; then if [ -f "$cache_file" ]; then
balance=$(head -1 "$cache_file") balance=$(head -1 "$cache_file")
[ -n "$balance" ] && printf " \033[00;35m\$%s\033[00m" "$balance" [ -n "$balance" ] && printf " \033[00;35m\$%s\033[00m" "$balance"
fi 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 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 if [ -n "$five_pct" ] && [ -n "$five_reset" ]; then
five_int=$(printf '%.0f' "$five_pct") five_int=$(printf '%.0f' "$five_pct")
remaining=$(fmt_remaining "$five_reset") remaining=$(fmt_remaining "$five_reset")
@@ -68,6 +128,9 @@ else
fi fi
fi fi
# ctx:0% при старте новой сессии (нет данных от API)
[ -z "$ctx_pct" ] && ctx_pct="0"
if [ -n "$ctx_pct" ]; then if [ -n "$ctx_pct" ]; then
ctx_int=$(printf '%.0f' "$ctx_pct") ctx_int=$(printf '%.0f' "$ctx_pct")
color=$(pct_color "$ctx_int") color=$(pct_color "$ctx_int")

View File

@@ -658,6 +658,11 @@ if os.path.exists(settings_path):
except json.JSONDecodeError: except json.JSONDecodeError:
pass pass
data["statusLine"] = {"type": "command", "command": f"bash {script_path}"} data["statusLine"] = {"type": "command", "command": f"bash {script_path}"}
# SessionStart хук - триггерит вызов statusLine при старте сессии
if "hooks" not in data:
data["hooks"] = {}
if "SessionStart" not in data["hooks"]:
data["hooks"]["SessionStart"] = [{"hooks": [{"type": "command", "command": "true"}]}]
with open(settings_path, "w") as f: with open(settings_path, "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False) json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n") f.write("\n")
@@ -1107,7 +1112,9 @@ if [ -z "$api_key" ]; then
fi fi
fi fi
SYS_PROMPT=$(_build_ai_sys_prompt) _PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
_build_ai_sys_prompt > "$_PROMPT_FILE"
ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic \ ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic \
ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_AUTH_TOKEN="$api_key" \
ANTHROPIC_MODEL=deepseek-v4-pro \ ANTHROPIC_MODEL=deepseek-v4-pro \
@@ -1116,7 +1123,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-v4-pro \
ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash \ ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash \
CLAUDE_CODE_SUBAGENT_MODEL=deepseek-v4-flash \ CLAUDE_CODE_SUBAGENT_MODEL=deepseek-v4-flash \
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@"
DEEPSEEKEOF DEEPSEEKEOF
chmod +x "$BIN_DIR/ai-deepseek" chmod +x "$BIN_DIR/ai-deepseek"
@@ -1177,7 +1184,9 @@ if ! command -v claude &>/dev/null; then
exit 1 exit 1
fi fi
SYS_PROMPT=$(_build_ai_sys_prompt) _PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
_build_ai_sys_prompt > "$_PROMPT_FILE"
ANTHROPIC_BASE_URL=https://api.kimi.com/coding \ ANTHROPIC_BASE_URL=https://api.kimi.com/coding \
ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_AUTH_TOKEN="$api_key" \
ANTHROPIC_MODEL=kimi-k2.6 \ ANTHROPIC_MODEL=kimi-k2.6 \
@@ -1186,7 +1195,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=kimi-k2.6 \
ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6 \ ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6 \
CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \ CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@"
KIMIEOF KIMIEOF
chmod +x "$BIN_DIR/ai-kimi" chmod +x "$BIN_DIR/ai-kimi"
@@ -1247,7 +1256,9 @@ if ! command -v claude &>/dev/null; then
exit 1 exit 1
fi fi
SYS_PROMPT=$(_build_ai_sys_prompt) _PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
_build_ai_sys_prompt > "$_PROMPT_FILE"
ANTHROPIC_BASE_URL=https://openrouter.ai/api \ ANTHROPIC_BASE_URL=https://openrouter.ai/api \
ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_AUTH_TOKEN="$api_key" \
ANTHROPIC_MODEL=openai/gpt-5.5 \ ANTHROPIC_MODEL=openai/gpt-5.5 \
@@ -1256,7 +1267,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=anthropic/claude-4.6-sonnet \
ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-5.5 \ ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-5.5 \
CLAUDE_CODE_SUBAGENT_MODEL=openai/gpt-5.5 \ CLAUDE_CODE_SUBAGENT_MODEL=openai/gpt-5.5 \
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@"
OPENROUTEREOF OPENROUTEREOF
chmod +x "$BIN_DIR/ai-openrouter" chmod +x "$BIN_DIR/ai-openrouter"
@@ -1295,8 +1306,10 @@ cat > "$BIN_DIR/ai-claude" << 'CLAUDEEOF'
#!/usr/bin/env bash #!/usr/bin/env bash
# ai-claude - запуск оригинального Claude Code (Anthropic) # ai-claude - запуск оригинального Claude Code (Anthropic)
source "$HOME/.local/bin/ai-api-helpers.sh" 2>/dev/null || true source "$HOME/.local/bin/ai-api-helpers.sh" 2>/dev/null || true
SYS_PROMPT=$(_build_ai_sys_prompt) _PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
exec claude --dangerously-skip-permissions --system-prompt "$SYS_PROMPT" "$@" trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
_build_ai_sys_prompt > "$_PROMPT_FILE"
claude --dangerously-skip-permissions --model sonnet --system-prompt-file "$_PROMPT_FILE" "$@"
CLAUDEEOF CLAUDEEOF
chmod +x "$BIN_DIR/ai-claude" chmod +x "$BIN_DIR/ai-claude"
@@ -1308,7 +1321,7 @@ if [ "$USE_VLESS" -eq 1 ]; then
sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-kimi" sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-kimi"
sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-openrouter" sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-openrouter"
sed -i 's/^\([[:space:]]*\)exec "\$agy_bin"/\1exec proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" "\$agy_bin"/' "$BIN_DIR/ai-gemini" sed -i 's/^\([[:space:]]*\)exec "\$agy_bin"/\1exec proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" "\$agy_bin"/' "$BIN_DIR/ai-gemini"
sed -i 's/^exec claude/exec proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude/' "$BIN_DIR/ai-claude" sed -i 's/^claude --dangerously-skip-permissions/proxychains4 -f "\$HOME\/\.proxychains-xray\.conf" claude --dangerously-skip-permissions/' "$BIN_DIR/ai-claude"
success "proxychains4 интегрирован" success "proxychains4 интегрирован"
fi fi