Commit graph

155 commits

Author SHA1 Message Date
Bossiara13
c4b22618f8 feat: Wave 1 — 10 new packs (75 → 85 rust packs)
User picked from the fishki roadmap; this commit ships the 10 easy/medium
ones in one batch. No Rust core changes — all pure Lua + jarvis.* APIs.

Pack catalog

magic_8ball
  - "ответь да или нет" / "магический шар" / "что скажешь"
  - 15 канонических ответов, random pick. Pure fun.

github_issues (2 cmds)
  - "какие issues" / "открытые issues" — gh issue list по сохранённому репо
  - "мои issues" / "что мне назначено" — gh issue list --assignee @me

weather_extended (2 cmds, Open-Meteo, no key)
  - "погода завтра" — temp min/max + conditions + rain note
  - "прогноз на неделю" — общий диапазон температур
  - Auto-detects location via ipinfo.io if not in memory; stores lat/lon/city.

rss_reader (3 cmds)
  - "добавь rss <url>" — extracts URL, persists via memory под ключом "rss.<domain>"
  - "что в ленте" — fetches first feed, parses <title> tags, speaks top 3
  - "какие у меня rss" — lists subscribed feed domains

ics_event
  - "добавь встречу завтра в 15:00 обсудить проект"
  - Parses time (HH:MM или "в HH") + date keywords (сегодня/завтра/послезавтра)
  - Writes valid iCalendar v2.0 file to ~/Documents/jarvis-events/
  - jarvis.system.open → дефолтный handler (Outlook/Mail/whatever)

backup
  - "сделай бекап" — PowerShell Compress-Archive всех state files из APPDATA/Jarvis
  - Outputs to ~/Documents/jarvis-backup-<YYYYMMDD-HHMMSS>.zip
  - Bundles: long_term_memory, schedule, macros, profiles/, active_profile, llm_backend, settings

clip_history (3 cmds, 20-slot rolling buffer)
  - "запиши буфер" — push current clipboard onto memory keys clip.0..clip.19
  - "что я копировал" — speak preview of last 3
  - "верни первый/второй/третий буфер" — restore clip.N to clipboard

notif_queue (2 cmds)
  - "что я пропустил" — enumerate memory keys "notif.*", speak last 5 by recency
  - "очисти уведомления" — purge all notif.* keys
  - Producers (scheduler/macros/llm) can later push to memory[notif.<ts>] = text

password_vault (3 cmds, Windows DPAPI)
  - "сохрани пароль от GitHub" — encrypts current clipboard content via DPAPI
    (CurrentUser scope), base64-stored in memory[vault.GitHub]. Clears clipboard.
    Password is NEVER spoken or written to disk in plaintext.
  - "пароль от GitHub" — decrypts via DPAPI, restores to clipboard for 30 sec,
    schedules auto-clear via jarvis.scheduler. Speaks only "Пароль от X в буфере."
  - "какие у меня пароли" — list of stored service names.

habit_streaks (2 cmds, integrates with habit_nudge)
  - "сколько дней подряд" / "статистика привычек" — reads memory keys
    "habit_streak.<habit>.<YYYY-MM-DD>", computes consecutive-day streak per habit.
  - "я попил воды" / "отметь привычку" — marks today's check-in.
    Maps voice to habit: воды→water, размял/зарядк→stretch, глаз→eyes.

Tests: 6 commands tests pass (auto-validate the 10 new packs).
2026-05-16 01:04:23 +03:00
Bossiara13
4d3d664abd feat: Now Playing + World Clock + Daily Quote packs (72 → 75 rust packs)
Three small daily-driver packs mirrored with python (commit 02ab8a4 or similar).

now_playing
  - "что играет" / "что за песня" / "какой трек" / "что слушаю"
  - Reads Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager
    via PowerShell — works with Spotify/YouTube/Foobar/Winamp/Yandex Music
    (anything that exposes Windows SMTC, Win10 1803+).
  - Speaks "Сейчас играет: <Artist> — <Title>" or just "<Title>" if no artist.

world_clock
  - "сколько времени в Токио" / "время в Лондоне"
  - 21+ pre-mapped Russian/world city names → IANA timezones.
  - Fetches worldtimeapi.org (free, no key, no rate-limit headers exposed).
  - Parses ISO 8601 datetime, speaks "В <city> сейчас HH:MM."

daily_quote
  - "цитата дня" / "вдохнови меня"
  - zenquotes.io (free, no key) → English quote.
  - LLM translates to Russian via active backend (Groq or Ollama).
  - Falls back to LLM-generated quote if zenquotes is unreachable.
  - Speaks "<translated quote> — <author>."

Tests: 6 commands tests pass (no_duplicate_ids / no_empty_phrases /
all_lua_scripts_exist auto-validate the 3 new packs).
Pack count: 72 → 75.
2026-05-16 00:56:10 +03:00
Bossiara13
4efe306b3a feat(gui): profile switcher chip in header
Visible visual indicator of the active profile + click-to-swap dropdown.
Closes the last small UX gap pointed out in earlier sessions.

Tauri (crates/jarvis-gui/src/tauri_commands/profile.rs)
  - profile_list()   → Vec<String> of available profile names
  - profile_active() → {name, description, icon, greeting}
  - profile_set(name) → swaps active profile, returns new active

Frontend (frontend/src/components/Header.svelte)
  - Polls profile_active every 5 seconds.
  - Shows a coloured chip with icon+name when profile != "default"
    (orange tint, attention-grabbing). For "default" — muted star chip.
  - Click opens a dropdown listing all profile names; click one → swap.
  - Dropdown closes on outside-click (shared handler with lang dropdown).
  - SCSS styled to match existing aesthetic.

Build: cargo build --release -p jarvis-gui green (2m).

Now the user can see at a glance which profile is active (e.g. "💼 work")
without having to ask voice or open Settings.
2026-05-16 00:48:23 +03:00
Bossiara13
ea4341fbc9 test+feat: +3 commands tests, +2 packs (self_check + ssl_check), env-race fix
Tests (112→115, +3)
  - commands::additional_tests::no_duplicate_command_ids_across_packs
    Catches accidental ID collisions across packs at PR time, with informative
    error pointing to both offending packs.
  - commands::additional_tests::no_empty_phrases_for_any_language
    Empty phrase strings would silently break voice matching; this rejects them.
  - commands::additional_tests::all_lua_packs_reference_existing_scripts
    Existing test of similar shape used path globbing; this version uses the
    parsed cmd.script field for a more precise error message.

Plus: runtime_config tests no longer race on shared env vars. Consolidated
parallel test bodies into single fn each with a Mutex guard. 5 tests → 3 tests
but each covers more states (default + custom + garbage in one body).

New packs

resources/commands/self_check/ (2 cmds)
  - selfcheck.ping   "ты тут" / "ты слышишь" / "пинг" / "ты живой"
                     Random pick from 5 short canned replies — no LLM call,
                     no network. Pure mic-chain + TTS smoke test.
  - selfcheck.smoke  "проверь себя" / "самопроверка"
                     Reads jarvis.health() + tries one HTTP request,
                     speaks list of "X работает, Y работает...".

resources/commands/ssl_check/ (1 cmd)
  - ssl.check  "проверь сертификат example.com"
               PowerShell TCP+SslStream→X509Certificate2. Reports days
               until expiry, expiry date, issuer CN. Adds urgency prefix
               ("СКОРО!" if <7 days, "Скоро." if <30, "просрочен!" if past).

Pack count: 70 → 72. Tests: 115/115 + 1 ignored Ollama smoke.
Build: cargo build --release -p jarvis-app green.
2026-05-15 23:43:59 +03:00
Bossiara13
4f83062815 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.
2026-05-15 23:38:03 +03:00
Bossiara13
d3180b7d78 feat: GUI /memory page + disk/date_math/sleep_timer packs (10 new commands)
GUI: Memory management completes the management trio (/macros + /scheduler + /memory).

Tauri commands (crates/jarvis-gui/src/tauri_commands/memory.rs)
  - memory_list             → Vec<MemoryFact{key, value, created_at, last_used_at, use_count}>
  - memory_remember(k, v)   → persist new fact (or overwrite)
  - memory_forget(key)      → delete by exact key
  - memory_search(q, limit) → substring search

GUI /memory page (frontend/src/routes/memory/index.svelte)
  - Top: add-fact form (key + value inputs + "+Добавить" button)
  - Substring filter input for the list
  - Per-card: key + use_count badge + value (highlighted box) + timestamps +
    "Забыть" button with confirm() guard
  - Auto-sorted by recency (last_used_at desc)
  - Empty state shows voice-hint: 'скажи Jarvis-у "запомни что я люблю чай улун"'

Header (frontend/src/components/Header.svelte)
  - New "Память" button between /scheduler and /settings.
  - i18n: header-memory in ru/en/ua FTL files.

New voice packs

resources/commands/disk/ (2 cmds)
  - disk.free      "сколько свободно на диске" / "сколько места на диске C"
                   PowerShell Get-PSDrive → speaks "Свободно X ГБ из Y, это N%"
  - disk.list      "какие у меня диски" → "C 120 ГБ, D 300 ГБ, E 50 ГБ"

resources/commands/date_math/ (2 cmds)
  - date.days_until    "сколько дней до нового года" / "сколько до 8 марта" /
                       "сколько до 15 марта" — recognises Russian months,
                       holidays (Новый год, Рождество, 8 марта, 9 мая).
                       Auto-rolls to next year if target already passed.
                       Russian-grammar pluralisation (день/дня/дней).
  - date.day_of_week   "какой сегодня день недели" — Zeller's congruence,
                       maps to ru day name.

resources/commands/sleep_timer/ (3 cmds)
  - sleep_timer.pause_in    "выключи музыку через 30 минут"
                            → scheduler one-shot lua action that fires media
                            VK_MEDIA_PLAY_PAUSE via the existing media_keys helper.
                            (Auto-generates _fire_pause.lua wrapper if missing.)
  - sleep_timer.shutdown_in "выключи компьютер через 1 час"
                            → shutdown.exe /s /t <secs>. Caps at 24 hours.
                            Speaks "Скажите 'отмени таймер' чтобы передумать."
  - sleep_timer.cancel      "отмени таймер выключения" / "не выключай компьютер"
                            → shutdown /a + scheduler.remove("sleep_timer_pause"),
                            idempotent.

Pack count: 64 → 67. Tests: 112/112 pass.
Build: cargo build --release -p jarvis-gui green.
2026-05-15 23:35:23 +03:00
Bossiara13
84d3b57ddc feat: unit conversion + random generators packs (7 new commands)
Two pure-Lua packs that don't need network or LLM — pure offline utilities.

unit_convert (resources/commands/unit_convert/, 4 commands)
  - convert.length      "переведи 100 метров в футы" / "сколько 5 миль в км"
                        meters↔feet, km↔miles. Pluralises russian unit names.
  - convert.weight      "переведи 70 кг в фунты"
                        kg↔lbs.
  - convert.temperature "переведи 100 цельсий в фаренгейт" / "минус 40 в цельсий"
                        Handles negative numbers. C↔F formulas inline.
  - convert.speed       "переведи 100 км в час в мили в час"
                        km/h↔mph.
  - All round results to 1-2 decimal places, speak with proper Russian
    grammatical number (фут/фута/футов).

