superlocalmemory 2.8.2 → 2.8.5

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 (73) hide show
  1. package/ATTRIBUTION.md +1 -1
  2. package/CHANGELOG.md +17 -0
  3. package/README.md +7 -5
  4. package/api_server.py +5 -0
  5. package/bin/slm +35 -0
  6. package/bin/slm.bat +3 -3
  7. package/docs/SECURITY-QUICK-REFERENCE.md +214 -0
  8. package/install.ps1 +11 -11
  9. package/mcp_server.py +78 -10
  10. package/package.json +2 -2
  11. package/requirements-core.txt +16 -18
  12. package/requirements-learning.txt +8 -8
  13. package/requirements.txt +9 -7
  14. package/scripts/prepack.js +33 -0
  15. package/scripts/verify-v27.ps1 +301 -0
  16. package/src/agent_registry.py +32 -28
  17. package/src/auto_backup.py +12 -6
  18. package/src/cache_manager.py +2 -2
  19. package/src/compression/__init__.py +25 -0
  20. package/src/compression/cli.py +150 -0
  21. package/src/compression/cold_storage.py +217 -0
  22. package/src/compression/config.py +72 -0
  23. package/src/compression/orchestrator.py +133 -0
  24. package/src/compression/tier2_compressor.py +228 -0
  25. package/src/compression/tier3_compressor.py +153 -0
  26. package/src/compression/tier_classifier.py +148 -0
  27. package/src/db_connection_manager.py +5 -5
  28. package/src/event_bus.py +24 -22
  29. package/src/hnsw_index.py +3 -3
  30. package/src/learning/__init__.py +5 -4
  31. package/src/learning/adaptive_ranker.py +14 -265
  32. package/src/learning/bootstrap/__init__.py +69 -0
  33. package/src/learning/bootstrap/constants.py +93 -0
  34. package/src/learning/bootstrap/db_queries.py +316 -0
  35. package/src/learning/bootstrap/sampling.py +82 -0
  36. package/src/learning/bootstrap/text_utils.py +71 -0
  37. package/src/learning/cross_project_aggregator.py +58 -57
  38. package/src/learning/db/__init__.py +40 -0
  39. package/src/learning/db/constants.py +44 -0
  40. package/src/learning/db/schema.py +279 -0
  41. package/src/learning/learning_db.py +15 -234
  42. package/src/learning/ranking/__init__.py +33 -0
  43. package/src/learning/ranking/constants.py +84 -0
  44. package/src/learning/ranking/helpers.py +278 -0
  45. package/src/learning/source_quality_scorer.py +66 -65
  46. package/src/learning/synthetic_bootstrap.py +28 -310
  47. package/src/memory/__init__.py +36 -0
  48. package/src/memory/cli.py +205 -0
  49. package/src/memory/constants.py +39 -0
  50. package/src/memory/helpers.py +28 -0
  51. package/src/memory/schema.py +166 -0
  52. package/src/memory-profiles.py +94 -86
  53. package/src/memory-reset.py +187 -185
  54. package/src/memory_compression.py +2 -2
  55. package/src/memory_store_v2.py +44 -354
  56. package/src/migrate_v1_to_v2.py +11 -10
  57. package/src/patterns/analyzers.py +104 -100
  58. package/src/patterns/learner.py +17 -13
  59. package/src/patterns/scoring.py +25 -21
  60. package/src/patterns/store.py +40 -38
  61. package/src/patterns/terminology.py +53 -51
  62. package/src/provenance_tracker.py +2 -2
  63. package/src/qualixar_attribution.py +1 -1
  64. package/src/search/engine.py +16 -14
  65. package/src/search/index_loader.py +13 -11
  66. package/src/setup_validator.py +160 -158
  67. package/src/subscription_manager.py +20 -18
  68. package/src/tree/builder.py +66 -64
  69. package/src/tree/nodes.py +103 -97
  70. package/src/tree/queries.py +142 -137
  71. package/src/tree/schema.py +46 -42
  72. package/src/webhook_dispatcher.py +3 -3
  73. package/ui_server.py +7 -4
@@ -59,9 +59,19 @@ except ImportError:
59
59
  import logging
60
60
  logger = logging.getLogger(__name__)
