Compare commits

..

3 Commits

Author SHA1 Message Date
Виталий Никитенко
57857cda4d remove bundled skills; skills now come from separate repo
Удалены все кастомные скиллы из home-configs/claude/skills и логика
их деплоя (секция 6.6 ai-setup.sh для Claude и Gemini). Из README
убраны упоминания про деплой скиллов, из test_fixes.sh — тест наличия
android-скилла. Хуки аккаунтов, команда android init, правило #5 про
reusable skills и симлинк общей папки ~/.claude/skills оставлены
нетронутыми — скиллы теперь устанавливаются из отдельного репозитория.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:44:05 +03:00
ekaterina soloveva
0c5057bc21 require node >=18 in ai-setup, not just presence
ai-setup.sh проверял только наличие node через command -v, поэтому системный Node v12 (Ubuntu 22.04) проходил проверку и установка NodeSource 22 не запускалась. Из-за этого хук claude-notifier падал с SyntaxError на optional chaining (?.) и nullish coalescing (??), которые поддерживаются только начиная с Node 14. Теперь скрипт извлекает мажорную версию и при значении меньше 18 ставит/обновляет Node, чтобы регенерация не возвращала сломанные хуки.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 11:26:49 +03:00
Виталий Никитенко
c379ee8efc feat(ai-setup): /logout для ai-glm, ai-deepseek, ai-kimi
Новый UserPromptSubmit хук logout-account-hook.sh перехватывает /logout
(провайдер по AI_LAUNCHER) и /logout-glm|/logout-deepseek|/logout-kimi
(явно), удаляет сохранённый ключ провайдера. Для ai-claude /logout
отступает — работает нативный oauth-logout.

- хук: удаляет glm_key/deepseek_key или текущий kimi_keys/<current>.key
  (+.meta) с пересчётом current; exit 2 глушит нативную /logout для
  api-key провайдеров
- 3 скилла logout-{glm,deepseek,kimi} для автокомплита
- ai-setup.sh: секция деплоя 6.7.3 + хук в копирование/регистрацию
  изолированных cfg

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 10:10:14 +03:00
9 changed files with 112 additions and 284 deletions

View File

