Commit graph

12 commits

Author SHA1 Message Date
Bossiara13
633850d0f7 feat: Python parity for wol / banter / conversation / ddg_answer + 11 tests
Some checks failed
Python CI / pytest (ubuntu-latest) (push) Failing after 5s
Python CI / ruff (push) Failing after 5s
Python CI / pytest (windows-latest) (push) Has been cancelled
New `wave59_handlers.py` (~250 lines) mirroring four Rust-only packs:

- wol: pure-Python magic packet via socket + SO_BROADCAST. No PowerShell
  shellout (unlike the Rust pack). Reads MAC from memory_store under
  'wol_<alias>' or JARVIS_WOL_TARGET env. Same MAC format tolerance
  (colons / dashes / dots / bare) as the Rust normaliser.

- banter: pause/resume state + fire-one. Python edition doesn't run a
  background banter thread (the Rust daemon already does), so this is
  just the user-facing surface — same voice phrases work.

- conversation: do_conversation_summary calls Groq with last 12 turns
  from main.py's `message_log`. set_history_ref() injects the live list
  reference at startup so the handler reads up-to-date history without
  a circular import. Offline-safe: GROQ_TOKEN missing → speak the last
  user message instead. do_conversation_repeat re-speaks the last
  assistant turn.

- ddg_answer: urllib + DuckDuckGo Instant Answer JSON. Picks
  AbstractText → Answer → Definition → RelatedTopics[0]. Opens
  duckduckgo.com search fallback when nothing instant.

extensions.py / main.py / commands.yaml: 7 new dispatch entries +
proxy fns. commands.yaml now at 207 entries.

tests/test_wave59_handlers.py: 11 unit tests for MAC normalisation,
magic packet shape, WOL error paths, banter state machine, DDG trigger
stripping, conversation history handling. Network calls deliberately
NOT exercised here — they belong to integration tests.

Tests: 104 → 115 (+11).
2026-05-24 23:12:04 +03:00
Bossiara13
f102b8dc95 feat: expense tracker Python parity (Wave 10)
Some checks failed
Python CI / pytest (ubuntu-latest) (push) Has been cancelled
Python CI / pytest (windows-latest) (push) Has been cancelled
Python CI / ruff (push) Has been cancelled
New `expenses_handlers.py`:
- parse_expense(phrase) -> (amount, category) | None  — pure fn, fully
  unit-tested. Handles "потратил 500 на еду", comma decimals "12,50",
  EN "spent 12.5 on coffee", default category "разное".
- do_expenses_log: append entry to expenses.json (atomic write-then-rename).
- do_expenses_today: sum today's entries (local-day boundary).
- do_expenses_week: rolling 7-day total + count.
- do_expenses_breakdown: per-category, top-5 + tail collapse.

extensions.py / main.py / commands.yaml: 4 new dispatch entries.
.gitignore: + expenses.json.

tests/test_expenses_handlers.py: 8 unit tests covering parse, log,
today/week math, breakdown ordering + tail collapse.

Tests: 96 → 104 (+8). Total Python coverage now mirrors Rust pack
behaviour for personal finance.
2026-05-16 15:02:07 +03:00
Bossiara13
94edb73f26 feat: Python parity for Wave 6-8 — memory admin / cooking / leftoff / trivia / routines
New module `wave68_handlers.py` (~270 lines) covering the Rust packs that
were Rust-only after Waves 6-8:

  - memory_admin: do_memory_count/list/wipe (with full RU plural decl.)
  - cooking: do_cooking + reusable match_recipe(phrase) for the 15-dish
    preset table. Schedules a "ready!" reminder via scheduler_store.
  - leftoff: do_leftoff stitches memory + scheduled tasks + last reply
    into a one-paragraph recap. Offline.
  - trivia: do_trivia random one-liner from RU/EN MCU pools.
  - routines: do_good_night (sleep profile + cancel one-shots),
    do_good_morning (default profile + agenda hint),
    do_coffee_break (5-min check-in via scheduler).

memory_store: new clear_all() returning removed count, atomic via _save.

extensions.py: imports + set_speak wiring + 9 proxy do_* fns.
main.py: 9 new dispatch elifs.
commands.yaml: 9 new entries (now 196 total).

tests/test_wave68_handlers.py: 15 unit tests covering each handler.
Test helper `_rewire()` re-imports state modules via the existing
isolated_state fixture then re-binds the wave68_handlers module-level
refs — fixes the gotcha where module-level imports cache the OLD
memory_store / scheduler_store instances from process startup.

Tests: 81 → 96 (+15).
2026-05-16 14:55:47 +03:00
Bossiara13
5265cb0fbe feat: Wave 5 Python parity — outlook COM + time tracker
Some checks are pending
Python CI / pytest (ubuntu-latest) (push) Waiting to run
Python CI / pytest (windows-latest) (push) Waiting to run
Python CI / ruff (push) Waiting to run
Outlook COM (`outlook_handlers.py`, agent):
- 4 handlers mirroring Rust pack: unread_count, latest, send_clipboard,
  summarize_inbox. Uses `win32com.client` with lazy import so the module
  loads even when pywin32 is missing — handlers degrade gracefully and
  speak a "Outlook недоступен" reply.
- `requirements.txt`: pywin32>=305 (optional).
- 7 unit tests mock `_outlook_session` via unittest.mock.

Time tracker (`time_tracker_handlers.py`, agent):
- 5 handlers: start, stop, today, week, reset. JSON store at
  `<here>/time_tracker.json`, atomic write-then-rename.
- Same shape as the Rust pack's `jarvis.state` blob: current_session_start
  + sessions[{start,end}]. Local-calendar today boundary.
- Full Russian pluralisation (час/часа/часов, минута/минуты/минут).
- 14 unit tests using the existing `isolated_state` fixture in conftest.py.

`extensions.py` + `main.py`: imports + 9 new proxy do_* functions + 9
new dispatch elifs. `commands.yaml`: 9 new entries.

Tests: 60 → 81 (+14 tracker + 7 outlook).
2026-05-16 14:34:44 +03:00
Bossiara13
45d9425ed7 feat: personality pack — Python parity
Some checks are pending
Python CI / pytest (ubuntu-latest) (push) Waiting to run
Python CI / pytest (windows-latest) (push) Waiting to run
Python CI / ruff (push) Waiting to run
Python parity for the 5 personality voice commands shipped on the
Rust side:

- New `personality_handlers.py` (~150 lines): do_personality_{greet,
  thanks, compliment, how_are_you, tony_quote}. Each picks one of
  7-10 phrases per language (RU/EN) at random; greet additionally
  buckets by time-of-day (morning/midday/evening/night).
- `extensions.py`: imports + 5 proxy `do_personality_*` functions
  pointing at the new module, plus set_speak wiring so handlers can
  TTS via the runtime's voice.
- `main.py`: 5 new dispatch elifs feeding the new action types.
- `commands.yaml`: 5 new entries (`personality_*`) mapping voice
  phrases to action types.
- `tests/test_personality_handlers.py`: 6 unit tests confirming
  each handler returns a non-empty string from the expected pool.
- `.gitignore`: stop tracking runtime state generated by tests
  (profiles/, long_term_memory.json, schedule.json, macros.json,
  llm_backend.txt, llm_history.json) — these are user data, not
  source.

Tests: 54 → 60 (+6).
2026-05-16 13:53:44 +03:00
Bossiara13
2bef1d5ea7 feat: Wave 1 python parity — 20 new commands (154 → 174)
Mirrors all 10 packs from rust commit c4b2261 to python.