61
61
 
62
- MEMORY_DIR = Path.home() / ".claude-memory"
63
- DB_PATH = MEMORY_DIR / "memory.db"
64
- VECTORS_PATH = MEMORY_DIR / "vectors"
62
+ # Import constants and utilities from memory package
63
+ from memory.constants import (
64
+ MEMORY_DIR, DB_PATH, VECTORS_PATH,
65
+ MAX_CONTENT_SIZE, MAX_SUMMARY_SIZE, MAX_TAG_LENGTH, MAX_TAGS,
66
+ CREATOR_METADATA
67
+ )
68
+ from memory.schema import (
69
+ V2_COLUMNS, V28_MIGRATIONS, V2_INDEXES,
70
+ get_memories_table_sql, get_sessions_table_sql, get_fts_table_sql,
71
+ get_fts_trigger_insert_sql, get_fts_trigger_delete_sql, get_fts_trigger_update_sql,
72
+ get_creator_metadata_table_sql
73
+ )
74
+ from memory.helpers import format_content
65
75
 
66
76
 
67
77
  class MemoryStoreV2:
@@ -247,62 +257,11 @@ class MemoryStoreV2:
247
257
  existing_columns = {row[1] for row in cursor.fetchall()}
248
258
 
249
259
  # Main memories table (V1 compatible + V2 extensions)
250
- cursor.execute('''
251
- CREATE TABLE IF NOT EXISTS memories (
252
- id INTEGER PRIMARY KEY AUTOINCREMENT,
253
- content TEXT NOT NULL,
254
- summary TEXT,
255
-
256
- -- Organization
257
- project_path TEXT,
258
- project_name TEXT,
259
- tags TEXT,
260
- category TEXT,
261
-
262
- -- Hierarchy (Layer 2 link)
263
- parent_id INTEGER,
264
- tree_path TEXT,
265
- depth INTEGER DEFAULT 0,
266
-
267
- -- Metadata
268
- memory_type TEXT DEFAULT 'session',
269
- importance INTEGER DEFAULT 5,
270
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
271
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
272
- last_accessed TIMESTAMP,
273
- access_count INTEGER DEFAULT 0,
274
-
275
- -- Deduplication
276
- content_hash TEXT UNIQUE,
277
-
278
- -- Graph (Layer 3 link)
279
- cluster_id INTEGER,
280
-
281
- FOREIGN KEY (parent_id) REFERENCES memories(id) ON DELETE CASCADE
282
- )
283
- ''')
260
+ cursor.execute(get_memories_table_sql())
284
261
 
285
262
  # Add missing V2 columns to existing table (migration support)
286
263
  # This handles upgrades from very old databases that might be missing columns
287
- v2_columns = {
288
- 'summary': 'TEXT',
289
- 'project_path': 'TEXT',
290
- 'project_name': 'TEXT',
291
- 'category': 'TEXT',
292
- 'parent_id': 'INTEGER',
293
- 'tree_path': 'TEXT',
294
- 'depth': 'INTEGER DEFAULT 0',
295
- 'memory_type': 'TEXT DEFAULT "session"',
296
- 'importance': 'INTEGER DEFAULT 5',
297
- 'updated_at': 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
298
- 'last_accessed': 'TIMESTAMP',
299
- 'access_count': 'INTEGER DEFAULT 0',
300
- 'content_hash': 'TEXT',
301
- 'cluster_id': 'INTEGER',
302
- 'profile': 'TEXT DEFAULT "default"'
303
- }
304
-
305
- for col_name, col_type in v2_columns.items():
264
+ for col_name, col_type in V2_COLUMNS.items():
306
265
  if col_name not in existing_columns:
307
266
  try:
308
267
  cursor.execute(f'ALTER TABLE memories ADD COLUMN {col_name} {col_type}')
@@ -311,73 +270,25 @@ class MemoryStoreV2:
311
270
  pass
312
271
 
313
272
  # v2.8.0 schema migration — lifecycle + access control columns
