memgit 0.1.4__tar.gz → 0.1.5__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.1.4 → memgit-0.1.5}/PKG-INFO +3 -3
- {memgit-0.1.4 → memgit-0.1.5}/README.md +2 -2
- {memgit-0.1.4 → memgit-0.1.5}/memgit/cli.py +63 -12
- {memgit-0.1.4 → memgit-0.1.5}/memgit.egg-info/PKG-INFO +3 -3
- {memgit-0.1.4 → memgit-0.1.5}/memgit.egg-info/SOURCES.txt +1 -0
- {memgit-0.1.4 → memgit-0.1.5}/pyproject.toml +1 -1
- memgit-0.1.5/tests/test_setup.py +78 -0
- {memgit-0.1.4 → memgit-0.1.5}/LICENSE +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/__init__.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/graph.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/http_server.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/importer.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/mcp_server.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/models.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/repo.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/scorer.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/store.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/tokens.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit/toon.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit.egg-info/dependency_links.txt +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit.egg-info/entry_points.txt +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit.egg-info/requires.txt +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/memgit.egg-info/top_level.txt +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/setup.cfg +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/tests/test_advanced.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/tests/test_store_repo.py +0 -0
- {memgit-0.1.4 → memgit-0.1.5}/tests/test_toon.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memgit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Git for AI memory — version-controlled context persistence across Claude, GPT, Gemini, Cursor, Windsurf, and more
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://memgit.dev
|
|
@@ -353,11 +353,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
353
353
|
- [x] `memgit git push/pull` — team sync via standard git
|
|
354
354
|
- [x] Flat `memories/` directory — grep/diff/blame your memories
|
|
355
355
|
- [x] D3.js graph visualization of memory relationships
|
|
356
|
-
- [x] PyPI + Homebrew (tap) + npm published (v0.1.
|
|
356
|
+
- [x] PyPI + Homebrew (tap) + npm published (v0.1.5)
|
|
357
357
|
- [ ] Chocolatey (not yet live on community.chocolatey.org)
|
|
358
358
|
- [x] Interactive setup wizard (`memgit setup`)
|
|
359
359
|
- [x] Smart `memgit init` (auto-detects tool, no path needed)
|
|
360
|
-
- [x] VS Code extension (v0.1.
|
|
360
|
+
- [x] VS Code extension (v0.1.5, Marketplace: code416-memgit.memgit)
|
|
361
361
|
- [ ] JetBrains plugin (Phase 3)
|
|
362
362
|
- [ ] Semantic search via embeddings (Phase 4)
|
|
363
363
|
- [x] memgit.dev website (live)
|
|
@@ -322,11 +322,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
322
322
|
- [x] `memgit git push/pull` — team sync via standard git
|
|
323
323
|
- [x] Flat `memories/` directory — grep/diff/blame your memories
|
|
324
324
|
- [x] D3.js graph visualization of memory relationships
|
|
325
|
-
- [x] PyPI + Homebrew (tap) + npm published (v0.1.
|
|
325
|
+
- [x] PyPI + Homebrew (tap) + npm published (v0.1.5)
|
|
326
326
|
- [ ] Chocolatey (not yet live on community.chocolatey.org)
|
|
327
327
|
- [x] Interactive setup wizard (`memgit setup`)
|
|
328
328
|
- [x] Smart `memgit init` (auto-detects tool, no path needed)
|
|
329
|
-
- [x] VS Code extension (v0.1.
|
|
329
|
+
- [x] VS Code extension (v0.1.5, Marketplace: code416-memgit.memgit)
|
|
330
330
|
- [ ] JetBrains plugin (Phase 3)
|
|
331
331
|
- [ ] Semantic search via embeddings (Phase 4)
|
|
332
332
|
- [x] memgit.dev website (live)
|
|
@@ -1143,7 +1143,11 @@ def _patch_mcp_servers(config_path: Path, dry_run: bool = False) -> str:
|
|
|
1143
1143
|
try:
|
|
1144
1144
|
data = _json.loads(config_path.read_text(encoding='utf-8'))
|
|
1145
1145
|
except _json.JSONDecodeError:
|
|
1146
|
-
|
|
1146
|
+
# Never clobber an existing config we can't parse — for Claude Code
|
|
1147
|
+
# this file (~/.claude.json) holds all user state, not just MCP.
|
|
1148
|
+
raise RuntimeError(
|
|
1149
|
+
f'{config_path} exists but is not valid JSON — fix it manually, then re-run setup'
|
|
1150
|
+
)
|
|
1147
1151
|
else:
|
|
1148
1152
|
data = {}
|
|
1149
1153
|
|
|
@@ -1189,7 +1193,11 @@ def _patch_continue(config_path: Path, dry_run: bool = False) -> str:
|
|
|
1189
1193
|
return 'registered'
|
|
1190
1194
|
|
|
1191
1195
|
|
|
1192
|
-
# Targets: (label,
|
|
1196
|
+
# Targets: (label, config_path, patch_fn, detect_path)
|
|
1197
|
+
# detect_path: existence marks the tool as installed; None falls back to
|
|
1198
|
+
# "config file or its parent dir exists". Claude Code needs an explicit one
|
|
1199
|
+
# because its real MCP config (~/.claude.json) sits directly in $HOME —
|
|
1200
|
+
# Claude Code ignores mcpServers in ~/.claude/settings.json.
|
|
1193
1201
|
def _all_targets():
|
|
1194
1202
|
home = Path.home()
|
|
1195
1203
|
app_support = home / 'Library' / 'Application Support'
|
|
@@ -1197,43 +1205,51 @@ def _all_targets():
|
|
|
1197
1205
|
return [
|
|
1198
1206
|
(
|
|
1199
1207
|
'Claude Code',
|
|
1200
|
-
home / '.claude
|
|
1208
|
+
home / '.claude.json',
|
|
1201
1209
|
_patch_mcp_servers,
|
|
1210
|
+
home / '.claude',
|
|
1202
1211
|
),
|
|
1203
1212
|
(
|
|
1204
1213
|
'Claude Desktop (macOS)',
|
|
1205
1214
|
app_support / 'Claude' / 'claude_desktop_config.json',
|
|
1206
1215
|
_patch_mcp_servers,
|
|
1216
|
+
None,
|
|
1207
1217
|
),
|
|
1208
1218
|
(
|
|
1209
1219
|
'Claude Desktop (Linux)',
|
|
1210
1220
|
linux_config / 'Claude' / 'claude_desktop_config.json',
|
|
1211
1221
|
_patch_mcp_servers,
|
|
1222
|
+
None,
|
|
1212
1223
|
),
|
|
1213
1224
|
(
|
|
1214
1225
|
'Cursor',
|
|
1215
1226
|
home / '.cursor' / 'mcp.json',
|
|
1216
1227
|
_patch_mcp_servers,
|
|
1228
|
+
None,
|
|
1217
1229
|
),
|
|
1218
1230
|
(
|
|
1219
1231
|
'Windsurf',
|
|
1220
1232
|
home / '.windsurf' / 'mcp.json',
|
|
1221
1233
|
_patch_mcp_servers,
|
|
1234
|
+
None,
|
|
1222
1235
|
),
|
|
1223
1236
|
(
|
|
1224
1237
|
'Cline (VS Code)',
|
|
1225
1238
|
app_support / 'Code' / 'User' / 'globalStorage' / 'saoudrizwan.claude-dev' / 'settings' / 'cline_mcp_settings.json',
|
|
1226
1239
|
_patch_mcp_servers,
|
|
1240
|
+
None,
|
|
1227
1241
|
),
|
|
1228
1242
|
(
|
|
1229
1243
|
'Roo-Code (VS Code)',
|
|
1230
1244
|
app_support / 'Code' / 'User' / 'globalStorage' / 'rooveterinaryinc.roo-cline' / 'settings' / 'cline_mcp_settings.json',
|
|
1231
1245
|
_patch_mcp_servers,
|
|
1246
|
+
None,
|
|
1232
1247
|
),
|
|
1233
1248
|
(
|
|
1234
1249
|
'Continue.dev',
|
|
1235
1250
|
home / '.continue' / 'config.json',
|
|
1236
1251
|
_patch_continue,
|
|
1252
|
+
None,
|
|
1237
1253
|
),
|
|
1238
1254
|
]
|
|
1239
1255
|
|
|
@@ -1243,10 +1259,13 @@ def _setup_wizard() -> None:
|
|
|
1243
1259
|
targets = _all_targets()
|
|
1244
1260
|
|
|
1245
1261
|
detected, missing = [], []
|
|
1246
|
-
for label, config_path, patch_fn in targets:
|
|
1247
|
-
|
|
1248
|
-
(
|
|
1262
|
+
for label, config_path, patch_fn, detect_path in targets:
|
|
1263
|
+
installed = (
|
|
1264
|
+
detect_path.exists()
|
|
1265
|
+
if detect_path is not None
|
|
1266
|
+
else config_path.exists() or config_path.parent.exists()
|
|
1249
1267
|
)
|
|
1268
|
+
(detected if installed else missing).append((label, config_path, patch_fn))
|
|
1250
1269
|
|
|
1251
1270
|
console.print('[bold]memgit setup[/bold] — interactive tool registration\n')
|
|
1252
1271
|
|
|
@@ -1327,6 +1346,33 @@ def _run_target(label: str, config_path: Path, patch_fn, dry_run: bool) -> None:
|
|
|
1327
1346
|
console.print(f'[red]✗[/red] {label}: {e}')
|
|
1328
1347
|
|
|
1329
1348
|
|
|
1349
|
+
def _cleanup_legacy_claude_code(dry_run: bool = False) -> None:
|
|
1350
|
+
"""Drop the memgit entry from ~/.claude/settings.json if present.
|
|
1351
|
+
|
|
1352
|
+
Releases before 0.1.5 registered there, but Claude Code only loads MCP
|
|
1353
|
+
servers from ~/.claude.json — the old entry is dead weight.
|
|
1354
|
+
"""
|
|
1355
|
+
legacy = Path.home() / '.claude' / 'settings.json'
|
|
1356
|
+
if not legacy.exists():
|
|
1357
|
+
return
|
|
1358
|
+
try:
|
|
1359
|
+
data = _json.loads(legacy.read_text(encoding='utf-8'))
|
|
1360
|
+
except _json.JSONDecodeError:
|
|
1361
|
+
return
|
|
1362
|
+
servers = data.get('mcpServers')
|
|
1363
|
+
if not isinstance(servers, dict) or 'memgit' not in servers:
|
|
1364
|
+
return
|
|
1365
|
+
servers.pop('memgit')
|
|
1366
|
+
if not servers:
|
|
1367
|
+
data.pop('mcpServers', None)
|
|
1368
|
+
if not dry_run:
|
|
1369
|
+
_write_json_safe(legacy, data)
|
|
1370
|
+
console.print(
|
|
1371
|
+
f'[yellow]↻[/yellow] removed stale memgit entry from {legacy} '
|
|
1372
|
+
f'[dim](Claude Code ignores mcpServers there)[/dim]'
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
|
|
1330
1376
|
@setup.command('all')
|
|
1331
1377
|
@click.option('--dry-run', is_flag=True, help='Show what would be changed without writing files.')
|
|
1332
1378
|
def setup_all(dry_run):
|
|
@@ -1340,14 +1386,18 @@ def setup_all(dry_run):
|
|
|
1340
1386
|
|
|
1341
1387
|
registered = 0
|
|
1342
1388
|
skipped = 0
|
|
1343
|
-
for label, config_path, patch_fn in _all_targets():
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1389
|
+
for label, config_path, patch_fn, detect_path in _all_targets():
|
|
1390
|
+
installed = (
|
|
1391
|
+
detect_path.exists()
|
|
1392
|
+
if detect_path is not None
|
|
1393
|
+
else config_path.exists() or config_path.parent.exists()
|
|
1394
|
+
)
|
|
1395
|
+
if not installed:
|
|
1347
1396
|
skipped += 1
|
|
1348
1397
|
continue
|
|
1349
1398
|
_run_target(label, config_path, patch_fn, dry_run)
|
|
1350
1399
|
registered += 1
|
|
1400
|
+
_cleanup_legacy_claude_code(dry_run)
|
|
1351
1401
|
|
|
1352
1402
|
console.print(f'\n[dim]{registered} tool(s) processed, {skipped} not installed (skipped)[/dim]')
|
|
1353
1403
|
if not dry_run and registered:
|
|
@@ -1357,9 +1407,10 @@ def setup_all(dry_run):
|
|
|
1357
1407
|
@setup.command('claude-code')
|
|
1358
1408
|
@click.option('--dry-run', is_flag=True)
|
|
1359
1409
|
def setup_claude_code(dry_run):
|
|
1360
|
-
"""Register with Claude Code (~/.claude
|
|
1361
|
-
path = Path.home() / '.claude
|
|
1410
|
+
"""Register with Claude Code (~/.claude.json, user scope)."""
|
|
1411
|
+
path = Path.home() / '.claude.json'
|
|
1362
1412
|
_run_target('Claude Code', path, _patch_mcp_servers, dry_run)
|
|
1413
|
+
_cleanup_legacy_claude_code(dry_run)
|
|
1363
1414
|
|
|
1364
1415
|
|
|
1365
1416
|
@setup.command('claude-desktop')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memgit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Git for AI memory — version-controlled context persistence across Claude, GPT, Gemini, Cursor, Windsurf, and more
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://memgit.dev
|
|
@@ -353,11 +353,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
353
353
|
- [x] `memgit git push/pull` — team sync via standard git
|
|
354
354
|
- [x] Flat `memories/` directory — grep/diff/blame your memories
|
|
355
355
|
- [x] D3.js graph visualization of memory relationships
|
|
356
|
-
- [x] PyPI + Homebrew (tap) + npm published (v0.1.
|
|
356
|
+
- [x] PyPI + Homebrew (tap) + npm published (v0.1.5)
|
|
357
357
|
- [ ] Chocolatey (not yet live on community.chocolatey.org)
|
|
358
358
|
- [x] Interactive setup wizard (`memgit setup`)
|
|
359
359
|
- [x] Smart `memgit init` (auto-detects tool, no path needed)
|
|
360
|
-
- [x] VS Code extension (v0.1.
|
|
360
|
+
- [x] VS Code extension (v0.1.5, Marketplace: code416-memgit.memgit)
|
|
361
361
|
- [ ] JetBrains plugin (Phase 3)
|
|
362
362
|
- [ ] Semantic search via embeddings (Phase 4)
|
|
363
363
|
- [x] memgit.dev website (live)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memgit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.5"
|
|
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"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Tests for `memgit setup` MCP registration paths."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from memgit import cli as cli_mod
|
|
9
|
+
from memgit.cli import (
|
|
10
|
+
_all_targets,
|
|
11
|
+
_cleanup_legacy_claude_code,
|
|
12
|
+
_patch_mcp_servers,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def fake_home(tmp_path, monkeypatch):
|
|
18
|
+
monkeypatch.setattr(Path, 'home', classmethod(lambda cls: tmp_path))
|
|
19
|
+
return tmp_path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_claude_code_target_is_claude_json(fake_home):
|
|
23
|
+
"""Claude Code loads MCP servers from ~/.claude.json, NOT ~/.claude/settings.json."""
|
|
24
|
+
targets = {label: (config, detect) for label, config, _, detect in _all_targets()}
|
|
25
|
+
config, detect = targets['Claude Code']
|
|
26
|
+
assert config == fake_home / '.claude.json'
|
|
27
|
+
assert detect == fake_home / '.claude'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_patch_registers_and_is_idempotent(fake_home):
|
|
31
|
+
config = fake_home / '.claude.json'
|
|
32
|
+
assert _patch_mcp_servers(config) == 'registered'
|
|
33
|
+
data = json.loads(config.read_text())
|
|
34
|
+
assert 'memgit' in data['mcpServers']
|
|
35
|
+
assert data['mcpServers']['memgit']['args'][-1] == 'serve'
|
|
36
|
+
assert _patch_mcp_servers(config) == 'already registered'
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_patch_preserves_existing_state(fake_home):
|
|
40
|
+
"""~/.claude.json holds all Claude Code user state — never drop other keys."""
|
|
41
|
+
config = fake_home / '.claude.json'
|
|
42
|
+
config.write_text(json.dumps({'projects': {'/x': {}}, 'mcpServers': {'other': {'command': 'x'}}}))
|
|
43
|
+
_patch_mcp_servers(config)
|
|
44
|
+
data = json.loads(config.read_text())
|
|
45
|
+
assert data['projects'] == {'/x': {}}
|
|
46
|
+
assert set(data['mcpServers']) == {'other', 'memgit'}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_patch_refuses_to_clobber_invalid_json(fake_home):
|
|
50
|
+
config = fake_home / '.claude.json'
|
|
51
|
+
config.write_text('{not json')
|
|
52
|
+
with pytest.raises(RuntimeError, match='not valid JSON'):
|
|
53
|
+
_patch_mcp_servers(config)
|
|
54
|
+
assert config.read_text() == '{not json'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_cleanup_removes_legacy_entry(fake_home):
|
|
58
|
+
legacy = fake_home / '.claude' / 'settings.json'
|
|
59
|
+
legacy.parent.mkdir()
|
|
60
|
+
legacy.write_text(json.dumps({'theme': 'dark', 'mcpServers': {'memgit': {'command': 'memgit'}}}))
|
|
61
|
+
_cleanup_legacy_claude_code()
|
|
62
|
+
data = json.loads(legacy.read_text())
|
|
63
|
+
assert data['theme'] == 'dark'
|
|
64
|
+
assert 'mcpServers' not in data
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_cleanup_keeps_other_servers(fake_home):
|
|
68
|
+
legacy = fake_home / '.claude' / 'settings.json'
|
|
69
|
+
legacy.parent.mkdir()
|
|
70
|
+
legacy.write_text(json.dumps({'mcpServers': {'memgit': {}, 'other': {'command': 'x'}}}))
|
|
71
|
+
_cleanup_legacy_claude_code()
|
|
72
|
+
data = json.loads(legacy.read_text())
|
|
73
|
+
assert set(data['mcpServers']) == {'other'}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_cleanup_noop_without_legacy_file(fake_home):
|
|
77
|
+
_cleanup_legacy_claude_code() # must not raise or create files
|
|
78
|
+
assert not (fake_home / '.claude' / 'settings.json').exists()
|
|
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
|