wave1_handlers.py (new, ~480 lines)
  Self-contained module with 20 handlers + DPAPI helpers + http/ps wrappers.
  Imports memory_store for state-backed packs (vault, clip, habit, rss, weather).

  Coverage:
    do_magic_8ball         — 15 random answers
    do_github_list_issues  — gh issue list (uses memory[github.repo])
    do_github_my_issues    — gh issue list --assignee @me
    do_weather_tomorrow    — Open-Meteo + ipinfo auto-detect
    do_weather_week        — 7-day forecast min/max range
    do_rss_add/read/list   — RSS subscriptions + parse <title> tags
    do_ics_create          — write .ics, open via os.startfile
    do_backup_export       — zipfile of long_term_memory.json + schedule + macros
                              + profiles/ + active_profile + llm_backend + settings
                              (searches script_dir + %APPDATA%/Jarvis)
    do_clip_save/list/restore — 20-slot rolling clipboard history in memory
    do_notif_missed/clear  — enumerate memory[notif.*] / purge
    do_vault_save/get/list — DPAPI encrypt via PowerShell, clears clipboard
                              after 30 sec via threading.Thread
    do_habit_checkin/streak — date-keyed memory state, consecutive-day counter

extensions.py: +20 proxy functions + wave1_handlers import + set_speak wiring.

main.py: +20 dispatch entries.

commands.yaml: +20 entries (154 → 174).

Tests: ast.parse passes for all .py files. 42 pytest still green.
2026-05-16 01:08:17 +03:00
Bossiara13
4e62049a98 feat+test: pytest suite + Now Playing + World Clock + Daily Quote packs (154 commands)
Adds first formal test suite for Python fork — 42 tests covering memory_store,
profiles_store, scheduler_store, macros_store, llm_backend. All pass.

Tests (tests/, ~290 lines)
  conftest.py — `isolated_state` fixture: rewires _PATH/_PROFILES_DIR etc to
                a per-test tmp_path so concurrent runs don't touch real data.
  test_memory_store.py     — 9 tests: remember/recall/forget round-trip,
                              search by key/value, limit/empty query,
                              persistence across reload, build_llm_context
  test_profiles_store.py   — 7 tests: seeded defaults, set_active persistence,
                              allows_command logic (default/work/driving)
  test_scheduler_store.py  — 12 tests: parser (daily/at/every/in, ru units,
                              bad input), add/remove/clear/remove_by_text,
                              _next_fire for each schedule kind
  test_macros_store.py     — 8 tests: is_macro_control filter, start/save
                              round-trip, control-phrase filtering, list_names
                              sorted, replay unknown raises
  test_llm_backend.py      — 6 tests: parse_backend aliases (ru + en),
                              persist round-trip, auto-detect logic

Run: cd /c/Jarvis/python && python -m pytest tests/

Three new fun packs (commands.yaml: 151 → 154)

now_playing — Windows Media Session API via PowerShell
              "что играет" / "что за песня" / "какой трек"
              Works with Spotify, YouTube, Foobar, Yandex Music, anything
              that exposes SMTC (Win10 1803+).

world_clock — worldtimeapi.org (free, no key)
              "сколько времени в Токио" / "время в Лондоне"
              21 Russian + world cities pre-mapped to IANA timezones.

daily_quote — zenquotes.io (free, no key) + LLM translation
              "цитата дня" / "вдохнови меня"
              Fetches English quote, translates to Russian via active LLM
              (Groq or Ollama), speaks "text — author."

Pack count: 151 → 154. Tests: 42 pytest + ast.parse + yaml.safe_load.
2026-05-16 00:56:02 +03:00
Bossiara13
8d3da4ea06 feat: LLM hot-swap voice command + Ollama backend (148 → 151 commands)
Closes the last functional parity gap with rust. Python now has voice-driven
Ollama↔Groq switching, persistent across restarts.

