memgit 0.6.1__tar.gz → 0.6.2__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.
- {memgit-0.6.1 → memgit-0.6.2}/PKG-INFO +1 -1
- {memgit-0.6.1 → memgit-0.6.2}/memgit/__init__.py +1 -1
- {memgit-0.6.1 → memgit-0.6.2}/memgit/hooks.py +16 -10
- {memgit-0.6.1 → memgit-0.6.2}/memgit/links.py +13 -8
- {memgit-0.6.1 → memgit-0.6.2}/memgit.egg-info/PKG-INFO +1 -1
- {memgit-0.6.1 → memgit-0.6.2}/pyproject.toml +1 -1
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_v060.py +22 -0
- {memgit-0.6.1 → memgit-0.6.2}/LICENSE +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/README.md +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cli.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cloud/__init__.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cloud/client.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cloud/commands.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cloud/crypto.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cloud/state.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/cloud/sync.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/delivery.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/gitdigest.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/graph.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/http_server.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/importer.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/mcp_server.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/models.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/project.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/repo.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/scorer.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/store.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/tokens.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/toon.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit/usage.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit.egg-info/SOURCES.txt +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit.egg-info/dependency_links.txt +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit.egg-info/entry_points.txt +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit.egg-info/requires.txt +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/memgit.egg-info/top_level.txt +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/setup.cfg +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_accrue.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_advanced.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_aliases.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_core.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_delivery.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_setup.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_store_repo.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_toon.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_v020.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_v030.py +0 -0
- {memgit-0.6.1 → memgit-0.6.2}/tests/test_v040.py +0 -0
|
@@ -137,7 +137,7 @@ def prompt_recall() -> int:
|
|
|
137
137
|
# (Measured before this line existed: 6.8% of recall-injected sessions
|
|
138
138
|
# ever ran a search.) Hinted-but-not-shown memories are deliberately NOT
|
|
139
139
|
# added to the dedup file or the usage ledger — they weren't surfaced.
|
|
140
|
-
more_line, hinted_tag = _depth_hint(results, mnemonics, seen)
|
|
140
|
+
more_line, hinted_tag = _depth_hint(results, mnemonics, seen, project)
|
|
141
141
|
if more_line:
|
|
142
142
|
lines.append(more_line)
|
|
143
143
|
lines.append('</memgit-recall>')
|
|
@@ -169,19 +169,25 @@ def prompt_recall() -> int:
|
|
|
169
169
|
return 0
|
|
170
170
|
|
|
171
171
|
|
|
172
|
-
def _depth_hint(results, mnemonics, seen: set[str]
|
|
172
|
+
def _depth_hint(results, mnemonics, seen: set[str],
|
|
173
|
+
project: Optional[str] = None) -> tuple[Optional[str], Optional[str]]:
|
|
173
174
|
"""One '+N more on <tag>' line advertising depth behind the injected hits.
|
|
174
175
|
|
|
175
176
|
Picks the single best tag: among tags carried by the injected results,
|
|
176
177
|
the one with the most ACTIVE memories that were neither injected nor
|
|
177
178
|
already seen this session. Requires >= 2 (a count of 1 is not depth and
|
|
178
|
-
teaches the model to ignore counts).
|
|
179
|
-
|
|
179
|
+
teaches the model to ignore counts). Project-label tags are excluded —
|
|
180
|
+
the same noise rule as the memory index and context recall. At most one
|
|
181
|
+
line, ever. Returns (line, tag) — both None when nothing clears the bar.
|
|
180
182
|
"""
|
|
183
|
+
from .links import label_noise
|
|
184
|
+
noise = label_noise(project)
|
|
181
185
|
injected = {r.mnemonic.slug for r in results}
|
|
182
186
|
tags: set[str] = set()
|
|
183
187
|
for r in results:
|
|
184
|
-
tags.update(t.strip() for t in r.mnemonic.tags
|
|
188
|
+
tags.update(t.strip() for t in r.mnemonic.tags
|
|
189
|
+
if t.strip() and len(t.strip()) <= 40
|
|
190
|
+
and t.strip().lower() not in noise)
|
|
185
191
|
if not tags:
|
|
186
192
|
return None, None
|
|
187
193
|
best_tag, best_n = None, 0
|
|
@@ -261,14 +267,14 @@ def context_recall() -> int:
|
|
|
261
267
|
if not tokens:
|
|
262
268
|
return 0
|
|
263
269
|
# Tag matches a path token exactly, case-insensitive. (Substring matching
|
|
264
|
-
# would fire 'api' against half the filesystem.)
|
|
265
|
-
#
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
# would fire 'api' against half the filesystem.) Project-label tags are
|
|
271
|
+
# excluded via the shared noise rule.
|
|
272
|
+
from .links import label_noise
|
|
273
|
+
noise = label_noise(project)
|
|
268
274
|
lower_tags = {t.lower(): t for t in tagmap}
|
|
269
275
|
candidates = []
|
|
270
276
|
for tok in tokens:
|
|
271
|
-
if tok in
|
|
277
|
+
if tok in noise:
|
|
272
278
|
continue
|
|
273
279
|
tag = lower_tags.get(tok)
|
|
274
280
|
if not tag:
|
|
@@ -149,6 +149,17 @@ def normalize_slug_list(value) -> list[str]:
|
|
|
149
149
|
|
|
150
150
|
# ── entity index ──────────────────────────────────────────────────────────────
|
|
151
151
|
|
|
152
|
+
def label_noise(project: Optional[str]) -> set[str]:
|
|
153
|
+
"""Lowercased tags that are noise for the current workspace: the project
|
|
154
|
+
label and its '-'-components. Importer-derived label tags ('personal',
|
|
155
|
+
'business') are not topics, and every path inside a workspace contains
|
|
156
|
+
the label's words — so no depth surface may advertise them."""
|
|
157
|
+
proj_lower = (project or '').lower()
|
|
158
|
+
parts = {p for p in proj_lower.split('-') if p}
|
|
159
|
+
if proj_lower:
|
|
160
|
+
parts.add(proj_lower)
|
|
161
|
+
return parts
|
|
162
|
+
|
|
152
163
|
def entity_index(mnemonics: list[Mnemonic], project: Optional[str],
|
|
153
164
|
min_count: int = 2, cap: int = 8) -> list[tuple[str, int]]:
|
|
154
165
|
"""Tag → memory-count pairs advertising the store's depth on each topic.
|
|
@@ -175,16 +186,10 @@ def entity_index(mnemonics: list[Mnemonic], project: Optional[str],
|
|
|
175
186
|
tag = tag.strip()
|
|
176
187
|
if tag:
|
|
177
188
|
counts[tag] += 1
|
|
178
|
-
|
|
179
|
-
# topics ("Personal-business" seeds importer tags 'personal'/'business'
|
|
180
|
-
# that would dominate the index and lead to junk searches).
|
|
181
|
-
proj_lower = (project or '').lower()
|
|
182
|
-
label_parts = {p for p in proj_lower.split('-') if p}
|
|
189
|
+
noise = label_noise(project)
|
|
183
190
|
items = [
|
|
184
191
|
(tag, n) for tag, n in counts.items()
|
|
185
|
-
if n >= min_count
|
|
186
|
-
and tag.lower() != proj_lower
|
|
187
|
-
and tag.lower() not in label_parts
|
|
192
|
+
if n >= min_count and tag.lower() not in noise
|
|
188
193
|
]
|
|
189
194
|
items.sort(key=lambda tn: (-tn[1], tn[0]))
|
|
190
195
|
return items[:cap]
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memgit"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.2"
|
|
8
8
|
description = "Git for AI memory — version-controlled context persistence across Claude, GPT, Gemini, Cursor, Windsurf, and more"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -373,6 +373,28 @@ class TestEntityIndex:
|
|
|
373
373
|
injected = {l.strip() for l in seen.splitlines()}
|
|
374
374
|
assert len(injected) <= 3
|
|
375
375
|
|
|
376
|
+
def test_depth_hint_skips_project_label_tags(self, repo, monkeypatch, capsys):
|
|
377
|
+
"""Third surface of the label-noise bug (found live: '+75 more on
|
|
378
|
+
'business''): the recall depth hint must apply the same exclusion as
|
|
379
|
+
the memory index and context recall."""
|
|
380
|
+
from memgit.hooks import prompt_recall
|
|
381
|
+
proj = "Personal-business"
|
|
382
|
+
repo.add(_mk("zebpay-caps", rule="ZebPay caps INR withdrawals at 10L/day",
|
|
383
|
+
tags=["business"], project=proj))
|
|
384
|
+
for i in range(5):
|
|
385
|
+
repo.add(_mk(f"biz-{i}", rule=f"unrelated business fact {i}",
|
|
386
|
+
tags=["business"], project=proj))
|
|
387
|
+
repo.commit(message="seed")
|
|
388
|
+
monkeypatch.setattr("memgit.hooks.project_label_from_path",
|
|
389
|
+
lambda p: proj, raising=False)
|
|
390
|
+
import memgit.project as project_mod
|
|
391
|
+
monkeypatch.setattr(project_mod, "project_label_from_path", lambda p: proj)
|
|
392
|
+
rc, out = _run_hook(monkeypatch, capsys, prompt_recall, {
|
|
393
|
+
"prompt": "what are the zebpay withdrawal caps and limits?",
|
|
394
|
+
"cwd": "/tmp", "session_id": "noise1"}, repo)
|
|
395
|
+
assert "zebpay-caps" in out
|
|
396
|
+
assert "more saved on 'business'" not in out
|
|
397
|
+
|
|
376
398
|
def test_no_hint_when_depth_below_two(self, repo, monkeypatch, capsys):
|
|
377
399
|
from memgit.hooks import prompt_recall
|
|
378
400
|
repo.add(_mk("zebpay-caps", rule="ZebPay caps INR withdrawals at 10L/day",
|
|
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
|