superlocalmemory 4.1.13 → 4.1.15

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 (740) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +103 -0
  3. package/README.md +3 -3
  4. package/package.json +1 -8
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/CLAUDE.md +3 -3
  7. package/plugin/agents/slm-governance-advisor.md +1 -1
  8. package/plugin/agents/slm-loop-runner.md +1 -1
  9. package/plugin/agents/slm-memory-advisor.md +1 -1
  10. package/plugin/agents/slm-optimize-advisor.md +1 -1
  11. package/plugin/requirements.txt +1 -1
  12. package/plugin/skills/slm-cache/SKILL.md +1 -1
  13. package/plugin/skills/slm-compress/SKILL.md +1 -1
  14. package/plugin/skills/slm-governance/SKILL.md +1 -1
  15. package/plugin/skills/slm-graph/SKILL.md +1 -1
  16. package/plugin/skills/slm-loop/SKILL.md +1 -1
  17. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  18. package/plugin/skills/slm-profile/SKILL.md +1 -1
  19. package/plugin/skills/slm-recall/SKILL.md +1 -1
  20. package/plugin/skills/slm-remember/SKILL.md +1 -1
  21. package/plugin/skills/slm-scope/SKILL.md +1 -1
  22. package/plugin/skills/slm-session/SKILL.md +1 -1
  23. package/plugin/skills/slm-status/SKILL.md +1 -1
  24. package/plugin-src/agents/slm-memory-advisor.md +1 -1
  25. package/plugin-src/agents/slm-optimize-advisor.md +1 -1
  26. package/plugin-src/rules/AGENTS.md +1 -1
  27. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-governance/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  31. package/plugin-src/skills/slm-loop/SKILL.md +1 -1
  32. package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
  33. package/plugin-src/skills/slm-profile/SKILL.md +1 -1
  34. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  36. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  37. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  38. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  39. package/scripts/postinstall.js +71 -2
  40. package/pyproject.toml +0 -250
  41. package/src/superlocalmemory/__init__.py +0 -82
  42. package/src/superlocalmemory/access/__init__.py +0 -3
  43. package/src/superlocalmemory/access/rbac.py +0 -575
  44. package/src/superlocalmemory/attribution/__init__.py +0 -9
  45. package/src/superlocalmemory/attribution/signer.py +0 -173
  46. package/src/superlocalmemory/attribution/watermark.py +0 -189
  47. package/src/superlocalmemory/brain/__init__.py +0 -5
  48. package/src/superlocalmemory/brain/truth.py +0 -418
  49. package/src/superlocalmemory/cli/__init__.py +0 -5
  50. package/src/superlocalmemory/cli/__main__.py +0 -17
  51. package/src/superlocalmemory/cli/_lazy_init.py +0 -115
  52. package/src/superlocalmemory/cli/cache_cmd.py +0 -198
  53. package/src/superlocalmemory/cli/commands.py +0 -4710
  54. package/src/superlocalmemory/cli/compress_cmd.py +0 -151
  55. package/src/superlocalmemory/cli/context_commands.py +0 -193
  56. package/src/superlocalmemory/cli/daemon.py +0 -909
  57. package/src/superlocalmemory/cli/db_migrate.py +0 -150
  58. package/src/superlocalmemory/cli/diagnostics_cmd.py +0 -101
  59. package/src/superlocalmemory/cli/escape_hatch.py +0 -220
  60. package/src/superlocalmemory/cli/evidence_cmd.py +0 -103
  61. package/src/superlocalmemory/cli/gdpr_cmd.py +0 -792
  62. package/src/superlocalmemory/cli/gdpr_io.py +0 -109
  63. package/src/superlocalmemory/cli/help_cmd.py +0 -197
  64. package/src/superlocalmemory/cli/host_upgrades.py +0 -189
  65. package/src/superlocalmemory/cli/ingest_cmd.py +0 -327
  66. package/src/superlocalmemory/cli/json_output.py +0 -81
  67. package/src/superlocalmemory/cli/loop_cmd.py +0 -187
  68. package/src/superlocalmemory/cli/main.py +0 -1145
  69. package/src/superlocalmemory/cli/mesh_cmd.py +0 -38
  70. package/src/superlocalmemory/cli/migrate_cmd.py +0 -55
  71. package/src/superlocalmemory/cli/ops_cmd.py +0 -281
  72. package/src/superlocalmemory/cli/optimize_cmd.py +0 -179
  73. package/src/superlocalmemory/cli/optimize_constants.py +0 -31
  74. package/src/superlocalmemory/cli/pending_store.py +0 -296
  75. package/src/superlocalmemory/cli/proxy_cmd.py +0 -108
  76. package/src/superlocalmemory/cli/scale_engine_cmd.py +0 -56
  77. package/src/superlocalmemory/cli/service_installer.py +0 -373
  78. package/src/superlocalmemory/cli/setup_wizard.py +0 -1162
  79. package/src/superlocalmemory/cli/summary_cmd.py +0 -215
  80. package/src/superlocalmemory/cli/version_banner.py +0 -202
  81. package/src/superlocalmemory/cli/wizard_v3426_options.py +0 -129
  82. package/src/superlocalmemory/code_graph/__init__.py +0 -46
  83. package/src/superlocalmemory/code_graph/blast_radius.py +0 -177
  84. package/src/superlocalmemory/code_graph/bridge/__init__.py +0 -36
  85. package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +0 -490
  86. package/src/superlocalmemory/code_graph/bridge/event_listeners.py +0 -206
  87. package/src/superlocalmemory/code_graph/bridge/fact_enricher.py +0 -159
  88. package/src/superlocalmemory/code_graph/bridge/hebbian_linker.py +0 -170
  89. package/src/superlocalmemory/code_graph/bridge/maintenance.py +0 -220
  90. package/src/superlocalmemory/code_graph/bridge/temporal_checker.py +0 -152
  91. package/src/superlocalmemory/code_graph/changes.py +0 -363
  92. package/src/superlocalmemory/code_graph/communities.py +0 -299
  93. package/src/superlocalmemory/code_graph/config.py +0 -154
  94. package/src/superlocalmemory/code_graph/database.py +0 -526
  95. package/src/superlocalmemory/code_graph/extractors/__init__.py +0 -95
  96. package/src/superlocalmemory/code_graph/extractors/python.py +0 -413
  97. package/src/superlocalmemory/code_graph/extractors/typescript.py +0 -556
  98. package/src/superlocalmemory/code_graph/flows.py +0 -350
  99. package/src/superlocalmemory/code_graph/git_hooks.py +0 -226
  100. package/src/superlocalmemory/code_graph/graph_engine.py +0 -295
  101. package/src/superlocalmemory/code_graph/graph_store.py +0 -335
  102. package/src/superlocalmemory/code_graph/incremental.py +0 -200
  103. package/src/superlocalmemory/code_graph/models.py +0 -130
  104. package/src/superlocalmemory/code_graph/parser.py +0 -687
  105. package/src/superlocalmemory/code_graph/resolver.py +0 -321
  106. package/src/superlocalmemory/code_graph/search.py +0 -460
  107. package/src/superlocalmemory/code_graph/service.py +0 -95
  108. package/src/superlocalmemory/code_graph/watcher.py +0 -207
  109. package/src/superlocalmemory/compliance/__init__.py +0 -0
  110. package/src/superlocalmemory/compliance/abac.py +0 -204
  111. package/src/superlocalmemory/compliance/audit.py +0 -385
  112. package/src/superlocalmemory/compliance/eu_ai_act.py +0 -101
  113. package/src/superlocalmemory/compliance/gdpr.py +0 -1479
  114. package/src/superlocalmemory/compliance/lifecycle.py +0 -158
  115. package/src/superlocalmemory/compliance/retention.py +0 -415
  116. package/src/superlocalmemory/compliance/scheduler.py +0 -217
  117. package/src/superlocalmemory/contracts/__init__.py +0 -1
  118. package/src/superlocalmemory/contracts/schemas/agent-experience-v1.schema.json +0 -92
  119. package/src/superlocalmemory/contracts/schemas/agent-integration-contract-v2.schema.json +0 -46
  120. package/src/superlocalmemory/contracts/schemas/cognitive-turn-receipt-v1.schema.json +0 -59
  121. package/src/superlocalmemory/contracts/v402.py +0 -62
  122. package/src/superlocalmemory/core/__init__.py +0 -0
  123. package/src/superlocalmemory/core/actor_context.py +0 -166
  124. package/src/superlocalmemory/core/admission.py +0 -769
  125. package/src/superlocalmemory/core/backend_orchestrator.py +0 -637
  126. package/src/superlocalmemory/core/block_hygiene.py +0 -147
  127. package/src/superlocalmemory/core/community_summary.py +0 -267
  128. package/src/superlocalmemory/core/component_healer.py +0 -144
  129. package/src/superlocalmemory/core/component_registry.py +0 -514
  130. package/src/superlocalmemory/core/config.py +0 -2204
  131. package/src/superlocalmemory/core/consolidation_engine.py +0 -983
  132. package/src/superlocalmemory/core/context_cache.py +0 -574
  133. package/src/superlocalmemory/core/derivation_lineage.py +0 -246
  134. package/src/superlocalmemory/core/embedding_worker.py +0 -208
  135. package/src/superlocalmemory/core/embeddings.py +0 -1052
  136. package/src/superlocalmemory/core/engine.py +0 -1395
  137. package/src/superlocalmemory/core/engine_capabilities.py +0 -24
  138. package/src/superlocalmemory/core/engine_ingestion.py +0 -983
  139. package/src/superlocalmemory/core/engine_lock.py +0 -75
  140. package/src/superlocalmemory/core/engine_wiring.py +0 -776
  141. package/src/superlocalmemory/core/entity_community.py +0 -178
  142. package/src/superlocalmemory/core/error_envelope.py +0 -60
  143. package/src/superlocalmemory/core/evidence_bundle.py +0 -528
  144. package/src/superlocalmemory/core/fact_consolidator.py +0 -812
  145. package/src/superlocalmemory/core/file_lock.py +0 -92
  146. package/src/superlocalmemory/core/graph_analyzer.py +0 -456
  147. package/src/superlocalmemory/core/graph_metrics.py +0 -597
  148. package/src/superlocalmemory/core/graph_pruner.py +0 -939
  149. package/src/superlocalmemory/core/health_monitor.py +0 -338
  150. package/src/superlocalmemory/core/hooks.py +0 -65
  151. package/src/superlocalmemory/core/ingest_gate.py +0 -133
  152. package/src/superlocalmemory/core/ingest_policy.py +0 -38
  153. package/src/superlocalmemory/core/ingestion_command.py +0 -1042
  154. package/src/superlocalmemory/core/injection.py +0 -434
  155. package/src/superlocalmemory/core/install_detector.py +0 -131
  156. package/src/superlocalmemory/core/key_expander.py +0 -138
  157. package/src/superlocalmemory/core/lifecycle_state.py +0 -153
  158. package/src/superlocalmemory/core/maintenance.py +0 -777
  159. package/src/superlocalmemory/core/maintenance_scheduler.py +0 -507
  160. package/src/superlocalmemory/core/materialization_control.py +0 -20
  161. package/src/superlocalmemory/core/mcp_embedder_proxy.py +0 -89
  162. package/src/superlocalmemory/core/memory_health.py +0 -266
  163. package/src/superlocalmemory/core/mode_capability.py +0 -111
  164. package/src/superlocalmemory/core/modes.py +0 -168
  165. package/src/superlocalmemory/core/mutations.py +0 -688
  166. package/src/superlocalmemory/core/ollama_embedder.py +0 -266
  167. package/src/superlocalmemory/core/ollama_validator.py +0 -315
  168. package/src/superlocalmemory/core/operation_policy.py +0 -92
  169. package/src/superlocalmemory/core/operation_policy_registry.py +0 -542
  170. package/src/superlocalmemory/core/operation_request.py +0 -127
  171. package/src/superlocalmemory/core/ops_remediation.py +0 -542
  172. package/src/superlocalmemory/core/pii.py +0 -105
  173. package/src/superlocalmemory/core/platform_utils.py +0 -138
  174. package/src/superlocalmemory/core/profiles.py +0 -234
  175. package/src/superlocalmemory/core/progressive_abstraction.py +0 -208
  176. package/src/superlocalmemory/core/projection_drain.py +0 -380
  177. package/src/superlocalmemory/core/queue_consumer.py +0 -168
  178. package/src/superlocalmemory/core/ram_lock.py +0 -160
  179. package/src/superlocalmemory/core/rate_limit.py +0 -151
  180. package/src/superlocalmemory/core/recall_gate.py +0 -95
  181. package/src/superlocalmemory/core/recall_pipeline.py +0 -1337
  182. package/src/superlocalmemory/core/recall_queue.py +0 -377
  183. package/src/superlocalmemory/core/recall_worker.py +0 -414
  184. package/src/superlocalmemory/core/registry.py +0 -121
  185. package/src/superlocalmemory/core/remember_admission.py +0 -161
  186. package/src/superlocalmemory/core/remember_runtime.py +0 -1190
  187. package/src/superlocalmemory/core/remote_mode.py +0 -214
  188. package/src/superlocalmemory/core/reranker_worker.py +0 -338
  189. package/src/superlocalmemory/core/safe_fs.py +0 -108
  190. package/src/superlocalmemory/core/scale_autopromote.py +0 -196
  191. package/src/superlocalmemory/core/scale_engine.py +0 -915
  192. package/src/superlocalmemory/core/score_contract.py +0 -82
  193. package/src/superlocalmemory/core/security_primitives.py +0 -672
  194. package/src/superlocalmemory/core/session_identity.py +0 -98
  195. package/src/superlocalmemory/core/shadow_router.py +0 -319
  196. package/src/superlocalmemory/core/slm_disabled.py +0 -85
  197. package/src/superlocalmemory/core/status_contract.py +0 -108
  198. package/src/superlocalmemory/core/store_pipeline.py +0 -1404
  199. package/src/superlocalmemory/core/summarizer.py +0 -200
  200. package/src/superlocalmemory/core/tier_manager.py +0 -461
  201. package/src/superlocalmemory/core/topic_signature.py +0 -156
  202. package/src/superlocalmemory/core/transactions/__init__.py +0 -78
  203. package/src/superlocalmemory/core/transactions/concrete_owners.py +0 -604
  204. package/src/superlocalmemory/core/transactions/erasure.py +0 -825
  205. package/src/superlocalmemory/core/transactions/manifest.py +0 -255
  206. package/src/superlocalmemory/core/transactions/manifest_key.py +0 -155
  207. package/src/superlocalmemory/core/transactions/obligations.py +0 -272
  208. package/src/superlocalmemory/core/transactions/owners.py +0 -114
  209. package/src/superlocalmemory/core/transactions/reconciler.py +0 -285
  210. package/src/superlocalmemory/core/transactions/service.py +0 -330
  211. package/src/superlocalmemory/core/worker_pool.py +0 -377
  212. package/src/superlocalmemory/core/working_memory.py +0 -288
  213. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  214. package/src/superlocalmemory/dynamics/activation_guided_quantization.py +0 -374
  215. package/src/superlocalmemory/dynamics/eap_scheduler.py +0 -294
  216. package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +0 -171
  217. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +0 -227
  218. package/src/superlocalmemory/encoding/__init__.py +0 -0
  219. package/src/superlocalmemory/encoding/auto_linker.py +0 -308
  220. package/src/superlocalmemory/encoding/cognitive_consolidator.py +0 -899
  221. package/src/superlocalmemory/encoding/consolidator.py +0 -472
  222. package/src/superlocalmemory/encoding/context_generator.py +0 -175
  223. package/src/superlocalmemory/encoding/emotional.py +0 -189
  224. package/src/superlocalmemory/encoding/entity_reflexion.py +0 -200
  225. package/src/superlocalmemory/encoding/entity_resolver.py +0 -687
  226. package/src/superlocalmemory/encoding/entropy_gate.py +0 -101
  227. package/src/superlocalmemory/encoding/fact_extractor.py +0 -877
  228. package/src/superlocalmemory/encoding/foresight.py +0 -93
  229. package/src/superlocalmemory/encoding/graph_builder.py +0 -346
  230. package/src/superlocalmemory/encoding/observation_builder.py +0 -177
  231. package/src/superlocalmemory/encoding/prospective_markers.py +0 -262
  232. package/src/superlocalmemory/encoding/scene_builder.py +0 -410
  233. package/src/superlocalmemory/encoding/signal_inference.py +0 -90
  234. package/src/superlocalmemory/encoding/temporal_parser.py +0 -432
  235. package/src/superlocalmemory/encoding/temporal_validator.py +0 -572
  236. package/src/superlocalmemory/encoding/type_router.py +0 -237
  237. package/src/superlocalmemory/evaluation/__init__.py +0 -13
  238. package/src/superlocalmemory/evaluation/calibration.py +0 -308
  239. package/src/superlocalmemory/evolution/__init__.py +0 -29
  240. package/src/superlocalmemory/evolution/blind_verifier.py +0 -122
  241. package/src/superlocalmemory/evolution/budget.py +0 -356
  242. package/src/superlocalmemory/evolution/evolution_store.py +0 -619
  243. package/src/superlocalmemory/evolution/llm_dispatch.py +0 -559
  244. package/src/superlocalmemory/evolution/model_selection.py +0 -175
  245. package/src/superlocalmemory/evolution/mutation_generator.py +0 -226
  246. package/src/superlocalmemory/evolution/skill_activator.py +0 -270
  247. package/src/superlocalmemory/evolution/skill_evolver.py +0 -928
  248. package/src/superlocalmemory/evolution/triggers.py +0 -376
  249. package/src/superlocalmemory/evolution/types.py +0 -114
  250. package/src/superlocalmemory/graph/__init__.py +0 -9
  251. package/src/superlocalmemory/graph/cozo_adjacency.py +0 -122
  252. package/src/superlocalmemory/graph/cozo_backend.py +0 -751
  253. package/src/superlocalmemory/hooks/__init__.py +0 -3
  254. package/src/superlocalmemory/hooks/_outcome_common.py +0 -523
  255. package/src/superlocalmemory/hooks/adapter_base.py +0 -347
  256. package/src/superlocalmemory/hooks/antigravity_adapter.py +0 -171
  257. package/src/superlocalmemory/hooks/auto_capture.py +0 -133
  258. package/src/superlocalmemory/hooks/auto_invoker.py +0 -521
  259. package/src/superlocalmemory/hooks/auto_parameterize.py +0 -147
  260. package/src/superlocalmemory/hooks/auto_recall.py +0 -191
  261. package/src/superlocalmemory/hooks/auto_recall_hook.py +0 -251
  262. package/src/superlocalmemory/hooks/before_web_hook.py +0 -131
  263. package/src/superlocalmemory/hooks/claude_code_hooks.py +0 -637
  264. package/src/superlocalmemory/hooks/codex_assets.py +0 -251
  265. package/src/superlocalmemory/hooks/codex_hooks.py +0 -186
  266. package/src/superlocalmemory/hooks/context_payload.py +0 -311
  267. package/src/superlocalmemory/hooks/copilot_adapter.py +0 -208
  268. package/src/superlocalmemory/hooks/cross_platform_connector.py +0 -90
  269. package/src/superlocalmemory/hooks/cursor_adapter.py +0 -173
  270. package/src/superlocalmemory/hooks/hook_daemon.py +0 -295
  271. package/src/superlocalmemory/hooks/hook_handlers.py +0 -822
  272. package/src/superlocalmemory/hooks/ide_connector.py +0 -246
  273. package/src/superlocalmemory/hooks/memory_protocol.py +0 -158
  274. package/src/superlocalmemory/hooks/portable_kit.py +0 -755
  275. package/src/superlocalmemory/hooks/post_tool_async_hook.py +0 -183
  276. package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +0 -351
  277. package/src/superlocalmemory/hooks/prewarm_auth.py +0 -187
  278. package/src/superlocalmemory/hooks/rules_engine.py +0 -99
  279. package/src/superlocalmemory/hooks/session_registry.py +0 -330
  280. package/src/superlocalmemory/hooks/stop_outcome_hook.py +0 -138
  281. package/src/superlocalmemory/hooks/sync_loop.py +0 -114
  282. package/src/superlocalmemory/hooks/topic_shift_hook.py +0 -302
  283. package/src/superlocalmemory/hooks/user_prompt_hook.py +0 -131
  284. package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +0 -210
  285. package/src/superlocalmemory/infra/__init__.py +0 -3
  286. package/src/superlocalmemory/infra/auth_middleware.py +0 -145
  287. package/src/superlocalmemory/infra/backup.py +0 -974
  288. package/src/superlocalmemory/infra/backup_obligations.py +0 -423
  289. package/src/superlocalmemory/infra/cache_manager.py +0 -267
  290. package/src/superlocalmemory/infra/cloud_backup.py +0 -788
  291. package/src/superlocalmemory/infra/daemon_identity.py +0 -300
  292. package/src/superlocalmemory/infra/data_root.py +0 -238
  293. package/src/superlocalmemory/infra/event_bus.py +0 -637
  294. package/src/superlocalmemory/infra/local_diagnostics.py +0 -327
  295. package/src/superlocalmemory/infra/pid_manager.py +0 -193
  296. package/src/superlocalmemory/infra/process_identity.py +0 -180
  297. package/src/superlocalmemory/infra/process_reaper.py +0 -624
  298. package/src/superlocalmemory/infra/rate_limiter.py +0 -228
  299. package/src/superlocalmemory/infra/self_heal.py +0 -401
  300. package/src/superlocalmemory/infra/version_integrity.py +0 -229
  301. package/src/superlocalmemory/ingestion/__init__.py +0 -13
  302. package/src/superlocalmemory/ingestion/adapter_manager.py +0 -255
  303. package/src/superlocalmemory/ingestion/base_adapter.py +0 -171
  304. package/src/superlocalmemory/ingestion/calendar_adapter.py +0 -349
  305. package/src/superlocalmemory/ingestion/credentials.py +0 -125
  306. package/src/superlocalmemory/ingestion/gmail_adapter.py +0 -378
  307. package/src/superlocalmemory/ingestion/parsers.py +0 -100
  308. package/src/superlocalmemory/ingestion/transcript_adapter.py +0 -161
  309. package/src/superlocalmemory/integrations/__init__.py +0 -1
  310. package/src/superlocalmemory/integrations/bounded_loops_mcp.py +0 -431
  311. package/src/superlocalmemory/integrations/bounded_loops_v051.py +0 -236
  312. package/src/superlocalmemory/learning/__init__.py +0 -0
  313. package/src/superlocalmemory/learning/adaptive.py +0 -172
  314. package/src/superlocalmemory/learning/arm_catalog.py +0 -97
  315. package/src/superlocalmemory/learning/assertion_miner.py +0 -403
  316. package/src/superlocalmemory/learning/bandit.py +0 -654
  317. package/src/superlocalmemory/learning/bandit_cache.py +0 -131
  318. package/src/superlocalmemory/learning/behavioral.py +0 -542
  319. package/src/superlocalmemory/learning/bootstrap.py +0 -298
  320. package/src/superlocalmemory/learning/consolidation_cycle.py +0 -398
  321. package/src/superlocalmemory/learning/consolidation_quantization_worker.py +0 -115
  322. package/src/superlocalmemory/learning/consolidation_worker.py +0 -261
  323. package/src/superlocalmemory/learning/cross_project.py +0 -408
  324. package/src/superlocalmemory/learning/database.py +0 -698
  325. package/src/superlocalmemory/learning/dedup_hnsw.py +0 -413
  326. package/src/superlocalmemory/learning/engagement.py +0 -487
  327. package/src/superlocalmemory/learning/engagement_features.py +0 -279
  328. package/src/superlocalmemory/learning/ensemble.py +0 -309
  329. package/src/superlocalmemory/learning/entity_compiler.py +0 -356
  330. package/src/superlocalmemory/learning/fact_outcome_joins.py +0 -207
  331. package/src/superlocalmemory/learning/features.py +0 -138
  332. package/src/superlocalmemory/learning/feedback.py +0 -724
  333. package/src/superlocalmemory/learning/forgetting_scheduler.py +0 -375
  334. package/src/superlocalmemory/learning/hnsw_dedup.py +0 -69
  335. package/src/superlocalmemory/learning/labeler.py +0 -85
  336. package/src/superlocalmemory/learning/legacy_migration.py +0 -316
  337. package/src/superlocalmemory/learning/lightgbm_subprocess.py +0 -236
  338. package/src/superlocalmemory/learning/memory_merge.py +0 -175
  339. package/src/superlocalmemory/learning/model_cache.py +0 -267
  340. package/src/superlocalmemory/learning/model_rollback.py +0 -281
  341. package/src/superlocalmemory/learning/outcome_queue.py +0 -306
  342. package/src/superlocalmemory/learning/outcomes.py +0 -286
  343. package/src/superlocalmemory/learning/pattern_miner.py +0 -465
  344. package/src/superlocalmemory/learning/pattern_miner_constants.py +0 -90
  345. package/src/superlocalmemory/learning/pcos.py +0 -291
  346. package/src/superlocalmemory/learning/project_context.py +0 -366
  347. package/src/superlocalmemory/learning/propensity.py +0 -131
  348. package/src/superlocalmemory/learning/ranker.py +0 -300
  349. package/src/superlocalmemory/learning/ranker_common.py +0 -163
  350. package/src/superlocalmemory/learning/ranker_retrain_legacy.py +0 -210
  351. package/src/superlocalmemory/learning/ranker_retrain_online.py +0 -423
  352. package/src/superlocalmemory/learning/reward.py +0 -888
  353. package/src/superlocalmemory/learning/reward_archive.py +0 -223
  354. package/src/superlocalmemory/learning/reward_boost.py +0 -211
  355. package/src/superlocalmemory/learning/reward_from_outcomes.py +0 -365
  356. package/src/superlocalmemory/learning/reward_model.py +0 -144
  357. package/src/superlocalmemory/learning/reward_proxy.py +0 -578
  358. package/src/superlocalmemory/learning/shadow_test.py +0 -524
  359. package/src/superlocalmemory/learning/signal_kinds.py +0 -79
  360. package/src/superlocalmemory/learning/signal_worker.py +0 -268
  361. package/src/superlocalmemory/learning/signals.py +0 -646
  362. package/src/superlocalmemory/learning/skill_performance_miner.py +0 -422
  363. package/src/superlocalmemory/learning/source_quality.py +0 -828
  364. package/src/superlocalmemory/learning/trigram_index.py +0 -548
  365. package/src/superlocalmemory/learning/workflows.py +0 -309
  366. package/src/superlocalmemory/llm/__init__.py +0 -0
  367. package/src/superlocalmemory/llm/backbone.py +0 -364
  368. package/src/superlocalmemory/loops/__init__.py +0 -58
  369. package/src/superlocalmemory/loops/budget.py +0 -58
  370. package/src/superlocalmemory/loops/engine.py +0 -174
  371. package/src/superlocalmemory/loops/ledger.py +0 -298
  372. package/src/superlocalmemory/loops/models.py +0 -152
  373. package/src/superlocalmemory/loops/rules.py +0 -52
  374. package/src/superlocalmemory/math/__init__.py +0 -0
  375. package/src/superlocalmemory/math/ebbinghaus.py +0 -352
  376. package/src/superlocalmemory/math/fisher.py +0 -356
  377. package/src/superlocalmemory/math/fisher_quantized.py +0 -255
  378. package/src/superlocalmemory/math/hopfield.py +0 -282
  379. package/src/superlocalmemory/math/langevin.py +0 -411
  380. package/src/superlocalmemory/math/polar_quant.py +0 -414
  381. package/src/superlocalmemory/math/qjl.py +0 -115
  382. package/src/superlocalmemory/math/sheaf.py +0 -261
  383. package/src/superlocalmemory/math/turbo_quant.py +0 -318
  384. package/src/superlocalmemory/mcp/__init__.py +0 -0
  385. package/src/superlocalmemory/mcp/_daemon_proxy.py +0 -203
  386. package/src/superlocalmemory/mcp/_pool_adapter.py +0 -181
  387. package/src/superlocalmemory/mcp/_stdin_guard.py +0 -60
  388. package/src/superlocalmemory/mcp/agent_context.py +0 -115
  389. package/src/superlocalmemory/mcp/cli_fallback.py +0 -602
  390. package/src/superlocalmemory/mcp/http_transport.py +0 -85
  391. package/src/superlocalmemory/mcp/profiles.py +0 -154
  392. package/src/superlocalmemory/mcp/resources.py +0 -281
  393. package/src/superlocalmemory/mcp/server.py +0 -480
  394. package/src/superlocalmemory/mcp/session_binding.py +0 -98
  395. package/src/superlocalmemory/mcp/shared.py +0 -112
  396. package/src/superlocalmemory/mcp/tools.py +0 -18
  397. package/src/superlocalmemory/mcp/tools_active.py +0 -958
  398. package/src/superlocalmemory/mcp/tools_brain.py +0 -298
  399. package/src/superlocalmemory/mcp/tools_code_graph.py +0 -1717
  400. package/src/superlocalmemory/mcp/tools_context.py +0 -239
  401. package/src/superlocalmemory/mcp/tools_core.py +0 -1112
  402. package/src/superlocalmemory/mcp/tools_evolution.py +0 -343
  403. package/src/superlocalmemory/mcp/tools_learning.py +0 -393
  404. package/src/superlocalmemory/mcp/tools_loops.py +0 -345
  405. package/src/superlocalmemory/mcp/tools_mesh.py +0 -429
  406. package/src/superlocalmemory/mcp/tools_ops.py +0 -115
  407. package/src/superlocalmemory/mcp/tools_optimize.py +0 -322
  408. package/src/superlocalmemory/mcp/tools_summaries.py +0 -147
  409. package/src/superlocalmemory/mcp/tools_v28.py +0 -292
  410. package/src/superlocalmemory/mcp/tools_v3.py +0 -398
  411. package/src/superlocalmemory/mcp/tools_v33.py +0 -507
  412. package/src/superlocalmemory/mesh/__init__.py +0 -12
  413. package/src/superlocalmemory/mesh/broker.py +0 -812
  414. package/src/superlocalmemory/mesh/broker_security.py +0 -470
  415. package/src/superlocalmemory/mesh/discovery.py +0 -365
  416. package/src/superlocalmemory/mesh/lock_protocol.py +0 -313
  417. package/src/superlocalmemory/mesh/node_identity.py +0 -97
  418. package/src/superlocalmemory/mesh/outbox_remote.py +0 -429
  419. package/src/superlocalmemory/mesh/remote_sync.py +0 -829
  420. package/src/superlocalmemory/mesh/state_sync.py +0 -286
  421. package/src/superlocalmemory/migrations/__init__.py +0 -5
  422. package/src/superlocalmemory/migrations/v3_4_25_to_v3_4_26.py +0 -144
  423. package/src/superlocalmemory/optimize/NOTICE +0 -6
  424. package/src/superlocalmemory/optimize/__init__.py +0 -0
  425. package/src/superlocalmemory/optimize/adapters/__init__.py +0 -68
  426. package/src/superlocalmemory/optimize/adapters/_agent_registry.py +0 -120
  427. package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +0 -112
  428. package/src/superlocalmemory/optimize/adapters/openai_adapter.py +0 -122
  429. package/src/superlocalmemory/optimize/adapters/wrap.py +0 -228
  430. package/src/superlocalmemory/optimize/cache/__init__.py +0 -31
  431. package/src/superlocalmemory/optimize/cache/boundary_store.py +0 -488
  432. package/src/superlocalmemory/optimize/cache/centroid_store.py +0 -199
  433. package/src/superlocalmemory/optimize/cache/context_key.py +0 -67
  434. package/src/superlocalmemory/optimize/cache/exact.py +0 -88
  435. package/src/superlocalmemory/optimize/cache/invalidation.py +0 -36
  436. package/src/superlocalmemory/optimize/cache/key_builder.py +0 -111
  437. package/src/superlocalmemory/optimize/cache/manager.py +0 -737
  438. package/src/superlocalmemory/optimize/cache/semantic.py +0 -637
  439. package/src/superlocalmemory/optimize/cache/stampede.py +0 -50
  440. package/src/superlocalmemory/optimize/compress/__init__.py +0 -17
  441. package/src/superlocalmemory/optimize/compress/align.py +0 -159
  442. package/src/superlocalmemory/optimize/compress/ccr.py +0 -116
  443. package/src/superlocalmemory/optimize/compress/prose_llmlingua.py +0 -71
  444. package/src/superlocalmemory/optimize/compress/router.py +0 -667
  445. package/src/superlocalmemory/optimize/config/__init__.py +0 -56
  446. package/src/superlocalmemory/optimize/config/defaults.py +0 -43
  447. package/src/superlocalmemory/optimize/config/schema.py +0 -327
  448. package/src/superlocalmemory/optimize/config/store.py +0 -270
  449. package/src/superlocalmemory/optimize/metrics/__init__.py +0 -8
  450. package/src/superlocalmemory/optimize/metrics/counters.py +0 -155
  451. package/src/superlocalmemory/optimize/metrics/estimator.py +0 -87
  452. package/src/superlocalmemory/optimize/metrics/exporters.py +0 -77
  453. package/src/superlocalmemory/optimize/metrics/persistence.py +0 -115
  454. package/src/superlocalmemory/optimize/proxy/__init__.py +0 -28
  455. package/src/superlocalmemory/optimize/proxy/_helpers.py +0 -730
  456. package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +0 -375
  457. package/src/superlocalmemory/optimize/proxy/capture.py +0 -550
  458. package/src/superlocalmemory/optimize/proxy/gemini_surface.py +0 -528
  459. package/src/superlocalmemory/optimize/proxy/lifecycle.py +0 -126
  460. package/src/superlocalmemory/optimize/proxy/openai_surface.py +0 -465
  461. package/src/superlocalmemory/optimize/proxy/server.py +0 -199
  462. package/src/superlocalmemory/optimize/proxy/vertex_surface.py +0 -246
  463. package/src/superlocalmemory/optimize/storage/__init__.py +0 -0
  464. package/src/superlocalmemory/optimize/storage/db.py +0 -1185
  465. package/src/superlocalmemory/optimize/storage/schema.py +0 -205
  466. package/src/superlocalmemory/parameterization/__init__.py +0 -47
  467. package/src/superlocalmemory/parameterization/cross_project.py +0 -12
  468. package/src/superlocalmemory/parameterization/pattern_extractor.py +0 -584
  469. package/src/superlocalmemory/parameterization/pii_filter.py +0 -106
  470. package/src/superlocalmemory/parameterization/prompt_injector.py +0 -219
  471. package/src/superlocalmemory/parameterization/prompt_lifecycle.py +0 -281
  472. package/src/superlocalmemory/parameterization/soft_prompt_generator.py +0 -542
  473. package/src/superlocalmemory/parameterization/workflow_miner.py +0 -17
  474. package/src/superlocalmemory/reliability/__init__.py +0 -45
  475. package/src/superlocalmemory/reliability/join_liveness.py +0 -301
  476. package/src/superlocalmemory/reliability/prior_distance.py +0 -243
  477. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  478. package/src/superlocalmemory/retrieval/agentic.py +0 -367
  479. package/src/superlocalmemory/retrieval/ann_index.py +0 -235
  480. package/src/superlocalmemory/retrieval/bm25_channel.py +0 -451
  481. package/src/superlocalmemory/retrieval/bridge_discovery.py +0 -253
  482. package/src/superlocalmemory/retrieval/channel_registry.py +0 -154
  483. package/src/superlocalmemory/retrieval/channel_status.py +0 -117
  484. package/src/superlocalmemory/retrieval/engine.py +0 -1615
  485. package/src/superlocalmemory/retrieval/entity_channel.py +0 -994
  486. package/src/superlocalmemory/retrieval/forgetting_filter.py +0 -160
  487. package/src/superlocalmemory/retrieval/fusion.py +0 -81
  488. package/src/superlocalmemory/retrieval/graph_adjacency.py +0 -219
  489. package/src/superlocalmemory/retrieval/hopfield_channel.py +0 -465
  490. package/src/superlocalmemory/retrieval/profile_channel.py +0 -105
  491. package/src/superlocalmemory/retrieval/quantization_aware_search.py +0 -147
  492. package/src/superlocalmemory/retrieval/remote_reranker.py +0 -758
  493. package/src/superlocalmemory/retrieval/reranker.py +0 -674
  494. package/src/superlocalmemory/retrieval/scope_policy.py +0 -126
  495. package/src/superlocalmemory/retrieval/semantic_channel.py +0 -638
  496. package/src/superlocalmemory/retrieval/spreading.py +0 -288
  497. package/src/superlocalmemory/retrieval/spreading_activation.py +0 -616
  498. package/src/superlocalmemory/retrieval/strategy.py +0 -248
  499. package/src/superlocalmemory/retrieval/temporal_channel.py +0 -433
  500. package/src/superlocalmemory/retrieval/temporal_frame.py +0 -102
  501. package/src/superlocalmemory/retrieval/temporal_utils.py +0 -122
  502. package/src/superlocalmemory/retrieval/temporal_validity_filter.py +0 -499
  503. package/src/superlocalmemory/retrieval/time_window.py +0 -181
  504. package/src/superlocalmemory/retrieval/vector_store.py +0 -863
  505. package/src/superlocalmemory/server/__init__.py +0 -1
  506. package/src/superlocalmemory/server/api.py +0 -310
  507. package/src/superlocalmemory/server/asset_versions.py +0 -171
  508. package/src/superlocalmemory/server/bandit_loops.py +0 -158
  509. package/src/superlocalmemory/server/config_file.py +0 -90
  510. package/src/superlocalmemory/server/consolidation_runner.py +0 -140
  511. package/src/superlocalmemory/server/egress_policy.py +0 -258
  512. package/src/superlocalmemory/server/loopback.py +0 -85
  513. package/src/superlocalmemory/server/middleware/__init__.py +0 -11
  514. package/src/superlocalmemory/server/middleware/security_headers.py +0 -144
  515. package/src/superlocalmemory/server/origin.py +0 -55
  516. package/src/superlocalmemory/server/profile_runtime.py +0 -515
  517. package/src/superlocalmemory/server/rbac_enforce.py +0 -194
  518. package/src/superlocalmemory/server/recall_health.py +0 -343
  519. package/src/superlocalmemory/server/recall_serializer.py +0 -320
  520. package/src/superlocalmemory/server/route_mutations.py +0 -104
  521. package/src/superlocalmemory/server/routes/__init__.py +0 -4
  522. package/src/superlocalmemory/server/routes/abstraction.py +0 -314
  523. package/src/superlocalmemory/server/routes/adapters.py +0 -63
  524. package/src/superlocalmemory/server/routes/agents.py +0 -303
  525. package/src/superlocalmemory/server/routes/backup.py +0 -869
  526. package/src/superlocalmemory/server/routes/behavioral.py +0 -659
  527. package/src/superlocalmemory/server/routes/brain.py +0 -1892
  528. package/src/superlocalmemory/server/routes/chat.py +0 -393
  529. package/src/superlocalmemory/server/routes/compliance.py +0 -533
  530. package/src/superlocalmemory/server/routes/config_api.py +0 -703
  531. package/src/superlocalmemory/server/routes/data_io.py +0 -329
  532. package/src/superlocalmemory/server/routes/entity.py +0 -237
  533. package/src/superlocalmemory/server/routes/events.py +0 -214
  534. package/src/superlocalmemory/server/routes/evolution.py +0 -510
  535. package/src/superlocalmemory/server/routes/helpers.py +0 -499
  536. package/src/superlocalmemory/server/routes/ingest.py +0 -137
  537. package/src/superlocalmemory/server/routes/insights.py +0 -366
  538. package/src/superlocalmemory/server/routes/learning.py +0 -834
  539. package/src/superlocalmemory/server/routes/learning_telemetry.py +0 -154
  540. package/src/superlocalmemory/server/routes/lifecycle.py +0 -184
  541. package/src/superlocalmemory/server/routes/memories.py +0 -1661
  542. package/src/superlocalmemory/server/routes/mesh.py +0 -517
  543. package/src/superlocalmemory/server/routes/mesh_lock.py +0 -54
  544. package/src/superlocalmemory/server/routes/mesh_state.py +0 -63
  545. package/src/superlocalmemory/server/routes/optimize.py +0 -197
  546. package/src/superlocalmemory/server/routes/prewarm.py +0 -173
  547. package/src/superlocalmemory/server/routes/profiles.py +0 -292
  548. package/src/superlocalmemory/server/routes/ratelimit.py +0 -132
  549. package/src/superlocalmemory/server/routes/rbac.py +0 -366
  550. package/src/superlocalmemory/server/routes/stats.py +0 -385
  551. package/src/superlocalmemory/server/routes/tiers.py +0 -222
  552. package/src/superlocalmemory/server/routes/timeline.py +0 -258
  553. package/src/superlocalmemory/server/routes/token.py +0 -90
  554. package/src/superlocalmemory/server/routes/v3_api.py +0 -3023
  555. package/src/superlocalmemory/server/routes/ws.py +0 -171
  556. package/src/superlocalmemory/server/security_middleware.py +0 -89
  557. package/src/superlocalmemory/server/ui.py +0 -354
  558. package/src/superlocalmemory/server/unified_daemon.py +0 -6326
  559. package/src/superlocalmemory/server/write_identity.py +0 -195
  560. package/src/superlocalmemory/storage/__init__.py +0 -0
  561. package/src/superlocalmemory/storage/_migration_internals.py +0 -638
  562. package/src/superlocalmemory/storage/_schema_version.py +0 -174
  563. package/src/superlocalmemory/storage/access_log.py +0 -170
  564. package/src/superlocalmemory/storage/admission_codec.py +0 -129
  565. package/src/superlocalmemory/storage/admission_journal.py +0 -843
  566. package/src/superlocalmemory/storage/agent_experience.py +0 -546
  567. package/src/superlocalmemory/storage/backup.py +0 -531
  568. package/src/superlocalmemory/storage/correction_cases.py +0 -670
  569. package/src/superlocalmemory/storage/database.py +0 -3180
  570. package/src/superlocalmemory/storage/deferred_writes.py +0 -209
  571. package/src/superlocalmemory/storage/embedding_codec.py +0 -200
  572. package/src/superlocalmemory/storage/embedding_migrator.py +0 -672
  573. package/src/superlocalmemory/storage/erasure_fence.py +0 -45
  574. package/src/superlocalmemory/storage/execution_learning.py +0 -285
  575. package/src/superlocalmemory/storage/external_evidence.py +0 -359
  576. package/src/superlocalmemory/storage/generation_fence.py +0 -63
  577. package/src/superlocalmemory/storage/lineage_retention.py +0 -236
  578. package/src/superlocalmemory/storage/logical_edges.py +0 -86
  579. package/src/superlocalmemory/storage/memory_write.py +0 -115
  580. package/src/superlocalmemory/storage/migration_runner.py +0 -895
  581. package/src/superlocalmemory/storage/migration_v33.py +0 -140
  582. package/src/superlocalmemory/storage/migrations/M001_add_signal_features_columns.py +0 -67
  583. package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +0 -107
  584. package/src/superlocalmemory/storage/migrations/M003_migration_log.py +0 -38
  585. package/src/superlocalmemory/storage/migrations/M004_cross_platform_sync_log.py +0 -46
  586. package/src/superlocalmemory/storage/migrations/M005_bandit_tables.py +0 -75
  587. package/src/superlocalmemory/storage/migrations/M006_action_outcomes_reward.py +0 -75
  588. package/src/superlocalmemory/storage/migrations/M007_pending_outcomes.py +0 -63
  589. package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +0 -94
  590. package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +0 -80
  591. package/src/superlocalmemory/storage/migrations/M011_archive_and_merge.py +0 -87
  592. package/src/superlocalmemory/storage/migrations/M012_shadow_observations.py +0 -72
  593. package/src/superlocalmemory/storage/migrations/M013_bi_temporal_columns.py +0 -55
  594. package/src/superlocalmemory/storage/migrations/M014_v345_scale_ready.py +0 -45
  595. package/src/superlocalmemory/storage/migrations/M015_add_pinned_column.py +0 -58
  596. package/src/superlocalmemory/storage/migrations/M016_add_scope_support.py +0 -120
  597. package/src/superlocalmemory/storage/migrations/M017_ccq_scope_column.py +0 -79
  598. package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +0 -120
  599. package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +0 -54
  600. package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +0 -52
  601. package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +0 -108
  602. package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +0 -86
  603. package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +0 -194
  604. package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +0 -87
  605. package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +0 -90
  606. package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +0 -136
  607. package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +0 -163
  608. package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +0 -305
  609. package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +0 -137
  610. package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +0 -93
  611. package/src/superlocalmemory/storage/migrations/M031_dead_letter_operations.py +0 -80
  612. package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +0 -188
  613. package/src/superlocalmemory/storage/migrations/M033_projection_transactions.py +0 -148
  614. package/src/superlocalmemory/storage/migrations/M034_obligation_integrity.py +0 -58
  615. package/src/superlocalmemory/storage/migrations/M035_erasure_receipts.py +0 -113
  616. package/src/superlocalmemory/storage/migrations/M036_vector_row_map.py +0 -107
  617. package/src/superlocalmemory/storage/migrations/M037_manifest_hmac_version.py +0 -162
  618. package/src/superlocalmemory/storage/migrations/M038_learning_feedback_channel.py +0 -77
  619. package/src/superlocalmemory/storage/migrations/M039_scene_fact_members.py +0 -137
  620. package/src/superlocalmemory/storage/migrations/M040_agent_experience_receipts.py +0 -254
  621. package/src/superlocalmemory/storage/migrations/M041_external_evidence_receipts.py +0 -189
  622. package/src/superlocalmemory/storage/migrations/M042_correction_case_ledger.py +0 -245
  623. package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +0 -512
  624. package/src/superlocalmemory/storage/migrations/M044_play_carries_its_own_evidence.py +0 -127
  625. package/src/superlocalmemory/storage/migrations/M045_fact_outcome_score.py +0 -158
  626. package/src/superlocalmemory/storage/migrations/M046_prospective_memory_has_its_own_name.py +0 -620
  627. package/src/superlocalmemory/storage/migrations/M047_fisher_vectors_are_stored_like_every_other_vector.py +0 -306
  628. package/src/superlocalmemory/storage/migrations/M048_upcoming_holds_only_what_is_upcoming.py +0 -229
  629. package/src/superlocalmemory/storage/migrations/M049_a_schema_version_marker_is_one_row.py +0 -201
  630. package/src/superlocalmemory/storage/migrations/M050_execution_learning_v2.py +0 -70
  631. package/src/superlocalmemory/storage/migrations/__init__.py +0 -103
  632. package/src/superlocalmemory/storage/migrations.py +0 -333
  633. package/src/superlocalmemory/storage/models.py +0 -500
  634. package/src/superlocalmemory/storage/projection_outbox.py +0 -346
  635. package/src/superlocalmemory/storage/quantized_store.py +0 -280
  636. package/src/superlocalmemory/storage/read_connection.py +0 -115
  637. package/src/superlocalmemory/storage/retention_policy.py +0 -860
  638. package/src/superlocalmemory/storage/schema.py +0 -1108
  639. package/src/superlocalmemory/storage/schema_code_graph.py +0 -282
  640. package/src/superlocalmemory/storage/schema_v32.py +0 -382
  641. package/src/superlocalmemory/storage/schema_v3410.py +0 -159
  642. package/src/superlocalmemory/storage/schema_v3411.py +0 -149
  643. package/src/superlocalmemory/storage/schema_v343.py +0 -315
  644. package/src/superlocalmemory/storage/schema_v345.py +0 -109
  645. package/src/superlocalmemory/storage/schema_v347.py +0 -140
  646. package/src/superlocalmemory/storage/sqlite_vectors.py +0 -169
  647. package/src/superlocalmemory/storage/v2_migrator.py +0 -466
  648. package/src/superlocalmemory/storage/write_coordinator.py +0 -949
  649. package/src/superlocalmemory/storage/write_lock.py +0 -88
  650. package/src/superlocalmemory/summaries/__init__.py +0 -37
  651. package/src/superlocalmemory/summaries/base.py +0 -267
  652. package/src/superlocalmemory/summaries/daily_reflection.py +0 -340
  653. package/src/superlocalmemory/summaries/non_answer.py +0 -223
  654. package/src/superlocalmemory/summaries/project_work_log.py +0 -440
  655. package/src/superlocalmemory/summaries/session_summary.py +0 -311
  656. package/src/superlocalmemory/trust/__init__.py +0 -0
  657. package/src/superlocalmemory/trust/gate.py +0 -171
  658. package/src/superlocalmemory/trust/provenance.py +0 -124
  659. package/src/superlocalmemory/trust/scorer.py +0 -413
  660. package/src/superlocalmemory/trust/signals.py +0 -153
  661. package/src/superlocalmemory/ui/assets/slm-icon-white.svg +0 -64
  662. package/src/superlocalmemory/ui/assets/slm-icon.svg +0 -36
  663. package/src/superlocalmemory/ui/css/brain.css +0 -409
  664. package/src/superlocalmemory/ui/css/design-system.css +0 -696
  665. package/src/superlocalmemory/ui/css/legacy-dashboard.css +0 -663
  666. package/src/superlocalmemory/ui/css/neural-glass.css +0 -1599
  667. package/src/superlocalmemory/ui/css/od-bridge.css +0 -158
  668. package/src/superlocalmemory/ui/favicon.svg +0 -36
  669. package/src/superlocalmemory/ui/index.html +0 -1638
  670. package/src/superlocalmemory/ui/js/agents.js +0 -192
  671. package/src/superlocalmemory/ui/js/auto-settings.js +0 -624
  672. package/src/superlocalmemory/ui/js/brain.js +0 -1400
  673. package/src/superlocalmemory/ui/js/clusters.js +0 -326
  674. package/src/superlocalmemory/ui/js/compliance.js +0 -307
  675. package/src/superlocalmemory/ui/js/core.js +0 -566
  676. package/src/superlocalmemory/ui/js/dashboard.js +0 -503
  677. package/src/superlocalmemory/ui/js/event-delegation.js +0 -113
  678. package/src/superlocalmemory/ui/js/events.js +0 -178
  679. package/src/superlocalmemory/ui/js/fact-detail.js +0 -142
  680. package/src/superlocalmemory/ui/js/feedback.js +0 -339
  681. package/src/superlocalmemory/ui/js/graph-event-bus.js +0 -83
  682. package/src/superlocalmemory/ui/js/graph-filters.js +0 -220
  683. package/src/superlocalmemory/ui/js/graph-ui.js +0 -214
  684. package/src/superlocalmemory/ui/js/ide-status.js +0 -115
  685. package/src/superlocalmemory/ui/js/init.js +0 -54
  686. package/src/superlocalmemory/ui/js/knowledge-graph.js +0 -945
  687. package/src/superlocalmemory/ui/js/lifecycle.js +0 -387
  688. package/src/superlocalmemory/ui/js/math-health.js +0 -114
  689. package/src/superlocalmemory/ui/js/memories.js +0 -394
  690. package/src/superlocalmemory/ui/js/memory-chat.js +0 -371
  691. package/src/superlocalmemory/ui/js/memory-timeline.js +0 -265
  692. package/src/superlocalmemory/ui/js/modal.js +0 -733
  693. package/src/superlocalmemory/ui/js/ng-entities.js +0 -298
  694. package/src/superlocalmemory/ui/js/ng-health.js +0 -208
  695. package/src/superlocalmemory/ui/js/ng-ingestion.js +0 -203
  696. package/src/superlocalmemory/ui/js/ng-mesh.js +0 -374
  697. package/src/superlocalmemory/ui/js/ng-shell.js +0 -524
  698. package/src/superlocalmemory/ui/js/ng-skills.js +0 -663
  699. package/src/superlocalmemory/ui/js/od-agents.js +0 -588
  700. package/src/superlocalmemory/ui/js/od-auth-gate.js +0 -257
  701. package/src/superlocalmemory/ui/js/od-backup.js +0 -878
  702. package/src/superlocalmemory/ui/js/od-boundedloops.js +0 -324
  703. package/src/superlocalmemory/ui/js/od-brain.js +0 -1095
  704. package/src/superlocalmemory/ui/js/od-compliance-ext.js +0 -301
  705. package/src/superlocalmemory/ui/js/od-components.js +0 -147
  706. package/src/superlocalmemory/ui/js/od-entities.js +0 -622
  707. package/src/superlocalmemory/ui/js/od-graph.js +0 -776
  708. package/src/superlocalmemory/ui/js/od-health.js +0 -579
  709. package/src/superlocalmemory/ui/js/od-mcp.js +0 -508
  710. package/src/superlocalmemory/ui/js/od-memories.js +0 -1499
  711. package/src/superlocalmemory/ui/js/od-mesh.js +0 -645
  712. package/src/superlocalmemory/ui/js/od-operations.js +0 -1268
  713. package/src/superlocalmemory/ui/js/od-ops-health.js +0 -417
  714. package/src/superlocalmemory/ui/js/od-optimize.js +0 -828
  715. package/src/superlocalmemory/ui/js/od-settings.js +0 -1275
  716. package/src/superlocalmemory/ui/js/od-shell.js +0 -819
  717. package/src/superlocalmemory/ui/js/od-skills.js +0 -600
  718. package/src/superlocalmemory/ui/js/od-team.js +0 -265
  719. package/src/superlocalmemory/ui/js/optimize.js +0 -191
  720. package/src/superlocalmemory/ui/js/profiles.js +0 -362
  721. package/src/superlocalmemory/ui/js/quick-actions.js +0 -334
  722. package/src/superlocalmemory/ui/js/recall-lab.js +0 -373
  723. package/src/superlocalmemory/ui/js/search.js +0 -86
  724. package/src/superlocalmemory/ui/js/settings.js +0 -556
  725. package/src/superlocalmemory/ui/js/timeline.js +0 -62
  726. package/src/superlocalmemory/ui/js/trust-dashboard.js +0 -225
  727. package/src/superlocalmemory/ui/vendor/bootstrap-icons/bootstrap-icons.css +0 -2018
  728. package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  729. package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  730. package/src/superlocalmemory/ui/vendor/bootstrap.bundle.min.js +0 -7
  731. package/src/superlocalmemory/ui/vendor/bootstrap.min.css +0 -6
  732. package/src/superlocalmemory/ui/vendor/d3.v7.min.js +0 -2
  733. package/src/superlocalmemory/ui/vendor/graphology-library.min.js +0 -2
  734. package/src/superlocalmemory/ui/vendor/graphology.umd.min.js +0 -2
  735. package/src/superlocalmemory/ui/vendor/inter-ui/inter-variable.min.css +0 -8
  736. package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable-Italic.woff2 +0 -0
  737. package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable.woff2 +0 -0
  738. package/src/superlocalmemory/ui/vendor/sigma.min.js +0 -1
  739. package/src/superlocalmemory/vector/__init__.py +0 -9
  740. package/src/superlocalmemory/vector/lancedb_backend.py +0 -366
