memoryledger 0.1.0__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.
- memoryledger/__init__.py +8 -0
- memoryledger/_version.py +24 -0
- memoryledger/adopt.py +173 -0
- memoryledger/cli.py +700 -0
- memoryledger/errors.py +18 -0
- memoryledger/evidence_scan.py +218 -0
- memoryledger/guardrails.py +157 -0
- memoryledger/intake.py +111 -0
- memoryledger/launcher.py +7 -0
- memoryledger/models.py +172 -0
- memoryledger/py.typed +0 -0
- memoryledger/render.py +313 -0
- memoryledger/review.py +19 -0
- memoryledger/run_import.py +136 -0
- memoryledger/storage.py +463 -0
- memoryledger/templates.py +161 -0
- memoryledger-0.1.0.dist-info/METADATA +67 -0
- memoryledger-0.1.0.dist-info/RECORD +24 -0
- memoryledger-0.1.0.dist-info/WHEEL +5 -0
- memoryledger-0.1.0.dist-info/entry_points.txt +3 -0
- memoryledger-0.1.0.dist-info/licenses/LICENSE +201 -0
- memoryledger-0.1.0.dist-info/scm_file_list.json +147 -0
- memoryledger-0.1.0.dist-info/scm_version.json +8 -0
- memoryledger-0.1.0.dist-info/top_level.txt +1 -0
memoryledger/__init__.py
ADDED
memoryledger/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'ga711d99f3'
|
memoryledger/adopt.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import shutil
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from ledgercore.atomic import atomic_write_text
|
|
9
|
+
|
|
10
|
+
from .errors import MemoryledgerError
|
|
11
|
+
from .guardrails import safe_to_replace
|
|
12
|
+
from .models import GENERATED_MARKER, EvidenceRef
|
|
13
|
+
from .render import render_all
|
|
14
|
+
from .review import transition
|
|
15
|
+
from .storage import Store
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class AdoptionProposal:
|
|
20
|
+
title: str
|
|
21
|
+
content: str
|
|
22
|
+
kind: str
|
|
23
|
+
origin: str
|
|
24
|
+
source_hash: str
|
|
25
|
+
|
|
26
|
+
def to_dict(self) -> dict[str, str]:
|
|
27
|
+
return {
|
|
28
|
+
"title": self.title,
|
|
29
|
+
"content": self.content,
|
|
30
|
+
"kind": self.kind,
|
|
31
|
+
"origin": self.origin,
|
|
32
|
+
"source_hash": self.source_hash,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def source_hash(text: str) -> str:
|
|
37
|
+
return hashlib.sha256(text.encode()).hexdigest()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def parse_markdown(path: Path, root: Path) -> tuple[str, list[AdoptionProposal]]:
|
|
41
|
+
text = path.read_text()
|
|
42
|
+
if GENERATED_MARKER in text:
|
|
43
|
+
raise MemoryledgerError("ALREADY_GENERATED", "target is already generated")
|
|
44
|
+
digest = source_hash(text)
|
|
45
|
+
rel = path.resolve().relative_to(root.resolve()).as_posix()
|
|
46
|
+
proposals: list[AdoptionProposal] = []
|
|
47
|
+
heading = "Imported agent document"
|
|
48
|
+
body: list[str] = []
|
|
49
|
+
index = 0
|
|
50
|
+
|
|
51
|
+
def flush() -> None:
|
|
52
|
+
nonlocal index, body
|
|
53
|
+
content = "\n".join(body).strip()
|
|
54
|
+
if not content:
|
|
55
|
+
body = []
|
|
56
|
+
return
|
|
57
|
+
index += 1
|
|
58
|
+
proposals.append(
|
|
59
|
+
AdoptionProposal(
|
|
60
|
+
heading,
|
|
61
|
+
content,
|
|
62
|
+
"document",
|
|
63
|
+
f"adopt:{rel}:{index}",
|
|
64
|
+
digest,
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
body = []
|
|
68
|
+
|
|
69
|
+
for line in text.splitlines():
|
|
70
|
+
if line.startswith("## "):
|
|
71
|
+
flush()
|
|
72
|
+
heading = line[3:].strip() or "Imported section"
|
|
73
|
+
elif not line.startswith("# "):
|
|
74
|
+
body.append(line)
|
|
75
|
+
flush()
|
|
76
|
+
if not proposals:
|
|
77
|
+
raise MemoryledgerError(
|
|
78
|
+
"EMPTY_CONTENT", "manual target has no importable content"
|
|
79
|
+
)
|
|
80
|
+
return digest, proposals
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def adopt(
|
|
84
|
+
store: Store,
|
|
85
|
+
target: Path,
|
|
86
|
+
*,
|
|
87
|
+
backup: bool,
|
|
88
|
+
accept: bool,
|
|
89
|
+
reason: str,
|
|
90
|
+
) -> tuple[list[str], Path]:
|
|
91
|
+
if not backup:
|
|
92
|
+
raise MemoryledgerError(
|
|
93
|
+
"ADOPTION_BACKUP_REQUIRED", "adoption requires --backup"
|
|
94
|
+
)
|
|
95
|
+
initial_hash, proposals = parse_markdown(target, store.config.root)
|
|
96
|
+
existing = {memory.origin: memory for memory in store.all_memories()}
|
|
97
|
+
for proposal in proposals:
|
|
98
|
+
old = existing.get(proposal.origin)
|
|
99
|
+
if old and old.origin_hash == proposal.source_hash:
|
|
100
|
+
continue
|
|
101
|
+
store.validate_new(
|
|
102
|
+
proposal.kind,
|
|
103
|
+
proposal.title,
|
|
104
|
+
proposal.content,
|
|
105
|
+
f"Adopted from {target.relative_to(store.config.root)}.",
|
|
106
|
+
"repo",
|
|
107
|
+
"",
|
|
108
|
+
"root_agents",
|
|
109
|
+
"adopt",
|
|
110
|
+
origin=proposal.origin,
|
|
111
|
+
origin_hash=proposal.source_hash,
|
|
112
|
+
)
|
|
113
|
+
ids: list[str] = []
|
|
114
|
+
evidence = EvidenceRef(
|
|
115
|
+
kind="file",
|
|
116
|
+
title="Original manual agent file",
|
|
117
|
+
uri=target.relative_to(store.config.root).as_posix(),
|
|
118
|
+
content_hash=initial_hash,
|
|
119
|
+
)
|
|
120
|
+
for proposal in proposals:
|
|
121
|
+
old = existing.get(proposal.origin)
|
|
122
|
+
if old and old.origin_hash == proposal.source_hash:
|
|
123
|
+
ids.append(old.id)
|
|
124
|
+
continue
|
|
125
|
+
memory = store.create(
|
|
126
|
+
proposal.kind,
|
|
127
|
+
proposal.title,
|
|
128
|
+
proposal.content,
|
|
129
|
+
f"Adopted from {target.relative_to(store.config.root)}.",
|
|
130
|
+
"repo",
|
|
131
|
+
"",
|
|
132
|
+
"root_agents",
|
|
133
|
+
"adopt",
|
|
134
|
+
origin=proposal.origin,
|
|
135
|
+
origin_hash=proposal.source_hash,
|
|
136
|
+
evidence_refs=[evidence],
|
|
137
|
+
)
|
|
138
|
+
ids.append(memory.id)
|
|
139
|
+
if accept:
|
|
140
|
+
transition(store, memory.id, "accepted", reason)
|
|
141
|
+
result = render_all(store.config)
|
|
142
|
+
if source_hash(target.read_text()) != initial_hash:
|
|
143
|
+
raise MemoryledgerError("ADOPTION_SOURCE_CHANGED", "manual target changed")
|
|
144
|
+
extra_targets = [
|
|
145
|
+
(store.config.root / rel, text) for rel, text in result.linked_docs.items()
|
|
146
|
+
]
|
|
147
|
+
if store.config.render.nested_agents_enabled:
|
|
148
|
+
extra_targets.extend(
|
|
149
|
+
(store.config.root / rel, text) for rel, text in result.nested_docs.items()
|
|
150
|
+
)
|
|
151
|
+
for path, _text in extra_targets:
|
|
152
|
+
safe_to_replace(path)
|
|
153
|
+
backup_path = _backup_path(target)
|
|
154
|
+
shutil.copy2(target, backup_path)
|
|
155
|
+
try:
|
|
156
|
+
atomic_write_text(target, result.root_text)
|
|
157
|
+
for path, text in extra_targets:
|
|
158
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
159
|
+
atomic_write_text(path, text)
|
|
160
|
+
except Exception:
|
|
161
|
+
if source_hash(backup_path.read_text()) == initial_hash:
|
|
162
|
+
shutil.copy2(backup_path, target)
|
|
163
|
+
raise
|
|
164
|
+
return ids, backup_path
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _backup_path(target: Path) -> Path:
|
|
168
|
+
number = 1
|
|
169
|
+
while True:
|
|
170
|
+
candidate = target.with_name(f"{target.name}.memoryledger-adopt-{number}.bak")
|
|
171
|
+
if not candidate.exists():
|
|
172
|
+
return candidate
|
|
173
|
+
number += 1
|