From 1cb4853dca925df1b5218c9163d325937fb9b784 Mon Sep 17 00:00:00 2001 From: vitaly Date: Mon, 15 Jun 2026 05:21:12 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=BD=D0=B0=D1=82=D0=B8=D0=B2?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20persistence=20effort,=20=D0=B2=D1=8B=D0=BF?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BC=D1=91=D1=80=D1=82=D0=B2=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20=D0=BA=D1=8D=D1=88=D0=B0=20effort=5F*?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EFFORT_MAPPING, statusline и _apply_effort переведены на нативное хранение уровня в settings.json лаунчера. Убран CLAUDE_CODE_EFFORT_LEVEL (он блокировал /effort внутри сессии) и кэш ~/.cache/ai-setup/effort_*, который никто не читал и который врал относительно реального уровня. Co-Authored-By: Claude Opus 4.8 --- EFFORT_MAPPING.md | 23 +- home-configs/claude/statusline-command.sh | 19 +- scripts/ai-setup.sh | 324 ++++++++++++++++------ 3 files changed, 260 insertions(+), 106 deletions(-) diff --git a/EFFORT_MAPPING.md b/EFFORT_MAPPING.md index 026e44b..917efad 100644 --- a/EFFORT_MAPPING.md +++ b/EFFORT_MAPPING.md @@ -82,31 +82,22 @@ on / off ## Persistence effort между сессиями Каждый Claude Code лаунчер (`ai-claude`, `ai-deepseek`, `ai-kimi`, `ai-openrouter`) -запоминает свой уровень effort отдельно. Логика гибридная: +запоминает свой уровень effort отдельно в `settings.json` лаунчера. -- `low` / `medium` / `high` / `xhigh` живут нативно в `settings.json` лаунчера. - `/effort` внутри сессии работает как обычно, уровень сохраняется между сессиями. -- `max` - единственный уровень, который Claude Code не сохраняет в `settings.json` - (он session-only). Поэтому его восстанавливаем через `CLAUDE_CODE_EFFORT_LEVEL`. - Текущий уровень, включая `max`, лаунчер записывает в `~/.cache/ai-setup/effort_`. +`/effort` внутри сессии работает для всех уровней. `CLAUDE_CODE_EFFORT_LEVEL` не используется. -Важное следствие только для `max`: когда восстановлена `max`-сессия, выставлена -`CLAUDE_CODE_EFFORT_LEVEL=max`, и `/effort` внутри нее не сменит уровень, потому что -env-переменная работает как жесткий override Claude Code. - -Как выйти из `max` или форсить любой уровень: +Форсировать уровень при запуске: ```bash -AI_EFFORT=max ai-deepseek # включить и запомнить max -AI_EFFORT=high ai-deepseek # вернуться на high -ai-deepseek # без флага - восстановить последний уровень +AI_EFFORT=max ai-deepseek # включить max +AI_EFFORT=high ai-deepseek # включить high +ai-deepseek # без флага - использовать уровень из settings.json ``` -Дефолты при пустом кеше: `xhigh` для `ai-claude`, `high` для остальных Claude Code лаунчеров. +Дефолты: `xhigh` для `ai-claude`, `high` для остальных Claude Code лаунчеров. ## Рекомендации - Для повседневной работы: `high` или `xhigh`. - `max` effort имеет реальный эффект у Anthropic и DeepSeek; у Kimi это все тот же thinking on. - `low`/`medium` у DeepSeek и Kimi фактически не снижают reasoning. -- Смена уровня на `low`..`xhigh`: обычным `/effort`; выход из `max`: через `AI_EFFORT= ai-`. diff --git a/home-configs/claude/statusline-command.sh b/home-configs/claude/statusline-command.sh index 3899947..2670e28 100755 --- a/home-configs/claude/statusline-command.sh +++ b/home-configs/claude/statusline-command.sh @@ -42,16 +42,6 @@ printf "\033[38;5;252m%s\033[00m" "$short_cwd" if [ -n "$model" ]; then brand_color=$(_brand_color "${AI_LAUNCHER:-}") effort=$(echo "$input" | jq -r ".effort.level // empty") - # Ловим выбранный уровень в кэш лаунчера (чтобы запомнить max между сессиями). - # Когда CLAUDE_CODE_EFFORT_LEVEL выставлена (восстановленная max-сессия) - уровень - # форсится env, кэш НЕ трогаем, чтобы дисплей-баг (.effort.level=xhigh) не затёр max. - if [ -n "${AI_LAUNCHER:-}" ] && [ -z "${CLAUDE_CODE_EFFORT_LEVEL:-}" ] && [ -n "$effort" ]; then - effort_file="$HOME/.cache/ai-setup/effort_${AI_LAUNCHER}" - if [ "$effort" != "$(cat "$effort_file" 2>/dev/null)" ]; then - mkdir -p "$HOME/.cache/ai-setup" - echo "$effort" > "$effort_file" - fi - fi # Аккаунт Claude.ai актуален только для нативных моделей Claude if [[ "$model_id" == claude-* ]]; then account=$(cat ~/.claude/accounts/current 2>/dev/null) @@ -94,6 +84,15 @@ if [ -n "$model" ]; then fi [ -n "$account" ] && printf " %s[%s]\033[00m" "$brand_color" "$account" fi + + # Аккаунт/ключ Kimi — показываем email/имя из .meta, если оно есть, иначе alias. + if [ "${AI_LAUNCHER:-}" = "kimi" ]; then + kimi_account=$(cat "$HOME/.config/ai-setup/kimi_keys/current" 2>/dev/null) + if [ -n "$kimi_account" ]; then + kimi_display=$(cat "$HOME/.config/ai-setup/kimi_keys/${kimi_account}.meta" 2>/dev/null || echo "$kimi_account") + printf " %s[%s]\033[00m" "$brand_color" "$kimi_display" + fi + fi if [ -n "$effort" ]; then printf " %s%s " "$brand_color" "$model" _effort_color "$effort" diff --git a/scripts/ai-setup.sh b/scripts/ai-setup.sh index 9dfa348..ba280f7 100755 --- a/scripts/ai-setup.sh +++ b/scripts/ai-setup.sh @@ -739,8 +739,8 @@ fi # Удаляем старый хук с диска и из ~/.claude/settings.json (нотифаер в Stop сохраняем). info "Удаляю устаревший хук effort-save..." rm -f "$HOME/.claude/hooks/effort-save-hook.sh" -# Осиротевший кэш моделей (источник старой протечки между ai-*); effort_* НЕ трогаем - -# он снова используется для персиста effort (включая max) через CLAUDE_CODE_EFFORT_LEVEL. +# Осиротевший кэш моделей (источник старой протечки между ai-*); effort_* не трогаем - +# они используются статусбаром для отображения текущего уровня. rm -f "$HOME"/.cache/ai-setup/model_* python3 - "$HOME/.claude/settings.json" <<'PYEOF' import sys, json, os @@ -1198,6 +1198,32 @@ _handle_api_response() { return 0 } +# _kimi_account_info: пытается получить email или имя аккаунта Kimi по API ключу. +# Сохраняет найденное значение в stdout. Если не удалось — exit 1. +_kimi_account_info() { + local api_key="$1" + local resp email name + # Пробуем несколько возможных endpoint'ов Moonshot/Kimi Code API. + for url in "https://api.kimi.com/coding/v1/account" "https://api.kimi.com/coding/v1/users/me"; do + resp=$(curl -s --max-time 10 "$url" \ + -H "x-api-key: $api_key" \ + -H "Accept: application/json" \ + 2>/dev/null || echo "") + [ -z "$resp" ] && continue + email=$(echo "$resp" | jq -r '.email // .data.email // .account.email // empty' 2>/dev/null) + name=$(echo "$resp" | jq -r '.name // .data.name // .account.name // empty' 2>/dev/null) + if [ -n "$email" ]; then + echo "$email" + return 0 + fi + if [ -n "$name" ]; then + echo "$name" + return 0 + fi + done + return 1 +} + _open_browser() { local url="$1" if command -v xdg-open &>/dev/null; then xdg-open "$url" 2>/dev/null @@ -1211,8 +1237,7 @@ _open_browser() { # поэтому выбор модели и кэш кастом-моделей НЕ протекают между ai-* лаунчерами. # Общие ресурсы (skills, CLAUDE.md) шарятся симлинком из ~/.claude. # model и effortLevel сидируются как дефолты - выбор юзера через /model и /effort -# (для low/medium/high/xhigh) сохраняется нативно в этом же settings.json. -# Уровень max обрабатывается отдельно в _apply_effort (settings.json его не хранит). +# сохраняется нативно в этом же settings.json (включая max через AI_EFFORT). # Использование: _setup_isolated_config _setup_isolated_config() { local launcher="$1" default_model="$2" default_effort="${3:-high}" avail="${4:-}" @@ -1252,36 +1277,54 @@ with open(cfg_settings, "w") as f: json.dump(data, f, indent=2, ensure_ascii=False) f.write("\n") PYEOF + + # Деплой shared хуков аккаунтов в изолированный config dir. + # ai-claude использует ~/.claude/hooks напрямую; остальные лаунчеры получают + # свою копию, чтобы /switch-account и /add-account работали изолированно. + mkdir -p "$cfg/hooks" + for hook in switch-account-hook.sh add-account-hook.sh account-email.sh; do + if [ -f "$HOME/.claude/hooks/$hook" ]; then + cp "$HOME/.claude/hooks/$hook" "$cfg/hooks/$hook" + chmod +x "$cfg/hooks/$hook" + fi + done + # Регистрируем UserPromptSubmit хуки в изолированном settings.json + python3 - "$cfg/settings.json" "$cfg/hooks/switch-account-hook.sh" "$cfg/hooks/add-account-hook.sh" <<'PYEOF' +import sys, json, os +settings_path, switch_hook, add_hook = sys.argv[1:4] +data = {} +if os.path.exists(settings_path): + try: + with open(settings_path) as f: + data = json.load(f) + except Exception: + pass +data.setdefault("hooks", {}).setdefault("UserPromptSubmit", [{"hooks": []}]) +ups = data["hooks"]["UserPromptSubmit"] +for hook_path in (switch_hook, add_hook): + hook_cmd = f'bash "{hook_path}"' + already = any( + any(h.get("command", "") == hook_cmd for h in entry.get("hooks", [])) + for entry in ups + ) + if not already: + ups[0]["hooks"].append({"type": "command", "command": hook_cmd}) +with open(settings_path, "w") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + f.write("\n") +PYEOF } -# _apply_effort: per-launcher persistence уровня effort (гибрид). -# - low/medium/high/xhigh живут нативно в settings.json лаунчера -> /effort работает, -# уровень сохраняется между сессиями, env-переменная НЕ ставится. -# - max единственный нельзя сохранить в settings.json (он session-only), поэтому -# его восстанавливаем через CLAUDE_CODE_EFFORT_LEVEL. В такой max-сессии /effort -# залочен env-переменной (ограничение Claude Code). -# Текущий уровень (вкл. max) ловит статусбар в ~/.cache/ai-setup/effort_. -# Сменить уровень из max: AI_EFFORT= ai-. +# _apply_effort: per-launcher persistence уровня effort. +# Все уровни (включая max) пишутся в settings.json лаунчера через AI_EFFORT. +# Без AI_EFFORT - effortLevel персистнут нативно в settings.json (Claude Code его читает). +# CLAUDE_CODE_EFFORT_LEVEL не используем - он блокирует /effort внутри сессии. # Использование: _apply_effort _apply_effort() { local launcher="$1" default_effort="${2:-high}" - local f="$HOME/.cache/ai-setup/effort_${launcher}" local settings="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" - local eff if [ -n "${AI_EFFORT:-}" ]; then - # Явный override: запоминаем и применяем - eff="$AI_EFFORT" - mkdir -p "$HOME/.cache/ai-setup" - echo "$eff" > "$f" - else - eff=$(cat "$f" 2>/dev/null) - fi - if [ "$eff" = "max" ]; then - # Единственный способ восстановить max между сессиями - export CLAUDE_CODE_EFFORT_LEVEL=max - elif [ -n "${AI_EFFORT:-}" ] && [ -n "$eff" ]; then - # Явный сброс на low/medium/high/xhigh - пишем нативно в settings.json лаунчера - python3 - "$settings" "$eff" <<'PYEOF' + python3 - "$settings" "$AI_EFFORT" <<'PYEOF' import sys, json, os p, eff = sys.argv[1], sys.argv[2] d = {} @@ -1294,7 +1337,7 @@ with open(p, "w") as fp: json.dump(d, fp, indent=2, ensure_ascii=False); fp.write("\n") PYEOF fi - # Иначе (≤xhigh без AI_EFFORT): ничего не делаем - effortLevel уже персистнут нативно. + # Без AI_EFFORT: effortLevel уже персистнут в settings.json - ничего не делаем. } _build_ai_sys_prompt() { @@ -1451,50 +1494,124 @@ cat > "$BIN_DIR/ai-kimi" << 'KIMIEOF' # ai-kimi - запуск Claude Code через официальный Kimi Code API source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/ai-api-helpers.sh" 2>/dev/null || true -key_file="$HOME/.config/ai-setup/kimi_key" -api_key="" +keys_dir="$HOME/.config/ai-setup/kimi_keys" +legacy_key_file="$HOME/.config/ai-setup/kimi_key" -[ -f "$key_file" ] && api_key=$(cat "$key_file") +# Миграция старого одиночного файла ключа в директорию с ключами. +if [ -f "$legacy_key_file" ] && [ ! -d "$keys_dir" ]; then + mkdir -p "$keys_dir" + cp "$legacy_key_file" "$keys_dir/account1.key" + chmod 600 "$keys_dir/account1.key" + echo "account1" > "$keys_dir/current" +fi -if [ -n "$api_key" ]; then - echo -n "Проверка сохранённого Kimi ключа... " - _claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7" - _handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code" - ret=$_API_RET - if [ $ret -eq 401 ]; then - rm -f "$key_file" - api_key="" - elif [ $ret -eq 429 ]; then - echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] " - read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac - elif [ $ret -ne 0 ]; then - exit 1 +mkdir -p "$keys_dir" + +get_current_alias() { + local current_file="$keys_dir/current" + local alias="" + [ -f "$current_file" ] && alias=$(cat "$current_file") + if [ -n "$alias" ] && [ ! -f "$keys_dir/${alias}.key" ]; then + alias="" + fi + if [ -z "$alias" ]; then + alias=$(ls "$keys_dir"/*.key 2>/dev/null | head -1 | xargs -I{} basename {} .key 2>/dev/null || true) + fi + echo "$alias" +} + +get_key_by_alias() { + local alias="$1" + [ -f "$keys_dir/${alias}.key" ] && cat "$keys_dir/${alias}.key" || echo "" +} + +set_current_alias() { + echo "$1" > "$keys_dir/current" +} + +save_new_key() { + local key="$1" + local alias="$2" + echo "$key" > "$keys_dir/${alias}.key" + chmod 600 "$keys_dir/${alias}.key" +} + +next_free_alias() { + local i=1 + while [ -f "$keys_dir/account${i}.key" ]; do + i=$((i + 1)) + done + echo "account${i}" +} + +# Миграция устаревшего default.key в accountN.key (новая схема именования). +if [ -f "$keys_dir/default.key" ]; then + old_default_alias=$(next_free_alias) + mv "$keys_dir/default.key" "$keys_dir/${old_default_alias}.key" + [ -f "$keys_dir/default.meta" ] && mv "$keys_dir/default.meta" "$keys_dir/${old_default_alias}.meta" + current=$(cat "$keys_dir/current" 2>/dev/null || true) + if [ "$current" = "default" ]; then + echo "$old_default_alias" > "$keys_dir/current" fi fi -if [ -z "$api_key" ]; then +prompt_and_save_key() { echo "Получить ключ: https://www.kimi.com/code" read -r -p "Введите ваш Kimi API ключ: " api_key - [ -z "$api_key" ] && { echo "Выход."; exit 1; } + [ -z "$api_key" ] && { echo "Выход."; return 1; } echo -n "Проверяю ключ и баланс... " _claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7" _handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code" - ret=$_API_RET + local ret=$_API_RET if [ $ret -eq 0 ] || [ $ret -eq 429 ]; then - mkdir -p "$(dirname "$key_file")" - echo "$api_key" > "$key_file" - chmod 600 "$key_file" - echo "Ключ сохранён." + local alias=$(next_free_alias) + save_new_key "$api_key" "$alias" + # Пытаемся получить email/имя аккаунта для отображения в статусной строке. + meta=$(_kimi_account_info "$api_key" 2>/dev/null || true) + if [ -n "$meta" ]; then + echo "$meta" > "$keys_dir/${alias}.meta" + chmod 600 "$keys_dir/${alias}.meta" + fi + set_current_alias "$alias" + echo "Ключ сохранён как: $alias." if [ $ret -eq 429 ]; then echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] " - read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac + read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) return 1 ;; esac fi + return 0 else echo "Ключ НЕ сохранён." - exit 1 + return 1 fi -fi +} + +validate_current_key() { + local alias="$1" + local api_key=$(get_key_by_alias "$alias") + if [ -z "$api_key" ]; then + return 1 + fi + echo -n "Проверка сохранённого Kimi ключа... " + _claude_test_api "https://api.kimi.com/coding/v1/messages" "x-api-key: $api_key" "kimi-k2.7" + _handle_api_response "Kimi" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Пополните баланс: https://www.kimi.com/code" + local ret=$_API_RET + if [ $ret -eq 401 ]; then + # Удаляем невалидный ключ. + rm -f "$keys_dir/${alias}.key" "$keys_dir/${alias}.meta" + # Если ключей не осталось — сбрасываем current. + if [ -z "$(ls "$keys_dir"/*.key 2>/dev/null)" ]; then + rm -f "$keys_dir/current" + fi + return 1 + elif [ $ret -eq 429 ]; then + echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] " + read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) return 2 ;; esac + elif [ $ret -ne 0 ]; then + return 2 + fi + return 0 +} if ! command -v claude &>/dev/null; then echo "Ошибка: Claude Code не найден. Установите через npm:" @@ -1502,32 +1619,71 @@ if ! command -v claude &>/dev/null; then exit 1 fi -_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX) -trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM -_build_ai_sys_prompt > "$_PROMPT_FILE" -export AI_LAUNCHER=kimi -export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/kimi" -# Пикер: Opus/Sonnet = Kimi K2.7 Code, Haiku = Kimi K2.6. availableModels НЕ задаём -# (он схлопывает пикер в Default). Claude Code навязывает 3 слота opus/sonnet/haiku; -# незаданный показал бы чужой Claude, поэтому opus+sonnet = K2.7, haiku = K2.6 -# DISABLE_1M убирает [1M] дубли из пикера. -_setup_isolated_config kimi opus high '' -_apply_effort kimi high -ANTHROPIC_BASE_URL=https://api.kimi.com/coding \ -ANTHROPIC_AUTH_TOKEN="$api_key" \ -ANTHROPIC_DEFAULT_OPUS_MODEL=kimi-k2.7 \ -ANTHROPIC_DEFAULT_OPUS_MODEL_NAME="Kimi K2.7 Code" \ -ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION="Kimi K2.7 Code — флагманский программист (Moonshot AI)" \ -ANTHROPIC_DEFAULT_SONNET_MODEL=kimi-k2.7 \ -ANTHROPIC_DEFAULT_SONNET_MODEL_NAME="Kimi K2.7 Code" \ -ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="Kimi K2.7 Code — флагманский программист (Moonshot AI)" \ -ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6 \ -ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="Kimi K2.6" \ -ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="Kimi K2.6 — быстрый универсал (Moonshot AI)" \ -CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \ -CLAUDE_CODE_DISABLE_1M_CONTEXT=1 \ -CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ -claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@" +last_alias="" +while true; do + current_alias=$(get_current_alias) + api_key=$(get_key_by_alias "$current_alias") + + # Если ключей нет — запросить первый. + if [ -z "$api_key" ]; then + prompt_and_save_key || exit 1 + current_alias=$(get_current_alias) + api_key=$(get_key_by_alias "$current_alias") + fi + + # Проверка сохранённого ключа. + if [ -n "$api_key" ]; then + validate_current_key "$current_alias" + ret=$? + if [ $ret -eq 1 ]; then + # Невалидный ключ удалён; пересчитаем current и попробуем снова. + continue + elif [ $ret -eq 2 ]; then + exit 1 + fi + fi + + _PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX) + trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM + _build_ai_sys_prompt > "$_PROMPT_FILE" + + last_alias="$current_alias" + + export AI_LAUNCHER=kimi + export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/kimi" + # Пикер: Opus/Sonnet = Kimi K2.7 Code, Haiku = Kimi K2.6. availableModels НЕ задаём + # (он схлопывает пикер в Default). Claude Code навязывает 3 слота opus/sonnet/haiku; + # незаданный показал бы чужой Claude, поэтому opus+sonnet = K2.7, haiku = K2.6 + # DISABLE_1M убирает [1M] дубли из пикера. + _setup_isolated_config kimi opus high '' + _apply_effort kimi high + ANTHROPIC_BASE_URL=https://api.kimi.com/coding \ + ANTHROPIC_AUTH_TOKEN="$api_key" \ + ANTHROPIC_DEFAULT_OPUS_MODEL=kimi-k2.7 \ + ANTHROPIC_DEFAULT_OPUS_MODEL_NAME="Kimi K2.7 Code" \ + ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION="Kimi K2.7 Code — флагманский программист (Moonshot AI)" \ + ANTHROPIC_DEFAULT_SONNET_MODEL=kimi-k2.7 \ + ANTHROPIC_DEFAULT_SONNET_MODEL_NAME="Kimi K2.7 Code" \ + ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="Kimi K2.7 Code — флагманский программист (Moonshot AI)" \ + ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6 \ + ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="Kimi K2.6" \ + ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="Kimi K2.6 — быстрый универсал (Moonshot AI)" \ + CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \ + CLAUDE_CODE_DISABLE_1M_CONTEXT=1 \ + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ + claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@" + claude_exit=$? + + # Если хук сменил ключ, перезапускаем ai-kimi с новым. + new_alias=$(get_current_alias) + if [ -n "$new_alias" ] && [ "$new_alias" != "$last_alias" ]; then + echo "" + echo "Kimi ключ изменён (/switch-account или /add-account). Перезапускаю ai-kimi с новым ключом..." + continue + fi + + exit $claude_exit +done KIMIEOF chmod +x "$BIN_DIR/ai-kimi" @@ -1688,6 +1844,14 @@ if [ "$USE_VLESS" -eq 1 ]; then success "proxychains4 интегрирован" fi +# Подготавливаем изолированные config dir'ы для Claude Code-лаунчеров, +# чтобы hooks (/switch-account, /add-account) и settings.json были на месте +# ещё до первого запуска пользователем. +source "$BIN_DIR/ai-api-helpers.sh" +_setup_isolated_config deepseek opus high '' +_setup_isolated_config kimi opus high '' +_setup_isolated_config openrouter "openai/gpt-5.5" high '' + info "Удаляю старые версии скриптов (claude_*)..." rm -f "$BIN_DIR/claude_gpt" "$BIN_DIR/claude_deepseek" "$BIN_DIR/claude_kimi" "$BIN_DIR/claude_gemini" "$BIN_DIR/claude_api_helpers.sh"