memory-seed 2.2.1__tar.gz → 2.2.3__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.
- {memory_seed-2.2.1 → memory_seed-2.2.3}/PKG-INFO +4 -4
- {memory_seed-2.2.1 → memory_seed-2.2.3}/README.md +3 -3
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/core.py +10 -6
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/agent-rules.md +3 -2
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/hooks/session-log-check.py +35 -23
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/project-bootstrap.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/code_search.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/data_architecture.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/index.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/local_compilation.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/memory_consolidation.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/memory_doctor.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/release_publishing.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/skills/security_triage.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/AGENTS.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/CLAUDE.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/GEMINI.md +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed.egg-info/PKG-INFO +4 -4
- {memory_seed-2.2.1 → memory_seed-2.2.3}/pyproject.toml +1 -1
- {memory_seed-2.2.1 → memory_seed-2.2.3}/tests/test_memory_seed.py +63 -16
- {memory_seed-2.2.1 → memory_seed-2.2.3}/LICENSE +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/__init__.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/cli.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/mcp_server.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/mcp_validate.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/archive/.gitkeep +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/hooks/memory-retrieval-check.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/sessions/.gitkeep +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/semantic_cache.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed.egg-info/SOURCES.txt +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed.egg-info/dependency_links.txt +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed.egg-info/entry_points.txt +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed.egg-info/requires.txt +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed.egg-info/top_level.txt +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/setup.cfg +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/tests/test_mcp_server.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/tests/test_mcp_validation.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/tests/test_semantic_cache.py +0 -0
- {memory_seed-2.2.1 → memory_seed-2.2.3}/tests/test_session_schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory-seed
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: Portable local memory seed for file-reading AI coding agents
|
|
5
5
|
Author: Jean Nathan Tshibuyi
|
|
6
6
|
License: MIT
|
|
@@ -136,7 +136,7 @@ The result is a lightweight memory workflow you can understand, commit, review,
|
|
|
136
136
|
| Agent or client | Support path |
|
|
137
137
|
| --- | --- |
|
|
138
138
|
| Codex | Starts from `AGENTS.md`; can use MCP when the client supports stdio MCP servers. |
|
|
139
|
-
| Claude Code | Starts from `CLAUDE.md`;
|
|
139
|
+
| Claude Code | Starts from `CLAUDE.md`; MCP server auto-registered via `uvx --from memory-seed`. |
|
|
140
140
|
| Gemini CLI | Starts from `GEMINI.md`. |
|
|
141
141
|
| Other file-reading agents | Start from `AGENTS.md` and follow nearest `.memory-seed/` runtime discovery. |
|
|
142
142
|
| MCP-capable clients | Use `memory_search` and `memory_get_chunk` through `memory-seed-mcp --stdio`. |
|
|
@@ -200,7 +200,7 @@ GEMINI.md
|
|
|
200
200
|
|
|
201
201
|
## Current Version
|
|
202
202
|
|
|
203
|
-
The current reusable control-plane version is `2.
|
|
203
|
+
The current reusable control-plane version is `2.2`.
|
|
204
204
|
|
|
205
205
|
Legacy `.AGENTS/` projects remain supported as a fallback during migration.
|
|
206
206
|
|
|
@@ -365,7 +365,7 @@ For the version distinction (`pip show memory-seed` reports the package version;
|
|
|
365
365
|
|
|
366
366
|
Memory Seed also includes a lightweight MCP server that lets agents search local session memory through structured tool calls instead of shelling out to broad compact summaries.
|
|
367
367
|
|
|
368
|
-
**Auto-registration:** `memory-seed init` and `memory-seed update` automatically register `memory-seed-mcp --stdio` in each supported vendor's config — `.claude/settings.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.gemini/settings.json` (Gemini CLI). No manual config is needed for projects initialised with Memory Seed.
|
|
368
|
+
**Auto-registration:** `memory-seed init` and `memory-seed update` automatically register `uvx --from memory-seed memory-seed-mcp --stdio` in each supported vendor's config — `.claude/settings.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.gemini/settings.json` (Gemini CLI). No manual config is needed for projects initialised with Memory Seed. The `uvx --from` form is used so the command works regardless of whether `~/.local/bin` is on the agent's PATH.
|
|
369
369
|
|
|
370
370
|
If you are configuring the server manually, run it over stdio:
|
|
371
371
|
|
|
@@ -115,7 +115,7 @@ The result is a lightweight memory workflow you can understand, commit, review,
|
|
|
115
115
|
| Agent or client | Support path |
|
|
116
116
|
| --- | --- |
|
|
117
117
|
| Codex | Starts from `AGENTS.md`; can use MCP when the client supports stdio MCP servers. |
|
|
118
|
-
| Claude Code | Starts from `CLAUDE.md`;
|
|
118
|
+
| Claude Code | Starts from `CLAUDE.md`; MCP server auto-registered via `uvx --from memory-seed`. |
|
|
119
119
|
| Gemini CLI | Starts from `GEMINI.md`. |
|
|
120
120
|
| Other file-reading agents | Start from `AGENTS.md` and follow nearest `.memory-seed/` runtime discovery. |
|
|
121
121
|
| MCP-capable clients | Use `memory_search` and `memory_get_chunk` through `memory-seed-mcp --stdio`. |
|
|
@@ -179,7 +179,7 @@ GEMINI.md
|
|
|
179
179
|
|
|
180
180
|
## Current Version
|
|
181
181
|
|
|
182
|
-
The current reusable control-plane version is `2.
|
|
182
|
+
The current reusable control-plane version is `2.2`.
|
|
183
183
|
|
|
184
184
|
Legacy `.AGENTS/` projects remain supported as a fallback during migration.
|
|
185
185
|
|
|
@@ -344,7 +344,7 @@ For the version distinction (`pip show memory-seed` reports the package version;
|
|
|
344
344
|
|
|
345
345
|
Memory Seed also includes a lightweight MCP server that lets agents search local session memory through structured tool calls instead of shelling out to broad compact summaries.
|
|
346
346
|
|
|
347
|
-
**Auto-registration:** `memory-seed init` and `memory-seed update` automatically register `memory-seed-mcp --stdio` in each supported vendor's config — `.claude/settings.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.gemini/settings.json` (Gemini CLI). No manual config is needed for projects initialised with Memory Seed.
|
|
347
|
+
**Auto-registration:** `memory-seed init` and `memory-seed update` automatically register `uvx --from memory-seed memory-seed-mcp --stdio` in each supported vendor's config — `.claude/settings.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.gemini/settings.json` (Gemini CLI). No manual config is needed for projects initialised with Memory Seed. The `uvx --from` form is used so the command works regardless of whether `~/.local/bin` is on the agent's PATH.
|
|
348
348
|
|
|
349
349
|
If you are configuring the server manually, run it over stdio:
|
|
350
350
|
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
|
|
11
11
|
PACKAGE_ROOT = Path(__file__).resolve().parent
|
|
12
12
|
SEED_ROOT = PACKAGE_ROOT / "seed"
|
|
13
|
-
VERSION = "2.
|
|
13
|
+
VERSION = "2.2"
|
|
14
14
|
MEMORY_DIR_NAME = ".memory-seed"
|
|
15
15
|
LEGACY_MEMORY_DIR_NAME = ".AGENTS"
|
|
16
16
|
BACKUP_IGNORE_ENTRY = ".memory-seed/backups/"
|
|
@@ -128,9 +128,10 @@ _CODEX_RETRIEVAL_COMMAND = "python3 .memory-seed/hooks/memory-retrieval-check.py
|
|
|
128
128
|
_CURSOR_RETRIEVAL_COMMAND = "python3 .memory-seed/hooks/memory-retrieval-check.py --cursor"
|
|
129
129
|
_GEMINI_RETRIEVAL_COMMAND = "python3 .memory-seed/hooks/memory-retrieval-check.py --gemini"
|
|
130
130
|
|
|
131
|
-
_MCP_SERVER_COMMAND = "
|
|
132
|
-
_MCP_SERVER_ARGS = ["--stdio"]
|
|
131
|
+
_MCP_SERVER_COMMAND = "uvx"
|
|
132
|
+
_MCP_SERVER_ARGS = ["--from", "memory-seed", "memory-seed-mcp", "--stdio"]
|
|
133
133
|
_MCP_SERVER_KEY = "memory-seed"
|
|
134
|
+
_OWN_MCP_COMMANDS = {"uvx", "memory-seed-mcp"}
|
|
134
135
|
|
|
135
136
|
BOOTSTRAP_GENERATED_FILES = [
|
|
136
137
|
".memory-seed/index.md",
|
|
@@ -364,7 +365,8 @@ def _merge_claude_mcp(target_root: Path) -> bool:
|
|
|
364
365
|
existing = data.get("mcpServers", {}).get(_MCP_SERVER_KEY, {})
|
|
365
366
|
if existing == expected:
|
|
366
367
|
return False
|
|
367
|
-
|
|
368
|
+
is_ours = existing.get("command") in _OWN_MCP_COMMANDS or "memory-seed-mcp" in existing.get("args", [])
|
|
369
|
+
if existing and not is_ours:
|
|
368
370
|
return False # a different server is using this key; don't overwrite
|
|
369
371
|
|
|
370
372
|
data.setdefault("mcpServers", {})[_MCP_SERVER_KEY] = expected
|
|
@@ -393,7 +395,8 @@ def _merge_cursor_mcp(target_root: Path) -> bool:
|
|
|
393
395
|
existing = data.get("mcpServers", {}).get(_MCP_SERVER_KEY, {})
|
|
394
396
|
if existing == expected:
|
|
395
397
|
return False
|
|
396
|
-
|
|
398
|
+
is_ours = existing.get("command") in _OWN_MCP_COMMANDS or "memory-seed-mcp" in existing.get("args", [])
|
|
399
|
+
if existing and not is_ours:
|
|
397
400
|
return False # a different server is using this key; don't overwrite
|
|
398
401
|
|
|
399
402
|
data.setdefault("mcpServers", {})[_MCP_SERVER_KEY] = expected
|
|
@@ -422,7 +425,8 @@ def _merge_gemini_mcp(target_root: Path) -> bool:
|
|
|
422
425
|
existing = data.get("mcpServers", {}).get(_MCP_SERVER_KEY, {})
|
|
423
426
|
if existing == expected:
|
|
424
427
|
return False
|
|
425
|
-
|
|
428
|
+
is_ours = existing.get("command") in _OWN_MCP_COMMANDS or "memory-seed-mcp" in existing.get("args", [])
|
|
429
|
+
if existing and not is_ours:
|
|
426
430
|
return False # a different server is using this key; don't overwrite
|
|
427
431
|
|
|
428
432
|
data.setdefault("mcpServers", {})[_MCP_SERVER_KEY] = expected
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
memory-system-version: 2.
|
|
2
|
+
memory-system-version: 2.2
|
|
3
3
|
tags:
|
|
4
4
|
- memory-seed
|
|
5
5
|
- agent-rules
|
|
@@ -292,11 +292,12 @@ Deferring or batching session log writes is a discipline failure, not an accepta
|
|
|
292
292
|
The session file is strictly append-only and must stay in ascending time order. To guarantee this without ever reordering:
|
|
293
293
|
|
|
294
294
|
- Append every new entry to the **end** of the day's file. Never insert an entry above an existing one.
|
|
295
|
+
- **Use `>>` shell redirection or Python append mode (`open(f, 'a')`) to write entries — do not use an editor replace/insert operation.** Replace/insert requires selecting an anchor line; if a prior edit already added content after that anchor, the new entry lands mid-file instead of at the end. Append mode writes to the physical end of the file unconditionally, with no anchor needed.
|
|
295
296
|
- The entry heading timestamp is the **actual current clock time** at the moment you write it. Read it from the system clock; never reuse a time from your context, memory, or an earlier message, and never backdate it to when the work happened.
|
|
296
297
|
- Because entries are always appended with the current time, file order, write order, and timestamp order are identical. No manual reordering is ever needed or allowed.
|
|
297
298
|
- If you are recording work that completed earlier in the session (you forgot, or you are catching up), still stamp the heading with the current time. If the original work time matters, state it in the entry body — do not move the entry above newer ones and do not rewrite earlier headings.
|
|
298
299
|
|
|
299
|
-
This is the invariant the "do not rewrite old session entries" rule protects: out-of-order or backdated entries force a human to manually re-sort the log, which is exactly what append-only is meant to prevent.
|
|
300
|
+
This is the invariant the "do not rewrite old session entries" rule protects: out-of-order or backdated entries force a human to manually re-sort the log, which is exactly what append-only is meant to prevent. The `>>` / append-mode discipline makes this invariant mechanical rather than reliant on careful anchor selection.
|
|
300
301
|
|
|
301
302
|
Detailed work logs belong in the nearest active runtime. Add a parent/root summary only when sub-project work changes parent-visible topology, shared design, release behavior, policy inheritance, cross-project dependencies, risks, or active priorities. Do not mirror sub-project logs into root memory.
|
|
302
303
|
|
{memory_seed-2.2.1 → memory_seed-2.2.3}/memory_seed/seed/.memory-seed/hooks/session-log-check.py
RENAMED
|
@@ -16,16 +16,34 @@ if not d.exists():
|
|
|
16
16
|
today = datetime.now().strftime("%Y-%m-%d")
|
|
17
17
|
messages = []
|
|
18
18
|
|
|
19
|
-
#
|
|
19
|
+
# Read today's entry timestamps once — used by both checks below.
|
|
20
|
+
# File mtime is intentionally not used: a git commit touching the session
|
|
21
|
+
# file would defeat a mtime-based staleness check.
|
|
22
|
+
heading_re = re.compile(r"^## (\d{4}-\d{2}-\d{2}) (\d{2}:\d{2})\b")
|
|
23
|
+
today_file = d / f"{today}.md"
|
|
24
|
+
stamps = []
|
|
25
|
+
if today_file.exists():
|
|
26
|
+
for line in today_file.read_text(encoding="utf-8").splitlines():
|
|
27
|
+
m = heading_re.match(line)
|
|
28
|
+
if m:
|
|
29
|
+
stamps.append(f"{m.group(1)} {m.group(2)}")
|
|
30
|
+
|
|
31
|
+
# Staleness check: the most recent timestamped entry heading must be within
|
|
32
|
+
# the last 15 minutes.
|
|
20
33
|
cutoff = datetime.now() - timedelta(minutes=15)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if
|
|
34
|
+
latest = None
|
|
35
|
+
for stamp in stamps:
|
|
36
|
+
try:
|
|
37
|
+
t = datetime.strptime(stamp, "%Y-%m-%d %H:%M")
|
|
38
|
+
if latest is None or t > latest:
|
|
39
|
+
latest = t
|
|
40
|
+
except ValueError:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
if latest is None or latest < cutoff:
|
|
26
44
|
messages.append(
|
|
27
45
|
f"SESSION LOG REMINDER: No .memory-seed/sessions/ entry has been "
|
|
28
|
-
f"
|
|
46
|
+
f"written in the last 15 minutes. If you completed meaningful work "
|
|
29
47
|
f"this turn, append an entry to .memory-seed/sessions/{today}.md "
|
|
30
48
|
f"now — before this turn ends. "
|
|
31
49
|
f"For decisions, use DRAFT labels: "
|
|
@@ -34,22 +52,16 @@ if not recent:
|
|
|
34
52
|
)
|
|
35
53
|
|
|
36
54
|
# Chronology check: today's entry headings must be in non-decreasing time order.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
f"SESSION LOG ORDER WARNING: Entries in "
|
|
48
|
-
f".memory-seed/sessions/{today}.md are not in ascending time "
|
|
49
|
-
f"order. The log is append-only: append new entries at the end "
|
|
50
|
-
f"with the current clock time, never backdated. Do not reorder "
|
|
51
|
-
f"existing entries unless the user asks for a repair."
|
|
52
|
-
)
|
|
55
|
+
if any(stamps[i] < stamps[i - 1] for i in range(1, len(stamps))):
|
|
56
|
+
messages.append(
|
|
57
|
+
f"SESSION LOG ORDER WARNING: Entries in "
|
|
58
|
+
f".memory-seed/sessions/{today}.md are not in ascending time "
|
|
59
|
+
f"order. The log is append-only. To repair: use >> shell "
|
|
60
|
+
f"redirection or Python append mode (open(f, 'a')) to move the "
|
|
61
|
+
f"out-of-order entry to the end of the file with the current "
|
|
62
|
+
f"clock time — do not use an editor replace/insert operation. "
|
|
63
|
+
f"Do not reorder existing entries unless the user asks for a repair."
|
|
64
|
+
)
|
|
53
65
|
|
|
54
66
|
if not messages:
|
|
55
67
|
sys.exit(0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory-seed
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: Portable local memory seed for file-reading AI coding agents
|
|
5
5
|
Author: Jean Nathan Tshibuyi
|
|
6
6
|
License: MIT
|
|
@@ -136,7 +136,7 @@ The result is a lightweight memory workflow you can understand, commit, review,
|
|
|
136
136
|
| Agent or client | Support path |
|
|
137
137
|
| --- | --- |
|
|
138
138
|
| Codex | Starts from `AGENTS.md`; can use MCP when the client supports stdio MCP servers. |
|
|
139
|
-
| Claude Code | Starts from `CLAUDE.md`;
|
|
139
|
+
| Claude Code | Starts from `CLAUDE.md`; MCP server auto-registered via `uvx --from memory-seed`. |
|
|
140
140
|
| Gemini CLI | Starts from `GEMINI.md`. |
|
|
141
141
|
| Other file-reading agents | Start from `AGENTS.md` and follow nearest `.memory-seed/` runtime discovery. |
|
|
142
142
|
| MCP-capable clients | Use `memory_search` and `memory_get_chunk` through `memory-seed-mcp --stdio`. |
|
|
@@ -200,7 +200,7 @@ GEMINI.md
|
|
|
200
200
|
|
|
201
201
|
## Current Version
|
|
202
202
|
|
|
203
|
-
The current reusable control-plane version is `2.
|
|
203
|
+
The current reusable control-plane version is `2.2`.
|
|
204
204
|
|
|
205
205
|
Legacy `.AGENTS/` projects remain supported as a fallback during migration.
|
|
206
206
|
|
|
@@ -365,7 +365,7 @@ For the version distinction (`pip show memory-seed` reports the package version;
|
|
|
365
365
|
|
|
366
366
|
Memory Seed also includes a lightweight MCP server that lets agents search local session memory through structured tool calls instead of shelling out to broad compact summaries.
|
|
367
367
|
|
|
368
|
-
**Auto-registration:** `memory-seed init` and `memory-seed update` automatically register `memory-seed-mcp --stdio` in each supported vendor's config — `.claude/settings.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.gemini/settings.json` (Gemini CLI). No manual config is needed for projects initialised with Memory Seed.
|
|
368
|
+
**Auto-registration:** `memory-seed init` and `memory-seed update` automatically register `uvx --from memory-seed memory-seed-mcp --stdio` in each supported vendor's config — `.claude/settings.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.gemini/settings.json` (Gemini CLI). No manual config is needed for projects initialised with Memory Seed. The `uvx --from` form is used so the command works regardless of whether `~/.local/bin` is on the agent's PATH.
|
|
369
369
|
|
|
370
370
|
If you are configuring the server manually, run it over stdio:
|
|
371
371
|
|
|
@@ -23,7 +23,7 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
23
23
|
return path
|
|
24
24
|
|
|
25
25
|
def test_version_reads_reusable_control_plane_version(self):
|
|
26
|
-
self.assertEqual(get_version(), "2.
|
|
26
|
+
self.assertEqual(get_version(), "2.2")
|
|
27
27
|
|
|
28
28
|
def test_init_dry_run_reports_seed_files_without_writing(self):
|
|
29
29
|
cwd = self.make_project()
|
|
@@ -90,7 +90,7 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
90
90
|
self.assertTrue(result.backed_up[0].startswith(".memory-seed/backups/"))
|
|
91
91
|
self.assertEqual((cwd / result.backed_up[0]).read_text(encoding="utf-8"), "existing")
|
|
92
92
|
self.assertIn(
|
|
93
|
-
"memory-system-version: 2.
|
|
93
|
+
"memory-system-version: 2.2",
|
|
94
94
|
(cwd / "AGENTS.md").read_text(encoding="utf-8"),
|
|
95
95
|
)
|
|
96
96
|
self.assertIn(".memory-seed/backups/", (cwd / ".gitignore").read_text(encoding="utf-8"))
|
|
@@ -111,7 +111,7 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
111
111
|
init_project(cwd=cwd)
|
|
112
112
|
gemini = cwd / "GEMINI.md"
|
|
113
113
|
gemini.write_text(
|
|
114
|
-
gemini.read_text(encoding="utf-8").replace("2.
|
|
114
|
+
gemini.read_text(encoding="utf-8").replace("2.2", "1.1"),
|
|
115
115
|
encoding="utf-8",
|
|
116
116
|
)
|
|
117
117
|
(cwd / "CLAUDE.md").unlink()
|
|
@@ -128,7 +128,7 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
128
128
|
)
|
|
129
129
|
self.assertEqual(
|
|
130
130
|
result.version_mismatches,
|
|
131
|
-
[{"file": "GEMINI.md", "expected": "2.
|
|
131
|
+
[{"file": "GEMINI.md", "expected": "2.2", "actual": "1.1"}],
|
|
132
132
|
)
|
|
133
133
|
|
|
134
134
|
def test_doctor_distinguishes_bootstrap_completeness_from_control_plane_health(self):
|
|
@@ -211,11 +211,11 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
211
211
|
self.assertIn("CLAUDE.md", result.created)
|
|
212
212
|
self.assertTrue(any(path.endswith("/AGENTS.md") for path in result.backed_up))
|
|
213
213
|
self.assertIn(
|
|
214
|
-
"memory-system-version: 2.
|
|
214
|
+
"memory-system-version: 2.2",
|
|
215
215
|
(cwd / "AGENTS.md").read_text(encoding="utf-8"),
|
|
216
216
|
)
|
|
217
217
|
self.assertIn(
|
|
218
|
-
"memory-system-version: 2.
|
|
218
|
+
"memory-system-version: 2.2",
|
|
219
219
|
(cwd / "CLAUDE.md").read_text(encoding="utf-8"),
|
|
220
220
|
)
|
|
221
221
|
self.assertEqual(
|
|
@@ -229,7 +229,7 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
229
229
|
init_project(cwd=cwd)
|
|
230
230
|
agents = cwd / "AGENTS.md"
|
|
231
231
|
agents.write_text(
|
|
232
|
-
agents.read_text(encoding="utf-8").replace("2.
|
|
232
|
+
agents.read_text(encoding="utf-8").replace("2.2", "1.4"),
|
|
233
233
|
encoding="utf-8",
|
|
234
234
|
)
|
|
235
235
|
|
|
@@ -313,14 +313,14 @@ class MemorySeedTests(unittest.TestCase):
|
|
|
313
313
|
self.assertTrue(
|
|
314
314
|
any(path.endswith("/.memory-seed/agent-rules.md") for path in result.backed_up)
|
|
315
315
|
)
|
|
316
|
-
self.assertIn("memory-system-version: 2.
|
|
316
|
+
self.assertIn("memory-system-version: 2.2", rules.read_text(encoding="utf-8"))
|
|
317
317
|
|
|
318
318
|
def test_control_plane_files_report_current_version(self):
|
|
319
319
|
for seed_file in SEED_FILES:
|
|
320
320
|
if not seed_file.source.suffix == ".md":
|
|
321
321
|
continue
|
|
322
322
|
content = seed_file.source.read_text(encoding="utf-8")
|
|
323
|
-
self.assertIn("memory-system-version: 2.
|
|
323
|
+
self.assertIn("memory-system-version: 2.2", content, seed_file.destination)
|
|
324
324
|
|
|
325
325
|
def test_seed_files_use_memory_seed_runtime(self):
|
|
326
326
|
destinations = sorted(seed_file.destination for seed_file in SEED_FILES)
|
|
@@ -593,6 +593,51 @@ class SessionLogOrderingHookTests(unittest.TestCase):
|
|
|
593
593
|
)
|
|
594
594
|
self.assertNotIn("ORDER WARNING", self._run(cwd))
|
|
595
595
|
|
|
596
|
+
def test_staleness_fires_when_no_session_file(self):
|
|
597
|
+
cwd = self.make_project()
|
|
598
|
+
out = self._run(cwd)
|
|
599
|
+
self.assertIn("SESSION LOG REMINDER", out)
|
|
600
|
+
|
|
601
|
+
def test_staleness_fires_when_last_entry_is_old(self):
|
|
602
|
+
import datetime
|
|
603
|
+
|
|
604
|
+
cwd = self.make_project()
|
|
605
|
+
today = datetime.date.today().isoformat()
|
|
606
|
+
(cwd / ".memory-seed" / "sessions" / f"{today}.md").write_text(
|
|
607
|
+
f"## {today} 01:00 - old entry\n\ntext\n",
|
|
608
|
+
encoding="utf-8",
|
|
609
|
+
)
|
|
610
|
+
self.assertIn("SESSION LOG REMINDER", self._run(cwd))
|
|
611
|
+
|
|
612
|
+
def test_staleness_silent_when_recent_entry(self):
|
|
613
|
+
import datetime
|
|
614
|
+
|
|
615
|
+
cwd = self.make_project()
|
|
616
|
+
now = datetime.datetime.now()
|
|
617
|
+
today = now.strftime("%Y-%m-%d")
|
|
618
|
+
recent_time = now.strftime("%H:%M")
|
|
619
|
+
(cwd / ".memory-seed" / "sessions" / f"{today}.md").write_text(
|
|
620
|
+
f"## {today} {recent_time} - recent entry\n\ntext\n",
|
|
621
|
+
encoding="utf-8",
|
|
622
|
+
)
|
|
623
|
+
self.assertNotIn("SESSION LOG REMINDER", self._run(cwd))
|
|
624
|
+
|
|
625
|
+
def test_staleness_not_defeated_by_file_mtime(self):
|
|
626
|
+
import datetime
|
|
627
|
+
import os
|
|
628
|
+
|
|
629
|
+
cwd = self.make_project()
|
|
630
|
+
today = datetime.date.today().isoformat()
|
|
631
|
+
session_file = cwd / ".memory-seed" / "sessions" / f"{today}.md"
|
|
632
|
+
session_file.write_text(
|
|
633
|
+
f"## {today} 01:00 - old entry\n\ntext\n",
|
|
634
|
+
encoding="utf-8",
|
|
635
|
+
)
|
|
636
|
+
# Touch the file to update mtime to now — simulating what git commit does.
|
|
637
|
+
os.utime(session_file, None)
|
|
638
|
+
# Staleness check should still fire because the entry heading is old.
|
|
639
|
+
self.assertIn("SESSION LOG REMINDER", self._run(cwd))
|
|
640
|
+
|
|
596
641
|
|
|
597
642
|
class McpMergeTests(unittest.TestCase):
|
|
598
643
|
def make_project(self):
|
|
@@ -609,8 +654,8 @@ class McpMergeTests(unittest.TestCase):
|
|
|
609
654
|
data = json.loads((cwd / ".claude" / "settings.json").read_text())
|
|
610
655
|
self.assertIn("memory-seed", data["mcpServers"])
|
|
611
656
|
entry = data["mcpServers"]["memory-seed"]
|
|
612
|
-
self.assertEqual(entry["command"], "
|
|
613
|
-
self.assertEqual(entry["args"], ["--stdio"])
|
|
657
|
+
self.assertEqual(entry["command"], "uvx")
|
|
658
|
+
self.assertEqual(entry["args"], ["--from", "memory-seed", "memory-seed-mcp", "--stdio"])
|
|
614
659
|
self.assertEqual(entry["type"], "stdio")
|
|
615
660
|
|
|
616
661
|
def test_init_installs_mcp_for_cursor(self):
|
|
@@ -622,8 +667,8 @@ class McpMergeTests(unittest.TestCase):
|
|
|
622
667
|
data = json.loads((cwd / ".cursor" / "mcp.json").read_text())
|
|
623
668
|
self.assertIn("memory-seed", data["mcpServers"])
|
|
624
669
|
entry = data["mcpServers"]["memory-seed"]
|
|
625
|
-
self.assertEqual(entry["command"], "
|
|
626
|
-
self.assertEqual(entry["args"], ["--stdio"])
|
|
670
|
+
self.assertEqual(entry["command"], "uvx")
|
|
671
|
+
self.assertEqual(entry["args"], ["--from", "memory-seed", "memory-seed-mcp", "--stdio"])
|
|
627
672
|
self.assertNotIn("type", entry)
|
|
628
673
|
|
|
629
674
|
def test_init_installs_mcp_for_gemini(self):
|
|
@@ -635,8 +680,8 @@ class McpMergeTests(unittest.TestCase):
|
|
|
635
680
|
data = json.loads((cwd / ".gemini" / "settings.json").read_text())
|
|
636
681
|
self.assertIn("memory-seed", data["mcpServers"])
|
|
637
682
|
entry = data["mcpServers"]["memory-seed"]
|
|
638
|
-
self.assertEqual(entry["command"], "
|
|
639
|
-
self.assertEqual(entry["args"], ["--stdio"])
|
|
683
|
+
self.assertEqual(entry["command"], "uvx")
|
|
684
|
+
self.assertEqual(entry["args"], ["--from", "memory-seed", "memory-seed-mcp", "--stdio"])
|
|
640
685
|
|
|
641
686
|
def test_mcp_merges_are_idempotent(self):
|
|
642
687
|
from memory_seed.core import (
|
|
@@ -669,7 +714,9 @@ class McpMergeTests(unittest.TestCase):
|
|
|
669
714
|
self.assertTrue(result)
|
|
670
715
|
|
|
671
716
|
data = json.loads(settings.read_text())
|
|
672
|
-
|
|
717
|
+
entry = data["mcpServers"]["memory-seed"]
|
|
718
|
+
self.assertEqual(entry["command"], "uvx")
|
|
719
|
+
self.assertEqual(entry["args"], ["--from", "memory-seed", "memory-seed-mcp", "--stdio"])
|
|
673
720
|
|
|
674
721
|
def test_mcp_merge_preserves_unrelated_mcp_server(self):
|
|
675
722
|
import json
|
|
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
|