agent-memory-git 0.1.14__tar.gz → 0.1.16__tar.gz
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.
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/PKG-INFO +1 -1
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/pyproject.toml +1 -1
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/pyproject.toml.orig +1 -1
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/PROTOCOL.md +1 -1
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/capture.py +72 -1
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/cli.py +4 -8
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/sessions.py +20 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/list.js +2 -2
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/LICENSE +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/README.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/__init__.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/__main__.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/config.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/context.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/personal/2026/08/20260816-blog-publishing-k3qm.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/personal/2026/09/20260906-push-personal-repos-w8re.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/personal/2026/09/20260921-home-server-n2vb.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/personal/2026/10/20261004-options-first-7hcx.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/personal/2026/10/20261009-thesis-setup-fr6e.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/08/20260804-alex-profile-ujzd.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/08/20260804-reply-language-xdnc.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/08/20260812-ask-before-push-pf3d.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/08/20260820-tests-before-done-odzd.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/08/20260827-etl-orders-export-deadline-pft7.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/08/20260828-etl-dashboard-72ce.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/09/20260902-report-eta-is2j.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/09/20260908-shop-web-stripe-checkout-lgmx.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/09/20260910-shop-web-deploys-dn73.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/09/20260915-etl-parquet-partitioning-seh2.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/09/20260922-design-tokens-source-uvw7.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/10/20261002-shop-web-v3-freeze-5xtp.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo/work/2026/10/20261012-payments-runbook-4syw.md +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/demo.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/embed_worker.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/frontmatter.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/gitsync.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/hooks.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/importers.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/install.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/lint.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/profiles.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/select.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/store.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/ui.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/ui_server.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/update.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/util.py +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/app.css +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/index.html +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/api.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/avatar.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/filters.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/format.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/h.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/icons.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/main.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/markdown.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/search.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/store.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/insights.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/memory.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/overlays.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/profiles.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/settings.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/shell.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/ui.js +0 -0
- {agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/workdir.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Shared memory
|
|
2
2
|
|
|
3
|
-
You have a persistent memory that Claude Code and Codex share. Every memory is one Markdown file in a git repository. At the start of each session a hook injects a block titled "Shared memory index": it lists the memories relevant to the current folder and repository, and gives the memories directory, this session's staging directory and the path of the `mem` command.
|
|
3
|
+
You have a persistent memory that Claude Code and Codex share. Every memory is one Markdown file in a git repository. At the start of each session a hook injects a block titled "Shared memory index": it lists the memories relevant to the current folder and repository, and gives the memories directory, this session's staging directory and the path of the `mem` command. When the conversation holds more than one index, only the latest counts: use its memories and paths, its staging directory above all. A new one arrives with your next message when the active profile changes, and a session resumed, forked or continued from another starts with that session's index still in view. If that block is missing, the memory system is not active in this session; ignore this section.
|
|
4
4
|
|
|
5
5
|
## Reading
|
|
6
6
|
|
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from . import frontmatter, gitsync, sessions, workdir
|
|
9
9
|
from .context import detect
|
|
10
|
-
from .store import memories_dir, normalize, read, staging_dir
|
|
10
|
+
from .store import load_all, memories_dir, normalize, read, staging_dir
|
|
11
11
|
from .util import atomic_write, id_path, log, new_id, now, now_iso
|
|
12
12
|
|
|
13
13
|
SESSION_FILE = ".session.json"
|
|
@@ -124,6 +124,49 @@ def promote(data_dir, sdir, session=None):
|
|
|
124
124
|
return count
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
PLACE_KEYS = ("cwd", "original_cwd", "git_root", "git_remote", "git_branch", "git_commit")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def relocated(source, ctx):
|
|
131
|
+
"""`source` moved to the project `ctx` points at (a context from relocate()), in the order _source() writes."""
|
|
132
|
+
items = [(k, v) for k, v in source.items() if k not in PLACE_KEYS]
|
|
133
|
+
place = [(k, ctx[k]) for k in PLACE_KEYS if k != "git_commit" and ctx.get(k)]
|
|
134
|
+
at = next((i for i, (k, _) in enumerate(items) if k == "transcript"), len(items))
|
|
135
|
+
return dict(items[:at] + place + items[at:])
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def rehome(data_dir, session):
|
|
139
|
+
"""Move this session's `here` memories still filed under its throwaway folder to the project it worked in.
|
|
140
|
+
|
|
141
|
+
promote() decides from the transcript so far, which finds nothing when a memory is written before the
|
|
142
|
+
session touches a project folder on this machine; each later turn tries again. Returns how many moved.
|
|
143
|
+
"""
|
|
144
|
+
ctx = session.get("ctx") or {}
|
|
145
|
+
tool, sid = session.get("tool"), session.get("session_id")
|
|
146
|
+
if not sid or not workdir.is_throwaway(ctx.get("cwd")):
|
|
147
|
+
return 0
|
|
148
|
+
left = [
|
|
149
|
+
m
|
|
150
|
+
for m in load_all(data_dir)
|
|
151
|
+
if m["scope"] == "here"
|
|
152
|
+
and m["source"].get("session_id") == sid
|
|
153
|
+
and m["source"].get("tool") == tool
|
|
154
|
+
and workdir.is_throwaway(m["source"].get("cwd"))
|
|
155
|
+
and not m["source"].get("original_cwd")
|
|
156
|
+
]
|
|
157
|
+
if not left:
|
|
158
|
+
return 0
|
|
159
|
+
moved = relocate(ctx, session.get("transcript_path") or sessions.transcript_path(tool, sid))
|
|
160
|
+
if not moved.get("original_cwd"):
|
|
161
|
+
return 0 # still no project to point at
|
|
162
|
+
for m in left:
|
|
163
|
+
path = memories_dir(data_dir) / m["rel"]
|
|
164
|
+
meta, body = read(path)
|
|
165
|
+
meta["source"] = relocated(meta["source"], moved)
|
|
166
|
+
atomic_write(path, frontmatter.join(meta, body))
|
|
167
|
+
return len(left)
|
|
168
|
+
|
|
169
|
+
|
|
127
170
|
def stamp_updates(data_dir):
|
|
128
171
|
"""Set `updated` on tracked memory files that were edited in place."""
|
|
129
172
|
r = gitsync.git(data_dir, "status", "--porcelain", "-z", "--", "memories")
|
|
@@ -175,6 +218,7 @@ def flush(data_dir, sdir, final=False, lock_timeout=5.0):
|
|
|
175
218
|
return False
|
|
176
219
|
session = read_session(sdir)
|
|
177
220
|
n = promote(data_dir, sdir, session)
|
|
221
|
+
rehome(data_dir, session)
|
|
178
222
|
committed = commit(data_dir, session)
|
|
179
223
|
if final and Path(sdir).is_dir() and not any(Path(sdir).glob("*.md")):
|
|
180
224
|
shutil.rmtree(sdir, ignore_errors=True)
|
|
@@ -183,6 +227,31 @@ def flush(data_dir, sdir, final=False, lock_timeout=5.0):
|
|
|
183
227
|
return committed
|
|
184
228
|
|
|
185
229
|
|
|
230
|
+
TOOLS = ("claude-code", "codex") # the prefixes of staging folder names: <tool>-<session id>
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def adopt(sdir):
|
|
234
|
+
"""Session info for a staging folder that has none, from its name and that session's transcript.
|
|
235
|
+
|
|
236
|
+
An agent can write into a staging folder that is not its session's own, such as the one named in the older
|
|
237
|
+
index of a session it was continued from; the folder it makes has no .session.json, and without this the
|
|
238
|
+
memories in it are committed with no provenance at all. They are credited to the folder's session.
|
|
239
|
+
"""
|
|
240
|
+
name = Path(sdir).name
|
|
241
|
+
tool = next((t for t in TOOLS if name.startswith(t + "-")), None)
|
|
242
|
+
if not tool:
|
|
243
|
+
return
|
|
244
|
+
sid = name[len(tool) + 1 :]
|
|
245
|
+
transcript = sessions.transcript_path(tool, sid)
|
|
246
|
+
info = {"tool": tool, "session_id": sid, "transcript_path": transcript}
|
|
247
|
+
folder = sessions.cwd(tool, sid, transcript)
|
|
248
|
+
if folder:
|
|
249
|
+
ctx = detect(folder)
|
|
250
|
+
ctx.pop("git_commit", None) # HEAD now, not what the session saw
|
|
251
|
+
info["ctx"] = ctx
|
|
252
|
+
write_session(sdir, info)
|
|
253
|
+
|
|
254
|
+
|
|
186
255
|
def recover_orphans(data_dir, keep=None):
|
|
187
256
|
"""Promote staging dirs left by sessions that ended without a final flush."""
|
|
188
257
|
root = staging_dir(data_dir)
|
|
@@ -196,6 +265,8 @@ def recover_orphans(data_dir, keep=None):
|
|
|
196
265
|
age = time.time() - (marker.stat().st_mtime if marker.exists() else sdir.stat().st_mtime)
|
|
197
266
|
has_files = any(sdir.glob("*.md"))
|
|
198
267
|
if has_files and age > ORPHAN_AGE:
|
|
268
|
+
if not marker.exists():
|
|
269
|
+
adopt(sdir)
|
|
199
270
|
committed |= flush(data_dir, sdir, final=True, lock_timeout=1.0)
|
|
200
271
|
elif not has_files and age > STALE_AGE:
|
|
201
272
|
shutil.rmtree(sdir, ignore_errors=True)
|
|
@@ -129,12 +129,7 @@ def cmd_relocate(args):
|
|
|
129
129
|
if args.dry_run:
|
|
130
130
|
continue
|
|
131
131
|
meta, body = store.read(mdir / m["rel"])
|
|
132
|
-
source = dict(meta.get("source") or {})
|
|
133
|
-
for key in ("cwd", "git_root", "git_remote", "git_branch"):
|
|
134
|
-
source.pop(key, None)
|
|
135
|
-
if ctx.get(key):
|
|
136
|
-
source[key] = ctx[key]
|
|
137
|
-
source["original_cwd"] = ctx["original_cwd"]
|
|
132
|
+
source = capture.relocated(dict(meta.get("source") or {}), ctx)
|
|
138
133
|
if transcript:
|
|
139
134
|
source.setdefault("transcript", transcript)
|
|
140
135
|
meta["source"] = source
|
|
@@ -274,11 +269,12 @@ def cmd_profile(args):
|
|
|
274
269
|
_switched(found[pid]["name"])
|
|
275
270
|
_access_note()
|
|
276
271
|
elif args.action == "remove":
|
|
277
|
-
found,
|
|
272
|
+
found, before = profiles.listing()
|
|
278
273
|
pid = profiles.find(args.profile, found)
|
|
279
274
|
active = profiles.remove(pid)
|
|
280
275
|
print(f"removed {found[pid]['name']}; {profiles.short(found[pid]['data_dir'])} is left as it is")
|
|
281
|
-
|
|
276
|
+
if active != before: # only removing the active profile switches sessions to another
|
|
277
|
+
_switched(profiles.listing()[0][active]["name"])
|
|
282
278
|
_access_note()
|
|
283
279
|
else:
|
|
284
280
|
for p in profiles.summary()["profiles"]:
|
|
@@ -53,6 +53,26 @@ def transcript_path(tool, session_id):
|
|
|
53
53
|
return str(hits[0]) if hits else ""
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
def cwd(tool, session_id, transcript=None):
|
|
57
|
+
"""The folder a session ran in: Codex's thread record, or the first Claude transcript line that names one."""
|
|
58
|
+
if tool == "codex":
|
|
59
|
+
return _codex_thread(session_id, "cwd")
|
|
60
|
+
try:
|
|
61
|
+
with open(transcript, encoding="utf-8", errors="replace") as f:
|
|
62
|
+
for raw in f:
|
|
63
|
+
if '"cwd"' not in raw:
|
|
64
|
+
continue
|
|
65
|
+
try:
|
|
66
|
+
found = json.loads(raw).get("cwd")
|
|
67
|
+
except ValueError:
|
|
68
|
+
continue
|
|
69
|
+
if found:
|
|
70
|
+
return found
|
|
71
|
+
except (OSError, TypeError):
|
|
72
|
+
pass
|
|
73
|
+
return ""
|
|
74
|
+
|
|
75
|
+
|
|
56
76
|
def title(tool, session_id, transcript=None):
|
|
57
77
|
if tool == "codex":
|
|
58
78
|
name = _codex_title(session_id)
|
|
@@ -52,7 +52,7 @@ function facet(def) {
|
|
|
52
52
|
${options.map((o) => {
|
|
53
53
|
const on = values.includes(o.value);
|
|
54
54
|
return html`<button type="button" role="menuitemcheckbox" class="${cx("menu-item", !o.count && !on && "faint")}"${attrs({ "aria-checked": String(on), "data-act": "toggle", "data-arg": `${def.key}:${o.value}`, "data-key": `${def.key}:${o.value}` })}>
|
|
55
|
-
<span class="tick">${on ? icon("check", "", 3) : ""}</span><span class="grow">${o.label}</span><span class="count">${o.count}</span></button>`;
|
|
55
|
+
<span class="tick">${on ? icon("check", "", 3) : ""}</span><span class="grow truncate"${attrs({ title: o.label })}>${o.label}</span><span class="count">${o.count}</span></button>`;
|
|
56
56
|
})}
|
|
57
57
|
</div>
|
|
58
58
|
${values.length ? html`<div class="menu-foot"><button type="button" class="menu-wide" data-act="setList" data-arg="${def.key}">Clear ${def.label.toLowerCase()}</button></div>` : ""}`;
|
|
@@ -73,7 +73,7 @@ function added() {
|
|
|
73
73
|
${opts.map((o) => {
|
|
74
74
|
const on = v === o.value;
|
|
75
75
|
return html`<button type="button" role="radio" class="${cx("menu-item", !o.count && !on && "faint")}"${attrs({ "aria-checked": String(on), "data-act": "setAdded", "data-arg": on ? "" : o.value })}>
|
|
76
|
-
<span class="radio">${on ? html`<i></i>` : ""}</span><span class="grow">${o.label}</span><span class="count">${o.count}</span></button>`;
|
|
76
|
+
<span class="radio">${on ? html`<i></i>` : ""}</span><span class="grow truncate"${attrs({ title: o.label })}>${o.label}</span><span class="count">${o.count}</span></button>`;
|
|
77
77
|
})}
|
|
78
78
|
</div>
|
|
79
79
|
<div class="menu-range">
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/insights.js
RENAMED
|
File without changes
|
{agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/memory.js
RENAMED
|
File without changes
|
{agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/overlays.js
RENAMED
|
File without changes
|
{agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/profiles.js
RENAMED
|
File without changes
|
{agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/settings.js
RENAMED
|
File without changes
|
{agent_memory_git-0.1.14 → agent_memory_git-0.1.16}/src/agent_memory/webui/js/views/shell.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|