Compare commits
5 Commits
60b915de5f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3468095c9 | ||
|
|
676b25217e | ||
|
|
9448212a26 | ||
|
|
4331ebb675 | ||
|
|
8c7c2e9e0f |
@@ -15,10 +15,10 @@
|
||||
|
||||
| Claude Code `/effort` | Anthropic (`ai-claude`) | DeepSeek V4 (`ai-deepseek`) | Kimi K2.7/K2.6 (`ai-kimi`) | GLM 5.3 (`ai-glm`) | OpenRouter/GPT-5.5 (`ai-openrouter`) | OpenCode Go (`ai-opencode`) |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| `low` | нативно `low` | провайдер поднимает до `high` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
| `low` | нативно `low` | нативно `low` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
| `medium` | нативно `medium` | провайдер поднимает до `high` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
| `high` | нативно `high` | нативно `high` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
| `xhigh` | нативно `xhigh` | провайдер поднимает до `max` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
| `xhigh` | нативно `xhigh` | провайдер поднимает до `high` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
| `max` | нативно `max` | нативно `max` | thinking on | на стороне сервера | без локального маппинга | без локального маппинга |
|
||||
|
||||
Для OpenRouter текущий лаунчер не делает локального преобразования effort и отправляет
|
||||
@@ -45,12 +45,13 @@ low -> medium -> high -> xhigh -> max
|
||||
### DeepSeek V4
|
||||
|
||||
```text
|
||||
high -> max
|
||||
low -> high -> max
|
||||
```
|
||||
|
||||
- 2 реальных уровня.
|
||||
- `low` и `medium` фактически поднимаются до `high`.
|
||||
- `xhigh` фактически поднимается до `max`.
|
||||
- 3 реальных уровня.
|
||||
- `low` остаётся `low` — реально снижает thinking бюджет.
|
||||
- `medium` и `xhigh` фактически поднимаются до `high`.
|
||||
- `max` остаётся `max` — максимальный бюджет thinking.
|
||||
- Маппинг выполняется на стороне DeepSeek API.
|
||||
- Дефолт `ai-deepseek`: `high`.
|
||||
|
||||
@@ -125,4 +126,4 @@ ai-deepseek # без флага - использовать ур
|
||||
|
||||
- Для повседневной работы: `high` или `xhigh`.
|
||||
- `max` effort имеет реальный эффект у Anthropic и DeepSeek; у Kimi и GLM это всё тот же thinking on / маппинг на стороне сервера.
|
||||
- `low`/`medium` у DeepSeek, Kimi и GLM фактически не снижают reasoning.
|
||||
- `low`/`medium` у Kimi и GLM фактически не снижают reasoning; у DeepSeek `low` теперь даёт реальный уровень `low` и экономит бюджет.
|
||||
|
||||
@@ -23,6 +23,7 @@ Use these defaults across projects, then apply any more specific repository inst
|
||||
- For requests to answer, explain, review, diagnose, or plan, inspect the relevant materials and report the result. Do not implement changes unless the request also asks for them.
|
||||
- For requests to change, build, or fix, perform safe in-scope local edits and validation without asking for routine confirmation.
|
||||
- Ask before destructive or difficult-to-recover actions, external writes or publication, purchases, or a material expansion of scope.
|
||||
- Exception for Eltex merge-request reviews: a bare review request or explicit `eltex-review` invocation authorizes the skill's documented GitLab writes, including inline discussions, approval, and its thank-you note. Explicit chat-only wording keeps the review read-only.
|
||||
- Preserve unrelated user changes and adapt to a dirty working tree. Never discard or overwrite work you did not create.
|
||||
|
||||
## 4. Plan and Verify Proportionally
|
||||
|
||||
@@ -6,7 +6,7 @@ The custom statusline script renders **only ASCII-safe characters and ANSI color
|
||||
|
||||
- ASCII brackets `[ ]` for git branch and effort level
|
||||
- ANSI 256-color escape sequences for model names, effort levels, and rate-limit percentages
|
||||
- The `¥` symbol (U+00A5) for CNY balance in DeepSeek output
|
||||
- The `$` symbol for the DeepSeek balance (USD only)
|
||||
|
||||
It does **not** use Nerd Font glyphs, powerline separators, or emoji.
|
||||
|
||||
|
||||
@@ -4,6 +4,12 @@ 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')
|
||||
# Суффикс [1m] в отображении не нужен: окно контекста задаётся
|
||||
# CLAUDE_CODE_MAX_CONTEXT_TOKENS, а не суффиксом модели
|
||||
model="${model/\[1m\]/}"
|
||||
model="${model% \[1M\]}"
|
||||
model="${model% (1M context)}"
|
||||
model_id="${model_id/\[1m\]/}"
|
||||
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')
|
||||
@@ -11,15 +17,18 @@ week_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
|
||||
ctx_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
|
||||
|
||||
|
||||
# Разделитель сегментов: жирная серая средняя точка ·
|
||||
_sep() { printf " \033[1m\033[38;5;240m·\033[00m "; }
|
||||
|
||||
# Цвет effort: насыщенные, глубже чем пастельные (jewel tones)
|
||||
_effort_color() {
|
||||
case "$1" in
|
||||
low) printf '\033[38;5;220m[low]\033[00m' ;; # золотой
|
||||
medium) printf '\033[38;5;50m[medium]\033[00m' ;; # насыщенный teal
|
||||
high) printf '\033[38;5;38m[high]\033[00m' ;; # глубокий голубой
|
||||
xhigh) printf '\033[38;5;206m[xhigh]\033[00m' ;; # насыщенный фиолетовый
|
||||
max) printf '\033[38;5;210m[\033[38;5;220mm\033[38;5;114ma\033[38;5;50mx\033[38;5;206m]\033[00m' ;; # радуга
|
||||
*) printf '\033[38;5;252m[%s]\033[00m' "$1" ;;
|
||||
low) printf '\033[38;5;220mlow\033[00m' ;; # золотой
|
||||
medium) printf '\033[38;5;50mmedium\033[00m' ;; # насыщенный teal
|
||||
high) printf '\033[38;5;38mhigh\033[00m' ;; # глубокий голубой
|
||||
xhigh) printf '\033[38;5;206mxhigh\033[00m' ;; # насыщенный фиолетовый
|
||||
max) printf '\033[38;5;220mm\033[38;5;114ma\033[38;5;50mx\033[38;5;206m\033[00m' ;; # радуга
|
||||
*) printf '\033[38;5;252m%s\033[00m' "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -29,6 +38,7 @@ _brand_color() {
|
||||
deepseek) printf '\033[38;5;69m' ;; # DeepSeek фирменный синий
|
||||
claude) printf '\033[38;5;173m' ;; # Anthropic оранжевый
|
||||
kimi) printf '\033[38;5;81m' ;; # Kimi голубой
|
||||
glm) printf '\033[38;5;223m' ;; # Z.ai кремовый (бренд-фон z.ai, #FFE4B5)
|
||||
openrouter) printf '\033[38;5;135m' ;; # OpenRouter фиолетовый
|
||||
*) printf '\033[38;5;223m' ;; # кремовый (fallback)
|
||||
esac
|
||||
@@ -39,7 +49,7 @@ branch=$(git -C "$cwd" --no-optional-locks symbolic-ref --short HEAD 2>/dev/null
|
||||
short_cwd="${cwd/#$HOME/\~}"
|
||||
printf "\033[38;5;252m%s\033[00m" "$short_cwd"
|
||||
|
||||
[ -n "$branch" ] && printf " \033[38;5;252m[%s]\033[00m" "$branch"
|
||||
[ -n "$branch" ] && { _sep; printf "\033[38;5;111m%s\033[00m" "$branch"; }
|
||||
if [ -n "$model" ]; then
|
||||
brand_color=$(_brand_color "${AI_LAUNCHER:-}")
|
||||
effort=$(echo "$input" | jq -r ".effort.level // empty")
|
||||
@@ -84,18 +94,20 @@ if [ -n "$model" ]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[ -n "$account" ] && printf " %s[%s]\033[00m" "$brand_color" "$account"
|
||||
[ -n "$account" ] && { _sep; printf "%s%s\033[00m" "$brand_color" "$account"; }
|
||||
fi
|
||||
|
||||
# Аккаунт/ключ Kimi — показываем alias (account1/account2/...).
|
||||
if [ "${AI_LAUNCHER:-}" = "kimi" ]; then
|
||||
kimi_account=$(cat "$HOME/.config/ai-setup/kimi_keys/current" 2>/dev/null)
|
||||
[ -n "$kimi_account" ] && printf " %s[%s]\033[00m" "$brand_color" "$kimi_account"
|
||||
[ -n "$kimi_account" ] && { _sep; printf "%s%s\033[00m" "$brand_color" "$kimi_account"; }
|
||||
fi
|
||||
if [ -n "$effort" ]; then
|
||||
_sep
|
||||
printf "%s%s " "$brand_color" "$model"
|
||||
_effort_color "$effort"
|
||||
else
|
||||
_sep
|
||||
printf "%s%s\033[00m" "$brand_color" "$model"
|
||||
fi
|
||||
fi
|
||||
@@ -138,7 +150,7 @@ 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[38;5;78m%s\033[00m" "$balance"
|
||||
[ -n "$balance" ] && { _sep; printf "\033[38;5;78m%s\033[00m" "$balance"; }
|
||||
fi
|
||||
|
||||
# Фоновое обновление баланса (не чаще раза в 30 секунд)
|
||||
@@ -159,13 +171,12 @@ if [[ "$model_id" == *deepseek* ]]; then
|
||||
import sys, json
|
||||
d = json.load(sys.stdin)
|
||||
infos = d.get('balance_infos', [])
|
||||
symbols = {'USD': '\$', 'CNY': '\u00a5'}
|
||||
parts = []
|
||||
for info in infos:
|
||||
curr = info.get('currency', '')
|
||||
if info.get('currency', '').upper() != 'USD':
|
||||
continue
|
||||
total = info.get('total_balance', '0')
|
||||
sym = symbols.get(curr, curr)
|
||||
parts.append(f'{sym}{total}')
|
||||
parts.append(f'\${total}')
|
||||
if parts:
|
||||
print(' '.join(parts))
|
||||
" 2>/dev/null)
|
||||
@@ -182,7 +193,7 @@ elif [ "${AI_LAUNCHER:-}" = "openrouter" ]; then
|
||||
cache_file="$HOME/.cache/ai-setup/openrouter_balance"
|
||||
if [ -f "$cache_file" ]; then
|
||||
balance=$(head -1 "$cache_file")
|
||||
[ -n "$balance" ] && printf " \033[38;5;78m%s\033[00m" "$balance"
|
||||
[ -n "$balance" ] && { _sep; printf "\033[38;5;78m%s\033[00m" "$balance"; }
|
||||
fi
|
||||
|
||||
# Фоновое обновление баланса (не чаще раза в 30 секунд)
|
||||
@@ -247,13 +258,13 @@ else
|
||||
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"
|
||||
_sep; 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"
|
||||
_sep; printf "%s%s:%s%%\033[00m" "$color" "$remaining" "$week_int"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -263,7 +274,7 @@ fi
|
||||
if [ -n "$ctx_pct" ]; then
|
||||
ctx_int=$(printf '%.0f' "$ctx_pct")
|
||||
color=$(pct_color "$ctx_int")
|
||||
printf " %sctx:%s%%\033[00m" "$color" "$ctx_int"
|
||||
_sep; printf "%sctx:%s%%\033[00m" "$color" "$ctx_int"
|
||||
|
||||
# Звуковой сигнал при первом достижении 60%
|
||||
alert_file="$HOME/.cache/ai-setup/ctx_alert_state"
|
||||
|
||||
@@ -706,25 +706,24 @@ try:
|
||||
d = json.load(sys.stdin)
|
||||
available = d.get('is_available', False)
|
||||
infos = d.get('balance_infos', [])
|
||||
# Показываем только USD (CNY не выводим)
|
||||
infos = [i for i in infos if i.get('currency', '').upper() == 'USD']
|
||||
if not infos:
|
||||
print(' \033[0;33m[БАЛАНС]\033[0m Нет данных о балансе')
|
||||
sys.exit(0)
|
||||
symbols = {'USD': '\$', 'CNY': '¥'}
|
||||
cache_parts = []
|
||||
for info in infos:
|
||||
curr = info.get('currency', '???')
|
||||
total = info.get('total_balance', '0')
|
||||
granted = info.get('granted_balance', '0')
|
||||
topped_up = info.get('topped_up_balance', '0')
|
||||
sym = symbols.get(curr, curr)
|
||||
status = '✅ доступен' if available else '❌ не активен'
|
||||
print(f' \033[1;36m💰 Баланс DeepSeek:\033[0m {total} {curr} {status}')
|
||||
print(f' \033[1;36m💰 Баланс DeepSeek:\033[0m \${total} {status}')
|
||||
if float(granted) > 0:
|
||||
print(f' └─ Начислено: {granted} {curr}')
|
||||
print(f' └─ Начислено: \${granted}')
|
||||
if float(topped_up) > 0:
|
||||
print(f' └─ Пополнено: {topped_up} {curr}')
|
||||
cache_parts.append(f'{sym}{total}')
|
||||
# Cache all currencies with symbols for statusline
|
||||
print(f' └─ Пополнено: \${topped_up}')
|
||||
cache_parts.append(f'\${total}')
|
||||
# Cache USD balance for statusline
|
||||
if cache_parts:
|
||||
cache_dir = os.path.expanduser('~/.cache/ai-setup')
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
@@ -1243,7 +1242,9 @@ fi
|
||||
|
||||
_build_ai_sys_prompt > /dev/null # сохраняет в ~/.codex/AGENTS.md (codex читает авто)
|
||||
_setup_codex_sound_hooks
|
||||
exec "$codex_bin" --dangerously-bypass-approvals-and-sandbox "$@"
|
||||
exec "$codex_bin" --dangerously-bypass-approvals-and-sandbox \
|
||||
-c 'tui.status_line=["model-with-reasoning","git-branch","context-used","five-hour-limit","current-dir"]' \
|
||||
"$@"
|
||||
GPTEOF
|
||||
chmod +x "$BIN_DIR/ai-gpt"
|
||||
|
||||
@@ -1644,9 +1645,10 @@ trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
|
||||
_build_ai_sys_prompt > "$_PROMPT_FILE"
|
||||
export AI_LAUNCHER=glm
|
||||
export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/glm"
|
||||
# Пикер: Opus/Sonnet = GLM 5.3, Haiku = GLM 5.3[1m]. availableModels НЕ задаём
|
||||
# (он схлопывает пикер). Claude Code навязывает 3 слота; незаданный показал бы
|
||||
# чужой Claude, поэтому opus+sonnet = glm-5.3, haiku = glm-5.3[1m].
|
||||
# Пикер: все три слота = glm-5.3. availableModels НЕ задаём (он схлопывает пикер).
|
||||
# Claude Code навязывает 3 слота; незаданный показал бы чужой Claude, поэтому
|
||||
# opus+sonnet+haiku = glm-5.3. Окно контекста у всех слотов задаёт env
|
||||
# CLAUDE_CODE_MAX_CONTEXT_TOKENS ниже — суффикс [1m] больше не нужен.
|
||||
_setup_isolated_config glm opus high ''
|
||||
# ai-glm стартует на /effort max. max невалиден в settings.json (там только
|
||||
# low/medium/high/xhigh), поэтому задаём через CLI --effort, а не _apply_effort.
|
||||
@@ -1657,6 +1659,10 @@ _setup_isolated_config glm opus high ''
|
||||
# а флаг заставлял бы счётчик ctx: считать окно = 200K (врал в ~5 раз). Прочие
|
||||
# лаунчеры (deepseek/kimi/openrouter/opencode) флаг оставляют — у них другой контекст
|
||||
# и флаг убирает [1M]-дубли из пикера; ai-claude его и так не использует.
|
||||
# CLAUDE_CODE_MAX_CONTEXT_TOKENS: glm-* нет в реестре моделей Claude Code, поэтому
|
||||
# окно по умолчанию считается = 200K — ctx: врал бы в ~5 раз, а автокомпакт жал бы
|
||||
# сессию в 200K. Задаём реальное окно 1M явно; env применяется к не-claude моделям
|
||||
# независимо от суффикса, поэтому [1m] у Haiku-слота не нужен и убран.
|
||||
ANTHROPIC_BASE_URL="$glm_base_url" \
|
||||
ANTHROPIC_AUTH_TOKEN="$api_key" \
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5.3 \
|
||||
@@ -1665,10 +1671,11 @@ ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION="GLM 5.3 — флагман Zhipu AI" \
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL=glm-5.3 \
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL_NAME="GLM 5.3" \
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="GLM 5.3 — флагман Zhipu AI" \
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL='glm-5.3[1m]' \
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="GLM 5.3 [1M]" \
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="GLM 5.3 [1M] — расширенный контекст" \
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL=glm-5.3 \
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="GLM 5.3" \
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="GLM 5.3 — флагман Zhipu AI" \
|
||||
CLAUDE_CODE_SUBAGENT_MODEL=glm-5.3 \
|
||||
CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000 \
|
||||
CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 \
|
||||
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
||||
claude --dangerously-skip-permissions --effort "${AI_EFFORT:-max}" --system-prompt-file "$_PROMPT_FILE" "$@"
|
||||
|
||||
@@ -41,6 +41,15 @@ test_gpt_no_proxy() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ── ai-gpt: informative Codex status line ───────────────────────────────────
|
||||
test_gpt_status_line() {
|
||||
if echo "$GPT_SECTION" | grep -Fq 'tui.status_line=["model-with-reasoning","git-branch","context-used","five-hour-limit","current-dir"]'; then
|
||||
ok "ai-gpt: shows model, branch, context usage, primary limit, and directory"
|
||||
else
|
||||
fail "ai-gpt: missing configured Codex status line"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── ai-kimi: launches claude with Kimi backend ────────────────────────────
|
||||
test_kimi_claude_launcher() {
|
||||
if echo "$KIMI_SECTION" | grep -q 'claude --dangerously-skip-permissions' \
|
||||
@@ -86,11 +95,12 @@ test_glm_claude_launcher() {
|
||||
test_glm_official_api() {
|
||||
if echo "$GLM_SECTION" | grep -q 'api.z.ai/api/anthropic' \
|
||||
&& echo "$GLM_SECTION" | grep -q 'ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5.3' \
|
||||
&& echo "$GLM_SECTION" | grep -q "ANTHROPIC_DEFAULT_HAIKU_MODEL='glm-5.3\[1m\]'" \
|
||||
&& echo "$GLM_SECTION" | grep -q 'ANTHROPIC_DEFAULT_HAIKU_MODEL=glm-5.3' \
|
||||
&& echo "$GLM_SECTION" | grep -q 'CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000' \
|
||||
&& echo "$GLM_SECTION" | grep -q 'x-api-key'; then
|
||||
ok "ai-glm: uses official Zhipu AI API (glm-5.3 opus/sonnet, glm-5.3[1m] haiku)"
|
||||
ok "ai-glm: uses official Zhipu AI API (glm-5.3 everywhere, ctx window 1M via env)"
|
||||
else
|
||||
fail "ai-glm: must use official Zhipu AI API with glm-5.3 and x-api-key"
|
||||
fail "ai-glm: must use official Zhipu AI API with glm-5.3, 1M context window and x-api-key"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -285,6 +295,7 @@ test_add_hook_syntax() {
|
||||
test_script_syntax
|
||||
test_gpt_autoinstall
|
||||
test_gpt_no_proxy
|
||||
test_gpt_status_line
|
||||
test_kimi_claude_launcher
|
||||
test_kimi_official_api
|
||||
test_kimi_no_artemox
|
||||
|
||||
Reference in New Issue
Block a user