docs+feat: USAGE.md + interesting_fact + mailto packs

USAGE.md (new, ~250 lines)
  - Complete voice command reference organised by 13 categories
  - Table-formatted for fast skim ("Команда → Что делает")
  - Sections: Управление ассистентом / LLM / Память+профили+macros /
    Расписание / Окна+апп / Аудио+медиа / Система / Файлы+буфер /
    Информация из интернета / Утилиты-калькуляторы / Разработка /
    Развлечения / Скриншоты
  - Bottom: "Как добавить свою команду" with Lua snippet template,
    "Голосовое vs GUI" mapping, troubleshooting checklist (mic / TTS /
    LLM / command-not-found / logs).
  - Covers all 67 packs visible in resources/commands/.

interesting_fact pack (resources/commands/interesting_fact/, 2 cmds)
  - fact.about         "удиви меня" / "интересный факт про космос"
                       → LLM with "curious conversationalist" prompt,
                       generic or topic-targeted (1-2 sentences, no fluff).
  - fact.history_today "что было в этот день" / "этот день в истории"
                       → Wikipedia "On this day" API (RU first, EN fallback)
                       → LLM picks top 2-3 events, narrates concisely.

mailto pack (resources/commands/mailto/, 1 cmd)
  - mailto.compose     "напиши письмо" / "напиши письмо маме"
                       → opens default mail client via mailto: URI.
                       Looks up recipient via jarvis.memory ("email_маме")
                       if a name is given.
  - Pairs well with: jarvis.memory.remember("email_маме", "mom@example.com")

Pack count: 67 → 70. Tests: 112/112.
This commit is contained in:
Bossiara13 2026-05-15 23:38:03 +03:00
parent d3180b7d78
commit 4f83062815
6 changed files with 430 additions and 0 deletions

259
USAGE.md Normal file
View file

