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
package/requirements.txt CHANGED
@@ -1,10 +1,12 @@
1
- # SuperLocalMemory V2 - Core Requirements
1
+ # SuperLocalMemory - Core Requirements
2
2
  # ============================================================================
3
- # SuperLocalMemory V2.2.0 has ZERO core dependencies
4
- # All functionality works with Python 3.8+ standard library only
3
+ # Core memory operations (store, recall, search) work with Python 3.8+
4
+ # standard library only no required dependencies.
5
5
  #
6
- # For optional advanced features, see:
7
- # - requirements-full.txt (all optional features)
8
- # - requirements-ui.txt (web dashboard only)
9
- # - requirements-search.txt (advanced search only)
6
+ # Advanced features (knowledge graph, dashboard, semantic search) require
7
+ # optional dependencies listed in the other requirements files:
8
+ # - requirements-core.txt (graph + dashboard)
9
+ # - requirements-search.txt (semantic search)
10
+ # - requirements-learning.txt (ML-based ranking)
11
+ # - requirements-full.txt (everything)
10
12
  # ============================================================================
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SuperLocalMemory - Cross-platform prepack cleanup
4
+ *
5
+ * Removes __pycache__ directories and .pyc files before npm pack.
6
+ * Works on Windows, macOS, and Linux.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ function removePycache(dir) {
13
+ if (!fs.existsSync(dir)) return;
14
+
15
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
16
+ for (const entry of entries) {
17
+ const fullPath = path.join(dir, entry.name);
18
+
19
+ if (entry.isDirectory()) {
20
+ if (entry.name === '__pycache__' || entry.name === 'node_modules') {
21
+ if (entry.name === '__pycache__') {
22
+ fs.rmSync(fullPath, { recursive: true, force: true });
23
+ }
24
+ continue;
25
+ }
26
+ removePycache(fullPath);
27
+ } else if (entry.isFile() && entry.name.endsWith('.pyc')) {
28
+ fs.unlinkSync(fullPath);
29
+ }
30
+ }
31
+ }
32
+
33
+ removePycache(process.cwd());
@@ -0,0 +1,301 @@
1
+ # ============================================================================
2
+ # SuperLocalMemory V2.7 — Quick Verification Script (PowerShell)
3
+ # Copyright (c) 2026 Varun Pratap Bhardwaj
4
+ # Licensed under MIT License
5
+ # Repository: https://github.com/varun369/SuperLocalMemoryV2
6
+ #
7
+ # Run this after installation to verify everything works:
8
+ # .\scripts\verify-v27.ps1
9
+ # ============================================================================
10
+
11
+ param()
12
+
13
+ $ErrorActionPreference = "Stop"
14
+
15
+ $INSTALL_DIR = Join-Path $env:USERPROFILE ".claude-memory"
16
+ $PASS = 0
17
+ $WARN = 0
18
+ $FAIL = 0
19
+
20
+ Write-Host ""
21
+ Write-Host "SuperLocalMemory v2.7 Verification"
22
+ Write-Host "==================================="
23
+ Write-Host ""
24
+
25
+ # ── Check 1: Installation directory ──────────────────────────────────────────
26
+ if (Test-Path $INSTALL_DIR) {
27
+ Write-Host "[PASS] Installation directory exists: $INSTALL_DIR" -ForegroundColor Green
28
+ $PASS++
29
+ } else {
30
+ Write-Host "[FAIL] Installation directory missing. Run install.ps1 first." -ForegroundColor Red
31
+ $FAIL++
32
+ Write-Host ""
33
+ Write-Host "==================================="
34
+ Write-Host "Result: FAIL — SuperLocalMemory is not installed."
35
+ Write-Host "Run: .\install.ps1"
36
+ exit 1
37
+ }
38
+
39
+ # ── Check 2: Core modules ────────────────────────────────────────────────────
40
+ Write-Host ""
41
+ Write-Host "Core Modules:"
42
+ $coreModules = @("memory_store_v2.py", "graph_engine.py", "pattern_learner.py", "mcp_server.py", "tree_manager.py")
43
+ foreach ($mod in $coreModules) {
44
+ $modPath = Join-Path $INSTALL_DIR $mod
45
+ if (Test-Path $modPath) {
46
+ Write-Host " [PASS] $mod" -ForegroundColor Green
47
+ $PASS++
48
+ } else {
49
+ Write-Host " [FAIL] Missing: $mod" -ForegroundColor Red
50
+ $FAIL++
51
+ }
52
+ }
53
+
54
+ # ── Check 3: v2.5 modules ────────────────────────────────────────────────────
55
+ Write-Host ""
56
+ Write-Host "Event System (v2.5):"
57
+ $v25Modules = @("event_bus.py", "subscription_manager.py", "webhook_dispatcher.py", "agent_registry.py", "provenance_tracker.py", "trust_scorer.py", "db_connection_manager.py")
58
+ foreach ($mod in $v25Modules) {
59
+ $modPath = Join-Path $INSTALL_DIR $mod
60
+ if (Test-Path $modPath) {
61
+ Write-Host " [PASS] $mod" -ForegroundColor Green
62
+ $PASS++
63
+ } else {
64
+ Write-Host " [WARN] Missing: $mod (v2.5 feature)" -ForegroundColor Yellow
65
+ $WARN++
66
+ }
67
+ }
68
+
69
+ # ── Check 4: Learning modules (v2.7) ─────────────────────────────────────────
70
+ Write-Host ""
71
+ Write-Host "Learning System (v2.7):"
72
+ $learningDir = Join-Path $INSTALL_DIR "learning"
73
+ if (Test-Path $learningDir) {
74
+ Write-Host " [PASS] learning/ directory exists" -ForegroundColor Green
75
+ $PASS++
76
+
77
+ $learningModules = @("__init__.py", "learning_db.py", "adaptive_ranker.py", "feedback_collector.py",
78
+ "engagement_tracker.py", "cross_project_aggregator.py", "project_context_manager.py",
79
+ "workflow_pattern_miner.py", "source_quality_scorer.py", "synthetic_bootstrap.py",
80
+ "feature_extractor.py")
81
+ foreach ($mod in $learningModules) {
82
+ $modPath = Join-Path $learningDir $mod
83
+ if (Test-Path $modPath) {
84
+ Write-Host " [PASS] learning/$mod" -ForegroundColor Green
85
+ $PASS++
86
+ } else {
87
+ Write-Host " [WARN] Missing: learning/$mod" -ForegroundColor Yellow
88
+ $WARN++
89
+ }
90
+ }
91
+ } else {
92
+ Write-Host " [FAIL] learning/ directory missing (v2.7 not fully installed)" -ForegroundColor Red
93
+ $FAIL++
94
+ }
95
+
96
+ # ── Check 5: Learning dependencies ───────────────────────────────────────────
97
+ Write-Host ""
98
+ Write-Host "Learning Dependencies:"
99
+ try {
100
+ python -c "import lightgbm; print(f' [PASS] LightGBM {lightgbm.__version__}')" 2>$null
101
+ if ($LASTEXITCODE -eq 0) {
102
+ $PASS++
103
+ } else {
104
+ throw
105
+ }
106
+ } catch {
107
+ Write-Host " [INFO] LightGBM not installed (optional — rule-based ranking will be used)" -ForegroundColor Yellow
108
+ $WARN++
109
+ }
110
+
111
+ try {
112
+ python -c "import scipy; print(f' [PASS] SciPy {scipy.__version__}')" 2>$null
113
+ if ($LASTEXITCODE -eq 0) {
114
+ $PASS++
115
+ } else {
116
+ throw
117
+ }
118
+ } catch {
119
+ Write-Host " [INFO] SciPy not installed (optional — install for full learning features)" -ForegroundColor Yellow
120
+ $WARN++
121
+ }
122
+
123
+ # ── Check 6: Core dependencies ───────────────────────────────────────────────
124
+ Write-Host ""
125
+ Write-Host "Core Dependencies:"
126
+ try {
127
+ python -c "import sklearn; print(f' [PASS] scikit-learn {sklearn.__version__}')" 2>$null
128
+ if ($LASTEXITCODE -eq 0) {
129
+ $PASS++
130
+ } else {
131
+ throw
132
+ }
133
+ } catch {
134
+ Write-Host " [WARN] scikit-learn not installed (needed for knowledge graph)" -ForegroundColor Yellow
135
+ $WARN++
136
+ }
137
+
138
+ try {
139
+ python -c "import numpy; print(f' [PASS] numpy {numpy.__version__}')" 2>$null
140
+ if ($LASTEXITCODE -eq 0) {
141
+ $PASS++
142
+ } else {
143
+ throw
144
+ }
145
+ } catch {
146
+ Write-Host " [WARN] numpy not installed" -ForegroundColor Yellow
147
+ $WARN++
148
+ }
149
+
150
+ try {
151
+ python -c "import igraph; print(f' [PASS] python-igraph {igraph.__version__}')" 2>$null
152
+ if ($LASTEXITCODE -eq 0) {
153
+ $PASS++
154
+ } else {
155
+ throw
156
+ }
157
+ } catch {
158
+ Write-Host " [WARN] python-igraph not installed (needed for graph clustering)" -ForegroundColor Yellow
159
+ $WARN++
160
+ }
161
+
162
+ # ── Check 7: Database ────────────────────────────────────────────────────────
163
+ Write-Host ""
164
+ Write-Host "Databases:"
165
+ $memoryDb = Join-Path $INSTALL_DIR "memory.db"
166
+ if (Test-Path $memoryDb) {
167
+ try {
168
+ $MEMORY_COUNT = sqlite3 $memoryDb "SELECT COUNT(*) FROM memories;" 2>$null
169
+ $DB_SIZE = [math]::Round((Get-Item $memoryDb).Length / 1KB, 2)
170
+ Write-Host " [PASS] memory.db exists ($MEMORY_COUNT memories, $DB_SIZE KB)" -ForegroundColor Green
171
+ $PASS++
172
+ } catch {
173
+ Write-Host " [INFO] memory.db exists but cannot query (sqlite3 not available)" -ForegroundColor Yellow
174
+ }
175
+ } else {
176
+ Write-Host " [INFO] memory.db not yet created (will auto-create on first use)" -ForegroundColor Yellow
177
+ }
178
+
179
+ $learningDb = Join-Path $INSTALL_DIR "learning.db"
180
+ if (Test-Path $learningDb) {
181
+ try {
182
+ $FEEDBACK_COUNT = sqlite3 $learningDb "SELECT COUNT(*) FROM ranking_feedback;" 2>$null
183
+ Write-Host " [PASS] learning.db exists ($FEEDBACK_COUNT feedback signals)" -ForegroundColor Green
184
+ $PASS++
185
+ } catch {
186
+ Write-Host " [INFO] learning.db exists but cannot query (sqlite3 not available)" -ForegroundColor Yellow
187
+ }
188
+ } else {
189
+ Write-Host " [INFO] learning.db not yet created (will auto-create on first recall)" -ForegroundColor Yellow
190
+ }
191
+
192
+ # ── Check 8: CLI ──────────────────────────────────────────────────────────────
193
+ Write-Host ""
194
+ Write-Host "CLI:"
195
+ $slmCommand = Get-Command slm -ErrorAction SilentlyContinue
196
+ if ($slmCommand) {
197
+ Write-Host " [PASS] slm command available in PATH" -ForegroundColor Green
198
+ $PASS++
199
+ } else {
200
+ $slmBin = Join-Path $INSTALL_DIR "bin\slm"
201
+ if (Test-Path $slmBin) {
202
+ Write-Host " [WARN] slm exists at $slmBin but not in PATH" -ForegroundColor Yellow
203
+ Write-Host " Add to PATH: `$env:PATH = `"`$env:USERPROFILE\.claude-memory\bin;`$env:PATH`""
204
+ $WARN++
205
+ } else {
206
+ Write-Host " [FAIL] slm command not found" -ForegroundColor Red
207
+ $FAIL++
208
+ }
209
+ }
210
+
211
+ # ── Check 9: MCP server ──────────────────────────────────────────────────────
212
+ Write-Host ""
213
+ Write-Host "MCP Server:"
214
+ $mcpServer = Join-Path $INSTALL_DIR "mcp_server.py"
215
+ if (Test-Path $mcpServer) {
216
+ Write-Host " [PASS] mcp_server.py installed" -ForegroundColor Green
217
+ $PASS++
218
+ } else {
219
+ Write-Host " [FAIL] mcp_server.py missing" -ForegroundColor Red
220
+ $FAIL++
221
+ }
222
+
223
+ try {
224
+ python -c "from mcp.server.fastmcp import FastMCP" 2>$null
225
+ if ($LASTEXITCODE -eq 0) {
226
+ Write-Host " [PASS] MCP SDK installed" -ForegroundColor Green
227
+ $PASS++
228
+ } else {
229
+ throw
230
+ }
231
+ } catch {
232
+ Write-Host " [WARN] MCP SDK not installed (install: pip install mcp)" -ForegroundColor Yellow
233
+ $WARN++
234
+ }
235
+
236
+ # ── Check 10: Import chain verification ───────────────────────────────────────
237
+ Write-Host ""
238
+ Write-Host "Import Chain:"
239
+ $importTest = @"
240
+ import sys
241
+ sys.path.insert(0, '$($INSTALL_DIR -replace '\\', '\\')')
242
+ try:
243
+ from learning import get_learning_db, get_status, FULL_LEARNING_AVAILABLE, ML_RANKING_AVAILABLE
244
+ status = get_status()
245
+ ml = 'yes' if status['ml_ranking_available'] else 'no'
246
+ full = 'yes' if status['learning_available'] else 'no'
247
+ print(f'OK ml_ranking={ml} full_learning={full}')
248
+ except ImportError as e:
249
+ print(f'IMPORT_ERROR {e}')
250
+ except Exception as e:
251
+ print(f'ERROR {e}')
252
+ "@
253
+
254
+ $IMPORT_RESULT = python -c $importTest 2>&1
255
+
256
+ if ($IMPORT_RESULT -like "OK*") {
257
+ Write-Host " [PASS] Learning system imports successfully" -ForegroundColor Green
258
+ Write-Host " $IMPORT_RESULT"
259
+ $PASS++
260
+ } elseif ($IMPORT_RESULT -like "IMPORT_ERROR*") {
261
+ Write-Host " [WARN] Learning import failed: $($IMPORT_RESULT -replace 'IMPORT_ERROR ', '')" -ForegroundColor Yellow
262
+ Write-Host " This may be normal if learning modules are not yet installed."
263
+ $WARN++
264
+ } else {
265
+ Write-Host " [WARN] Learning check: $IMPORT_RESULT" -ForegroundColor Yellow
266
+ $WARN++
267
+ }
268
+
269
+ # ── Summary ───────────────────────────────────────────────────────────────────
270
+ Write-Host ""
271
+ Write-Host "==================================="
272
+ Write-Host "Verification Summary"
273
+ Write-Host " Passed: $PASS"
274
+ Write-Host " Warnings: $WARN"
275
+ Write-Host " Failed: $FAIL"
276
+ Write-Host "==================================="
277
+ Write-Host ""
278
+
279
+ if ($FAIL -eq 0) {
280
+ Write-Host "Status: READY" -ForegroundColor Green
281
+ Write-Host ""
282
+ Write-Host "Quick start:"
283
+ Write-Host " slm remember `"My first memory`""
284
+ Write-Host " slm recall `"first`""
285
+ Write-Host " slm status"
286
+ Write-Host ""
287
+ if ($WARN -gt 0) {
288
+ Write-Host "Some optional features may not be available."
289
+ Write-Host "Install missing dependencies to enable them:"
290
+ Write-Host " pip install lightgbm scipy # Learning system"
291
+ Write-Host " pip install scikit-learn igraph # Knowledge graph"
292
+ Write-Host ""
293
+ }
294
+ } else {
295
+ Write-Host "Status: INCOMPLETE" -ForegroundColor Red
296
+ Write-Host ""
297
+ Write-Host "Fix the failed checks above, then re-run:"
298
+ Write-Host " .\scripts\verify-v27.ps1"
299
+ Write-Host ""
300
+ exit 1
301
+ }
@@ -105,24 +105,26 @@ class AgentRegistry:
105
105
  except ImportError:
106
106
  import sqlite3
107
107
  conn = sqlite3.connect(str(self.db_path))
108
- conn.execute('''
109
- CREATE TABLE IF NOT EXISTS agent_registry (
110
- id INTEGER PRIMARY KEY AUTOINCREMENT,
111
- agent_id TEXT NOT NULL UNIQUE,
112
- agent_name TEXT,
113
- protocol TEXT NOT NULL,
114
- first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
115
- last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
116
- memories_written INTEGER DEFAULT 0,
117
- memories_recalled INTEGER DEFAULT 0,
118
- trust_score REAL DEFAULT 0.667,
119
- metadata TEXT DEFAULT '{}'
120
- )
121
- ''')
122
- conn.execute('CREATE INDEX IF NOT EXISTS idx_agent_protocol ON agent_registry(protocol)')
123
- conn.execute('CREATE INDEX IF NOT EXISTS idx_agent_last_seen ON agent_registry(last_seen)')
124
- conn.commit()
125
- conn.close()
108
+ try:
109
+ conn.execute('''
110
+ CREATE TABLE IF NOT EXISTS agent_registry (
111
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
112
+ agent_id TEXT NOT NULL UNIQUE,
113
+ agent_name TEXT,
114
+ protocol TEXT NOT NULL,
115
+ first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
116
+ last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
117
+ memories_written INTEGER DEFAULT 0,
118
+ memories_recalled INTEGER DEFAULT 0,
119
+ trust_score REAL DEFAULT 0.667,
120
+ metadata TEXT DEFAULT '{}'
121
+ )
122
+ ''')
123
+ conn.execute('CREATE INDEX IF NOT EXISTS idx_agent_protocol ON agent_registry(protocol)')
124
+ conn.execute('CREATE INDEX IF NOT EXISTS idx_agent_last_seen ON agent_registry(last_seen)')
125
+ conn.commit()
126
+ finally:
127
+ conn.close()
126
128
 
127
129
  # =========================================================================
128
130
  # Agent Registration
@@ -179,16 +181,18 @@ class AgentRegistry:
179
181
  except ImportError:
180
182
  import sqlite3
181
183
  conn = sqlite3.connect(str(self.db_path))
182
- conn.execute('''
183
- INSERT INTO agent_registry (agent_id, agent_name, protocol, first_seen, last_seen, metadata)
184
- VALUES (?, ?, ?, ?, ?, ?)
185
- ON CONFLICT(agent_id) DO UPDATE SET
186
- last_seen = excluded.last_seen,
187
- metadata = excluded.metadata,
188
- agent_name = COALESCE(excluded.agent_name, agent_registry.agent_name)
189
- ''', (agent_id, agent_name, protocol, now, now, meta_json))
190
- conn.commit()
191
- conn.close()
184
+ try:
185
+ conn.execute('''
186
+ INSERT INTO agent_registry (agent_id, agent_name, protocol, first_seen, last_seen, metadata)
187
+ VALUES (?, ?, ?, ?, ?, ?)
188
+ ON CONFLICT(agent_id) DO UPDATE SET
189
+ last_seen = excluded.last_seen,
190
+ metadata = excluded.metadata,
191
+ agent_name = COALESCE(excluded.agent_name, agent_registry.agent_name)
192
+ ''', (agent_id, agent_name, protocol, now, now, meta_json))
193
+ conn.commit()
194
+ finally:
195
+ conn.close()
192
196
 
193
197
  # Emit agent.connected event
194
198
  try:
@@ -157,8 +157,10 @@ class AutoBackup:
157
157
  # Use SQLite backup API for consistency (safe even during writes)
158
158
  source_conn = sqlite3.connect(self.db_path)
159
159
  backup_conn = sqlite3.connect(backup_path)
160
- source_conn.backup(backup_conn)
161
- backup_conn.close()
160
+ try:
161
+ source_conn.backup(backup_conn)
162
+ finally:
163
+ backup_conn.close()
162
164
  source_conn.close()
163
165
 
164
166
  # Get backup size
@@ -179,8 +181,10 @@ class AutoBackup:
179
181
  learning_backup_path = self.backup_dir / learning_backup_name
180
182
  l_source = sqlite3.connect(learning_db)
181
183
  l_backup = sqlite3.connect(learning_backup_path)
182
- l_source.backup(l_backup)
183
- l_backup.close()
184
+ try:
185
+ l_source.backup(l_backup)
186
+ finally:
187
+ l_backup.close()
184
188
  l_source.close()
185
189
  l_size = learning_backup_path.stat().st_size / (1024 * 1024)
186
190
  logger.info(f"Learning backup created: {learning_backup_name} ({l_size:.1f} MB)")
@@ -281,8 +285,10 @@ class AutoBackup:
281
285
  # Restore using SQLite backup API
282
286
  source_conn = sqlite3.connect(backup_path)
283
287
  target_conn = sqlite3.connect(target_db)
284
- source_conn.backup(target_conn)
285
- target_conn.close()
288
+ try:
289
+ source_conn.backup(target_conn)
290
+ finally:
291
+ target_conn.close()
286
292
  source_conn.close()
287
293
 
288
294
  logger.info(f"Restored from backup: {filename} → {target_db.name}")
@@ -88,7 +88,7 @@ class CacheEntry:
88
88
  age = time.time() - self.timestamp
89
89
  return age > ttl_seconds
90
90
 
91
- def mark_accessed(self):
91
+ def mark_accessed(self) -> None:
92
92
  """Mark entry as accessed (increment counter)."""
93
93
  self.access_count += 1
94
94
 
@@ -425,7 +425,7 @@ if __name__ == "__main__":
425
425
  ]
426
426
 
427
427
  # Mock search results
428
- def mock_search(query: str):
428
+ def mock_search(query: str) -> None:
429
429
  """Simulate search result."""
430
430
  return [
431
431
  (f"doc_{i}", random.random())
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
+ """
5
+ Compression sub-package for SuperLocalMemory.
6
+ Provides tier-based progressive summarization and archival.
7
+
8
+ Note: Uses relative imports to avoid collision with Python stdlib.
9
+ """
10
+
11
+ from .config import CompressionConfig
12
+ from .tier_classifier import TierClassifier
13
+ from .tier2_compressor import Tier2Compressor
14
+ from .tier3_compressor import Tier3Compressor
15
+ from .cold_storage import ColdStorageManager
16
+ from .orchestrator import CompressionOrchestrator
17
+
18
+ __all__ = [
19
+ 'CompressionConfig',
20
+ 'TierClassifier',
21
+ 'Tier2Compressor',
22
+ 'Tier3Compressor',
23
+ 'ColdStorageManager',
24
+ 'CompressionOrchestrator',
25
+ ]
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
+ """
5
+ CLI interface for compression operations.
6
+ """
7
+
8
+ import sys
9
+ import json
10
+
11
+ from compression.config import CompressionConfig
12
+ from compression.tier_classifier import TierClassifier
13
+ from compression.tier2_compressor import Tier2Compressor
14
+ from compression.tier3_compressor import Tier3Compressor
15
+ from compression.cold_storage import ColdStorageManager
16
+ from compression.orchestrator import CompressionOrchestrator
17
+
18
+
19
+ def main():
20
+ """Main CLI entry point."""
21
+ if len(sys.argv) < 2:
22
+ print("Progressive Summarization Compression for SuperLocalMemory\n")
23
+ print("Usage:")
24
+ print(" python compression.py classify # Classify memories into tiers")
25
+ print(" python compression.py compress # Run full compression cycle")
26
+ print(" python compression.py stats # Show compression statistics")
27
+ print(" python compression.py tier2 <id> # Compress specific memory to Tier 2")
28
+ print(" python compression.py tier3 <id> # Compress specific memory to Tier 3")
29
+ print(" python compression.py cold-storage # Move old memories to cold storage")
30
+ print(" python compression.py restore <id> # Restore memory from cold storage")
31
+ print(" python compression.py init-config # Initialize compression config")
32
+ sys.exit(0)
33
+
34
+ command = sys.argv[1]
35
+ orchestrator = CompressionOrchestrator()
36
+
37
+ if command == "classify":
38
+ classifier = TierClassifier()
39
+ updates = classifier.classify_memories()
40
+ print(f"Classified {len(updates)} memories")
41
+
42
+ stats = classifier.get_tier_stats()
43
+ print(f"\nTier breakdown:")
44
+ print(f" Tier 1 (Full content): {stats['tier1']} memories")
45
+ print(f" Tier 2 (Summary+excerpts): {stats['tier2']} memories")
46
+ print(f" Tier 3 (Bullets only): {stats['tier3']} memories")
47
+
48
+ elif command == "compress":
49
+ print("Running full compression cycle...")
50
+ stats = orchestrator.run_full_compression()
51
+
52
+ print(f"\nCompression Results:")
53
+ print(f" Tier updates: {stats['tier_updates']}")
54
+ print(f" Tier 2 compressed: {stats['tier2_compressed']}")
55
+ print(f" Tier 3 compressed: {stats['tier3_compressed']}")
56
+ print(f" Moved to cold storage: {stats['cold_stored']}")
57
+
58
+ if 'space_savings' in stats:
59
+ savings = stats['space_savings']
60
+ print(f"\nSpace Savings:")
61
+ print(f" Original size: {savings['estimated_original_bytes']:,} bytes")
62
+ print(f" Current size: {savings['current_size_bytes']:,} bytes")
63
+ print(f" Savings: {savings['savings_bytes']:,} bytes ({savings['savings_percent']}%)")
64
+
65
+ if stats.get('errors'):
66
+ print(f"\nErrors: {stats['errors']}")
67
+
68
+ elif command == "stats":
69
+ classifier = TierClassifier()
70
+ tier_stats = classifier.get_tier_stats()
71
+
72
+ cold_storage = ColdStorageManager()
73
+ cold_stats = cold_storage.get_cold_storage_stats()
74
+
75
+ savings = orchestrator._calculate_space_savings()
76
+
77
+ print("Compression Statistics\n")
78
+ print("Tier Breakdown:")
79
+ print(f" Tier 1 (Full content): {tier_stats['tier1']} memories")
80
+ print(f" Tier 2 (Summary+excerpts): {tier_stats['tier2']} memories")
81
+ print(f" Tier 3 (Bullets only): {tier_stats['tier3']} memories")
82
+
83
+ print(f"\nCold Storage:")
84
+ print(f" Archive files: {cold_stats['archive_count']}")
85
+ print(f" Total memories: {cold_stats['total_memories']}")
86
+ print(f" Total size: {cold_stats['total_size_bytes']:,} bytes")
87
+
88
+ print(f"\nSpace Savings:")
89
+ print(f" Estimated original: {savings['estimated_original_bytes']:,} bytes")
90
+ print(f" Current size: {savings['current_size_bytes']:,} bytes")
91
+ print(f" Savings: {savings['savings_bytes']:,} bytes ({savings['savings_percent']}%)")
92
+
93
+ elif command == "tier2" and len(sys.argv) >= 3:
94
+ try:
95
+ memory_id = int(sys.argv[2])
96
+ compressor = Tier2Compressor()
97
+ if compressor.compress_to_tier2(memory_id):
98
+ print(f"Memory #{memory_id} compressed to Tier 2")
99
+ else:
100
+ print(f"Failed to compress memory #{memory_id}")
101
+ except ValueError:
102
+ print("Error: Memory ID must be a number")
103
+
104
+ elif command == "tier3" and len(sys.argv) >= 3:
105
+ try:
106
+ memory_id = int(sys.argv[2])
107
+ compressor = Tier3Compressor()
108
+ if compressor.compress_to_tier3(memory_id):
109
+ print(f"Memory #{memory_id} compressed to Tier 3")
110
+ else:
111
+ print(f"Failed to compress memory #{memory_id}")
112
+ except ValueError:
113
+ print("Error: Memory ID must be a number")
114
+
115
+ elif command == "cold-storage":
116
+ cold_storage = ColdStorageManager()
117
+ candidates = cold_storage.get_cold_storage_candidates()
118
+
119
+ if not candidates:
120
+ print("No memories ready for cold storage")
121
+ else:
122
+ print(f"Moving {len(candidates)} memories to cold storage...")
123
+ count = cold_storage.move_to_cold_storage(candidates)
124
+ print(f"Archived {count} memories")
125
+
126
+ elif command == "restore" and len(sys.argv) >= 3:
127
+ try:
128
+ memory_id = int(sys.argv[2])
129
+ cold_storage = ColdStorageManager()
130
+ content = cold_storage.restore_from_cold_storage(memory_id)
131
+
132
+ if content:
133
+ print(f"Memory #{memory_id} restored from cold storage")
134
+ else:
135
+ print(f"Memory #{memory_id} not found in cold storage")
136
+ except ValueError:
137
+ print("Error: Memory ID must be a number")
138
+
139
+ elif command == "init-config":
140
+ config = CompressionConfig()
141
+ config.initialize_defaults()
142
+ print("Compression configuration initialized")
143
+ print(json.dumps(config.compression_settings, indent=2))
144
+
145
+ else:
146
+ print(f"Unknown command: {command}")
147
+
148
+
149
+ if __name__ == "__main__":
150
+ main()