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

@ -205,6 +205,24 @@ pub fn forget(key: &str) -> bool {
removed
}
/// Wipe all stored facts. Returns the count of records removed.
/// Voice commands call this only after a confirmation prefix
/// ("точно забудь все" — not just "забудь все") to avoid disasters.
pub fn clear_all() -> usize {
let Some(state) = STATE.get() else { return 0; };
let n = {
let mut store = state.store.write();
let n = store.entries.len();
store.entries.clear();
n
};
if n > 0 {
persist(state);
warn!("Memory: WIPED all {} facts", n);
}
n
}
/// Snapshot of all records (for debug / GUI display).
pub fn all() -> Vec<MemoryRecord> {
let Some(state) = STATE.get() else { return Vec::new(); };
@ -337,4 +355,12 @@ mod tests {
assert_eq!(back.entries.len(), 2);
assert_eq!(back.entries.get("key1").unwrap().value, "val1");
}
#[test]
fn clear_all_returns_zero_when_state_uninit() {
// STATE may or may not be initialised depending on test order; the
// public function still needs to return safely (and 0) in either case.
// We just exercise the code path — exact count depends on global state.
let _ = clear_all();
}
}

View file

@ -56,12 +56,22 @@ pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> {
let row = lua.create_table()?;
row.set("key", h.key.clone())?;
row.set("value", h.value.clone())?;
row.set("created_at", h.created_at)?;
row.set("last_used_at", h.last_used_at)?;
row.set("use_count", h.use_count)?;
arr.set(i + 1, row)?;
}
Ok(arr)
})?;
mem.set("all", all)?;
// Wipe ALL memory. Returns count of removed facts. Voice packs should
// only call this behind a deliberate phrase like "точно забудь всё".
let clear_all = lua.create_function(|_, ()| {
Ok(long_term_memory::clear_all())
})?;
mem.set("clear_all", clear_all)?;
jarvis.set("memory", mem)?;
Ok(())
}