memory-fabric 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- memory_fabric/__init__.py +41 -0
- memory_fabric/cli.py +397 -0
- memory_fabric/contracts.py +212 -0
- memory_fabric/eval.py +1165 -0
- memory_fabric/frontmatter.py +120 -0
- memory_fabric/llm.py +372 -0
- memory_fabric/locking.py +95 -0
- memory_fabric/paths.py +130 -0
- memory_fabric/py.typed +1 -0
- memory_fabric/security.py +72 -0
- memory_fabric/server.py +562 -0
- memory_fabric/storage/__init__.py +61 -0
- memory_fabric/storage/_shared.py +228 -0
- memory_fabric/storage/consolidation.py +402 -0
- memory_fabric/storage/context.py +287 -0
- memory_fabric/storage/dream.py +410 -0
- memory_fabric/storage/finalize.py +430 -0
- memory_fabric/storage/journal.py +89 -0
- memory_fabric/storage/lifecycle.py +425 -0
- memory_fabric/storage/patch.py +165 -0
- memory_fabric/storage/search.py +120 -0
- memory_fabric/storage/sections.py +143 -0
- memory_fabric/storage/snapshots.py +61 -0
- memory_fabric/storage/store.py +279 -0
- memory_fabric/templates.py +220 -0
- memory_fabric/version.py +5 -0
- memory_fabric-0.4.1.dist-info/METADATA +432 -0
- memory_fabric-0.4.1.dist-info/RECORD +31 -0
- memory_fabric-0.4.1.dist-info/WHEEL +5 -0
- memory_fabric-0.4.1.dist-info/entry_points.txt +3 -0
- memory_fabric-0.4.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Memory Fabric public package API."""
|
|
2
|
+
|
|
3
|
+
from memory_fabric.version import __version__
|
|
4
|
+
from memory_fabric.storage import (
|
|
5
|
+
doctor,
|
|
6
|
+
dream,
|
|
7
|
+
initialize_memory_fabric,
|
|
8
|
+
keyword_search,
|
|
9
|
+
propose_memory_patch,
|
|
10
|
+
read_combined_context,
|
|
11
|
+
read_section,
|
|
12
|
+
rollback,
|
|
13
|
+
status,
|
|
14
|
+
write_local_memory,
|
|
15
|
+
prepare_dream_payload,
|
|
16
|
+
apply_dream_results,
|
|
17
|
+
)
|
|
18
|
+
from memory_fabric.eval import (
|
|
19
|
+
evaluate_dream_quality,
|
|
20
|
+
evaluate_memory_fabric,
|
|
21
|
+
evaluate_memory_quality,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"__version__",
|
|
26
|
+
"doctor",
|
|
27
|
+
"dream",
|
|
28
|
+
"evaluate_dream_quality",
|
|
29
|
+
"evaluate_memory_fabric",
|
|
30
|
+
"evaluate_memory_quality",
|
|
31
|
+
"initialize_memory_fabric",
|
|
32
|
+
"keyword_search",
|
|
33
|
+
"propose_memory_patch",
|
|
34
|
+
"read_combined_context",
|
|
35
|
+
"read_section",
|
|
36
|
+
"rollback",
|
|
37
|
+
"status",
|
|
38
|
+
"write_local_memory",
|
|
39
|
+
"prepare_dream_payload",
|
|
40
|
+
"apply_dream_results",
|
|
41
|
+
]
|
memory_fabric/cli.py
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"""Command line interface for Memory Fabric."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from memory_fabric.version import __version__
|
|
13
|
+
from memory_fabric.contracts import DreamEvalResult, EvalResult
|
|
14
|
+
from memory_fabric.eval import evaluate_dream_quality, evaluate_memory_fabric
|
|
15
|
+
from memory_fabric.paths import local_memory_dir
|
|
16
|
+
from memory_fabric.storage import (
|
|
17
|
+
delete_memory_store,
|
|
18
|
+
doctor,
|
|
19
|
+
dream,
|
|
20
|
+
initialize_memory_fabric,
|
|
21
|
+
keyword_search,
|
|
22
|
+
list_memory_store,
|
|
23
|
+
propose_memory_patch,
|
|
24
|
+
read_memory_store,
|
|
25
|
+
rollback,
|
|
26
|
+
status,
|
|
27
|
+
write_memory_store,
|
|
28
|
+
sync_agent_rules,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main(argv: list[str] | None = None) -> int:
|
|
33
|
+
parser = build_parser()
|
|
34
|
+
args = parser.parse_args(argv)
|
|
35
|
+
|
|
36
|
+
if getattr(args, "debug_llm", False):
|
|
37
|
+
import os
|
|
38
|
+
|
|
39
|
+
os.environ["MEMORY_FABRIC_LLM_DEBUG"] = "1"
|
|
40
|
+
|
|
41
|
+
cwd = str(Path(args.cwd).expanduser().resolve())
|
|
42
|
+
|
|
43
|
+
from memory_fabric.llm import load_env_from_cwd
|
|
44
|
+
|
|
45
|
+
load_env_from_cwd(cwd)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
if args.command == "init":
|
|
49
|
+
init_result = initialize_memory_fabric(
|
|
50
|
+
cwd,
|
|
51
|
+
install_hooks=args.install_hooks,
|
|
52
|
+
memory_prompt=args.memory_prompt,
|
|
53
|
+
)
|
|
54
|
+
_print_result(init_result, args.json)
|
|
55
|
+
return 0
|
|
56
|
+
if args.command == "status":
|
|
57
|
+
_print_result(status(cwd), args.json)
|
|
58
|
+
return 0
|
|
59
|
+
if args.command == "doctor":
|
|
60
|
+
doctor_result = doctor(cwd)
|
|
61
|
+
_print_result(doctor_result, args.json)
|
|
62
|
+
return 0 if doctor_result["ok"] else 1
|
|
63
|
+
if args.command == "dream":
|
|
64
|
+
dream_result = asyncio.run(
|
|
65
|
+
dream(
|
|
66
|
+
cwd,
|
|
67
|
+
mode=args.mode,
|
|
68
|
+
apply=args.apply,
|
|
69
|
+
llm_rewrite=args.llm_rewrite,
|
|
70
|
+
max_rewrite_tasks=args.max_rewrite_tasks,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
if args.eval and args.apply:
|
|
74
|
+
dream_result["evaluation"] = asyncio.run(
|
|
75
|
+
evaluate_dream_quality(
|
|
76
|
+
cwd,
|
|
77
|
+
snapshot=dream_result["snapshot"] or "latest",
|
|
78
|
+
save_report=True,
|
|
79
|
+
llm_review=args.llm_review,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
elif args.eval and not args.apply:
|
|
83
|
+
dream_result["warnings"].append(
|
|
84
|
+
"Dream evaluation requires --apply because candidate mode does not mutate live memory."
|
|
85
|
+
)
|
|
86
|
+
_print_result(dream_result, args.json)
|
|
87
|
+
return 0
|
|
88
|
+
if args.command == "eval":
|
|
89
|
+
eval_result: EvalResult | DreamEvalResult
|
|
90
|
+
if args.dream_snapshot:
|
|
91
|
+
eval_result = asyncio.run(
|
|
92
|
+
evaluate_dream_quality(
|
|
93
|
+
cwd,
|
|
94
|
+
snapshot=args.dream_snapshot,
|
|
95
|
+
save_report=not args.no_save,
|
|
96
|
+
llm_review=args.llm_review,
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
eval_result = asyncio.run(
|
|
101
|
+
evaluate_memory_fabric(
|
|
102
|
+
cwd,
|
|
103
|
+
save_report=not args.no_save,
|
|
104
|
+
llm_review=args.llm_review,
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
_print_result(eval_result, args.json)
|
|
108
|
+
return 0 if eval_result["status"] != "fail" else 1
|
|
109
|
+
if args.command == "query":
|
|
110
|
+
query_result = keyword_search(cwd, args.query, max_results=args.max_results)
|
|
111
|
+
_print_result(query_result, args.json)
|
|
112
|
+
return 0
|
|
113
|
+
if args.command == "sync-agents":
|
|
114
|
+
sync_result = sync_agent_rules(cwd)
|
|
115
|
+
_print_result(sync_result, args.json)
|
|
116
|
+
return 0 if sync_result.get("success") else 1
|
|
117
|
+
if args.command == "sync-global":
|
|
118
|
+
memory_dir = local_memory_dir(cwd)
|
|
119
|
+
if not args.json and sys.stdin.isatty() and memory_dir.exists():
|
|
120
|
+
from memory_fabric.paths import global_memory_dir
|
|
121
|
+
from memory_fabric.storage import (
|
|
122
|
+
_iter_markdown_files,
|
|
123
|
+
_is_ignored_local_memory_path,
|
|
124
|
+
)
|
|
125
|
+
from memory_fabric.locking import locked_file
|
|
126
|
+
import shutil
|
|
127
|
+
|
|
128
|
+
promoted_count = 0
|
|
129
|
+
local_files = [
|
|
130
|
+
path
|
|
131
|
+
for path in _iter_markdown_files(memory_dir)
|
|
132
|
+
if path.name != "index.md"
|
|
133
|
+
and not _is_ignored_local_memory_path(memory_dir, path)
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
if not local_files:
|
|
137
|
+
print("No local memory files found to promote.")
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
print("Interactive Global Memory Sync:")
|
|
141
|
+
print("===============================")
|
|
142
|
+
for path in local_files:
|
|
143
|
+
rel_name = path.name
|
|
144
|
+
choice = (
|
|
145
|
+
input(f"Promote local/{rel_name} to global/{rel_name}? [y/N]: ")
|
|
146
|
+
.strip()
|
|
147
|
+
.lower()
|
|
148
|
+
)
|
|
149
|
+
if choice in {"y", "yes"}:
|
|
150
|
+
target_dir = global_memory_dir()
|
|
151
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
152
|
+
target_path = target_dir / rel_name
|
|
153
|
+
|
|
154
|
+
action = "copy"
|
|
155
|
+
if target_path.exists():
|
|
156
|
+
overwrite = (
|
|
157
|
+
input(f" global/{rel_name} already exists. Overwrite? [y/N]: ")
|
|
158
|
+
.strip()
|
|
159
|
+
.lower()
|
|
160
|
+
)
|
|
161
|
+
if overwrite in {"y", "yes"}:
|
|
162
|
+
action = "copy"
|
|
163
|
+
else:
|
|
164
|
+
append = (
|
|
165
|
+
input(f" Append content to global/{rel_name} instead? [y/N]: ")
|
|
166
|
+
.strip()
|
|
167
|
+
.lower()
|
|
168
|
+
)
|
|
169
|
+
if append in {"y", "yes"}:
|
|
170
|
+
action = "append"
|
|
171
|
+
else:
|
|
172
|
+
action = "skip"
|
|
173
|
+
|
|
174
|
+
if action == "copy":
|
|
175
|
+
with locked_file(target_path):
|
|
176
|
+
shutil.copy2(path, target_path)
|
|
177
|
+
print(f" -> Promoted to {target_path}")
|
|
178
|
+
promoted_count += 1
|
|
179
|
+
elif action == "append":
|
|
180
|
+
from memory_fabric.frontmatter import (
|
|
181
|
+
parse_frontmatter,
|
|
182
|
+
dump_frontmatter,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
with locked_file(target_path):
|
|
186
|
+
local_meta, local_body = parse_frontmatter(
|
|
187
|
+
path.read_text(encoding="utf-8")
|
|
188
|
+
)
|
|
189
|
+
global_meta, global_body = parse_frontmatter(
|
|
190
|
+
target_path.read_text(encoding="utf-8")
|
|
191
|
+
)
|
|
192
|
+
new_body = global_body.rstrip() + "\n\n" + local_body.lstrip()
|
|
193
|
+
global_meta["last_updated"] = local_meta.get("last_updated", "")
|
|
194
|
+
target_path.write_text(
|
|
195
|
+
dump_frontmatter(global_meta, new_body), encoding="utf-8"
|
|
196
|
+
)
|
|
197
|
+
print(f" -> Appended to {target_path}")
|
|
198
|
+
promoted_count += 1
|
|
199
|
+
else:
|
|
200
|
+
print(f" -> Skipped global/{rel_name}")
|
|
201
|
+
|
|
202
|
+
print(f"\nSync complete. Promoted {promoted_count} section(s) to global memory.")
|
|
203
|
+
return 0
|
|
204
|
+
else:
|
|
205
|
+
preview = propose_memory_patch(
|
|
206
|
+
cwd,
|
|
207
|
+
"Review local memory for durable rules before manually promoting them to global memory.",
|
|
208
|
+
)
|
|
209
|
+
_print_result(
|
|
210
|
+
{
|
|
211
|
+
"message": "Global sync is preview-only in v1 when non-interactive or JSON requested. Review local memory before promoting.",
|
|
212
|
+
"preview": preview,
|
|
213
|
+
},
|
|
214
|
+
args.json,
|
|
215
|
+
)
|
|
216
|
+
return 0
|
|
217
|
+
if args.command == "rollback":
|
|
218
|
+
rollback_result = rollback(cwd, args.to)
|
|
219
|
+
_print_result(rollback_result, args.json)
|
|
220
|
+
return 0
|
|
221
|
+
if args.command == "store":
|
|
222
|
+
store_action = getattr(args, "store_action", None)
|
|
223
|
+
if store_action == "write":
|
|
224
|
+
tag_list = (
|
|
225
|
+
[t.strip() for t in args.tags.split(",") if t.strip()] if args.tags else None
|
|
226
|
+
)
|
|
227
|
+
store_write_result = write_memory_store(
|
|
228
|
+
cwd,
|
|
229
|
+
store_path=args.store_path,
|
|
230
|
+
content=args.content,
|
|
231
|
+
title=args.title,
|
|
232
|
+
tags=tag_list,
|
|
233
|
+
priority=args.priority,
|
|
234
|
+
mode=args.mode,
|
|
235
|
+
)
|
|
236
|
+
_print_result(store_write_result, args.json)
|
|
237
|
+
return 0
|
|
238
|
+
elif store_action == "read":
|
|
239
|
+
store_read_result = read_memory_store(cwd, store_path=args.store_path)
|
|
240
|
+
_print_result(store_read_result, args.json)
|
|
241
|
+
return 0
|
|
242
|
+
elif store_action == "list":
|
|
243
|
+
tag_list = (
|
|
244
|
+
[t.strip() for t in args.tags.split(",") if t.strip()] if args.tags else None
|
|
245
|
+
)
|
|
246
|
+
store_list_result = list_memory_store(
|
|
247
|
+
cwd,
|
|
248
|
+
prefix=args.prefix,
|
|
249
|
+
tags=tag_list,
|
|
250
|
+
max_results=args.max_results,
|
|
251
|
+
)
|
|
252
|
+
_print_result(store_list_result, args.json)
|
|
253
|
+
return 0
|
|
254
|
+
elif store_action == "delete":
|
|
255
|
+
store_delete_result = delete_memory_store(cwd, store_path=args.store_path)
|
|
256
|
+
_print_result(store_delete_result, args.json)
|
|
257
|
+
return 0
|
|
258
|
+
else:
|
|
259
|
+
parser.parse_args(["store", "--help"])
|
|
260
|
+
return 1
|
|
261
|
+
except Exception as exc: # noqa: BLE001 - CLI should surface all operational failures.
|
|
262
|
+
print(f"ai-memory: {exc}", file=sys.stderr)
|
|
263
|
+
return 1
|
|
264
|
+
|
|
265
|
+
parser.print_help()
|
|
266
|
+
return 1
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
270
|
+
parser = argparse.ArgumentParser(prog="ai-memory", description="Memory Fabric CLI")
|
|
271
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
272
|
+
parser.add_argument("--cwd", default=".", help="Project working directory")
|
|
273
|
+
parser.add_argument("--json", action="store_true", help="Print JSON output")
|
|
274
|
+
parser.add_argument(
|
|
275
|
+
"--debug-llm", action="store_true", help="Enable LLM prompt and response logging"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
279
|
+
init_parser = subparsers.add_parser("init", help="Create .ai-memory scaffolding")
|
|
280
|
+
init_parser.add_argument(
|
|
281
|
+
"--install-hooks", action="store_true", help="Install opt-in git hooks"
|
|
282
|
+
)
|
|
283
|
+
init_parser.add_argument(
|
|
284
|
+
"--memory-prompt", default=None, help="Steering instructions for agent memory capture"
|
|
285
|
+
)
|
|
286
|
+
subparsers.add_parser("status", help="Show memory status")
|
|
287
|
+
subparsers.add_parser("doctor", help="Validate memory files and environment")
|
|
288
|
+
|
|
289
|
+
dream_parser = subparsers.add_parser("dream", help="Run local memory maintenance")
|
|
290
|
+
dream_parser.add_argument("--mode", choices=["light", "deep"], default="light")
|
|
291
|
+
dream_parser.add_argument(
|
|
292
|
+
"--apply", action="store_true", help="Apply candidate changes to live .ai-memory files"
|
|
293
|
+
)
|
|
294
|
+
dream_parser.add_argument(
|
|
295
|
+
"--llm-rewrite",
|
|
296
|
+
action="store_true",
|
|
297
|
+
help="Generate agent-assisted rewrite tasks from Dreaming output",
|
|
298
|
+
)
|
|
299
|
+
dream_parser.add_argument("--max-rewrite-tasks", type=int, default=5)
|
|
300
|
+
dream_parser.add_argument(
|
|
301
|
+
"--eval", action="store_true", help="Evaluate quality before and after Dreaming"
|
|
302
|
+
)
|
|
303
|
+
dream_parser.add_argument(
|
|
304
|
+
"--llm-review", action="store_true", help="Add optional qualitative LLM review notes"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
eval_parser = subparsers.add_parser("eval", help="Evaluate memory and Dreaming quality")
|
|
308
|
+
eval_parser.add_argument(
|
|
309
|
+
"--llm-review", action="store_true", help="Add optional qualitative LLM review notes"
|
|
310
|
+
)
|
|
311
|
+
eval_parser.add_argument(
|
|
312
|
+
"--dream",
|
|
313
|
+
dest="dream_snapshot",
|
|
314
|
+
help="Evaluate a Dreaming run against a snapshot name or latest",
|
|
315
|
+
)
|
|
316
|
+
eval_parser.add_argument(
|
|
317
|
+
"--no-save", action="store_true", help="Do not save eval reports under .ai-memory/evals"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
query_parser = subparsers.add_parser("query", help="Search memory")
|
|
321
|
+
query_parser.add_argument("query")
|
|
322
|
+
query_parser.add_argument("--max-results", type=int, default=10)
|
|
323
|
+
|
|
324
|
+
subparsers.add_parser(
|
|
325
|
+
"sync-agents",
|
|
326
|
+
help="Synchronize agent instruction files using AGENTS.md as the source of truth",
|
|
327
|
+
)
|
|
328
|
+
subparsers.add_parser("sync-global", help="Preview local-to-global promotion")
|
|
329
|
+
|
|
330
|
+
rollback_parser = subparsers.add_parser("rollback", help="Restore local memory from a snapshot")
|
|
331
|
+
rollback_parser.add_argument("--to", required=True, help="Snapshot name")
|
|
332
|
+
|
|
333
|
+
store_parser = subparsers.add_parser("store", help="Memory store operations")
|
|
334
|
+
store_subs = store_parser.add_subparsers(dest="store_action")
|
|
335
|
+
|
|
336
|
+
store_write = store_subs.add_parser("write", help="Write a store file")
|
|
337
|
+
store_write.add_argument(
|
|
338
|
+
"store_path", help="Semantic path (e.g. architecture/decisions/auth-service)"
|
|
339
|
+
)
|
|
340
|
+
store_write.add_argument("--content", required=True, help="Content to write")
|
|
341
|
+
store_write.add_argument("--title", default="", help="Title for the memory")
|
|
342
|
+
store_write.add_argument("--tags", default="", help="Comma-separated tags")
|
|
343
|
+
store_write.add_argument("--priority", choices=["high", "medium", "low"], default="medium")
|
|
344
|
+
store_write.add_argument("--mode", choices=["replace", "append"], default="replace")
|
|
345
|
+
|
|
346
|
+
store_read = store_subs.add_parser("read", help="Read a store file")
|
|
347
|
+
store_read.add_argument("store_path", help="Semantic path")
|
|
348
|
+
|
|
349
|
+
store_list = store_subs.add_parser("list", help="List store files")
|
|
350
|
+
store_list.add_argument("--prefix", default="", help="Filter by path prefix")
|
|
351
|
+
store_list.add_argument("--tags", default="", help="Comma-separated tags to filter by")
|
|
352
|
+
store_list.add_argument("--max-results", type=int, default=50)
|
|
353
|
+
|
|
354
|
+
store_delete = store_subs.add_parser("delete", help="Delete a store file")
|
|
355
|
+
store_delete.add_argument("store_path", help="Semantic path")
|
|
356
|
+
|
|
357
|
+
return parser
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _print_result(result: Any, as_json: bool) -> None:
|
|
361
|
+
if as_json:
|
|
362
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
363
|
+
return
|
|
364
|
+
|
|
365
|
+
if isinstance(result, list):
|
|
366
|
+
for item in result:
|
|
367
|
+
print(_format_item(item))
|
|
368
|
+
return
|
|
369
|
+
|
|
370
|
+
if isinstance(result, dict):
|
|
371
|
+
for key, value in result.items():
|
|
372
|
+
print(f"{key}: {_format_value(value)}")
|
|
373
|
+
return
|
|
374
|
+
|
|
375
|
+
print(result)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _format_item(item: Any) -> str:
|
|
379
|
+
if isinstance(item, dict):
|
|
380
|
+
if {"path", "line", "snippet"}.issubset(item):
|
|
381
|
+
return f"{item['path']}:{item['line']}: {item['snippet']}"
|
|
382
|
+
return json.dumps(item, ensure_ascii=False)
|
|
383
|
+
return str(item)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _format_value(value: Any) -> str:
|
|
387
|
+
if isinstance(value, list):
|
|
388
|
+
if not value:
|
|
389
|
+
return "[]"
|
|
390
|
+
return "\n - " + "\n - ".join(str(item) for item in value)
|
|
391
|
+
if isinstance(value, dict):
|
|
392
|
+
return "\n" + json.dumps(value, indent=2, ensure_ascii=False)
|
|
393
|
+
return str(value)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
if __name__ == "__main__":
|
|
397
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Typed result contracts for Memory Fabric public APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
|
|
8
|
+
# pydantic (pulled in by the optional `mcp` extra, to build tool schemas from these
|
|
9
|
+
# TypedDicts) rejects typing.TypedDict on Python < 3.12 in favor of typing_extensions'
|
|
10
|
+
# version. typing_extensions is only guaranteed present when `mcp` is installed (it's a
|
|
11
|
+
# transitive pydantic dependency) — core-only installs on 3.11 fall back to stdlib, which
|
|
12
|
+
# is fine there since pydantic never touches these types without the `mcp` extra.
|
|
13
|
+
if sys.version_info >= (3, 12):
|
|
14
|
+
from typing import NotRequired, TypedDict
|
|
15
|
+
else:
|
|
16
|
+
try:
|
|
17
|
+
from typing_extensions import NotRequired, TypedDict
|
|
18
|
+
except ImportError:
|
|
19
|
+
from typing import NotRequired, TypedDict
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Priority = Literal["high", "medium", "low"]
|
|
23
|
+
WriteMode = Literal["append", "replace"]
|
|
24
|
+
DreamMode = Literal["light", "deep"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InitResult(TypedDict):
|
|
28
|
+
created: bool
|
|
29
|
+
memory_dir: str
|
|
30
|
+
files_created: list[str]
|
|
31
|
+
warnings: list[str]
|
|
32
|
+
resource_uris: NotRequired[list[str]]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ContextBundle(TypedDict):
|
|
36
|
+
text: str
|
|
37
|
+
included_sections: list[str]
|
|
38
|
+
omitted_sections: list[str]
|
|
39
|
+
token_budget: int
|
|
40
|
+
estimated_tokens: int
|
|
41
|
+
warnings: list[str]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MemorySection(TypedDict):
|
|
45
|
+
section: str
|
|
46
|
+
path: str
|
|
47
|
+
text: str
|
|
48
|
+
metadata: dict[str, Any]
|
|
49
|
+
truncated: bool
|
|
50
|
+
warnings: list[str]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SearchResult(TypedDict):
|
|
54
|
+
section: str
|
|
55
|
+
path: str
|
|
56
|
+
line: int
|
|
57
|
+
snippet: str
|
|
58
|
+
backend: NotRequired[str] # 'ripgrep' | 'python' — which search backend was used
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class WriteResult(TypedDict):
|
|
62
|
+
changed: bool
|
|
63
|
+
path: str
|
|
64
|
+
redactions: int
|
|
65
|
+
warnings: list[str]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PatchPreview(TypedDict):
|
|
69
|
+
patch: str
|
|
70
|
+
affected_files: list[str]
|
|
71
|
+
redactions: int
|
|
72
|
+
warnings: list[str]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class EpisodicJournalResult(TypedDict):
|
|
76
|
+
"""Result returned by write_session_journal_tool."""
|
|
77
|
+
|
|
78
|
+
changed: bool
|
|
79
|
+
store_path: str
|
|
80
|
+
path: str
|
|
81
|
+
date: str
|
|
82
|
+
redactions: int
|
|
83
|
+
warnings: list[str]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class StoreWriteResult(TypedDict):
|
|
87
|
+
changed: bool
|
|
88
|
+
path: str
|
|
89
|
+
store_path: str
|
|
90
|
+
redactions: int
|
|
91
|
+
warnings: list[str]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class StoreReadResult(TypedDict):
|
|
95
|
+
store_path: str
|
|
96
|
+
path: str
|
|
97
|
+
text: str
|
|
98
|
+
metadata: dict[str, Any]
|
|
99
|
+
truncated: bool
|
|
100
|
+
warnings: list[str]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class StoreEntry(TypedDict):
|
|
104
|
+
store_path: str
|
|
105
|
+
path: str
|
|
106
|
+
summary: str
|
|
107
|
+
priority: str
|
|
108
|
+
tags: list[str]
|
|
109
|
+
last_updated: str
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class StoreListResult(TypedDict):
|
|
113
|
+
entries: list[StoreEntry]
|
|
114
|
+
total: int
|
|
115
|
+
warnings: list[str]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class DoctorResult(TypedDict):
|
|
119
|
+
ok: bool
|
|
120
|
+
errors: list[str]
|
|
121
|
+
warnings: list[str]
|
|
122
|
+
checked_files: list[str]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class StatusResult(TypedDict):
|
|
126
|
+
cwd: str
|
|
127
|
+
memory_dir: str
|
|
128
|
+
memory_exists: bool
|
|
129
|
+
global_dir: str
|
|
130
|
+
provider_configured: bool
|
|
131
|
+
local_files: list[str]
|
|
132
|
+
memory_sizes: dict[str, dict[str, int]]
|
|
133
|
+
version: str
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class DreamResult(TypedDict):
|
|
137
|
+
changed: bool
|
|
138
|
+
snapshot: str | None
|
|
139
|
+
warnings: list[str]
|
|
140
|
+
checked_files: list[str]
|
|
141
|
+
candidate_store: str
|
|
142
|
+
patch_preview: str
|
|
143
|
+
affected_files: list[str]
|
|
144
|
+
consolidation: "DreamConsolidation"
|
|
145
|
+
rewrite_tasks: list["DreamRewriteTask"]
|
|
146
|
+
apply_required: bool
|
|
147
|
+
redactions: int
|
|
148
|
+
evaluation: NotRequired["DreamEvalResult"]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class DreamConsolidation(TypedDict):
|
|
152
|
+
duplicates_found: int
|
|
153
|
+
lines_removed: int
|
|
154
|
+
files_touched: list[str]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class DreamRewriteTask(TypedDict):
|
|
158
|
+
section: str
|
|
159
|
+
reason: str
|
|
160
|
+
instruction: str
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class EvalCheck(TypedDict):
|
|
164
|
+
id: str
|
|
165
|
+
status: Literal["pass", "warn", "fail"]
|
|
166
|
+
severity: Literal["info", "low", "medium", "high"]
|
|
167
|
+
message: str
|
|
168
|
+
recommendation: str
|
|
169
|
+
command: NotRequired[str]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class EvalCategory(TypedDict):
|
|
173
|
+
name: str
|
|
174
|
+
score: int
|
|
175
|
+
status: Literal["pass", "warn", "fail"]
|
|
176
|
+
weight: int
|
|
177
|
+
checks: list[EvalCheck]
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class EvalResult(TypedDict):
|
|
181
|
+
kind: Literal["memory"]
|
|
182
|
+
generated_at: str
|
|
183
|
+
cwd: str
|
|
184
|
+
memory_dir: str
|
|
185
|
+
score: int
|
|
186
|
+
status: Literal["pass", "warn", "fail"]
|
|
187
|
+
categories: list[EvalCategory]
|
|
188
|
+
recommendations: list[str]
|
|
189
|
+
report_paths: list[str]
|
|
190
|
+
warnings: list[str]
|
|
191
|
+
llm_notes: list[str]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class DreamEvalResult(TypedDict):
|
|
195
|
+
kind: Literal["dream"]
|
|
196
|
+
generated_at: str
|
|
197
|
+
cwd: str
|
|
198
|
+
memory_dir: str
|
|
199
|
+
baseline_snapshot: str
|
|
200
|
+
before_score: int
|
|
201
|
+
after_score: int
|
|
202
|
+
delta: int
|
|
203
|
+
score: int
|
|
204
|
+
status: Literal["pass", "warn", "fail"]
|
|
205
|
+
changed_files: list[str]
|
|
206
|
+
improvements: list[str]
|
|
207
|
+
regressions: list[str]
|
|
208
|
+
categories: list[EvalCategory]
|
|
209
|
+
recommendations: list[str]
|
|
210
|
+
report_paths: list[str]
|
|
211
|
+
warnings: list[str]
|
|
212
|
+
llm_notes: list[str]
|