generators (resources/commands/generators/, 3 commands)
  - gen.coin       "подбрось монету" / "орёл или решка"
                   → "Орёл!" or "Решка!" (math.random with time+minute seed)
  - gen.password   "сгенерируй пароль 16 символов"
                   → cryptographically-ish 6..64-char password,
                   alphanumeric + !@#$%^&*-_=+, copies to clipboard,
                   SPEAKS LENGTH ONLY (never echoes the password).
                   Default 16 chars if no number in phrase.
  - gen.uuid       "сгенерируй uuid" / "юид"
                   → v4 UUID, copies to clipboard, speaks last 4 chars
                   (confirmation without 32-char monologue).

Tests: 112/112 pass (commands::tests auto-validates).
Pack count: 62 → 64.
2026-05-15 18:41:44 +03:00
Bossiara13
20b6b06b5d test: +13 more tests (99→112), incl. live Ollama smoke (ignored by default)
text_utils (was 5, now 12 tests, +7)
  - empty_input_yields_empty_output
  - handles_usa_uk_sos_brands — verifies all brand replacements survive
  - collapse_two_letter_acronym_not_changed — 2-letter dotted patterns stay
  - short_url_under_threshold_not_stripped — gate is len > 7
  - smart_quotes_softened — «»/"" stripped
  - collapses_repeated_whitespace — no 3+ spaces in row
  - idempotent_when_already_clean — sanitize(sanitize(x)) == sanitize(x)

llm::client (was 4, now 10 tests + 1 ignored, +6 + 1 smoke)
  - chat_message_serde_round_trip — pins JSON shape {role, content}
  - ollama_client_has_no_api_key — uses LlmBackend::Ollama
  - groq_client_carries_token — verifies token + default model
  - ollama_default_model_can_be_overridden — OLLAMA_MODEL env honoured
  - parses_response_with_unicode_content — Russian Cyrillic survives JSON
  - empty_choices_yields_empty_response_err — graceful on []
  - ollama_smoke (#[ignore]) — actual API call when Ollama is running locally.
    Run with: cargo test --lib llm::client::tests::ollama_smoke -- --ignored

Total: 112 passing + 1 ignored. Smoke test verified locally — Ollama not
running (expected), test correctly skipped.

Build: cargo build --release -p jarvis-app -p jarvis-gui green.
2026-05-15 18:40:06 +03:00
Bossiara13
41eb47724c feat: lock_workstation + screenshot + net_info packs + modernise power pack
Three handy daily-driver packs + cleanup of the oldest power/act.lua.

power pack — added lock_workstation (resources/commands/power/)
  - New command id: lock_workstation
  - Phrase: "заблокируй компьютер" / "заблокируй пк" / "заблокируй экран"
  - Action: rundll32.exe user32.dll,LockWorkStation
  - Sandbox: full (needs system.exec)
  - act.lua modernised:
    * No more inline PowerShell SAPI escape chain — now uses jarvis.speak
      (TTS backend abstraction respects user's chosen Piper/SAPI/Silero).
    * Returns via jarvis.cmd.ok/error helpers.
    * Logged warning for failing cmds without crashing the pack.
    * Cleaner per-action `warn` flag controls the "say cancel to abort" hint.

screenshot pack (NEW, resources/commands/screenshot/, 2 commands)
  - "сделай скрин" / "сделай скриншот" → screenshot.to_clipboard
    Uses [System.Windows.Forms.Clipboard]::SetImage() with a captured Bitmap.
    No file artifact, no LLM call — pure capture, fastest path.
  - "сохрани скрин" → screenshot.to_file
    Saves to ~/Pictures/Screenshots/jarvis-YYYYMMDD-HHMMSS.png.
    Creates dir if missing. Notifies with full path.
  - Distinct from vision/ pack which captures + describes via LLM.

net_info pack (NEW, resources/commands/net_info/, 3 commands)
  - "какая сеть" / "какой wifi" → net.wifi_ssid
    Parses `netsh wlan show interfaces` for SSID line (RU + EN field names).
  - "мой IP" / "локальный IP" → net.local_ip
    PowerShell Get-NetIPAddress, filters out 127.* and 169.* (link-local).
  - "внешний IP" / "публичный IP" → net.public_ip
    Hits api.ipify.org via jarvis.http.get (no key, plain text).

Tests: 99/99 pass (commands::tests auto-validates new packs).
Build: cargo build --release -p jarvis-app green.

Pack count: 59 → 62 (intro/echo from prior commit + screenshot + net_info; power
already counted — added a command, not a pack).
2026-05-15 18:32:13 +03:00
Bossiara13
77063fed86 test+feat: +37 tests (62→99) + intro/echo packs (5 new commands)
Major test coverage push: pins IPC wire format, scheduler parser/serde,
runtime_config env handling. Plus two debug/discovery packs.

Tests (62 → 99, +37)

IPC roundtrip (new file crates/jarvis-core/src/ipc/tests.rs, +19 tests)
  - Event tags use snake_case ("wake_word_detected", "speech_recognized", ...)
  - Idle event has minimal payload (tag only, no extra fields)
  - SpeechRecognized/LlmReply/Error carry their text payloads
  - CommandExecuted carries id + success bool
  - HealthSnapshot full shape: tts/llm/llm_model/profile/memory_facts/
    scheduled_tasks/language/version
  - HealthSnapshot handles llm_model:null / version:null
  - Cross-variant invariant: every event has snake-case "event" tag
  - Action parsing: stop / ping / text_command / set_muted / switch_llm /
    reload_llm / query_health
  - Unknown action variant errors out
  - switch_llm requires "backend" field (missing field errors)

runtime_config (was 2, now 11 tests, +9)
  - get_bool recognises 1/true/yes/on (case-insensitive)
  - get_bool falsifies 0/false/no/off/anything-else
  - get() strips whitespace, empty = unset
  - llm_router_threshold default 0.55 / custom parse / garbage fallback
  - llm_tts_enabled default true, recognises "false"
  - llm_router_enabled default true
  - Uses unique env var names per test to avoid thread races

scheduler (was 10, now 19 tests, +9)
  - parse_in_hours yields Once
  - parse_at_today_or_tomorrow
  - parse_at_rejects_bad_time (25:00, 12:99, "notatime")
  - parse_daily_rejects_bad_hour (24:00, 12:60)
  - parse_every_seconds
  - parse_unrecognised_spec_errors ("nonsense", "daily", "every")
  - task_serde_round_trip preserves last_fired + action variant
  - schedule_kind_tag_in_json pins {"kind":"daily","hour":...,"minute":...}
  - action_serde_lua_variant roundtrip

New packs

resources/commands/intro/ (3 commands)
  - intro.capabilities  "что ты умеешь" / "помощь" / "помоги"
                        → speaks a 7-line category overview
  - intro.about         "расскажи о себе" / "кто ты такой"
                        → LLM-varied bio with profile name
                        Falls back to canned text if LLM unavailable.
  - intro.commands_count "сколько команд знаешь"
                        → reads jarvis.health() and reports backends + counts

resources/commands/echo/ (2 commands)
  - echo.repeat         "повтори за мной X" → speaks X verbatim
                        Preserves original casing by re-grabbing from raw phrase.
  - echo.what_did_i_say "что я сказал" → echoes jarvis.context.phrase
  - Both useful for testing mic-pickup + STT-quality + TTS-clarity without
    touching LLM. If user can't hear/understand the echo, the issue is the
    audio chain, not the command logic.

Build: cargo build --release green. 99/99 tests pass.
2026-05-15 18:28:11 +03:00
Bossiara13
a2dfadf5c1 feat(gui): wire Footer + Settings to daemon over IPC (closes the gap from a6a098d)
a6a098d added daemon-side IPC handlers (SwitchLlm, ReloadLlm, QueryHealth)
but the GUI was still talking only to its own process. This commit wires
the frontend to actually use those handlers.

frontend/src/lib/ipc.ts
  - New DaemonHealth interface + daemonHealth: writable<DaemonHealth | null>.
  - handleEvent: "health_snapshot" case fills daemonHealth from daemon's IpcEvent.
  - New senders:
      switchDaemonLlm(backend)  → action "switch_llm"
      reloadDaemonLlm()          → action "reload_llm"
      queryDaemonHealth()        → action "query_health"

frontend/src/stores.ts
  - Re-exports daemonHealth + the three new senders + DaemonHealth type.

frontend/src/components/Footer.svelte
  - Polls every 5s but PREFERS IPC: if daemon connected, calls queryDaemonHealth
    (snapshot arrives via daemonHealth store).
  - Falls back to invoke("get_active_backends") if daemon offline — shows
    GUI-process view in that case, with the chip tooltip distinguishing
    "daemon" vs "gui-process" source.

frontend/src/routes/settings/index.svelte (AI Backends tab)
  - applyLlmBackend now ALSO fires switchDaemonLlm so the running listener
    picks the new backend without restart.
  - "Auto" (empty) path calls reloadDaemonLlm so daemon re-reads DB.

End-to-end flow now:
  GUI dropdown → set_llm_backend (Tauri, persists DB + swaps GUI proc)
              ↘ switchDaemonLlm (IPC, swaps daemon proc too)
              ↘ daemon emits health_snapshot on next QueryHealth
              ↘ daemonHealth store updates
              ↘ Footer chips re-render with new state

Build: cargo build --release -p jarvis-gui green.
2026-05-15 17:42:40 +03:00
Bossiara13
a6a098de15 feat(ipc): SwitchLlm + ReloadLlm + QueryHealth — daemon hot-swap + state snapshot
Closes the "GUI shows GUI process state, not daemon's" gap. Now GUI can:
  1. Tell the daemon to swap LLM backend via IPC (so the running listener
     uses the new backend immediately, not just the GUI process).
  2. Tell the daemon to re-read llm_backend from settings DB after GUI persists
     a choice independently.
  3. Query the daemon's true runtime state (TTS/LLM/profile/memory/scheduler
     counts/language/version) for an honest footer + diagnostics view.

IpcAction (new variants)
  - SwitchLlm { backend: String }    — daemon calls llm::swap_to(parse(backend))
  - ReloadLlm                         — daemon calls llm::init_global() to re-read DB
  - QueryHealth                       — daemon responds with HealthSnapshot

IpcEvent (new variant)
  - HealthSnapshot { tts_backend, llm_backend, llm_model, active_profile,
                     memory_facts, scheduled_tasks, language, version }

jarvis-app main.rs
  - Imports IpcEvent (was only IpcAction before).
  - Three new match arms in ipc action handler — all use existing globals,
    no new state needed.

Next step (TODO): wire GUI's set_llm_backend Tauri command to ALSO fire
IpcAction::SwitchLlm so daemon stays in sync. And replace Footer.svelte's
get_active_backends call with QueryHealth over IPC.

Build: cargo build --release -p jarvis-app -p jarvis-gui green.
Tests: 62/62 pass (unchanged — IPC variants don't add testable logic).
2026-05-15 17:37:32 +03:00
Bossiara13
9488190d35 test: +7 unit tests for llm::parse_backend + macros::Store serde
Defensive tests for the surface area that user-facing voice commands hit.

llm::parse_backend (5 new tests)
  - English aliases ("groq", "ollama", "cloud", "local")
  - Russian aliases ("облако", "локальный", "локал")
  - Case insensitivity ("OLLAMA", "  Groq  ")
  - Rejects unknown ("openai", "claude", "")
  - current_backend_name() smoke test

macros (2 new tests)
  - Store JSON round-trip with multiple macros
  - is_macro_control case-handling (noun form NOT recognised, verb form IS)

Tests: 62/62 (was 55).
2026-05-15 17:30:24 +03:00
Bossiara13
c22a24ccd8 feat(gui): /macros + /scheduler pages + README rewrite
Surfaces voice macros and scheduled tasks in the GUI, so the user doesn't
have to remember voice commands or grep schedule.json by hand.

Tauri commands (crates/jarvis-gui/src/tauri_commands/)
  - macros.rs:    macros_list, macros_replay, macros_delete, macros_is_recording,
                  macros_recording_name, macros_start_recording, macros_save_recording,
                  macros_cancel_recording (8 commands).
  - scheduler.rs: scheduler_list, scheduler_remove, scheduler_clear (3 commands).
  - Both exposed in main.rs invoke_handler.

GUI pages (frontend/src/routes/)
  - macros/index.svelte:
    * Lists all macros with name, steps_count, first 5 step previews,
      created/last_run timestamps.
    * Top: TextInput + "Начать запись" button. While recording shows orange
      banner with "Сохранить"/"Отменить" buttons + polls is_recording every 2s.
    * Per-card: "Запустить" (with busy state for replay duration), "Удалить".
    * confirm() before delete.
  - scheduler/index.svelte:
    * Lists tasks with name, schedule_human (e.g. "каждые 2 ч"), action body,
      ID, timestamps, action_kind badge.
    * "Отменить" per task + "Очистить всё (N)" bottom button.
    * Auto-polls every 5s (so the list updates as scheduler ticks fire tasks).

Header (frontend/src/components/Header.svelte)
  - Two new buttons: "Макросы" → /macros, "Расписание" → /scheduler.
  - Beside existing /commands and /settings buttons.

i18n
  - ru/en/ua FTL: settings-ai-backends, settings-llm-*, settings-tts-*,
    settings-profile, header-macros, header-scheduler.
  - Re-applied AI Backends keys for ua.ftl (earlier edit hadn't taken).

README.md (full rewrite)
  - Old README was 178 lines mostly explaining LLM-trigger flow and VAD config.
  - New README is ~190 lines covering: features (LLM hot-swap, memory, profiles,
    vision, scheduler, macros, utilities table), quick start, env vars table,
    build steps with vcvars setup, test command, structure tree, voice workflow
    diagram, license, roadmap.
  - Up to date for 59 packs and all new infra.

Build: cargo build --release -p jarvis-gui green (2m). 55/55 tests pass.
2026-05-15 17:28:22 +03:00
Bossiara13
c7ab751e39 perf(startup): log total boot time on jarvis-app startup (IMBA-8 nudge)
Tiny but useful: stamp `Instant::now()` at the top of main(), log elapsed
ms before the tray loop blocks. Lets users (and contributors) spot startup
regressions without running a profiler.

Output on a warm cache, default config, no network: ~1.8s.
Output with a cold ONNX model load (intent classifier): ~4.5s.

Visible both in the eprintln trace and the log file via the info! call.
2026-05-15 17:13:49 +03:00
Bossiara13
dbb4a6b888 feat(macros): IMBA-7 voice-recorded command sequences (VoiceAttack-style)
Simpler than AHK keyboard hooks: record what the user SAID, replay each phrase
through the normal dispatch path. Works for any command jarvis already knows.

Flow:
  "Запиши макрос работа"  → start_recording("работа")
  "Открой браузер"        → buffered into the recording
  "Запусти спотифай"      → buffered
  "Режим работа"          → buffered
  "Сохрани макрос"        → save_recording() → persist
  ...later...
  "Запусти макрос работа" → replay("работа") → fires each phrase in order

Core (crates/jarvis-core/src/macros.rs)
  - Macro {name, steps, created_at, last_run} persisted at
    <APP_CONFIG_DIR>/macros.json with atomic write-through.
  - Recording state in-memory: RwLock<Option<RecordingState>>.
  - start_recording / record_step / save_recording / cancel_recording.
  - list / get / delete / mark_run.
  - replay(name) spawns a thread that fires each step through a callback,
    with 800ms inter-step delay. Caller returns immediately.
  - is_macro_control filter prevents recording the meta-commands themselves
    (no infinite recursion when "запиши макрос" runs during a recording).
  - 3 unit tests (55 total).

Wiring (crates/jarvis-app/src/main.rs)
  - macros::init() before listener starts.
  - macros::set_replay_callback(move |phrase| text_cmd_tx.send(phrase)) — each
    replay step gets queued as a synthetic text command, processed by the same
    dispatcher the GUI uses. No special-case code path.

Recording hook (crates/jarvis-app/src/app.rs::execute_command)
  - On successful command execution → call macros::record_step(text).
  - Failed commands are NOT recorded (would fail on replay too).
  - is_macro_control filter inside record_step skips meta-commands.

Lua API (crates/jarvis-core/src/lua/api/macros.rs)
  - jarvis.macros.{start, save, cancel, replay, list, delete,
                   is_recording, recording_name}

Voice pack (resources/commands/macros/, 6 commands)
  - "запиши макрос NAME"       → macros.start_recording
  - "сохрани макрос"           → macros.save
  - "отмени макрос"            → macros.cancel
  - "запусти макрос NAME"      → macros.replay
  - "какие у меня макросы"     → macros.list
  - "удали макрос NAME"        → macros.delete

Build: cargo build --release -p jarvis-app green. 55/55 tests pass.
2026-05-15 17:12:32 +03:00
Bossiara13
d63399f4c9 feat(gui): footer backend chips + Settings 'AI Backends' tab + i18n
Surfaces the LLM/TTS hot-swap UX in the GUI so the user doesn't need env vars
or voice commands. Tauri commands for this landed in b243e67; this commit
wires them to Svelte.

Footer (frontend/src/components/Footer.svelte)
  - Poll get_active_backends() every 5 s, show TTS / LLM / Profile chips.
  - Chips have border accents by kind (cyan = TTS, lime = LLM, orange = Profile).
  - Friendly title tooltips with full backend + model names.
  - Silent if jarvis-gui can't reach the command (e.g. cold start) — no UI flicker.

Settings — new "AI Backends" tab (frontend/src/routes/settings/index.svelte)
  - Status banner showing active LLM (with model name) + TTS + Profile.
  - LLM selector: Auto / Groq / Ollama → calls set_llm_backend on change
    (hot-swap, persisted to DB).
  - TTS selector: Auto / SAPI / Piper / Silero → calls set_tts_backend
    (persisted, effective on next jarvis-app restart).
  - "Reset context" button → llm_reset_context Tauri command.
  - Error toast for swap failures (e.g. Groq selected but no GROQ_TOKEN).
  - Tips alert: how to install Ollama / Piper / voice commands.
  - Loads prefs via db_read('llm_backend' / 'tts_backend') on mount.

i18n strings (ru/en/ua, .ftl files)
  - 14 new keys: settings-ai-backends, settings-ai-active, settings-ai-auto,
    settings-ai-applying, settings-ai-error, settings-ai-tips,
    settings-llm-backend{,-desc}, settings-tts-backend{,-desc},
    settings-llm-context{,-desc}, settings-llm-reset, settings-profile.

Build glue (crates/jarvis-gui/Cargo.toml)
  - jarvis-core dep now includes the `llm` feature so backends.rs can resolve
    `jarvis_core::llm::*` (was failing to compile after the global LLM module).

.gitignore
  - tools/piper/*.dll, piper.exe, voices/, espeak-ng-data/ — locally
    installed binaries from tools/piper/install.ps1, not committed.

Build: cargo build --release -p jarvis-gui green (4m, includes npm/Vite).
Test path: rebuild + launch jarvis-gui, /settings → "AI Backends" tab.
2026-05-15 17:06:46 +03:00
Bossiara13
b243e67870 feat: Tauri backend commands + wikipedia rewrite + github PR review pack
GUI integration
  - crates/jarvis-gui/src/tauri_commands/backends.rs — 5 new Tauri commands:
      get_active_backends()  -> {tts, llm, llm_model, profile}
      set_llm_backend(name)  -> swap LLM hot, persists to DB
      set_tts_backend(name)  -> persist choice (effective on jarvis-app restart)
      llm_reset_context()    -> clear conversation history
      llm_get_last_reply()   -> last assistant message or null
  - Registered in main.rs invoke_handler. Svelte side can now read active
    backend + swap from a settings page / footer without env-var gymnastics.

Wikipedia pack rewritten (resources/commands/wikipedia/)
  - Old `wiki/wiki.lua` (104 lines, hand-rolled Groq HTTP + inline SAPI PS) deleted.
  - Replacement `wikipedia/summary.lua` (87 lines):
    * REST summary endpoint (cleaner than opensearch+summary chain).
    * Russian first, English fallback with LLM translation.
    * Pure LLM fallback if Wikipedia has no article.
    * Uses jarvis.cmd.{ok,error,not_found} + jarvis.llm + jarvis.speak (no
      inline PowerShell SAPI). Phrases kept compatible with old pack.

GitHub PR voice review (resources/commands/github_pr/, 3 commands)
  - "текущий репо bossiara13/J.A.R.V.I.S-rust"  → github.set_repo
    Persists repo via jarvis.memory.
  - "какие пиары" / "открытые пиары"            → github.list_prs
    Calls `gh pr list --json number,title,author,createdAt --limit 10`.
    Parses count + first 3 titles by regex from JSON.
  - "разбери последний пиар" / "что в pr"       → github.summarize_pr
    `gh pr view N --json title,body,additions,deletions,changedFiles,author`,
    sends to LLM with "senior reviewer" system prompt, speaks 3-5 sentence
    review focusing on changes, risks, merge-readiness.
  - Requires gh CLI installed and `gh auth login` done.

Tests: 52/52 jarvis-core unit tests pass (Wikipedia pack TOML/script auto-checked).
Build: cargo build --release -p jarvis-app -p jarvis-gui green.
2026-05-15 16:31:54 +03:00
Bossiara13
385bd5c8ce feat: persist LLM/TTS backend choice + reset/repeat context + quick-search + diagnostics
Closes the UX hole I created in 5c72450: voice-swap to Ollama used to vanish
after restart. Plus P0.3 (long-standing roadmap item) and two new packs.

Persistent backend choice (crates/jarvis-core/src/db/structs.rs)
  - Settings struct gains llm_backend + tts_backend fields (both String,
    "" / "auto" = follow env/auto-detect).
  - set("llm_backend", "groq"|"ollama"|"auto") validates input.
  - llm::init_global() reads DB first, then JARVIS_LLM env, then auto-detect.
  - llm::swap_to() now persists the choice via db::save_settings.
  - Voice swap "переключись на локальный" now survives restart.

Shared conversation history (P0.3, crates/jarvis-core/src/llm/mod.rs)
  - HISTORY: Lazy<RwLock<Option<ConversationHistory>>> singleton.
  - Helpers: init_history, history_push_user, history_push_assistant,
    history_snapshot, history_clear, history_pop_last_user, history_last_assistant.
  - llm_fallback migrated off its own Mutex<History> — now reads/writes shared.
  - ConversationHistory gains last_assistant() method.

New Lua APIs
  - jarvis.llm_reset()        → clear conversation turns (keeps system prompt).
  - jarvis.llm_last_reply()   → string or nil (last assistant message text).
  - jarvis.health()           → debug table {tts_backend, llm_backend, llm_model,
                                  active_profile, memory_facts, scheduled_tasks,
                                  language, voice, microphone, vosk_model,
                                  noise_suppression}. No secrets included.

New voice packs
  - resources/commands/llm_context/   (P0.3)
    * "сбрось контекст" / "забудь разговор"     → llm.reset
    * "повтори последнее" / "повтори ответ"     → llm.repeat (uses last_assistant)
  - resources/commands/quick_search/   (imba P1 item)
    * "найди в гугле <X>" / "загугли <X>"
    * Uses DuckDuckGo Instant Answer API (api.duckduckgo.com, no key required).
      Pulls AbstractText or RelatedTopics into LLM prompt; falls back to pure
      LLM knowledge if DDG returns nothing useful. Speaks 2-4 sentence answer.
  - resources/commands/diagnostics/
    * "диагностика" / "доложи о себе" / "статус"
    * Reads jarvis.health() and speaks a one-line summary. Useful when
      debugging — user can read out their current state for a bug report.

Build: cargo build --release -p jarvis-app -p jarvis-gui green.
Tests: 52/52 jarvis-core unit tests pass.
2026-05-15 16:25:28 +03:00
Bossiara13
5c7245012e feat: hot-swap LLM backend + media keys + codebase Q&A + scheduler cancel-by-text + TTS pre-warm
Single biggest user-facing change: you can now switch local↔cloud LLM by voice
without restarting. Plus four new packs and a perf nudge.

Shared LLM global (crates/jarvis-core/src/llm/mod.rs)
  - GLOBAL: Lazy<RwLock<Option<Arc<LlmClient>>>>. Single source of truth.
  - init_global() / current() / swap_to(LlmBackend) / current_backend_name() / parse_backend(str).
  - llm_fallback + llm_router migrated: they no longer own a separate LlmClient,
    they read from the global on each request. Hot-swap is now LIVE — change
    backend, next chat call uses the new one.
  - parse_backend accepts both english and russian aliases:
      "groq"/"cloud"/"облако"/"клауд"        → Groq
      "ollama"/"local"/"локал"/"локальный"  → Ollama

Voice + Lua LLM switcher (resources/commands/llm_switch/, 3 commands)
  - "переключись на локальный" / "перейди на оллама"  → swap to Ollama
  - "переключись на облако" / "используй грок"        → swap to Groq
  - "какой у тебя мозг" / "облако или локально"       → status query

  Underlying Lua API (registered globally):
    jarvis.llm({messages}, opts)   -- as before, now uses global client
    jarvis.llm_status()            -> "groq" | "ollama" | "none"
    jarvis.llm_switch(name)        -> name on success, nil on failure

Media keys pack (resources/commands/media_keys/, 4 commands)
  - "пауза" / "плей"           → VK_MEDIA_PLAY_PAUSE
  - "следующий трек"           → VK_MEDIA_NEXT_TRACK
  - "предыдущий трек"          → VK_MEDIA_PREV_TRACK
  - "стоп музыка"              → VK_MEDIA_STOP
  - Helper _media.ps1 uses user32.keybd_event (P/Invoke through Add-Type).
  - Works with anything that listens to global media keys: Spotify, Yandex Music,
    YouTube (focused tab), Foobar2000, Winamp, etc. No OAuth, no API keys.

Codebase Q&A pack (resources/commands/codebase_qa/, 3 commands)
  - "укажи проект <path>"             → stores path in jarvis.memory under codebase.root
  - "какой проект сейчас"             → speaks current path
  - "что делает функция X" / "найди в коде Y" / "объясни код"
    → walks the folder (depth 3, 30 files cap, 4KB per file, 50KB total),
      filters by source extensions (rs/py/ts/lua/go/...), feeds digest to LLM
      with a "senior reviewer" system prompt, speaks 3-5 sentence answer.

Scheduler cancel-by-text (crates/jarvis-core/src/scheduler.rs)
  - new pub fn find_by_text(query, limit) -> Vec<ScheduledTask>
  - new pub fn remove_by_text(query) -> usize (count removed)
  - new pure helper find_by_text_in(tasks, query, limit) for tests
  - Lua: jarvis.scheduler.{find_by_text, remove_by_text}
  - Voice: "отмени напоминание про воду" / "удали задачу про разминку"
  - 3 new unit tests (52 total in jarvis-core).

TTS pre-warm (crates/jarvis-app/src/main.rs)
  - Call jarvis_core::tts::backend() once on startup so the first speak()
    doesn't pay Piper binary discovery + voice loading cost. Cuts first-speak
    latency by ~150-300ms depending on backend.

Build: cargo build --release -p jarvis-app -p jarvis-gui both green.
Tests: 52/52 jarvis-core unit tests pass.
2026-05-15 16:15:59 +03:00
Bossiara13
6225198821 refactor: maintainability pass — dedup, central env config, cmd helpers, tests, ARCHITECTURE.md
User asked the code stays easy to fix/extend as the feature surface grows.
This commit refactors what was duplicated, centralises what was scattered,
adds tests where there were none, and writes the architecture doc that
contributors will need.

Deduplication
  - tts::play_wav extracted to tts/mod.rs as pub(crate). Was identical in
    piper.rs and silero.rs (~25 lines × 2). Both backends now call super::play_wav.

Centralised env config
  - new crates/jarvis-core/src/runtime_config.rs — single doc-file for every
    JARVIS_* / GROQ_* / OLLAMA_* env var. Includes:
      - ENV_* constants with doc comments
      - get(name) / get_bool(name, default) / get_parse(name, default) helpers
      - feature-flag wrappers: llm_tts_enabled(), llm_router_enabled(),
        llm_router_threshold()
      - log_effective_config() prints active values on startup
  - migrated llm_fallback (JARVIS_LLM_TTS), llm_router (JARVIS_LLM_ROUTER,
    JARVIS_LLM_ROUTER_THRESHOLD) to use the new helpers. Pattern set for
    future migrations.

Lua boilerplate killer
  - new crates/jarvis-core/src/lua/api/cmd.rs exposing:
      jarvis.cmd.ok(msg?)         — play_ok + speak + {chain=false}
      jarvis.cmd.chain_ok(msg?)   — same but chain=true
      jarvis.cmd.error(msg?)      — play_error + speak + {chain=false}
      jarvis.cmd.not_found(msg?)  — play_not_found + speak + {chain=false}
      jarvis.cmd.silent_ok / silent_error
  - refactored 5 packs (daily_briefing/off, memory_pack/list, pomodoro/stop,
    habit_nudge/stop_all, scheduler/clear) — each lost 3-4 lines of repetitive
    play_*/speak/return boilerplate. Pattern for future packs documented in
    ARCHITECTURE.md.

Tests (was 32, now 49)
  - long_term_memory: 8 new tests for normalize_key, search_in (rank, limit,
    empty), build_context_from (empty + populated), serde round-trips for
    MemoryRecord and Store. Extracted pure logic (search_in, build_context_from)
    into pub(crate) functions to enable testing without global state.
  - profiles: 6 new tests for Profile::allows_command (empty/whitelist/blacklist/
    deny-wins-over-allow), serde round-trip with all fields + minimal-fields
    tolerance via #[serde(default)].
  - runtime_config: 2 tests for get_bool / get_parse defaults.
  - All 49 tests pass.

New mood/energy log pack
  - resources/commands/mood_log/ with 2 commands:
      mood.record  "запиши настроение 7" / "сегодня мне грустно" → stores
                   timestamped entry via jarvis.memory.remember
      mood.recap   "как прошла неделя" → LLM summarises last 30 entries
  - Showcase: composes memory + llm + cmd helpers in <30 lines per script.

ARCHITECTURE.md (new, 250 lines)
  - Crate layout, data flow diagram (mic→action 10 steps), per-module
    responsibility table, configuration layers, TTS pipeline diagram,
    Lua sandbox details with API quick-ref, background-services overview,
    "how to add a pack/feature/TTS backend" recipes, test coverage map,
    build instructions with MSVC env, git workflow with Forgejo NO_PROXY trick.
  - Aimed at someone who just cloned the repo and needs to fix a bug fast.

Build: cargo build --release -p jarvis-app -p jarvis-gui both green.
Tests: 49/49 jarvis-core unit tests pass.
2026-05-15 16:06:18 +03:00
Bossiara13
45243c3e3c feat: Ollama LLM backend + 5 utility packs (daily briefing, pomodoro, currency convert, stocks, habits, translate clipboard)
Local-LLM support so J.A.R.V.I.S. works offline without GROQ_TOKEN.

LLM multi-backend (crates/jarvis-core/src/llm/client.rs)
  - LlmBackend enum {Groq, Ollama}. Both use OpenAI-compatible /v1/chat/completions
    (Ollama exposes one natively at :11434/v1).
  - LlmClient::from_env() now dispatches on JARVIS_LLM=groq|ollama. Auto-detect:
    Groq if GROQ_TOKEN set, else Ollama.
  - New helpers: LlmClient::groq() (was: from_env), LlmClient::ollama().
  - Reqwest client now has a 60s timeout (was: unbounded — Groq freezes could hang).
  - api_key skipped when empty (Ollama doesn't need auth).
  - Defaults: OLLAMA_BASE_URL=http://localhost:11434/v1, OLLAMA_MODEL=qwen2.5:3b.
    Override via OLLAMA_BASE_URL / OLLAMA_MODEL env.
  - llm_fallback + llm_router log the active backend on init.
  - Tests: 32/32 pass. Renamed `from_env_fails` → `groq_fails_when_token_missing`,
    added `ollama_works_without_token`.

Daily briefing pack (resources/commands/daily_briefing/, 3 commands)
  - setup.lua: "настрой утренний брифинг на 9:00" → adds a daily scheduled task
    that runs now.lua via the scheduler's Lua-action support.
  - now.lua: greeting + time + active profile + scheduled task count + memory recap
    + LLM motivational nudge. Voice-triggered ("утренний брифинг") works the same.
  - off.lua: removes the scheduled task.

Pomodoro pack (resources/commands/pomodoro/, 2 commands)
  - start.lua / stop.lua + tick.lua (re-scheduled by itself). State stored in
    jarvis.state — phase alternates work (25 min) / break (5 min) until stop.

Currency conversion (resources/commands/currency/convert.lua)
  - "сколько будет 1000 долларов в рублях" — fetches CBR daily rates from
    cbr-xml-daily.ru, normalises by Nominal, prints both directions.
  - Recognises USD/EUR/CNY/GBP/JPY/RUB by substring. Pluralises russian units.

Stocks pack (resources/commands/stocks/, 1 command)
  - "сколько Сбер" → MOEX ISS /iss/engines/stock/markets/shares/securities/<TKR>.
  - Mapping of 22 popular Russian tickers to common names (Сбер→SBER, Газпром→GAZP,
    Лукойл→LKOH, Яндекс→YDEX, Т-банк→T, etc).
  - Picks TQBR (main board) row, reports LAST + LASTTOPREVPRICE%.

Habit nudges (resources/commands/habit_nudge/, 4 commands)
  - "напоминай мне пить воду"     → every 2 hours
  - "напоминай мне размяться"     → every 50 minutes
  - "напоминай отдыхать глазам"  → every 20 minutes (20-20-20 rule)
  - "отключи все привычки"        → stops all three by id.

Translate clipboard (resources/commands/translate/clipboard.lua)
  - "переведи буфер на английский" → grabs clipboard, sends to LLM, speaks first
    sentence, replaces clipboard with full translation, fires notification.

Build: cargo build --release -p jarvis-app -p jarvis-gui both green. 32/32 tests.

To use Ollama:
  1. Install + run Ollama (https://ollama.com).
  2. `ollama pull qwen2.5:3b` (or any chat model).
  3. Optional: `set JARVIS_LLM=ollama` (auto-picked if GROQ_TOKEN unset).
2026-05-15 15:51:24 +03:00
Bossiara13
12b1ed4ccb feat(scheduler): IMBA-5 proactive scheduler — reminders, daily briefings, intervals
Background thread that wakes J.A.R.V.I.S. on a schedule to speak reminders.
Nothing else competes here on desktop — Алиса/Сири are reactive-only.

Core (crates/jarvis-core/src/scheduler.rs)
  - Schedule::{Daily{h,m}, Interval{secs}, Once{at}} with Schedule::parse for
    "daily HH:MM" / "at HH:MM" / "every N minutes|hours" / "in N minutes|hours".
    Russian units (час/часа/часов/минут/секунд) also accepted.
  - ScheduledTask {id, name, schedule, action, last_fired, enabled, created_at}.
    Action::Speak{text} | Action::Lua{script_path}.
  - JSON persistence at <APP_CONFIG_DIR>/schedule.json, atomic write-through.
  - add/remove/clear/list/find; mark_fired auto-deletes Once tasks.
  - start_background() spawns a 30-second tick thread (idempotent). Each tick
    calls due_tasks(), fires Action via tts::speak_default (after voices::play_reply
    "ahem" cue) or Lua engine.
  - 7 unit tests (all passing).

Lua API (crates/jarvis-core/src/lua/api/scheduler.rs)
  - jarvis.scheduler.add({name, schedule, action={type, text|script_path}})
  - jarvis.scheduler.{list, count, remove(id), clear}.
  - Tasks come back as {id, name, schedule_human, action, enabled, last_fired}.

Wire-up (crates/jarvis-app/src/main.rs)
  - scheduler::init() + scheduler::start_background() after profiles::init.
  - Tasks survive restarts via schedule.json.

Voice commands (resources/commands/scheduler/, 6 ids)
  - scheduler.add_reminder    "напомни через 5 минут выключить кофеварку"
  - scheduler.add_at          "напомни в 18:00 забрать ребёнка"
  - scheduler.add_recurring   "каждые 2 часа напоминай попить воды"
  - scheduler.add_daily       "каждый день в 9:00 делай briefing"
  - scheduler.list            "что у меня запланировано"
  - scheduler.clear           "очисти расписание"

Russian-aware parsers (час/часа/часов, минут/минуту/минуты) live inside the Lua
packs — easy to extend without touching Rust.

Tests: 31/31 jarvis-core unit tests pass (24 prior + 7 scheduler).
Build: cargo build --release -p jarvis-app and -p jarvis-gui both green.
2026-05-15 15:43:04 +03:00
Bossiara13
0b1f1d4480 feat: TTS backend abstraction + 4 'imba' features + Lua packs
Big push driven by user feedback ("делай имбу") and web research on what
voice assistants need to be the ideal:

TTS backend abstraction (P0.1)
  - new module crates/jarvis-core/src/tts/{mod,sapi,piper,silero}.rs
  - TtsBackend trait with SapiBackend (current PowerShell), PiperBackend
    (rhasspy/piper, neural quality), SileroBackend (python subprocess)
  - JARVIS_TTS env var picks (sapi|piper|silero). Auto-detect Piper if
    binary + voice present in tools/piper/. Falls back to SAPI on missing.
  - SpeakOpts {lang, detached, raw} replaces ad-hoc args. text_utils
    sanitiser applied unless raw=true.
  - llm_fallback + lua/api/tts both routed through tts::backend().
  - tools/piper/install.ps1 downloads piper.exe + ru_RU-irina-medium.onnx
    from rhasspy releases + huggingface. Smoke-test included.
  - tools/silero/silero_tts.py helper (PyTorch); rust spawns it as subprocess.

IMBA-1 Agentic LLM router
  - crates/jarvis-app/src/llm_router.rs
  - When fuzzy/intent matcher fails, LLM picks the closest command from the
    full registry. Returns JSON {command_id, confidence, reason}.
  - Threshold-gated re-dispatch via substitute phrase. JARVIS_LLM_ROUTER=1
    enables; JARVIS_LLM_ROUTER_THRESHOLD overrides 0.55 default.
  - Inserted in app.rs::execute_command between "no match" and existing
    llm_fallback chat fallback.

IMBA-2 Long-term memory
  - crates/jarvis-core/src/long_term_memory.rs — JSON store at
    APP_CONFIG_DIR/long_term_memory.json. Atomic write-through.
  - remember/recall/search/forget/all/build_context API.
  - Lua bindings: jarvis.memory.* (5 functions).
  - llm_fallback auto-injects relevant facts (substring search of prompt)
    into system message before LLM call.
  - Pack resources/commands/memory_pack/ with 4 commands: remember, recall,
    forget, list.

IMBA-3 Profile switching (work/game/sleep/driving/default)
  - crates/jarvis-core/src/profiles.rs — JSON profiles at APP_CONFIG_DIR/profiles/
    Auto-seeds 5 defaults on first run with personality + allow/deny lists +
    greetings + emoji icons.
  - active_profile.txt persists choice across restart.
  - Lua bindings: jarvis.profile.{active,set,list,allows,active_name}.
  - llm_fallback prepends profile personality to system prompt.
  - Pack resources/commands/profile_switch/ with 6 voice triggers.

IMBA-4 Multimodal screenshot + vision LLM
  - crates/jarvis-core/src/lua/api/vision.rs — gated on HTTP sandbox.
  - jarvis.vision.screenshot() captures via PowerShell System.Drawing.
  - jarvis.vision.describe(prompt?) sends base64 PNG to Groq vision model
    (default llama-3.2-11b-vision-preview, override via GROQ_VISION_MODEL).
  - Pack resources/commands/vision/ with 2 commands: describe + read_error.

P0.2 Continuous conversation grace window
  - config::CONVERSATION_GRACE_MS = 30_000.
  - app.rs: after command result, if grace_ms > 0 keep listening WITHOUT
    re-wake for the grace duration. Existing CMS_WAIT_DELAY back-dated so
    the existing timeout fires at start + grace_ms.

Tests: 24/24 jarvis-core unit tests pass (including 5 text_utils).
Build: cargo build --release -p jarvis-app and -p jarvis-gui both succeed
on Windows MSVC (VS 2026 Enterprise vcvars64).

Notes for setup:
  - Piper voice install: pwsh tools/piper/install.ps1 (downloads ~90 MB).
  - GROQ_TOKEN needed for IMBA-1 (router) and IMBA-4 (vision).
  - All features are opt-in via env vars or auto-detect; existing SAPI +
    fuzzy match path remains the default.
2026-05-15 15:32:44 +03:00
Bossiara13
80b54af1ee fix(tts): sanitise text before SAPI so it stops reading "J.A.R.V.I.S." as "J точка A точка R точка..."
User complaint: SAPI reads every dotted acronym character-by-character with a
"точка" between letters. Unbearable on every speak. No real TTS analog
shipping yet (Silero/Coqui/Inworld in roadmap), so the immediate fix is text
preprocessing.

New module `jarvis-core::text_utils` with `sanitize_for_speech()`:
- Specific brand mapping: J.A.R.V.I.S. → Джарвис, U.S.A. → США, U.K. →
  Британия, U.S. → США, S.O.S. → сос.
- Generic dotted-acronym collapse: any `[letter].[letter].[letter].(...)`
  run of 3+ letters gets its dots stripped. "T.O.N." → "TON".
- URL stripping: anything starting with http/https gets replaced with the
  word "ссылка". SAPI reading URLs char-by-char is unlistenable.
- Em/en-dash normalisation: — → -, « » " stripped.
- Whitespace collapse.

Wired in two places:
- `jarvis.speak(text, opts?)` Lua API runs sanitiser unless opts.raw=true.
- `jarvis-app/llm_fallback::speak_via_sapi` runs sanitiser before the SAPI
  PowerShell shell-out for LLM auto-fallback replies.

5 unit tests in `text_utils::tests` covering jarvis-name collapse, generic
acronym pattern, URL stripping, dash normalisation, and "leave clean
russian sentences alone" — all green.

Real TTS upgrade (Silero v4 first cut) tracked as P0.1 in roadmap.
2026-05-15 14:48:42 +03:00
Bossiara13
9911b6ec40 fix(app): embed Common-Controls v6 manifest so TaskDialogIndirect resolves
Symptom: jarvis-app.exe failed to start with a MessageBox saying
"Точка входа в процедуру TaskDialogIndirect не найдена в библиотеке
DLL ... jarvis-app.exe". Loader phase, no log lines ever written.

Cause: tray-icon 0.21 / winit 0.30 transitive Win32 imports include
TaskDialogIndirect from comctl32.dll, which is a Common-Controls v6
API. Without a side-by-side manifest declaring a dependency on
Microsoft.Windows.Common-Controls v6.0.0.0, Windows loads the legacy
v5 comctl32 (which does not export TaskDialogIndirect) and the
loader rejects the exe before main().

jarvis-gui does not hit this because tauri-build embeds its own
manifest as part of the Tauri compile step. jarvis-app had no
manifest at all.

Fix:
- app.manifest: Common-Controls v6 dependency, supportedOS for
  Win7..Win11, PerMonitorV2 DPI awareness, UTF-8 active code page,
  asInvoker execution level.
- app.manifest.rc: 3-line .rc that embeds the manifest with
  CREATEPROCESS_MANIFEST_RESOURCE_ID (1) and RT_MANIFEST (24).
- build.rs: on cfg(windows), embed_resource::compile() compiles the
  .rc and links the resulting .res into the exe. rerun-if-changed
  on the manifest sources.
- Cargo.toml: target-windows build-dependency on embed-resource 3.

Verified: jarvis-app.exe now runs cleanly through all init steps:
commands parsed, audio init, recorder init found "Микрофон (5-
Fifine Microphone)", IPC server up on ws://127.0.0.1:9712, VAD
already flushing frames.

UTF-8 active code page in the manifest also helps russian-character
fidelity when win32 APIs are invoked from PowerShell helpers.
2026-05-15 13:26:00 +03:00
Bossiara13
a3bdf90237 build(gui): build.rs auto-runs npm run build, no more stale frontend bundle
Reproduction:
  cargo build --release -p jarvis-gui   # before this commit
  → Tauri'\''s generate_context!() reads frontend/dist/client which is
    whatever the user (or me) last built, possibly hours ago. Result:
    new release exe ships with stale UI. Caught when /commands page
    kept rendering the placeholder after the new code was committed
    and the binary was rebuilt — because dist/ was generated before
    the Svelte file was rewritten.

Fix: build.rs now invokes `npm run build` in frontend/ on every
relink, and emits cargo:rerun-if-changed for frontend/src and
package.json so cargo also reruns build.rs when Svelte/TS sources
change. `cargo tauri build` still works (it runs npm twice, which
is wasteful but harmless). If npm is not installed (unlikely but
possible on a Rust-only CI), prints a cargo warning and bundles
whatever dist is on disk.

`dist` stays in frontend/.gitignore — this just makes sure a local
working copy always has a fresh dist before Tauri picks it up.
2026-05-15 13:18:08 +03:00
Bossiara13
a7c002c9d4 arch + features: jarvis.text API + brightness/window_switch/spelling/network/summarize (41 packs)
Architecture — close the trigger-strip duplication:

  lua/api/text.rs (new):
  + jarvis.text.strip_trigger(phrase, triggers) — case-insensitive
    "longest trigger wins" stripping, returns trimmed remainder. The
    11 packs that previously inlined a `for _, t in ipairs(triggers) do
    string.find ... s == 1 then sub` loop can drop to one call.
    Demonstrated in spelling/, window_switch/, summarize/.
  + jarvis.text.contains_any(phrase, needles) — boolean check for any
    of N substrings, case-insensitive. Useful for keyword routing.

5 new portable Lua packs (sandbox=full). 36 → 41 total. cargo test
commands::tests still 3/3.

brightness/ — brightness_up / down / max / min. WMI WmiMonitorBrightness
(read CurrentBrightness) + WmiMonitorBrightnessMethods.WmiSetBrightness
(write). ±20% step for up/down; max=100, min=10. Desktop monitors that
don'\''t expose the WMI namespace get a friendly "не поддерживается"
toast instead of a silent failure.

window_switch/ — switch_to_window. Strip trigger ("переключись на" /
"switch to" / etc.), apply the same alias map as process_kill
(хром→chrome, телега→telegram, ...), then PowerShell finds processes
by name OR window-title substring sorted by StartTime desc and uses
(New-Object -ComObject WScript.Shell).AppActivate($pid) on the top
match. Speaks the window title that got focus.

spelling/ — spell_out. Strips "произнеси по буквам" / "spell out" /
etc., iterates `word:gmatch(".")`, joins letters with ". " so SAPI
gives each one a natural micro-pause. Uses jarvis.text.strip_trigger
+ jarvis.speak — 24 lines total.

network/ — open_wifi_settings (ms-settings:network-wifi) +
open_bluetooth_settings (ms-settings:bluetooth) + my_ip. my_ip pulls
the first non-loopback, non-APIPA IPv4 with Get-NetIPAddress and
fetches WAN IP from api.ipify.org (5s timeout); speaks "Локальный X.
Внешний Y".

summarize/ — summarize_selection. Synthesises Ctrl+C through user32!
keybd_event (so the focused window copies its selection), waits
220 ms for the clipboard to populate, reads via
jarvis.system.clipboard.get(), bails if < 20 chars, truncates at
8000, sends to Groq @ temp 0.3 with a "3-5 sentences, keep names"
prompt, drops the summary back into the clipboard and speaks it.
Lets you select ANY text in ANY app (browser, PDF, IDE) and say
"суммируй" to get a spoken TL;DR.

The new packs explicitly use the new jarvis.text + jarvis.llm +
jarvis.speak API surface — no inline PS-SAPI boilerplate, no inline
Groq plumbing. Code is now intent-first.
2026-05-15 13:11:18 +03:00
Bossiara13
a16d2401e7 arch + features: Lua jarvis.speak() / jarvis.llm() API, /commands GUI page, games / mouse / random_choice packs
Architecture — DRY at the Lua API surface:

  lua/api/tts.rs (new): jarvis.speak(text, opts?). opts.lang = ISO
  two-letter code (default "ru"); auto-picks first installed voice
  matching the culture. opts.async = true|false (default true). The
  9 packs that previously inlined a 60-character PS-SAPI string for
  every spoken reply can now reduce to one line. Refactored fun/,
  dice/, clipboard_read/ as proof — fun/ask.lua went from 75 lines
  of HTTP+SAPI boilerplate to ~28 lines of intent code.

  lua/api/llm.rs (new): jarvis.llm(messages, opts?). messages is a
  list of {role, content} tables, opts.max_tokens / .temperature /
  .top_p. Returns the assistant string or nil. Gated by sandbox
  allows_http() (so Standard+ packs only). Internally uses the
  existing jarvis_core::llm::LlmClient::complete_with, no duplicated
  HTTP plumbing.

  llm/client.rs: split complete() into complete_with(messages,
  max_tokens, temperature, top_p) for the new caller; old complete()
  is now a thin wrapper at temp=0.7 top_p=1.0 for backward compat.

GUI — /commands page rewrite:

  Replaces the "Раздел в разработке" placeholder. Calls Tauri command
  get_commands_list (already existed in tauri_commands/commands.rs,
  was wired but unused). Renders a search-filterable card grid:
    - cmd id (monospace)
    - type badge with colour by kind (lua=blue, ahk=red, cli=grey,
      voice=green, terminate=red, stop_chaining=violet)
    - sandbox badge
    - description line (if non-empty)
    - all phrases for currentLanguage, fallback to en, fallback to
      first available; each phrase in a small inline pill
  Toolbar: text input with magnifier icon + reload button (↻).
  Counter line shows visible/total + "Скажи Джарвис + любую фразу"
  hint. Counts down as you type the filter.

New packs (3, total 33 → 36):

  games/ — launch_game / list_games. Reads or creates
  %USERPROFILE%\Documents\jarvis-games.json (the sample preloads dota,
  cs2, elden ring, witcher 3, factorio + minecraft launcher path +
  fortnite epic URI). Trigger-strip + fuzzy name/alias match, then
  launches via steam://rungameid/N (Steam), epic:// (Epic Games), or
  start "" "path" (anything else). list_games speaks the configured
  library.

  mouse/ — left/right/middle/double click + scroll up/down. Single
  dispatch.lua + _mouse_helper.ps1. PS P/Invokes user32!mouse_event
  with the right flag pair (LEFTDOWN+LEFTUP / etc.), 30ms gap, doubles
  do two LEFT cycles with a 50ms gap, wheel uses ±360 ticks.

  random_choice/ — "выбери из X или Y или Z". Strips the trigger,
  splits on " или " / " or " / ", " / " либо " / " чи ", math.random
  picks one, speaks "Я выбираю N".

cargo test commands::tests still 3/3. 36 packs verified via
jarvis-gui startup log.
2026-05-15 12:58:16 +03:00
Bossiara13
9d8b917149 feat(commands): analog-inspired tier-2 — news, currency, crypto, fun, wiki, power, help (33 packs)
Seven more portable Lua packs lifted from common voice-assistant
playbooks (Siri/Alexa/Алиса/Cortana). All sandbox=full, jarvis.http
+ jarvis.system.exec, zero new Rust code. cargo test still 3/3.

news/ — pulls Lenta.ru RSS (en switches to BBC), regex-extracts the
top 5 <title> entries (skipping the channel header), drops them into
the clipboard, then asks Groq for a 2-3 sentence summary at temp 0.4
and speaks it via SAPI. If GROQ_TOKEN is missing it falls back to
just reading the first three headlines.

currency/ — cbr-xml-daily.ru/daily_json.js. Picks USD/EUR/CNY/GBP/
JPY/KZT by keyword in the phrase, computes value / nominal, diffs
against Valute.Previous, says "X стоит N рублей, поднялся/опустился
на M" with ▲/▼ in the notify body.

crypto/ — CoinGecko simple/price with usd,rub and 24hr_change.
Recognises bitcoin / ethereum / solana / dogecoin / the-open-network.
Reports price + direction in plain Russian.

fun/ — joke / fact / quote / compliment, single ask.lua dispatched by
command_id. Per-command system prompt at temp 1.0; seeds with a
random integer so consecutive calls don't repeat.

wiki/ — ru.wikipedia opensearch → REST summary endpoint. If Groq is
available and extract > 350 chars, asks for a 2-3 sentence retell;
otherwise speaks the first 400 chars verbatim. Full extract goes to
clipboard.

power/ — shutdown_pc / restart_pc / sleep_pc / hibernate_pc /
logoff_user / cancel_shutdown. Single act.lua keyed by command_id.
shutdown/restart use shutdown.exe /s|/r /t 30 with a /c message —
30-second grace window so "отмени выключение" actually works.
sleep uses rundll32 powrprof,SetSuspendState, hibernate uses
shutdown.exe /h.

help/ — "что ты умеешь". Reads the parent commands/ dir via
jarvis.fs.list, sorts subdirs (pack names), pairs each with a
hardcoded Russian/English one-liner description, copies the full
list to clipboard and speaks a count + "see clipboard for full list".

Total command packs is now 33 (was 26).
2026-05-15 12:43:09 +03:00
Bossiara13
dcee98bddf feat: 5 new packs + dotenvy + regen icon.ico + shortcuts redo
Iron Man icon regenerated from resources/icons/icon.png at sizes
16/24/32/48/64/128/256 via Pillow (make_ico.py committed alongside);
icon.ico is now a clean 83 KB multi-res for sharp display at any
scale instead of whatever placeholder Tauri originally emitted.

dotenvy added to workspace deps. jarvis-app/main.rs and jarvis-gui/
main.rs both walk up from current_exe() looking for dev.env (up to
5 parent levels), so the Desktop shortcut can launch the exe
directly without a wrapper .bat to pre-set GROQ_TOKEN. Falls back
to dotenvy::dotenv() (cwd lookup) if nothing found near the binary.

5 new portable Lua command packs. Bumps total from 21 to 26.
cargo test -p jarvis-core --lib commands::tests still passes 3/3.

reminders/ — set_reminder. Parses "напомни через N (секунд|минут|
часов) <body>" or English equivalents; understands one Russian word
numerals (один, две, пять, десять, пятнадцать, ...) for the count
when speech recognition returns words instead of digits. Defaults to
5 minutes if number/unit missing. Spawns a detached PowerShell that
Start-Sleeps then fires: BurntToast if installed, System.Speech
SAPI (ru-RU voice preference), and a WScript.Shell.Popup as the
guaranteed-visible last resort.

date_query/ — today / tomorrow / yesterday. Single answer.lua keyed
by command_id, computes the offset via PowerShell Get-Date.AddDays()
in ru-RU culture so the weekday/month come out in Russian, then
speaks via SAPI.

voice_type/ — type_text. Strips the trigger, drops the remainder to
clipboard via jarvis.system.clipboard.set, then synthesises Ctrl+V
through user32!keybd_event so it lands in whatever window currently
has focus. Works in any text field (note app, browser, IDE,
Telegram, etc.).

dice/ — coin_flip / roll_dice / random_number. Single roll.lua,
seeds math.random with os.time + jitter, speaks the result. "1-6"
for dice, "1-100" for random.

stopwatch/ — start / check / stop. Uses jarvis.state.get/set
(persisted via jarvis-core's settings DB) to remember the start
timestamp, computes elapsed via jarvis.context.time.timestamp,
formats as "X сек" / "X мин Y сек" / "X ч Y мин Z сек".

All new packs follow the established patterns (sandbox=full, PS-via-
exec helper where needed, USERPROFILE/SystemDrive everywhere, no
hardcoded paths).
2026-05-15 12:28:44 +03:00
Bossiara13
ae279aeef7 feat(gui): footer badge "Rust" + one-click run.bat launcher
GUI: Footer.svelte gets a small inline "Rust" pill next to the
copyright line (red bg #b7411a, white 11px, rounded). Now obvious
at-a-glance which fork's UI you're looking at when both python and
rust are open side-by-side. Python edition mirrors with a console
banner.

Launcher: run.bat at the repo root loads dev.env (skipping #-comments
and blank lines), then `start ""` spawns target\release\jarvis-gui.exe.
The new Desktop shortcut "Jarvis (Rust).lnk" points at this batch
file with resources\icons\icon.ico as its icon. WindowStyle=7 means
the cmd host stays minimized — only the GUI window pops up.

If the user hasn't built yet, the bat prints the cargo build command
and pauses instead of silently doing nothing.
2026-05-15 12:12:11 +03:00
Bossiara13
100db82bd6 feat(commands): tier-1 — translate, math, theme toggle, project opener, sound panel
Five more portable Lua command packs. Bumps the total from 16 to 21
packs. cargo test -p jarvis-core --lib commands::tests still 3/3.

translate/ — translate.lua. Picks the target language from triggers
like "переведи на английский" / "translate to russian"; falls back to
English when only "переведи X" is heard. Sends to Groq with a strict
"translation only" system prompt at temp 0.3. Drops the result into
the clipboard, fires a notify, and speaks via PowerShell SAPI in the
matching ISO voice (ru-RU / uk-UA / en-US / de-DE etc.) so a German
translation actually sounds German.

math/ — math.lua. Strips the trigger ("посчитай" / "calculate" /
"сколько будет" / "what is" / ...), sends the remainder to Groq with
temp=0.0 and max_tokens=64, asking ONLY for the numeric result. If
the model replies "нет" (the system-prompt sentinel for non-math),
plays not_found instead of speaking nonsense. Otherwise speaks
"Получилось X" via SAPI.

theme/ — set.lua dispatched by command_id (theme_dark / theme_light).
Flips AppsUseLightTheme + SystemUsesLightTheme under
HKCU\...\Themes\Personalize via Set-ItemProperty. No reboot needed,
Windows shell re-themes immediately.

projects/ — open.lua + list.lua. Reads/creates
%USERPROFILE%\Documents\jarvis-projects.json (a flat
[{"name": ..., "path": ...}] list). open.lua strips the trigger,
exact-matches then substring-matches the project name, opens the path
in VS Code if `code.cmd` is on PATH, otherwise in Explorer. First
call creates a sample config with jarvis-rust / jarvis-python /
dietpi entries pointing under USERPROFILE so it works on any machine.
list.lua just notifies the configured project names.

sound_panel/ — open.lua. Two-liner: rundll32 mmsys.cpl,,1 (Recording
tab). Useful both as a standalone "открой настройки звука" and as
the place to fix the mic-disabled state that prevents jarvis-app
from starting.

Portability: USERPROFILE / SystemDrive / PATH lookup throughout.
2026-05-15 12:06:30 +03:00
Bossiara13
01ea46c091 fix(app): show toast + open Sound settings when mic enumeration returns nothing
Before: clicking "Запустить" in jarvis-gui spawned jarvis-app, which
silently exited with code 1 (or in release builds, looked like the
console window flashed and closed). The pv_recorder library returns
INVALID_ARGUMENT from pv_recorder_init when Windows Core Audio reports
zero capture endpoints (some other app holding the mic exclusively,
or all input devices disabled in mmsys.cpl). User saw no actionable
feedback.

Now: on recorder::init failure jarvis-app calls notify_mic_problem()
which (Windows-only):
- Fires a long-duration Windows toast titled "J.A.R.V.I.S.: микрофон
  не найден" with a hint pointing at mmsys.cpl / Recording.
- Spawns "start ms-settings:sound" so the Sound settings page opens
  automatically — user can re-enable the mic in two clicks.

Then the original app::close(1, ...) path runs to keep the same exit
behaviour the GUI's get_jarvis_app_stats poller expects.

Cargo.toml: jarvis-app now pulls winrt-notification (already in
workspace.dependencies via jarvis-core) for the toast.

Also incidentally fixed: the release-build C0000139 (entrypoint not
found) loader crash that was showing up before this change. It went
away after the workspace dep was added and the release relink ran.
Most likely the previous release exe had a stale import table from an
earlier partial rebuild; the clean relink resolves it.

Non-Windows builds get a no-op eprintln so the binary still compiles
for Linux/macOS.
2026-05-15 11:58:47 +03:00
Bossiara13
16ef413962 feat(commands): tier-1 PC tools — window mgmt, clipboard, notes, process killer, web search
Five new portable Lua command packs. Bumps the total from 11 to 16 packs.
All sandbox=full, all dispatched via jarvis.system.exec or jarvis.fs / jarvis.system
APIs, no new Rust code, post_build.py --sync copies them under target/.
cargo test -p jarvis-core --lib commands::tests passes 3/3 against all 16.

window/ — 8 commands, single dispatch.lua keyed by jarvis.context.command_id:
  show_desktop / maximize_window / minimize_window / snap_left / snap_right
  / close_window / restore_all / task_view. _window_helper.ps1 P/Invokes
  user32!keybd_event for arbitrary combos like "win+d", "alt+f4",
  "win+shift+m". Uses one shared script per pack — much less duplication
  than the volume/ approach.

clipboard_read/ — read_clipboard. Pulls jarvis.system.clipboard.get(),
caps preview at 400 chars, then shells PowerShell System.Speech to actually
speak it aloud (auto-picks the first ru-RU voice if present). Also fires a
notify with a 120-char snippet.

notes/ — add_note + open_notes. add_note strips the trigger phrase from
the recognized voice, appends "[YYYY-MM-DD HH:MM] body\n" to
%USERPROFILE%\Documents\jarvis-notes.txt via jarvis.fs.append (creates
the file on first call). open_notes opens the file in the default editor
via jarvis.system.open. Trigger list covers RU/EN/UA variants of
"запиши" / "запомни" / "write down" / etc.

process_kill/ — kill_process. Trigger-strips "закрой программу" / "убей
процесс" / etc., then runs the remainder through an alias map (хром→
chrome, телега→telegram, спотифай→spotify, edge/edge/едж→msedge etc.,
20+ entries) before invoking taskkill /F /IM. Notifies success/not-found.

websearch/ — search_google / search_youtube / search_wiki / search_yandex.
Single dispatch.lua reads jarvis.context.command_id to pick the base URL,
URL-encodes the query (Lua %02X gsub, not relying on jarvis.http), opens
the resulting link via jarvis.system.open (system default browser).

Portability check: every helper resolves paths via $env:USERPROFILE /
jarvis.context.command_path / jarvis.system.env — no hardcoded C:\ refs.
2026-05-15 11:27:02 +03:00
Bossiara13
3d127f05c0 feat(app): speak LLM reply via Windows SAPI server-side
GUI front-end does not subscribe to IpcEvent::LlmReply, so the LLM
auto-fallback was effectively silent — user got the OK chime but no
spoken answer. Backlog item resolved.

llm_fallback::handle now also invokes speak_via_sapi() after firing
IpcEvent::LlmReply (both for successful replies and the fallback
error message). The text goes through System.Speech.Synthesis.
SpeechSynthesizer, with a preference pass that picks the first
installed ru-RU voice if present (Microsoft Irina / Pavel etc.) so
Russian replies sound right out of the box, and falls back to the
default English voice otherwise.

PowerShell is invoked fire-and-forget (no Wait, stdout/stderr null)
so the voice loop never blocks. A long answer keeps speaking while
jarvis returns to wake-word listening; the mic may pick up some of
the speech which is the same compromise voices::play_* already makes.
A cleaner version would pause pv_recorder for the duration of the
utterance — that goes in the backlog as part of the eventual
unified IpcEvent::Speak refactor.

Toggle: set JARVIS_LLM_TTS=false to disable (e.g. on a server where
the GUI is the speaker). Non-Windows builds get a no-op stub.
2026-05-15 01:51:02 +03:00
Bossiara13
69a38b0d89 feat: LLM auto-fallback + codegen pack + OCR pack
LLM auto-fallback (jarvis-app/src/app.rs + jarvis-core/src/config.rs):
- When neither intent classifier nor levenshtein finds a command match,
  route the utterance straight to the LLM instead of just playing the
  "not found" sound. Triggers ("скажи X", "answer Y") still work and
  take precedence — they short-circuit before command lookup.
- Two new config knobs:
    LLM_AUTO_FALLBACK = true        — master toggle
    LLM_AUTO_FALLBACK_MIN_CHARS = 4 — suppress for very short utterances
                                       so background noise doesn't burn
                                       Groq quota
- Requires GROQ_TOKEN; if absent, behaviour is unchanged (play_not_found).

codegen/ command pack:
- Phrases: "напиши код X", "сгенерируй скрипт Y", "write code Z", etc.
- Strips the trigger from the recognized phrase, sends what remains to
  Groq with a strict system prompt ("return ONLY code, no fences, no
  commentary") at temperature 0.2.
- Parses the JSON content, unescapes \n / \" / \\ / \t, strips any
  remaining ```lang ... ``` fences, drops the result into the clipboard
  via jarvis.system.clipboard.set.
- Notifies a 120-char preview + plays ok-sound. Works for any language
  the model handles (Python by default if unspecified).
- GROQ_TOKEN / GROQ_MODEL / GROQ_BASE_URL read from env at call time —
  same envvars the voice-loop fallback already uses.

ocr/ command pack:
- Phrases: "прочитай экран", "что на экране", "read screen", etc.
- Captures the primary screen via System.Windows.Forms + System.Drawing
  to a temp PNG, then shells to tesseract.exe (-l rus+eng for ru/ua,
  -l eng for en).
- Resolves Tesseract by PATH first, then C:\Program Files\Tesseract-OCR
  and the x86 install dir; if none works the user gets a friendly
  "winget install UB-Mannheim.TesseractOCR" hint in a notification.
- Recognized text goes to clipboard + a 200-char preview notification.

All three features are portable (env-var resolution, no hardcoded user
paths). Command-pack total is now 11. cargo test -p jarvis-core --lib
commands::tests passes 3/3.
2026-05-15 01:39:21 +03:00
Bossiara13
429113175a build(core): add build.rs so cargo test links against libvosk
cargo test on jarvis-core was failing with `LNK1181 cannot open input
file "libvosk.lib"`. The link-search path is declared in jarvis-app's
build.rs (and jarvis-cli's), but the test binary for jarvis-core has
no such config and the workspace .cargo/config.toml didn't cover it.

Adds a minimal build.rs that emits `cargo:rustc-link-search=native=...`
pointing at lib/windows/amd64 (resolved relative to CARGO_MANIFEST_DIR,
no hardcoded absolute paths so the project still builds for anyone
cloning under a different drive/path).

Verified: cargo test -p jarvis-core --lib commands::tests now passes
3/3 (every_command_toml_parses, lua_command_scripts_exist,
every_command_has_phrases).
2026-05-15 01:32:17 +03:00
Bossiara13
b257f03f65 feat(commands): add media controls + system info packs
media/ — play_pause, next, prev, stop via user32!keybd_event (VK_MEDIA_*
0xB0..0xB3). Helper _media_helper.ps1 mirrors the volume/ pack pattern,
so the same C# P/Invoke style works for the four standard media keys
without needing any external tooling. Catches Spotify, foobar, browsers,
Yandex Music PWA — anything that responds to system media keys.

sysinfo/ — battery / time / cpu / ram / disk and an "all" combo.
_sysinfo.ps1 uses CIM queries (Win32_Battery, Win32_OperatingSystem,
Win32_LogicalDisk, Win32_Processor + Win32_PerfFormattedData_PerfOS_
Processor) — no external deps. Time is rendered in ru-RU culture
("01:17, 15 мая, пятница"). Battery falls back to a friendly "no
battery, looks like a desktop" when Win32_Battery is empty. SystemDrive
env var resolves the right disk on non-C:/ installs.

Each sysinfo command notifies the line as a Windows toast and logs it.

Triggered locally: sysinfo_all currently reports:
  Сейчас: 01:17, 15 мая, пятница
  Батарея не обнаружена (видимо, десктоп)
  CPU: AMD Ryzen 7 5700X3D 8-Core Processor, загрузка 4%
  RAM: 23.4 из 47.9 ГБ (49% занято)
  Диск C:: свободно 118.2 из 1906.8 ГБ

Total command packs is now 9 (verified via jarvis-gui startup log).

Portability: all PowerShell helpers use $env:SystemDrive and CIM
classes that ship with every Windows install; Lua wrappers resolve
the helper path via jarvis.context.command_path (no hardcoded user
profile paths or absolute C:\ refs).
2026-05-15 01:19:30 +03:00
Bossiara13
24457ad6c9 chore(gui): rebrand — strip TG/Boosty/Patreon/feedback-bot, keep CC BY-NC-SA attribution
The previous feature/gui-rebrand-polish work only updated the appInfo store
keys; Footer.svelte, settings, and the commands page were still rendering
upstream's Telegram channel, Boosty/Patreon support links, feedback bot,
and the original author byline. This finishes the job.

Rust side (applies after `cargo build` — currently a no-op until MSVC Build
Tools are reinstalled):
- Cargo.toml: authors = ["Bossiara13"], repository = J.A.R.V.I.S-rust fork.
  Original attribution stays in LICENSE.txt per CC BY-NC-SA 4.0; the UI
  no longer renders an author byline.
- config.rs: dropped TG_OFFICIAL_LINK / FEEDBACK_LINK / SUPPORT_BOOSTY_LINK
  / SUPPORT_PATREON_LINK. Added UPSTREAM_REPOSITORY_LINK (Priler/jarvis for
  the licence attribution), ISSUES_LINK (this fork's GH issues), and
  PYTHON_FORK_LINK.
- tauri_commands/etc.rs: dropped get_tg_official_link/get_boosty_link/
  get_patreon_link/get_feedback_link. Added get_upstream_link, get_issues_link,
  get_python_fork_link. Tidied up the Option<&str> -> String boilerplate
  with unwrap_or.
- main.rs: invoke_handler updated to match.

Frontend side (visible only after Rust rebuild — Tauri bundles the dist into
the exe):
- stores.ts: appInfo shape reduced to repositoryLink / upstreamLink /
  issuesLink / pythonForkLink / logFilePath. loadAppInfo() uses fetchOr()
  with hardcoded fallbacks so the UI still populates correctly when run
  against an un-rebuilt binary; fetchRepoOr() additionally overrides
  CARGO_PKG_REPOSITORY if it still resolves to upstream's Priler/jarvis.
- Footer.svelte: rewritten — © year + "J.A.R.V.I.S." + repo link + issues
  link + small "fork of Priler/jarvis · CC BY-NC-SA 4.0" attribution line.
  No author byline (it's in LICENSE.txt).
- routes/settings/index.svelte: feedback link now points to GitHub Issues.
- routes/commands/index.svelte: TG-channel reference replaced with a link
  to the Python fork (where the command builder lives) plus a pointer to
  resources/commands/ in this repo. Bruh GIF removed.
- locales/{ru,en,ua}.ftl: removed footer-author / footer-telegram /
  footer-support, added footer-issues / footer-fork-of, rewrote
  commands-wip-* strings, retargeted settings-beta-bot to "GitHub Issues".

Bundle identifier (com.priler.jarvis) kept intact to avoid moving the app
data directory.
2026-05-15 01:09:14 +03:00
Bossiara13
85a004e263 test(commands): schema and Lua script validation for resources/commands/
Three unit tests catch the kind of bug that broke weather/set_city (a
phrases array instead of lang→array map silently dropped the whole pack):

- every_command_toml_parses: every resources/commands/*/command.toml round-
  trips through toml::from_str::<JCommandsList>. Reports all failures at
  once instead of failing on the first.
- lua_command_scripts_exist: for every type="lua" command, the named (or
  default script.lua) script file exists in the pack dir.
- every_command_has_phrases: structural commands (terminate/stop_chaining)
  excluded, every other command has ≥1 phrase across all languages.

Tests use env!("CARGO_MANIFEST_DIR") -> ../.. -> resources/commands so they
work from any cwd.

NB: cargo test does not run on this machine right now — aws-lc-sys needs
cl.exe (MSVC Build Tools missing). The release binary was built earlier
with MSVC available; toolchain install is a follow-up. The tests are still
valid for CI / a re-armed dev box.
2026-05-15 00:55:01 +03:00
Bossiara13
9c25108356 feat(commands): add PC control packs — volume, apps, file_search, output_device
All four packs are pure Lua (sandbox=full) and call out to PowerShell helpers
where COM / native APIs are needed. No new Rust code, no rebuild required:
post_build.py --sync copies them under target/{debug,release}/resources/.

volume/
  - volume_up / volume_down: 5x VK_VOLUME_UP|DOWN via keybd_event
  - volume_mute: VK_VOLUME_MUTE
  - volume_max: 50x VK_VOLUME_UP (system clamps)
  Helper _volume_helper.ps1 P/Invokes user32!keybd_event.

apps/
  - open_browser  : start "" https://www.google.com (uses system default browser)
  - open_notepad  : notepad.exe
  - open_calculator: calc.exe
  - open_explorer : explorer.exe
  - open_terminal : wt.exe, falls back to powershell.exe
  - open_settings : ms-settings: protocol
  - open_task_manager: taskmgr.exe
  - lock_screen   : rundll32 user32.dll,LockWorkStation
  - screenshot    : System.Drawing capture to ~/Pictures/jarvis_screenshot_*.png

file_search/
  - find file <query> : recursive Get-ChildItem in Desktop/Documents/Downloads
    (depth 3, top 5 matches). Strips Russian/English/Ukrainian trigger
    prefixes from the recognized phrase, then opens explorer /select on the
    first hit and notifies with the total count.

output_device/
  - list_output_devices : enumerates active render endpoints via
    IMMDeviceEnumerator and notifies friendly names with indices
  - next_output_device  : cycles the system default render device via the
    undocumented IPolicyConfig::SetDefaultEndpoint (eConsole/Multimedia/
    Communications). Tested locally — listed 7 devices, current correctly
    identified at index 5.
2026-05-15 00:54:52 +03:00
Bossiara13
16240609de fix(commands): repair weather set_city schema, retire broken AHK browser pack
set_city used a plain `phrases = [...]` array which fails the lang-map schema
in JCommand — the whole weather pack was being skipped at load. Normalized
to per-language phrases.

browser/command.toml pointed at Priler's bundled AutoHotKey exes that do not
ship in this fork, so the entries were dead. Renamed to .disabled (the loader
skips packs without command.toml) so we do not collide with the new Lua
apps pack while keeping the directory for reference.
2026-05-15 00:54:34 +03:00
Bossiara13
c234c46642 chore(app): instrument init steps with eprintln + label app::close calls 2026-04-27 21:07:33 +03:00
Bossiara13
9a1c047282 Merge feature/vad-rust into dev
Adds webrtc-vad-driven listening window for the post-wake-word phase.
State machine in jarvis-core::audio_processing::vad::listen_window;
adapter in vad::webrtc; integrated in app.rs::recognize_command and
stt::vosk::finalize_speech. Same parameters as Python v0.2.0
(aggressiveness 2, end-silence 1200ms, min-speech 500ms,
min-listen 1000ms, hard-cap 15000ms).
2026-04-23 11:10:06 +03:00
Bossiara13
d23c7fbd12 docs: README — VAD smart command end 2026-04-23 11:08:09 +03:00
Bossiara13
acebb02a8e test: vad listening window timing 2026-04-23 11:08:02 +03:00
Bossiara13
a3389f8bf7 feat(app): close post-wake listening on silence (webrtc-vad) 2026-04-23 11:07:42 +03:00
Bossiara13
37c8371f3f feat(core): vad-driven listening window state machine 2026-04-23 11:07:35 +03:00
Bossiara13
3f19366ad0 chore: add webrtc-vad workspace dep 2026-04-23 11:07:04 +03:00