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.
Files changed (29) hide show
  1. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/PKG-INFO +15 -12
  2. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/README.md +14 -11
  3. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/pyproject.toml +1 -1
  4. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/pyproject.toml.orig +1 -1
  5. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/config.py +1 -9
  6. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/install.py +28 -12
  7. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/ui_server.py +2 -0
  8. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/LICENSE +0 -0
  9. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/PROTOCOL.md +0 -0
  10. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/__init__.py +0 -0
  11. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/__main__.py +0 -0
  12. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/capture.py +0 -0
  13. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/cli.py +0 -0
  14. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/context.py +0 -0
  15. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/embed_worker.py +0 -0
  16. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/frontmatter.py +0 -0
  17. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/gitsync.py +0 -0
  18. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/hooks.py +0 -0
  19. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/importers.py +0 -0
  20. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/lint.py +0 -0
  21. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/select.py +0 -0
  22. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/sessions.py +0 -0
  23. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/store.py +0 -0
  24. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/ui.py +0 -0
  25. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/util.py +0 -0
  26. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/webui/app.css +0 -0
  27. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/webui/app.js +0 -0
  28. {agent_memory_git-0.1.0 → agent_memory_git-0.1.1}/src/agent_memory/webui/index.html +0 -0
  29. {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.0
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
- ![mem ui listing memories, with one open on the right](docs/mem-ui.png)
33
+ ![mem ui listing memories, with one open on the right](https://raw.githubusercontent.com/Ki-Seki/agent-memory/main/docs/mem-ui.png)
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
- cd agent-memory
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
- `AGENT_MEMORY_DATA` points `mem` at the demo repository, so nothing is installed and no hooks
47
- change. The page needs `numpy`. `--keywords-only` skips the optional embedding model; leave it
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
- | `~/.local/bin/mem` | symlink to `mem`, only if that file does not exist yet (`uv tool install` already puts one there) |
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
- From a checkout, `bin/mem` (or `uv run mem`) runs the code in place; `mem install` from there
165
- points the hooks at that checkout, so edits take effect in the next session.
166
- `uv tool install --editable .` does the same with `mem` on PATH.
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
- ![mem ui listing memories, with one open on the right](docs/mem-ui.png)
13
+ ![mem ui listing memories, with one open on the right](https://raw.githubusercontent.com/Ki-Seki/agent-memory/main/docs/mem-ui.png)
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
- cd agent-memory
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
- `AGENT_MEMORY_DATA` points `mem` at the demo repository, so nothing is installed and no hooks
27
- change. The page needs `numpy`. `--keywords-only` skips the optional embedding model; leave it
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
- | `~/.local/bin/mem` | symlink to `mem`, only if that file does not exist yet (`uv tool install` already puts one there) |
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
- From a checkout, `bin/mem` (or `uv run mem`) runs the code in place; `mem install` from there
145
- points the hooks at that checkout, so edits take effect in the next session.
146
- `uv tool install --editable .` does the same with `mem` on PATH.
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-memory-git"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "Shared, git-backed memory for Claude Code and Codex."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-memory-git"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "Shared, git-backed memory for Claude Code and Codex."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -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
- # Running from a source checkout (bin/mem, `uv run`, or an editable install) rather than an
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
- if not link_ok and not LOCAL_BIN.exists():
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 and Path(local).is_symlink():
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 "").count("ERROR")
482
- check(errors == 0, f"hook errors in {logf}: {errors}")
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: