superlocalmemory 2.8.5 → 3.0.0

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 (434) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +9 -1
  3. package/NOTICE +63 -0
  4. package/README.md +165 -480
  5. package/bin/slm +17 -449
  6. package/bin/slm-npm +2 -2
  7. package/bin/slm.bat +4 -2
  8. package/conftest.py +5 -0
  9. package/docs/api-reference.md +284 -0
  10. package/docs/architecture.md +149 -0
  11. package/docs/auto-memory.md +150 -0
  12. package/docs/cli-reference.md +276 -0
  13. package/docs/compliance.md +191 -0
  14. package/docs/configuration.md +182 -0
  15. package/docs/getting-started.md +102 -0
  16. package/docs/ide-setup.md +261 -0
  17. package/docs/mcp-tools.md +220 -0
  18. package/docs/migration-from-v2.md +170 -0
  19. package/docs/profiles.md +173 -0
  20. package/docs/troubleshooting.md +310 -0
  21. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  22. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  23. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  24. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  25. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  26. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  27. package/ide/configs/cursor-mcp.json +15 -0
  28. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  29. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  30. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  31. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  32. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  33. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  34. package/{configs → ide/configs}/zed-mcp.json +2 -2
  35. package/{hooks → ide/hooks}/context-hook.js +9 -20
  36. package/ide/hooks/memory-list-skill.js +70 -0
  37. package/ide/hooks/memory-profile-skill.js +101 -0
  38. package/ide/hooks/memory-recall-skill.js +62 -0
  39. package/ide/hooks/memory-remember-skill.js +68 -0
  40. package/ide/hooks/memory-reset-skill.js +160 -0
  41. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  42. package/ide/integrations/langchain/README.md +106 -0
  43. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  44. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  45. package/ide/integrations/langchain/pyproject.toml +38 -0
  46. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  47. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  48. package/ide/integrations/langchain/tests/test_security.py +117 -0
  49. package/ide/integrations/llamaindex/README.md +81 -0
  50. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  51. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  52. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  53. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  54. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  55. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  56. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  57. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  58. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  59. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  60. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  61. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  62. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  63. package/package.json +13 -22
  64. package/pyproject.toml +85 -0
  65. package/scripts/build-dmg.sh +417 -0
  66. package/scripts/install-skills.ps1 +334 -0
  67. package/{install.ps1 → scripts/install.ps1} +36 -4
  68. package/{install.sh → scripts/install.sh} +14 -13
  69. package/scripts/postinstall.js +2 -2
  70. package/scripts/start-dashboard.ps1 +52 -0
  71. package/scripts/start-dashboard.sh +41 -0
  72. package/scripts/sync-wiki.ps1 +127 -0
  73. package/scripts/sync-wiki.sh +82 -0
  74. package/scripts/test-dmg.sh +161 -0
  75. package/scripts/test-npm-package.ps1 +252 -0
  76. package/scripts/test-npm-package.sh +207 -0
  77. package/scripts/verify-install.ps1 +294 -0
  78. package/scripts/verify-install.sh +266 -0
  79. package/src/superlocalmemory/__init__.py +0 -0
  80. package/src/superlocalmemory/attribution/__init__.py +9 -0
  81. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  82. package/src/superlocalmemory/attribution/signer.py +153 -0
  83. package/src/superlocalmemory/attribution/watermark.py +189 -0
  84. package/src/superlocalmemory/cli/__init__.py +5 -0
  85. package/src/superlocalmemory/cli/commands.py +245 -0
  86. package/src/superlocalmemory/cli/main.py +89 -0
  87. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  88. package/src/superlocalmemory/cli/post_install.py +99 -0
  89. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  90. package/src/superlocalmemory/compliance/__init__.py +0 -0
  91. package/src/superlocalmemory/compliance/abac.py +204 -0
  92. package/src/superlocalmemory/compliance/audit.py +314 -0
  93. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  94. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  95. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  96. package/src/superlocalmemory/compliance/retention.py +232 -0
  97. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  98. package/src/superlocalmemory/core/__init__.py +0 -0
  99. package/src/superlocalmemory/core/config.py +391 -0
  100. package/src/superlocalmemory/core/embeddings.py +293 -0
  101. package/src/superlocalmemory/core/engine.py +701 -0
  102. package/src/superlocalmemory/core/hooks.py +65 -0
  103. package/src/superlocalmemory/core/maintenance.py +172 -0
  104. package/src/superlocalmemory/core/modes.py +140 -0
  105. package/src/superlocalmemory/core/profiles.py +234 -0
  106. package/src/superlocalmemory/core/registry.py +117 -0
  107. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  108. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  109. package/src/superlocalmemory/encoding/__init__.py +0 -0
  110. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  111. package/src/superlocalmemory/encoding/emotional.py +125 -0
  112. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  113. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  114. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  115. package/src/superlocalmemory/encoding/foresight.py +91 -0
  116. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  117. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  118. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  119. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  120. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  121. package/src/superlocalmemory/encoding/type_router.py +235 -0
  122. package/src/superlocalmemory/hooks/__init__.py +3 -0
  123. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  124. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  125. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  126. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  127. package/src/superlocalmemory/infra/__init__.py +3 -0
  128. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  129. package/src/superlocalmemory/infra/backup.py +317 -0
  130. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  131. package/src/superlocalmemory/infra/event_bus.py +381 -0
  132. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  133. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  134. package/src/superlocalmemory/learning/__init__.py +0 -0
  135. package/src/superlocalmemory/learning/adaptive.py +172 -0
  136. package/src/superlocalmemory/learning/behavioral.py +490 -0
  137. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  138. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  139. package/src/superlocalmemory/learning/cross_project.py +399 -0
  140. package/src/superlocalmemory/learning/database.py +376 -0
  141. package/src/superlocalmemory/learning/engagement.py +323 -0
  142. package/src/superlocalmemory/learning/features.py +138 -0
  143. package/src/superlocalmemory/learning/feedback.py +316 -0
  144. package/src/superlocalmemory/learning/outcomes.py +255 -0
  145. package/src/superlocalmemory/learning/project_context.py +366 -0
  146. package/src/superlocalmemory/learning/ranker.py +155 -0
  147. package/src/superlocalmemory/learning/source_quality.py +303 -0
  148. package/src/superlocalmemory/learning/workflows.py +309 -0
  149. package/src/superlocalmemory/llm/__init__.py +0 -0
  150. package/src/superlocalmemory/llm/backbone.py +316 -0
  151. package/src/superlocalmemory/math/__init__.py +0 -0
  152. package/src/superlocalmemory/math/fisher.py +356 -0
  153. package/src/superlocalmemory/math/langevin.py +398 -0
  154. package/src/superlocalmemory/math/sheaf.py +257 -0
  155. package/src/superlocalmemory/mcp/__init__.py +0 -0
  156. package/src/superlocalmemory/mcp/resources.py +245 -0
  157. package/src/superlocalmemory/mcp/server.py +61 -0
  158. package/src/superlocalmemory/mcp/tools.py +18 -0
  159. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  160. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  161. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  162. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  163. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  164. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  165. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  166. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  167. package/src/superlocalmemory/retrieval/engine.py +390 -0
  168. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  169. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  170. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  171. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  172. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  173. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  174. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  175. package/src/superlocalmemory/server/__init__.py +1 -0
  176. package/src/superlocalmemory/server/api.py +248 -0
  177. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  178. package/src/superlocalmemory/server/routes/agents.py +107 -0
  179. package/src/superlocalmemory/server/routes/backup.py +91 -0
  180. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  181. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  182. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  183. package/src/superlocalmemory/server/routes/events.py +183 -0
  184. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  185. package/src/superlocalmemory/server/routes/learning.py +273 -0
  186. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  187. package/src/superlocalmemory/server/routes/memories.py +399 -0
  188. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  189. package/src/superlocalmemory/server/routes/stats.py +346 -0
  190. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  191. package/src/superlocalmemory/server/routes/ws.py +82 -0
  192. package/src/superlocalmemory/server/security_middleware.py +57 -0
  193. package/src/superlocalmemory/server/ui.py +245 -0
  194. package/src/superlocalmemory/storage/__init__.py +0 -0
  195. package/src/superlocalmemory/storage/access_control.py +182 -0
  196. package/src/superlocalmemory/storage/database.py +594 -0
  197. package/src/superlocalmemory/storage/migrations.py +303 -0
  198. package/src/superlocalmemory/storage/models.py +406 -0
  199. package/src/superlocalmemory/storage/schema.py +726 -0
  200. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  201. package/src/superlocalmemory/trust/__init__.py +0 -0
  202. package/src/superlocalmemory/trust/gate.py +130 -0
  203. package/src/superlocalmemory/trust/provenance.py +124 -0
  204. package/src/superlocalmemory/trust/scorer.py +347 -0
  205. package/src/superlocalmemory/trust/signals.py +153 -0
  206. package/ui/index.html +278 -5
  207. package/ui/js/auto-settings.js +70 -0
  208. package/ui/js/dashboard.js +90 -0
  209. package/ui/js/fact-detail.js +92 -0
  210. package/ui/js/feedback.js +2 -2
  211. package/ui/js/ide-status.js +102 -0
  212. package/ui/js/math-health.js +98 -0
  213. package/ui/js/recall-lab.js +127 -0
  214. package/ui/js/settings.js +2 -2
  215. package/ui/js/trust-dashboard.js +73 -0
  216. package/api_server.py +0 -724
  217. package/bin/aider-smart +0 -72
  218. package/bin/superlocalmemoryv2-learning +0 -4
  219. package/bin/superlocalmemoryv2-list +0 -3
  220. package/bin/superlocalmemoryv2-patterns +0 -4
  221. package/bin/superlocalmemoryv2-profile +0 -3
  222. package/bin/superlocalmemoryv2-recall +0 -3
  223. package/bin/superlocalmemoryv2-remember +0 -3
  224. package/bin/superlocalmemoryv2-reset +0 -3
  225. package/bin/superlocalmemoryv2-status +0 -3
  226. package/configs/chatgpt-desktop-mcp.json +0 -16
  227. package/configs/cursor-mcp.json +0 -15
  228. package/docs/SECURITY-QUICK-REFERENCE.md +0 -214
  229. package/hooks/memory-list-skill.js +0 -139
  230. package/hooks/memory-profile-skill.js +0 -273
  231. package/hooks/memory-recall-skill.js +0 -114
  232. package/hooks/memory-remember-skill.js +0 -127
  233. package/hooks/memory-reset-skill.js +0 -274
  234. package/mcp_server.py +0 -1800
  235. package/requirements-core.txt +0 -22
  236. package/requirements-learning.txt +0 -12
  237. package/requirements.txt +0 -12
  238. package/src/agent_registry.py +0 -411
  239. package/src/auth_middleware.py +0 -61
  240. package/src/auto_backup.py +0 -459
  241. package/src/behavioral/__init__.py +0 -49
  242. package/src/behavioral/behavioral_listener.py +0 -203
  243. package/src/behavioral/behavioral_patterns.py +0 -275
  244. package/src/behavioral/cross_project_transfer.py +0 -206
  245. package/src/behavioral/outcome_inference.py +0 -194
  246. package/src/behavioral/outcome_tracker.py +0 -193
  247. package/src/behavioral/tests/__init__.py +0 -4
  248. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  249. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  250. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  251. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  252. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  253. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  254. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  255. package/src/cache_manager.py +0 -518
  256. package/src/compliance/__init__.py +0 -48
  257. package/src/compliance/abac_engine.py +0 -149
  258. package/src/compliance/abac_middleware.py +0 -116
  259. package/src/compliance/audit_db.py +0 -215
  260. package/src/compliance/audit_logger.py +0 -148
  261. package/src/compliance/retention_manager.py +0 -289
  262. package/src/compliance/retention_scheduler.py +0 -186
  263. package/src/compliance/tests/__init__.py +0 -4
  264. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  265. package/src/compliance/tests/test_abac_engine.py +0 -124
  266. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  267. package/src/compliance/tests/test_audit_db.py +0 -123
  268. package/src/compliance/tests/test_audit_logger.py +0 -98
  269. package/src/compliance/tests/test_mcp_audit.py +0 -128
  270. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  271. package/src/compliance/tests/test_retention_manager.py +0 -131
  272. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  273. package/src/compression/__init__.py +0 -25
  274. package/src/compression/cli.py +0 -150
  275. package/src/compression/cold_storage.py +0 -217
  276. package/src/compression/config.py +0 -72
  277. package/src/compression/orchestrator.py +0 -133
  278. package/src/compression/tier2_compressor.py +0 -228
  279. package/src/compression/tier3_compressor.py +0 -153
  280. package/src/compression/tier_classifier.py +0 -148
  281. package/src/db_connection_manager.py +0 -536
  282. package/src/embedding_engine.py +0 -63
  283. package/src/embeddings/__init__.py +0 -47
  284. package/src/embeddings/cache.py +0 -70
  285. package/src/embeddings/cli.py +0 -113
  286. package/src/embeddings/constants.py +0 -47
  287. package/src/embeddings/database.py +0 -91
  288. package/src/embeddings/engine.py +0 -247
  289. package/src/embeddings/model_loader.py +0 -145
  290. package/src/event_bus.py +0 -562
  291. package/src/graph/__init__.py +0 -36
  292. package/src/graph/build_helpers.py +0 -74
  293. package/src/graph/cli.py +0 -87
  294. package/src/graph/cluster_builder.py +0 -188
  295. package/src/graph/cluster_summary.py +0 -148
  296. package/src/graph/constants.py +0 -47
  297. package/src/graph/edge_builder.py +0 -162
  298. package/src/graph/entity_extractor.py +0 -95
  299. package/src/graph/graph_core.py +0 -226
  300. package/src/graph/graph_search.py +0 -231
  301. package/src/graph/hierarchical.py +0 -207
  302. package/src/graph/schema.py +0 -99
  303. package/src/graph_engine.py +0 -52
  304. package/src/hnsw_index.py +0 -628
  305. package/src/hybrid_search.py +0 -46
  306. package/src/learning/__init__.py +0 -217
  307. package/src/learning/adaptive_ranker.py +0 -682
  308. package/src/learning/bootstrap/__init__.py +0 -69
  309. package/src/learning/bootstrap/constants.py +0 -93
  310. package/src/learning/bootstrap/db_queries.py +0 -316
  311. package/src/learning/bootstrap/sampling.py +0 -82
  312. package/src/learning/bootstrap/text_utils.py +0 -71
  313. package/src/learning/cross_project_aggregator.py +0 -857
  314. package/src/learning/db/__init__.py +0 -40
  315. package/src/learning/db/constants.py +0 -44
  316. package/src/learning/db/schema.py +0 -279
  317. package/src/learning/engagement_tracker.py +0 -628
  318. package/src/learning/feature_extractor.py +0 -708
  319. package/src/learning/feedback_collector.py +0 -806
  320. package/src/learning/learning_db.py +0 -915
  321. package/src/learning/project_context_manager.py +0 -572
  322. package/src/learning/ranking/__init__.py +0 -33
  323. package/src/learning/ranking/constants.py +0 -84
  324. package/src/learning/ranking/helpers.py +0 -278
  325. package/src/learning/source_quality_scorer.py +0 -676
  326. package/src/learning/synthetic_bootstrap.py +0 -755
  327. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  328. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  329. package/src/learning/tests/test_aggregator.py +0 -306
  330. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  331. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  332. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  333. package/src/learning/tests/test_feedback_collector.py +0 -294
  334. package/src/learning/tests/test_learning_db.py +0 -602
  335. package/src/learning/tests/test_learning_db_v28.py +0 -110
  336. package/src/learning/tests/test_learning_init_v28.py +0 -48
  337. package/src/learning/tests/test_outcome_signals.py +0 -48
  338. package/src/learning/tests/test_project_context.py +0 -292
  339. package/src/learning/tests/test_schema_migration.py +0 -319
  340. package/src/learning/tests/test_signal_inference.py +0 -397
  341. package/src/learning/tests/test_source_quality.py +0 -351
  342. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  343. package/src/learning/tests/test_workflow_miner.py +0 -318
  344. package/src/learning/workflow_pattern_miner.py +0 -655
  345. package/src/lifecycle/__init__.py +0 -54
  346. package/src/lifecycle/bounded_growth.py +0 -239
  347. package/src/lifecycle/compaction_engine.py +0 -226
  348. package/src/lifecycle/lifecycle_engine.py +0 -355
  349. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  350. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  351. package/src/lifecycle/retention_policy.py +0 -285
  352. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  353. package/src/lifecycle/tests/test_compaction.py +0 -179
  354. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  355. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  356. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  357. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  358. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  359. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  360. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  361. package/src/mcp_tools_v28.py +0 -281
  362. package/src/memory/__init__.py +0 -36
  363. package/src/memory/cli.py +0 -205
  364. package/src/memory/constants.py +0 -39
  365. package/src/memory/helpers.py +0 -28
  366. package/src/memory/schema.py +0 -166
  367. package/src/memory-profiles.py +0 -595
  368. package/src/memory-reset.py +0 -491
  369. package/src/memory_compression.py +0 -989
  370. package/src/memory_store_v2.py +0 -1155
  371. package/src/migrate_v1_to_v2.py +0 -629
  372. package/src/pattern_learner.py +0 -34
  373. package/src/patterns/__init__.py +0 -24
  374. package/src/patterns/analyzers.py +0 -251
  375. package/src/patterns/learner.py +0 -271
  376. package/src/patterns/scoring.py +0 -171
  377. package/src/patterns/store.py +0 -225
  378. package/src/patterns/terminology.py +0 -140
  379. package/src/provenance_tracker.py +0 -312
  380. package/src/qualixar_attribution.py +0 -139
  381. package/src/qualixar_watermark.py +0 -78
  382. package/src/query_optimizer.py +0 -511
  383. package/src/rate_limiter.py +0 -83
  384. package/src/search/__init__.py +0 -20
  385. package/src/search/cli.py +0 -77
  386. package/src/search/constants.py +0 -26
  387. package/src/search/engine.py +0 -241
  388. package/src/search/fusion.py +0 -122
  389. package/src/search/index_loader.py +0 -114
  390. package/src/search/methods.py +0 -162
  391. package/src/search_engine_v2.py +0 -401
  392. package/src/setup_validator.py +0 -482
  393. package/src/subscription_manager.py +0 -391
  394. package/src/tree/__init__.py +0 -59
  395. package/src/tree/builder.py +0 -185
  396. package/src/tree/nodes.py +0 -202
  397. package/src/tree/queries.py +0 -257
  398. package/src/tree/schema.py +0 -80
  399. package/src/tree_manager.py +0 -19
  400. package/src/trust/__init__.py +0 -45
  401. package/src/trust/constants.py +0 -66
  402. package/src/trust/queries.py +0 -157
  403. package/src/trust/schema.py +0 -95
  404. package/src/trust/scorer.py +0 -299
  405. package/src/trust/signals.py +0 -95
  406. package/src/trust_scorer.py +0 -44
  407. package/ui/app.js +0 -1588
  408. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  409. package/ui/js/graph-cytoscape.js +0 -1168
  410. package/ui/js/graph-d3-backup.js +0 -32
  411. package/ui/js/graph.js +0 -32
  412. package/ui_server.py +0 -266
  413. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  414. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  415. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  416. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  417. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  418. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  419. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  420. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  421. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  422. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  423. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  424. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  425. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  426. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  427. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  428. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  429. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  430. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  431. /package/{completions → ide/completions}/slm.bash +0 -0
  432. /package/{completions → ide/completions}/slm.zsh +0 -0
  433. /package/{configs → ide/configs}/cody-commands.json +0 -0
  434. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
