agent-memory-git 0.1.0__tar.gz → 0.1.1__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.0 → agent_memory_git-0.1.1}/PKG-INFO +15 -12
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/README.md +14 -11
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/pyproject.toml +1 -1
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/pyproject.toml.orig +1 -1
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/config.py +1 -9
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/install.py +28 -12
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/ui_server.py +2 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/LICENSE +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/PROTOCOL.md +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/__init__.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/__main__.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/capture.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/cli.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/context.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/embed_worker.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/frontmatter.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/gitsync.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/hooks.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/importers.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/lint.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/select.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/sessions.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/store.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/ui.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/util.py +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/webui/app.css +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/webui/app.js +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/webui/index.html +0 -0
- {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/workdir.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-memory-git
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Shared, git-backed memory for Claude Code and Codex.
|
|
5
5
|
Keywords: claude-code,codex,memory,agents,git
|
|
6
6
|
Author: Shichao Song
|
|
@@ -30,22 +30,20 @@ you gave Codex is there the next time Claude Code starts, on any machine that pu
|
|
|
30
30
|
repository. This repository holds only the code; the memories live in a separate data
|
|
31
31
|
repository.
|
|
32
32
|
|
|
33
|
-

|
|
33
|
+

|
|
34
34
|
|
|
35
35
|
<sub>`mem ui` on the [demo data repository](https://github.com/Ki-Seki/agent-memory-data-demo).</sub>
|
|
36
36
|
|
|
37
37
|
## Try it without installing
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
git clone https://github.com/Ki-Seki/agent-memory
|
|
41
40
|
git clone https://github.com/Ki-Seki/agent-memory-data-demo
|
|
42
|
-
|
|
43
|
-
AGENT_MEMORY_DATA=../agent-memory-data-demo bin/mem ui --keywords-only
|
|
41
|
+
AGENT_MEMORY_DATA=agent-memory-data-demo uvx --from "agent-memory-git[ui]" mem ui --keywords-only
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
`
|
|
47
|
-
|
|
48
|
-
out after `mem ui --fetch-model` to also search by meaning.
|
|
44
|
+
`uvx` runs `mem` from a throwaway environment and `AGENT_MEMORY_DATA` points it at the demo
|
|
45
|
+
repository, so nothing is installed and no hooks change. `--keywords-only` skips the optional
|
|
46
|
+
embedding model; leave it out after `mem ui --fetch-model` to also search by meaning.
|
|
49
47
|
|
|
50
48
|
## How it works
|
|
51
49
|
|
|
@@ -99,7 +97,7 @@ protocol copies; `mem doctor` says when they are out of date.
|
|
|
99
97
|
| `~/.codex/hooks.json` | the same three hooks |
|
|
100
98
|
| `~/.codex/config.toml` | `memories.generate_memories = false`, `memories.use_memories = false`, data repository added to `sandbox_workspace_write.writable_roots` |
|
|
101
99
|
| `~/.codex/AGENTS.md` | a marked block with the protocol |
|
|
102
|
-
| `~/.
|
|
100
|
+
| `~/.zshrc` or `~/.bashrc` | a marked block putting `~/.local/bin`, where uv installs `mem`, on PATH, only if a new login shell lacks it |
|
|
103
101
|
|
|
104
102
|
Codex asks you to trust new hooks once: open `/hooks` in Codex and enable the three
|
|
105
103
|
agent-memory hooks.
|
|
@@ -161,9 +159,14 @@ uv run pytest
|
|
|
161
159
|
uv run ruff check .
|
|
162
160
|
```
|
|
163
161
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
`uv run mem ...` runs the working copy without touching the installed one. To have every
|
|
163
|
+
session use the working copy, install it in place of the release and rewire the hooks; go back
|
|
164
|
+
the same way:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
uv tool install --force --editable ".[ui]" && mem install # the checkout, live
|
|
168
|
+
uv tool install --force "agent-memory-git[ui]" && mem install # the release again
|
|
169
|
+
```
|
|
167
170
|
|
|
168
171
|
To release, run `uv run scripts/release.py prepare patch` (or `minor`, `major`) to open a
|
|
169
172
|
version PR. After it is merged, `uv run scripts/release.py publish <PR_NUMBER>` creates the GitHub
|
|
@@ -10,22 +10,20 @@ you gave Codex is there the next time Claude Code starts, on any machine that pu
|
|
|
10
10
|
repository. This repository holds only the code; the memories live in a separate data
|
|
11
11
|
repository.
|
|
12
12
|
|
|
13
|
-

|
|
13
|
+

|
|
14
14
|
|
|
15
15
|
<sub>`mem ui` on the [demo data repository](https://github.com/Ki-Seki/agent-memory-data-demo).</sub>
|
|
16
16
|
|
|
17
17
|
## Try it without installing
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
git clone https://github.com/Ki-Seki/agent-memory
|
|
21
20
|
git clone https://github.com/Ki-Seki/agent-memory-data-demo
|
|
22
|
-
|
|
23
|
-
AGENT_MEMORY_DATA=../agent-memory-data-demo bin/mem ui --keywords-only
|
|
21
|
+
AGENT_MEMORY_DATA=agent-memory-data-demo uvx --from "agent-memory-git[ui]" mem ui --keywords-only
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
`
|
|
27
|
-
|
|
28
|
-
out after `mem ui --fetch-model` to also search by meaning.
|
|
24
|
+
`uvx` runs `mem` from a throwaway environment and `AGENT_MEMORY_DATA` points it at the demo
|
|
25
|
+
repository, so nothing is installed and no hooks change. `--keywords-only` skips the optional
|
|
26
|
+
embedding model; leave it out after `mem ui --fetch-model` to also search by meaning.
|
|
29
27
|
|
|
30
28
|
## How it works
|
|
31
29
|
|
|
@@ -79,7 +77,7 @@ protocol copies; `mem doctor` says when they are out of date.
|
|
|
79
77
|
| `~/.codex/hooks.json` | the same three hooks |
|
|
80
78
|
| `~/.codex/config.toml` | `memories.generate_memories = false`, `memories.use_memories = false`, data repository added to `sandbox_workspace_write.writable_roots` |
|
|
81
79
|
| `~/.codex/AGENTS.md` | a marked block with the protocol |
|
|
82
|
-
| `~/.
|
|
80
|
+
| `~/.zshrc` or `~/.bashrc` | a marked block putting `~/.local/bin`, where uv installs `mem`, on PATH, only if a new login shell lacks it |
|
|
83
81
|
|
|
84
82
|
Codex asks you to trust new hooks once: open `/hooks` in Codex and enable the three
|
|
85
83
|
agent-memory hooks.
|
|
@@ -141,9 +139,14 @@ uv run pytest
|
|
|
141
139
|
uv run ruff check .
|
|
142
140
|
```
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
`uv run mem ...` runs the working copy without touching the installed one. To have every
|
|
143
|
+
session use the working copy, install it in place of the release and rewire the hooks; go back
|
|
144
|
+
the same way:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv tool install --force --editable ".[ui]" && mem install # the checkout, live
|
|
148
|
+
uv tool install --force "agent-memory-git[ui]" && mem install # the release again
|
|
149
|
+
```
|
|
147
150
|
|
|
148
151
|
To release, run `uv run scripts/release.py prepare patch` (or `minor`, `major`) to open a
|
|
149
152
|
version PR. After it is merged, `uv run scripts/release.py publish <PR_NUMBER>` creates the GitHub
|
|
@@ -7,11 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
PACKAGE_DIR = Path(__file__).resolve().parent
|
|
8
8
|
PROTOCOL = PACKAGE_DIR / "PROTOCOL.md"
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
# installed package: hooks then call the checkout's bin/mem, so edits take effect at once.
|
|
12
|
-
_ROOT = PACKAGE_DIR.parent.parent
|
|
13
|
-
CHECKOUT = _ROOT if (_ROOT / "pyproject.toml").is_file() and (_ROOT / "bin" / "mem").is_file() else None
|
|
14
|
-
CODE_DIR = CHECKOUT or PACKAGE_DIR
|
|
10
|
+
CODE_DIR = PACKAGE_DIR
|
|
15
11
|
|
|
16
12
|
CONFIG_DIR = Path(os.environ.get("AGENT_MEMORY_CONFIG_DIR", "~/.config/agent-memory")).expanduser()
|
|
17
13
|
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
@@ -20,14 +16,10 @@ PROTOCOL_COPY = CONFIG_DIR / "PROTOCOL.md" # what Claude Code imports; `mem ins
|
|
|
20
16
|
|
|
21
17
|
def mem_command():
|
|
22
18
|
"""The argv that runs `mem` with this interpreter, for hooks and background sync."""
|
|
23
|
-
if CHECKOUT:
|
|
24
|
-
return [sys.executable, str(CHECKOUT / "bin" / "mem")]
|
|
25
19
|
return [sys.executable, "-m", "agent_memory"]
|
|
26
20
|
|
|
27
21
|
|
|
28
22
|
def _mem_path():
|
|
29
|
-
if CHECKOUT:
|
|
30
|
-
return CHECKOUT / "bin" / "mem"
|
|
31
23
|
found = shutil.which("mem")
|
|
32
24
|
return Path(found) if found else Path(sys.executable).with_name("mem")
|
|
33
25
|
|
|
@@ -18,7 +18,7 @@ import tomllib
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
20
|
from . import config, gitsync
|
|
21
|
-
from .util import HOME
|
|
21
|
+
from .util import HOME, parse_time
|
|
22
22
|
|
|
23
23
|
STATE_FILE = config.CONFIG_DIR / "install-state.json"
|
|
24
24
|
BEGIN = "<!-- agent-memory:begin -->"
|
|
@@ -327,19 +327,13 @@ def install(dry_run=False, claude=True, codex=True):
|
|
|
327
327
|
_path_plan(plan, state)
|
|
328
328
|
|
|
329
329
|
plan.show()
|
|
330
|
-
link_ok = LOCAL_BIN.is_symlink() and LOCAL_BIN.resolve() == config.BIN.resolve()
|
|
331
|
-
if not link_ok:
|
|
332
|
-
print(f"=== {LOCAL_BIN} -> {config.BIN} (symlink)\n")
|
|
333
330
|
if dry_run:
|
|
334
331
|
print("dry run: nothing changed")
|
|
335
332
|
return
|
|
336
333
|
|
|
337
334
|
stamp = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
338
335
|
plan.apply(config.CONFIG_DIR / "backups" / stamp)
|
|
339
|
-
|
|
340
|
-
LOCAL_BIN.parent.mkdir(parents=True, exist_ok=True)
|
|
341
|
-
LOCAL_BIN.symlink_to(config.BIN)
|
|
342
|
-
state["local_bin"] = str(LOCAL_BIN)
|
|
336
|
+
state.pop("local_bin", None) # `uv tool install` owns ~/.local/bin/mem now
|
|
343
337
|
state["installed_at"] = stamp
|
|
344
338
|
state["claude"] = state.get("claude") or (claude and CLAUDE_DIR.is_dir())
|
|
345
339
|
state["codex"] = state.get("codex") or (codex and CODEX_DIR.is_dir())
|
|
@@ -427,20 +421,41 @@ def uninstall(dry_run=False):
|
|
|
427
421
|
plan.edit(Path(rc), _unblock(text, SH_BEGIN, SH_END))
|
|
428
422
|
|
|
429
423
|
plan.show()
|
|
424
|
+
# Older installs linked ~/.local/bin/mem to a checkout's bin/mem. Remove that link only once
|
|
425
|
+
# it points nowhere; a live one now belongs to `uv tool install`.
|
|
430
426
|
local = state.get("local_bin")
|
|
427
|
+
local = local if local and Path(local).is_symlink() and not Path(local).exists() else None
|
|
431
428
|
if local:
|
|
432
|
-
print(f"=== remove symlink {local}\n")
|
|
429
|
+
print(f"=== remove dangling symlink {local}\n")
|
|
433
430
|
if dry_run:
|
|
434
431
|
print("dry run: nothing changed")
|
|
435
432
|
return
|
|
436
433
|
stamp = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
437
434
|
plan.apply(config.CONFIG_DIR / "backups" / f"uninstall-{stamp}")
|
|
438
|
-
if local
|
|
435
|
+
if local:
|
|
439
436
|
Path(local).unlink()
|
|
440
437
|
STATE_FILE.rename(STATE_FILE.with_name(f"install-state.uninstalled-{stamp}.json"))
|
|
441
438
|
print("uninstalled. Native memory settings restored; the data repository is untouched.")
|
|
442
439
|
|
|
443
440
|
|
|
441
|
+
ERROR_DAYS = 7
|
|
442
|
+
_ERROR_LINE = re.compile(r"^(\S+) ERROR in (hook [^:]*):", re.M)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def recent_hook_errors(log_text, now=None, days=ERROR_DAYS):
|
|
446
|
+
"""'<time> <where>' for each hook failure logged in the last `days` days, oldest first.
|
|
447
|
+
|
|
448
|
+
Other entries, such as errors from the `mem ui` server, are not hook failures.
|
|
449
|
+
"""
|
|
450
|
+
now = now or _dt.datetime.now().astimezone()
|
|
451
|
+
out = []
|
|
452
|
+
for stamp, where in _ERROR_LINE.findall(log_text):
|
|
453
|
+
t = parse_time(stamp)
|
|
454
|
+
if t and (now - t).days < days:
|
|
455
|
+
out.append(f"{stamp} {where}")
|
|
456
|
+
return out
|
|
457
|
+
|
|
458
|
+
|
|
444
459
|
def doctor():
|
|
445
460
|
ok = True
|
|
446
461
|
|
|
@@ -478,6 +493,7 @@ def doctor():
|
|
|
478
493
|
staged = list((data / "staging").glob("*/*.md")) if (data / "staging").exists() else []
|
|
479
494
|
check(not staged, f"no stuck staged files ({len(staged)})")
|
|
480
495
|
logf = data / ".cache" / "agent-memory.log"
|
|
481
|
-
errors = (_read(logf) or "")
|
|
482
|
-
|
|
496
|
+
errors = recent_hook_errors(_read(logf) or "")
|
|
497
|
+
latest = f"; latest: {errors[-1]} (details in {logf})" if errors else ""
|
|
498
|
+
check(not errors, f"no hook errors in the last {ERROR_DAYS} days ({len(errors)}{latest})")
|
|
483
499
|
return 0 if ok else 1
|
|
@@ -296,6 +296,8 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
296
296
|
def _run(self, fn):
|
|
297
297
|
try:
|
|
298
298
|
self._send(200, fn())
|
|
299
|
+
except (BrokenPipeError, ConnectionResetError):
|
|
300
|
+
pass # the page went away (reload, closed tab) before the reply was written
|
|
299
301
|
except ApiError as e:
|
|
300
302
|
self._send(e.status, {"error": e.message})
|
|
301
303
|
except Exception as e:
|
|
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
|