feat: Wave 3 — plugin loader + Flet GUI (parity with Rust fork)

Plugin loader (mirrors Rust #29):
- plugins_store.py walks %APPDATA%\com.priler.jarvis\plugins\<name>\
  for commands.yaml fragments and merges them into VA_CMD_LIST at
  startup. Same schema as the project root commands.yaml. Per-pack
  `disabled` flag file skips a pack without deletion. Built-in ids
  win conflicts so a malicious pack can't silently override
  open_browser. 9 unit tests (parity with the Rust tests).
- main.py: plugin merge runs after the root yaml load; logs the
  number of extra commands picked up.

Flet GUI (#27 — first parity with the Rust Tauri GUI):
- New gui/ package: __main__.py launches via `python -m gui` (or
  gui.bat). app.py routes between 7 pages (Главная / Команды /
  Макросы / Расписание / Память / Плагины / Настройки) via
  NavigationRail; dark theme matching the Tauri look.
- gui/services.py centralises every store-module call the pages
  share — single place to swap implementations later.
- One file per page in gui/pages/, all expose `build(page)` and use
  the same card layout idiom. Pages mirror the Tauri ones feature-
  for-feature: home (status + counters + launcher), commands
  (filterable list of all VA_CMD_LIST entries), macros (list +
  delete + recording banner), scheduler (list + remove), memory
  (add/forget/search), plugins (toggle/open folder), settings
  (profile + LLM hot-swap).
- requirements.txt: flet>=0.85 (optional; assistant runs without).
- 3 smoke tests verify imports + build callables; skip cleanly when
  Flet isn't installed.

README: documents both the GUI and the plugin layout in user-facing
terms. Tests: 42 → 54 (+9 plugins +3 GUI smoke).
This commit is contained in:
Bossiara13 2026-05-16 13:27:04 +03:00
parent 2bef1d5ea7
commit 59a1258dcc
19 changed files with 1233 additions and 0 deletions

49
tests/test_gui_smoke.py Normal file
View file

@ -0,0 +1,49 @@
"""Smoke tests for the Flet GUI module — verifies that imports work and that
each page builder is callable. We do NOT launch a real Flet window; we mock
the `flet.Page` object enough that the builders execute without errors.
If Flet isn't installed, these tests skip cleanly so the suite still passes
on console-only installs.
"""
import pytest
flet = pytest.importorskip("flet")
def test_gui_app_module_imports():
from gui import app # pylint: disable=import-outside-toplevel
# The module must expose the canonical 7 pages.
titles = [t for t, _ in app.PAGES]
assert "Главная" in titles
assert "Команды" in titles
assert "Макросы" in titles
assert "Расписание" in titles
assert "Память" in titles
assert "Плагины" in titles
assert "Настройки" in titles
def test_services_module_imports_cleanly():
# services pulls in memory_store, profiles_store, macros_store, scheduler_store
# and llm_backend. None of those should fail to import in a fresh process.
from gui import services # pylint: disable=import-outside-toplevel
# Just verify the public surface exists.
for fn in (
"memory_all", "memory_remember", "memory_forget", "memory_search",
"profile_list", "profile_active", "profile_set",
"macros_all", "macros_delete", "macros_recording",
"scheduler_all", "scheduler_remove",
"plugins_all", "plugins_open_folder", "plugins_set_enabled",
"llm_active", "llm_swap",
"assistant_run", "assistant_is_running",
):
assert callable(getattr(services, fn)), f"missing or non-callable: {fn}"
def test_each_page_module_exposes_build_callable():
from gui.pages import home, commands, macros, scheduler, memory, plugins, settings
for mod in (home, commands, macros, scheduler, memory, plugins, settings):
assert callable(getattr(mod, "build", None)), \
f"{mod.__name__}.build missing"