@@ -17,7 +17,6 @@ home-configs/
├── GLOBAL_RULES.md # глобальные правила для всех агентов
├── claude/
│ ├── hooks/ # хуки аккаунтов Claude Code
│ ├── skills/ # кастомные скиллы для Claude Code (и Gemini)
│ └── statusline-command.sh # статусная строка Claude Code
└── network/
└── README.md # подробная документация по сетевой настройке
@@ -164,7 +163,6 @@ bash tests/test_network.sh # полные тесты
Также устанавливается:
- `~/.config/ai-setup/global_rules.md` и native rule-файлы (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, `~/.kimi-code/AGENTS.md`, `~/.gemini/GEMINI.md`)
- Скиллы из `home-configs/claude/skills/` в `~/.claude/skills/` и `~/.gemini/config/plugins/local-setup/skills/`
## Android CLI

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Copyright (c) 2026 Виталий Никитенко. MIT License.
# UserPromptSubmit hook: перехватывает /logout, /logout-glm, /logout-deepseek, /logout-kimi.
# Удаляет сохранённый API-ключ провайдера, чтобы при следующем запуске лаунчер
# запросил новый.
# /logout-glm | /logout-deepseek | /logout-kimi — явно указанный провайдер.
# /logout (без суффикса) — провайдер по AI_LAUNCHER (glm/deepseek/kimi).
# Для ai-claude и прочих лаунчеров /logout отступает (exit 0) — пусть работает
# нативный oauth-logout. exit 2 после удаления: LLM не нужна, и нативная
# /logout не пытается сделать бессмысленный oauth-logout для api-key провайдеров.
input=$(cat)
prompt=$(echo "$input" | jq -r '.user_prompt // .prompt // empty' 2>/dev/null)
normalized=$(echo "$prompt" | sed 's|^[[:space:]]*/||; s|[[:space:]]*$||')
cmd=$(echo "$normalized" | awk '{print $1}')
target=""
case "$cmd" in
logout-glm) target=glm ;;
logout-deepseek) target=deepseek ;;
logout-kimi) target=kimi ;;
logout)
case "${AI_LAUNCHER:-claude}" in
glm|deepseek|kimi) target="$AI_LAUNCHER" ;;
*) exit 0 ;;
esac
;;
*) exit 0 ;;
esac
CFG="$HOME/.config/ai-setup"
case "$target" in
glm)
rm -f "$CFG/glm_key"
echo "✓ GLM ключ удалён. Перезапусти ai-glm, чтобы ввести новый." >&2
;;
deepseek)
rm -f "$CFG/deepseek_key"
echo "✓ DeepSeek ключ удалён. Перезапусти ai-deepseek, чтобы ввести новый." >&2
;;
kimi)
KEYS_DIR="$CFG/kimi_keys"
current=$(cat "$KEYS_DIR/current" 2>/dev/null || true)
if [ -n "$current" ]; then
rm -f "$KEYS_DIR/${current}.key" "$KEYS_DIR/${current}.meta"
fi
# Пересчёт current: переключаемся на первый оставшийся ключ либо сбрасываем.
mapfile -t keys < <(ls "$KEYS_DIR"/*.key 2>/dev/null | xargs -I{} basename {} .key | sort)
if [ "${#keys[@]}" -gt 0 ]; then
echo "${keys[0]}" > "$KEYS_DIR/current"
echo "✓ Kimi ключ (${current:-нет}) удалён. Активен: ${keys[0]}. Перезапусти ai-kimi." >&2
else
rm -f "$KEYS_DIR/current"
echo "✓ Kimi ключ (${current:-нет}) удалён. Других ключей нет. Перезапусти ai-kimi." >&2
fi
;;
esac
exit 2

View File

@@ -1,12 +0,0 @@
---
name: add-account
description: Add a new account or API key (handled by UserPromptSubmit hook, no LLM needed)
---
Хук уже обработал запрос. Ответь ТОЛЬКО этим текстом (без markdown, без лишних слов):
Для Claude.ai: браузер открыт — авторизуйся там. После авторизации новый аккаунт сохранится автоматически и сразу станет активным (Claude Code на Linux перечитывает токен на лету).
Для Kimi: укажи API-ключ в том же сообщении: `/add-account sk-...`. Хук проверит и сохранит ключ; ai-kimi перезапустится с ним.
/switch-account переключает между всеми сохранёнными аккаунтами/ключами по кругу.

View File

@@ -1,87 +0,0 @@
---
name: android-cli
description: Google Android CLI - официальный терминальный инструмент для Android-разработки, SDK, эмуляторов и проектов
---
# Android CLI
## Назначение
Google Android CLI (preview) - официальный инструмент от Google для Android-разработки через терминал. Он создан специально для AI-агентов, CI/CD и автоматизации. Позволяет управлять SDK, создавать проекты, запускать эмуляторы и собирать APK без необходимости вручную разбираться с `sdkmanager`, `adb` и Gradle.
## Установка и обновление
```bash
# Установка (выполняется ai-setup.sh)
curl -fsSL https://dl.google.com/android/cli/latest/linux_x86_64/install.sh | bash
# Обновление
android update
```
## Ключевые команды
| Команда | Назначение |
|---------|------------|
| `android --version` | Версия Android CLI |
| `android info` | Информация об SDK, путях, эмуляторах |
| `android sdk list` | Список доступных SDK-пакетов |
| `android sdk install <package>` | Установить пакет SDK |
| `android create --name="App" --output=./app` | Создать новый Android-проект |
| `android emulator create` | Создать виртуальное устройство |
| `android emulator start` | Запустить эмулятор |
| `android run --apks <path>` | Установить и запустить APK на устройстве/эмуляторе |
| `android init` | Развернуть skills для AI-агентов в `~/.claude/skills/` |
| `android update` | Обновить сам Android CLI |
## Workflow с Claude Code
1. **Проверь окружение**:
```bash
android info
```
2. **Установи нужные SDK-компоненты**:
```bash
android sdk install platforms/android-35 build-tools/35.0.0
```
3. **Создай проект** (если нужен новый):
```bash
android create --name="MyApp" --output=./myapp
```
4. **Собери проект**:
```bash
./gradlew assembleDebug
```
5. **Запусти на эмуляторе или устройстве**:
```bash
android emulator start
android run --apks app/build/outputs/apk/debug/app-debug.apk
```
## Когда использовать Android CLI
- Для управления SDK и установки build-tools/platforms без Android Studio.
- Для создания новых Android-проектов из терминала.
- Для запуска эмулятора и установки APK одной командой.
- Для работы в CI/CD или внутри AI-агентов.
## Когда НЕ нужен Android CLI
- Для визуального редактирования UI/Layout - используй Android Studio.
- Для отладки с breakpoints, Layout Inspector, Compose Preview - используй Android Studio.
- Для сложного рефакторинга больших проектов - используй Claude Code в паре с Android Studio.
## Ограничения
- **Preview-версия**: API и команды могут меняться. Обновляй через `android update`.
- **Windows**: `android emulator` отключён в v0.7.
- **Существующий SDK**: если уже установлен Android Studio или `sdkmanager`, Android CLI может использовать тот же SDK или создать новый в `~/.android/sdk`. Проверяй активный путь через `android info`.
## Полезные ссылки
- Официальная документация: https://d.android.com/tools/agents
- Android CLI skills: https://github.com/android/skills

View File

@@ -1,62 +0,0 @@
---
name: el-review-heavy
description: Use when пользователь запрашивает тяжелое code-review между двумя Git-ветками с диффом в стиле GitLab (от merge-base)
---
# Eltex Review Heavy
## Overview
Тяжелое ревью диффа между двумя Git-ветками в стиле GitLab Merge Request (от merge-base). Запускает полный `/code-review` с кучей агентов.
## When to Use
- Пользователь просит "поревьюй ветку feature относительно master" тщательно
- Нужен дифф как на GitLab (без лишних изменений из целевой ветки)
- Формат вызова: `/el-review-heavy <наша-ветка-с-изменениями> <целевая-ветка>`
## Workflow
1. Определи аргументы:
- `source` — первая ветка (наша, с изменениями, которую ревьюим)
- `target` — вторая ветка (целевая, в которую мерж-реквест)
2. Выполни `git fetch origin` — обнови remote refs
3. Если текущая checkout-ветка == `source`, выполни `git pull --ff-only origin source` — обнови локальную ветку
4. Построй дифф от merge-base: `git diff origin/target...origin/source > /tmp/el-review-heavy.diff` (три точки!)
5. Прочитай файл `/tmp/el-review-heavy.diff`
6. Запусти скилл `code-review` с этим файлом как аргумент
7. Полученные findings переформатируй:
- Пронумеруй проблемы
- Оставь только критические замечания
- Для каждой предложи готовый дифф с исправлением
- Объясни простым языком
## Примеры вызова
```
/el-review-heavy feature main
/el-review-heavy my-fix develop
/el-review-heavy TASK-123-payment-screen master
```
## Ошибки и крайние случаи
- Если ветка не существует на remote - сообщи пользователю и не продолжай
- Если дифф пустой - сообщи "изменений между ветками нет" и не продолжай
- Если аргументы не указаны - попроси уточнить: `/el-review-heavy <source-ветка> <target-ветка>`
## Формат вывода
```
## Проблема N: [краткое название]
**Где:** `file.kt:123`
**Что не так:** простым языком, зачем это плохо
**Исправление:**
```diff
- старая строка
+ новая строка
```
```

View File

@@ -1,63 +0,0 @@
---
name: el-review
description: Use when пользователь запрашивает легкое/быстрое code-review между двумя Git-ветками с диффом в стиле GitLab (от merge-base). Триггеры: "быстрое ревью", "легкое ревью", "/el-review".
---
# Eltex Review
## Overview
Лёгкое ревью диффа между двумя Git-ветками в стиле GitLab Merge Request (от merge-base). **Claude делает ревью сам, без multi-agent workflows** — читает дифф и выдаёт только критические проблемы с готовыми исправлениями.
## When to Use
- Пользователь просит "поревьюй ветку feature относительно master" быстро
- Пользователь говорит "быстрое ревью", "лёгкое ревью", "поверхностное ревью"
- Нужен дифф как на GitLab (без лишних изменений из целевой ветки)
- Формат вызова: `/el-review <наша-ветка-с-изменениями> <целевая-ветка>`
## Workflow
1. Определи аргументы:
- `source` — первая ветка (наша, с изменениями, которую ревьюим)
- `target` — вторая ветка (целевая, в которую мерж-реквест)
2. Выполни `git fetch origin` — обнови remote refs
3. Если текущая checkout-ветка == `source`, выполни `git pull --ff-only origin source` — обнови локальную ветку
4. Построй дифф от merge-base: `git diff origin/target...origin/source > /tmp/el-review.diff` (три точки!)
5. Прочитай файл `/tmp/el-review.diff`
6. **Сделай инлайн-ревью сам** — не запускай `/review`, не спавни multi-agent workflows, не делай deep cross-file investigations:
- Найди только критические проблемы: баги, утечки, падения, гонки, ошибки в бизнес-логике, нарушения инвариантов
- **Не репорти:** форматирование, именование переменных, стиль кода, предложения по рефакторингу (если это не баг)
- Для каждой проблемы сразу пиши готовый diff с исправлением
- Объясняй кратко, на русском, простым языком
- Если критических проблем нет — напиши **«Критических проблем не найдено»** и остановись
## Примеры вызова
```
/el-review feature main
/el-review my-fix develop
/el-review TASK-123-payment-screen master
```
## Ошибки и крайние случаи
- Если ветка не существует на remote - сообщи пользователю и не продолжай
- Если дифф пустой - сообщи "изменений между ветками нет" и не продолжай
- Если аргументы не указаны - попроси уточнить: `/el-review <source-ветка> <target-ветка>`
## Формат вывода
```
## Проблема N: [краткое название]
**Где:** `file.kt:123`
**Что не так:** простым языком, зачем это плохо
**Исправление:**
```diff
- старая строка
+ новая строка
```
```

View File

@@ -1,8 +0,0 @@
---
name: switch-account
description: Switch to next account or API key (handled by UserPromptSubmit hook, no LLM needed)
---
Переключение аккаунта/ключа уже выполнено хуком до того, как ты это читаешь.
Ответь ровно одним символом: `✓`
Никаких инструментов. Никаких объяснений. Только `✓`.

View File

@@ -94,8 +94,19 @@ fi
# ── 1. Node.js ───────────────────────────────────────────────
info "Проверяю Node.js..."
if ! command -v node &>/dev/null; then
info "Пытаюсь установить Node.js..."
# Хуки (claude-notifier и др.) используют optional chaining (?.) и nullish
# coalescing (??) — нужен Node >=18. Проверяем не только наличие node, но и
# мажорную версию, иначе системный v12 (Ubuntu 22.04) проходит проверку, а хуки падают.
node_major=0
if command -v node &>/dev/null; then
node_major="$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)"
fi
if [ "${node_major:-0}" -lt 18 ]; then
if [ "${node_major:-0}" -gt 0 ]; then
info "Найден Node.js v${node_major}, нужен >=18. Обновляю..."
else
info "Пытаюсь установить Node.js..."
fi
if command -v brew &>/dev/null; then
brew install node
elif command -v apt-get &>/dev/null; then
@@ -372,40 +383,6 @@ cp "$CONFIG_DIR/global_rules.md" "$HOME/.gemini/GEMINI.md"
cp "$CONFIG_DIR/global_rules.md" "$HOME/.cursorrules"
success "Native rule-файлы обновлены"
# ── 6.6. Деплой Claude и Gemini skills ───────────────────────
info "Обновляю skills для Claude и Gemini..."
SKILLS_SRC="$SCRIPT_DIR/home-configs/claude/skills"
CLAUDE_SKILLS_DST="$HOME/.claude/skills"
GEMINI_SKILLS_DST="$HOME/.gemini/config/plugins/local-setup/skills"
if [ -d "$SKILLS_SRC" ]; then
mkdir -p "$CLAUDE_SKILLS_DST" "$GEMINI_SKILLS_DST"
# Для Gemini нужен plugin.json, чтобы плагин со скилами загрузился
GEMINI_PLUGIN_DIR="$HOME/.gemini/config/plugins/local-setup"
cat <<EOF > "$GEMINI_PLUGIN_DIR/plugin.json"
{
"name": "local-setup",
"description": "Local custom skills deployed via ai-setup"
}
EOF
for skill_dir in "$SKILLS_SRC"/*; do
[ -d "$skill_dir" ] || continue
skill_name=$(basename "$skill_dir")
# Деплой для Claude
mkdir -p "$CLAUDE_SKILLS_DST/$skill_name"
cp -r "$skill_dir/"* "$CLAUDE_SKILLS_DST/$skill_name/"
# Деплой для Gemini (agy)
mkdir -p "$GEMINI_SKILLS_DST/$skill_name"
cp -r "$skill_dir/"* "$GEMINI_SKILLS_DST/$skill_name/"
done
success "Skills обновлены для Claude и Gemini"
else
info "Папка со skills не найдена, пропускаю"
fi
# ── 6.7. Статусная строка Claude Code ───────────────────────
info "Настраиваю статусную строку Claude Code..."
STATUSLINE_SRC="$SCRIPT_DIR/home-configs/claude/statusline-command.sh"
@@ -557,6 +534,41 @@ else
warn "Файл $ADD_HOOK_SRC не найден, пропускаю"
fi
# ── 6.7.3. Хук logout-account ───────────────────────────────────
info "Деплою хук logout-account..."
LOGOUT_HOOK_SRC="$SCRIPT_DIR/home-configs/claude/hooks/logout-account-hook.sh"
LOGOUT_HOOK_DST="$HOME/.claude/hooks/logout-account-hook.sh"
mkdir -p "$HOME/.claude/hooks"
if [ -f "$LOGOUT_HOOK_SRC" ]; then
cp "$LOGOUT_HOOK_SRC" "$LOGOUT_HOOK_DST"
chmod +x "$LOGOUT_HOOK_DST"
# Прописываем хук в settings.json (идемпотентно)
python3 - "$HOME/.claude/settings.json" "$LOGOUT_HOOK_DST" <<'PYEOF'
import sys, json, os
settings_path, hook_path = sys.argv[1], sys.argv[2]
data = {}
if os.path.exists(settings_path):
with open(settings_path) as f:
try: data = json.load(f)
except json.JSONDecodeError: pass
data.setdefault("hooks", {}).setdefault("UserPromptSubmit", [{"hooks": []}])
hook_cmd = f'bash "{hook_path}"'
ups = data["hooks"]["UserPromptSubmit"]
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
success "Хук logout-account установлен"
else
warn "Файл $LOGOUT_HOOK_SRC не найден, пропускаю"
fi
# ── 6.8. Регистрация официального маркетплейса плагинов Claude ──
info "Настраиваю маркетплейс плагинов Claude Code..."
if ! command -v claude &>/dev/null; then
@@ -989,16 +1001,16 @@ PYEOF
# 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
for hook in switch-account-hook.sh add-account-hook.sh logout-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'
python3 - "$cfg/settings.json" "$cfg/hooks/switch-account-hook.sh" "$cfg/hooks/add-account-hook.sh" "$cfg/hooks/logout-account-hook.sh" <<'PYEOF'
import sys, json, os
settings_path, switch_hook, add_hook = sys.argv[1:4]
settings_path = sys.argv[1]
data = {}
if os.path.exists(settings_path):
try:
@@ -1008,7 +1020,7 @@ if os.path.exists(settings_path):
pass
data.setdefault("hooks", {}).setdefault("UserPromptSubmit", [{"hooks": []}])
ups = data["hooks"]["UserPromptSubmit"]
for hook_path in (switch_hook, add_hook):
for hook_path in sys.argv[2:]:
hook_cmd = f'bash "{hook_path}"'
already = any(
any(h.get("command", "") == hook_cmd for h in entry.get("hooks", []))

View File

@@ -188,15 +188,6 @@ test_android_symlink_to_bin_dir() {
fi
}
test_android_skill_exists() {
skill_file="$(cd "$(dirname "$0")/.." && pwd)/home-configs/claude/skills/android-cli/SKILL.md"
if [ -f "$skill_file" ]; then
ok "android-cli: custom skill file exists"
else
fail "android-cli: custom skill file is missing"
fi
}
# ── ai-opencode: launches claude with routatic-proxy backend ──────────────────
test_opencode_claude_launcher() {
if echo "$OPENCODE_SECTION" | grep -q 'claude --dangerously-skip-permissions' \
@@ -257,7 +248,6 @@ test_fix7_trap_tmp
test_android_cli_install_url
test_android_init_called
test_android_symlink_to_bin_dir
test_android_skill_exists
test_opencode_claude_launcher
test_opencode_routatic_proxy
test_opencode_key_management