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,655 +0,0 @@
1
- #!/usr/bin/env python3
2
- # SPDX-License-Identifier: MIT
3
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
- """
5
- WorkflowPatternMiner -- Layer 3: Sliding-window sequence and temporal pattern mining.
6
-
7
- Detects repeating workflow sequences and time-of-day activity preferences
8
- from memory creation timestamps and content. Uses a custom sliding-window
9
- n-gram approach inspired by TSW-PrefixSpan (IEEE 2020) -- NO external
10
- dependencies beyond stdlib.
11
-
12
- How it works:
13
- 1. Fetches recent memories from memory.db (read-only).
14
- 2. Classifies each memory into one of 7 activity types via keyword scoring.
15
- 3. Extracts n-gram sequences (length 2-5) from the ordered activity stream.
16
- 4. Calculates support (frequency / total windows) for each n-gram.
17
- 5. Mines temporal patterns (dominant activity per time-of-day bucket).
18
- 6. Stores discovered patterns in learning.db via LearningDB.
19
-
20
- Design decisions:
21
- - Word-boundary matching prevents false positives (e.g. "document" != "docs").
22
- - Consecutive identical activities in an n-gram are skipped as noise.
23
- - Minimum evidence threshold (5 memories) prevents weak temporal claims.
24
- - Patterns are cleared and re-mined each run (idempotent operation).
25
- """
26
-
27
- import json
28
- import logging
29
- import re
30
- import sqlite3
31
- from collections import Counter
32
- from datetime import datetime
33
- from pathlib import Path
34
- from typing import Dict, List, Optional, Any
35
-
36
- logger = logging.getLogger("superlocalmemory.learning.workflow")
37
-
38
- MEMORY_DIR = Path.home() / ".claude-memory"
39
- MEMORY_DB_PATH = MEMORY_DIR / "memory.db"
40
-
41
- # ---------------------------------------------------------------------------
42
- # Activity type taxonomy (7 categories)
43
- # ---------------------------------------------------------------------------
44
- # Each key maps to a list of keyword phrases. Scoring is word-boundary aware
45
- # to avoid partial matches (e.g. "test" won't match inside "latest").
46
-
47
- ACTIVITY_TYPES: Dict[str, List[str]] = {
48
- "docs": [
49
- "documentation", "readme", "wiki", "spec", "prd",
50
- "design doc", "changelog", "api doc",
51
- ],
52
- "architecture": [
53
- "architecture", "diagram", "system design", "schema",
54
- "api design", "data model", "erd",
55
- ],
56
- "code": [
57
- "implement", "function", "class", "module", "refactor",
58
- "code", "feature", "component",
59
- ],
60
- "test": [
61
- "test", "pytest", "jest", "coverage", "assertion",
62
- "mock", "spec", "unit test",
63
- ],
64
- "debug": [
65
- "bug", "fix", "error", "stack trace", "debug",
66
- "issue", "exception", "traceback",
67
- ],
68
- "deploy": [
69
- "deploy", "docker", "ci/cd", "pipeline", "release",
70
- "production", "staging", "build",
71
- ],
72
- "config": [
73
- "config", "env", "settings", "setup", "install",
74
- "dependency", "package", "requirements",
75
- ],
76
- }
77
-
78
- # Pre-compile word-boundary regexes per keyword for performance.
79
- # Each entry: (activity_type, compiled_regex)
80
- _KEYWORD_PATTERNS: List[tuple] = []
81
- for _act_type, _keywords in ACTIVITY_TYPES.items():
82
- for _kw in _keywords:
83
- # Use re.escape for phrases that may contain special chars (e.g. "ci/cd")
84
- _KEYWORD_PATTERNS.append(
85
- (_act_type, re.compile(r"\b" + re.escape(_kw) + r"\b", re.IGNORECASE))
86
- )
87
-
88
-
89
- class WorkflowPatternMiner:
90
- """
91
- Mines workflow sequences and temporal patterns from memory history.
92
-
93
- Reads from memory.db (never writes to it) and stores discovered
94
- patterns in learning.db via the shared LearningDB instance.
95
-
96
- Usage:
97
- from learning.learning_db import LearningDB
98
- miner = WorkflowPatternMiner(learning_db=LearningDB())
99
- results = miner.mine_all()
100
- print(results['sequences']) # Top workflow sequences
101
- print(results['temporal']) # Time-of-day preferences
102
- """
103
-
104
- def __init__(
105
- self,
106
- memory_db_path: Optional[Path] = None,
107
- learning_db: Optional[Any] = None,
108
- ):
109
- """
110
- Args:
111
- memory_db_path: Path to memory.db for reading memories.
112
- Defaults to ~/.claude-memory/memory.db.
113
- learning_db: LearningDB instance for storing patterns.
114
- If None, patterns are returned but not persisted.
115
- """
116
- self.memory_db_path = Path(memory_db_path) if memory_db_path else MEMORY_DB_PATH
117
- self.learning_db = learning_db
118
-
119
- # ======================================================================
120
- # Public API
121
- # ======================================================================
122
-
123
- def mine_sequences(
124
- self,
125
- memories: Optional[List[dict]] = None,
126
- min_support: float = 0.3,
127
- ) -> List[dict]:
128
- """
129
- Mine repeating workflow sequences from memory content.
130
-
131
- Algorithm:
132
- 1. Classify each memory into an activity type.
133
- 2. Build an ordered activity stream (chronological).
134
- 3. Extract n-grams of length 2-5 via sliding window.
135
- 4. Filter out n-grams with consecutive identical activities.
136
- 5. Compute support = count / total_windows_for_that_length.
137
- 6. Return top 20 patterns above *min_support*.
138
-
139
- Args:
140
- memories: List of memory dicts with 'content' and 'created_at'.
141
- If None, fetches the last 500 from memory.db.
142
- min_support: Minimum support threshold (0.0 - 1.0). Default 0.3.
143
-
144
- Returns:
145
- Sorted list of dicts:
146
- [{'sequence': ['docs', 'code', 'test'],
147
- 'support': 0.45, 'count': 12, 'length': 3}, ...]
148
- """
149
- if memories is None:
150
- memories = self._fetch_memories(limit=500)
151
-
152
- if not memories:
153
- logger.info("No memories to mine sequences from")
154
- return []
155
-
156
- # Step 1 + 2: classify and build activity stream
157
- activity_stream: List[str] = []
158
- for mem in memories:
159
- activity = self._classify_activity(mem.get("content", ""))
160
- if activity != "unknown":
161
- activity_stream.append(activity)
162
-
163
- if len(activity_stream) < 2:
164
- logger.info(
165
- "Activity stream too short (%d) for sequence mining",
166
- len(activity_stream),
167
- )
168
- return []
169
-
170
- # Step 3-5: extract n-grams and compute support
171
- all_patterns: List[dict] = []
172
-
173
- for n in range(2, 6): # lengths 2, 3, 4, 5
174
- if len(activity_stream) < n:
175
- continue
176
-
177
- ngram_counts: Counter = Counter()
178
- total_windows = len(activity_stream) - n + 1
179
-
180
- for i in range(total_windows):
181
- ngram = tuple(activity_stream[i : i + n])
182
-
183
- # Skip n-grams where any consecutive pair repeats (noise)
184
- has_repeat = any(
185
- ngram[j] == ngram[j + 1] for j in range(len(ngram) - 1)
186
- )
187
- if has_repeat:
188
- continue
189
-
190
- ngram_counts[ngram] += 1
191
-
192
- # Convert to pattern dicts with support
193
- for ngram, count in ngram_counts.items():
194
- support = count / total_windows if total_windows > 0 else 0.0
195
- if support >= min_support:
196
- all_patterns.append({
197
- "sequence": list(ngram),
198
- "support": round(support, 4),
199
- "count": count,
200
- "length": n,
201
- })
202
-
203
- # Step 6: sort by support descending, limit to top 20
204
- all_patterns.sort(key=lambda p: (-p["support"], -p["length"]))
205
- top_patterns = all_patterns[:20]
206
-
207
- logger.info(
208
- "Mined %d sequence patterns from %d activities (top %d returned)",
209
- len(all_patterns),
210
- len(activity_stream),
211
- len(top_patterns),
212
- )
213
- return top_patterns
214
-
215
- def mine_temporal_patterns(
216
- self,
217
- memories: Optional[List[dict]] = None,
218
- ) -> Dict[str, dict]:
219
- """
220
- Detect time-of-day activity preferences.
221
-
222
- Buckets each memory by hour into morning/afternoon/evening/night,
223
- counts activity types per bucket, and identifies the dominant
224
- activity type for each time period.
225
-
226
- Args:
227
- memories: List of memory dicts with 'content' and 'created_at'.
228
- If None, fetches from memory.db.
229
-
230
- Returns:
231
- Dict keyed by bucket name:
232
- {'morning': {
233
- 'dominant_activity': 'code',
234
- 'confidence': 0.65,
235
- 'evidence_count': 23,
236
- 'distribution': {'code': 15, 'test': 5, 'debug': 3}
237
- }, ...}
238
-
239
- Buckets with fewer than 5 evidence memories are omitted.
240
- """
241
- if memories is None:
242
- memories = self._fetch_memories(limit=500)
243
-
244
- if not memories:
245
- logger.info("No memories to mine temporal patterns from")
246
- return {}
247
-
248
- # bucket_name -> Counter of activity types
249
- bucket_activities: Dict[str, Counter] = {
250
- "morning": Counter(),
251
- "afternoon": Counter(),
252
- "evening": Counter(),
253
- "night": Counter(),
254
- }
255
-
256
- for mem in memories:
257
- activity = self._classify_activity(mem.get("content", ""))
258
- if activity == "unknown":
259
- continue
260
-
261
- hour = self._parse_hour(mem.get("created_at"))
262
- if hour is None:
263
- continue
264
-
265
- bucket = self._hour_to_bucket(hour)
266
- bucket_activities[bucket][activity] += 1
267
-
268
- # Build result: only include buckets with minimum evidence
269
- min_evidence = 5
270
- result: Dict[str, dict] = {}
271
-
272
- for bucket, counter in bucket_activities.items():
273
- total = sum(counter.values())
274
- if total < min_evidence:
275
- continue
276
-
277
- dominant_activity, dominant_count = counter.most_common(1)[0]
278
- confidence = round(dominant_count / total, 4) if total > 0 else 0.0
279
-
280
- result[bucket] = {
281
- "dominant_activity": dominant_activity,
282
- "confidence": confidence,
283
- "evidence_count": total,
284
- "distribution": dict(counter),
285
- }
286
-
287
- logger.info(
288
- "Mined temporal patterns for %d/%d buckets",
289
- len(result),
290
- len(bucket_activities),
291
- )
292
- return result
293
-
294
- def mine_all(
295
- self,
296
- memories: Optional[List[dict]] = None,
297
- ) -> dict:
298
- """
299
- Run all mining methods and optionally persist results to learning.db.
300
-
301
- Fetches memories once and passes to both miners. If a LearningDB
302
- instance was provided at init, clears old patterns and stores new ones.
303
-
304
- Args:
305
- memories: Pre-fetched memories, or None to auto-fetch.
306
-
307
- Returns:
308
- {'sequences': [...], 'temporal': {...}}
309
- """
310
- if memories is None:
311
- memories = self._fetch_memories(limit=500)
312
-
313
- sequences = self.mine_sequences(memories=memories)
314
- temporal = self.mine_temporal_patterns(memories=memories)
315
-
316
- # Persist to learning.db if available
317
- if self.learning_db is not None:
318
- self._persist_patterns(sequences, temporal)
319
-
320
- return {
321
- "sequences": sequences,
322
- "temporal": temporal,
323
- }
324
-
325
- def get_workflow_insights(self) -> dict:
326
- """
327
- Read stored patterns from learning.db and format for display.
328
-
329
- Returns a user-friendly summary suitable for CLI output or
330
- dashboard rendering. If no learning_db, returns empty structure.
331
- """
332
- if self.learning_db is None:
333
- return {
334
- "sequences": [],
335
- "temporal": {},
336
- "summary": "No learning database connected",
337
- }
338
-
339
- try:
340
- seq_patterns = self.learning_db.get_workflow_patterns(
341
- pattern_type="sequence",
342
- )
343
- temp_patterns = self.learning_db.get_workflow_patterns(
344
- pattern_type="temporal",
345
- )
346
- except Exception as e:
347
- logger.error("Failed to read workflow patterns: %s", e)
348
- return {
349
- "sequences": [],
350
- "temporal": {},
351
- "summary": "Error reading patterns",
352
- }
353
-
354
- # Parse stored JSON values back into structured data
355
- sequences = []
356
- for p in seq_patterns:
357
- try:
358
- value = json.loads(p.get("pattern_value", "{}"))
359
- sequences.append({
360
- "sequence": value.get("sequence", []),
361
- "support": p.get("confidence", 0.0),
362
- "count": p.get("evidence_count", 0),
363
- "length": len(value.get("sequence", [])),
364
- })
365
- except (json.JSONDecodeError, TypeError):
366
- continue
367
-
368
- temporal = {}
369
- for p in temp_patterns:
370
- try:
371
- value = json.loads(p.get("pattern_value", "{}"))
372
- bucket_name = p.get("pattern_key", "unknown")
373
- temporal[bucket_name] = value
374
- except (json.JSONDecodeError, TypeError):
375
- continue
376
-
377
- # Build a natural language summary
378
- summary_parts = []
379
- if sequences:
380
- top = sequences[0]
381
- seq_str = " -> ".join(top["sequence"])
382
- summary_parts.append(
383
- f"Most common workflow: {seq_str} "
384
- f"(support={top['support']:.0%}, seen {top['count']}x)"
385
- )
386
- if temporal:
387
- for bucket, info in sorted(temporal.items()):
388
- dominant = info.get("dominant_activity", "?")
389
- conf = info.get("confidence", 0)
390
- summary_parts.append(
391
- f" {bucket}: mostly {dominant} ({conf:.0%} confidence)"
392
- )
393
-
394
- return {
395
- "sequences": sequences,
396
- "temporal": temporal,
397
- "summary": "\n".join(summary_parts) if summary_parts else "No patterns discovered yet",
398
- }
399
-
400
- # ======================================================================
401
- # Internal helpers
402
- # ======================================================================
403
-
404
- def _classify_activity(self, content: str) -> str:
405
- """
406
- Classify a memory's content into one of the 7 activity types.
407
-
408
- Scores each type by counting word-boundary keyword matches in the
409
- content. Returns the highest-scoring type, or 'unknown' if no
410
- keywords matched.
411
-
412
- Args:
413
- content: Raw memory content string.
414
-
415
- Returns:
416
- Activity type string (e.g. 'code', 'test') or 'unknown'.
417
- """
418
- if not content:
419
- return "unknown"
420
-
421
- scores: Counter = Counter()
422
-
423
- for act_type, pattern in _KEYWORD_PATTERNS:
424
- if pattern.search(content):
425
- scores[act_type] += 1
426
-
427
- if not scores:
428
- return "unknown"
429
-
430
- # Return the type with the highest score
431
- best_type, _best_count = scores.most_common(1)[0]
432
- return best_type
433
-
434
- def _hour_to_bucket(self, hour: int) -> str:
435
- """
436
- Map an hour (0-23) to a time-of-day bucket.
437
-
438
- Buckets:
439
- morning = 6-11
440
- afternoon = 12-17
441
- evening = 18-23
442
- night = 0-5
443
- """
444
- if 6 <= hour <= 11:
445
- return "morning"
446
- elif 12 <= hour <= 17:
447
- return "afternoon"
448
- elif 18 <= hour <= 23:
449
- return "evening"
450
- else: # 0-5
451
- return "night"
452
-
453
- def _parse_hour(self, timestamp: Optional[str]) -> Optional[int]:
454
- """
455
- Extract the hour from a timestamp string.
456
-
457
- Handles multiple formats gracefully:
458
- - ISO 8601: '2026-02-14T09:30:00'
459
- - SQLite: '2026-02-14 09:30:00'
460
- - Date only: '2026-02-14' (returns None -- no time info)
461
-
462
- Returns:
463
- Hour as int (0-23), or None if parsing fails.
464
- """
465
- if not timestamp:
466
- return None
467
-
468
- # Try ISO format first (handles both 'T' and space separator)
469
- for fmt in ("%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S",
470
- "%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f"):
471
- try:
472
- dt = datetime.strptime(timestamp, fmt)
473
- return dt.hour
474
- except (ValueError, TypeError):
475
- continue
476
-
477
- # Last resort: fromisoformat (Python 3.7+), handles wider range
478
- try:
479
- dt = datetime.fromisoformat(timestamp)
480
- return dt.hour
481
- except (ValueError, TypeError):
482
- pass
483
-
484
- logger.debug("Could not parse timestamp: %s", timestamp)
485
- return None
486
-
487
- def _fetch_memories(self, limit: int = 500) -> List[dict]:
488
- """
489
- Read recent memories from memory.db (read-only).
490
-
491
- Fetches id, content, created_at, and project_name ordered
492
- chronologically (ASC) so the activity stream preserves the
493
- user's actual workflow order.
494
-
495
- Args:
496
- limit: Maximum number of memories to fetch.
497
-
498
- Returns:
499
- List of dicts with keys: id, content, created_at, project_name.
500
- Returns empty list on any error.
501
- """
502
- if not self.memory_db_path.exists():
503
- logger.warning("memory.db not found at %s", self.memory_db_path)
504
- return []
505
-
506
- try:
507
- conn = sqlite3.connect(str(self.memory_db_path), timeout=5)
508
- conn.row_factory = sqlite3.Row
509
- # Read-only pragmas
510
- conn.execute("PRAGMA query_only=ON")
511
- cursor = conn.cursor()
512
-
513
- cursor.execute(
514
- """
515
- SELECT id, content, created_at, project_name
516
- FROM memories
517
- ORDER BY created_at ASC
518
- LIMIT ?
519
- """,
520
- (limit,),
521
- )
522
- rows = cursor.fetchall()
523
- return [dict(row) for row in rows]
524
- except sqlite3.Error as e:
525
- logger.error("Failed to fetch memories: %s", e)
526
- return []
527
- finally:
528
- try:
529
- conn.close()
530
- except Exception:
531
- pass
532
-
533
- def _persist_patterns(
534
- self,
535
- sequences: List[dict],
536
- temporal: Dict[str, dict],
537
- ) -> None:
538
- """
539
- Clear old patterns and store newly mined ones in learning.db.
540
-
541
- Uses LearningDB.clear_workflow_patterns() then store_workflow_pattern()
542
- for each discovered pattern. This is idempotent -- safe to call
543
- repeatedly.
544
- """
545
- if self.learning_db is None:
546
- return
547
-
548
- try:
549
- # Clear previous patterns (idempotent re-mine)
550
- self.learning_db.clear_workflow_patterns(pattern_type="sequence")
551
- self.learning_db.clear_workflow_patterns(pattern_type="temporal")
552
-
553
- # Store sequence patterns
554
- for pat in sequences:
555
- pattern_key = " -> ".join(pat["sequence"])
556
- pattern_value = json.dumps({
557
- "sequence": pat["sequence"],
558
- "count": pat["count"],
559
- })
560
- self.learning_db.store_workflow_pattern(
561
- pattern_type="sequence",
562
- pattern_key=pattern_key,
563
- pattern_value=pattern_value,
564
- confidence=pat["support"],
565
- evidence_count=pat["count"],
566
- metadata={"length": pat["length"]},
567
- )
568
-
569
- # Store temporal patterns
570
- for bucket_name, info in temporal.items():
571
- pattern_value = json.dumps(info)
572
- self.learning_db.store_workflow_pattern(
573
- pattern_type="temporal",
574
- pattern_key=bucket_name,
575
- pattern_value=pattern_value,
576
- confidence=info.get("confidence", 0.0),
577
- evidence_count=info.get("evidence_count", 0),
578
- metadata={"dominant_activity": info.get("dominant_activity")},
579
- )
580
-
581
- total_stored = len(sequences) + len(temporal)
582
- logger.info("Persisted %d workflow patterns to learning.db", total_stored)
583
-
584
- # Update engagement metric
585
- try:
586
- self.learning_db.increment_engagement(
587
- "patterns_updated",
588
- count=total_stored,
589
- )
590
- except Exception:
591
- pass # Engagement tracking is best-effort
592
-
593
- except Exception as e:
594
- logger.error("Failed to persist workflow patterns: %s", e)
595
-
596
-
597
- # ======================================================================
598
- # Standalone execution (for CLI: python3 workflow_pattern_miner.py)
599
- # ======================================================================
600
-
601
- def main():
602
- """Run workflow mining from CLI and print results."""
603
- import sys
604
-
605
- logging.basicConfig(
606
- level=logging.INFO,
607
- format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
608
- )
609
-
610
- # Try to get LearningDB
611
- learning_db = None
612
- try:
613
- # When run from src/learning/ directory or installed path
614
- sys.path.insert(0, str(Path(__file__).parent))
615
- from learning_db import LearningDB
616
- learning_db = LearningDB()
617
- except ImportError:
618
- logger.warning("LearningDB not available -- patterns will not be persisted")
619
-
620
- miner = WorkflowPatternMiner(learning_db=learning_db)
621
- results = miner.mine_all()
622
-
623
- # Pretty-print sequences
624
- sequences = results.get("sequences", [])
625
- if sequences:
626
- print(f"\n{'='*60}")
627
- print(f" Workflow Sequences ({len(sequences)} patterns)")
628
- print(f"{'='*60}")
629
- for i, pat in enumerate(sequences, 1):
630
- seq_str = " -> ".join(pat["sequence"])
631
- print(f" {i:2d}. {seq_str}")
632
- print(f" support={pat['support']:.1%} count={pat['count']} length={pat['length']}")
633
- else:
634
- print("\n No workflow sequences found.")
635
-
636
- # Pretty-print temporal patterns
637
- temporal = results.get("temporal", {})
638
- if temporal:
639
- print(f"\n{'='*60}")
640
- print(f" Temporal Patterns")
641
- print(f"{'='*60}")
642
- for bucket in ("morning", "afternoon", "evening", "night"):
643
- if bucket in temporal:
644
- info = temporal[bucket]
645
- print(f" {bucket:>10s}: {info['dominant_activity']:<14s} "
646
- f"confidence={info['confidence']:.0%} "
647
- f"evidence={info['evidence_count']}")
648
- else:
649
- print("\n No temporal patterns found (need >= 5 memories per time bucket).")
650
-
651
- print()
652
-
653
-
654
- if __name__ == "__main__":
655
- main()