diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..643a197 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,272 @@ +# AGENTS.md — Глобальные правила для AI-агентов + +> Этот файл читают все агенты: Codex, Gemini, Grok, Claude Code, Factory Droid. +> Он описывает **единственный** разрешённый рабочий процесс. +> Нарушение любого пункта = задача не принята. + +--- + +## 0. Главное правило + +**Каждое изменение в коде начинается с GitHub Issue и заканчивается закрытием этого Issue.** + +Агент не пишет код "в воздух". Нет Issue — нет работы. + +--- + +## 1. Перед началом любой задачи + +### 1.1 Найти Issue + +``` +gh issue list --state open --label agent +``` + +Работать только с Issue у которых: +- label `agent` — готово к выполнению агентом +- label `blocked` отсутствует +- assignee пустой или назначен на тебя + +### 1.2 Назначить себя + +``` +gh issue edit --add-assignee "@me" +gh issue comment --body "🤖 Начинаю выполнение. Агент: " +``` + +### 1.3 Прочитать Issue полностью + +Issue содержит: +- **Что сделать** — конкретная задача +- **Критерии приёмки** — как проверить что готово +- **Что нельзя трогать** — файлы и модули вне задачи + +Если чего-то не хватает для выполнения — оставить комментарий с вопросом, +снять assignee, **не начинать код**. + +--- + +## 2. Рабочий процесс (обязательный порядок) + +``` +Issue открыт + ↓ +git checkout -b issue-- + ↓ +Реализация (только в рамках Issue) + ↓ +cargo test --locked # все тесты зелёные +cargo clippy --all-targets -- -D warnings # ноль предупреждений +cargo fmt --check # форматирование чистое + ↓ +git commit -m "fix/feat/docs(): <описание> (closes #)" + ↓ +git push origin issue-- + ↓ +gh pr create --title "..." --body "Closes #" --base main + ↓ +gh issue comment --body "✅ PR готов: " +``` + +### Формат ветки + +``` +issue-42-add-daily-limit +issue-57-fix-ci-linux-gtk +issue-103-theme-dark-light +``` + +### Формат коммита + +``` +feat(monitor): add daily limit display (closes #42) +fix(ci): install gtk deps for linux gui build (closes #57) +docs(readme): add demo screenshots (closes #103) +``` + +Типы: `feat` `fix` `docs` `refactor` `test` `ci` `chore` + +--- + +## 3. Ограничения при реализации + +### 3.1 Трогать только то что в Issue + +Если Issue про `src/notify.rs` — не рефакторить `src/monitor.rs` попутно. +Попутные улучшения = отдельный Issue. + +### 3.2 Запрещено без явного указания в Issue + +- Менять `Cargo.toml` (добавлять/удалять зависимости) +- Менять `ci.yml` или `release.yml` +- Переименовывать публичные функции и структуры +- Менять формат JSON-вывода (`--json`) +- Трогать `SUBMISSION.ru.md` +- Коммитить `.env` файлы с ключами + +### 3.3 API-ключи + +Никогда не логировать `VIBEMODE_API_KEY`, `NEUROGATE_API_KEY`. +Если ключ попал в вывод — задача не завершена, нужен отдельный Issue на исправление. + +--- + +## 4. Проверка перед PR + +Все три команды должны завершиться с кодом 0: + +```bash +cargo test --locked +cargo clippy --all-targets -- -D warnings +cargo fmt --check +``` + +Дополнительно для GUI-изменений: + +```bash +cargo build --features gui --locked +``` + +Если тесты красные — **не создавать PR**. Исправить и перепроверить. + +--- + +## 5. Оформление PR + +```markdown +## Что сделано +<одна-две строки что изменилось> + +## Как проверить +<конкретные команды для ручной проверки> + +## Closes +Closes # +``` + +Не писать: "я сделал X, Y, Z, W" — только то что в Issue. + +--- + +## 6. После merge + +```bash +gh issue close --comment "✅ Выполнено в PR #" +git checkout main +git pull origin main +git branch -d issue-- +``` + +--- + +## 7. Если что-то пошло не так + +### Тесты не проходят после реализации + +```bash +gh issue comment --body "⚠️ Тесты не проходят: <вывод ошибки>. Нужна помощь." +``` +Снять assignee. Не пушить сломанный код. + +### Issue оказался больше чем ожидалось + +```bash +gh issue comment --body "⚠️ Задача требует изменений в — это за рамками Issue. Создаю дочерний Issue." +gh issue create --title "..." --body "Дочерняя задача от #" --label agent +``` + +### Конфликт с main + +```bash +git fetch origin main +git rebase origin/main +# решить конфликты +git push --force-with-lease origin issue-- +``` + +--- + +## 8. Labels (система меток) + +| Label | Значение | +|-------|----------| +| `agent` | Готово к выполнению агентом | +| `blocked` | Заблокировано — не брать | +| `needs-review` | Ждёт ревью человека | +| `bug` | Баг | +| `feat` | Новая фича | +| `ci` | CI/CD | +| `docs` | Документация | +| `gui` | Изменения в Slint GUI | +| `tui` | Изменения в ratatui TUI | +| `priority:high` | Сделать в первую очередь | +| `priority:low` | Можно отложить | + +Агент берёт задачи в порядке: `priority:high` → без приоритета → `priority:low`. + +--- + +## 9. Что агент НЕ делает самостоятельно + +- Не создаёт Issue сам (только человек) +- Не закрывает чужие Issue +- Не делает force push в `main` +- Не удаляет файлы без явного указания в Issue +- Не меняет версию в `Cargo.toml` (это делает человек перед релизом) +- Не создаёт теги и релизы + +--- + +## 10. Контекст проекта + +**Репозиторий:** https://github.com/xodapi/vimit +**Язык:** Rust 2024 edition +**Тесты:** `cargo test --locked` (58 тестов) +**GUI:** Slint (`--features gui`) +**TUI:** ratatui +**API:** `GET /v1/me` → `VIBEMODE_API_KEY` +**Платформы:** Windows x86_64, Linux x86_64/aarch64, macOS aarch64 + +Структура `src/`: +``` +src/ + main.rs — точка входа, CLI парсинг + api.rs — запрос /v1/me, модели данных + monitor.rs — TUI ratatui монитор + notify.rs — desktop notifications + cli/ + update.rs — self-update логика + bin/ + vimit-gui.rs — Slint GUI +ui/ + app.slint — главный Slint файл +``` + +--- + +## 11. Быстрый старт для нового агента + +```bash +# 1. Посмотреть открытые задачи +gh issue list --state open --label agent --assignee "" + +# 2. Взять задачу +gh issue edit --add-assignee "@me" +gh issue comment --body "🤖 Начинаю" + +# 3. Создать ветку +git checkout -b issue-- + +# 4. Сделать работу, проверить +cargo test --locked && cargo clippy --all-targets -- -D warnings && cargo fmt --check + +# 5. Коммит и PR +git commit -m "feat(...): ... (closes #)" +git push origin issue-- +gh pr create --fill --base main +``` +gh issue create \ + --title "feat(gui): replace 24h card with weekly credit donut chart" \ + --label "agent,gui,feat" \ + --body "..." \ + --repo xodapi/vimit \ No newline at end of file diff --git a/docs/AGENTS (1).md b/docs/AGENTS (1).md new file mode 100644 index 0000000..289221a --- /dev/null +++ b/docs/AGENTS (1).md @@ -0,0 +1,279 @@ +# AGENTS.md — Глобальные правила для AI-агентов + +> Этот файл читают все агенты: Codex, Gemini, Grok, Claude Code, Factory Droid. +> Он описывает **единственный** разрешённый рабочий процесс. +> Нарушение любого пункта = задача не принята. + +--- + +## 0. Главное правило + +**Каждое изменение в коде начинается с GitHub Issue и заканчивается закрытием этого Issue.** + +Агент не пишет код "в воздух". Нет Issue — нет работы. + +--- + +## 1. Перед началом любой задачи + +### 1.1 Найти Issue + +``` +gh issue list --state open --label agent +``` + +Работать только с Issue у которых: +- label `agent` — готово к выполнению агентом +- label `blocked` отсутствует +- assignee пустой или назначен на тебя + +### 1.2 Назначить себя + +``` +gh issue edit --add-assignee "@me" +gh issue comment --body "🤖 Начинаю выполнение. Агент: " +``` + +### 1.3 Прочитать Issue полностью + +Issue содержит: +- **Что сделать** — конкретная задача +- **Критерии приёмки** — как проверить что готово +- **Что нельзя трогать** — файлы и модули вне задачи + +Если чего-то не хватает для выполнения — оставить комментарий с вопросом, +снять assignee, **не начинать код**. + +--- + +## 2. Рабочий процесс (обязательный порядок) + +``` +Issue открыт + ↓ +git checkout -b issue-- + ↓ +Реализация (только в рамках Issue) + ↓ +cargo test --locked # все тесты зелёные +cargo clippy --all-targets -- -D warnings # ноль предупреждений +cargo fmt --check # форматирование чистое + ↓ +git commit -m "fix/feat/docs(): <описание> (closes #)" + ↓ +git push origin issue-- + ↓ +gh pr create --title "..." --body "Closes #" --base main + ↓ +gh issue comment --body "✅ PR готов: " +``` + +### Формат ветки + +``` +issue-42-add-daily-limit +issue-57-fix-ci-linux-gtk +issue-103-theme-dark-light +``` + +### Формат коммита + +``` +feat(monitor): add daily limit display (closes #42) +fix(ci): install gtk deps for linux gui build (closes #57) +docs(readme): add demo screenshots (closes #103) +``` + +Типы: `feat` `fix` `docs` `refactor` `test` `ci` `chore` + +--- + +## 3. Ограничения при реализации + +### 3.1 Трогать только то что в Issue + +Если Issue про `src/notify.rs` — не рефакторить `src/monitor.rs` попутно. +Попутные улучшения = отдельный Issue. + +### 3.2 Запрещено без явного указания в Issue + +- Менять `Cargo.toml` (добавлять/удалять зависимости) +- Менять `ci.yml` или `release.yml` +- Переименовывать публичные функции и структуры +- Менять формат JSON-вывода (`--json`) +- Трогать `SUBMISSION.ru.md` +- Коммитить `.env` файлы с ключами + +### 3.3 API-ключи + +Никогда не логировать `VIBEMODE_API_KEY`, `NEUROGATE_API_KEY`. +Если ключ попал в вывод — задача не завершена, нужен отдельный Issue на исправление. + +--- + +## 4. Проверка перед PR + +Все три команды должны завершиться с кодом 0: + +```bash +cargo test --locked +cargo clippy --all-targets -- -D warnings +cargo fmt --check +``` + +Дополнительно для GUI-изменений: + +```bash +cargo build --features gui --locked +``` + +Если тесты красные — **не создавать PR**. Исправить и перепроверить. + +--- + +## 5. Оформление PR + +```markdown +## Что сделано +<одна-две строки что изменилось> + +## Как проверить +<конкретные команды для ручной проверки> + +## Closes +Closes # +``` + +Не писать: "я сделал X, Y, Z, W" — только то что в Issue. + +--- + +## 6. После merge + +```bash +gh issue close --comment "✅ Выполнено в PR #" +git checkout main +git pull origin main +git branch -d issue-- +``` + +--- + +## 7. Если что-то пошло не так + +### Тесты не проходят после реализации + +```bash +gh issue comment --body "⚠️ Тесты не проходят: <вывод ошибки>. Нужна помощь." +``` +Снять assignee. Не пушить сломанный код. + +### Issue оказался больше чем ожидалось + +```bash +gh issue comment --body "⚠️ Задача требует изменений в — это за рамками Issue. Создаю дочерний Issue." +gh issue create --title "..." --body "Дочерняя задача от #" --label agent +``` + +### Конфликт с main + +```bash +git fetch origin main +git rebase origin/main +# решить конфликты +git push --force-with-lease origin issue-- +``` + +--- + +## 8. Labels (система меток) + +| Label | Значение | +|-------|----------| +| `agent` | Готово к выполнению агентом | +| `blocked` | Заблокировано — не брать | +| `needs-review` | Ждёт ревью человека | +| `bug` | Баг | +| `feat` | Новая фича | +| `ci` | CI/CD | +| `docs` | Документация | +| `gui` | Изменения в Slint GUI | +| `tui` | Изменения в ratatui TUI | +| `priority:high` | Сделать в первую очередь | +| `priority:low` | Можно отложить | + +Агент берёт задачи в порядке: `priority:high` → без приоритета → `priority:low`. + +--- + +## 9. Что агент НЕ делает самостоятельно + +- Не создаёт Issue сам (только человек) +- Не закрывает чужие Issue +- Не делает force push в `main` +- Не удаляет файлы без явного указания в Issue +- Не меняет версию в `Cargo.toml` (это делает человек перед релизом) +- Не создаёт теги и релизы + +--- + +## 10. Контекст проекта + +**Репозиторий:** https://github.com/xodapi/vimit +**Язык:** Rust 2024 edition +**Тесты:** `cargo test --locked` (58 тестов) +**GUI:** Slint (`--features gui`) +**TUI:** ratatui +**API:** `GET /v1/me` → `VIBEMODE_API_KEY` +**Платформы:** Windows x86_64, Linux x86_64/aarch64, macOS aarch64 + +Структура `src/`: +``` +src/ + main.rs — точка входа, CLI парсинг + api.rs — запрос /v1/me, модели данных + monitor.rs — TUI ratatui монитор + notify.rs — desktop notifications + cli/ + update.rs — self-update логика + bin/ + vimit-gui.rs — Slint GUI +ui/ + app.slint — главный Slint файл +``` + +--- + +## 11. Быстрый старт для нового агента + +```bash +# 1. Посмотреть открытые задачи +gh issue list --state open --label agent --assignee "" + +# 2. Взять задачу +gh issue edit --add-assignee "@me" +gh issue comment --body "🤖 Начинаю" + +# 3. Создать ветку +git checkout -b issue-- + +# 4. Сделать работу, проверить +cargo test --locked && cargo clippy --all-targets -- -D warnings && cargo fmt --check + +# 5. Коммит и PR +git commit -m "feat(...): ... (closes #)" +git push origin issue-- +gh pr create --fill --base main +``` + +--- + +## 12. Параллельная работа нескольких агентов + +При работе 2+ агентов одновременно — читать **MULTI-AGENT.md**. + +Краткие правила: +- Каждый Issue имеет label `zone:X` — два агента не берут одну зону +- Проверять зависимости (`Зависит от #N`) перед стартом +- Файлы `Cargo.toml`, `ui/app.slint`, `src/main.rs` — только один агент +- При rebase-конфликте в `Cargo.lock` — пересоздать через `cargo generate-lockfile` diff --git a/docs/AGENTS.md b/docs/AGENTS.md new file mode 100644 index 0000000..e958ccf --- /dev/null +++ b/docs/AGENTS.md @@ -0,0 +1,267 @@ +# AGENTS.md — Глобальные правила для AI-агентов + +> Этот файл читают все агенты: Codex, Gemini, Grok, Claude Code, Factory Droid. +> Он описывает **единственный** разрешённый рабочий процесс. +> Нарушение любого пункта = задача не принята. + +--- + +## 0. Главное правило + +**Каждое изменение в коде начинается с GitHub Issue и заканчивается закрытием этого Issue.** + +Агент не пишет код "в воздух". Нет Issue — нет работы. + +--- + +## 1. Перед началом любой задачи + +### 1.1 Найти Issue + +``` +gh issue list --state open --label agent +``` + +Работать только с Issue у которых: +- label `agent` — готово к выполнению агентом +- label `blocked` отсутствует +- assignee пустой или назначен на тебя + +### 1.2 Назначить себя + +``` +gh issue edit --add-assignee "@me" +gh issue comment --body "🤖 Начинаю выполнение. Агент: " +``` + +### 1.3 Прочитать Issue полностью + +Issue содержит: +- **Что сделать** — конкретная задача +- **Критерии приёмки** — как проверить что готово +- **Что нельзя трогать** — файлы и модули вне задачи + +Если чего-то не хватает для выполнения — оставить комментарий с вопросом, +снять assignee, **не начинать код**. + +--- + +## 2. Рабочий процесс (обязательный порядок) + +``` +Issue открыт + ↓ +git checkout -b issue-- + ↓ +Реализация (только в рамках Issue) + ↓ +cargo test --locked # все тесты зелёные +cargo clippy --all-targets -- -D warnings # ноль предупреждений +cargo fmt --check # форматирование чистое + ↓ +git commit -m "fix/feat/docs(): <описание> (closes #)" + ↓ +git push origin issue-- + ↓ +gh pr create --title "..." --body "Closes #" --base main + ↓ +gh issue comment --body "✅ PR готов: " +``` + +### Формат ветки + +``` +issue-42-add-daily-limit +issue-57-fix-ci-linux-gtk +issue-103-theme-dark-light +``` + +### Формат коммита + +``` +feat(monitor): add daily limit display (closes #42) +fix(ci): install gtk deps for linux gui build (closes #57) +docs(readme): add demo screenshots (closes #103) +``` + +Типы: `feat` `fix` `docs` `refactor` `test` `ci` `chore` + +--- + +## 3. Ограничения при реализации + +### 3.1 Трогать только то что в Issue + +Если Issue про `src/notify.rs` — не рефакторить `src/monitor.rs` попутно. +Попутные улучшения = отдельный Issue. + +### 3.2 Запрещено без явного указания в Issue + +- Менять `Cargo.toml` (добавлять/удалять зависимости) +- Менять `ci.yml` или `release.yml` +- Переименовывать публичные функции и структуры +- Менять формат JSON-вывода (`--json`) +- Трогать `SUBMISSION.ru.md` +- Коммитить `.env` файлы с ключами + +### 3.3 API-ключи + +Никогда не логировать `VIBEMODE_API_KEY`, `NEUROGATE_API_KEY`. +Если ключ попал в вывод — задача не завершена, нужен отдельный Issue на исправление. + +--- + +## 4. Проверка перед PR + +Все три команды должны завершиться с кодом 0: + +```bash +cargo test --locked +cargo clippy --all-targets -- -D warnings +cargo fmt --check +``` + +Дополнительно для GUI-изменений: + +```bash +cargo build --features gui --locked +``` + +Если тесты красные — **не создавать PR**. Исправить и перепроверить. + +--- + +## 5. Оформление PR + +```markdown +## Что сделано +<одна-две строки что изменилось> + +## Как проверить +<конкретные команды для ручной проверки> + +## Closes +Closes # +``` + +Не писать: "я сделал X, Y, Z, W" — только то что в Issue. + +--- + +## 6. После merge + +```bash +gh issue close --comment "✅ Выполнено в PR #" +git checkout main +git pull origin main +git branch -d issue-- +``` + +--- + +## 7. Если что-то пошло не так + +### Тесты не проходят после реализации + +```bash +gh issue comment --body "⚠️ Тесты не проходят: <вывод ошибки>. Нужна помощь." +``` +Снять assignee. Не пушить сломанный код. + +### Issue оказался больше чем ожидалось + +```bash +gh issue comment --body "⚠️ Задача требует изменений в — это за рамками Issue. Создаю дочерний Issue." +gh issue create --title "..." --body "Дочерняя задача от #" --label agent +``` + +### Конфликт с main + +```bash +git fetch origin main +git rebase origin/main +# решить конфликты +git push --force-with-lease origin issue-- +``` + +--- + +## 8. Labels (система меток) + +| Label | Значение | +|-------|----------| +| `agent` | Готово к выполнению агентом | +| `blocked` | Заблокировано — не брать | +| `needs-review` | Ждёт ревью человека | +| `bug` | Баг | +| `feat` | Новая фича | +| `ci` | CI/CD | +| `docs` | Документация | +| `gui` | Изменения в Slint GUI | +| `tui` | Изменения в ratatui TUI | +| `priority:high` | Сделать в первую очередь | +| `priority:low` | Можно отложить | + +Агент берёт задачи в порядке: `priority:high` → без приоритета → `priority:low`. + +--- + +## 9. Что агент НЕ делает самостоятельно + +- Не создаёт Issue сам (только человек) +- Не закрывает чужие Issue +- Не делает force push в `main` +- Не удаляет файлы без явного указания в Issue +- Не меняет версию в `Cargo.toml` (это делает человек перед релизом) +- Не создаёт теги и релизы + +--- + +## 10. Контекст проекта + +**Репозиторий:** https://github.com/xodapi/vimit +**Язык:** Rust 2024 edition +**Тесты:** `cargo test --locked` (58 тестов) +**GUI:** Slint (`--features gui`) +**TUI:** ratatui +**API:** `GET /v1/me` → `VIBEMODE_API_KEY` +**Платформы:** Windows x86_64, Linux x86_64/aarch64, macOS aarch64 + +Структура `src/`: +``` +src/ + main.rs — точка входа, CLI парсинг + api.rs — запрос /v1/me, модели данных + monitor.rs — TUI ratatui монитор + notify.rs — desktop notifications + cli/ + update.rs — self-update логика + bin/ + vimit-gui.rs — Slint GUI +ui/ + app.slint — главный Slint файл +``` + +--- + +## 11. Быстрый старт для нового агента + +```bash +# 1. Посмотреть открытые задачи +gh issue list --state open --label agent --assignee "" + +# 2. Взять задачу +gh issue edit --add-assignee "@me" +gh issue comment --body "🤖 Начинаю" + +# 3. Создать ветку +git checkout -b issue-- + +# 4. Сделать работу, проверить +cargo test --locked && cargo clippy --all-targets -- -D warnings && cargo fmt --check + +# 5. Коммит и PR +git commit -m "feat(...): ... (closes #)" +git push origin issue-- +gh pr create --fill --base main +``` diff --git a/docs/MULTI-AGENT.md b/docs/MULTI-AGENT.md new file mode 100644 index 0000000..4749b4f --- /dev/null +++ b/docs/MULTI-AGENT.md @@ -0,0 +1,176 @@ +# MULTI-AGENT.md — Координация нескольких агентов + +> Дополнение к AGENTS.md для случаев когда 2+ агента работают параллельно. +> Читать вместе с AGENTS.md. + +--- + +## Проблема параллельной работы + +Когда два агента берут разные Issue одновременно — они могут: +- Редактировать один и тот же файл → merge conflict +- Зависеть друг от друга → deadlock +- Оба добавить зависимость в Cargo.toml → конфликт lock-файла + +Решение — **файловые зоны** и **явные зависимости между Issue**. + +--- + +## 1. Файловые зоны (кто что трогает) + +Каждый Issue получает label зоны. Два агента не берут Issue из одной зоны одновременно. + +| Label зоны | Файлы | +|------------|-------| +| `zone:api` | `src/api.rs`, `src/cli/update.rs` | +| `zone:tui` | `src/monitor.rs`, `src/main.rs` | +| `zone:notify` | `src/notify.rs` | +| `zone:gui` | `ui/*.slint`, `src/bin/vimit-gui.rs` | +| `zone:ci` | `.github/workflows/*.yml` | +| `zone:docs` | `README*.md`, `SUBMISSION*.md`, `ROADMAP.md` | +| `zone:cargo` | `Cargo.toml`, `Cargo.lock`, `build.rs` | + +### Правило зон + +Перед тем как взять Issue — проверить: +```bash +gh issue list --state open --label "zone:gui" --assignee "*" +``` +Если кто-то уже работает в этой зоне — **не брать** Issue из той же зоны. +Взять Issue из другой зоны или подождать. + +--- + +## 2. Зависимости между Issue + +В теле Issue может быть секция: +``` +## Зависит от +- #12 (должен быть merged перед началом) +- #15 (должен быть merged перед началом) +``` + +Агент **не начинает** Issue если его зависимости не закрыты. +Проверить: +```bash +gh issue view | grep "Зависит от" +gh issue view --json state | jq .state +``` +Если зависимость открыта — оставить комментарий и не брать задачу: +```bash +gh issue comment \ + --body "⏳ Ожидаю закрытия #. Вернусь после merge." +``` + +--- + +## 3. Алгоритм выбора задачи для агента + +``` +1. Получить список свободных Issues: + gh issue list --state open --label agent --assignee "" + +2. Для каждого Issue (по приоритету): + a. Проверить зависимости → все closed? + b. Определить zone: label + c. Проверить что zone свободна: + gh issue list --label "zone:" --assignee "*" --state open + d. Если зона свободна И зависимости closed → взять + +3. Назначить себя + оставить комментарий с именем агента +``` + +--- + +## 4. Опасные файлы — только один агент + +Эти файлы **никогда не редактируются параллельно**: + +| Файл | Почему опасен | +|------|--------------| +| `Cargo.toml` | Конфликт зависимостей + Cargo.lock | +| `Cargo.lock` | Автогенерируется, всегда конфликт | +| `src/main.rs` | Точка входа, много агентов → chaos | +| `ui/app.slint` | Монолитный UI файл | +| `.github/workflows/ci.yml` | CI логика | + +Для этих файлов действует правило: **label `zone:cargo` / `zone:ci` / `zone:gui` — не более одного активного Issue одновременно**. + +--- + +## 5. Rebase-стратегия для параллельных веток + +Когда твой PR готов, но в main уже появились новые коммиты от другого агента: + +```bash +git fetch origin main +git rebase origin/main + +# Если конфликт в Cargo.lock — не решать вручную: +cargo generate-lockfile +git add Cargo.lock +git rebase --continue + +# Если конфликт в src/ файле: +# Прочитать оба изменения, применить оба, не терять чужую работу +git rebase --continue + +git push --force-with-lease origin issue-- +``` + +--- + +## 6. Коммуникация через Issue + +Если агент видит что его работа влияет на чужой открытый Issue: + +```bash +gh issue comment <ЧУЖОЙ_ISSUE> \ + --body "ℹ️ Heads up: мой PR # меняет <файл/функцию> которую ты используешь. + Merge планируется сегодня. Учти при rebase." +``` + +--- + +## 7. Пример параллельной работы (без конфликтов) + +``` +Агент A берёт #57 (zone:ci) → трогает только .github/workflows/ +Агент B берёт #42 (zone:gui) → трогает только ui/*.slint +Агент C берёт #103 (zone:docs) → трогает только README*.md + +✅ Нет пересечений → merge без конфликтов +``` + +``` +Агент A берёт #57 (zone:ci) +Агент B хочет #44 (zone:ci) → СТОП: зона занята агентом A +Агент B берёт #90 (zone:tui) → OK: другая зона + +✅ B выбрал свободную зону +``` + +--- + +## 8. Чеклист перед взятием задачи (multi-agent) + +``` +[ ] Issue имеет label `agent` +[ ] assignee пустой +[ ] Зависимости (если есть) — все closed +[ ] Зона файлов — нет активного Issue в той же зоне +[ ] git pull origin main (актуальная база) +[ ] Создать ветку от свежего main +``` + +--- + +## 9. Человек-координатор + +Человек (синтографист) управляет приоритетами через: +- Назначение labels `priority:high` +- Добавление/снятие label `blocked` +- Закрытие Issues которые стали неактуальными +- Слияние PR в main + +Агент не принимает решения о приоритетах — только выполняет. diff --git a/docs/deep-research-report.md b/docs/deep-research-report.md new file mode 100644 index 0000000..a45f47c --- /dev/null +++ b/docs/deep-research-report.md @@ -0,0 +1,147 @@ +# Аналитический отчёт по функциям и болям пользователей проекта vimit + +## Executive summary + +`vimit` уже решает понятную и острую задачу: локально показывает расход квоты VibeMode по окнам 5h / 24h / 7d / 30d, умеет работать как CLI, TUI и опциональный GUI, поддерживает demo/mock, диагностику, интерактивную настройку, несколько аккаунтов, desktop-уведомления, тренды и автообновление. При этом продукт очень молод: публичные релизы выходили серией с 21 по 27 июня 2026 года, а в период между `v0.6.0` и `v0.6.1` проект даже переименовался из `neurogate-limit-watch` в `vimit`; в репозитории на момент анализа видно 15 issues, 0 открытых PR, 2 закрытых PR и одну discussion-тему с 0 комментариев. Это говорит о высокой скорости итераций, но и о незакрытом базовом UX-долге. citeturn41view0turn46view0turn46view1turn27view0turn45view0 + +Главная ценность проекта сегодня — не в нехватке функций, а в том, что текущие функции уже хороши, но местами не доведены до “безошибочного первого опыта”. Самые заметные проблемы: путаница в именах ключей и брендинге (`NEUROGATE_API_KEY` против `VIBEMODE_API_KEY`), неоднозначный путь к `.env` и конфигу для Windows-установки, слабая визуальная документация для новичка, недораскрытые возможности TUI, а также не до конца оформленная история приватности: проект заявляет local-first и “no telemetry”, но для пользователя это не собрано в единый понятный UX-слой доверия. citeturn36view0turn38view2turn14view1turn24view0turn38view5turn42view0 + +Если выбирать, что даст наибольший эффект в ближайшие недели, то приоритеты такие: сначала устранить несогласованность онбординга и документации, затем сделать “первый запуск без боли” для Windows/GUI, после этого усилить multi-account UX и видимость ключевых команд в TUI. Уже после этого имеет смысл инвестировать в более длинные темы: privacy center, IDE overlay/MCP и ресурсные guard-rail’ы для weak machines. Это даст рост не только удобства, но и доверия, а также снизит вероятность ложного отказа на самом первом запуске — критичная точка для молодой утилиты без широкой внешней воронки. citeturn43view0turn43view2turn18view0turn18view1turn26view1turn26view0 + +Коротко: у `vimit` уже есть сильный функциональный фундамент, но продукту нужен не “ещё один экран”, а выравнивание базового UX-контракта. Лучшие инвестиции сейчас — это консистентность, first-run flow, поясняющие состояния и прозрачность приватности. citeturn41view4turn36view1turn13view1turn14view0 + +## Исходные данные и текущее состояние продукта + +В рамках анализа были просмотрены: русскоязычный README, ROADMAP, SECURITY/AUDIT, releases, discussion, issues, код CLI/TUI/GUI, а также сценарии `init`, `doctor`, `update` и Windows-скрипты установки/удаления. Отдельный `CHANGELOG.md` в просмотренных источниках не указан; фактическую историю изменений лучше считать по GitHub Releases. citeturn36view3turn36view2turn36view0turn36view1turn46view0turn45view0turn13view0turn13view1turn13view2turn24view0turn24view1 + +По сути продукта `vimit` — это нативная Rust-утилита для мониторинга квот VibeMode без Python/Node/SDK, с human/JSON/compact-выводом, полноэкранным ratatui-монитором, темами, оповещениями, multi-account, 30-дневными трендами и demo/mock режимами. В `Cargo.toml` продукт определён как “Safe VibeMode quota monitor for Codex/Droid workflows”, а релиз `v0.6.2` дополнительно подчёркивает single native binary, TUI-предустановки, пер-оконные пороги и Slint GUI. citeturn10view0turn41view0turn46view0 + +Сильная сторона проекта — уже сейчас есть три слоя интерфейса под разные сценарии: CLI для скриптов и CI, TUI для живого наблюдения, GUI для desktop-использования. В TUI есть `full/compact/mini`, спарклайны, темы и управление панелями; в GUI есть системный трей, скрытый режим, dropdown аккаунтов, ручная и авто-проверка обновлений, светлая/тёмная тема и панель настроек. В CLI есть интерактивный мастер `--init`, диагностика `--doctor`, а также self-update с кешированием проверки на 24 часа. citeturn41view4turn18view0turn18view1turn17view0turn13view0turn13view1turn13view2turn14view5 + +Но из этих же источников видно, что продукт ещё “сырой” именно как пользовательский путь. В ROADMAP и Discussion повторяются темы компактного виджета, Termux/Android, уведомлений, пер-оконных порогов и примеров реальных workflow. В issues отдельно поднимаются скриншоты README, автообнаружение ключей, self-update, дневной лимит, MCP, ресурсные ограничения и PII masking. Это хороший сигнал: команда уже интуитивно видит правильные направления, но часть из них пока существует как backlog, а не как завершённый пользовательский опыт. citeturn37view3turn45view0turn43view0turn43view2turn43view1turn25view0turn26view1turn26view0turn26view2 + +Самая важная находка анализа — консистентность ещё не доведена до продуктового стандарта. В `SECURITY.md` фигурирует `NEUROGATE_API_KEY`, в README примеры уже ведутся через `VIBEMODE_API_KEY`, а код `init` и `main` подсказывает именно `VIBEMODE_API_KEY`; при этом релизная история показывает недавнее переименование проекта. Для пользователя это выглядит как возможная “ложная ошибка настройки”, даже если сама логика кода в порядке. citeturn36view0turn38view2turn10view1turn14view1turn46view1 + +Ещё один критический UX-риск — Windows installer. PowerShell-скрипт создаёт `.env` в `%USERPROFILE%\.vimit\.env`, тогда как README объясняет поиск `.env` через `--env-file`, текущую директорию и папку рядом с бинарником; явного пользовательского описания чтения `%USERPROFILE%\.vimit\.env` в публичной документации нет. Это не обязательно логическая ошибка, но уже точно продуктовая неоднозначность, которая способна ломать “установил → запустил → не заработало”. citeturn24view0turn38view5 + +## Пользовательские персоны и реальные боли + +Ниже — пять ключевых персон, которых сам проект уже частично адресует, и соответствующие точки фрустрации. + +| Персона | Что уже покрыто | Главная боль | Почему это боль сейчас | +|---|---|---|---| +| **Novice Windows user** | demo, `--init`, `--doctor`, PowerShell install/uninstall, helper `.cmd`, GUI/трей | Первый запуск неоднозначен: где ключ, где `.env`, что запускать — `vimit.exe` или GUI, почему после двойного клика просто консоль | README просит CLI-команды и `.env` рядом с бинарником, installer пишет `.env` в другой путь, README пока без скриншотов, а issue на скриншоты помечен high priority. citeturn38view5turn24view0turn43view0 | +| **Developer / CLI user** | JSON/compact, TUI presets, `--fail-on`, thresholds, `--doctor`, trend storage | Мощность есть, но discoverability неполная: часть важных функций скрыта за клавишами и режимами | README отдельно объясняет, что надо нажать `5` для трендов, TUI по умолчанию держит `show_trends = false`, а AUDIT прямо отмечает недостаток help/discoverability в monitoring output. citeturn41view4turn33view2turn42view0turn42view4 | +| **Privacy-conscious user** | заявлены local-first, no telemetry, only `/v1/me`, stealth mode в GUI | Нет единого “центра доверия”: что именно уходит в сеть, что скрывает stealth, что попадает в notifications/JSON | README и SECURITY обещают высокий privacy baseline, но open issue про PII masking показывает, что расширенная модель приватности ещё не реализована; stealth mode пока скрывает суммы в GUI, а не всю информационную поверхность. citeturn36view0turn41view4turn17view0turn26view2 | +| **Multi-account user** | `accounts.toml`, Tab в TUI, dropdown в GUI | В TUI переключение аккаунтов линейное и слабо обозримое; нет быстрых сравнений аккаунтов | README обещает multi-account, GUI даёт dropdown, но TUI использует только последовательный `Tab`, а футер показывает лишь подсказку `Tab account`. citeturn38view4turn18view0turn31view0turn34view0 | +| **Power user / IDE overlay user** | compact output, tray/tooltip, TUI, идеи status-bar/widget, backlog MCP | Нет “always visible” слоя внутри IDE/overlays; проект всё ещё живёт как отдельное окно/терминал | В discussion и roadmap повторяются status bar / widget / Termux widget сценарии, а issue #12 описывает MCP-интеграцию как отсутствующую, но желанную. citeturn45view0turn37view3turn26view1 | + +Из этих персон самая недообслуженная сегодня — **novice Windows user**. Не потому, что Windows не поддержан, а потому что именно здесь накладываются сразу четыре источника трения: CLI-центричная документация, отсутствие скриншотов, недавний rename-переход и спорная история с путями `.env`. Для молодой утилиты это главный барьер конверсии “скачал из релизов → остался пользоваться”. citeturn38view5turn43view0turn46view1turn24view0 + +Следом идёт **developer/CLI user**. Здесь проблема не в функциональном дефиците, а в “скрытой мощности”: продукт уже умеет много, но не всегда подсказывает это вовремя. Когда полезные функции включаются по клавишам, комментариям в README или по памяти, это работает для автора инструмента, но хуже работает для пользователя “после двух недель перерыва”. citeturn41view4turn33view2turn34view0turn42view0 + +## Рекомендации по функциям и UX-решениям + +Ниже — приоритетный список улучшений. Приоритет и сложность — экспертная оценка по текущему коду, текущим backlog-сигналам и масштабу затронутых поверхностей. + +| Рекомендация | Кого решает | UX-решение | Priority | Сложность | KPI | +|---|---|---|---|---|---| +| **Единый first-run onboarding для Windows и GUI** | novice Windows | При первом запуске показывать wizard: “Проверить demo → ввести ключ → протестировать соединение → выбрать режим интерфейса”. В GUI — стартовая страница вместо пустого/ошибочного состояния. Основание: уже есть `--init`, `--doctor`, GUI settings, update panel. citeturn13view0turn13view1turn18view0 | High | Medium | First successful setup rate, доля пользователей, дошедших до первого успешного `OK (4 window(s))`, снижение abandon rate после первого запуска | +| **Убрать naming/config debt** | novice Windows, developer | Полностью унифицировать `VIBEMODE_*` против `NEUROGATE_*`, пути `.env`, название проекта и текст ошибок. Добавить одну canonical-страницу “Где vimit ищет ключ”. Основание: README, SECURITY, release rename, installer path расходятся. citeturn36view0turn38view2turn24view0turn46view1 | High | Low | Снижение doctor-ошибок “key not found”, меньше GitHub issues по установке, рост доли успешных запусков без ручного чтения документации | +| **Скриншоты и “выбор интерфейса” в README/releases** | novice Windows, power user | Закрыть issue #22 и добавить в README блок “Что выбрать: CLI / TUI / GUI”. В release asset/README: 1 TUI screenshot, 1 GUI screenshot, 1 таблица “для кого что”. citeturn43view0turn41view3 | High | Low | CTR с README/release на первый запуск, снижение времени до первого полезного действия, меньше вопросов “а как это выглядит?” | +| **Страница ошибок и health center** | novice Windows, developer | Вместо сухих ошибок — error page с действиями: “Открыть `.env`”, “Запустить диагностику”, “Проверить сеть/API”, “Запустить demo”. Для CLI — короткий код ошибки + hint + команда-фикс. Основание: в коде уже есть error hints, `doctor` и `init`. citeturn10view1turn13view1turn14view0 | High | Medium | Успешный self-recovery rate, снижение повторных запусков с той же ошибкой, снижение issue rate на setup/network | +| **Account Hub и быстрый switcher** | multi-account | В GUI: mini summary по всем аккаунтам в dropdown. В TUI: popup по `a`/`Tab`, где видно alias + peak% + reset. Запоминать последний выбранный аккаунт. Основание: сейчас GUI лучше TUI по account UX. citeturn18view0turn31view0turn34view0 | High | Medium | Частота использования multi-account, снижение времени до переключения аккаунта, доля пользователей с 2+ аккаунтами, которые реально переключаются | +| **Повысить discoverability TUI** | developer, power user | Постоянная action-ribbon: `? help`, `5 trends`, `Tab account`, `p route`, `r refresh`. Пустые состояния должны подсказывать, что именно включить. Основание: тренды существуют, но скрыты; help есть, но видимость ограничена. citeturn33view2turn34view0turn42view0 | Medium | Low | Использование трендов, help open rate, retention у TUI-пользователей, снижение “feature invisibility” в пользовательских отзывах | +| **Daily budget planner** | developer, power user | Сделать “Сегодня” полноценным first-class объектом и в GUI, и в TUI: recommended/day, spent today, burn pace, прогноз “хватит ли до reset”. Основание: issue #21 уже формулирует почти готовый UX. citeturn25view0turn35view0 | High | Medium | Доля пользователей, настроивших daily limit, число дней без превышения weekly budget, engagement с daily card | +| **Privacy Center и полнофункциональный stealth** | privacy-conscious | Единая вкладка: “Что читает vimit”, “Что отправляет vimit”, “Что хранится локально”, “Что скрывает Stealth Mode”. Распространить stealth на notifications/tray/overlay. Если развивать proxy/MCP-направление — делать redaction как отдельный режим доверия, а не как второстепенную галочку. citeturn36view0turn41view4turn17view0turn26view2 | Medium | Medium | Включаемость stealth, снижение privacy-concern issues, trust score в опросе “понимаю, какие данные уходят” | +| **IDE overlay и MCP-интеграция** | power user / IDE overlay | Две ступени: сначала menubar/always-on-top mini overlay, затем `--mcp` для IDE discovery. Это даст “квота всегда перед глазами” без отдельного терминала. Основание: status-bar/widget ideas в discussion/roadmap и issue о MCP. citeturn45view0turn37view3turn26view1 | Medium | High | DAU overlay/MCP, среднее время открытия отдельного TUI/GUI, доля пользователей, использующих vimit внутри IDE workflow | +| **Ресурсные guard-rail’ы в watch/monitor** | novice Windows, power user | Degraded mode при перегрузе: увеличивать интервал, выключать тяжёлые панели, показывать badge “degraded”. Особенно важно для старых Windows-машин и IDE-heavy workflow. Основание: issue #13 довольно точно описывает нужный режим. citeturn26view0 | Medium | Medium | Средний CPU/RAM footprint, crash/freezing rate, session length в `--watch`/`--monitor` | + +Текущий путь первого запуска логически уже существует, но он распределён по нескольким поверхностям — README, installer, `--init`, `--doctor`, ручные команды. Для novice-пользователя это нужно собрать в один UX-поток. citeturn38view2turn24view0turn13view1 + +```mermaid +flowchart TD + A[Скачал релиз] --> B[Распаковал архив] + B --> C{Что запускать?} + C -->|CLI| D[vimit --demo или vimit] + C -->|GUI| E[Неочевидно в публичном потоке] + D --> F{Ключ найден?} + F -->|Нет| G[Читать README или запускать --init/--doctor] + F -->|Да| H[Получить статус лимитов] +``` + +Целевой поток должен быть короче и не требовать от пользователя знания внутренних терминов проекта. + +```mermaid +flowchart TD + A[Первый запуск vimit] --> B[Экран выбора: Demo / Подключить ключ] + B --> C[Wizard настройки] + C --> D[Проверка ключа и сети] + D --> E[Выбор интерфейса: GUI / TUI / CLI] + E --> F[Первый успешный статус] + F --> G[Подсказка: уведомления, multi-account, daily budget] +``` + +## Риски, privacy и безопасность + +С точки зрения позиции доверия у проекта хорошая база: в `SECURITY.md` и `README.ru.md` зафиксированы локальность, отсутствие телеметрии, работа с ключом через env и то, что в normal mode сетевой вызов сводится к `GET /v1/me`. AUDIT также повторяет, что JSON не должен включать account identity, а `--with-abtop` использует privacy-safe summaries. Это сильный фундамент для persona “privacy-conscious user”. citeturn36view0turn41view4turn42view0turn42view2 + +Но именно из-за сильных privacy-обещаний любая неоднозначность становится опаснее. Если продукт обещает “ключ не пишется на диск”, а пользователь одновременно видит installer, который создаёт конфиг сам, плюс разные имена env-переменных в документах, это снижает доверие — даже если реальной утечки нет. Здесь риск не столько технический, сколько продуктовый: пользователь перестаёт понимать модель безопасности. citeturn36view0turn24view0turn38view2 + +| Риск | Где проявляется | Последствие | Смягчение | +|---|---|---|---| +| **Путаница env-ключей и брендинга** | README / SECURITY / rename-история | Ошибки первого запуска, недоверие к документации | Один canonical namespace, migration-note в README/releases, doctor с явным указанием “ожидается именно этот ключ”. citeturn36view0turn38view2turn46view1 | +| **Неочевидный путь конфигурации в Windows** | installer vs README | “Установил, но не работает” | Привести installer и runtime search order к одному сценарию; в wizard показать реальный путь конфигурации. citeturn24view0turn38view5 | +| **Stealth Mode закрывает не все поверхности** | GUI/tray/notifications | Privacy gap при screen sharing | Сделать stealth глобальным режимом отображения, включая tray, popup и overlay. citeturn17view0turn41view4 | +| **Auto-discovery ключей может стать небезопасным по умолчанию** | issue #11 | Ложный выбор ключа, трудно объяснимая магия | Делать discovery opt-in или first-run only; всегда показывать, какой источник выбран, без вывода секрета. citeturn43view2 | +| **Развитие proxy/MCP без privacy-design** | issues #10, #12, #15 | Расширение поверхности риска раньше UX-доверия | Сначала Privacy Center, auditability и redaction policy; только затем глубокие интеграции. citeturn26view1turn26view2turn26view3 | +| **In-place self-update без понятного rollback UX** | `update.rs`, GUI updates | Страх обновления в корпоративной среде | Показать current → target version, changelog summary, ручной rollback/help link, checksum/status page. citeturn13view2turn18view1turn43view1 | + +Практический вывод здесь такой: у `vimit` не проблема “безопасность не продумана”; наоборот, она продумана лучше среднего для маленькой утилиты. Проблема в том, что этот security story пока недостаточно хорошо упакован для обычного пользователя. citeturn36view0turn42view0turn42view1 + +## Метрики успеха и roadmap + +Чтобы не распыляться, я бы измерял успех не “по числу фич”, а по трём воронкам: **успешный первый запуск**, **переход в регулярное использование**, **доверие и предсказуемость**. Для этого достаточно простых KPI, которые можно собирать даже без телеметрии: через opt-in crash/error report, ручные опросы, GitHub issue labels, и локальные события в явном debug/export-режиме. Если телеметрия принципиально не используется, это должно быть прямо отражено: часть KPI придётся измерять “беднее”, но честно. citeturn36view0turn42view0 + +### Быстрые wins + +| Горизонт | Что делать | Почему это быстро | +|---|---|---| +| **1–2 дня** | Закрыть naming debt: README, SECURITY, doctor example, error copy, release notes | Изменения в текстах и хинтах, без тяжёлой архитектуры. citeturn36view0turn38view2turn10view1 | +| **1–2 дня** | Закрыть issue со скриншотами и вставить “CLI / TUI / GUI — что выбрать” | Уже сформулировано как отдельный high-priority issue. citeturn43view0 | +| **2–3 дня** | Исправить Windows install path contract | Нужно синхронизировать install script и docs. citeturn24view0turn38view5 | +| **2–4 дня** | Добавить TUI ribbon и пустые состояния для трендов/help/account | Поверхностное изменение TUI, без смены модели данных. citeturn33view2turn34view0 | +| **3–5 дней** | GUI first-run error page с кнопками “Demo / Test connection / Open config / Doctor” | Логика уже есть в `init`, `doctor`, update/settings. citeturn13view0turn13view1turn18view0 | +| **4–7 дней** | Daily budget planner в TUI/JSON и затем в GUI | Issue #21 уже содержит хорошую спецификацию. citeturn25view0turn35view0 | + +### Долгосрочные функции + +| Горизонт | Что делать | Ожидаемый эффект | +|---|---|---| +| **До 3 месяцев** | Account Hub и быстрый switcher | Сильный рост удобства для heavy users и команд с несколькими ключами | +| **До 3 месяцев** | Privacy Center и глобальный stealth | Рост доверия и лучшее позиционирование local-first | +| **До 3 месяцев** | Overlay/menubar mini mode | Уменьшение необходимости держать отдельный терминал | +| **До 3 месяцев** | MCP/IDE integration | Выход в power-user сценарии Cursor/Codex/Windsurf | +| **До 3 месяцев** | Resource self-throttle/degraded mode | Стабильность на слабых машинах и в `--watch`/`--monitor` | + +### Роадмап на 7 дней + +| День | Фокус | Результат | +|---|---|---| +| **День 1** | Аудит терминов и env names | Один canonical namespace во всех docs и errors | +| **День 2** | README/release UX | Скриншоты, matrix “какой интерфейс выбрать”, блок first-run | +| **День 3** | Windows install contract | Installer кладёт конфиг туда, где приложение его реально ожидает, либо создаёт `config.toml` с `env_file` | +| **День 4** | TUI discoverability | Нижняя ribbon, CTA для трендов, явная account/help подсказка | +| **День 5** | GUI first-run health page | Самоисцеление без похода в README | +| **День 6** | Daily budget planner v1 | “Сегодня” в TUI/JSON | +| **День 7** | Финальная полировка | Smoke test Windows/Linux/macOS, обновление release notes | + +### Роадмап на 3 месяца + +| Месяц | Фокус | Выход | +|---|---|---| +| **Первый месяц** | Онбординг, аккаунты, TUI/GUI consistency | “Первый запуск без боли”, multi-account UX v2 | +| **Второй месяц** | Privacy и устойчивость | Privacy Center, глобальный stealth, resource guard | +| **Третий месяц** | Power-user expansion | Overlay/menubar, MCP beta, IDE-oriented workflows | + +Итоговая ставка простая: **сначала снять friction в базовом UX, потом наращивать advanced-функции**. Для `vimit` это особенно важно, потому что core value уже доказан кодом и релизами; сейчас выигрывает не тот backlog, который “самый технологичный”, а тот, который сильнее всего повышает вероятность, что пользователь дойдёт до полезного статуса лимитов за первые 3–5 минут. citeturn46view0turn13view1turn41view4turn42view1 \ No newline at end of file diff --git a/docs/kimi 2.6 audit.md b/docs/kimi 2.6 audit.md new file mode 100644 index 0000000..e22d671 --- /dev/null +++ b/docs/kimi 2.6 audit.md @@ -0,0 +1,147 @@ +Критический анализ кодовой базы vimit +Ниже разбор по категориям: от критических ошибок компиляции до архитектурных проблем. + +1. Критические ошибки (проект не собирается) +src/cli/monitor.rs:497 — mismatched types + +rust + +let mut final_style = pal.bold_level_style(level); +// ... +final_style = pal.danger; // Ошибка: ожидается Style, найден Color +pal.danger имеет тип Color, а final_style — Style. Исправление: pal.danger.into(). + +src/cli/monitor.rs:1489 — missing field offline_duration_min + +rust + +StatusSnapshot { stale: false, windows: ..., daily: None, ... } +В тестовой фабрике test_snapshot() не заполнено поле offline_duration_min, добавленное позже в структуру, но не в тесты. + +Результат: cargo test --locked и cargo clippy падают с ошибкой компиляции. + +2. Логические ошибки / баги +2.1 Recovery-уведомления никогда не отправляются +Файл: src/cli/notify.rs, метод check_windows + +rust + +if let Some(msg) = next_notification(&mut self.last_levels, window) { ... } +if let Some(msg) = recovery_notification(&mut self.last_levels, window) { ... } +next_notification сначала записывает текущий уровень в last_levels. После этого recovery_notification читает уже обновленное значение, поэтому previous == current == Ok, и условие previous == AlertLevel::Ok всегда срабатывает, возвращая None. Recovery-нотификации мертвы. + +2.2 Overlay-режим не работает на Linux / macOS +Файл: src/main.rs + +rust + +let gui_path = current.with_file_name("vimit-gui.exe"); +На Unix-системах бинарь называется vimit-gui без .exe. Overlay просто не запустится. + +2.3 Дублирование и рассинхрон логики daily-окна +Файл: src/cli/monitor.rs, collect_status + +При успешном fetch: + +вызывается daily_file.update(c.remaining) + daily_file.save() +затем вставляется "today" window +При fallback на кэш (ветка Err(error)): + +daily_file.update() не вызывается +"today" window вставляется через get_state, но расход за день не обновляется +Это приводит к тому, что в offline-режиме daily-метрика показывает устаревшие или некорректные данные. + +2.4 Race condition при записи daily.toml +Файл: src/cli/daily.rs + +DailyFile::save() делает простое fs::write. Если vimit запущен одновременно из нескольких терминалов или в monitor + вручную, файл может повредиться. + +3. Архитектурные проблемы +3.1 God-object lib.rs +Файл src/lib.rs (~800+ строк кода) смешивает: + +HTTP-клиент и retry-логику +Парсинг 5 разных схем JSON +Форматирование строк и цветов +Работу с .env +Выполнение внешних процессов (abtop) +Глобальный mutable state OFFLINE_SINCE +Это нарушает SRP и затрудняет юнит-тестирование. + +3.2 God-object monitor.rs +src/cli/monitor.rs (~1500 строк) объединяет: + +Event loop TUI +Рендеринг 3 пресетов +Сбор данных (collect_status) +Хелперы для plain-text тестов +Управление панелями +3.3 Глобальный mutable state +rust + +pub static OFFLINE_SINCE: Mutex> = Mutex::new(None); +Делает параллельные тесты невозможными без cargo test -- --test-threads=1 +При мониторинге нескольких аккаунтов один неудачный запрос помечает всё приложение "offline" +3.4 Массовое дублирование в vimit-gui.rs +GUI-бинарь полностью дублирует: + +Логику загрузки accounts.toml (load_gui_accounts вместо AccountsConfig::load) +Поиск .env / dirs_or_default +Конфигурацию HTTP и Router +Обработку demo/live режимов +Любое изменение в CLI-логике требует ручной синхронизации с GUI. + +4. Проблемы безопасности +4.1 PowerShell injection в уведомлениях +Файл: src/cli/notify.rs + +rust + +fn powershell_quote(text: &str) -> String { + text.replace('\'', "''").replace(['\r', '\n'], " ") +} +Экранируется только одиночная кавычка. Спецсимволы PowerShell ($, `, ;, |, &) не обрабатываются. Хотя входные данные приходят из API, вредоносный ответ или скомпрометированный API может выполнить код на машине пользователя. + +4.2 API-ключ в открытом виде в кэше +Файл: src/cli/cache.rs + +rust + +fn cache_key(api_key: &str, api_base: &str) -> String { + format!("{}|{}", api_key, api_base) +} +Ключ API хранится в открытом виде как ключ записи в redb. Файл cache.redb лежит в APPDATA / ~/.config и может быть доступен другим пользователям системы. + +5. Проблемы производительности +5.1 Спам потоками в GUI +Файл: src/bin/vimit-gui.rs, start_refresh + +rust + +fn start_refresh(...) { + thread::spawn(move || { ... }); +} +Каждый клик "Refresh" или изменение настроек порождает новый поток без throttle / debounce / семафора. При частых кликах возможен спам запросами. + +5.2 Полное сканирование trend-базы +Файл: src/cli/trends.rs, query_trends + +rust + +let range = table.range(start_ts..=end_ts)?; +for result in range { ... serde_json::from_slice(...) ... } +Все записи за N дней десериализуются целиком. При частых снапшотах (каждые 5 сек в monitor) объем данных растет быстро, а тренды запрашиваются редко. + +6. Мелкие, но важные замечания +Проблема Где Почему важно +cargo fmt --check может не пройти lib.rs, monitor.rs Много ручного форматирования, нестандартные отступы +Смешение языков vimit-gui.rs (русский), lib.rs (русский), output.rs (английский) Неконсистентный UX +#[allow(clippy::collapsible_if)] в lib.rs и main.rs глобально Подавляет полезные линты без причины +bin/test.rs src/bin/test.rs Бесполезный бинарь, дублирует demo_payload +windows_console_process_count main.rs unsafe FFI без обработки ошибок (GetConsoleProcessList может вернуть 0) +Итоговая сводка +Хочешь, чтобы я исправил критические ошибки компиляции и основные логические баги? + + +Thought for 3s +Анализ завершен. Если нужно исправить найденные ошибки, дай знать. \ No newline at end of file diff --git a/docs/setup-issues.sh b/docs/setup-issues.sh new file mode 100644 index 0000000..978d2a7 --- /dev/null +++ b/docs/setup-issues.sh @@ -0,0 +1,246 @@ +#!/bin/bash +# setup-issues.sh +# Создаёт стартовый набор Issues для vimit с правильными labels +# Запускать один раз: bash setup-issues.sh + +set -e + +REPO="xodapi/vimit" + +echo "=== Создание labels ===" + +labels=( + "agent:0075ca:Готово к выполнению агентом" + "blocked:e11d48:Заблокировано" + "gui:7c3aed:Изменения в Slint GUI" + "tui:0891b2:Изменения в ratatui TUI" + "priority:high:dc2626:Высокий приоритет" + "priority:low:6b7280:Низкий приоритет" +) + +for label_def in "${labels[@]}"; do + IFS=':' read -r name color desc <<< "$label_def" + gh label create "$name" --color "$color" --description "$desc" --repo "$REPO" 2>/dev/null || \ + gh label edit "$name" --color "$color" --description "$desc" --repo "$REPO" 2>/dev/null || true +done + +echo "=== Labels созданы ===" + +echo "=== Создание Issues ===" + +# Issue 1: CI fix (самый срочный) +gh issue create \ + --repo "$REPO" \ + --title "fix(ci): install gtk deps for linux, split gui feature matrix" \ + --label "ci,agent,priority:high" \ + --body "## Что сделать + +В \`.github/workflows/ci.yml\` исправить провал Linux-сборки с \`--features gui\`. + +## Конкретные изменения + +1. В job \`check\` для \`ubuntu-latest\` добавить шаг перед \`cargo check\`: +\`\`\`yaml +- name: Install Linux system deps + if: matrix.os == 'ubuntu-latest' + run: sudo apt-get update && sudo apt-get install -y libglib2.0-dev libgtk-3-dev +\`\`\` + +2. Либо (проще): для \`ubuntu-latest\` убрать \`--features gui\` из clippy и test — GUI-фичу проверять только на Windows/macOS. + +3. В README.md и README.ru.md заменить бадж \`rust-2021\` на \`rust-2024\`. + +## Критерии приёмки + +- CI #48+ проходит зелёным на всех трёх ОС +- \`cargo test --locked\` локально чистый +- Бадж в README обновлён + +## Что нельзя трогать + +- \`src/\` — никаких изменений кода +- \`SUBMISSION.ru.md\`" + +# Issue 2: Theme system в Slint +gh issue create \ + --repo "$REPO" \ + --title "feat(gui): add theme.slint with dark/light mode support" \ + --label "feat,gui,agent" \ + --body "## Что сделать + +Создать \`ui/theme.slint\` с глобальной цветовой системой и подключить в \`ui/app.slint\`. + +## Конкретные изменения + +1. Создать \`ui/theme.slint\`: +\`\`\`slint +export global Theme { + in-out property dark-mode: true; + property bg-primary: dark-mode ? #0f131a : #f8fafc; + property bg-card: dark-mode ? #1a202c : #ffffff; + property bg-surface: dark-mode ? #111827 : #f1f5f9; + property border: dark-mode ? #334155 : #e2e8f0; + property text-primary: dark-mode ? #f1f5f9 : #0f172a; + property text-muted: dark-mode ? #64748b : #94a3b8; + property success: #22c55e; + property warning: #f59e0b; + property danger: #ef4444; +} +\`\`\` + +2. В \`LimitCard\` добавить: +\`\`\`slint +private property accent: + root.percent >= 90 ? Theme.danger : + root.percent >= 75 ? Theme.warning : Theme.success; +\`\`\` + +3. Большой процент: \`font-size: 44px; font-weight: 800; color: root.accent\` + +4. В панели настроек добавить CheckBox «Светлая тема» → переключает \`Theme.dark-mode\` + +5. Переименовать «пик» → «макс» везде в UI + +## Критерии приёмки + +- \`cargo build --features gui --locked\` без ошибок +- GUI запускается в demo-режиме, переключение темы работает +- Большой % виден сразу при открытии окна + +## Что нельзя трогать + +- \`src/\` Rust-код — только \`ui/\` файлы +- Логика polling и API" + +# Issue 3: Дневной лимит +gh issue create \ + --repo "$REPO" \ + --title "feat(core): add daily credit limit tracking" \ + --label "feat,agent,tui,gui" \ + --body "## Что сделать + +Добавить отслеживание дневного лимита расхода кредитов. + +## Логика + +Расчётный рекомендуемый лимит/день = \`остаток_7d / дней_до_сброса_7d\` + +Если пользователь задал свой лимит через \`--daily-limit N\` — использовать его. + +Хранить в \`~/.config/vimit/daily.toml\`: +\`\`\`toml +limit = 56.0 # кредиты на день (0 = не задан, считать авто) +date = \"2026-06-28\" # при смене даты сбрасывать +spent_today = 12.3 +\`\`\` + +## Что показывать + +В TUI (compact и full preset) — строка под окном 7d: +\`\`\` +День: 12.3 / 56.0 (22%) [████░░░░░░] +\`\`\` + +В JSON-выводе — поле \`daily\` рядом с \`windows\`. + +## CLI + +\`\`\` +vimit --daily-limit 56 # задать лимит +vimit --daily-limit 0 # сбросить (использовать авто) +\`\`\` + +## Критерии приёмки + +- \`cargo test --locked\` — все тесты зелёные +- \`vimit --demo --daily-limit 50\` показывает строку дневного лимита +- \`vimit --demo --json\` содержит поле \`daily\` +- При смене даты spent_today сбрасывается в 0 + +## Что нельзя трогать + +- \`ui/app.slint\` (GUI версия — отдельный Issue) +- \`SUBMISSION.ru.md\`" + +# Issue 4: Скриншоты в README +gh issue create \ + --repo "$REPO" \ + --title "docs(readme): add demo screenshots to README" \ + --label "docs,agent,priority:high" \ + --body "## Что сделать + +Добавить скриншоты TUI-монитора и GUI в README.md и README.ru.md. + +## Шаги + +1. Запустить \`.\target\release\vimit.exe --demo --monitor\`, сделать скриншот терминала → сохранить в \`assets/demo-tui.png\` + +2. Запустить \`.\target\release\vimit-gui.exe --demo\` (или \`--features gui\`), сделать скриншот → сохранить в \`assets/demo-gui.png\` + +3. В README.md после раздела \`## Features\` добавить: +\`\`\`markdown +## Screenshots + +![TUI Monitor](assets/demo-tui.png) +![GUI](assets/demo-gui.png) +\`\`\` + +4. То же самое в README.ru.md + +## Критерии приёмки + +- Оба файла \`assets/demo-tui.png\` и \`assets/demo-gui.png\` существуют в репо +- В обоих README изображения отображаются на странице GitHub +- Размер каждого PNG < 500KB + +## Что нельзя трогать + +- \`src/\`, \`ui/\`, \`Cargo.toml\`" + +# Issue 5: SUBMISSION update +gh issue create \ + --repo "$REPO" \ + --title "docs: update SUBMISSION.ru.md for v0.6.2 final" \ + --label "docs,agent,priority:high" \ + --body "## Что сделать + +Обновить \`SUBMISSION.ru.md\` для финальной подачи на конкурс (дедлайн 1 июля). + +## Конкретные изменения + +1. В разделе «Что уже работает» добавить: + - Система self-update (\`vimit update\` / \`vimit update --check\`) + - System tray tooltip с процентами окон + - Панель настроек с авто-проверкой обновлений + - Slint GUI с поддержкой dark/light темы (если Issue #N уже закрыт) + +2. Из раздела «Что ещё не готово» убрать пункты которые уже реализованы. + +3. Обновить версию в заголовке на v0.6.2. + +4. Формат подачи — заполнить все поля из объявления конкурса: +\`\`\` +Название: vimit +Что делает: ... +Для кого полезно: ... +GitHub: https://github.com/xodapi/vimit +Как запустить: ... +Какие ОС поддерживаются: Windows, Linux, macOS, Android/Termux +Что уже работает: ... +Что ещё не готово: ... +\`\`\` + +## Критерии приёмки + +- Версия в заголовке = v0.6.2 +- Все поля конкурсной формы заполнены +- Ни один реализованный пункт не стоит в «не готово» + +## Что нельзя трогать + +- \`src/\`, \`ui/\`, \`Cargo.toml\`, CI-файлы" + +echo "" +echo "=== Готово! ===" +echo "Открытые Issues:" +gh issue list --repo "$REPO" --state open --label agent diff --git a/docs/vimit_floating_overlay.html b/docs/vimit_floating_overlay.html new file mode 100644 index 0000000..ce04f18 --- /dev/null +++ b/docs/vimit_floating_overlay.html @@ -0,0 +1,255 @@ + + + +
+ +
+
+
+
+
5h: 78% · 1.8 кред/мин
+
нажать чтобы раскрыть
+
+
+
+ +
+
+ vimit · VibeMode +
+ + + +
+
+ +
+
+ 1.8 + кред/мин +
+ +12% к норме +
+ +
+ +
+ +
расход за 15 мин (кред/мин)
+
+ График расхода +
+ +
+ сброс 5h окна + через 3ч 59м +
+ +
+ + + +
+
+ +
← прототип плавающего оверлея для Issue агенту
+
+ + + diff --git a/overlay_issue.md b/overlay_issue.md new file mode 100644 index 0000000..49482db --- /dev/null +++ b/overlay_issue.md @@ -0,0 +1,13 @@ +Новая команда: `vimit overlay` (или `vimit --overlay`) +Открывает отдельное маленькое окно (300x260px) поверх всех окон (window_level: AlwaysOnTop в Slint/winit). + +Компоненты: +1. Строка расхода: "1.8 кред/мин" (большой шрифт) + дельта-бадж +2. 4 строки окон: `[5h] [████░░] 78% | через 3ч 59м` +3. Sparkline последних 15 точек polling (canvas или встроенный рендер) +4. Переключатель единиц: кред/мин / токены/мин +5. Кнопка свернуть → compact строка в системный трей или мини-бар +6. Таймер сброса ближайшего окна с обратным отсчётом + +Данные берутся из того же polling loop что и основной GUI. +zone:gui — только `ui/` и `src/bin/vimit-gui.rs` diff --git a/setup-issues.sh b/setup-issues.sh new file mode 100644 index 0000000..73054b8 --- /dev/null +++ b/setup-issues.sh @@ -0,0 +1,246 @@ +#!/bin/bash +# setup-issues.sh +# Создаёт стартовый набор Issues для vimit с правильными labels +# Запускать один раз: bash setup-issues.sh + +set -e + +REPO="xodapi/vimit" + +echo "=== Создание labels ===" + +labels=( + "agent|0075ca|Готово к выполнению агентом" + "blocked|e11d48|Заблокировано" + "gui|7c3aed|Изменения в Slint GUI" + "tui|0891b2|Изменения в ratatui TUI" + "priority:high|dc2626|Высокий приоритет" + "priority:low|6b7280|Низкий приоритет" +) + +for label_def in "${labels[@]}"; do + IFS='|' read -r name color desc <<< "$label_def" + gh label create "$name" --color "$color" --description "$desc" --repo "$REPO" 2>/dev/null || \ + gh label edit "$name" --color "$color" --description "$desc" --repo "$REPO" 2>/dev/null || true +done + +echo "=== Labels созданы ===" + +echo "=== Создание Issues ===" + +# Issue 1: CI fix (самый срочный) +gh issue create \ + --repo "$REPO" \ + --title "fix(ci): install gtk deps for linux, split gui feature matrix" \ + --label "ci,agent,priority:high" \ + --body "## Что сделать + +В \`.github/workflows/ci.yml\` исправить провал Linux-сборки с \`--features gui\`. + +## Конкретные изменения + +1. В job \`check\` для \`ubuntu-latest\` добавить шаг перед \`cargo check\`: +\`\`\`yaml +- name: Install Linux system deps + if: matrix.os == 'ubuntu-latest' + run: sudo apt-get update && sudo apt-get install -y libglib2.0-dev libgtk-3-dev +\`\`\` + +2. Либо (проще): для \`ubuntu-latest\` убрать \`--features gui\` из clippy и test — GUI-фичу проверять только на Windows/macOS. + +3. В README.md и README.ru.md заменить бадж \`rust-2021\` на \`rust-2024\`. + +## Критерии приёмки + +- CI #48+ проходит зелёным на всех трёх ОС +- \`cargo test --locked\` локально чистый +- Бадж в README обновлён + +## Что нельзя трогать + +- \`src/\` — никаких изменений кода +- \`SUBMISSION.ru.md\`" + +# Issue 2: Theme system в Slint +gh issue create \ + --repo "$REPO" \ + --title "feat(gui): add theme.slint with dark/light mode support" \ + --label "feat,gui,agent" \ + --body "## Что сделать + +Создать \`ui/theme.slint\` с глобальной цветовой системой и подключить в \`ui/app.slint\`. + +## Конкретные изменения + +1. Создать \`ui/theme.slint\`: +\`\`\`slint +export global Theme { + in-out property dark-mode: true; + property bg-primary: dark-mode ? #0f131a : #f8fafc; + property bg-card: dark-mode ? #1a202c : #ffffff; + property bg-surface: dark-mode ? #111827 : #f1f5f9; + property border: dark-mode ? #334155 : #e2e8f0; + property text-primary: dark-mode ? #f1f5f9 : #0f172a; + property text-muted: dark-mode ? #64748b : #94a3b8; + property success: #22c55e; + property warning: #f59e0b; + property danger: #ef4444; +} +\`\`\` + +2. В \`LimitCard\` добавить: +\`\`\`slint +private property accent: + root.percent >= 90 ? Theme.danger : + root.percent >= 75 ? Theme.warning : Theme.success; +\`\`\` + +3. Большой процент: \`font-size: 44px; font-weight: 800; color: root.accent\` + +4. В панели настроек добавить CheckBox «Светлая тема» → переключает \`Theme.dark-mode\` + +5. Переименовать «пик» → «макс» везде в UI + +## Критерии приёмки + +- \`cargo build --features gui --locked\` без ошибок +- GUI запускается в demo-режиме, переключение темы работает +- Большой % виден сразу при открытии окна + +## Что нельзя трогать + +- \`src/\` Rust-код — только \`ui/\` файлы +- Логика polling и API" + +# Issue 3: Дневной лимит +gh issue create \ + --repo "$REPO" \ + --title "feat(core): add daily credit limit tracking" \ + --label "feat,agent,tui,gui" \ + --body "## Что сделать + +Добавить отслеживание дневного лимита расхода кредитов. + +## Логика + +Расчётный рекомендуемый лимит/день = \`остаток_7d / дней_до_сброса_7d\` + +Если пользователь задал свой лимит через \`--daily-limit N\` — использовать его. + +Хранить в \`~/.config/vimit/daily.toml\`: +\`\`\`toml +limit = 56.0 # кредиты на день (0 = не задан, считать авто) +date = \"2026-06-28\" # при смене даты сбрасывать +spent_today = 12.3 +\`\`\` + +## Что показывать + +В TUI (compact и full preset) — строка под окном 7d: +\`\`\` +День: 12.3 / 56.0 (22%) [████░░░░░░] +\`\`\` + +В JSON-выводе — поле \`daily\` рядом с \`windows\`. + +## CLI + +\`\`\` +vimit --daily-limit 56 # задать лимит +vimit --daily-limit 0 # сбросить (использовать авто) +\`\`\` + +## Критерии приёмки + +- \`cargo test --locked\` — все тесты зелёные +- \`vimit --demo --daily-limit 50\` показывает строку дневного лимита +- \`vimit --demo --json\` содержит поле \`daily\` +- При смене даты spent_today сбрасывается в 0 + +## Что нельзя трогать + +- \`ui/app.slint\` (GUI версия — отдельный Issue) +- \`SUBMISSION.ru.md\`" + +# Issue 4: Скриншоты в README +gh issue create \ + --repo "$REPO" \ + --title "docs(readme): add demo screenshots to README" \ + --label "docs,agent,priority:high" \ + --body "## Что сделать + +Добавить скриншоты TUI-монитора и GUI в README.md и README.ru.md. + +## Шаги + +1. Запустить \`.\target\release\vimit.exe --demo --monitor\`, сделать скриншот терминала → сохранить в \`assets/demo-tui.png\` + +2. Запустить \`.\target\release\vimit-gui.exe --demo\` (или \`--features gui\`), сделать скриншот → сохранить в \`assets/demo-gui.png\` + +3. В README.md после раздела \`## Features\` добавить: +\`\`\`markdown +## Screenshots + +![TUI Monitor](assets/demo-tui.png) +![GUI](assets/demo-gui.png) +\`\`\` + +4. То же самое в README.ru.md + +## Критерии приёмки + +- Оба файла \`assets/demo-tui.png\` и \`assets/demo-gui.png\` существуют в репо +- В обоих README изображения отображаются на странице GitHub +- Размер каждого PNG < 500KB + +## Что нельзя трогать + +- \`src/\`, \`ui/\`, \`Cargo.toml\`" + +# Issue 5: SUBMISSION update +gh issue create \ + --repo "$REPO" \ + --title "docs: update SUBMISSION.ru.md for v0.6.2 final" \ + --label "docs,agent,priority:high" \ + --body "## Что сделать + +Обновить \`SUBMISSION.ru.md\` для финальной подачи на конкурс (дедлайн 1 июля). + +## Конкретные изменения + +1. В разделе «Что уже работает» добавить: + - Система self-update (\`vimit update\` / \`vimit update --check\`) + - System tray tooltip с процентами окон + - Панель настроек с авто-проверкой обновлений + - Slint GUI с поддержкой dark/light темы (если Issue #N уже закрыт) + +2. Из раздела «Что ещё не готово» убрать пункты которые уже реализованы. + +3. Обновить версию в заголовке на v0.6.2. + +4. Формат подачи — заполнить все поля из объявления конкурса: +\`\`\` +Название: vimit +Что делает: ... +Для кого полезно: ... +GitHub: https://github.com/xodapi/vimit +Как запустить: ... +Какие ОС поддерживаются: Windows, Linux, macOS, Android/Termux +Что уже работает: ... +Что ещё не готово: ... +\`\`\` + +## Критерии приёмки + +- Версия в заголовке = v0.6.2 +- Все поля конкурсной формы заполнены +- Ни один реализованный пункт не стоит в «не готово» + +## Что нельзя трогать + +- \`src/\`, \`ui/\`, \`Cargo.toml\`, CI-файлы" + +echo "" +echo "=== Готово! ===" +echo "Открытые Issues:" +gh issue list --repo "$REPO" --state open --label agent diff --git a/src/bin/test.rs b/src/bin/test.rs new file mode 100644 index 0000000..8600faa --- /dev/null +++ b/src/bin/test.rs @@ -0,0 +1,7 @@ +fn main() { + let payload = vimit::demo_payload(); + let windows = vimit::summarize_me(&payload, 75.0, 90.0); + for w in windows { + println!("{:?}", w); + } +} diff --git a/src/bin/vimit-gui.rs b/src/bin/vimit-gui.rs index ea3c333..9d99557 100644 --- a/src/bin/vimit-gui.rs +++ b/src/bin/vimit-gui.rs @@ -60,56 +60,20 @@ struct GuiAccount { } fn load_gui_accounts() -> (Vec, Vec) { - use std::fs; - use std::path::PathBuf; - let home = if cfg!(windows) { - std::env::var("APPDATA") - .ok() - .map(PathBuf::from) - .or_else(|| std::env::var("USERPROFILE").ok().map(PathBuf::from)) - } else { - std::env::var("HOME").ok().map(PathBuf::from) - }; - let Some(home) = home else { - return (vec![], vec![]); - }; - let config_dir = if cfg!(windows) { - home.join("vimit") + if let Ok(config) = ng::cli::accounts::AccountsConfig::load() { + let names = config.list_names(); + let configs: Vec = names + .iter() + .filter_map(|n| config.resolve(n).ok()) + .map(|r| GuiAccount { + api_key_env: r.api_key_env.clone(), + api_base: r.api_base.clone(), + }) + .collect(); + (names, configs) } else { - home.join(".config").join("vimit") - }; - let path = config_dir.join("accounts.toml"); - if !path.is_file() { - return (vec![], vec![]); + (vec![], vec![]) } - let raw = match fs::read_to_string(&path) { - Ok(r) => r, - Err(_) => return (vec![], vec![]), - }; - #[derive(serde::Deserialize)] - struct RawAcct { - api_key_env: Option, - api_base: Option, - } - #[derive(serde::Deserialize)] - struct RawRoot { - accounts: HashMap, - } - let parsed: RawRoot = match toml::from_str(&raw) { - Ok(p) => p, - Err(_) => return (vec![], vec![]), - }; - let mut names: Vec = parsed.accounts.keys().cloned().collect(); - names.sort(); - let configs: Vec = names - .iter() - .filter_map(|n| parsed.accounts.get(n)) - .map(|r| GuiAccount { - api_key_env: r.api_key_env.clone(), - api_base: r.api_base.clone(), - }) - .collect(); - (names, configs) } fn main() { @@ -213,10 +177,16 @@ fn main() { let state = current_acct.clone(); let configs = account_configs.clone(); + let weak_app = app.as_weak(); app.on_account_changed(move |name| { if let Some(idx) = account_names.iter().position(|n| n == name.as_str()) { - let mut cur = state.lock().unwrap(); - *cur = Some(configs[idx].clone()); + { + let mut cur = state.lock().unwrap(); + *cur = Some(configs[idx].clone()); + } + if let Some(app) = weak_app.upgrade() { + app.invoke_refresh_requested(); + } } }); } @@ -387,6 +357,43 @@ fn main() { } }); + app.on_open_accounts_config(move || { + let home = if cfg!(windows) { + std::env::var("APPDATA") + .ok() + .map(std::path::PathBuf::from) + .or_else(|| { + std::env::var("USERPROFILE") + .ok() + .map(std::path::PathBuf::from) + }) + } else { + std::env::var("HOME").ok().map(std::path::PathBuf::from) + }; + if let Some(home) = home { + let config_dir = if cfg!(windows) { + home.join("vimit") + } else { + home.join(".config").join("vimit") + }; + let accounts_toml = config_dir.join("accounts.toml"); + if accounts_toml.exists() { + #[cfg(windows)] + let _ = std::process::Command::new("explorer") + .arg(&accounts_toml) + .spawn(); + #[cfg(target_os = "macos")] + let _ = std::process::Command::new("open") + .arg(&accounts_toml) + .spawn(); + #[cfg(target_os = "linux")] + let _ = std::process::Command::new("xdg-open") + .arg(&accounts_toml) + .spawn(); + } + } + }); + app.run().expect("Slint event loop failed"); } diff --git a/src/cli/monitor.rs b/src/cli/monitor.rs index 0c3333e..b6654ff 100644 --- a/src/cli/monitor.rs +++ b/src/cli/monitor.rs @@ -320,12 +320,7 @@ pub fn run_monitor( .saturating_duration_since(Instant::now()) .as_secs(); - let current_account_name = if has_accounts && total_accounts > 1 { - Some(account_names[cur_account].as_str()) - } else { - None - }; - + // Removed current_account_name calculation here terminal .draw(|frame| { draw_frame( @@ -340,7 +335,8 @@ pub fn run_monitor( args.preset, args.theme, &panels, - current_account_name, + account_names, + cur_account, &trend_days, ); }) @@ -399,7 +395,8 @@ fn draw_frame( preset: Preset, theme: Theme, panels: &PanelState, - current_account: Option<&str>, + account_names: &[String], + cur_account: usize, trend_days: &[TrendDay], ) { if panels.show_help { @@ -434,7 +431,15 @@ fn draw_frame( let mut idx = 0; if panels.show_header { - draw_header(frame, chunks[idx], snapshot, &pal, current_account, panels); + draw_header( + frame, + chunks[idx], + snapshot, + &pal, + account_names, + cur_account, + panels, + ); idx += 1; } if panels.show_quota { @@ -457,13 +462,20 @@ fn draw_frame( idx += 1; } if panels.show_footer { + let current_account_name = if account_names.len() > 1 { + Some(account_names[cur_account].as_str()) + } else if account_names.len() == 1 { + Some(account_names[0].as_str()) + } else { + None + }; draw_footer( frame, chunks[idx], interval_secs, next_refresh_secs, &pal, - current_account, + current_account_name, ); } } @@ -473,12 +485,30 @@ fn draw_header( area: Rect, snapshot: Option<&StatusSnapshot>, pal: &Palette, - current_account: Option<&str>, + account_names: &[String], + cur_account: usize, panels: &PanelState, ) { - let account_prefix = current_account - .map(|name| format!(" [{name}]")) - .unwrap_or_default(); + let account_prefix = if account_names.len() > 1 { + let mut list = String::new(); + for (i, name) in account_names.iter().enumerate() { + if i > 0 { + list.push_str(" | "); + } + if i == cur_account { + list.push('*'); + list.push_str(name); + list.push('*'); + } else { + list.push_str(name); + } + } + format!(" [ {list} ] (Tab to switch)") + } else if account_names.len() == 1 { + format!(" [{}]", account_names[0]) + } else { + String::new() + }; let vpn_label = if panels.vpn_mode { " VPN" } else { " Dir" }; let (title, style) = match snapshot { Some(s) => { @@ -601,10 +631,16 @@ fn draw_body( ); } } else { - let waiting = Paragraph::new(Span::styled( - "Collecting VibeMode status...", - pal.muted_style(), - )) + let waiting = Paragraph::new(vec![ + Line::from(Span::styled( + "Collecting VibeMode status...", + pal.muted_style(), + )), + Line::from(Span::styled( + "Press r to refresh, ? for help", + pal.muted_style(), + )), + ]) .block( Block::default() .title("Limits") @@ -839,35 +875,36 @@ fn draw_footer( let has_multi_account = current_account.is_some(); let height = area.height; let footer = if height <= 1 { - let mut text = format!("q quit | r refresh | {interval_secs}s/{next_refresh_secs}s"); + let mut text = "? help | 5 trends".to_string(); if has_multi_account { - text = format!("Tab acct | {text}"); + text = format!("{text} | Tab account"); } + text = format!("{text} | q quit | r refresh | {interval_secs}s/{next_refresh_secs}s"); if let Some(latest) = super::update::latest_checked_version() { - text = format!("{text} | ⚠️ Update v{latest}!"); + text = format!("{text} | ⚠ Update v{latest}!"); } Paragraph::new(Line::from(vec![Span::styled(text, pal.muted_style())])) } else { let mut spans = vec![ - Span::styled(" q ", pal.key_binding_style()), - Span::raw("quit "), - Span::styled(" r ", pal.key_binding_style()), - Span::raw(format!( - "refresh auto {interval_secs}s next {next_refresh_secs}s" - )), + Span::styled(" ? ", pal.key_binding_style()), + Span::raw("help "), + Span::styled(" 5 ", pal.key_binding_style()), + Span::raw("trends "), ]; if has_multi_account { - let mut with_tab = vec![ - Span::styled(" Tab ", pal.key_binding_style()), - Span::raw("account "), - ]; - with_tab.extend(spans); - spans = with_tab; + spans.push(Span::styled(" Tab ", pal.key_binding_style())); + spans.push(Span::raw("account ")); } + spans.push(Span::styled(" q ", pal.key_binding_style())); + spans.push(Span::raw("quit ")); + spans.push(Span::styled(" r ", pal.key_binding_style())); + spans.push(Span::raw(format!( + "refresh auto {interval_secs}s next {next_refresh_secs}s" + ))); if let Some(latest) = super::update::latest_checked_version() { spans.push(Span::raw(" ")); spans.push(Span::styled( - format!("⚠️ Update v{latest} available! Run `vimit update`"), + format!("⚠ Update v{latest}"), pal.bold_level_style("warning"), )); } @@ -1326,7 +1363,7 @@ pub fn render_monitor_lines( lines.push(fit_text( &format!( - "q quit | Esc quit | r refresh now | auto {}s | next {}s | .env next to binary supported", + "? help | 5 trends | Tab account | q quit | r refresh | auto {}s | next {}s", interval_secs, next_refresh_secs ), width, @@ -1641,7 +1678,8 @@ mod tests { preset, theme, &panels, - None, + &[], + 0, &[], ); }) diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_compact_preset.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_compact_preset.snap index 4b649ca..e33f190 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_compact_preset.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_compact_preset.snap @@ -26,4 +26,4 @@ expression: output └──────────────────────────────────────────────────────────────────────────────┘ ┌ agents ──────────────────────────────────────────────────────────────────────┐ └──────────────────────────────────────────────────────────────────────────────┘ -q quit | r refresh | 5s/3s +? help | 5 trends | q quit | r refresh | 5s/3s diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_dracula_theme.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_dracula_theme.snap index 21306a1..6c2fe5b 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_dracula_theme.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_dracula_theme.snap @@ -40,5 +40,5 @@ expression: output │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ q quit r refresh auto 5s next 3s │ +│ ? help 5 trends q quit r refresh auto 5s next 3s │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_full_preset.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_full_preset.snap index e19d22b..5932141 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_full_preset.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_full_preset.snap @@ -40,5 +40,5 @@ expression: output │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ q quit r refresh auto 5s next 3s │ +│ ? help 5 trends q quit r refresh auto 5s next 3s │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_high_contrast_theme.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_high_contrast_theme.snap index 21306a1..6c2fe5b 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_high_contrast_theme.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_high_contrast_theme.snap @@ -40,5 +40,5 @@ expression: output │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ q quit r refresh auto 5s next 3s │ +│ ? help 5 trends q quit r refresh auto 5s next 3s │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_mini_preset.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_mini_preset.snap index 82a0fd0..0377baf 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_mini_preset.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_mini_preset.snap @@ -16,4 +16,4 @@ expression: output └──────────────────────────────────────────────────────────┘ ┌ alerts ──────────────────────────────────────────────────┐ ┌ agents ──────────────────────────────────────────────────┐ -q quit | r refresh | 5s/3s +? help | 5 trends | q quit | r refresh | 5s/3s diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_waiting.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_waiting.snap index 2559248..8c034c5 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_waiting.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_waiting.snap @@ -7,7 +7,7 @@ expression: output └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ┌Limits────────────────────────────────────────────────────────────────────────────────────────────┐ │Collecting VibeMode status... │ -│ │ +│Press r to refresh, ? for help │ │ │ │ │ │ │ @@ -30,5 +30,5 @@ expression: output │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ q quit r refresh auto 5s next 3s │ +│ ? help 5 trends q quit r refresh auto 5s next 3s │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘ diff --git a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_with_error.snap b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_with_error.snap index c75c52d..f8cdc26 100644 --- a/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_with_error.snap +++ b/src/cli/snapshots/vimit__cli__monitor__tests__tui_snapshot_with_error.snap @@ -30,5 +30,5 @@ expression: output │ │ ! connection timeout──────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ q quit r refresh auto 5s next 3s │ +│ ? help 5 trends q quit r refresh auto 5s next 3s │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘ diff --git a/ui/app.slint b/ui/app.slint index b374c65..926674a 100644 --- a/ui/app.slint +++ b/ui/app.slint @@ -197,6 +197,7 @@ export component AppWindow inherits Window { callback auto-update-changed(bool); callback close-overlay(); callback open-config-dir(); + callback open-accounts-config(); in-out property error-text: ""; in-out property needs-setup: false; @@ -547,6 +548,11 @@ export component AppWindow inherits Window { } } + Button { + text: "📂 Config"; + clicked => { root.open-accounts-config(); } + } + Rectangle { } } }