superlocalmemory 2.8.6 → 3.0.1

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 +62 -48
  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
@@ -0,0 +1,235 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3 — Typed Memory Router.
6
+
7
+ Routes extracted facts to appropriate typed stores (ENGRAM pattern).
8
+ Typed separation gave +31 points on LoCoMo benchmark.
9
+
10
+ Mode A: all-MiniLM similarity against type templates.
11
+ Mode B/C: LLM classifies fact type.
12
+
13
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ import re
20
+ from typing import TYPE_CHECKING
21
+
22
+ from superlocalmemory.storage.models import AtomicFact, FactType, Mode
23
+
24
+ if TYPE_CHECKING:
25
+ from superlocalmemory.core.embeddings import EmbeddingService
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Type templates for Mode A (all-MiniLM classification)
31
+ # ---------------------------------------------------------------------------
32
+
33
+ _EPISODIC_TEMPLATES = [
34
+ "someone did something at a place",
35
+ "a person went somewhere or performed an action",
36
+ "an event happened on a specific date",
37
+ "someone traveled to a location",
38
+ "a meeting or gathering occurred",
39
+ ]
40
+
41
+ _SEMANTIC_TEMPLATES = [
42
+ "a person is a specific profession or role",
43
+ "something is a fact about the world",
44
+ "a place is located somewhere",
45
+ "an organization does something",
46
+ "a general knowledge statement",
47
+ ]
48
+
49
+ _OPINION_TEMPLATES = [
50
+ "someone thinks or believes something",
51
+ "a person likes or dislikes something",
52
+ "someone prefers one thing over another",
53
+ "a subjective judgment or evaluation",
54
+ "an emotional reaction to something",
55
+ ]
56
+
57
+ _TEMPORAL_TEMPLATES = [
58
+ "something will happen on a future date",
59
+ "an event is scheduled or planned",
60
+ "a deadline or due date for something",
61
+ "something started or will end at a time",
62
+ "a recurring event or appointment",
63
+ ]
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Keyword patterns for Mode A fallback
67
+ # ---------------------------------------------------------------------------
68
+
69
+ _OPINION_MARKERS = re.compile(
70
+ r"\b(think|believe|feel|prefer|like|love|hate|dislike|enjoy|"
71
+ r"opinion|seems?|probably|maybe|might|could be|i guess|"
72
+ r"personally|in my view|i'd say)\b",
73
+ re.IGNORECASE,
74
+ )
75
+
76
+ _TEMPORAL_MARKERS = re.compile(
77
+ r"\b(scheduled|deadline|appointment|planned|tomorrow|"
78
+ r"next week|next month|upcoming|due date|starts?|ends?|"
79
+ r"will happen|going to|plan to)\b",
80
+ re.IGNORECASE,
81
+ )
82
+
83
+ _EPISODIC_MARKERS = re.compile(
84
+ r"\b(went|visited|traveled|attended|met|saw|did|"
85
+ r"happened|occurred|took place|experienced)\b",
86
+ re.IGNORECASE,
87
+ )
88
+
89
+
90
+ class TypeRouter:
91
+ """Route facts to typed stores based on content classification.
92
+
93
+ Uses embedding similarity (Mode A) or LLM (Mode B/C) to classify
94
+ each fact as episodic, semantic, opinion, or temporal.
95
+ """
96
+
97
+ def __init__(
98
+ self,
99
+ mode: Mode = Mode.A,
100
+ embedder: EmbeddingService | None = None,
101
+ llm: object | None = None,
102
+ ) -> None:
103
+ self._mode = mode
104
+ self._embedder = embedder
105
+ self._llm = llm
106
+ self._template_embeddings: dict[FactType, list[list[float]]] | None = None
107
+
108
+ def classify(self, fact: AtomicFact) -> FactType:
109
+ """Classify a fact into a typed store category."""
110
+ if self._mode in (Mode.B, Mode.C) and self._llm is not None:
111
+ return self._classify_llm(fact)
112
+ if self._embedder is not None:
113
+ return self._classify_embedding(fact)
114
+ return self._classify_keywords(fact)
115
+
116
+ def route_facts(self, facts: list[AtomicFact]) -> list[AtomicFact]:
117
+ """Classify and update fact_type for a batch of facts."""
118
+ result = []
119
+ for fact in facts:
120
+ classified_type = self.classify(fact)
121
+ # Create new fact with updated type (immutability pattern)
122
+ updated = AtomicFact(
123
+ fact_id=fact.fact_id,
124
+ memory_id=fact.memory_id,
125
+ profile_id=fact.profile_id,
126
+ content=fact.content,
127
+ fact_type=classified_type,
128
+ entities=fact.entities,
129
+ canonical_entities=fact.canonical_entities,
130
+ observation_date=fact.observation_date,
131
+ referenced_date=fact.referenced_date,
132
+ interval_start=fact.interval_start,
133
+ interval_end=fact.interval_end,
134
+ confidence=fact.confidence,
135
+ importance=fact.importance,
136
+ evidence_count=fact.evidence_count,
137
+ source_turn_ids=fact.source_turn_ids,
138
+ session_id=fact.session_id,
139
+ embedding=fact.embedding,
140
+ fisher_mean=fact.fisher_mean,
141
+ fisher_variance=fact.fisher_variance,
142
+ emotional_valence=fact.emotional_valence,
143
+ emotional_arousal=fact.emotional_arousal,
144
+ signal_type=fact.signal_type,
145
+ created_at=fact.created_at,
146
+ )
147
+ result.append(updated)
148
+ return result
149
+
150
+ # -- Classification strategies -----------------------------------------
151
+
152
+ def _classify_keywords(self, fact: AtomicFact) -> FactType:
153
+ """Keyword-based classification (fastest, lowest quality)."""
154
+ text = fact.content
155
+
156
+ if _OPINION_MARKERS.search(text):
157
+ return FactType.OPINION
158
+ if _TEMPORAL_MARKERS.search(text):
159
+ return FactType.TEMPORAL
160
+ if _EPISODIC_MARKERS.search(text):
161
+ return FactType.EPISODIC
162
+ return FactType.SEMANTIC
163
+
164
+ def _classify_embedding(self, fact: AtomicFact) -> FactType:
165
+ """Embedding similarity against type templates (Mode A)."""
166
+ if self._embedder is None:
167
+ return self._classify_keywords(fact)
168
+
169
+ if self._template_embeddings is None:
170
+ self._build_template_embeddings()
171
+
172
+ assert self._template_embeddings is not None
173
+ fact_emb = self._embedder.embed(fact.content)
174
+
175
+ best_type = FactType.SEMANTIC
176
+ best_score = -1.0
177
+
178
+ for ftype, template_embs in self._template_embeddings.items():
179
+ avg_sim = sum(
180
+ _cosine(fact_emb, t) for t in template_embs
181
+ ) / max(len(template_embs), 1)
182
+ if avg_sim > best_score:
183
+ best_score = avg_sim
184
+ best_type = ftype
185
+
186
+ return best_type
187
+
188
+ def _classify_llm(self, fact: AtomicFact) -> FactType:
189
+ """LLM-based classification (Mode B/C, highest quality)."""
190
+ if self._llm is None:
191
+ return self._classify_embedding(fact)
192
+
193
+ prompt = (
194
+ f"Classify this fact into exactly one category.\n"
195
+ f"Fact: \"{fact.content}\"\n"
196
+ f"Categories:\n"
197
+ f"- episodic: An event that happened (who did what when)\n"
198
+ f"- semantic: A general fact about the world (X is Y)\n"
199
+ f"- opinion: A subjective belief or preference\n"
200
+ f"- temporal: A scheduled/planned future event\n"
201
+ f"Reply with ONLY the category name (one word)."
202
+ )
203
+ try:
204
+ response = self._llm.generate(prompt).strip().lower()
205
+ type_map = {
206
+ "episodic": FactType.EPISODIC,
207
+ "semantic": FactType.SEMANTIC,
208
+ "opinion": FactType.OPINION,
209
+ "temporal": FactType.TEMPORAL,
210
+ }
211
+ return type_map.get(response, FactType.SEMANTIC)
212
+ except Exception:
213
+ logger.warning("LLM classification failed, falling back to keywords")
214
+ return self._classify_keywords(fact)
215
+
216
+ def _build_template_embeddings(self) -> None:
217
+ """Pre-compute embeddings for type templates."""
218
+ if self._embedder is None:
219
+ return
220
+ self._template_embeddings = {
221
+ FactType.EPISODIC: self._embedder.embed_batch(_EPISODIC_TEMPLATES),
222
+ FactType.SEMANTIC: self._embedder.embed_batch(_SEMANTIC_TEMPLATES),
223
+ FactType.OPINION: self._embedder.embed_batch(_OPINION_TEMPLATES),
224
+ FactType.TEMPORAL: self._embedder.embed_batch(_TEMPORAL_TEMPLATES),
225
+ }
226
+
227
+
228
+ def _cosine(a: list[float], b: list[float]) -> float:
229
+ """Cosine similarity between two vectors."""
230
+ dot = sum(x * y for x, y in zip(a, b))
231
+ norm_a = sum(x * x for x in a) ** 0.5
232
+ norm_b = sum(x * x for x in b) ** 0.5
233
+ if norm_a == 0 or norm_b == 0:
234
+ return 0.0
235
+ return dot / (norm_a * norm_b)
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
@@ -0,0 +1,111 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """Auto-capture — detect and store important information automatically."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import re
11
+ from dataclasses import dataclass
12
+ from typing import Any
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Patterns that indicate capture-worthy content
17
+ _DECISION_PATTERNS = [
18
+ r"(?i)\b(decided|chose|picked|selected|went with|using|switched to)\b",
19
+ r"(?i)\b(because|reason|rationale|due to|since)\b.*\b(chose|use|prefer)\b",
20
+ ]
21
+
22
+ _BUG_PATTERNS = [
23
+ r"(?i)\b(fixed|resolved|solved|root cause|bug|issue|error)\b",
24
+ r"(?i)\b(the (?:fix|solution|problem) (?:was|is))\b",
25
+ ]
26
+
27
+ _PREFERENCE_PATTERNS = [
28
+ r"(?i)\b(prefer|always use|never use|I like|I hate|don't like)\b",
29
+ r"(?i)\b(convention|standard|style|pattern)\b.*\b(is|should be|must be)\b",
30
+ ]
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class CaptureDecision:
35
+ """Result of evaluating content for auto-capture."""
36
+
37
+ capture: bool
38
+ confidence: float
39
+ category: str # "decision", "bug", "preference", "session_summary", "none"
40
+ reason: str
41
+
42
+
43
+ class AutoCapture:
44
+ """Detect and classify content for automatic storage."""
45
+
46
+ def __init__(self, engine=None, config: dict | None = None):
47
+ self._engine = engine
48
+ self._config = config or {}
49
+ self._enabled = self._config.get("enabled", True)
50
+ self._min_confidence = self._config.get("min_confidence", 0.5)
51
+ self._capture_decisions = self._config.get("capture_decisions", True)
52
+ self._capture_bugs = self._config.get("capture_bugs", True)
53
+ self._capture_preferences = self._config.get("capture_preferences", True)
54
+
55
+ def evaluate(self, content: str) -> CaptureDecision:
56
+ """Evaluate whether content should be auto-captured.
57
+
58
+ Returns a CaptureDecision with capture=True/False,
59
+ confidence score, and category.
60
+ """
61
+ if not self._enabled:
62
+ return CaptureDecision(False, 0.0, "none", "auto-capture disabled")
63
+
64
+ if len(content.strip()) < 20:
65
+ return CaptureDecision(False, 0.0, "none", "content too short")
66
+
67
+ # Check for decisions
68
+ if self._capture_decisions:
69
+ score = self._match_patterns(content, _DECISION_PATTERNS)
70
+ if score >= self._min_confidence:
71
+ return CaptureDecision(True, score, "decision", "decision pattern detected")
72
+
73
+ # Check for bug fixes
74
+ if self._capture_bugs:
75
+ score = self._match_patterns(content, _BUG_PATTERNS)
76
+ if score >= self._min_confidence:
77
+ return CaptureDecision(True, score, "bug", "bug fix pattern detected")
78
+
79
+ # Check for preferences
80
+ if self._capture_preferences:
81
+ score = self._match_patterns(content, _PREFERENCE_PATTERNS)
82
+ if score >= self._min_confidence:
83
+ return CaptureDecision(True, score, "preference", "preference pattern detected")
84
+
85
+ return CaptureDecision(False, 0.0, "none", "no patterns matched")
86
+
87
+ def capture(self, content: str, category: str = "", metadata: dict | None = None) -> bool:
88
+ """Store content via engine if auto-capture decides to."""
89
+ if not self._engine:
90
+ return False
91
+
92
+ try:
93
+ meta = metadata or {}
94
+ meta["source"] = "auto-capture"
95
+ meta["category"] = category
96
+ fact_ids = self._engine.store(content, metadata=meta)
97
+ return len(fact_ids) > 0
98
+ except Exception as exc:
99
+ logger.debug("Auto-capture store failed: %s", exc)
100
+ return False
101
+
102
+ def _match_patterns(self, content: str, patterns: list[str]) -> float:
103
+ """Match content against regex patterns. Returns confidence 0.0-1.0."""
104
+ matches = sum(1 for p in patterns if re.search(p, content))
105
+ if matches == 0:
106
+ return 0.0
107
+ return min(1.0, 0.5 + (matches * 0.25))
108
+
109
+ @property
110
+ def enabled(self) -> bool:
111
+ return self._enabled
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """Auto-recall — inject relevant memories into AI context automatically."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from typing import Any
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class AutoRecall:
16
+ """Automatically recalls relevant context for AI sessions.
17
+
18
+ Called at session start or before each prompt to inject
19
+ relevant memories without user intervention.
20
+ """
21
+
22
+ def __init__(self, engine=None, config: dict | None = None):
23
+ self._engine = engine
24
+ self._config = config or {}
25
+ self._enabled = self._config.get("enabled", True)
26
+ self._max_memories = self._config.get("max_memories_injected", 10)
27
+ self._threshold = self._config.get("relevance_threshold", 0.3)
28
+
29
+ def get_session_context(self, project_path: str = "", query: str = "") -> str:
30
+ """Get relevant context for a session or query.
31
+
32
+ Returns a formatted string of relevant memories suitable
33
+ for injection into an AI's system prompt.
34
+ """
35
+ if not self._enabled or not self._engine:
36
+ return ""
37
+
38
+ try:
39
+ # Build query from project path or explicit query
40
+ search_query = query or f"project context {project_path}"
41
+ response = self._engine.recall(search_query, limit=self._max_memories)
42
+
43
+ if not response.results:
44
+ return ""
45
+
46
+ # Filter by relevance threshold
47
+ relevant = [r for r in response.results if r.score >= self._threshold]
48
+
49
+ if not relevant:
50
+ return ""
51
+
52
+ # Format for injection
53
+ lines = ["# Relevant Memory Context", ""]
54
+ for r in relevant[:self._max_memories]:
55
+ lines.append(f"- {r.fact.content[:200]}")
56
+
57
+ return "\n".join(lines)
58
+ except Exception as exc:
59
+ logger.debug("Auto-recall failed: %s", exc)
60
+ return ""
61
+
62
+ def get_query_context(self, query: str) -> list[dict]:
63
+ """Get relevant memories for a specific query.
64
+
65
+ Returns structured data (not formatted string) for MCP tools.
66
+ """
67
+ if not self._enabled or not self._engine:
68
+ return []
69
+
70
+ try:
71
+ response = self._engine.recall(query, limit=self._max_memories)
72
+ results = []
73
+ for r in response.results:
74
+ if r.score >= self._threshold:
75
+ results.append({
76
+ "fact_id": r.fact.fact_id,
77
+ "content": r.fact.content[:300],
78
+ "score": round(r.score, 3),
79
+ })
80
+ return results
81
+ except Exception as exc:
82
+ logger.debug("Auto-recall query failed: %s", exc)
83
+ return []
84
+
85
+ @property
86
+ def enabled(self) -> bool:
87
+ return self._enabled
88
+
89
+ def enable(self) -> None:
90
+ self._enabled = True
91
+
92
+ def disable(self) -> None:
93
+ self._enabled = False
@@ -0,0 +1,204 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """IDE connector — detect installed IDEs and generate SLM integration configs.
6
+
7
+ Supports: Claude Code, Cursor, VS Code/Copilot, Windsurf, Gemini CLI,
8
+ JetBrains IDEs, Continue.dev, Zed, Aider.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # SLM marker to detect our config sections (for idempotency)
21
+ SLM_MARKER = "## SuperLocalMemory"
22
+ SLM_MARKER_START = "<!-- SLM-START -->"
23
+ SLM_MARKER_END = "<!-- SLM-END -->"
24
+
25
+ # Known IDE config paths (relative to home directory)
26
+ IDE_CONFIGS: dict[str, dict[str, Any]] = {
27
+ "claude_code": {
28
+ "name": "Claude Code",
29
+ "detect_paths": [".claude"],
30
+ "config_files": [".claude/CLAUDE.md"],
31
+ "type": "markdown_append",
32
+ },
33
+ "cursor": {
34
+ "name": "Cursor",
35
+ "detect_paths": [".cursor"],
36
+ "config_files": [".cursorrules"],
37
+ "type": "markdown_append",
38
+ },
39
+ "vscode": {
40
+ "name": "VS Code / Copilot",
41
+ "detect_paths": [".vscode", "Library/Application Support/Code"],
42
+ "config_files": [".github/copilot-instructions.md"],
43
+ "type": "markdown_append",
44
+ },
45
+ "windsurf": {
46
+ "name": "Windsurf",
47
+ "detect_paths": [".codeium/windsurf"],
48
+ "config_files": [".windsurfrules"],
49
+ "type": "markdown_append",
50
+ },
51
+ "gemini": {
52
+ "name": "Gemini CLI",
53
+ "detect_paths": [".gemini"],
54
+ "config_files": ["GEMINI.md"],
55
+ "type": "markdown_append",
56
+ },
57
+ "continue_dev": {
58
+ "name": "Continue.dev",
59
+ "detect_paths": [".continue"],
60
+ "config_files": [".continue/config.json"],
61
+ "type": "json_merge",
62
+ },
63
+ "zed": {
64
+ "name": "Zed Editor",
65
+ "detect_paths": [".config/zed"],
66
+ "config_files": [".config/zed/settings.json"],
67
+ "type": "json_merge",
68
+ },
69
+ }
70
+
71
+ # Template for markdown-based IDE rules
72
+ MARKDOWN_TEMPLATE = """
73
+ {marker_start}
74
+ {marker}
75
+ - Before responding to questions, check SuperLocalMemory for relevant context using the `recall` tool
76
+ - After making decisions, fixing bugs, or learning preferences, store them using the `remember` tool
77
+ - On session start, load project context from SuperLocalMemory
78
+ - Available tools: remember, recall, search, fetch, get_status, set_mode, health
79
+ - Documentation: https://superlocalmemory.com
80
+ {marker_end}
81
+ """
82
+
83
+
84
+ class IDEConnector:
85
+ """Detect installed IDEs and generate SLM integration configs."""
86
+
87
+ def __init__(self, home: Path | None = None) -> None:
88
+ self._home = home or Path.home()
89
+
90
+ def detect_ides(self) -> dict[str, bool]:
91
+ """Detect which IDEs are installed.
92
+
93
+ Returns a dict mapping IDE id to whether it was detected.
94
+ """
95
+ result: dict[str, bool] = {}
96
+ for ide_id, config in IDE_CONFIGS.items():
97
+ detected = any(
98
+ (self._home / p).exists() for p in config["detect_paths"]
99
+ )
100
+ result[ide_id] = detected
101
+ return result
102
+
103
+ def connect(self, ide_id: str) -> bool:
104
+ """Configure a specific IDE for SLM integration.
105
+
106
+ Returns True if configuration was written successfully.
107
+ Idempotent — safe to run multiple times.
108
+ Does NOT overwrite existing user rules.
109
+ """
110
+ if ide_id not in IDE_CONFIGS:
111
+ logger.warning("Unknown IDE: %s", ide_id)
112
+ return False
113
+
114
+ config = IDE_CONFIGS[ide_id]
115
+ config_type = config["type"]
116
+
117
+ for config_file in config["config_files"]:
118
+ path = self._home / config_file
119
+
120
+ if config_type == "markdown_append":
121
+ return self._append_markdown(path)
122
+ elif config_type == "json_merge":
123
+ return self._merge_json(path)
124
+
125
+ return False
126
+
127
+ def connect_all(self) -> dict[str, str]:
128
+ """Detect and configure all installed IDEs.
129
+
130
+ Returns dict of {ide_id: status} where status is
131
+ "connected", "not_installed", or "error".
132
+ """
133
+ detected = self.detect_ides()
134
+ results: dict[str, str] = {}
135
+
136
+ for ide_id, is_installed in detected.items():
137
+ if not is_installed:
138
+ results[ide_id] = "not_installed"
139
+ continue
140
+ try:
141
+ success = self.connect(ide_id)
142
+ results[ide_id] = "connected" if success else "error"
143
+ except Exception as exc:
144
+ logger.debug("Failed to connect %s: %s", ide_id, exc)
145
+ results[ide_id] = "error"
146
+
147
+ return results
148
+
149
+ def get_status(self) -> list[dict[str, Any]]:
150
+ """Get connection status for all known IDEs."""
151
+ detected = self.detect_ides()
152
+ status: list[dict[str, Any]] = []
153
+ for ide_id, config in IDE_CONFIGS.items():
154
+ status.append({
155
+ "id": ide_id,
156
+ "name": config["name"],
157
+ "installed": detected.get(ide_id, False),
158
+ "config_path": str(self._home / config["config_files"][0]),
159
+ })
160
+ return status
161
+
162
+ def _append_markdown(self, path: Path) -> bool:
163
+ """Append SLM section to a markdown file. Idempotent."""
164
+ content = ""
165
+ if path.exists():
166
+ content = path.read_text()
167
+
168
+ # Check if already configured (idempotent)
169
+ if SLM_MARKER in content:
170
+ return True
171
+
172
+ # Append SLM section
173
+ section = MARKDOWN_TEMPLATE.format(
174
+ marker=SLM_MARKER,
175
+ marker_start=SLM_MARKER_START,
176
+ marker_end=SLM_MARKER_END,
177
+ )
178
+
179
+ path.parent.mkdir(parents=True, exist_ok=True)
180
+ path.write_text(content + "\n" + section)
181
+ return True
182
+
183
+ def _merge_json(self, path: Path) -> bool:
184
+ """Merge SLM config into a JSON config file."""
185
+ data: dict[str, Any] = {}
186
+ if path.exists():
187
+ try:
188
+ data = json.loads(path.read_text())
189
+ except json.JSONDecodeError:
190
+ data = {}
191
+
192
+ # Add MCP server config
193
+ if "mcpServers" not in data:
194
+ data["mcpServers"] = {}
195
+
196
+ data["mcpServers"]["superlocalmemory"] = {
197
+ "command": "python3",
198
+ "args": ["-m", "superlocalmemory.mcp.server"],
199
+ "enabled": True,
200
+ }
201
+
202
+ path.parent.mkdir(parents=True, exist_ok=True)
203
+ path.write_text(json.dumps(data, indent=2))
204
+ return True