llm_backend.py (new, ~130 lines)
  Singleton module:
    current_client()     → active OpenAI client (or None)
    current_backend()    → 'groq' | 'ollama' | 'none'
    current_model()      → active model name
    swap_to(name)        → hot-swap + persist to <here>/llm_backend.txt
    parse_backend(name)  → ru/en alias normaliser ('облако'→groq, 'локальный'→ollama)

  Backends:
    - Groq:   config.GROQ_TOKEN + GROQ_BASE_URL + GROQ_MODEL (defaults)
    - Ollama: OLLAMA_BASE_URL (default http://localhost:11434/v1) +
              OLLAMA_MODEL (default qwen2.5:3b). api_key='ollama' (placeholder,
              openai lib insists on a non-empty string; Ollama ignores it).

  Init precedence (idempotent _ensure_init):
    1. Persisted choice from llm_backend.txt
    2. JARVIS_LLM env var
    3. Auto-detect: Groq if token present, else Ollama

Voice commands (commands.yaml, +3 entries → 151)
  llm_switch_local  → ollama
  llm_switch_cloud  → groq
  llm_status        → speaks current backend

extensions.py
  + do_llm_switch / do_llm_status handlers
  + llm_backend import
  do_interesting_fact migrated off direct OpenAI(...) to use llm_backend.current_*

dev_handlers.py
  + llm_backend import
  do_codebase_ask + do_github_summarize_pr migrated to llm_backend.current_*

vision_handler.py
  Vision call documented as Groq-specific (Ollama doesn't expose vision via
  OpenAI-compat in our stack), kept direct config.GROQ_TOKEN reading.

Tests: ast.parse passes for all 5 modules. yaml.safe_load = 151 entries.

PYTHON PARITY VS RUST IS NOW FUNCTIONALLY COMPLETE.
Only remaining rust-only feature: the Tauri GUI (python is console-only).
2026-05-16 00:44:42 +03:00
Bossiara13
31ff997782 feat: codebase_qa + github_pr in Python (142 → 148 commands)
Closes the Python parity gap with rust. All major imba features now mirrored
EXCEPT live LLM hot-swap (python LLM backend is still env-driven, not voice-
switchable like rust).

dev_handlers.py (new, ~250 lines)
  Codebase Q&A:
    do_codebase_set        "укажи проект C:\path" → memory_store.remember("codebase.root", ...)
    do_codebase_where      "какой проект сейчас"
    do_codebase_ask        "что делает функция X" / "найди в коде Y"
                           Walks the root with same caps as rust:
                             depth=3, files=30, file_bytes=4096, total=50000
                           Filters by extension (rs/py/ts/lua/go/...).
                           Skips .git/target/node_modules/__pycache__/.venv etc.
                           Feeds digest to Groq LLM with "senior reviewer" prompt.

  GitHub PR review (requires `gh` CLI authenticated):
    do_github_set_repo     "текущий репо owner/repo" → memory_store
    do_github_list_prs     "какие пиары" → `gh pr list --json number,title,author`,
                           speaks count + first 3 titles
    do_github_summarize_pr "разбери последний пиар" → gh pr view of latest open
                           PR, feeds title+body+diff stat to LLM, speaks 3-5
                           sentence review.

extensions.py — 6 new proxy handlers + import dev_handlers + set_speak wires it.

main.py — 6 new action_type dispatches in run_action.

commands.yaml (+6 entries, 142 → 148):
  codebase_set, codebase_where, codebase_ask,
  github_set_repo, github_list_prs, github_summarize_pr.

Tests: ast.parse passes for all .py files. yaml.safe_load = 148 entries.

PYTHON PARITY VS RUST IS NOW COMPLETE except:
  ✗ LLM hot-swap voice command (rust calls llm::swap_to + persists DB;
    python config.GROQ_TOKEN is read once at startup)
  ✗ GUI pages (rust has Tauri+Svelte, python is console-only)

Everything else has full parity: memory / profiles / vision / macros /
scheduler / codebase Q&A / GitHub PR review / 14 utility packs.
2026-05-16 00:40:08 +03:00
Bossiara13
6a328077bc feat: vision + macros + scheduler in Python (129 → 142 commands)
Three big rust packs ported. Python parity now covers the FULL imba lineup
except codebase_qa and github_pr.

vision_handler.py (new, ~125 lines)
  - Screenshot via PowerShell System.Drawing → temp PNG → base64 → Groq
    vision API (llama-3.2-11b-vision-preview, override via GROQ_VISION_MODEL).
  - do_vision_describe   "что на экране" / "опиши экран"
  - do_vision_read_error "прочитай ошибку"
  - Requires GROQ_TOKEN; speaks "Не удалось" on missing.

macros_store.py (new, ~170 lines)
  - JSON store at <here>/macros.json (atomic write-through).
  - start(name) / save() / cancel() / replay(name, dispatch_fn)
  - is_recording / recording_name / list_names / get / delete
  - record_step(phrase) called by execute_cmd hook (see main.py)
  - is_macro_control filter blocks "запиши макрос ..." from recording itself
    (no infinite recursion).
  - replay spawns daemon thread, fires each step with 800ms delay.

scheduler_store.py (new, ~220 lines)
  - JSON store at <here>/schedule.json.
  - parse_schedule(spec) handles "daily HH:MM", "at HH:MM",
    "every/in N minutes|hours|seconds" — Russian (минут/час/секунд) + English.
  - Background daemon thread ticks every 30s, fires Speak actions through the
    same speak callback used by main.py.
  - Once-tasks auto-delete after firing; daily/interval cycle forever.
  - add/remove/clear/remove_by_text/list_all.

extensions.py (+13 handlers)
  do_vision_describe / do_vision_read_error → thin proxies to vision_handler
  do_macros_start / save / cancel / replay / list / delete
  do_scheduler_add_reminder / add_recurring / list / clear / cancel_by_text

main.py
  - extensions.init_background_services() at startup → scheduler thread boots.
  - Wraps execute_cmd so every successful dispatch feeds macros_store.record_step.
  - set_dispatch_fn(_execute_phrase_for_macro) enables macro replay through the
    normal command pipeline (recognize_cmd → execute_cmd).

commands.yaml (+13 entries, 129 → 142)
  vision_describe, vision_read_error, macros_start, macros_save, macros_cancel,
  macros_replay, macros_list, macros_delete, scheduler_reminder,
  scheduler_recurring, scheduler_list, scheduler_clear, scheduler_cancel_text.

Tests: ast.parse passes for all five .py files. yaml.safe_load = 142 entries.

Python parity status vs rust (72 packs):
  ✓ memory_pack, profile_switch, vision, scheduler, macros, llm switch
    (via env), llm_context (basic), media_keys (existing), screenshot, net_info,
    unit_convert, generators, disk, date_math, sleep_timer (via shutdown),
    interesting_fact, mailto, self_check, ssl_check, intro/echo
  ✗ codebase_qa, github_pr (require gh CLI + file walk — TODO)
  ✗ pomodoro / daily_briefing / habit_nudge / quick_search / diagnostics
    (could be ported but need scheduler integration + LLM glue)
2026-05-16 00:00:22 +03:00
Bossiara13
02c6f2f90d feat: memory_store + profiles_store + 10 new yaml entries (119 → 129)
Mirrors `long_term_memory` and `profiles` modules from rust fork.

memory_store.py (new, ~95 lines)
  - JSON store at <here>/long_term_memory.json (atomic write).
  - remember(key, value) / recall(key) / forget(key) / search(query, limit) /
    all_facts() / build_llm_context(prompt, limit).
  - Uses the same JSON shape as the rust side, so the file is
    interoperable if you ever symlink the two installations to the same dir.

profiles_store.py (new, ~145 lines)
  - Profiles at <here>/profiles/<name>.json. active_profile.txt persists.
  - Seeds 5 defaults on first init: default ★, work 💼, game 🎮, sleep 🌙,
    driving 🚗 — same as rust fork.
  - active() / active_name() / set_active(name) / list_names() /
    allows_command(cmd_id).

extensions.py (+6 handlers, +160 lines)
  - do_memory_remember  — derives key (first 6 words or split on '=')
  - do_memory_recall    — substring search or top-3 if no query
  - do_memory_forget    — exact then substring fallback
  - do_memory_list      — count + first 5 keys
  - do_profile_set      — target via action.target or voice ("работа" → work)
  - do_profile_what     — speaks current profile + description

main.py
  - +6 dispatch entries for the new action types.

commands.yaml (+10 entries, 119 → 129)
  - memory_remember, memory_recall, memory_forget, memory_list
  - profile_work, profile_game, profile_sleep, profile_driving, profile_default
  - profile_what

Tests: ast.parse passes for all .py files. yaml.safe_load gives 129 entries.

Python parity is now meaningful: memory + profiles are usable by voice
without any rust install. Still missing for full parity: scheduler (needs
background thread), macros (needs phrase-capture state), vision (needs
Groq vision model call), codebase_qa, github_pr.
2026-05-15 23:54:33 +03:00
Bossiara13
f82427c7b6 feat: extensions module + 19 new commands (100 → 119)
Mirror of new packs from the rust fork. Self-contained `extensions.py` with
new do_* handlers, registered via 'set_speak' / 'set_clipboard_fn' callbacks
from main.py.

Wiring (main.py)
  - `import extensions` at top.
  - `extensions.set_speak(_speak)` + `extensions.set_clipboard_fn(_set_clipboard)`
    right after _set_clipboard is defined.
  - Added 15 new action-type dispatches in run_action.

extensions.py (new, ~360 lines)
  Action types:
    lock_workstation  → rundll32 user32.dll,LockWorkStation
    screenshot {mode} → clipboard via System.Windows.Forms.Clipboard.SetImage
                        OR file → ~/Pictures/Screenshots/jarvis-<ts>.png
    disk_free / disk_list → PowerShell Get-PSDrive
    coin_flip / uuid_gen / password_gen (length 6..64, copies to clipboard,
                        speaks length only — never echoes the password)
    ssl_check {domain} → PowerShell TCP+SslStream→X509Certificate2 expiry
    wifi_ssid → netsh wlan show interfaces parser (ru + en field names)
    public_ip → urllib request to api.ipify.org
    unit_convert {mode: length|weight|temperature|speed}
                  Russian-grammar pluralisation (фут/фута/футов).
    date_math {mode: days_until|day_of_week}
                  Recognises Новый год, Рождество, 8 марта, 9 мая + "до N <месяц>".
    echo_repeat / self_ping — debug commands
    interesting_fact → Groq LLM (requires GROQ_TOKEN)

commands.yaml — 19 new entries:
  lock_workstation, screenshot_clipboard, screenshot_file, disk_free, disk_list,
  coin_flip, password_gen, uuid_gen, ssl_check, wifi_ssid, public_ip,
  convert_length, convert_weight, convert_temperature, convert_speed,
  days_until, day_of_week, echo_repeat, self_ping, interesting_fact.

Tests: python syntax check + yaml.safe_load both pass. Total commands: 119.

NOT YET mirrored from rust (would require deeper rework):
  memory_pack / profile_switch / scheduler / macros / vision / codebase_qa
  / github_pr / llm_switch / llm_context / media_keys / mood_log / pomodoro
  / habit_nudge / daily_briefing / quick_search / diagnostics / sleep_timer
  / wikipedia (the rewritten one) / intro / mailto / generators (UUID, coin,
  password ARE included — others can follow).

The reason these weren't mirrored: they need new state files
(memory.json, profiles.json, schedule.json, macros.json) and background
threads (scheduler tick). Doable as a follow-up but out of scope here.
2026-05-15 23:51:13 +03:00