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,44 @@
# Cooking timer — recognises common dishes and starts a preset timer.
# "поставь чайник" → 4 min, "омлет" → 5 min, "макароны" → 10 min, etc.
# The phrase decides the duration via a built-in table in the lua script.
[[commands]]
id = "cooking.timer"
type = "lua"
script = "timer.lua"
sandbox = "full"
timeout = 5000
[commands.phrases]
ru = [
"поставь чайник",
"поставил чайник",
"варю яйца",
"варю макароны",
"варю пасту",
"жарю омлет",
"жарю яичницу",
"варю кашу",
"варю рис",
"варю гречку",
"варю картошку",
"запекаю курицу",
"пеку пиццу",
"завариваю чай",
"завариваю кофе",
"завариваю френч-пресс",
]
en = [
"boiling water",
"boiling eggs",
"boiling pasta",
"cooking pasta",
"frying omelette",
"frying eggs",
"cooking rice",
"cooking porridge",
"baking pizza",
"brewing tea",
"brewing coffee",
"french press",
]

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))