@@ -1,628 +0,0 @@
1
- #!/usr/bin/env python3
2
- # SPDX-License-Identifier: MIT
3
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
- """
5
- EngagementTracker — Local-only engagement metrics.
6
-
7
- Measures how actively the user interacts with the memory system.
8
- All data stays local — NEVER transmitted anywhere.
9
-
10
- Capabilities:
11
- - Comprehensive engagement stats (days active, staleness, per-day rates)
12
- - Health status classification (HEALTHY / DECLINING / AT_RISK / INACTIVE)
13
- - Activity recording (delegates to LearningDB.increment_engagement)
14
- - Weekly summary aggregation
15
- - CLI-friendly formatted output for `slm engagement`
16
- - MCP resource exposure (read-only stats)
17
-
18
- Data sources:
19
- - memory.db (read-only) — creation dates, total count, source agents
20
- - learning.db (read/write via LearningDB) — feedback counts, patterns,
21
- engagement_metrics daily rows
22
-
23
- Design:
24
- - Thread-safe: each method opens/closes its own connection
25
- - Division-by-zero safe: all ratios default to 0.0 for empty databases
26
- - Graceful degradation: works even if learning.db does not exist yet
27
- """
28
-
29
- import json
30
- import logging
31
- import sqlite3
32
- import threading
33
- from datetime import datetime, date, timedelta
34
- from pathlib import Path
35
- from typing import Optional, List, Dict, Any
36
-
37
- logger = logging.getLogger("superlocalmemory.learning.engagement")
38
-
39
- MEMORY_DIR = Path.home() / ".claude-memory"
40
- MEMORY_DB_PATH = MEMORY_DIR / "memory.db"
41
-
42
-
43
- class EngagementTracker:
44
- """
45
- Local-only engagement metrics for the SuperLocalMemory system.
46
-
47
- Usage:
48
- tracker = EngagementTracker()
49
- stats = tracker.get_engagement_stats()
50
- print(tracker.format_for_cli())
51
-
52
- Thread-safe: all methods use per-call connections.
53
- """
54
-
55
- def __init__(
56
- self,
57
- memory_db_path: Optional[Path] = None,
58
- learning_db: Optional[Any] = None,
59
- ):
60
- """
61
- Initialize EngagementTracker.
62
-
63
- Args:
64
- memory_db_path: Path to memory.db. Defaults to
65
- ~/.claude-memory/memory.db. Opened read-only.
66
- learning_db: A LearningDB instance for reading/writing
67
- engagement metrics. If None, lazily created on first use.
68
- """
69
- self._memory_db_path = (
70
- Path(memory_db_path) if memory_db_path else MEMORY_DB_PATH
71
- )
72
- self._learning_db = learning_db
73
- self._lock = threading.Lock()
74
- logger.info(
75
- "EngagementTracker initialized: memory_db=%s",
76
- self._memory_db_path,
77
- )
78
-
79
- # ------------------------------------------------------------------
80
- # LearningDB access (lazy)
81
- # ------------------------------------------------------------------
82
-
83
- def _get_learning_db(self):
84
- """
85
- Get or lazily create the LearningDB instance.
86
-
87
- Returns None if LearningDB cannot be imported or initialized.
88
- """
89
- if self._learning_db is not None:
90
- return self._learning_db
91
-
92
- try:
93
- from .learning_db import LearningDB
94
- self._learning_db = LearningDB()
95
- return self._learning_db
96
- except Exception as e:
97
- logger.warning("Failed to initialize LearningDB: %s", e)
98
- return None
99
-
100
- # ------------------------------------------------------------------
101
- # Memory.db read-only access
102
- # ------------------------------------------------------------------
103
-
104
- def _open_memory_db(self) -> sqlite3.Connection:
105
- """
106
- Open a read-only connection to memory.db.
107
-
108
- Uses URI mode=ro when supported; falls back to regular connection.
109
- """
110
- db_str = str(self._memory_db_path)
111
- try:
112
- uri = f"file:{db_str}?mode=ro"
113
- conn = sqlite3.connect(uri, uri=True, timeout=5)
114
- except (sqlite3.OperationalError, sqlite3.NotSupportedError):
115
- conn = sqlite3.connect(db_str, timeout=5)
116
- conn.execute("PRAGMA busy_timeout=3000")
117
- return conn
118
-
119
- def _get_memory_db_columns(self) -> set:
120
- """Get available columns in the memories table."""
121
- if not self._memory_db_path.exists():
122
- return set()
123
- try:
124
- conn = self._open_memory_db()
125
- try:
126
- cursor = conn.cursor()
127
- cursor.execute("PRAGMA table_info(memories)")
128
- return {row[1] for row in cursor.fetchall()}
129
- finally:
130
- conn.close()
131
- except sqlite3.Error:
132
- return set()
133
-
134
- # ------------------------------------------------------------------
135
- # Core stats
136
- # ------------------------------------------------------------------
137
-
138
- def get_engagement_stats(self) -> Dict[str, Any]:
139
- """
140
- Return a comprehensive engagement report.
141
-
142
- Returns:
143
- Dict with keys:
144
- days_active — Days since first memory was created
145
- days_since_last — Days since most recent activity
146
- staleness_ratio — days_since_last / days_active (0=active, 1=abandoned)
147
- total_memories — Total memory count
148
- memories_per_day — Average memories created per active day
149
- recalls_per_day — Average recalls per active day (from feedback)
150
- patterns_learned — Transferable patterns with confidence > 0.6
151
- feedback_signals — Total feedback count
152
- health_status — 'HEALTHY', 'DECLINING', 'AT_RISK', 'INACTIVE'
153
- active_sources — List of tool sources used recently
154
- """
155
- # --- Memory.db stats ---
156
- mem_stats = self._get_memory_stats()
157
-
158
- # --- Learning.db stats ---
159
- learn_stats = self._get_learning_stats()
160
-
161
- # --- Derived metrics ---
162
- days_active = mem_stats['days_active']
163
- days_since_last = mem_stats['days_since_last']
164
- total_memories = mem_stats['total_memories']
165
-
166
- # Staleness: 0.0 = used today, 1.0 = abandoned
167
- if days_active > 0:
168
- staleness_ratio = round(days_since_last / days_active, 4)
169
- else:
170
- staleness_ratio = 0.0 # Brand-new user — not stale
171
-
172
- # Cap staleness at 1.0 (can exceed if days_since_last > days_active
173
- # due to timezone edge cases)
174
- staleness_ratio = min(staleness_ratio, 1.0)
175
-
176
- # Per-day rates
177
- if days_active > 0:
178
- memories_per_day = round(total_memories / days_active, 2)
179
- recalls_per_day = round(
180
- learn_stats['feedback_signals'] / days_active, 2
181
- )
182
- else:
183
- memories_per_day = float(total_memories) # Day 0 — show raw count
184
- recalls_per_day = 0.0
185
-
186
- health_status = self._compute_health_status(
187
- staleness_ratio, recalls_per_day
188
- )
189
-
190
- return {
191
- 'days_active': days_active,
192
- 'days_since_last': days_since_last,
193
- 'staleness_ratio': staleness_ratio,
194
- 'total_memories': total_memories,
195
- 'memories_per_day': memories_per_day,
196
- 'recalls_per_day': recalls_per_day,
197
- 'patterns_learned': learn_stats['patterns_learned'],
198
- 'feedback_signals': learn_stats['feedback_signals'],
199
- 'health_status': health_status,
200
- 'active_sources': mem_stats['active_sources'],
201
- }
202
-
203
- def _get_memory_stats(self) -> Dict[str, Any]:
204
- """
205
- Gather stats from memory.db (read-only).
206
-
207
- Returns dict with: days_active, days_since_last, total_memories,
208
- active_sources.
209
- """
210
- default = {
211
- 'days_active': 0,
212
- 'days_since_last': 0,
213
- 'total_memories': 0,
214
- 'active_sources': [],
215
- }
216
-
217
- if not self._memory_db_path.exists():
218
- return default
219
-
220
- available = self._get_memory_db_columns()
221
-
222
- try:
223
- conn = self._open_memory_db()
224
- try:
225
- cursor = conn.cursor()
226
-
227
- # Total memories
228
- cursor.execute("SELECT COUNT(*) FROM memories")
229
- total = cursor.fetchone()[0]
230
- if total == 0:
231
- return default
232
-
233
- # Date range
234
- if 'created_at' in available:
235
- cursor.execute(
236
- "SELECT MIN(created_at), MAX(created_at) "
237
- "FROM memories"
238
- )
239
- row = cursor.fetchone()
240
- first_ts, last_ts = row[0], row[1]
241
-
242
- first_date = self._parse_date(first_ts)
243
- last_date = self._parse_date(last_ts)
244
- today = date.today()
245
-
246
- if first_date and last_date:
247
- days_active = max((today - first_date).days, 1)
248
- days_since_last = max((today - last_date).days, 0)
249
- else:
250
- days_active = 1
251
- days_since_last = 0
252
- else:
253
- days_active = 1
254
- days_since_last = 0
255
-
256
- # Active sources (created_by field, v2.5+)
257
- active_sources = []
258
- if 'created_by' in available:
259
- try:
260
- cursor.execute(
261
- "SELECT DISTINCT created_by FROM memories "
262
- "WHERE created_by IS NOT NULL "
263
- "AND created_by != '' "
264
- "ORDER BY created_by"
265
- )
266
- active_sources = [
267
- row[0] for row in cursor.fetchall()
268
- ]
269
- except sqlite3.OperationalError:
270
- pass # Column might not be queryable
271
-
272
- return {
273
- 'days_active': days_active,
274
- 'days_since_last': days_since_last,
275
- 'total_memories': total,
276
- 'active_sources': active_sources,
277
- }
278
- finally:
279
- conn.close()
280
- except sqlite3.Error as e:
281
- logger.warning("Failed to read memory stats: %s", e)
282
- return default
283
-
284
- def _get_learning_stats(self) -> Dict[str, Any]:
285
- """
286
- Gather stats from learning.db via LearningDB.
287
-
288
- Returns dict with: patterns_learned, feedback_signals.
289
- """
290
- default = {
291
- 'patterns_learned': 0,
292
- 'feedback_signals': 0,
293
- }
294
-
295
- ldb = self._get_learning_db()
296
- if ldb is None:
297
- return default
298
-
299
- try:
300
- # Feedback signals
301
- feedback_count = ldb.get_feedback_count()
302
-
303
- # High-confidence patterns
304
- patterns = ldb.get_transferable_patterns(min_confidence=0.6)
305
- patterns_count = len(patterns)
306
-
307
- return {
308
- 'patterns_learned': patterns_count,
309
- 'feedback_signals': feedback_count,
310
- }
311
- except Exception as e:
312
- logger.warning("Failed to read learning stats: %s", e)
313
- return default
314
-
315
- # ------------------------------------------------------------------
316
- # Health classification
317
- # ------------------------------------------------------------------
318
-
319
- @staticmethod
320
- def _compute_health_status(
321
- staleness_ratio: float,
322
- recalls_per_day: float,
323
- ) -> str:
324
- """
325
- Classify engagement health.
326
-
327
- Tiers:
328
- HEALTHY — staleness < 0.1 AND recalls > 0.5/day
329
- DECLINING — staleness < 0.3 OR recalls > 0.2/day
330
- AT_RISK — staleness < 0.5
331
- INACTIVE — staleness >= 0.5
332
-
333
- Args:
334
- staleness_ratio: 0.0 (active) to 1.0 (abandoned).
335
- recalls_per_day: Average recall operations per day.
336
-
337
- Returns:
338
- One of 'HEALTHY', 'DECLINING', 'AT_RISK', 'INACTIVE'.
339
- """
340
- if staleness_ratio < 0.1 and recalls_per_day > 0.5:
341
- return 'HEALTHY'
342
- if staleness_ratio < 0.3 or recalls_per_day > 0.2:
343
- return 'DECLINING'
344
- if staleness_ratio < 0.5:
345
- return 'AT_RISK'
346
- return 'INACTIVE'
347
-
348
- # ------------------------------------------------------------------
349
- # Activity recording
350
- # ------------------------------------------------------------------
351
-
352
- def record_activity(
353
- self,
354
- activity_type: str,
355
- source: Optional[str] = None,
356
- ):
357
- """
358
- Record an engagement activity event.
359
-
360
- Delegates to LearningDB.increment_engagement() which maintains
361
- daily engagement_metrics rows.
362
-
363
- Args:
364
- activity_type: One of 'memory_created', 'recall_performed',
365
- 'feedback_given', 'pattern_updated'.
366
- source: Source tool identifier (e.g., "claude-desktop",
367
- "cursor", "cli").
368
- """
369
- # Map activity_type to LearningDB metric column names
370
- metric_map = {
371
- 'memory_created': 'memories_created',
372
- 'recall_performed': 'recalls_performed',
373
- 'feedback_given': 'feedback_signals',
374
- 'pattern_updated': 'patterns_updated',
375
- }
376
-
377
- metric_type = metric_map.get(activity_type)
378
- if metric_type is None:
379
- logger.warning(
380
- "Unknown activity type: %r (expected one of %s)",
381
- activity_type,
382
- list(metric_map.keys()),
383
- )
384
- return
385
-
386
- ldb = self._get_learning_db()
387
- if ldb is None:
388
- logger.debug(
389
- "LearningDB unavailable — cannot record activity '%s'",
390
- activity_type,
391
- )
392
- return
393
-
394
- try:
395
- ldb.increment_engagement(
396
- metric_type=metric_type,
397
- count=1,
398
- source=source,
399
- )
400
- logger.debug(
401
- "Recorded activity: type=%s, source=%s",
402
- activity_type, source,
403
- )
404
- except Exception as e:
405
- logger.warning("Failed to record activity: %s", e)
406
-
407
- # ------------------------------------------------------------------
408
- # Weekly summary
409
- # ------------------------------------------------------------------
410
-
411
- def get_weekly_summary(self) -> Dict[str, Any]:
412
- """
413
- Aggregate the last 7 days of engagement_metrics.
414
-
415
- Returns:
416
- Dict with:
417
- period_start — ISO date string
418
- period_end — ISO date string
419
- days_with_data — Number of days that had engagement rows
420
- total_memories_created
421
- total_recalls
422
- total_feedback
423
- total_patterns_updated
424
- avg_memories_per_day
425
- avg_recalls_per_day
426
- all_sources — Unique tools used across the week
427
- """
428
- ldb = self._get_learning_db()
429
- default = {
430
- 'period_start': (date.today() - timedelta(days=6)).isoformat(),
431
- 'period_end': date.today().isoformat(),
432
- 'days_with_data': 0,
433
- 'total_memories_created': 0,
434
- 'total_recalls': 0,
435
- 'total_feedback': 0,
436
- 'total_patterns_updated': 0,
437
- 'avg_memories_per_day': 0.0,
438
- 'avg_recalls_per_day': 0.0,
439
- 'all_sources': [],
440
- }
441
-
442
- if ldb is None:
443
- return default
444
-
445
- try:
446
- history = ldb.get_engagement_history(days=7)
447
- except Exception as e:
448
- logger.warning("Failed to get engagement history: %s", e)
449
- return default
450
-
451
- if not history:
452
- return default
453
-
454
- total_mem = 0
455
- total_rec = 0
456
- total_fb = 0
457
- total_pat = 0
458
- all_sources: set = set()
459
-
460
- for row in history:
461
- total_mem += row.get('memories_created', 0) or 0
462
- total_rec += row.get('recalls_performed', 0) or 0
463
- total_fb += row.get('feedback_signals', 0) or 0
464
- total_pat += row.get('patterns_updated', 0) or 0
465
-
466
- sources_raw = row.get('active_sources', '[]')
467
- if isinstance(sources_raw, str):
468
- try:
469
- sources = json.loads(sources_raw)
470
- all_sources.update(sources)
471
- except (json.JSONDecodeError, TypeError):
472
- pass
473
-
474
- days_with_data = len(history)
475
-
476
- return {
477
- 'period_start': (date.today() - timedelta(days=6)).isoformat(),
478
- 'period_end': date.today().isoformat(),
479
- 'days_with_data': days_with_data,
480
- 'total_memories_created': total_mem,
481
- 'total_recalls': total_rec,
482
- 'total_feedback': total_fb,
483
- 'total_patterns_updated': total_pat,
484
- 'avg_memories_per_day': (
485
- round(total_mem / days_with_data, 1)
486
- if days_with_data > 0 else 0.0
487
- ),
488
- 'avg_recalls_per_day': (
489
- round(total_rec / days_with_data, 1)
490
- if days_with_data > 0 else 0.0
491
- ),
492
- 'all_sources': sorted(all_sources),
493
- }
494
-
495
- # ------------------------------------------------------------------
496
- # CLI formatting
497
- # ------------------------------------------------------------------
498
-
499
- def format_for_cli(self) -> str:
500
- """
501
- Format engagement stats as human-readable CLI output.
502
-
503
- Example:
504
- Active for: 94 days
505
- Last activity: 2 days ago
506
- Memories per day: 3.2
507
- Recalls per day: 1.8
508
- Patterns learned: 23
509
- Engagement: HEALTHY (staleness: 0.02)
510
- """
511
- try:
512
- stats = self.get_engagement_stats()
513
- except Exception as e:
514
- return f"Error computing engagement stats: {e}"
515
-
516
- # Format "last activity" human-friendly
517
- days_since = stats['days_since_last']
518
- if days_since == 0:
519
- last_activity = "today"
520
- elif days_since == 1:
521
- last_activity = "yesterday"
522
- else:
523
- last_activity = f"{days_since} days ago"
524
-
525
- # Health status with optional color hint for terminals
526
- health = stats['health_status']
527
- staleness = stats['staleness_ratio']
528
-
529
- lines = [
530
- f"Active for: {stats['days_active']} days",
531
- f"Last activity: {last_activity}",
532
- f"Total memories: {stats['total_memories']}",
533
- f"Memories per day: {stats['memories_per_day']}",
534
- f"Recalls per day: {stats['recalls_per_day']}",
535
- f"Patterns learned: {stats['patterns_learned']}",
536
- f"Feedback signals: {stats['feedback_signals']}",
537
- f"Engagement: {health} (staleness: {staleness:.2f})",
538
- ]
539
-
540
- if stats['active_sources']:
541
- lines.append(f"Active sources: {', '.join(stats['active_sources'])}")
542
-
543
- return "\n".join(lines)
544
-
545
- # ------------------------------------------------------------------
546
- # Utilities
547
- # ------------------------------------------------------------------
548
-
549
- @staticmethod
550
- def _parse_date(timestamp: Any) -> Optional[date]:
551
- """
552
- Parse a timestamp string into a date object.
553
-
554
- Handles multiple formats from SQLite:
555
- - '2026-02-16 14:30:00'
556
- - '2026-02-16T14:30:00'
557
- - '2026-02-16'
558
- """
559
- if timestamp is None:
560
- return None
561
-
562
- ts = str(timestamp).strip()
563
- if not ts:
564
- return None
565
-
566
- # Try ISO formats
567
- for fmt in (
568
- "%Y-%m-%d %H:%M:%S",
569
- "%Y-%m-%dT%H:%M:%S",
570
- "%Y-%m-%d %H:%M:%S.%f",
571
- "%Y-%m-%dT%H:%M:%S.%f",
572
- "%Y-%m-%d",
573
- ):
574
- try:
575
- return datetime.strptime(ts, fmt).date()
576
- except ValueError:
577
- continue
578
-
579
- # Last resort: try to parse just the date portion
580
- try:
581
- return datetime.strptime(ts[:10], "%Y-%m-%d").date()
582
- except (ValueError, IndexError):
583
- logger.debug("Unparseable timestamp: %r", timestamp)
584
- return None
585
-
586
-
587
- # ======================================================================
588
- # Standalone testing
589
- # ======================================================================
590
-
591
- if __name__ == "__main__":
592
- logging.basicConfig(
593
- level=logging.DEBUG,
594
- format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
595
- )
596
-
597
- tracker = EngagementTracker()
598
-
599
- print("=== Engagement Stats ===")
600
- stats = tracker.get_engagement_stats()
601
- for k, v in stats.items():
602
- print(f" {k}: {v}")
603
-
604
- print("\n=== CLI Output ===")
605
- print(tracker.format_for_cli())
606
-
607
- print("\n=== Weekly Summary ===")
608
- weekly = tracker.get_weekly_summary()
609
- for k, v in weekly.items():
610
- print(f" {k}: {v}")
611
-
612
- print("\n=== Health Classification Tests ===")
613
- test_cases = [
614
- (0.02, 1.5, "HEALTHY"),
615
- (0.05, 0.3, "DECLINING"),
616
- (0.25, 0.3, "DECLINING"),
617
- (0.35, 0.1, "AT_RISK"),
618
- (0.60, 0.0, "INACTIVE"),
619
- (0.0, 0.0, "DECLINING"), # Active but no recalls
620
- (0.99, 5.0, "DECLINING"), # High staleness but high recall
621
- ]
622
- for staleness, recalls, expected in test_cases:
623
- actual = EngagementTracker._compute_health_status(staleness, recalls)
624
- status = "PASS" if actual == expected else "FAIL"
625
- print(
626
- f" [{status}] staleness={staleness:.2f}, recalls={recalls:.1f}"
627
- f" -> {actual} (expected {expected})"
628
- )