superlocalmemory 2.8.6 → 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 (431) hide show
  1. package/LICENSE +9 -1
  2. package/NOTICE +63 -0
  3. package/README.md +165 -480
  4. package/bin/slm +17 -449
  5. package/bin/slm-npm +1 -1
  6. package/conftest.py +5 -0
  7. package/docs/api-reference.md +284 -0
  8. package/docs/architecture.md +149 -0
  9. package/docs/auto-memory.md +150 -0
  10. package/docs/cli-reference.md +276 -0
  11. package/docs/compliance.md +191 -0
  12. package/docs/configuration.md +182 -0
  13. package/docs/getting-started.md +102 -0
  14. package/docs/ide-setup.md +261 -0
  15. package/docs/mcp-tools.md +220 -0
  16. package/docs/migration-from-v2.md +170 -0
  17. package/docs/profiles.md +173 -0
  18. package/docs/troubleshooting.md +310 -0
  19. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  20. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  21. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  22. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  23. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  24. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  25. package/ide/configs/cursor-mcp.json +15 -0
  26. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  27. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  28. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  29. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  30. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  31. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  32. package/{configs → ide/configs}/zed-mcp.json +2 -2
  33. package/{hooks → ide/hooks}/context-hook.js +9 -20
  34. package/ide/hooks/memory-list-skill.js +70 -0
  35. package/ide/hooks/memory-profile-skill.js +101 -0
  36. package/ide/hooks/memory-recall-skill.js +62 -0
  37. package/ide/hooks/memory-remember-skill.js +68 -0
  38. package/ide/hooks/memory-reset-skill.js +160 -0
  39. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  40. package/ide/integrations/langchain/README.md +106 -0
  41. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  42. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  43. package/ide/integrations/langchain/pyproject.toml +38 -0
  44. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  45. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  46. package/ide/integrations/langchain/tests/test_security.py +117 -0
  47. package/ide/integrations/llamaindex/README.md +81 -0
  48. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  49. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  50. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  51. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  52. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  53. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  54. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  55. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  56. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  57. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  58. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  59. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  60. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  61. package/package.json +13 -22
  62. package/pyproject.toml +85 -0
  63. package/scripts/build-dmg.sh +417 -0
  64. package/scripts/install-skills.ps1 +334 -0
  65. package/scripts/postinstall.js +2 -2
  66. package/scripts/start-dashboard.ps1 +52 -0
  67. package/scripts/start-dashboard.sh +41 -0
  68. package/scripts/sync-wiki.ps1 +127 -0
  69. package/scripts/sync-wiki.sh +82 -0
  70. package/scripts/test-dmg.sh +161 -0
  71. package/scripts/test-npm-package.ps1 +252 -0
  72. package/scripts/test-npm-package.sh +207 -0
  73. package/scripts/verify-install.ps1 +294 -0
  74. package/scripts/verify-install.sh +266 -0
  75. package/src/superlocalmemory/__init__.py +0 -0
  76. package/src/superlocalmemory/attribution/__init__.py +9 -0
  77. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  78. package/src/superlocalmemory/attribution/signer.py +153 -0
  79. package/src/superlocalmemory/attribution/watermark.py +189 -0
  80. package/src/superlocalmemory/cli/__init__.py +5 -0
  81. package/src/superlocalmemory/cli/commands.py +245 -0
  82. package/src/superlocalmemory/cli/main.py +89 -0
  83. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  84. package/src/superlocalmemory/cli/post_install.py +99 -0
  85. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  86. package/src/superlocalmemory/compliance/__init__.py +0 -0
  87. package/src/superlocalmemory/compliance/abac.py +204 -0
  88. package/src/superlocalmemory/compliance/audit.py +314 -0
  89. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  90. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  91. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  92. package/src/superlocalmemory/compliance/retention.py +232 -0
  93. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  94. package/src/superlocalmemory/core/__init__.py +0 -0
  95. package/src/superlocalmemory/core/config.py +391 -0
  96. package/src/superlocalmemory/core/embeddings.py +293 -0
  97. package/src/superlocalmemory/core/engine.py +701 -0
  98. package/src/superlocalmemory/core/hooks.py +65 -0
  99. package/src/superlocalmemory/core/maintenance.py +172 -0
  100. package/src/superlocalmemory/core/modes.py +140 -0
  101. package/src/superlocalmemory/core/profiles.py +234 -0
  102. package/src/superlocalmemory/core/registry.py +117 -0
  103. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  104. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  105. package/src/superlocalmemory/encoding/__init__.py +0 -0
  106. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  107. package/src/superlocalmemory/encoding/emotional.py +125 -0
  108. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  109. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  110. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  111. package/src/superlocalmemory/encoding/foresight.py +91 -0
  112. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  113. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  114. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  115. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  116. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  117. package/src/superlocalmemory/encoding/type_router.py +235 -0
  118. package/src/superlocalmemory/hooks/__init__.py +3 -0
  119. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  120. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  121. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  122. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  123. package/src/superlocalmemory/infra/__init__.py +3 -0
  124. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  125. package/src/superlocalmemory/infra/backup.py +317 -0
  126. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  127. package/src/superlocalmemory/infra/event_bus.py +381 -0
  128. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  129. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  130. package/src/superlocalmemory/learning/__init__.py +0 -0
  131. package/src/superlocalmemory/learning/adaptive.py +172 -0
  132. package/src/superlocalmemory/learning/behavioral.py +490 -0
  133. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  134. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  135. package/src/superlocalmemory/learning/cross_project.py +399 -0
  136. package/src/superlocalmemory/learning/database.py +376 -0
  137. package/src/superlocalmemory/learning/engagement.py +323 -0
  138. package/src/superlocalmemory/learning/features.py +138 -0
  139. package/src/superlocalmemory/learning/feedback.py +316 -0
  140. package/src/superlocalmemory/learning/outcomes.py +255 -0
  141. package/src/superlocalmemory/learning/project_context.py +366 -0
  142. package/src/superlocalmemory/learning/ranker.py +155 -0
  143. package/src/superlocalmemory/learning/source_quality.py +303 -0
  144. package/src/superlocalmemory/learning/workflows.py +309 -0
  145. package/src/superlocalmemory/llm/__init__.py +0 -0
  146. package/src/superlocalmemory/llm/backbone.py +316 -0
  147. package/src/superlocalmemory/math/__init__.py +0 -0
  148. package/src/superlocalmemory/math/fisher.py +356 -0
  149. package/src/superlocalmemory/math/langevin.py +398 -0
  150. package/src/superlocalmemory/math/sheaf.py +257 -0
  151. package/src/superlocalmemory/mcp/__init__.py +0 -0
  152. package/src/superlocalmemory/mcp/resources.py +245 -0
  153. package/src/superlocalmemory/mcp/server.py +61 -0
  154. package/src/superlocalmemory/mcp/tools.py +18 -0
  155. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  156. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  157. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  158. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  159. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  160. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  161. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  162. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  163. package/src/superlocalmemory/retrieval/engine.py +390 -0
  164. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  165. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  166. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  167. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  168. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  169. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  170. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  171. package/src/superlocalmemory/server/__init__.py +1 -0
  172. package/src/superlocalmemory/server/api.py +248 -0
  173. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  174. package/src/superlocalmemory/server/routes/agents.py +107 -0
  175. package/src/superlocalmemory/server/routes/backup.py +91 -0
  176. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  177. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  178. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  179. package/src/superlocalmemory/server/routes/events.py +183 -0
  180. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  181. package/src/superlocalmemory/server/routes/learning.py +273 -0
  182. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  183. package/src/superlocalmemory/server/routes/memories.py +399 -0
  184. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  185. package/src/superlocalmemory/server/routes/stats.py +346 -0
  186. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  187. package/src/superlocalmemory/server/routes/ws.py +82 -0
  188. package/src/superlocalmemory/server/security_middleware.py +57 -0
  189. package/src/superlocalmemory/server/ui.py +245 -0
  190. package/src/superlocalmemory/storage/__init__.py +0 -0
  191. package/src/superlocalmemory/storage/access_control.py +182 -0
  192. package/src/superlocalmemory/storage/database.py +594 -0
  193. package/src/superlocalmemory/storage/migrations.py +303 -0
  194. package/src/superlocalmemory/storage/models.py +406 -0
  195. package/src/superlocalmemory/storage/schema.py +726 -0
  196. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  197. package/src/superlocalmemory/trust/__init__.py +0 -0
  198. package/src/superlocalmemory/trust/gate.py +130 -0
  199. package/src/superlocalmemory/trust/provenance.py +124 -0
  200. package/src/superlocalmemory/trust/scorer.py +347 -0
  201. package/src/superlocalmemory/trust/signals.py +153 -0
  202. package/ui/index.html +278 -5
  203. package/ui/js/auto-settings.js +70 -0
  204. package/ui/js/dashboard.js +90 -0
  205. package/ui/js/fact-detail.js +92 -0
  206. package/ui/js/feedback.js +2 -2
  207. package/ui/js/ide-status.js +102 -0
  208. package/ui/js/math-health.js +98 -0
  209. package/ui/js/recall-lab.js +127 -0
  210. package/ui/js/settings.js +2 -2
  211. package/ui/js/trust-dashboard.js +73 -0
  212. package/api_server.py +0 -724
  213. package/bin/aider-smart +0 -72
  214. package/bin/superlocalmemoryv2-learning +0 -4
  215. package/bin/superlocalmemoryv2-list +0 -3
  216. package/bin/superlocalmemoryv2-patterns +0 -4
  217. package/bin/superlocalmemoryv2-profile +0 -3
  218. package/bin/superlocalmemoryv2-recall +0 -3
  219. package/bin/superlocalmemoryv2-remember +0 -3
  220. package/bin/superlocalmemoryv2-reset +0 -3
  221. package/bin/superlocalmemoryv2-status +0 -3
  222. package/configs/chatgpt-desktop-mcp.json +0 -16
  223. package/configs/cursor-mcp.json +0 -15
  224. package/hooks/memory-list-skill.js +0 -139
  225. package/hooks/memory-profile-skill.js +0 -273
  226. package/hooks/memory-recall-skill.js +0 -114
  227. package/hooks/memory-remember-skill.js +0 -127
  228. package/hooks/memory-reset-skill.js +0 -274
  229. package/mcp_server.py +0 -1808
  230. package/requirements-core.txt +0 -22
  231. package/requirements-learning.txt +0 -12
  232. package/requirements.txt +0 -12
  233. package/src/agent_registry.py +0 -411
  234. package/src/auth_middleware.py +0 -61
  235. package/src/auto_backup.py +0 -459
  236. package/src/behavioral/__init__.py +0 -49
  237. package/src/behavioral/behavioral_listener.py +0 -203
  238. package/src/behavioral/behavioral_patterns.py +0 -275
  239. package/src/behavioral/cross_project_transfer.py +0 -206
  240. package/src/behavioral/outcome_inference.py +0 -194
  241. package/src/behavioral/outcome_tracker.py +0 -193
  242. package/src/behavioral/tests/__init__.py +0 -4
  243. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  244. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  245. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  246. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  247. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  248. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  249. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  250. package/src/cache_manager.py +0 -518
  251. package/src/compliance/__init__.py +0 -48
  252. package/src/compliance/abac_engine.py +0 -149
  253. package/src/compliance/abac_middleware.py +0 -116
  254. package/src/compliance/audit_db.py +0 -215
  255. package/src/compliance/audit_logger.py +0 -148
  256. package/src/compliance/retention_manager.py +0 -289
  257. package/src/compliance/retention_scheduler.py +0 -186
  258. package/src/compliance/tests/__init__.py +0 -4
  259. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  260. package/src/compliance/tests/test_abac_engine.py +0 -124
  261. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  262. package/src/compliance/tests/test_audit_db.py +0 -123
  263. package/src/compliance/tests/test_audit_logger.py +0 -98
  264. package/src/compliance/tests/test_mcp_audit.py +0 -128
  265. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  266. package/src/compliance/tests/test_retention_manager.py +0 -131
  267. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  268. package/src/compression/__init__.py +0 -25
  269. package/src/compression/cli.py +0 -150
  270. package/src/compression/cold_storage.py +0 -217
  271. package/src/compression/config.py +0 -72
  272. package/src/compression/orchestrator.py +0 -133
  273. package/src/compression/tier2_compressor.py +0 -228
  274. package/src/compression/tier3_compressor.py +0 -153
  275. package/src/compression/tier_classifier.py +0 -148
  276. package/src/db_connection_manager.py +0 -536
  277. package/src/embedding_engine.py +0 -63
  278. package/src/embeddings/__init__.py +0 -47
  279. package/src/embeddings/cache.py +0 -70
  280. package/src/embeddings/cli.py +0 -113
  281. package/src/embeddings/constants.py +0 -47
  282. package/src/embeddings/database.py +0 -91
  283. package/src/embeddings/engine.py +0 -247
  284. package/src/embeddings/model_loader.py +0 -145
  285. package/src/event_bus.py +0 -562
  286. package/src/graph/__init__.py +0 -36
  287. package/src/graph/build_helpers.py +0 -74
  288. package/src/graph/cli.py +0 -87
  289. package/src/graph/cluster_builder.py +0 -188
  290. package/src/graph/cluster_summary.py +0 -148
  291. package/src/graph/constants.py +0 -47
  292. package/src/graph/edge_builder.py +0 -162
  293. package/src/graph/entity_extractor.py +0 -95
  294. package/src/graph/graph_core.py +0 -226
  295. package/src/graph/graph_search.py +0 -231
  296. package/src/graph/hierarchical.py +0 -207
  297. package/src/graph/schema.py +0 -99
  298. package/src/graph_engine.py +0 -52
  299. package/src/hnsw_index.py +0 -628
  300. package/src/hybrid_search.py +0 -46
  301. package/src/learning/__init__.py +0 -217
  302. package/src/learning/adaptive_ranker.py +0 -682
  303. package/src/learning/bootstrap/__init__.py +0 -69
  304. package/src/learning/bootstrap/constants.py +0 -93
  305. package/src/learning/bootstrap/db_queries.py +0 -316
  306. package/src/learning/bootstrap/sampling.py +0 -82
  307. package/src/learning/bootstrap/text_utils.py +0 -71
  308. package/src/learning/cross_project_aggregator.py +0 -857
  309. package/src/learning/db/__init__.py +0 -40
  310. package/src/learning/db/constants.py +0 -44
  311. package/src/learning/db/schema.py +0 -279
  312. package/src/learning/engagement_tracker.py +0 -628
  313. package/src/learning/feature_extractor.py +0 -708
  314. package/src/learning/feedback_collector.py +0 -806
  315. package/src/learning/learning_db.py +0 -915
  316. package/src/learning/project_context_manager.py +0 -572
  317. package/src/learning/ranking/__init__.py +0 -33
  318. package/src/learning/ranking/constants.py +0 -84
  319. package/src/learning/ranking/helpers.py +0 -278
  320. package/src/learning/source_quality_scorer.py +0 -676
  321. package/src/learning/synthetic_bootstrap.py +0 -755
  322. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  323. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  324. package/src/learning/tests/test_aggregator.py +0 -306
  325. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  326. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  327. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  328. package/src/learning/tests/test_feedback_collector.py +0 -294
  329. package/src/learning/tests/test_learning_db.py +0 -602
  330. package/src/learning/tests/test_learning_db_v28.py +0 -110
  331. package/src/learning/tests/test_learning_init_v28.py +0 -48
  332. package/src/learning/tests/test_outcome_signals.py +0 -48
  333. package/src/learning/tests/test_project_context.py +0 -292
  334. package/src/learning/tests/test_schema_migration.py +0 -319
  335. package/src/learning/tests/test_signal_inference.py +0 -397
  336. package/src/learning/tests/test_source_quality.py +0 -351
  337. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  338. package/src/learning/tests/test_workflow_miner.py +0 -318
  339. package/src/learning/workflow_pattern_miner.py +0 -655
  340. package/src/lifecycle/__init__.py +0 -54
  341. package/src/lifecycle/bounded_growth.py +0 -239
  342. package/src/lifecycle/compaction_engine.py +0 -226
  343. package/src/lifecycle/lifecycle_engine.py +0 -355
  344. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  345. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  346. package/src/lifecycle/retention_policy.py +0 -285
  347. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  348. package/src/lifecycle/tests/test_compaction.py +0 -179
  349. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  350. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  351. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  352. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  353. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  354. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  355. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  356. package/src/mcp_tools_v28.py +0 -281
  357. package/src/memory/__init__.py +0 -36
  358. package/src/memory/cli.py +0 -205
  359. package/src/memory/constants.py +0 -39
  360. package/src/memory/helpers.py +0 -28
  361. package/src/memory/schema.py +0 -166
  362. package/src/memory-profiles.py +0 -595
  363. package/src/memory-reset.py +0 -491
  364. package/src/memory_compression.py +0 -989
  365. package/src/memory_store_v2.py +0 -1155
  366. package/src/migrate_v1_to_v2.py +0 -629
  367. package/src/pattern_learner.py +0 -34
  368. package/src/patterns/__init__.py +0 -24
  369. package/src/patterns/analyzers.py +0 -251
  370. package/src/patterns/learner.py +0 -271
  371. package/src/patterns/scoring.py +0 -171
  372. package/src/patterns/store.py +0 -225
  373. package/src/patterns/terminology.py +0 -140
  374. package/src/provenance_tracker.py +0 -312
  375. package/src/qualixar_attribution.py +0 -139
  376. package/src/qualixar_watermark.py +0 -78
  377. package/src/query_optimizer.py +0 -511
  378. package/src/rate_limiter.py +0 -83
  379. package/src/search/__init__.py +0 -20
  380. package/src/search/cli.py +0 -77
  381. package/src/search/constants.py +0 -26
  382. package/src/search/engine.py +0 -241
  383. package/src/search/fusion.py +0 -122
  384. package/src/search/index_loader.py +0 -114
  385. package/src/search/methods.py +0 -162
  386. package/src/search_engine_v2.py +0 -401
  387. package/src/setup_validator.py +0 -482
  388. package/src/subscription_manager.py +0 -391
  389. package/src/tree/__init__.py +0 -59
  390. package/src/tree/builder.py +0 -185
  391. package/src/tree/nodes.py +0 -202
  392. package/src/tree/queries.py +0 -257
  393. package/src/tree/schema.py +0 -80
  394. package/src/tree_manager.py +0 -19
  395. package/src/trust/__init__.py +0 -45
  396. package/src/trust/constants.py +0 -66
  397. package/src/trust/queries.py +0 -157
  398. package/src/trust/schema.py +0 -95
  399. package/src/trust/scorer.py +0 -299
  400. package/src/trust/signals.py +0 -95
  401. package/src/trust_scorer.py +0 -44
  402. package/ui/app.js +0 -1588
  403. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  404. package/ui/js/graph-cytoscape.js +0 -1168
  405. package/ui/js/graph-d3-backup.js +0 -32
  406. package/ui/js/graph.js +0 -32
  407. package/ui_server.py +0 -286
  408. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  409. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  410. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  411. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  412. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  413. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  414. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  415. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  416. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  417. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  418. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  419. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  420. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  421. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  422. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  423. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  424. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  425. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  426. /package/{completions → ide/completions}/slm.bash +0 -0
  427. /package/{completions → ide/completions}/slm.zsh +0 -0
  428. /package/{configs → ide/configs}/cody-commands.json +0 -0
  429. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
  430. /package/{install.ps1 → scripts/install.ps1} +0 -0
  431. /package/{install.sh → scripts/install.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()