@@ -1,1717 +0,0 @@
1
- # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under AGPL-3.0-or-later - see LICENSE file
3
- # Part of SuperLocalMemory v3.4 — CodeGraph MCP Tools
4
-
5
- """22 MCP tools for CodeGraph: 17 graph + 5 bridge.
6
-
7
- Registered against `_target` (filtered like all other tools) — set
8
- SLM_MCP_ALL_TOOLS=1 or use a profile containing code-graph names to expose
9
- them. Each tool self-guards (returns error if graph not built).
10
-
11
- All tools return {"success": bool, ...} envelope. Never raise.
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- import logging
17
- import os
18
- import time
19
- from pathlib import Path
20
- from typing import Any, Callable
21
-
22
- from mcp.types import ToolAnnotations
23
-
24
- from superlocalmemory.core.admission import admits
25
- from superlocalmemory.core.operation_request import OperationKind
26
- from superlocalmemory.core.security_primitives import (
27
- PathTraversalError,
28
- safe_resolve,
29
- )
30
-
31
- logger = logging.getLogger(__name__)
32
-
33
- # ---------------------------------------------------------------------------
34
- # Lazy service singleton
35
- # ---------------------------------------------------------------------------
36
-
37
- _service = None
38
- _service_config = None
39
-
40
-
41
- def _get_service():
42
- """Lazy-create the CodeGraphService singleton."""
43
- global _service
44
- if _service is not None:
45
- return _service
46
-
47
- try:
48
- from superlocalmemory.code_graph.config import CodeGraphConfig
49
- from superlocalmemory.code_graph.service import CodeGraphService
50
-
51
- config = CodeGraphConfig(enabled=True)
52
- _service = CodeGraphService(config)
53
- return _service
54
- except Exception as exc:
55
- logger.warning("Failed to create CodeGraphService: %s", exc)
56
- return None
57
-
58
-
59
- def _get_db():
60
- """Get the CodeGraphDatabase from the service."""
61
- svc = _get_service()
62
- if svc is None:
63
- return None
64
- try:
65
- return svc.db
66
- except Exception:
67
- return None
68
-
69
-
70
- def _graph_not_built_error() -> dict[str, Any]:
71
- """Standard error when graph is not built."""
72
- return {
73
- "success": False,
74
- "error": "Code graph not built. Run build_code_graph first.",
75
- }
76
-
77
-
78
- def _bridge_is_enabled() -> bool:
79
- """Whether the code↔memory bridge is switched on in the saved settings."""
80
- try:
81
- cfg = _get_service()
82
- if cfg is not None and getattr(cfg.config, "bridge_enabled", False):
83
- return True
84
- # No live service yet (e.g. a tool called before any build): read the
85
- # saved settings directly rather than reporting "off" by default.
86
- from superlocalmemory.code_graph.config import CodeGraphConfig
87
- return bool(CodeGraphConfig.load().bridge_enabled)
88
- except Exception:
89
- return False
90
-
91
-
92
- def _bridge_not_enabled_error() -> dict[str, Any]:
93
- """Standard error when the code↔memory bridge is not enabled.
94
-
95
- The remediation text used to name ``code_graph.bridge.enabled``, which is not
96
- a key that exists anywhere. The real field is ``bridge_enabled`` in
97
- ``code_graph_config.json``, so anyone who followed this message edited
98
- nothing that mattered. This helper was also never called from any tool, so
99
- the message could not appear even when it was correct.
100
- """
101
- return {
102
- "success": False,
103
- "error": (
104
- 'Code↔memory bridge not enabled. Set "bridge_enabled": true in '
105
- "~/.superlocalmemory/code_graph_config.json, then run "
106
- "build_code_graph. Links are created during background maintenance."
107
- ),
108
- }
109
-
110
-
111
- def _error_response(msg: str) -> dict[str, Any]:
112
- """Standard error response."""
113
- return {"success": False, "error": msg}
114
-
115
-
116
- def _check_graph_exists() -> dict[str, Any] | None:
117
- """Check if graph has been built. Returns error dict or None if OK."""
118
- db = _get_db()
119
- if db is None:
120
- return _graph_not_built_error()
121
- stats = db.get_stats()
122
- if stats.get("nodes", 0) == 0 and stats.get("files", 0) == 0:
123
- return _graph_not_built_error()
124
- return None
125
-
126
-
127
- # ---------------------------------------------------------------------------
128
- # Registration function
129
- # ---------------------------------------------------------------------------
130
-
131
-
132
- def register_code_graph_tools(server, get_engine: Callable) -> None:
133
- """Register 22 code graph MCP tools on *server*."""
134
-
135
- # ==================================================================
136
- # Tool 1: build_code_graph
137
- # ==================================================================
138
-
139
- @server.tool()
140
- @admits(OperationKind.CORRECT)
141
- async def build_code_graph(
142
- repo_path: str,
143
- languages: str = "",
144
- exclude_patterns: str = "",
145
- ) -> dict:
146
- """Build a complete code knowledge graph from a repository.
147
-
148
- Parses all supported source files, extracts functions/classes/imports,
149
- builds call graph, detects flows, and identifies communities.
150
-
151
- Args:
152
- repo_path: Absolute path to repository root.
153
- languages: Comma-separated language filter (e.g. "python,typescript"). Empty = all.
154
- exclude_patterns: Comma-separated glob patterns to exclude.
155
- """
156
- try:
157
- # SEC: contain the untrusted repo_path under $HOME (same guard as
158
- # update_code_graph) so repo_path="/" cannot ingest the whole
159
- # filesystem into the code-graph DB.
160
- from superlocalmemory.core.security_primitives import PathTraversalError
161
- try:
162
- repo = safe_resolve(Path.home(), repo_path)
163
- except (PathTraversalError, OSError, ValueError) as exc:
164
- return _error_response(f"Invalid repo_path: {exc}")
165
- if not repo.exists():
166
- return _error_response(
167
- f"Repository path does not exist: {repo_path}"
168
- )
169
-
170
- from superlocalmemory.code_graph.config import CodeGraphConfig
171
- from superlocalmemory.code_graph.service import CodeGraphService
172
- from superlocalmemory.code_graph.parser import CodeParser
173
- from superlocalmemory.code_graph.graph_store import GraphStore
174
- from superlocalmemory.code_graph.graph_engine import GraphEngine
175
- from superlocalmemory.code_graph.search import HybridSearch
176
- from superlocalmemory.code_graph.flows import FlowDetector
177
- from superlocalmemory.code_graph.communities import CommunityDetector
178
-
179
- t0 = time.time()
180
-
181
- # Build config
182
- config_kwargs: dict[str, Any] = {
183
- "enabled": True,
184
- "repo_root": repo,
185
- }
186
- if languages:
187
- config_kwargs["languages"] = frozenset(
188
- l.strip() for l in languages.split(",") if l.strip()
189
- )
190
- if exclude_patterns:
191
- config_kwargs["exclude_patterns"] = tuple(
192
- p.strip() for p in exclude_patterns.split(",") if p.strip()
193
- )
194
-
195
- # Start from the user's saved settings, then apply this call's
196
- # arguments. Previously this constructed CodeGraphConfig(**kwargs)
197
- # from scratch, so every field the caller did not name reverted to a
198
- # class default — including bridge_enabled, which the setup wizard
199
- # writes to code_graph_config.json. A user who enabled the code graph
200
- # during setup therefore had the flag on disk and off in every build.
201
- config = CodeGraphConfig.load(**config_kwargs)
202
- global _service
203
- _service = CodeGraphService(config)
204
-
205
- # Parse
206
- parser = CodeParser(config)
207
- nodes, edges, file_records = parser.parse_all(repo)
208
-
209
- if not nodes:
210
- return {
211
- "success": True,
212
- "files_parsed": 0,
213
- "nodes": 0,
214
- "edges": 0,
215
- "flows": 0,
216
- "communities": 0,
217
- "duration_ms": int((time.time() - t0) * 1000),
218
- "message": f"No supported source files found in {repo_path}.",
219
- }
220
-
221
- # Store in DB
222
- db = _service.db
223
- store = GraphStore(db)
224
-
225
- # Group by file for atomic replacement
226
- file_groups: dict[str, tuple[list, list, Any]] = {}
227
- for fr in file_records:
228
- file_groups[fr.file_path] = ([], [], fr)
229
- for n in nodes:
230
- fp = n.file_path
231
- if fp in file_groups:
232
- file_groups[fp][0].append(n)
233
- for e in edges:
234
- fp = e.file_path
235
- if fp in file_groups:
236
- file_groups[fp][1].append(e)
237
-
238
- # Two-phase commit: all nodes first, then edges.
239
- # This makes storage order-independent so cross-file CALLS edges
240
- # (e.g., a.py calls bar() defined in b.py) are never silently
241
- # dropped because of the file iteration order.
242
- batch = [(fp, ns, es, fr) for fp, (ns, es, fr) in file_groups.items()]
243
- store.commit_build_batch(batch)
244
-
245
- # Build in-memory graph
246
- engine = GraphEngine(store)
247
- engine.build_graph()
248
-
249
- # Detect flows
250
- flow_detector = FlowDetector(db)
251
- entry_points = flow_detector.detect_entry_points()
252
- flows = []
253
- for ep in entry_points[:50]:
254
- try:
255
- flow = flow_detector.trace_flow(ep)
256
- if flow is not None:
257
- flows.append(flow)
258
- except Exception:
259
- pass
260
-
261
- # Detect communities
262
- comm_detector = CommunityDetector(db)
263
- communities = comm_detector.detect_communities()
264
-
265
- duration_ms = int((time.time() - t0) * 1000)
266
-
267
- return {
268
- "success": True,
269
- "files_parsed": len(file_records),
270
- "nodes": db.get_node_count(),
271
- "edges": db.get_edge_count(),
272
- "flows": len(flows),
273
- "communities": len(communities),
274
- "duration_ms": duration_ms,
275
- }
276
- except Exception as exc:
277
- logger.exception("build_code_graph failed")
278
- return _error_response(str(exc))
279
-
280
- # ==================================================================
281
- # Tool 2: update_code_graph
282
- # ==================================================================
283
-
284
- @server.tool()
285
- @admits(OperationKind.CORRECT)
286
- async def update_code_graph(
287
- repo_path: str = "",
288
- changed_files: str = "",
289
- ) -> dict:
290
- """Incrementally update the code graph for changed files.
291
-
292
- If changed_files is empty, auto-detects changes via git diff HEAD~1.
293
-
294
- Args:
295
- repo_path: Absolute path to repository root. Empty = use last built repo.
296
- changed_files: Comma-separated file paths (relative to repo root).
297
- """
298
- try:
299
- err = _check_graph_exists()
300
- if err is not None:
301
- return err
302
-
303
- t0 = time.time()
304
- svc = _get_service()
305
- db = svc.db
306
-
307
- # SEC 3.7.9 (B1): contain untrusted repo_path under $HOME and confirm
308
- # it is a real git repo BEFORE any git subprocess runs with it as cwd.
309
- # Blocks RCE via attacker-controlled .git config/hooks (core.fsmonitor,
310
- # core.hooksPath) that git would otherwise execute in a hostile repo.
311
- if repo_path:
312
- try:
313
- repo = safe_resolve(Path.home(), repo_path)
314
- except PathTraversalError as exc:
315
- return _error_response(f"Invalid repo_path: {exc}")
316
- if not (repo / ".git").exists():
317
- return _error_response(
318
- f"Not a git repository (no .git dir): {repo_path}"
319
- )
320
- else:
321
- repo = svc.config.repo_root
322
-
323
- files_list = [
324
- f.strip() for f in changed_files.split(",") if f.strip()
325
- ] if changed_files else []
326
-
327
- if not files_list:
328
- # Auto-detect via git
329
- try:
330
- import subprocess
331
- # repo is the safe_resolve-contained path computed above.
332
- # Harden git: disable hooks + fsmonitor + system config so a
333
- # hostile repo cannot execute code via the git invocation (B1).
334
- result = subprocess.run(
335
- [
336
- "git", "-c", "core.hooksPath=/dev/null",
337
- "-c", "core.fsmonitor=",
338
- "diff", "--name-only", "HEAD~1", "HEAD",
339
- ],
340
- capture_output=True, text=True, timeout=30,
341
- cwd=str(repo),
342
- env={
343
- **os.environ,
344
- "GIT_CONFIG_NOSYSTEM": "1",
345
- "GIT_TERMINAL_PROMPT": "0",
346
- },
347
- )
348
- files_list = [
349
- f.strip() for f in result.stdout.strip().split("\n")
350
- if f.strip()
351
- ]
352
- except Exception as exc:
353
- return _error_response(f"Git not available or not a git repository: {exc}")
354
-
355
- if not files_list:
356
- return {
357
- "success": True,
358
- "files_updated": 0,
359
- "nodes_added": 0,
360
- "nodes_removed": 0,
361
- "edges_added": 0,
362
- "edges_removed": 0,
363
- "duration_ms": 0,
364
- }
365
-
366
- # Re-parse changed files
367
- from superlocalmemory.code_graph.parser import CodeParser
368
- from superlocalmemory.code_graph.graph_store import GraphStore
369
-
370
- config = svc.config
371
- parser = CodeParser(config)
372
- store = GraphStore(db)
373
- # repo already contained via safe_resolve above (B1).
374
-
375
- nodes_before = db.get_node_count()
376
- edges_before = db.get_edge_count()
377
-
378
- for fp in files_list:
379
- # fp comes from caller-supplied changed_files; contain it under
380
- # the (already safe-resolved) repo so "../../.ssh/id_rsa" cannot
381
- # be read into the graph.
382
- try:
383
- full = safe_resolve(repo, fp)
384
- except Exception:
385
- continue
386
- if not full.exists():
387
- store.remove_file(fp)
388
- continue
389
- ext = full.suffix
390
- lang = config.extension_map.get(ext)
391
- if lang is None:
392
- continue
393
- try:
394
- source = full.read_bytes()
395
- file_nodes, file_edges, file_import_map = parser.parse_file(
396
- Path(fp), source, lang
397
- )
398
- import hashlib
399
- from superlocalmemory.code_graph.models import FileRecord
400
- from superlocalmemory.code_graph.parser import (
401
- _clean_and_resolve_edges,
402
- )
403
- fr = FileRecord(
404
- file_path=fp,
405
- content_hash=hashlib.sha256(source).hexdigest(),
406
- mtime=full.stat().st_mtime,
407
- language=lang,
408
- node_count=len(file_nodes),
409
- edge_count=len(file_edges),
410
- last_indexed=time.time(),
411
- )
412
- # Wire the resolver for the incremental path.
413
- # parse_all has _clean_and_resolve_edges built in, but
414
- # update_code_graph goes through parse_file which emits raw
415
- # placeholder targets (__call__<name>). Without resolution,
416
- # Fix B (defensive filter) drops ALL CALLS edges — silent
417
- # data loss on every incremental update.
418
- #
419
- # Load the full DB node set as the resolution universe so
420
- # Strategy 3 (global heuristic) can match cross-file calls.
421
- db_nodes, _ = store.get_all_nodes_and_edges()
422
- resolution_universe = list(file_nodes) + [
423
- n for n in db_nodes if n.file_path != fp
424
- ]
425
- resolved_edges = _clean_and_resolve_edges(
426
- resolution_universe,
427
- list(file_edges),
428
- {fp: file_import_map},
429
- repo,
430
- config,
431
- )
432
- store.store_file_nodes_edges(fp, file_nodes, resolved_edges, fr)
433
- except Exception as exc:
434
- logger.warning("Failed to update %s: %s", fp, exc)
435
-
436
- duration_ms = int((time.time() - t0) * 1000)
437
- nodes_after = db.get_node_count()
438
- edges_after = db.get_edge_count()
439
-
440
- return {
441
- "success": True,
442
- "files_updated": len(files_list),
443
- "nodes_added": max(0, nodes_after - nodes_before),
444
- "nodes_removed": max(0, nodes_before - nodes_after),
445
- "edges_added": max(0, edges_after - edges_before),
446
- "edges_removed": max(0, edges_before - edges_after),
447
- "duration_ms": duration_ms,
448
- }
449
- except Exception as exc:
450
- logger.exception("update_code_graph failed")
451
- return _error_response(str(exc))
452
-
453
- # ==================================================================
454
- # Tool 3: get_blast_radius
455
- # ==================================================================
456
-
457
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
458
- async def get_blast_radius(
459
- changed_files: str,
460
- max_depth: int = 2,
461
- max_nodes: int = 500,
462
- ) -> dict:
463
- """Compute impact radius for changed files.
464
-
465
- Uses BFS in both directions (callers + callees) to find all impacted
466
- code entities. Essential for code review.
467
-
468
- Args:
469
- changed_files: Comma-separated file paths (relative to repo root).
470
- max_depth: Maximum BFS depth (default 2).
471
- max_nodes: Maximum nodes to return (default 500).
472
- """
473
- try:
474
- err = _check_graph_exists()
475
- if err is not None:
476
- return err
477
-
478
- files_list = [
479
- f.strip() for f in changed_files.split(",") if f.strip()
480
- ]
481
- if not files_list:
482
- return _error_response("No changed files provided.")
483
-
484
- db = _get_db()
485
- from superlocalmemory.code_graph.graph_store import GraphStore
486
- from superlocalmemory.code_graph.graph_engine import GraphEngine
487
- from superlocalmemory.code_graph.blast_radius import BlastRadius
488
-
489
- store = GraphStore(db)
490
- engine = GraphEngine(store)
491
- br = BlastRadius(engine)
492
-
493
- result = br.compute(
494
- changed_files=files_list,
495
- max_depth=max_depth,
496
- max_nodes=max_nodes,
497
- )
498
-
499
- return {
500
- "success": True,
501
- "changed_nodes": list(result.changed_nodes),
502
- "impacted_nodes": list(result.impacted_nodes),
503
- "impacted_files": list(result.impacted_files),
504
- "edges": [
505
- {"source": s, "target": t, "kind": d.get("kind", "")}
506
- for s, t, d in result.edges
507
- ],
508
- "depth_reached": result.depth_reached,
509
- "truncated": result.truncated,
510
- }
511
- except Exception as exc:
512
- logger.exception("get_blast_radius failed")
513
- return _error_response(str(exc))
514
-
515
- # ==================================================================
516
- # Tool 4: get_review_context
517
- # ==================================================================
518
-
519
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
520
- async def get_review_context(
521
- changed_files: str,
522
- include_source: bool = True,
523
- ) -> dict:
524
- """Get token-optimized review context for changed files.
525
-
526
- Args:
527
- changed_files: Comma-separated file paths.
528
- include_source: Whether to include source code snippets (default True).
529
- """
530
- try:
531
- err = _check_graph_exists()
532
- if err is not None:
533
- return err
534
-
535
- files_list = [
536
- f.strip() for f in changed_files.split(",") if f.strip()
537
- ]
538
- db = _get_db()
539
- from superlocalmemory.code_graph.changes import ChangeAnalyzer
540
-
541
- analyzer = ChangeAnalyzer(db)
542
- ctx = analyzer.get_review_context(files_list)
543
-
544
- return {
545
- "success": True,
546
- "summary": ctx.summary,
547
- "review_items": [
548
- {
549
- "node_id": n.node_id,
550
- "name": n.name,
551
- "kind": n.kind,
552
- "file_path": n.file_path,
553
- "risk_score": round(n.risk_score, 3),
554
- }
555
- for n in ctx.changed_nodes
556
- ],
557
- "test_gaps": [
558
- {"node_id": n.node_id, "name": n.name, "file_path": n.file_path}
559
- for n in ctx.test_gaps
560
- ],
561
- "risk_score": round(ctx.overall_risk, 3),
562
- }
563
- except Exception as exc:
564
- logger.exception("get_review_context failed")
565
- return _error_response(str(exc))
566
-
567
- # ==================================================================
568
- # Tool 5: query_graph
569
- # ==================================================================
570
-
571
- VALID_PATTERNS = frozenset({
572
- "callers_of", "callees_of", "imports_of", "imported_by",
573
- "tests_for", "inherits_from", "inherited_by", "contains",
574
- })
575
-
576
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
577
- async def query_graph(
578
- pattern: str,
579
- target: str = "",
580
- limit: int = 20,
581
- ) -> dict:
582
- """Query the code graph for relationships.
583
-
584
- Args:
585
- pattern: Query type: callers_of, callees_of, imports_of, imported_by,
586
- tests_for, inherits_from, inherited_by, contains.
587
- target: Qualified name or partial name to match.
588
- limit: Maximum results (default 20).
589
- """
590
- try:
591
- if pattern not in VALID_PATTERNS:
592
- return _error_response(
593
- f"Invalid pattern '{pattern}'. Must be one of: "
594
- + ", ".join(sorted(VALID_PATTERNS))
595
- )
596
-
597
- err = _check_graph_exists()
598
- if err is not None:
599
- return err
600
-
601
- db = _get_db()
602
- from superlocalmemory.code_graph.graph_store import GraphStore
603
- from superlocalmemory.code_graph.graph_engine import GraphEngine
604
- from superlocalmemory.code_graph.models import EdgeKind
605
-
606
- store = GraphStore(db)
607
- engine = GraphEngine(store)
608
-
609
- # Resolve target to node_id
610
- node_id = _resolve_target(db, target)
611
- if node_id is None:
612
- return {
613
- "success": True,
614
- "pattern": pattern,
615
- "target": target,
616
- "results": [],
617
- "message": f"No node found matching '{target}'.",
618
- }
619
-
620
- # Map pattern to engine query
621
- results: list[dict[str, Any]] = []
622
-
623
- edge_kind_map = {
624
- "callers_of": (True, {EdgeKind.CALLS.value}),
625
- "callees_of": (False, {EdgeKind.CALLS.value}),
626
- "imports_of": (False, {EdgeKind.IMPORTS.value}),
627
- "imported_by": (True, {EdgeKind.IMPORTS.value}),
628
- "inherits_from": (False, {EdgeKind.INHERITS.value}),
629
- "inherited_by": (True, {EdgeKind.INHERITS.value}),
630
- "contains": (False, {EdgeKind.CONTAINS.value}),
631
- }
632
-
633
- if pattern == "tests_for":
634
- raw = engine.get_tests_for(node_id)
635
- results = [
636
- {
637
- "qualified_name": r.get("qualified_name", ""),
638
- "kind": r.get("kind", ""),
639
- "file_path": r.get("file_path", ""),
640
- "name": r.get("name", ""),
641
- }
642
- for r in raw[:limit]
643
- ]
644
- elif pattern in edge_kind_map:
645
- is_incoming, kinds = edge_kind_map[pattern]
646
- if is_incoming:
647
- raw = engine.get_callers(node_id, edge_kinds=kinds)
648
- else:
649
- raw = engine.get_callees(node_id, edge_kinds=kinds)
650
-
651
- results = [
652
- {
653
- "qualified_name": r["node"].get("qualified_name", ""),
654
- "kind": r["node"].get("kind", ""),
655
- "file_path": r["node"].get("file_path", ""),
656
- "name": r["node"].get("name", ""),
657
- }
658
- for r in raw[:limit]
659
- ]
660
-
661
- return {
662
- "success": True,
663
- "pattern": pattern,
664
- "target": target,
665
- "results": results,
666
- }
667
- except Exception as exc:
668
- logger.exception("query_graph failed")
669
- return _error_response(str(exc))
670
-
671
- # ==================================================================
672
- # Tool 6: semantic_search_code
673
- # ==================================================================
674
-
675
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
676
- async def semantic_search_code(
677
- query: str,
678
- kind: str = "",
679
- limit: int = 20,
680
- ) -> dict:
681
- """Search code entities by semantic meaning using hybrid FTS5 + vector search.
682
-
683
- Args:
684
- query: Natural language query (e.g. "authentication handler").
685
- kind: Filter by node kind: "Function", "Class", "File", "Test", or "" for all.
686
- limit: Maximum results (default 20).
687
- """
688
- try:
689
- err = _check_graph_exists()
690
- if err is not None:
691
- return err
692
-
693
- db = _get_db()
694
- from superlocalmemory.code_graph.search import HybridSearch
695
-
696
- searcher = HybridSearch(db)
697
- raw = searcher.search(query, limit=limit * 2)
698
-
699
- # Kind filter
700
- if kind:
701
- kind_lower = kind.lower()
702
- raw = [r for r in raw if r.kind.lower() == kind_lower]
703
-
704
- results = [
705
- {
706
- "qualified_name": r.qualified_name,
707
- "kind": r.kind,
708
- "file_path": r.file_path,
709
- "score": round(r.score, 4),
710
- "line_start": r.line_start,
711
- "name": r.name,
712
- }
713
- for r in raw[:limit]
714
- ]
715
-
716
- return {"success": True, "results": results}
717
- except Exception as exc:
718
- logger.exception("semantic_search_code failed")
719
- return _error_response(str(exc))
720
-
721
- # ==================================================================
722
- # Tool 7: list_graph_stats
723
- # ==================================================================
724
-
725
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
726
- async def list_graph_stats() -> dict:
727
- """Get code graph size and health metrics."""
728
- try:
729
- svc = _get_service()
730
- if svc is None:
731
- return _graph_not_built_error()
732
-
733
- stats = svc.get_stats()
734
-
735
- # Count code_memory_links
736
- stale_links = 0
737
- total_links = 0
738
- db = _get_db()
739
- if db is not None:
740
- try:
741
- rows = db.execute(
742
- "SELECT COUNT(*) as cnt FROM code_memory_links", ()
743
- )
744
- total_links = rows[0]["cnt"] if rows else 0
745
- rows = db.execute(
746
- "SELECT COUNT(*) as cnt FROM code_memory_links WHERE is_stale = 1",
747
- (),
748
- )
749
- stale_links = rows[0]["cnt"] if rows else 0
750
- except Exception:
751
- pass
752
-
753
- return {
754
- "success": True,
755
- "repo_root": stats.get("repo_root", ""),
756
- "total_files": stats.get("files", 0),
757
- "total_nodes": stats.get("nodes", 0),
758
- "total_edges": stats.get("edges", 0),
759
- "total_code_memory_links": total_links,
760
- "stale_links": stale_links,
761
- # Without this, total_code_memory_links == 0 is ambiguous: it
762
- # means either "no memory mentions your code" or "the feature
763
- # that creates links is switched off". Those call for opposite
764
- # actions, so the reader has to be told which one it is.
765
- "bridge_enabled": _bridge_is_enabled(),
766
- "built": stats.get("built", False),
767
- "db_path": stats.get("db_path", ""),
768
- }
769
- except Exception as exc:
770
- logger.exception("list_graph_stats failed")
771
- return _error_response(str(exc))
772
-
773
- # ==================================================================
774
- # Tool 8: find_large_functions
775
- # ==================================================================
776
-
777
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
778
- async def find_large_functions(
779
- threshold: int = 50,
780
- limit: int = 20,
781
- ) -> dict:
782
- """Find functions exceeding a line count threshold.
783
-
784
- Args:
785
- threshold: Minimum lines to flag (default 50).
786
- limit: Maximum results (default 20).
787
- """
788
- try:
789
- err = _check_graph_exists()
790
- if err is not None:
791
- return err
792
-
793
- db = _get_db()
794
- rows = db.execute(
795
- """SELECT node_id, name, qualified_name, kind, file_path,
796
- line_start, line_end
797
- FROM graph_nodes
798
- WHERE kind IN ('function', 'method')
799
- AND (line_end - line_start) >= ?
800
- ORDER BY (line_end - line_start) DESC
801
- LIMIT ?""",
802
- (threshold, limit),
803
- )
804
-
805
- functions = [
806
- {
807
- "qualified_name": r["qualified_name"],
808
- "lines": r["line_end"] - r["line_start"],
809
- "file_path": r["file_path"],
810
- "name": r["name"],
811
- }
812
- for r in rows
813
- ]
814
-
815
- return {"success": True, "functions": functions}
816
- except Exception as exc:
817
- logger.exception("find_large_functions failed")
818
- return _error_response(str(exc))
819
-
820
- # ==================================================================
821
- # Tool 9: list_flows
822
- # ==================================================================
823
-
824
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
825
- async def list_flows(
826
- sort_by: str = "criticality",
827
- limit: int = 20,
828
- ) -> dict:
829
- """List execution flows sorted by criticality or size.
830
-
831
- Args:
832
- sort_by: "criticality" (default) or "size".
833
- limit: Maximum flows to return (default 20).
834
- """
835
- try:
836
- err = _check_graph_exists()
837
- if err is not None:
838
- return err
839
-
840
- db = _get_db()
841
- from superlocalmemory.code_graph.flows import FlowDetector
842
-
843
- detector = FlowDetector(db)
844
- entries = detector.detect_entry_points()[:50]
845
-
846
- flows = []
847
- for ep in entries:
848
- try:
849
- flow = detector.trace_flow(ep)
850
- if flow is not None:
851
- flows.append(flow)
852
- except Exception:
853
- pass
854
-
855
- if sort_by == "size":
856
- flows.sort(key=lambda f: -f.node_count)
857
- else:
858
- flows.sort(key=lambda f: -f.criticality)
859
-
860
- result = [
861
- {
862
- "name": f.name,
863
- "entry_point": f.entry_node_id,
864
- "depth": f.depth,
865
- "node_count": f.node_count,
866
- "file_count": f.file_count,
867
- "criticality": round(f.criticality, 3),
868
- }
869
- for f in flows[:limit]
870
- ]
871
-
872
- return {"success": True, "flows": result}
873
- except Exception as exc:
874
- logger.exception("list_flows failed")
875
- return _error_response(str(exc))
876
-
877
- # ==================================================================
878
- # Tool 10: get_flow
879
- # ==================================================================
880
-
881
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
882
- async def get_flow(
883
- flow_name: str,
884
- ) -> dict:
885
- """Get detailed information about a single execution flow.
886
-
887
- Args:
888
- flow_name: The flow name or entry point name.
889
- """
890
- try:
891
- err = _check_graph_exists()
892
- if err is not None:
893
- return err
894
-
895
- db = _get_db()
896
- from superlocalmemory.code_graph.flows import FlowDetector
897
-
898
- detector = FlowDetector(db)
899
- entries = detector.detect_entry_points()
900
-
901
- for ep in entries:
902
- try:
903
- flow = detector.trace_flow(ep)
904
- if flow is not None and (
905
- flow.name == flow_name
906
- or flow.entry_node_id == flow_name
907
- ):
908
- return {
909
- "success": True,
910
- "flow": {
911
- "name": flow.name,
912
- "entry_point": flow.entry_node_id,
913
- "depth": flow.depth,
914
- "node_count": flow.node_count,
915
- "file_count": flow.file_count,
916
- "criticality": round(flow.criticality, 3),
917
- "path": list(flow.path_node_ids),
918
- },
919
- }
920
- except Exception:
921
- pass
922
-
923
- return _error_response(f"Flow '{flow_name}' not found.")
924
- except Exception as exc:
925
- logger.exception("get_flow failed")
926
- return _error_response(str(exc))
927
-
928
- # ==================================================================
929
- # Tool 11: get_affected_flows
930
- # ==================================================================
931
-
932
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
933
- async def get_affected_flows(
934
- changed_files: str,
935
- ) -> dict:
936
- """Find execution flows impacted by file changes.
937
-
938
- Args:
939
- changed_files: Comma-separated file paths.
940
- """
941
- try:
942
- err = _check_graph_exists()
943
- if err is not None:
944
- return err
945
-
946
- files_list = [
947
- f.strip() for f in changed_files.split(",") if f.strip()
948
- ]
949
- if not files_list:
950
- return _error_response("No changed files provided.")
951
-
952
- db = _get_db()
953
- from superlocalmemory.code_graph.flows import FlowDetector
954
-
955
- # Get all changed node IDs
956
- changed_node_ids: set[str] = set()
957
- for fp in files_list:
958
- rows = db.execute(
959
- "SELECT node_id FROM graph_nodes WHERE file_path = ?",
960
- (fp,),
961
- )
962
- changed_node_ids.update(r["node_id"] for r in rows)
963
-
964
- detector = FlowDetector(db)
965
- entries = detector.detect_entry_points()[:50]
966
-
967
- affected = []
968
- for ep in entries:
969
- try:
970
- flow = detector.trace_flow(ep)
971
- if flow is None:
972
- continue
973
- overlap = changed_node_ids.intersection(flow.path_node_ids)
974
- if overlap:
975
- affected.append({
976
- "name": flow.name,
977
- "criticality": round(flow.criticality, 3),
978
- "affected_nodes": list(overlap),
979
- })
980
- except Exception:
981
- pass
982
-
983
- return {"success": True, "affected_flows": affected}
984
- except Exception as exc:
985
- logger.exception("get_affected_flows failed")
986
- return _error_response(str(exc))
987
-
988
- # ==================================================================
989
- # Tool 12: list_communities
990
- # ==================================================================
991
-
992
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
993
- async def list_communities(
994
- sort_by: str = "cohesion",
995
- limit: int = 20,
996
- ) -> dict:
997
- """List detected code communities (clusters of related code).
998
-
999
- Args:
1000
- sort_by: "cohesion" (default) or "size".
1001
- limit: Maximum communities (default 20).
1002
- """
1003
- try:
1004
- err = _check_graph_exists()
1005
- if err is not None:
1006
- return err
1007
-
1008
- db = _get_db()
1009
- from superlocalmemory.code_graph.communities import CommunityDetector
1010
-
1011
- detector = CommunityDetector(db)
1012
- comms = detector.detect_communities()
1013
-
1014
- if sort_by == "size":
1015
- comms.sort(key=lambda c: -c.size)
1016
- else:
1017
- comms.sort(key=lambda c: -c.cohesion)
1018
-
1019
- result = [
1020
- {
1021
- "community_id": c.community_id,
1022
- "name": c.name,
1023
- "size": c.size,
1024
- "cohesion": round(c.cohesion, 3),
1025
- "dominant_language": c.dominant_language,
1026
- }
1027
- for c in comms[:limit]
1028
- ]
1029
-
1030
- return {"success": True, "communities": result}
1031
- except Exception as exc:
1032
- logger.exception("list_communities failed")
1033
- return _error_response(str(exc))
1034
-
1035
- # ==================================================================
1036
- # Tool 13: get_community
1037
- # ==================================================================
1038
-
1039
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1040
- async def get_community(
1041
- community_id: int,
1042
- ) -> dict:
1043
- """Get detailed information about a single code community.
1044
-
1045
- Args:
1046
- community_id: The community ID.
1047
- """
1048
- try:
1049
- err = _check_graph_exists()
1050
- if err is not None:
1051
- return err
1052
-
1053
- db = _get_db()
1054
- from superlocalmemory.code_graph.communities import CommunityDetector
1055
-
1056
- detector = CommunityDetector(db)
1057
- comms = detector.detect_communities()
1058
-
1059
- for c in comms:
1060
- if c.community_id == community_id:
1061
- return {
1062
- "success": True,
1063
- "community": {
1064
- "community_id": c.community_id,
1065
- "name": c.name,
1066
- "size": c.size,
1067
- "cohesion": round(c.cohesion, 3),
1068
- "dominant_language": c.dominant_language,
1069
- "directory": c.directory,
1070
- "file_count": c.file_count,
1071
- "members": list(c.node_ids[:100]),
1072
- },
1073
- }
1074
-
1075
- return _error_response(
1076
- f"Community {community_id} not found."
1077
- )
1078
- except Exception as exc:
1079
- logger.exception("get_community failed")
1080
- return _error_response(str(exc))
1081
-
1082
- # ==================================================================
1083
- # Tool 14: get_architecture_overview
1084
- # ==================================================================
1085
-
1086
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1087
- async def get_architecture_overview() -> dict:
1088
- """Get high-level architecture map showing communities and their relationships."""
1089
- try:
1090
- err = _check_graph_exists()
1091
- if err is not None:
1092
- return err
1093
-
1094
- db = _get_db()
1095
- from superlocalmemory.code_graph.communities import CommunityDetector
1096
-
1097
- detector = CommunityDetector(db)
1098
- overview = detector.get_architecture_overview()
1099
-
1100
- communities = [
1101
- {
1102
- "community_id": c.community_id,
1103
- "name": c.name,
1104
- "size": c.size,
1105
- "cohesion": round(c.cohesion, 3),
1106
- }
1107
- for c in overview.communities
1108
- ]
1109
-
1110
- warnings = [
1111
- {
1112
- "from": w.source_community,
1113
- "to": w.target_community,
1114
- "edge_count": w.edge_count,
1115
- "severity": w.severity,
1116
- }
1117
- for w in overview.coupling_warnings
1118
- ]
1119
-
1120
- return {
1121
- "success": True,
1122
- "communities": communities,
1123
- "cross_community_edges": warnings,
1124
- "total_nodes": overview.total_nodes,
1125
- "total_communities": overview.total_communities,
1126
- }
1127
- except Exception as exc:
1128
- logger.exception("get_architecture_overview failed")
1129
- return _error_response(str(exc))
1130
-
1131
- # ==================================================================
1132
- # Tool 15: detect_changes
1133
- # ==================================================================
1134
-
1135
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1136
- async def detect_changes(
1137
- base: str = "HEAD~1",
1138
- ) -> dict:
1139
- """Detect code changes and compute risk scores.
1140
-
1141
- Args:
1142
- base: Git ref to diff against (default "HEAD~1").
1143
- """
1144
- try:
1145
- err = _check_graph_exists()
1146
- if err is not None:
1147
- return err
1148
-
1149
- db = _get_db()
1150
- svc = _get_service()
1151
- from superlocalmemory.code_graph.changes import ChangeAnalyzer
1152
-
1153
- try:
1154
- hunks = ChangeAnalyzer.parse_git_diff(svc.config.repo_root, base)
1155
- except Exception as exc:
1156
- return _error_response(
1157
- f"Git not available or not a git repository: {exc}"
1158
- )
1159
-
1160
- changed_files = list({h.file_path for h in hunks})
1161
- analyzer = ChangeAnalyzer(db)
1162
- ctx = analyzer.analyze_changes(changed_files)
1163
-
1164
- return {
1165
- "success": True,
1166
- "summary": ctx.summary,
1167
- "risk_score": round(ctx.overall_risk, 3),
1168
- "changed_functions": [
1169
- {
1170
- "name": n.name,
1171
- "kind": n.kind,
1172
- "file_path": n.file_path,
1173
- "risk_score": round(n.risk_score, 3),
1174
- }
1175
- for n in ctx.changed_nodes
1176
- ],
1177
- "test_gaps": [
1178
- {"name": n.name, "file_path": n.file_path}
1179
- for n in ctx.test_gaps
1180
- ],
1181
- "review_priorities": [
1182
- {"name": n.name, "risk_score": round(n.risk_score, 3)}
1183
- for n in ctx.review_priorities
1184
- ],
1185
- }
1186
- except Exception as exc:
1187
- logger.exception("detect_changes failed")
1188
- return _error_response(str(exc))
1189
-
1190
- # ==================================================================
1191
- # Tool 16: refactor_preview
1192
- # ==================================================================
1193
-
1194
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1195
- async def refactor_preview(
1196
- action: str,
1197
- target: str,
1198
- new_name: str = "",
1199
- ) -> dict:
1200
- """Preview a refactoring operation without executing it.
1201
-
1202
- Args:
1203
- action: "rename" | "find_dead_code" | "find_duplicates"
1204
- target: Qualified name or partial name of the target.
1205
- new_name: New name (required for "rename" action).
1206
- """
1207
- try:
1208
- err = _check_graph_exists()
1209
- if err is not None:
1210
- return err
1211
-
1212
- if action == "rename" and not new_name:
1213
- return _error_response(
1214
- "new_name is required for rename action."
1215
- )
1216
-
1217
- db = _get_db()
1218
-
1219
- if action == "rename":
1220
- node_id = _resolve_target(db, target)
1221
- if node_id is None:
1222
- return _error_response(f"Target '{target}' not found.")
1223
-
1224
- # Find all references
1225
- from superlocalmemory.code_graph.graph_store import GraphStore
1226
- from superlocalmemory.code_graph.graph_engine import GraphEngine
1227
-
1228
- store = GraphStore(db)
1229
- engine = GraphEngine(store)
1230
- callers = engine.get_callers(node_id)
1231
-
1232
- affected_files = set()
1233
- node_data = engine.get_node_data(node_id)
1234
- affected_files.add(node_data.get("file_path", ""))
1235
- for c in callers:
1236
- affected_files.add(c["node"].get("file_path", ""))
1237
-
1238
- return {
1239
- "success": True,
1240
- "action": "rename",
1241
- "affected_files": list(affected_files),
1242
- "affected_references": len(callers),
1243
- "estimated_changes": len(callers) + 1,
1244
- }
1245
-
1246
- elif action == "find_dead_code":
1247
- from superlocalmemory.code_graph.graph_store import GraphStore
1248
- from superlocalmemory.code_graph.graph_engine import GraphEngine
1249
-
1250
- store = GraphStore(db)
1251
- engine = GraphEngine(store)
1252
- graph = engine.graph
1253
- index = engine.index
1254
-
1255
- dead = []
1256
- for node_id_str, rx_idx in index.id_to_rx.items():
1257
- data = graph[rx_idx]
1258
- if data.get("kind") in ("function", "method"):
1259
- in_edges = list(graph.in_edges(rx_idx))
1260
- # No callers and not a test and not an entry point
1261
- if not in_edges and not data.get("is_test"):
1262
- dead.append({
1263
- "qualified_name": data.get("qualified_name", ""),
1264
- "file_path": data.get("file_path", ""),
1265
- "kind": data.get("kind", ""),
1266
- })
1267
-
1268
- return {
1269
- "success": True,
1270
- "action": "find_dead_code",
1271
- "affected_files": list({d["file_path"] for d in dead}),
1272
- "affected_references": dead[:50],
1273
- "estimated_changes": len(dead),
1274
- }
1275
-
1276
- return _error_response(
1277
- f"Unsupported action: {action}. Use rename or find_dead_code."
1278
- )
1279
- except Exception as exc:
1280
- logger.exception("refactor_preview failed")
1281
- return _error_response(str(exc))
1282
-
1283
- # ==================================================================
1284
- # Tool 17: apply_refactor
1285
- # ==================================================================
1286
-
1287
- @server.tool()
1288
- @admits(OperationKind.CORRECT)
1289
- async def apply_refactor(
1290
- action: str,
1291
- target: str,
1292
- new_name: str = "",
1293
- dry_run: bool = True,
1294
- ) -> dict:
1295
- """Execute a refactoring operation (stub for MVP).
1296
-
1297
- Args:
1298
- action: "rename" (only supported action currently).
1299
- target: Qualified name of the target to rename.
1300
- new_name: New name.
1301
- dry_run: If True (default), show what would change without modifying files.
1302
- """
1303
- try:
1304
- # MVP stub — always dry_run
1305
- preview = await refactor_preview(action, target, new_name)
1306
- if not preview.get("success"):
1307
- return preview
1308
-
1309
- preview["dry_run"] = True
1310
- preview["message"] = (
1311
- "Apply refactor is a stub in MVP. "
1312
- "Use the preview to guide manual refactoring."
1313
- )
1314
- return preview
1315
- except Exception as exc:
1316
- logger.exception("apply_refactor failed")
1317
- return _error_response(str(exc))
1318
-
1319
- # ==================================================================
1320
- # Tool 18 (BRIDGE): code_memory_search
1321
- # ==================================================================
1322
-
1323
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1324
- async def code_memory_search(
1325
- code_entity: str,
1326
- link_type: str = "",
1327
- limit: int = 10,
1328
- ) -> dict:
1329
- """Search SLM memories linked to a code entity.
1330
-
1331
- BRIDGE TOOL: Combines code graph structure with SLM memory content.
1332
-
1333
- Args:
1334
- code_entity: Function/class/file name or qualified name.
1335
- link_type: Filter by link type. Empty = all.
1336
- limit: Maximum results (default 10).
1337
- """
1338
- try:
1339
- err = _check_graph_exists()
1340
- if err is not None:
1341
- return err
1342
-
1343
- db = _get_db()
1344
- node_id = _resolve_target(db, code_entity)
1345
- if node_id is None:
1346
- return {
1347
- "success": True,
1348
- "code_entity": code_entity,
1349
- "matched_node": None,
1350
- "memories": [],
1351
- }
1352
-
1353
- # Get links
1354
- links = db.get_links_for_node(node_id)
1355
- if link_type:
1356
- links = [lnk for lnk in links if lnk.link_type.value == link_type]
1357
-
1358
- # Get node data
1359
- node = db.get_node(node_id)
1360
- node_info = {
1361
- "node_id": node.node_id,
1362
- "name": node.name,
1363
- "qualified_name": node.qualified_name,
1364
- "kind": node.kind.value,
1365
- "file_path": node.file_path,
1366
- } if node else None
1367
-
1368
- memories = [
1369
- {
1370
- "fact_id": lnk.slm_fact_id,
1371
- "link_type": lnk.link_type.value,
1372
- "confidence": round(lnk.confidence, 3),
1373
- "created_at": lnk.created_at,
1374
- "is_stale": lnk.is_stale,
1375
- }
1376
- for lnk in links[:limit]
1377
- ]
1378
-
1379
- return {
1380
- "success": True,
1381
- "code_entity": code_entity,
1382
- "matched_node": node_info,
1383
- "memories": memories,
1384
- }
1385
- except Exception as exc:
1386
- logger.exception("code_memory_search failed")
1387
- return _error_response(str(exc))
1388
-
1389
- # ==================================================================
1390
- # Tool 19 (BRIDGE): code_entity_history
1391
- # ==================================================================
1392
-
1393
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1394
- async def code_entity_history(
1395
- code_entity: str,
1396
- ) -> dict:
1397
- """Get the complete memory timeline for a code entity.
1398
-
1399
- BRIDGE TOOL: Shows all memories about a function/class ordered by time.
1400
-
1401
- Args:
1402
- code_entity: Function/class/file name or qualified name.
1403
- """
1404
- try:
1405
- err = _check_graph_exists()
1406
- if err is not None:
1407
- return err
1408
-
1409
- db = _get_db()
1410
- node_id = _resolve_target(db, code_entity)
1411
- if node_id is None:
1412
- return {
1413
- "success": True,
1414
- "code_entity": code_entity,
1415
- "node": None,
1416
- "timeline": [],
1417
- "total_memories": 0,
1418
- "stale_count": 0,
1419
- }
1420
-
1421
- node = db.get_node(node_id)
1422
- links = db.get_links_for_node(node_id)
1423
-
1424
- # Sort by created_at
1425
- links_sorted = sorted(links, key=lambda lnk: lnk.created_at)
1426
- stale_count = sum(1 for lnk in links if lnk.is_stale)
1427
-
1428
- node_info = {
1429
- "node_id": node.node_id,
1430
- "name": node.name,
1431
- "qualified_name": node.qualified_name,
1432
- "kind": node.kind.value,
1433
- "file_path": node.file_path,
1434
- } if node else None
1435
-
1436
- timeline = [
1437
- {
1438
- "fact_id": lnk.slm_fact_id,
1439
- "link_type": lnk.link_type.value,
1440
- "created_at": lnk.created_at,
1441
- "is_stale": lnk.is_stale,
1442
- "confidence": round(lnk.confidence, 3),
1443
- }
1444
- for lnk in links_sorted
1445
- ]
1446
-
1447
- return {
1448
- "success": True,
1449
- "code_entity": code_entity,
1450
- "node": node_info,
1451
- "timeline": timeline,
1452
- "total_memories": len(links),
1453
- "stale_count": stale_count,
1454
- }
1455
- except Exception as exc:
1456
- logger.exception("code_entity_history failed")
1457
- return _error_response(str(exc))
1458
-
1459
- # ==================================================================
1460
- # Tool 20 (BRIDGE): enrich_blast_radius
1461
- # ==================================================================
1462
-
1463
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1464
- async def enrich_blast_radius(
1465
- changed_files: str,
1466
- max_depth: int = 2,
1467
- ) -> dict:
1468
- """Compute blast radius PLUS institutional memory for each impacted node.
1469
-
1470
- BRIDGE TOOL: Returns impact analysis enriched with relevant SLM memories.
1471
-
1472
- Args:
1473
- changed_files: Comma-separated file paths.
1474
- max_depth: BFS depth for blast radius (default 2).
1475
- """
1476
- try:
1477
- err = _check_graph_exists()
1478
- if err is not None:
1479
- return err
1480
-
1481
- files_list = [
1482
- f.strip() for f in changed_files.split(",") if f.strip()
1483
- ]
1484
- if not files_list:
1485
- return _error_response("No changed files provided.")
1486
-
1487
- db = _get_db()
1488
- from superlocalmemory.code_graph.graph_store import GraphStore
1489
- from superlocalmemory.code_graph.graph_engine import GraphEngine
1490
- from superlocalmemory.code_graph.blast_radius import BlastRadius
1491
-
1492
- store = GraphStore(db)
1493
- engine = GraphEngine(store)
1494
- br = BlastRadius(engine)
1495
- result = br.compute(changed_files=files_list, max_depth=max_depth)
1496
-
1497
- # Enrich each impacted node with memories
1498
- total_memories = 0
1499
- impacted = []
1500
- for nid in result.impacted_nodes:
1501
- links = db.get_links_for_node(nid)
1502
- try:
1503
- data = engine.get_node_data(nid)
1504
- except Exception:
1505
- data = {}
1506
- memories = [
1507
- {
1508
- "fact_id": lnk.slm_fact_id,
1509
- "link_type": lnk.link_type.value,
1510
- "is_stale": lnk.is_stale,
1511
- }
1512
- for lnk in links[:5]
1513
- ]
1514
- total_memories += len(memories)
1515
- impacted.append({
1516
- "qualified_name": data.get("qualified_name", nid),
1517
- "kind": data.get("kind", ""),
1518
- "file_path": data.get("file_path", ""),
1519
- "memories": memories,
1520
- "memory_count": len(links),
1521
- })
1522
-
1523
- return {
1524
- "success": True,
1525
- "changed_nodes": list(result.changed_nodes),
1526
- "impacted_nodes": impacted,
1527
- "total_memories_surfaced": total_memories,
1528
- }
1529
- except Exception as exc:
1530
- logger.exception("enrich_blast_radius failed")
1531
- return _error_response(str(exc))
1532
-
1533
- # ==================================================================
1534
- # Tool 21 (BRIDGE): code_stale_check
1535
- # ==================================================================
1536
-
1537
- @server.tool(annotations=ToolAnnotations(readOnlyHint=True))
1538
- async def code_stale_check(
1539
- scope: str = "all",
1540
- ) -> dict:
1541
- """Find SLM memories that reference deleted or changed code.
1542
-
1543
- BRIDGE TOOL: Identifies memories that may be outdated.
1544
-
1545
- Args:
1546
- scope: "all" (check everything) or a file path to scope the check.
1547
- """
1548
- try:
1549
- err = _check_graph_exists()
1550
- if err is not None:
1551
- return err
1552
-
1553
- # Every answer this tool can give comes from code_memory_links, and
1554
- # only the bridge populates that table. With the bridge off the query
1555
- # returns an empty list, which reads as "nothing is stale" — the
1556
- # strongest possible reassurance, produced by a feature that never
1557
- # ran. Say so instead.
1558
- if not _bridge_is_enabled():
1559
- return _bridge_not_enabled_error()
1560
-
1561
- db = _get_db()
1562
-
1563
- if scope == "all":
1564
- rows = db.execute(
1565
- """SELECT cml.link_id, cml.slm_fact_id, cml.code_node_id,
1566
- cml.is_stale, cml.last_verified,
1567
- gn.qualified_name, gn.file_path
1568
- FROM code_memory_links cml
1569
- LEFT JOIN graph_nodes gn ON cml.code_node_id = gn.node_id
1570
- WHERE cml.is_stale = 1""",
1571
- (),
1572
- )
1573
- else:
1574
- rows = db.execute(
1575
- """SELECT cml.link_id, cml.slm_fact_id, cml.code_node_id,
1576
- cml.is_stale, cml.last_verified,
1577
- gn.qualified_name, gn.file_path
1578
- FROM code_memory_links cml
1579
- LEFT JOIN graph_nodes gn ON cml.code_node_id = gn.node_id
1580
- WHERE cml.is_stale = 1 AND gn.file_path = ?""",
1581
- (scope,),
1582
- )
1583
-
1584
- stale_memories = [
1585
- {
1586
- "fact_id": r["slm_fact_id"],
1587
- "code_entity": r["qualified_name"] or r["code_node_id"],
1588
- "reason": "Code entity deleted or changed",
1589
- "stale_since": r["last_verified"] or "",
1590
- }
1591
- for r in rows
1592
- ]
1593
-
1594
- # Total links count
1595
- total_rows = db.execute(
1596
- "SELECT COUNT(*) as cnt FROM code_memory_links", ()
1597
- )
1598
- total_links = total_rows[0]["cnt"] if total_rows else 0
1599
-
1600
- return {
1601
- "success": True,
1602
- "stale_memories": stale_memories,
1603
- "total_stale": len(stale_memories),
1604
- "total_links": total_links,
1605
- }
1606
- except Exception as exc:
1607
- logger.exception("code_stale_check failed")
1608
- return _error_response(str(exc))
1609
-
1610
- # ==================================================================
1611
- # Tool 22 (BRIDGE): link_memory_to_code
1612
- # ==================================================================
1613
-
1614
- VALID_LINK_TYPES = frozenset({
1615
- "mentions", "decision_about", "bug_fix", "refactor", "design_rationale",
1616
- })
1617
-
1618
- @server.tool()
1619
- @admits(OperationKind.CORRECT)
1620
- async def link_memory_to_code(
1621
- fact_id: str,
1622
- code_entity: str,
1623
- link_type: str = "mentions",
1624
- ) -> dict:
1625
- """Manually link an SLM memory to a code graph node.
1626
-
1627
- BRIDGE TOOL: Creates an explicit link between a memory and a code entity.
1628
-
1629
- Args:
1630
- fact_id: The SLM atomic fact ID.
1631
- code_entity: Function/class/file qualified name or partial name.
1632
- link_type: One of: mentions, decision_about, bug_fix, refactor, design_rationale.
1633
- """
1634
- try:
1635
- if link_type not in VALID_LINK_TYPES:
1636
- return _error_response(
1637
- f"Invalid link_type '{link_type}'. Must be one of: "
1638
- + ", ".join(sorted(VALID_LINK_TYPES))
1639
- )
1640
-
1641
- err = _check_graph_exists()
1642
- if err is not None:
1643
- return err
1644
-
1645
- db = _get_db()
1646
- node_id = _resolve_target(db, code_entity)
1647
- if node_id is None:
1648
- return _error_response(
1649
- f"Code entity '{code_entity}' not found in graph."
1650
- )
1651
-
1652
- from superlocalmemory.code_graph.models import CodeMemoryLink, LinkType
1653
- from superlocalmemory.storage.models import _new_id
1654
- from datetime import datetime, timezone
1655
-
1656
- now_str = datetime.now(timezone.utc).isoformat()
1657
- link = CodeMemoryLink(
1658
- link_id=_new_id(),
1659
- code_node_id=node_id,
1660
- slm_fact_id=fact_id,
1661
- link_type=LinkType(link_type),
1662
- confidence=1.0,
1663
- created_at=now_str,
1664
- last_verified=now_str,
1665
- is_stale=False,
1666
- )
1667
- db.upsert_link(link)
1668
-
1669
- node = db.get_node(node_id)
1670
- node_info = {
1671
- "node_id": node.node_id,
1672
- "name": node.name,
1673
- "qualified_name": node.qualified_name,
1674
- } if node else {"node_id": node_id}
1675
-
1676
- return {
1677
- "success": True,
1678
- "link_id": link.link_id,
1679
- "code_node": node_info,
1680
- "confidence": 1.0,
1681
- }
1682
- except Exception as exc:
1683
- logger.exception("link_memory_to_code failed")
1684
- return _error_response(str(exc))
1685
-
1686
-
1687
- # ---------------------------------------------------------------------------
1688
- # Helpers
1689
- # ---------------------------------------------------------------------------
1690
-
1691
-
1692
- def _resolve_target(db, target: str) -> str | None:
1693
- """Resolve a target name to a node_id. Returns None if not found."""
1694
- if not target:
1695
- return None
1696
-
1697
- # Try exact qualified_name match
1698
- node = db.get_node_by_qualified_name(target)
1699
- if node is not None:
1700
- return node.node_id
1701
-
1702
- # Try exact node_id match
1703
- node = db.get_node(target)
1704
- if node is not None:
1705
- return node.node_id
1706
-
1707
- # Try LIKE match on name or qualified_name
1708
- rows = db.execute(
1709
- """SELECT node_id FROM graph_nodes
1710
- WHERE name = ? OR qualified_name LIKE ?
1711
- LIMIT 1""",
1712
- (target, f"%{target}%"),
1713
- )
1714
- if rows:
1715
- return rows[0]["node_id"]
1716
-
1717
- return None