feat: Wave 6 — focus mode, cooking timer, memory admin
Some checks are pending
Rust CI / cargo test (jarvis-core) (push) Waiting to run
Rust CI / cargo clippy (push) Waiting to run
Rust CI / cargo check (workspace) (push) Waiting to run

Focus mode (`focus/`):
- focus.start ("режим фокуса" / "focus mode"): three side-effects in one
  voice trigger:
    1. switch profile to "work" (silences fun/banter)
    2. enable Windows Focus Assist via registry tweak (QuietHours key)
    3. schedule a stretch reminder in 50 minutes through the persistent
       scheduler — so it survives daemon restart
- focus.stop: undoes all three, including cancelling pending stretch
  reminders by matching their scheduler name. Best-effort: any one
  failing doesn't block the others.

Cooking timer (`cooking/`):
- Recognises 16+ dish names (чай, кофе, омлет, яйца, макароны, паста,
  рис, гречка, картошка, курица, пицца, etc.) and starts a preset-
  duration scheduler reminder ("Сэр, чай готов.").
- Single Lua script, table-driven — adding a recipe is one line.
- EN phrases for the same dishes ("boiling pasta", "frying eggs", ...).

Memory admin (`memory_admin/`):
- memory_admin.count → speak fact count with full RU pluralisation.
- memory_admin.list → speak 5 most-recent + toast the rest.
- memory_admin.wipe → ONLY fires on "точно забудь все" prefix (not bare
  "забудь все") so we can't disaster-wipe by accident.
- Adds `long_term_memory::clear_all() -> usize` returning removed count
  + Lua binding `jarvis.memory.clear_all()`.
- One extra unit test for `clear_all`.

Tests: 139 → 140 (+1). Pack count: 92 → 95.
This commit is contained in:
Bossiara13 2026-05-16 14:39:55 +03:00
parent df799b6cd2
commit 8f46bd735a
11 changed files with 467 additions and 0 deletions

View file

@ -0,0 +1,84 @@
-- Cooking timer — recognise the dish in the phrase and schedule a "ready!"
-- reminder for the right number of minutes. Falls through to a generic
-- "couldn't recognise" reply if nothing matched.
--
-- The duration table is opinionated but covers the common Russian kitchen
-- staples. Power users can override via `recipes.txt` (TODO future) — for
-- now, edit this file or use the generic `reminders/set` pack.
local lang = jarvis.context.language
local phrase = (jarvis.context.phrase or ""):lower()
-- pattern -> { minutes, ru_label, en_label }
local recipes = {
{ "чайник", 4, "чайник", "kettle" },
{ "boiling water",4, "вода", "boiling water" },
{ "французск", 4, "френч-пресс", "french press" },
{ "french press", 4, "френч-пресс", "french press" },
{ "чай", 5, "чай", "tea" },
{ "brewing tea", 5, "чай", "tea" },
{ "омлет", 5, "омлет", "omelette" },
{ "омлеt", 5, "омлет", "omelette" }, -- mis-STT guard
{ "яичниц", 5, "яичница", "fried eggs" },
{ "frying eggs", 5, "яичница", "fried eggs" },
{ "frying omelet",5, "омлет", "omelette" },
{ "кофе", 5, "кофе", "coffee" },
{ "brewing coff", 5, "кофе", "coffee" },
{ "яйц", 8, "яйца", "eggs" },
{ "boiling eggs", 8, "яйца", "eggs" },
{ "макарон", 10, "макароны", "pasta" },
{ "паст", 10, "паста", "pasta" },
{ "cooking pasta",10, "паста", "pasta" },
{ "boiling pasta",10, "паста", "pasta" },
{ "рис", 18, "рис", "rice" },
{ "cooking rice", 18, "рис", "rice" },
{ "греч", 20, "гречка", "buckwheat" },
{ "каш", 20, "каша", "porridge" },
{ "porridge", 20, "каша", "porridge" },
{ "картош", 22, "картошка", "potatoes" },
{ "пицц", 15, "пицца", "pizza" },
{ "baking pizza", 15, "пицца", "pizza" },
{ "куриц", 35, "курица", "chicken" },
{ "chicken", 35, "курица", "chicken" },
}
local matched_minutes, label_ru, label_en = nil, nil, nil
for _, r in ipairs(recipes) do
if phrase:find(r[1], 1, true) then
matched_minutes, label_ru, label_en = r[2], r[3], r[4]
break
end
end
if not matched_minutes then
return jarvis.cmd.not_found(lang == "ru"
and "Не понял, что ты готовишь, сэр."
or "I didn't catch what you're cooking, sir.")
end
local label = (lang == "ru") and label_ru or label_en
local speak_text = (lang == "ru" and "Сэр, " or "Sir, ")
.. label .. (lang == "ru" and " готов." or " is ready.")
local ok, err = pcall(function()
jarvis.scheduler.add({
name = (lang == "ru" and "Кухня: " or "Cooking: ") .. label,
schedule = "in " .. tostring(matched_minutes) .. " minutes",
action = { type = "speak", text = speak_text },
})
end)
if not ok then
jarvis.log("error", "cooking timer: scheduler.add failed: " .. tostring(err))
return jarvis.cmd.error(lang == "ru" and "Не получилось поставить таймер."
or "Couldn't set timer.")
end
jarvis.system.notify(
lang == "ru" and "Таймер" or "Timer",
string.format("%s — %d мин", label, matched_minutes)
)
return jarvis.cmd.ok(string.format(lang == "ru"
and "Хорошо, напомню через %d минут когда %s будет готов."
or "Right — I'll remind you in %d minutes when %s is ready.",
matched_minutes, label))