#!/usr/bin/env bash # UserPromptSubmit hook: перехватывает /switch-account и циклически меняет аккаунт. # Текущий аккаунт определяется ПО ТОКЕНУ в .credentials.json (account-email.sh), # а не по claude auth status — он читает рассинхронизированный oauthAccount. # На Linux Claude Code перечитывает .credentials.json на лету: новый аккаунт # применяется со следующего сообщения, перезапуск не нужен. # exit 0 (не exit 2): /switch-account доходит до Claude, грузится скилл, # отвечает "✓" (1 токен) — так перерисовывается статусная строка. input=$(cat) prompt=$(echo "$input" | jq -r '.user_prompt // .prompt // empty' 2>/dev/null) normalized=$(echo "$prompt" | sed 's|^[[:space:]]*/||; s|[[:space:]]*$||') [ "$normalized" != "switch-account" ] && exit 0 ACCOUNTS_DIR="$HOME/.claude/accounts" CREDS="$HOME/.claude/.credentials.json" CURRENT_FILE="$ACCOUNTS_DIR/current" EMAIL_HELPER="$HOME/.claude/hooks/account-email.sh" mkdir -p "$ACCOUNTS_DIR" # Реальный текущий аккаунт — по токену активной сессии (не по хрупкому current) current=$(bash "$EMAIL_HELPER" "$CREDS" 2>/dev/null) [ -z "$current" ] && current=$(cat "$CURRENT_FILE" 2>/dev/null) # Сохранить актуальные (возможно обновлённые рефрешем) токены под реальным email. # current выведен из самого токена — порча файла другого аккаунта исключена. if [ -n "$current" ] && [ -f "$CREDS" ]; then cp "$CREDS" "$ACCOUNTS_DIR/${current}.credentials.json" chmod 600 "$ACCOUNTS_DIR/${current}.credentials.json" fi mapfile -t accounts < <(ls "$ACCOUNTS_DIR"/*.credentials.json 2>/dev/null \ | xargs -I{} basename {} .credentials.json | sort) if [ ${#accounts[@]} -le 1 ]; then echo "Только один аккаунт (${current:-нет}). Добавь второй через /add-account." >&2 exit 2 fi # Найти следующий по кругу idx=-1 for i in "${!accounts[@]}"; do [ "${accounts[$i]}" = "$current" ] && idx=$i && break done next_idx=$(( (idx + 1) % ${#accounts[@]} )) next="${accounts[$next_idx]}" cp "$ACCOUNTS_DIR/${next}.credentials.json" "$CREDS" chmod 600 "$CREDS" echo "$next" > "$CURRENT_FILE" exit 0