314
- _v28_migrations = [
315
- ("lifecycle_state", "TEXT DEFAULT 'active'"),
316
- ("lifecycle_updated_at", "TIMESTAMP"),
317
- ("lifecycle_history", "TEXT DEFAULT '[]'"),
318
- ("access_level", "TEXT DEFAULT 'public'"),
319
- ]
320
- for col_name, col_type in _v28_migrations:
273
+ for col_name, col_type in V28_MIGRATIONS:
321
274
  try:
322
275
  cursor.execute(f"ALTER TABLE memories ADD COLUMN {col_name} {col_type}")
323
276
  except sqlite3.OperationalError:
324
277
  pass # Column already exists
325
278
 
326
279
  # Sessions table (V1 compatible)
327
- cursor.execute('''
328
- CREATE TABLE IF NOT EXISTS sessions (
329
- id INTEGER PRIMARY KEY AUTOINCREMENT,
330
- session_id TEXT UNIQUE,
331
- project_path TEXT,
332
- started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
333
- ended_at TIMESTAMP,
334
- summary TEXT
335
- )
336
- ''')
280
+ cursor.execute(get_sessions_table_sql())
337
281
 
338
282
  # Full-text search index (V1 compatible)
339
- cursor.execute('''
340
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts
341
- USING fts5(content, summary, tags, content='memories', content_rowid='id')
342
- ''')
283
+ cursor.execute(get_fts_table_sql())
343
284
 
344
285
  # FTS Triggers (V1 compatible)
345
- cursor.execute('''
346
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
347
- INSERT INTO memories_fts(rowid, content, summary, tags)
348
- VALUES (new.id, new.content, new.summary, new.tags);
349
- END
350
- ''')
351
-
352
- cursor.execute('''
353
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
354
- INSERT INTO memories_fts(memories_fts, rowid, content, summary, tags)
355
- VALUES('delete', old.id, old.content, old.summary, old.tags);
356
- END
357
- ''')
358
-
359
- cursor.execute('''
360
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
361
- INSERT INTO memories_fts(memories_fts, rowid, content, summary, tags)
362
- VALUES('delete', old.id, old.content, old.summary, old.tags);
363
- INSERT INTO memories_fts(rowid, content, summary, tags)
364
- VALUES (new.id, new.content, new.summary, new.tags);
365
- END
366
- ''')
286
+ cursor.execute(get_fts_trigger_insert_sql())
287
+ cursor.execute(get_fts_trigger_delete_sql())
288
+ cursor.execute(get_fts_trigger_update_sql())
367
289
 
368
290
  # Create indexes for V2 fields (safe for old databases without V2 columns)
369
- v2_indexes = [
370
- ('idx_project', 'project_path'),
371
- ('idx_tags', 'tags'),
372
- ('idx_category', 'category'),
373
- ('idx_tree_path', 'tree_path'),
374
- ('idx_cluster', 'cluster_id'),
375
- ('idx_last_accessed', 'last_accessed'),
376
- ('idx_parent_id', 'parent_id'),
377
- ('idx_profile', 'profile')
378
- ]
379
-
380
- for idx_name, col_name in v2_indexes:
291
+ for idx_name, col_name in V2_INDEXES:
381
292
  try:
382
293
  cursor.execute(f'CREATE INDEX IF NOT EXISTS {idx_name} ON memories({col_name})')
383
294
  except sqlite3.OperationalError:
@@ -394,31 +305,10 @@ class MemoryStoreV2:
394
305
 
395
306
  # Creator Attribution Metadata Table (REQUIRED by MIT License)
396
307
  # This table embeds creator information directly in the database
397
- cursor.execute('''
398
- CREATE TABLE IF NOT EXISTS creator_metadata (
399
- key TEXT PRIMARY KEY,
400
- value TEXT NOT NULL,
401
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
402
- )
403
- ''')
308
+ cursor.execute(get_creator_metadata_table_sql())
404
309
 
405
310
  # Insert creator attribution (embedded in database body)