@ -0,0 +1,259 @@
# Voice command reference
Все голосовые команды J.A.R.V.I.S. (текущее состояние, **67 паков**).
Каждая команда — это фраза, которую можно произнести после wake-word ("Джарвис").
Группы:
- [Управление ассистентом](#управление-ассистентом)
- [LLM / мозг](#llm--мозг)
- [Память / профили / macros](#память--профили--macros)
- [Расписание / напоминания / привычки](#расписание--напоминания--привычки)
- [Окна / приложения / процессы](#окна--приложения--процессы)
- [Аудио / медиа](#аудио--медиа)
- [Система / устройство](#система--устройство)
- [Файлы / буфер / заметки](#файлы--буфер--заметки)
- [Информация из интернета](#информация-из-интернета)
- [Утилиты-калькуляторы](#утилиты-калькуляторы)
- [Разработка](#разработка)
- [Развлечения](#развлечения)
- [Скриншоты / экран](#скриншоты--экран)
После каждой команды есть **30-секундное grace-окно** для follow-up без повторного "Джарвис".
Конфигурируется через `CONVERSATION_GRACE_MS`.
---
## Управление ассистентом
| Команда (RU) | Что делает |
|---|---|
| "Помощь" / "что ты умеешь" | Перечисляет категории возможностей |
| "Расскажи о себе" / "кто ты такой" | Краткое представление (LLM варьирует) |
| "Сколько команд знаешь" | Health snapshot — все активные бэкенды + счётчики |
| "Диагностика" / "доложи о себе" | Одной строкой: TTS, LLM, профиль, памяти/задач |
| "Повтори за мной X" | Эхо — для теста микрофона + TTS |
| "Что я сказал" | Что услышал из последней фразы |
| "Стоп" / "хватит" / "прекрати" | Прерывает текущую команду |
| "Сбрось контекст" / "забудь разговор" | Очищает историю LLM |
| "Повтори последнее" / "повтори ответ" | Озвучивает последний ответ LLM |
## LLM / мозг
| Команда | Что делает |
|---|---|
| "Переключись на локальный" / "перейди на оллама" | Hot-swap на Ollama (persistent в БД) |
| "Переключись на облако" / "используй грок" | Hot-swap на Groq |
| "Какой у тебя мозг" / "облако или локально" | Текущий backend |
Любая фраза без распознанной команды → LLM router (если включён) → если нет матча → LLM chat fallback.
## Память / профили / macros
| Команда | Что делает |
|---|---|
| "Запомни что я люблю чай улун" | Сохраняет факт |
| "Что ты помнишь про чай" | Substring-поиск по памяти |
| "Что ты знаешь обо мне" | Все факты |
| "Забудь про X" | Удалить факт |
| "Режим работа" / "режим игра" / "режим сон" / "режим за рулём" / "обычный режим" | Профиль (5 встроенных) |
| "Какой сейчас режим" | Активный профиль |
| "Запиши макрос имя" | Начать запись макроса |
| "Сохрани макрос" | Завершить запись и сохранить |
| "Отмени макрос" | Отменить запись |
| "Запусти макрос имя" | Воспроизвести (с интервалом 800мс) |
| "Какие у меня макросы" / "удали макрос имя" | Управление списком |
Также: GUI страницы `/memory` (добавить/забыть факты), `/macros` (запуск/удаление), `/scheduler` (отмена задач).
## Расписание / напоминания / привычки
| Команда | Что делает |
|---|---|
| "Напомни через 5 минут выключить кофеварку" | One-shot reminder |
| "Напомни в 18:00 забрать ребёнка" | Today at HH:MM |
| "Каждые 2 часа напоминай попить воды" | Interval |
| "Каждый день в 9:00 делай брифинг" | Daily |
| "Что у меня запланировано" / "очисти расписание" | Управление |
| "Отмени напоминание про воду" | Substring-match remove |
| "Запусти помодоро" / "стоп помодоро" | 25/5 циклы |
| "Напоминай мне пить воду" | Каждые 2 часа |
| "Напоминай размяться" | Каждые 50 минут |
| "Напоминай отдыхать глазам" | 20-20-20 (каждые 20 мин) |
| "Отключи все привычки" | Стоп всех habit_nudge задач |
| "Настрой утренний брифинг на 9:00" | Daily briefing в указанное время |
| "Утренний брифинг" | Запустить брифинг сейчас |
| "Отключи утренний брифинг" | Снять daily задачу |
## Окна / приложения / процессы
| Команда | Что делает |
|---|---|
| "Открой Telegram" / "запусти Steam" / "Spotify" | Через `apps/` пак (имя в config) |
| "Закрой Telegram" / "убей процесс X" | Process kill |
| "Сверни всё" / "разверни окно" / "переключи окно" | Window manager |
| "Какие открыты окна" | Список Z-order |
| "Сверни активное" / "максимизируй" | Action на foreground |
| "Открой папку загрузок" / "файлы" / "проводник" | Explorer |
## Аудио / медиа
| Команда | Что делает |
|---|---|
| "Громче" / "тише" / "громкость на 30" | Volume up/down/set |
| "Заглуши" / "сними заглушку" | Mute toggle |
| "Какая громкость" | Текущий уровень |
| "Открой панель звука" | Sound panel |
| "Включи Realtek" / "переключи звук на Headphones" | Output device swap |
| "Пауза" / "плей" / "следующий трек" / "предыдущий трек" / "стоп музыка" | Media keys — Spotify / YouTube / Foobar / Yandex Music |
| "Запиши голосовую заметку" | (см. notes/) |
| "Выключи музыку через 30 минут" | Sleep timer для медиа |
## Система / устройство
| Команда | Что делает |
|---|---|
| "Сколько процессор грузится" / "сколько RAM" / "температура" | Sysinfo |
| "Сколько заряда" / "заряд батареи" | Battery % |
| "Сколько свободно на диске" / "сколько на диске C" | PowerShell Get-PSDrive |
| "Какие у меня диски" | C: D: E: со свободным местом |
| "Какая сеть" / "какой WiFi" | Net SSID via netsh |
| "Мой IP" / "локальный IP" | Get-NetIPAddress |
| "Внешний IP" | api.ipify.org |
| "Прибавь яркость" / "тусклее" / "яркость 70" | Brightness via WMI |
| "Тёмная тема" / "светлая тема" | Windows theme |
| "Заблокируй компьютер" | LockWorkStation |
| "Выключи компьютер" / "перезагрузи" / "ребутни" / "сон" / "гибернация" / "выйди из системы" / "отмени выключение" | Power management |
| "Выключи компьютер через 30 минут" | Sleep timer для системы |
| "Отмени таймер выключения" | shutdown /a + scheduler clear |
## Файлы / буфер / заметки
| Команда | Что делает |
|---|---|
| "Найди файл report" / "поищи documents" | File search |
| "Запиши заметку купить хлеб" | Append в notes file |
| "Покажи заметки" / "что я записал" | Открывает notes |
| "Прочитай буфер" / "что в буфере" | Clipboard contents speak |
| "Переведи буфер на английский" | LLM перевод + replace clipboard |
## Информация из интернета
| Команда | Что делает |
|---|---|
| "Какая погода" / "погода завтра" / "что с погодой" | Weather pack |
| "Курс доллара" / "курс юаня" | CBR rates |
| "Сколько 1000 долларов в рублях" | Currency convert |
| "Сколько Сбер" / "сколько Газпром" | MOEX котировки (22 тикера) |
| "Сколько биткоин" / "сколько эфир" | Crypto |
| "Новости" / "что нового в мире" | News RSS |
| "Найди в гугле X" / "загугли X" | DuckDuckGo Instant + LLM |
| "Википедия X" / "что такое X" / "кто такой Y" | Wikipedia REST |
| "Открыть Google поиск X" | Websearch (open browser) |
| "Переведи на английский X" | Translate |
## Утилиты-калькуляторы
| Команда | Что делает |
|---|---|
| "Посчитай 2 плюс 2" / "сколько будет 10 в степени 3" | Math |
| "Переведи 100 метров в футы" | Length convert |
| "70 кг в фунты" | Weight convert |
| "100 цельсий в фаренгейт" | Temperature convert |
| "100 км в час в мили в час" | Speed convert |
| "Сколько дней до нового года" | Date math (8 марта, 9 мая, Рождество, Новый год + любая дата) |
| "Какой сегодня день недели" | Zeller's congruence |
| "Время" / "сколько времени" / "какое сегодня число" | Date query |
| "Подбрось монету" / "орёл или решка" | Coin flip |
| "Сгенерируй пароль 16 символов" | Secure password → clipboard |
| "Сгенерируй UUID" | v4 UUID → clipboard |
| "Брось кубик" / "брось два кубика" | Dice |
| "Выбери случайно из X или Y или Z" | Random choice |
| "Запусти секундомер" / "сколько прошло" / "стоп секундомер" | Stopwatch |
| "Прибавь счётчик" / "сколько на счётчике" / "сбрось счётчик" | Counter |
| "Как пишется X" | Spelling |
## Разработка
| Команда | Что делает |
|---|---|
| "Укажи проект C:\..." | Codebase Q&A: установить root |
| "Какой проект сейчас" | Текущий root |
| "Что делает функция X" / "найди в коде Y" / "объясни код" | Walks codebase → LLM |
| "Текущий репо owner/repo" | GitHub: установить repo |
| "Какие пиары" / "открытые пиары" | List PRs via gh CLI |
| "Разбери последний пиар" | LLM-ревью PR (gh pr view) |
| "Сгенерируй код для X" | Code generation pack |
| "Открой Cursor" / "VS Code" | apps/ |
## Развлечения
| Команда | Что делает |
|---|---|
| "Расскажи анекдот" / "пошути" / "скажи фразу" / "тостер" | Fun pack (jokes) |
| "Запиши настроение 7" / "сегодня мне грустно" | Mood log |
| "Как прошла неделя" / "сводка настроения" | Mood recap via LLM |
| "Запусти Steam игру X" / "поиграй в Y" | Games pack |
## Скриншоты / экран
| Команда | Что делает |
|---|---|
| "Что на экране" / "опиши экран" | Vision LLM описание |
| "Прочитай ошибку" | Vision LLM: ищет stack trace, объясняет |
| "Сделай скриншот" / "скрин в буфер" | Screenshot → clipboard |
| "Сохрани скриншот" | Screenshot → ~/Pictures/Screenshots/ |
| "OCR экрана" / "прочитай текст" | OCR (existing pack) |
| "Суммаризируй экран" | Summarize visible window |
---
## Как добавить свою команду
Скопируй `resources/commands/echo/` как шаблон, отредактируй `command.toml`
(id, phrases) и `*.lua` (логика). Рекомендуемая структура — см.
[ARCHITECTURE.md → Recommended pack structure](ARCHITECTURE.md#recommended-pack-structure).
Минимум:
```lua
-- thing.lua
local phrase = (jarvis.context.phrase or ""):lower()
local body = jarvis.text.strip_trigger(phrase, { "сделай штуку" })
body = body:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
if body == "" then
return jarvis.cmd.error("Что именно?")
end
-- ... do work ...
return jarvis.cmd.ok("Готово.")
```
Затем `cargo test -p jarvis-core --lib commands::tests` — он автоматически
проверит что TOML парсится и Lua-скрипт существует.
## Голосовое vs GUI
GUI страницы — для случаев когда голосом неудобно:
- `/commands` — все 67 паков с поиском (для discovery)
- `/macros` — список с кнопкой "Запустить"
- `/scheduler` — задачи с auto-poll каждые 5 сек
- `/memory` — добавление/удаление фактов с фильтром
- `/settings → AI Backends` — переключение LLM/TTS через dropdown
## Что-то не работает?
1. **Голос не распознаётся** — проверь микрофон в Settings → Devices. Скажи
"повтори за мной привет" — если эхо проходит, проблема не в STT.
2. **Не слышу ответ** — проверь TTS backend в Settings → AI Backends. Скажи
"проверка эхо" — должен быть слышен голос. Если нет, проверь динамики +
попробуй `JARVIS_TTS=sapi`.
3. **"Не могу связаться с сервером"** — LLM unreachable. Если Groq —
проверь GROQ_TOKEN и интернет. Если Ollama — `ollama serve` запущен?
4. **Команда не найдена** — посмотри `/commands` в GUI, поищи фразу. Если
фразы похожи но не та — agentic router сделает попытку, или подскажет.
5. **Что-то странное в логах**`<APP_LOG_DIR>/jarvis.log` (путь видно
в первых строках `jarvis-app` stdout).

View file

@ -0,0 +1,38 @@
# Interesting fact about X via LLM.
[[commands]]
id = "fact.about"
type = "lua"
script = "fact.lua"
sandbox = "full"
timeout = 15000
[commands.phrases]
ru = [
"расскажи интересный факт",
"интересный факт о",
"интересный факт про",
"удиви меня",
"интересный факт",
"расскажи факт",
]
en = ["tell me an interesting fact", "fun fact about", "surprise me"]
ua = ["цікавий факт"]
[[commands]]
id = "fact.history_today"
type = "lua"
script = "history_today.lua"
sandbox = "full"
timeout = 15000
[commands.phrases]
ru = [
"что было в этот день",
"что произошло в этот день",
"этот день в истории",
"что случилось",
]
en = ["this day in history", "what happened today"]
ua = ["що сталося в цей день"]

View file

@ -0,0 +1,35 @@
-- "Интересный факт о космосе" / "удиви меня"
local phrase = (jarvis.context.phrase or ""):lower()
local topic = jarvis.text.strip_trigger(phrase, {
"расскажи интересный факт про",
"расскажи интересный факт о",
"интересный факт про",
"интересный факт о",
"расскажи интересный факт",
"интересный факт",
"расскажи факт",
"удиви меня",
"tell me an interesting fact",
"fun fact about",
"surprise me",
"цікавий факт",
})
topic = topic:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
local user_prompt
if topic == "" then
user_prompt = "Расскажи один реально неочевидный научно-проверенный факт. На русском, 1-2 предложения. Без вступлений типа 'знали ли вы'."
else
user_prompt = "Расскажи один интересный факт про " .. topic .. ". На русском, 1-2 предложения. Без вступлений."
end
local reply = jarvis.llm({
{ role = "system", content = "Ты любопытный собеседник. Говоришь короткими, цепляющими фактами без воды." },
{ role = "user", content = user_prompt }
}, { max_tokens = 200, temperature = 0.8 })
if not reply or reply == "" then
return jarvis.cmd.error("Сегодня нет интересных фактов.")
end
return jarvis.cmd.ok(reply)

View file

@ -0,0 +1,48 @@
-- "Что было в этот день в истории" — Wikipedia API + LLM сжатие.
local t = jarvis.context.time
-- Wikipedia's "On this day" Featured Content API:
-- https://api.wikimedia.org/feed/v1/wikipedia/ru/onthisday/events/MM/DD
local url = string.format(
"https://api.wikimedia.org/feed/v1/wikipedia/ru/onthisday/events/%02d/%02d",
t.month, t.day
)
local data = jarvis.http.json(url)
if not data or not data.events or #data.events == 0 then
-- fallback: EN
url = string.format(
"https://api.wikimedia.org/feed/v1/wikipedia/en/onthisday/events/%02d/%02d",
t.month, t.day
)
data = jarvis.http.json(url)
end
if not data or not data.events or #data.events == 0 then
return jarvis.cmd.not_found("На этот день в истории ничего интересного.")
end
-- Pick first 5 events to feed into LLM for summarisation
local sample = {}
for i = 1, math.min(5, #data.events) do
local ev = data.events[i]
local year = ev.year or "?"
local text = ev.text or ""
if text ~= "" then
table.insert(sample, year .. ": " .. text)
end
end
local context = table.concat(sample, "\n")
local reply = jarvis.llm({
{ role = "system", content = "Ты — историк. Из списка событий выбери 2-3 самых интересных для русскоязычного слушателя, расскажи коротко на русском. Без преамбул." },
{ role = "user", content = "Сегодня " .. t.day .. " число месяца " .. t.month .. ". События в этот день:\n\n" .. context }
}, { max_tokens = 350, temperature = 0.4 })
if not reply or reply == "" then
-- Fallback: just speak first event
return jarvis.cmd.ok(sample[1] or "Не нашёл интересного.")
end
return jarvis.cmd.ok(reply)

View file

@ -0,0 +1,18 @@
# Mailto: open default mail client to compose a new message.
[[commands]]
id = "mailto.compose"
type = "lua"
script = "compose.lua"
sandbox = "full"
timeout = 5000
[commands.phrases]
ru = [
"напиши письмо",
"новое письмо",
"открой почту",
"составить письмо",
]
en = ["compose email", "new email", "open mail"]
ua = ["напиши лист", "новий лист"]

View file

@ -0,0 +1,32 @@
-- "Напиши письмо имя_контакта" → opens mailto: URI in default handler.
-- For now just opens a blank compose; could be extended to pull recipient
-- from memory ("маме", "коллеге") via lookups.
local phrase = (jarvis.context.phrase or ""):lower()
local target = jarvis.text.strip_trigger(phrase, {
"напиши письмо",
"новое письмо",
"открой почту",
"составить письмо",
"compose email",
"new email",
"open mail",
})
target = target:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
local recipient = ""
if target ~= "" then
-- Try memory lookup — "напиши письмо маме" → memory.recall("email_маме")
local key = "email_" .. target
local addr = jarvis.memory.recall(key)
if addr and addr ~= "" then
recipient = addr
end
end
local uri = recipient ~= "" and ("mailto:" .. recipient) or "mailto:"
jarvis.system.open(uri)
if recipient ~= "" then
return jarvis.cmd.ok("Открываю письмо для " .. target .. ".")
end
return jarvis.cmd.ok("Открываю почтовый клиент.")