Coverage for src / lilbee / cli / commands / memory.py: 100%
70 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-06-28 01:01 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-06-28 01:01 +0000
1"""Local memory commands: add, list, recall, remove."""
3from __future__ import annotations
5from pathlib import Path
7import typer
8from rich.table import Table
10from lilbee.app.memory import (
11 MEMORY_DISABLED_HINT,
12 forget,
13 list_memories,
14 memory_enabled,
15 recall,
16 remember,
17)
18from lilbee.cli.app import (
19 apply_overrides,
20 console,
21 data_dir_option,
22 global_option,
23)
24from lilbee.cli.helpers import json_output
25from lilbee.core.config import cfg
26from lilbee.data.store import MemoryKind
28memory_app = typer.Typer(help="Manage your local long-term memory (off unless enabled).")
30_ID_PREVIEW_CHARS = 8
33def _disabled() -> None:
34 """Report that memory is off, as JSON or a console line."""
35 if cfg.json_mode:
36 json_output({"error": MEMORY_DISABLED_HINT})
37 else:
38 console.print(MEMORY_DISABLED_HINT)
41@memory_app.command(name="add")
42def memory_add(
43 text: str,
44 preference: bool = typer.Option(
45 False, "--preference", "-p", help="Store as an always-recalled preference."
46 ),
47 shared: bool = typer.Option(False, "--shared", help="Also expose this memory to agents."),
48 data_dir: Path | None = data_dir_option,
49 use_global: bool = global_option,
50) -> None:
51 """Remember a fact (or preference) in your local memory."""
52 apply_overrides(data_dir=data_dir, use_global=use_global)
53 if not memory_enabled():
54 _disabled()
55 return
56 kind = MemoryKind.PREFERENCE if preference else MemoryKind.FACT
57 memory_id = remember(text, kind=kind, shared=shared)
58 if cfg.json_mode:
59 json_output({"id": memory_id, "kind": kind.value})
60 return
61 console.print(f"Remembered ({kind.value}).")
64@memory_app.command(name="list")
65def memory_list_cmd(
66 data_dir: Path | None = data_dir_option,
67 use_global: bool = global_option,
68) -> None:
69 """List your stored memories."""
70 apply_overrides(data_dir=data_dir, use_global=use_global)
71 if not memory_enabled():
72 _disabled()
73 return
74 memories = list_memories()
75 if cfg.json_mode:
76 json_output(
77 {
78 "memories": [
79 {
80 "id": m.id,
81 "kind": m.kind.value,
82 "shared": m.shared,
83 "text": m.text,
84 }
85 for m in memories
86 ]
87 }
88 )
89 return
90 if not memories:
91 console.print("No memories stored.")
92 return
93 table = Table("id", "kind", "shared", "text")
94 for m in memories:
95 table.add_row(m.id[:_ID_PREVIEW_CHARS], m.kind.value, "yes" if m.shared else "no", m.text)
96 console.print(table)
99@memory_app.command(name="recall")
100def memory_recall_cmd(
101 query: str,
102 data_dir: Path | None = data_dir_option,
103 use_global: bool = global_option,
104) -> None:
105 """Recall facts relevant to a query."""
106 apply_overrides(data_dir=data_dir, use_global=use_global)
107 if not memory_enabled():
108 _disabled()
109 return
110 memories = recall(query)
111 if cfg.json_mode:
112 json_output({"memories": [{"id": m.id, "text": m.text} for m in memories]})
113 return
114 if not memories:
115 console.print("No relevant memories.")
116 return
117 for m in memories:
118 console.print(f"- {m.text}")
121@memory_app.command(name="remove")
122def memory_remove(
123 memory_id: str,
124 data_dir: Path | None = data_dir_option,
125 use_global: bool = global_option,
126) -> None:
127 """Delete a memory by id."""
128 apply_overrides(data_dir=data_dir, use_global=use_global)
129 if not memory_enabled():
130 _disabled()
131 return
132 forget(memory_id)
133 if cfg.json_mode:
134 json_output({"removed": memory_id})
135 return
136 console.print(f"Removed {memory_id}.")