406
- creator_data = {
407
- 'creator_name': 'Varun Pratap Bhardwaj',
408
- 'creator_role': 'Solution Architect & Original Creator',
409
- 'creator_github': 'varun369',
410
- 'project_name': 'SuperLocalMemory V2',
411
- 'project_url': 'https://github.com/varun369/SuperLocalMemoryV2',
412
- 'license': 'MIT',
413
- 'attribution_required': 'yes',
414
- 'version': '2.5.0',
415
- 'architecture_date': '2026-01-15',
416
- 'release_date': '2026-02-07',
417
- 'signature': 'VBPB-SLM-V2-2026-ARCHITECT',
418
- 'verification_hash': 'sha256:c9f3d1a8b5e2f4c6d8a9b3e7f1c4d6a8b9c3e7f2d5a8c1b4e6f9d2a7c5b8e1'
419
- }
420
-
421
- for key, value in creator_data.items():
311
+ for key, value in CREATOR_METADATA.items():
422
312
  cursor.execute('''
423
313
  INSERT OR IGNORE INTO creator_metadata (key, value)
424
314
  VALUES (?, ?)
@@ -432,12 +322,6 @@ class MemoryStoreV2:
432
322
  """Generate hash for deduplication."""
433
323
  return hashlib.sha256(content.encode()).hexdigest()[:32]
434
324
 
435
- # SECURITY: Input validation limits
436
- MAX_CONTENT_SIZE = 1_000_000 # 1MB max content
437
- MAX_SUMMARY_SIZE = 10_000 # 10KB max summary
438
- MAX_TAG_LENGTH = 50 # 50 chars per tag
439
- MAX_TAGS = 20 # 20 tags max
440
-
441
325
  def add_memory(
442
326
  self,
443
327
  content: str,
@@ -478,18 +362,18 @@ class MemoryStoreV2:
478
362
  if not content:
479
363
  raise ValueError("Content cannot be empty")
480
364
 
481
- if len(content) > self.MAX_CONTENT_SIZE:
482
- raise ValueError(f"Content exceeds maximum size of {self.MAX_CONTENT_SIZE} bytes")
365
+ if len(content) > MAX_CONTENT_SIZE:
366
+ raise ValueError(f"Content exceeds maximum size of {MAX_CONTENT_SIZE} bytes")
483
367
 
484
- if summary and len(summary) > self.MAX_SUMMARY_SIZE:
485
- raise ValueError(f"Summary exceeds maximum size of {self.MAX_SUMMARY_SIZE} bytes")
368
+ if summary and len(summary) > MAX_SUMMARY_SIZE:
369
+ raise ValueError(f"Summary exceeds maximum size of {MAX_SUMMARY_SIZE} bytes")
486
370
 
487
371
  if tags:
488
- if len(tags) > self.MAX_TAGS:
489
- raise ValueError(f"Too many tags (max {self.MAX_TAGS})")
372
+ if len(tags) > MAX_TAGS:
373
+ raise ValueError(f"Too many tags (max {MAX_TAGS})")
490
374
  for tag in tags:
491
- if len(tag) > self.MAX_TAG_LENGTH:
492
- raise ValueError(f"Tag '{tag[:20]}...' exceeds max length of {self.MAX_TAG_LENGTH}")
375
+ if len(tag) > MAX_TAG_LENGTH:
376
+ raise ValueError(f"Tag '{tag[:20]}...' exceeds max length of {MAX_TAG_LENGTH}")
493
377
 
494
378
  if importance < 1 or importance > 10:
495
379
  importance = max(1, min(10, importance)) # Clamp to valid range
@@ -1221,7 +1105,8 @@ class MemoryStoreV2:
1221
1105
 
1222
1106
  # Qualixar platform provenance (non-breaking additions)
1223
1107
  attribution['platform'] = 'Qualixar'
1224
- attribution['verify_url'] = 'https://qualixar.com'
1108
+ attribution['website'] = 'https://superlocalmemory.com'
1109
+ attribution['author_website'] = 'https://varunpratap.com'
1225
1110
 
1226
1111
  return attribution
1227
1112
 
@@ -1252,214 +1137,19 @@ class MemoryStoreV2:
1252
1137
  output.append(entry)
1253
1138
  char_count += len(entry)
1254
1139
 
1255
- return ''.join(output)
1256
-
1140
+ text = ''.join(output)
1257
1141
 
1258
- def format_content(content: str, full: bool = False, threshold: int = 5000, preview_len: int = 2000) -> str:
1259
- """
1260
- Smart content formatting with optional truncation.
1142
+ # Layer 3: Steganographic watermark on text exports
1143
+ try:
1144
+ from qualixar_watermark import encode_watermark
1145
+ text = encode_watermark(text, "slm")
1146
+ except ImportError:
1147
+ pass
1261
1148
 
1262
- Args:
1263
- content: Content to format
1264
- full: If True, always show full content
1265
- threshold: Max length before truncation (default 5000)
1266
- preview_len: Preview length when truncating (default 2000)
1267
-
1268
- Returns:
1269
- Formatted content string
1270
- """
1271
- if full or len(content) < threshold:
1272
- return content
1273
- else:
1274
- return f"{content[:preview_len]}..."
1149
+ return text
1275
1150
 
1276
1151
 
1277
1152
  # CLI interface (V1 compatible + V2 extensions)
1278
1153
  if __name__ == "__main__":
1279
- import sys
1280
-
1281
- store = MemoryStoreV2()
1282
-
1283
- if len(sys.argv) < 2:
1284
- print("MemoryStore V2 CLI")
1285
- print("\nV1 Compatible Commands:")
1286
- print(" python memory_store_v2.py add <content> [--project <path>] [--tags tag1,tag2]")
1287
- print(" python memory_store_v2.py search <query> [--full]")
1288
- print(" python memory_store_v2.py list [limit] [--full]")
1289
- print(" python memory_store_v2.py get <id>")
1290
- print(" python memory_store_v2.py recent [limit] [--full]")
1291
- print(" python memory_store_v2.py stats")
1292
- print(" python memory_store_v2.py context <query>")
1293
- print(" python memory_store_v2.py delete <id>")
1294
- print("\nV2 Extensions:")
1295
- print(" python memory_store_v2.py tree [parent_id]")
1296
- print(" python memory_store_v2.py cluster <cluster_id> [--full]")
1297
- print("\nOptions:")
1298
- print(" --full Show complete content (default: smart truncation at 5000 chars)")
1299
- sys.exit(0)
1300
-
1301
- command = sys.argv[1]
1302
-
1303
- if command == "tree":
1304
- parent_id = int(sys.argv[2]) if len(sys.argv) > 2 else None
1305
- results = store.get_tree(parent_id)
1306
-
1307
- if not results:
1308
- print("No memories in tree.")
1309
- else:
1310
- for r in results:
1311
- indent = " " * r['depth']
1312
- print(f"{indent}[{r['id']}] {r['content'][:50]}...")
1313
- if r.get('category'):
1314
- print(f"{indent} Category: {r['category']}")
1315
-
1316
- elif command == "cluster" and len(sys.argv) >= 3:
1317
- cluster_id = int(sys.argv[2])
1318
- show_full = '--full' in sys.argv
1319
- results = store.get_by_cluster(cluster_id)
1320
-
1321
- if not results:
1322
- print(f"No memories in cluster {cluster_id}.")
1323
- else:
1324
- print(f"Cluster {cluster_id} - {len(results)} memories:")
1325
- for r in results:
1326
- print(f"\n[{r['id']}] Importance: {r['importance']}")
1327
- print(f" {format_content(r['content'], full=show_full)}")
1328
-
1329
- elif command == "stats":
1330
- stats = store.get_stats()
1331
- print(json.dumps(stats, indent=2))
1332
-
1333
- elif command == "add":
1334
- # Parse content and options
1335
- if len(sys.argv) < 3:
1336
- print("Error: Content required")
1337
- print("Usage: python memory_store_v2.py add <content> [--project <path>] [--tags tag1,tag2]")
1338
- sys.exit(1)
1339
-
1340
- content = sys.argv[2]
1341
- project_path = None
1342
- tags = []
1343
-
1344
- i = 3
1345
- while i < len(sys.argv):
1346
- if sys.argv[i] == '--project' and i + 1 < len(sys.argv):
1347
- project_path = sys.argv[i + 1]
1348
- i += 2
1349
- elif sys.argv[i] == '--tags' and i + 1 < len(sys.argv):
1350
- tags = [t.strip() for t in sys.argv[i + 1].split(',')]
1351
- i += 2
1352
- else:
1353
- i += 1
1354
-
1355
- mem_id = store.add_memory(content, project_path=project_path, tags=tags)
1356
- print(f"Memory added with ID: {mem_id}")
1357
-
1358
- elif command == "search":
1359
- if len(sys.argv) < 3:
1360
- print("Error: Search query required")
1361
- print("Usage: python memory_store_v2.py search <query> [--full]")
1362
- sys.exit(1)
1363
-
1364
- query = sys.argv[2]
1365
- show_full = '--full' in sys.argv
1366
- results = store.search(query, limit=5)
1367
-
1368
- if not results:
1369
- print("No results found.")
1370
- else:
1371
- for r in results:
1372
- print(f"\n[{r['id']}] Score: {r['score']:.2f}")
1373
- if r.get('project_name'):
1374
- print(f"Project: {r['project_name']}")
1375
- if r.get('tags'):
1376
- print(f"Tags: {', '.join(r['tags'])}")
1377
- print(f"Content: {format_content(r['content'], full=show_full)}")
1378
- print(f"Created: {r['created_at']}")
1379
-
1380
- elif command == "recent":
1381
- show_full = '--full' in sys.argv
1382
- # Parse limit (skip --full flag)
1383
- limit = 10
1384
- for i, arg in enumerate(sys.argv[2:], start=2):
1385
- if arg != '--full' and arg.isdigit():
1386
- limit = int(arg)
1387
- break
1388
-
1389
- results = store.get_recent(limit)
1390
-
1391
- if not results:
1392
- print("No memories found.")
1393
- else:
1394
- for r in results:
1395
- print(f"\n[{r['id']}] {r['created_at']}")
1396
- if r.get('project_name'):
1397
- print(f"Project: {r['project_name']}")
1398
- if r.get('tags'):
1399
- print(f"Tags: {', '.join(r['tags'])}")
1400
- print(f"Content: {format_content(r['content'], full=show_full)}")
1401
-
1402
- elif command == "list":
1403
- show_full = '--full' in sys.argv
1404
- # Parse limit (skip --full flag)
1405
- limit = 10
1406
- for i, arg in enumerate(sys.argv[2:], start=2):
1407
- if arg != '--full' and arg.isdigit():
1408
- limit = int(arg)
1409
- break
1410
-
1411
- results = store.get_recent(limit)
1412
-
1413
- if not results:
1414
- print("No memories found.")
1415
- else:
1416
- for r in results:
1417
- print(f"[{r['id']}] {format_content(r['content'], full=show_full)}")
1418
-
1419
- elif command == "get":
1420
- if len(sys.argv) < 3:
1421
- print("Error: Memory ID required")
1422
- print("Usage: python memory_store_v2.py get <id>")
1423
- sys.exit(1)
1424
-
1425
- mem_id = int(sys.argv[2])
1426
- memory = store.get_by_id(mem_id)
1427
-
1428
- if not memory:
1429
- print(f"Memory {mem_id} not found.")
1430
- else:
1431
- print(f"\nID: {memory['id']}")
1432
- print(f"Content: {memory['content']}")
1433
- if memory.get('summary'):
1434
- print(f"Summary: {memory['summary']}")
1435
- if memory.get('project_name'):
1436
- print(f"Project: {memory['project_name']}")
1437
- if memory.get('tags'):
1438
- print(f"Tags: {', '.join(memory['tags'])}")
1439
- print(f"Created: {memory['created_at']}")
1440
- print(f"Importance: {memory['importance']}")
1441
- print(f"Access Count: {memory['access_count']}")
1442
-
1443
- elif command == "context":
1444
- if len(sys.argv) < 3:
1445
- print("Error: Query required")
1446
- print("Usage: python memory_store_v2.py context <query>")
1447
- sys.exit(1)
1448
-
1449
- query = sys.argv[2]
1450
- context = store.export_for_context(query)
1451
- print(context)
1452
-
1453
- elif command == "delete":
1454
- if len(sys.argv) < 3:
1455
- print("Error: Memory ID required")
1456
- print("Usage: python memory_store_v2.py delete <id>")
1457
- sys.exit(1)
1458
-
1459
- mem_id = int(sys.argv[2])
1460
- store.delete_memory(mem_id)
1461
- print(f"Memory {mem_id} deleted.")
1462
-
1463
- else:
1464
- print(f"Unknown command: {command}")
1465
- print("Run without arguments to see available commands.")
1154
+ from memory.cli import run_cli
1155
+ run_cli()
@@ -25,12 +25,13 @@ from pathlib import Path
25
25
  from datetime import datetime
26
26
  import sys
27
27
  import traceback
28
+ from typing import Tuple, Dict, Any
28
29
 
29
30
  DB_PATH = Path.home() / '.claude-memory' / 'memory.db'
30
31
  BACKUP_PATH = Path.home() / '.claude-memory' / 'backups' / f'pre-v2-{datetime.now().strftime("%Y%m%d-%H%M%S")}.db'
31
32
 
32
33
 
33
- def create_backup():
34
+ def create_backup() -> None:
34
35
  """Create a backup of the database before migration."""
35
36
  print("=" * 60)
36
37
  print("CREATING BACKUP")
@@ -49,7 +50,7 @@ def create_backup():
49
50
  print()
50
51
 
51
52
 
52
- def check_schema_version(conn):
53
+ def check_schema_version(conn: sqlite3.Connection) -> bool:
53
54
  """Check if migration has already been completed."""
54
55
  cursor = conn.cursor()
55
56
 
@@ -67,7 +68,7 @@ def check_schema_version(conn):
67
68
  return False
68
69
 
69
70
 
70
- def add_new_columns(conn):
71
+ def add_new_columns(conn: sqlite3.Connection) -> None:
71
72
  """Add new columns to the memories table."""
72
73
  print("=" * 60)
73
74
  print("ADDING NEW COLUMNS TO MEMORIES TABLE")
@@ -99,7 +100,7 @@ def add_new_columns(conn):
99
100
  print()
100
101
 
101
102
 
102
- def create_new_tables(conn):
103
+ def create_new_tables(conn: sqlite3.Connection) -> None:
103
104
  """Create all new tables for V2 architecture."""
104
105
  print("=" * 60)
105
106
  print("CREATING NEW TABLES")
@@ -223,7 +224,7 @@ def create_new_tables(conn):
223
224
  print()
224
225
 
225
226
 
226
- def create_indexes(conn):
227
+ def create_indexes(conn: sqlite3.Connection) -> None:
227
228
  """Create all indexes for performance optimization."""
228
229
  print("=" * 60)
229
230
  print("CREATING INDEXES")
@@ -259,7 +260,7 @@ def create_indexes(conn):
259
260
  print()
260
261
 
261
262
 
262
- def migrate_to_tree_structure(conn):
263
+ def migrate_to_tree_structure(conn: sqlite3.Connection) -> None:
263
264
  """Migrate existing memories to tree structure."""
264
265
  print("=" * 60)
265
266
  print("MIGRATING MEMORIES TO TREE STRUCTURE")
@@ -384,7 +385,7 @@ def migrate_to_tree_structure(conn):
384
385
  print()
385
386
 
386
387
 
387
- def update_metadata(conn):
388
+ def update_metadata(conn: sqlite3.Connection) -> None:
388
389
  """Update system metadata with migration info."""
389
390
  print("=" * 60)
390
391
  print("UPDATING SYSTEM METADATA")
@@ -417,7 +418,7 @@ def update_metadata(conn):
417
418
  print()
418
419
 
419
420
 
420
- def verify_migration(conn):
421
+ def verify_migration(conn: sqlite3.Connection) -> Tuple[bool, Dict[str, Any]]:
421
422
  """Verify that migration completed successfully."""
422
423
  print("=" * 60)
423
424
  print("VERIFYING MIGRATION")
@@ -477,7 +478,7 @@ def verify_migration(conn):
477
478
  return True
478
479
 
479
480
 
480
- def print_summary(conn):
481
+ def print_summary(conn: sqlite3.Connection) -> None:
481
482
  """Print migration summary statistics."""
482
483
  print("=" * 60)
483
484
  print("MIGRATION SUMMARY")
@@ -519,7 +520,7 @@ def print_summary(conn):
519
520
  print()
520
521
 
521
522
 
522
- def migrate():
523
+ def migrate() -> None:
523
524
  """Main migration function."""
524
525
  print()
525
526
  print("╔" + "═" * 58 + "╗")