superlocalmemory 2.8.6 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +9 -1
- package/NOTICE +63 -0
- package/README.md +165 -480
- package/bin/slm +17 -449
- package/bin/slm-npm +62 -48
- package/conftest.py +5 -0
- package/docs/api-reference.md +284 -0
- package/docs/architecture.md +149 -0
- package/docs/auto-memory.md +150 -0
- package/docs/cli-reference.md +276 -0
- package/docs/compliance.md +191 -0
- package/docs/configuration.md +182 -0
- package/docs/getting-started.md +102 -0
- package/docs/ide-setup.md +261 -0
- package/docs/mcp-tools.md +220 -0
- package/docs/migration-from-v2.md +170 -0
- package/docs/profiles.md +173 -0
- package/docs/troubleshooting.md +310 -0
- package/{configs → ide/configs}/antigravity-mcp.json +3 -3
- package/ide/configs/chatgpt-desktop-mcp.json +16 -0
- package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
- package/{configs → ide/configs}/codex-mcp.toml +4 -4
- package/{configs → ide/configs}/continue-mcp.yaml +4 -3
- package/{configs → ide/configs}/continue-skills.yaml +6 -6
- package/ide/configs/cursor-mcp.json +15 -0
- package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
- package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
- package/{configs → ide/configs}/opencode-mcp.json +2 -2
- package/{configs → ide/configs}/perplexity-mcp.json +2 -2
- package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
- package/{configs → ide/configs}/windsurf-mcp.json +3 -3
- package/{configs → ide/configs}/zed-mcp.json +2 -2
- package/{hooks → ide/hooks}/context-hook.js +9 -20
- package/ide/hooks/memory-list-skill.js +70 -0
- package/ide/hooks/memory-profile-skill.js +101 -0
- package/ide/hooks/memory-recall-skill.js +62 -0
- package/ide/hooks/memory-remember-skill.js +68 -0
- package/ide/hooks/memory-reset-skill.js +160 -0
- package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
- package/ide/integrations/langchain/README.md +106 -0
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
- package/ide/integrations/langchain/pyproject.toml +38 -0
- package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
- package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
- package/ide/integrations/langchain/tests/test_security.py +117 -0
- package/ide/integrations/llamaindex/README.md +81 -0
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
- package/ide/integrations/llamaindex/pyproject.toml +43 -0
- package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
- package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
- package/ide/integrations/llamaindex/tests/test_security.py +241 -0
- package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
- package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
- package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
- package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
- package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
- package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
- package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
- package/package.json +13 -22
- package/pyproject.toml +85 -0
- package/scripts/build-dmg.sh +417 -0
- package/scripts/install-skills.ps1 +334 -0
- package/scripts/postinstall.js +2 -2
- package/scripts/start-dashboard.ps1 +52 -0
- package/scripts/start-dashboard.sh +41 -0
- package/scripts/sync-wiki.ps1 +127 -0
- package/scripts/sync-wiki.sh +82 -0
- package/scripts/test-dmg.sh +161 -0
- package/scripts/test-npm-package.ps1 +252 -0
- package/scripts/test-npm-package.sh +207 -0
- package/scripts/verify-install.ps1 +294 -0
- package/scripts/verify-install.sh +266 -0
- package/src/superlocalmemory/__init__.py +0 -0
- package/src/superlocalmemory/attribution/__init__.py +9 -0
- package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
- package/src/superlocalmemory/attribution/signer.py +153 -0
- package/src/superlocalmemory/attribution/watermark.py +189 -0
- package/src/superlocalmemory/cli/__init__.py +5 -0
- package/src/superlocalmemory/cli/commands.py +245 -0
- package/src/superlocalmemory/cli/main.py +89 -0
- package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
- package/src/superlocalmemory/cli/post_install.py +99 -0
- package/src/superlocalmemory/cli/setup_wizard.py +129 -0
- package/src/superlocalmemory/compliance/__init__.py +0 -0
- package/src/superlocalmemory/compliance/abac.py +204 -0
- package/src/superlocalmemory/compliance/audit.py +314 -0
- package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
- package/src/superlocalmemory/compliance/gdpr.py +294 -0
- package/src/superlocalmemory/compliance/lifecycle.py +158 -0
- package/src/superlocalmemory/compliance/retention.py +232 -0
- package/src/superlocalmemory/compliance/scheduler.py +148 -0
- package/src/superlocalmemory/core/__init__.py +0 -0
- package/src/superlocalmemory/core/config.py +391 -0
- package/src/superlocalmemory/core/embeddings.py +293 -0
- package/src/superlocalmemory/core/engine.py +701 -0
- package/src/superlocalmemory/core/hooks.py +65 -0
- package/src/superlocalmemory/core/maintenance.py +172 -0
- package/src/superlocalmemory/core/modes.py +140 -0
- package/src/superlocalmemory/core/profiles.py +234 -0
- package/src/superlocalmemory/core/registry.py +117 -0
- package/src/superlocalmemory/dynamics/__init__.py +0 -0
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
- package/src/superlocalmemory/encoding/__init__.py +0 -0
- package/src/superlocalmemory/encoding/consolidator.py +485 -0
- package/src/superlocalmemory/encoding/emotional.py +125 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
- package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
- package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
- package/src/superlocalmemory/encoding/foresight.py +91 -0
- package/src/superlocalmemory/encoding/graph_builder.py +302 -0
- package/src/superlocalmemory/encoding/observation_builder.py +160 -0
- package/src/superlocalmemory/encoding/scene_builder.py +183 -0
- package/src/superlocalmemory/encoding/signal_inference.py +90 -0
- package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
- package/src/superlocalmemory/encoding/type_router.py +235 -0
- package/src/superlocalmemory/hooks/__init__.py +3 -0
- package/src/superlocalmemory/hooks/auto_capture.py +111 -0
- package/src/superlocalmemory/hooks/auto_recall.py +93 -0
- package/src/superlocalmemory/hooks/ide_connector.py +204 -0
- package/src/superlocalmemory/hooks/rules_engine.py +99 -0
- package/src/superlocalmemory/infra/__init__.py +3 -0
- package/src/superlocalmemory/infra/auth_middleware.py +82 -0
- package/src/superlocalmemory/infra/backup.py +317 -0
- package/src/superlocalmemory/infra/cache_manager.py +267 -0
- package/src/superlocalmemory/infra/event_bus.py +381 -0
- package/src/superlocalmemory/infra/rate_limiter.py +135 -0
- package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
- package/src/superlocalmemory/learning/__init__.py +0 -0
- package/src/superlocalmemory/learning/adaptive.py +172 -0
- package/src/superlocalmemory/learning/behavioral.py +490 -0
- package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
- package/src/superlocalmemory/learning/bootstrap.py +298 -0
- package/src/superlocalmemory/learning/cross_project.py +399 -0
- package/src/superlocalmemory/learning/database.py +376 -0
- package/src/superlocalmemory/learning/engagement.py +323 -0
- package/src/superlocalmemory/learning/features.py +138 -0
- package/src/superlocalmemory/learning/feedback.py +316 -0
- package/src/superlocalmemory/learning/outcomes.py +255 -0
- package/src/superlocalmemory/learning/project_context.py +366 -0
- package/src/superlocalmemory/learning/ranker.py +155 -0
- package/src/superlocalmemory/learning/source_quality.py +303 -0
- package/src/superlocalmemory/learning/workflows.py +309 -0
- package/src/superlocalmemory/llm/__init__.py +0 -0
- package/src/superlocalmemory/llm/backbone.py +316 -0
- package/src/superlocalmemory/math/__init__.py +0 -0
- package/src/superlocalmemory/math/fisher.py +356 -0
- package/src/superlocalmemory/math/langevin.py +398 -0
- package/src/superlocalmemory/math/sheaf.py +257 -0
- package/src/superlocalmemory/mcp/__init__.py +0 -0
- package/src/superlocalmemory/mcp/resources.py +245 -0
- package/src/superlocalmemory/mcp/server.py +61 -0
- package/src/superlocalmemory/mcp/tools.py +18 -0
- package/src/superlocalmemory/mcp/tools_core.py +305 -0
- package/src/superlocalmemory/mcp/tools_v28.py +223 -0
- package/src/superlocalmemory/mcp/tools_v3.py +286 -0
- package/src/superlocalmemory/retrieval/__init__.py +0 -0
- package/src/superlocalmemory/retrieval/agentic.py +295 -0
- package/src/superlocalmemory/retrieval/ann_index.py +223 -0
- package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
- package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
- package/src/superlocalmemory/retrieval/engine.py +390 -0
- package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
- package/src/superlocalmemory/retrieval/fusion.py +78 -0
- package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
- package/src/superlocalmemory/retrieval/reranker.py +154 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
- package/src/superlocalmemory/retrieval/strategy.py +96 -0
- package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
- package/src/superlocalmemory/server/__init__.py +1 -0
- package/src/superlocalmemory/server/api.py +248 -0
- package/src/superlocalmemory/server/routes/__init__.py +4 -0
- package/src/superlocalmemory/server/routes/agents.py +107 -0
- package/src/superlocalmemory/server/routes/backup.py +91 -0
- package/src/superlocalmemory/server/routes/behavioral.py +127 -0
- package/src/superlocalmemory/server/routes/compliance.py +160 -0
- package/src/superlocalmemory/server/routes/data_io.py +188 -0
- package/src/superlocalmemory/server/routes/events.py +183 -0
- package/src/superlocalmemory/server/routes/helpers.py +85 -0
- package/src/superlocalmemory/server/routes/learning.py +273 -0
- package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
- package/src/superlocalmemory/server/routes/memories.py +399 -0
- package/src/superlocalmemory/server/routes/profiles.py +219 -0
- package/src/superlocalmemory/server/routes/stats.py +346 -0
- package/src/superlocalmemory/server/routes/v3_api.py +365 -0
- package/src/superlocalmemory/server/routes/ws.py +82 -0
- package/src/superlocalmemory/server/security_middleware.py +57 -0
- package/src/superlocalmemory/server/ui.py +245 -0
- package/src/superlocalmemory/storage/__init__.py +0 -0
- package/src/superlocalmemory/storage/access_control.py +182 -0
- package/src/superlocalmemory/storage/database.py +594 -0
- package/src/superlocalmemory/storage/migrations.py +303 -0
- package/src/superlocalmemory/storage/models.py +406 -0
- package/src/superlocalmemory/storage/schema.py +726 -0
- package/src/superlocalmemory/storage/v2_migrator.py +317 -0
- package/src/superlocalmemory/trust/__init__.py +0 -0
- package/src/superlocalmemory/trust/gate.py +130 -0
- package/src/superlocalmemory/trust/provenance.py +124 -0
- package/src/superlocalmemory/trust/scorer.py +347 -0
- package/src/superlocalmemory/trust/signals.py +153 -0
- package/ui/index.html +278 -5
- package/ui/js/auto-settings.js +70 -0
- package/ui/js/dashboard.js +90 -0
- package/ui/js/fact-detail.js +92 -0
- package/ui/js/feedback.js +2 -2
- package/ui/js/ide-status.js +102 -0
- package/ui/js/math-health.js +98 -0
- package/ui/js/recall-lab.js +127 -0
- package/ui/js/settings.js +2 -2
- package/ui/js/trust-dashboard.js +73 -0
- package/api_server.py +0 -724
- package/bin/aider-smart +0 -72
- package/bin/superlocalmemoryv2-learning +0 -4
- package/bin/superlocalmemoryv2-list +0 -3
- package/bin/superlocalmemoryv2-patterns +0 -4
- package/bin/superlocalmemoryv2-profile +0 -3
- package/bin/superlocalmemoryv2-recall +0 -3
- package/bin/superlocalmemoryv2-remember +0 -3
- package/bin/superlocalmemoryv2-reset +0 -3
- package/bin/superlocalmemoryv2-status +0 -3
- package/configs/chatgpt-desktop-mcp.json +0 -16
- package/configs/cursor-mcp.json +0 -15
- package/hooks/memory-list-skill.js +0 -139
- package/hooks/memory-profile-skill.js +0 -273
- package/hooks/memory-recall-skill.js +0 -114
- package/hooks/memory-remember-skill.js +0 -127
- package/hooks/memory-reset-skill.js +0 -274
- package/mcp_server.py +0 -1808
- package/requirements-core.txt +0 -22
- package/requirements-learning.txt +0 -12
- package/requirements.txt +0 -12
- package/src/agent_registry.py +0 -411
- package/src/auth_middleware.py +0 -61
- package/src/auto_backup.py +0 -459
- package/src/behavioral/__init__.py +0 -49
- package/src/behavioral/behavioral_listener.py +0 -203
- package/src/behavioral/behavioral_patterns.py +0 -275
- package/src/behavioral/cross_project_transfer.py +0 -206
- package/src/behavioral/outcome_inference.py +0 -194
- package/src/behavioral/outcome_tracker.py +0 -193
- package/src/behavioral/tests/__init__.py +0 -4
- package/src/behavioral/tests/test_behavioral_integration.py +0 -108
- package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
- package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
- package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
- package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
- package/src/behavioral/tests/test_outcome_inference.py +0 -107
- package/src/behavioral/tests/test_outcome_tracker.py +0 -96
- package/src/cache_manager.py +0 -518
- package/src/compliance/__init__.py +0 -48
- package/src/compliance/abac_engine.py +0 -149
- package/src/compliance/abac_middleware.py +0 -116
- package/src/compliance/audit_db.py +0 -215
- package/src/compliance/audit_logger.py +0 -148
- package/src/compliance/retention_manager.py +0 -289
- package/src/compliance/retention_scheduler.py +0 -186
- package/src/compliance/tests/__init__.py +0 -4
- package/src/compliance/tests/test_abac_enforcement.py +0 -95
- package/src/compliance/tests/test_abac_engine.py +0 -124
- package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
- package/src/compliance/tests/test_audit_db.py +0 -123
- package/src/compliance/tests/test_audit_logger.py +0 -98
- package/src/compliance/tests/test_mcp_audit.py +0 -128
- package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
- package/src/compliance/tests/test_retention_manager.py +0 -131
- package/src/compliance/tests/test_retention_scheduler.py +0 -99
- package/src/compression/__init__.py +0 -25
- package/src/compression/cli.py +0 -150
- package/src/compression/cold_storage.py +0 -217
- package/src/compression/config.py +0 -72
- package/src/compression/orchestrator.py +0 -133
- package/src/compression/tier2_compressor.py +0 -228
- package/src/compression/tier3_compressor.py +0 -153
- package/src/compression/tier_classifier.py +0 -148
- package/src/db_connection_manager.py +0 -536
- package/src/embedding_engine.py +0 -63
- package/src/embeddings/__init__.py +0 -47
- package/src/embeddings/cache.py +0 -70
- package/src/embeddings/cli.py +0 -113
- package/src/embeddings/constants.py +0 -47
- package/src/embeddings/database.py +0 -91
- package/src/embeddings/engine.py +0 -247
- package/src/embeddings/model_loader.py +0 -145
- package/src/event_bus.py +0 -562
- package/src/graph/__init__.py +0 -36
- package/src/graph/build_helpers.py +0 -74
- package/src/graph/cli.py +0 -87
- package/src/graph/cluster_builder.py +0 -188
- package/src/graph/cluster_summary.py +0 -148
- package/src/graph/constants.py +0 -47
- package/src/graph/edge_builder.py +0 -162
- package/src/graph/entity_extractor.py +0 -95
- package/src/graph/graph_core.py +0 -226
- package/src/graph/graph_search.py +0 -231
- package/src/graph/hierarchical.py +0 -207
- package/src/graph/schema.py +0 -99
- package/src/graph_engine.py +0 -52
- package/src/hnsw_index.py +0 -628
- package/src/hybrid_search.py +0 -46
- package/src/learning/__init__.py +0 -217
- package/src/learning/adaptive_ranker.py +0 -682
- package/src/learning/bootstrap/__init__.py +0 -69
- package/src/learning/bootstrap/constants.py +0 -93
- package/src/learning/bootstrap/db_queries.py +0 -316
- package/src/learning/bootstrap/sampling.py +0 -82
- package/src/learning/bootstrap/text_utils.py +0 -71
- package/src/learning/cross_project_aggregator.py +0 -857
- package/src/learning/db/__init__.py +0 -40
- package/src/learning/db/constants.py +0 -44
- package/src/learning/db/schema.py +0 -279
- package/src/learning/engagement_tracker.py +0 -628
- package/src/learning/feature_extractor.py +0 -708
- package/src/learning/feedback_collector.py +0 -806
- package/src/learning/learning_db.py +0 -915
- package/src/learning/project_context_manager.py +0 -572
- package/src/learning/ranking/__init__.py +0 -33
- package/src/learning/ranking/constants.py +0 -84
- package/src/learning/ranking/helpers.py +0 -278
- package/src/learning/source_quality_scorer.py +0 -676
- package/src/learning/synthetic_bootstrap.py +0 -755
- package/src/learning/tests/test_adaptive_ranker.py +0 -325
- package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
- package/src/learning/tests/test_aggregator.py +0 -306
- package/src/learning/tests/test_auto_retrain_v28.py +0 -35
- package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
- package/src/learning/tests/test_feature_extractor_v28.py +0 -93
- package/src/learning/tests/test_feedback_collector.py +0 -294
- package/src/learning/tests/test_learning_db.py +0 -602
- package/src/learning/tests/test_learning_db_v28.py +0 -110
- package/src/learning/tests/test_learning_init_v28.py +0 -48
- package/src/learning/tests/test_outcome_signals.py +0 -48
- package/src/learning/tests/test_project_context.py +0 -292
- package/src/learning/tests/test_schema_migration.py +0 -319
- package/src/learning/tests/test_signal_inference.py +0 -397
- package/src/learning/tests/test_source_quality.py +0 -351
- package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
- package/src/learning/tests/test_workflow_miner.py +0 -318
- package/src/learning/workflow_pattern_miner.py +0 -655
- package/src/lifecycle/__init__.py +0 -54
- package/src/lifecycle/bounded_growth.py +0 -239
- package/src/lifecycle/compaction_engine.py +0 -226
- package/src/lifecycle/lifecycle_engine.py +0 -355
- package/src/lifecycle/lifecycle_evaluator.py +0 -257
- package/src/lifecycle/lifecycle_scheduler.py +0 -130
- package/src/lifecycle/retention_policy.py +0 -285
- package/src/lifecycle/tests/test_bounded_growth.py +0 -193
- package/src/lifecycle/tests/test_compaction.py +0 -179
- package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
- package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
- package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
- package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
- package/src/lifecycle/tests/test_mcp_compact.py +0 -149
- package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
- package/src/lifecycle/tests/test_retention_policy.py +0 -162
- package/src/mcp_tools_v28.py +0 -281
- package/src/memory/__init__.py +0 -36
- package/src/memory/cli.py +0 -205
- package/src/memory/constants.py +0 -39
- package/src/memory/helpers.py +0 -28
- package/src/memory/schema.py +0 -166
- package/src/memory-profiles.py +0 -595
- package/src/memory-reset.py +0 -491
- package/src/memory_compression.py +0 -989
- package/src/memory_store_v2.py +0 -1155
- package/src/migrate_v1_to_v2.py +0 -629
- package/src/pattern_learner.py +0 -34
- package/src/patterns/__init__.py +0 -24
- package/src/patterns/analyzers.py +0 -251
- package/src/patterns/learner.py +0 -271
- package/src/patterns/scoring.py +0 -171
- package/src/patterns/store.py +0 -225
- package/src/patterns/terminology.py +0 -140
- package/src/provenance_tracker.py +0 -312
- package/src/qualixar_attribution.py +0 -139
- package/src/qualixar_watermark.py +0 -78
- package/src/query_optimizer.py +0 -511
- package/src/rate_limiter.py +0 -83
- package/src/search/__init__.py +0 -20
- package/src/search/cli.py +0 -77
- package/src/search/constants.py +0 -26
- package/src/search/engine.py +0 -241
- package/src/search/fusion.py +0 -122
- package/src/search/index_loader.py +0 -114
- package/src/search/methods.py +0 -162
- package/src/search_engine_v2.py +0 -401
- package/src/setup_validator.py +0 -482
- package/src/subscription_manager.py +0 -391
- package/src/tree/__init__.py +0 -59
- package/src/tree/builder.py +0 -185
- package/src/tree/nodes.py +0 -202
- package/src/tree/queries.py +0 -257
- package/src/tree/schema.py +0 -80
- package/src/tree_manager.py +0 -19
- package/src/trust/__init__.py +0 -45
- package/src/trust/constants.py +0 -66
- package/src/trust/queries.py +0 -157
- package/src/trust/schema.py +0 -95
- package/src/trust/scorer.py +0 -299
- package/src/trust/signals.py +0 -95
- package/src/trust_scorer.py +0 -44
- package/ui/app.js +0 -1588
- package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
- package/ui/js/graph-cytoscape.js +0 -1168
- package/ui/js/graph-d3-backup.js +0 -32
- package/ui/js/graph.js +0 -32
- package/ui_server.py +0 -286
- /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
- /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
- /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
- /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
- /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
- /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
- /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
- /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
- /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
- /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
- /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
- /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
- /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
- /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
- /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
- /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
- /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
- /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
- /package/{completions → ide/completions}/slm.bash +0 -0
- /package/{completions → ide/completions}/slm.zsh +0 -0
- /package/{configs → ide/configs}/cody-commands.json +0 -0
- /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
- /package/{install.ps1 → scripts/install.ps1} +0 -0
- /package/{install.sh → scripts/install.sh} +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3 — Typed Memory Router.
|
|
6
|
+
|
|
7
|
+
Routes extracted facts to appropriate typed stores (ENGRAM pattern).
|
|
8
|
+
Typed separation gave +31 points on LoCoMo benchmark.
|
|
9
|
+
|
|
10
|
+
Mode A: all-MiniLM similarity against type templates.
|
|
11
|
+
Mode B/C: LLM classifies fact type.
|
|
12
|
+
|
|
13
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import re
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
|
|
22
|
+
from superlocalmemory.storage.models import AtomicFact, FactType, Mode
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from superlocalmemory.core.embeddings import EmbeddingService
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Type templates for Mode A (all-MiniLM classification)
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
_EPISODIC_TEMPLATES = [
|
|
34
|
+
"someone did something at a place",
|
|
35
|
+
"a person went somewhere or performed an action",
|
|
36
|
+
"an event happened on a specific date",
|
|
37
|
+
"someone traveled to a location",
|
|
38
|
+
"a meeting or gathering occurred",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
_SEMANTIC_TEMPLATES = [
|
|
42
|
+
"a person is a specific profession or role",
|
|
43
|
+
"something is a fact about the world",
|
|
44
|
+
"a place is located somewhere",
|
|
45
|
+
"an organization does something",
|
|
46
|
+
"a general knowledge statement",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
_OPINION_TEMPLATES = [
|
|
50
|
+
"someone thinks or believes something",
|
|
51
|
+
"a person likes or dislikes something",
|
|
52
|
+
"someone prefers one thing over another",
|
|
53
|
+
"a subjective judgment or evaluation",
|
|
54
|
+
"an emotional reaction to something",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
_TEMPORAL_TEMPLATES = [
|
|
58
|
+
"something will happen on a future date",
|
|
59
|
+
"an event is scheduled or planned",
|
|
60
|
+
"a deadline or due date for something",
|
|
61
|
+
"something started or will end at a time",
|
|
62
|
+
"a recurring event or appointment",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
# Keyword patterns for Mode A fallback
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
_OPINION_MARKERS = re.compile(
|
|
70
|
+
r"\b(think|believe|feel|prefer|like|love|hate|dislike|enjoy|"
|
|
71
|
+
r"opinion|seems?|probably|maybe|might|could be|i guess|"
|
|
72
|
+
r"personally|in my view|i'd say)\b",
|
|
73
|
+
re.IGNORECASE,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
_TEMPORAL_MARKERS = re.compile(
|
|
77
|
+
r"\b(scheduled|deadline|appointment|planned|tomorrow|"
|
|
78
|
+
r"next week|next month|upcoming|due date|starts?|ends?|"
|
|
79
|
+
r"will happen|going to|plan to)\b",
|
|
80
|
+
re.IGNORECASE,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
_EPISODIC_MARKERS = re.compile(
|
|
84
|
+
r"\b(went|visited|traveled|attended|met|saw|did|"
|
|
85
|
+
r"happened|occurred|took place|experienced)\b",
|
|
86
|
+
re.IGNORECASE,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class TypeRouter:
|
|
91
|
+
"""Route facts to typed stores based on content classification.
|
|
92
|
+
|
|
93
|
+
Uses embedding similarity (Mode A) or LLM (Mode B/C) to classify
|
|
94
|
+
each fact as episodic, semantic, opinion, or temporal.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
mode: Mode = Mode.A,
|
|
100
|
+
embedder: EmbeddingService | None = None,
|
|
101
|
+
llm: object | None = None,
|
|
102
|
+
) -> None:
|
|
103
|
+
self._mode = mode
|
|
104
|
+
self._embedder = embedder
|
|
105
|
+
self._llm = llm
|
|
106
|
+
self._template_embeddings: dict[FactType, list[list[float]]] | None = None
|
|
107
|
+
|
|
108
|
+
def classify(self, fact: AtomicFact) -> FactType:
|
|
109
|
+
"""Classify a fact into a typed store category."""
|
|
110
|
+
if self._mode in (Mode.B, Mode.C) and self._llm is not None:
|
|
111
|
+
return self._classify_llm(fact)
|
|
112
|
+
if self._embedder is not None:
|
|
113
|
+
return self._classify_embedding(fact)
|
|
114
|
+
return self._classify_keywords(fact)
|
|
115
|
+
|
|
116
|
+
def route_facts(self, facts: list[AtomicFact]) -> list[AtomicFact]:
|
|
117
|
+
"""Classify and update fact_type for a batch of facts."""
|
|
118
|
+
result = []
|
|
119
|
+
for fact in facts:
|
|
120
|
+
classified_type = self.classify(fact)
|
|
121
|
+
# Create new fact with updated type (immutability pattern)
|
|
122
|
+
updated = AtomicFact(
|
|
123
|
+
fact_id=fact.fact_id,
|
|
124
|
+
memory_id=fact.memory_id,
|
|
125
|
+
profile_id=fact.profile_id,
|
|
126
|
+
content=fact.content,
|
|
127
|
+
fact_type=classified_type,
|
|
128
|
+
entities=fact.entities,
|
|
129
|
+
canonical_entities=fact.canonical_entities,
|
|
130
|
+
observation_date=fact.observation_date,
|
|
131
|
+
referenced_date=fact.referenced_date,
|
|
132
|
+
interval_start=fact.interval_start,
|
|
133
|
+
interval_end=fact.interval_end,
|
|
134
|
+
confidence=fact.confidence,
|
|
135
|
+
importance=fact.importance,
|
|
136
|
+
evidence_count=fact.evidence_count,
|
|
137
|
+
source_turn_ids=fact.source_turn_ids,
|
|
138
|
+
session_id=fact.session_id,
|
|
139
|
+
embedding=fact.embedding,
|
|
140
|
+
fisher_mean=fact.fisher_mean,
|
|
141
|
+
fisher_variance=fact.fisher_variance,
|
|
142
|
+
emotional_valence=fact.emotional_valence,
|
|
143
|
+
emotional_arousal=fact.emotional_arousal,
|
|
144
|
+
signal_type=fact.signal_type,
|
|
145
|
+
created_at=fact.created_at,
|
|
146
|
+
)
|
|
147
|
+
result.append(updated)
|
|
148
|
+
return result
|
|
149
|
+
|
|
150
|
+
# -- Classification strategies -----------------------------------------
|
|
151
|
+
|
|
152
|
+
def _classify_keywords(self, fact: AtomicFact) -> FactType:
|
|
153
|
+
"""Keyword-based classification (fastest, lowest quality)."""
|
|
154
|
+
text = fact.content
|
|
155
|
+
|
|
156
|
+
if _OPINION_MARKERS.search(text):
|
|
157
|
+
return FactType.OPINION
|
|
158
|
+
if _TEMPORAL_MARKERS.search(text):
|
|
159
|
+
return FactType.TEMPORAL
|
|
160
|
+
if _EPISODIC_MARKERS.search(text):
|
|
161
|
+
return FactType.EPISODIC
|
|
162
|
+
return FactType.SEMANTIC
|
|
163
|
+
|
|
164
|
+
def _classify_embedding(self, fact: AtomicFact) -> FactType:
|
|
165
|
+
"""Embedding similarity against type templates (Mode A)."""
|
|
166
|
+
if self._embedder is None:
|
|
167
|
+
return self._classify_keywords(fact)
|
|
168
|
+
|
|
169
|
+
if self._template_embeddings is None:
|
|
170
|
+
self._build_template_embeddings()
|
|
171
|
+
|
|
172
|
+
assert self._template_embeddings is not None
|
|
173
|
+
fact_emb = self._embedder.embed(fact.content)
|
|
174
|
+
|
|
175
|
+
best_type = FactType.SEMANTIC
|
|
176
|
+
best_score = -1.0
|
|
177
|
+
|
|
178
|
+
for ftype, template_embs in self._template_embeddings.items():
|
|
179
|
+
avg_sim = sum(
|
|
180
|
+
_cosine(fact_emb, t) for t in template_embs
|
|
181
|
+
) / max(len(template_embs), 1)
|
|
182
|
+
if avg_sim > best_score:
|
|
183
|
+
best_score = avg_sim
|
|
184
|
+
best_type = ftype
|
|
185
|
+
|
|
186
|
+
return best_type
|
|
187
|
+
|
|
188
|
+
def _classify_llm(self, fact: AtomicFact) -> FactType:
|
|
189
|
+
"""LLM-based classification (Mode B/C, highest quality)."""
|
|
190
|
+
if self._llm is None:
|
|
191
|
+
return self._classify_embedding(fact)
|
|
192
|
+
|
|
193
|
+
prompt = (
|
|
194
|
+
f"Classify this fact into exactly one category.\n"
|
|
195
|
+
f"Fact: \"{fact.content}\"\n"
|
|
196
|
+
f"Categories:\n"
|
|
197
|
+
f"- episodic: An event that happened (who did what when)\n"
|
|
198
|
+
f"- semantic: A general fact about the world (X is Y)\n"
|
|
199
|
+
f"- opinion: A subjective belief or preference\n"
|
|
200
|
+
f"- temporal: A scheduled/planned future event\n"
|
|
201
|
+
f"Reply with ONLY the category name (one word)."
|
|
202
|
+
)
|
|
203
|
+
try:
|
|
204
|
+
response = self._llm.generate(prompt).strip().lower()
|
|
205
|
+
type_map = {
|
|
206
|
+
"episodic": FactType.EPISODIC,
|
|
207
|
+
"semantic": FactType.SEMANTIC,
|
|
208
|
+
"opinion": FactType.OPINION,
|
|
209
|
+
"temporal": FactType.TEMPORAL,
|
|
210
|
+
}
|
|
211
|
+
return type_map.get(response, FactType.SEMANTIC)
|
|
212
|
+
except Exception:
|
|
213
|
+
logger.warning("LLM classification failed, falling back to keywords")
|
|
214
|
+
return self._classify_keywords(fact)
|
|
215
|
+
|
|
216
|
+
def _build_template_embeddings(self) -> None:
|
|
217
|
+
"""Pre-compute embeddings for type templates."""
|
|
218
|
+
if self._embedder is None:
|
|
219
|
+
return
|
|
220
|
+
self._template_embeddings = {
|
|
221
|
+
FactType.EPISODIC: self._embedder.embed_batch(_EPISODIC_TEMPLATES),
|
|
222
|
+
FactType.SEMANTIC: self._embedder.embed_batch(_SEMANTIC_TEMPLATES),
|
|
223
|
+
FactType.OPINION: self._embedder.embed_batch(_OPINION_TEMPLATES),
|
|
224
|
+
FactType.TEMPORAL: self._embedder.embed_batch(_TEMPORAL_TEMPLATES),
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _cosine(a: list[float], b: list[float]) -> float:
|
|
229
|
+
"""Cosine similarity between two vectors."""
|
|
230
|
+
dot = sum(x * y for x, y in zip(a, b))
|
|
231
|
+
norm_a = sum(x * x for x in a) ** 0.5
|
|
232
|
+
norm_b = sum(x * x for x in b) ** 0.5
|
|
233
|
+
if norm_a == 0 or norm_b == 0:
|
|
234
|
+
return 0.0
|
|
235
|
+
return dot / (norm_a * norm_b)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""Auto-capture — detect and store important information automatically."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import re
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Patterns that indicate capture-worthy content
|
|
17
|
+
_DECISION_PATTERNS = [
|
|
18
|
+
r"(?i)\b(decided|chose|picked|selected|went with|using|switched to)\b",
|
|
19
|
+
r"(?i)\b(because|reason|rationale|due to|since)\b.*\b(chose|use|prefer)\b",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
_BUG_PATTERNS = [
|
|
23
|
+
r"(?i)\b(fixed|resolved|solved|root cause|bug|issue|error)\b",
|
|
24
|
+
r"(?i)\b(the (?:fix|solution|problem) (?:was|is))\b",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
_PREFERENCE_PATTERNS = [
|
|
28
|
+
r"(?i)\b(prefer|always use|never use|I like|I hate|don't like)\b",
|
|
29
|
+
r"(?i)\b(convention|standard|style|pattern)\b.*\b(is|should be|must be)\b",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class CaptureDecision:
|
|
35
|
+
"""Result of evaluating content for auto-capture."""
|
|
36
|
+
|
|
37
|
+
capture: bool
|
|
38
|
+
confidence: float
|
|
39
|
+
category: str # "decision", "bug", "preference", "session_summary", "none"
|
|
40
|
+
reason: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AutoCapture:
|
|
44
|
+
"""Detect and classify content for automatic storage."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, engine=None, config: dict | None = None):
|
|
47
|
+
self._engine = engine
|
|
48
|
+
self._config = config or {}
|
|
49
|
+
self._enabled = self._config.get("enabled", True)
|
|
50
|
+
self._min_confidence = self._config.get("min_confidence", 0.5)
|
|
51
|
+
self._capture_decisions = self._config.get("capture_decisions", True)
|
|
52
|
+
self._capture_bugs = self._config.get("capture_bugs", True)
|
|
53
|
+
self._capture_preferences = self._config.get("capture_preferences", True)
|
|
54
|
+
|
|
55
|
+
def evaluate(self, content: str) -> CaptureDecision:
|
|
56
|
+
"""Evaluate whether content should be auto-captured.
|
|
57
|
+
|
|
58
|
+
Returns a CaptureDecision with capture=True/False,
|
|
59
|
+
confidence score, and category.
|
|
60
|
+
"""
|
|
61
|
+
if not self._enabled:
|
|
62
|
+
return CaptureDecision(False, 0.0, "none", "auto-capture disabled")
|
|
63
|
+
|
|
64
|
+
if len(content.strip()) < 20:
|
|
65
|
+
return CaptureDecision(False, 0.0, "none", "content too short")
|
|
66
|
+
|
|
67
|
+
# Check for decisions
|
|
68
|
+
if self._capture_decisions:
|
|
69
|
+
score = self._match_patterns(content, _DECISION_PATTERNS)
|
|
70
|
+
if score >= self._min_confidence:
|
|
71
|
+
return CaptureDecision(True, score, "decision", "decision pattern detected")
|
|
72
|
+
|
|
73
|
+
# Check for bug fixes
|
|
74
|
+
if self._capture_bugs:
|
|
75
|
+
score = self._match_patterns(content, _BUG_PATTERNS)
|
|
76
|
+
if score >= self._min_confidence:
|
|
77
|
+
return CaptureDecision(True, score, "bug", "bug fix pattern detected")
|
|
78
|
+
|
|
79
|
+
# Check for preferences
|
|
80
|
+
if self._capture_preferences:
|
|
81
|
+
score = self._match_patterns(content, _PREFERENCE_PATTERNS)
|
|
82
|
+
if score >= self._min_confidence:
|
|
83
|
+
return CaptureDecision(True, score, "preference", "preference pattern detected")
|
|
84
|
+
|
|
85
|
+
return CaptureDecision(False, 0.0, "none", "no patterns matched")
|
|
86
|
+
|
|
87
|
+
def capture(self, content: str, category: str = "", metadata: dict | None = None) -> bool:
|
|
88
|
+
"""Store content via engine if auto-capture decides to."""
|
|
89
|
+
if not self._engine:
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
meta = metadata or {}
|
|
94
|
+
meta["source"] = "auto-capture"
|
|
95
|
+
meta["category"] = category
|
|
96
|
+
fact_ids = self._engine.store(content, metadata=meta)
|
|
97
|
+
return len(fact_ids) > 0
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
logger.debug("Auto-capture store failed: %s", exc)
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
def _match_patterns(self, content: str, patterns: list[str]) -> float:
|
|
103
|
+
"""Match content against regex patterns. Returns confidence 0.0-1.0."""
|
|
104
|
+
matches = sum(1 for p in patterns if re.search(p, content))
|
|
105
|
+
if matches == 0:
|
|
106
|
+
return 0.0
|
|
107
|
+
return min(1.0, 0.5 + (matches * 0.25))
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def enabled(self) -> bool:
|
|
111
|
+
return self._enabled
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""Auto-recall — inject relevant memories into AI context automatically."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AutoRecall:
|
|
16
|
+
"""Automatically recalls relevant context for AI sessions.
|
|
17
|
+
|
|
18
|
+
Called at session start or before each prompt to inject
|
|
19
|
+
relevant memories without user intervention.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, engine=None, config: dict | None = None):
|
|
23
|
+
self._engine = engine
|
|
24
|
+
self._config = config or {}
|
|
25
|
+
self._enabled = self._config.get("enabled", True)
|
|
26
|
+
self._max_memories = self._config.get("max_memories_injected", 10)
|
|
27
|
+
self._threshold = self._config.get("relevance_threshold", 0.3)
|
|
28
|
+
|
|
29
|
+
def get_session_context(self, project_path: str = "", query: str = "") -> str:
|
|
30
|
+
"""Get relevant context for a session or query.
|
|
31
|
+
|
|
32
|
+
Returns a formatted string of relevant memories suitable
|
|
33
|
+
for injection into an AI's system prompt.
|
|
34
|
+
"""
|
|
35
|
+
if not self._enabled or not self._engine:
|
|
36
|
+
return ""
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
# Build query from project path or explicit query
|
|
40
|
+
search_query = query or f"project context {project_path}"
|
|
41
|
+
response = self._engine.recall(search_query, limit=self._max_memories)
|
|
42
|
+
|
|
43
|
+
if not response.results:
|
|
44
|
+
return ""
|
|
45
|
+
|
|
46
|
+
# Filter by relevance threshold
|
|
47
|
+
relevant = [r for r in response.results if r.score >= self._threshold]
|
|
48
|
+
|
|
49
|
+
if not relevant:
|
|
50
|
+
return ""
|
|
51
|
+
|
|
52
|
+
# Format for injection
|
|
53
|
+
lines = ["# Relevant Memory Context", ""]
|
|
54
|
+
for r in relevant[:self._max_memories]:
|
|
55
|
+
lines.append(f"- {r.fact.content[:200]}")
|
|
56
|
+
|
|
57
|
+
return "\n".join(lines)
|
|
58
|
+
except Exception as exc:
|
|
59
|
+
logger.debug("Auto-recall failed: %s", exc)
|
|
60
|
+
return ""
|
|
61
|
+
|
|
62
|
+
def get_query_context(self, query: str) -> list[dict]:
|
|
63
|
+
"""Get relevant memories for a specific query.
|
|
64
|
+
|
|
65
|
+
Returns structured data (not formatted string) for MCP tools.
|
|
66
|
+
"""
|
|
67
|
+
if not self._enabled or not self._engine:
|
|
68
|
+
return []
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
response = self._engine.recall(query, limit=self._max_memories)
|
|
72
|
+
results = []
|
|
73
|
+
for r in response.results:
|
|
74
|
+
if r.score >= self._threshold:
|
|
75
|
+
results.append({
|
|
76
|
+
"fact_id": r.fact.fact_id,
|
|
77
|
+
"content": r.fact.content[:300],
|
|
78
|
+
"score": round(r.score, 3),
|
|
79
|
+
})
|
|
80
|
+
return results
|
|
81
|
+
except Exception as exc:
|
|
82
|
+
logger.debug("Auto-recall query failed: %s", exc)
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def enabled(self) -> bool:
|
|
87
|
+
return self._enabled
|
|
88
|
+
|
|
89
|
+
def enable(self) -> None:
|
|
90
|
+
self._enabled = True
|
|
91
|
+
|
|
92
|
+
def disable(self) -> None:
|
|
93
|
+
self._enabled = False
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""IDE connector — detect installed IDEs and generate SLM integration configs.
|
|
6
|
+
|
|
7
|
+
Supports: Claude Code, Cursor, VS Code/Copilot, Windsurf, Gemini CLI,
|
|
8
|
+
JetBrains IDEs, Continue.dev, Zed, Aider.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# SLM marker to detect our config sections (for idempotency)
|
|
21
|
+
SLM_MARKER = "## SuperLocalMemory"
|
|
22
|
+
SLM_MARKER_START = "<!-- SLM-START -->"
|
|
23
|
+
SLM_MARKER_END = "<!-- SLM-END -->"
|
|
24
|
+
|
|
25
|
+
# Known IDE config paths (relative to home directory)
|
|
26
|
+
IDE_CONFIGS: dict[str, dict[str, Any]] = {
|
|
27
|
+
"claude_code": {
|
|
28
|
+
"name": "Claude Code",
|
|
29
|
+
"detect_paths": [".claude"],
|
|
30
|
+
"config_files": [".claude/CLAUDE.md"],
|
|
31
|
+
"type": "markdown_append",
|
|
32
|
+
},
|
|
33
|
+
"cursor": {
|
|
34
|
+
"name": "Cursor",
|
|
35
|
+
"detect_paths": [".cursor"],
|
|
36
|
+
"config_files": [".cursorrules"],
|
|
37
|
+
"type": "markdown_append",
|
|
38
|
+
},
|
|
39
|
+
"vscode": {
|
|
40
|
+
"name": "VS Code / Copilot",
|
|
41
|
+
"detect_paths": [".vscode", "Library/Application Support/Code"],
|
|
42
|
+
"config_files": [".github/copilot-instructions.md"],
|
|
43
|
+
"type": "markdown_append",
|
|
44
|
+
},
|
|
45
|
+
"windsurf": {
|
|
46
|
+
"name": "Windsurf",
|
|
47
|
+
"detect_paths": [".codeium/windsurf"],
|
|
48
|
+
"config_files": [".windsurfrules"],
|
|
49
|
+
"type": "markdown_append",
|
|
50
|
+
},
|
|
51
|
+
"gemini": {
|
|
52
|
+
"name": "Gemini CLI",
|
|
53
|
+
"detect_paths": [".gemini"],
|
|
54
|
+
"config_files": ["GEMINI.md"],
|
|
55
|
+
"type": "markdown_append",
|
|
56
|
+
},
|
|
57
|
+
"continue_dev": {
|
|
58
|
+
"name": "Continue.dev",
|
|
59
|
+
"detect_paths": [".continue"],
|
|
60
|
+
"config_files": [".continue/config.json"],
|
|
61
|
+
"type": "json_merge",
|
|
62
|
+
},
|
|
63
|
+
"zed": {
|
|
64
|
+
"name": "Zed Editor",
|
|
65
|
+
"detect_paths": [".config/zed"],
|
|
66
|
+
"config_files": [".config/zed/settings.json"],
|
|
67
|
+
"type": "json_merge",
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Template for markdown-based IDE rules
|
|
72
|
+
MARKDOWN_TEMPLATE = """
|
|
73
|
+
{marker_start}
|
|
74
|
+
{marker}
|
|
75
|
+
- Before responding to questions, check SuperLocalMemory for relevant context using the `recall` tool
|
|
76
|
+
- After making decisions, fixing bugs, or learning preferences, store them using the `remember` tool
|
|
77
|
+
- On session start, load project context from SuperLocalMemory
|
|
78
|
+
- Available tools: remember, recall, search, fetch, get_status, set_mode, health
|
|
79
|
+
- Documentation: https://superlocalmemory.com
|
|
80
|
+
{marker_end}
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class IDEConnector:
|
|
85
|
+
"""Detect installed IDEs and generate SLM integration configs."""
|
|
86
|
+
|
|
87
|
+
def __init__(self, home: Path | None = None) -> None:
|
|
88
|
+
self._home = home or Path.home()
|
|
89
|
+
|
|
90
|
+
def detect_ides(self) -> dict[str, bool]:
|
|
91
|
+
"""Detect which IDEs are installed.
|
|
92
|
+
|
|
93
|
+
Returns a dict mapping IDE id to whether it was detected.
|
|
94
|
+
"""
|
|
95
|
+
result: dict[str, bool] = {}
|
|
96
|
+
for ide_id, config in IDE_CONFIGS.items():
|
|
97
|
+
detected = any(
|
|
98
|
+
(self._home / p).exists() for p in config["detect_paths"]
|
|
99
|
+
)
|
|
100
|
+
result[ide_id] = detected
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
def connect(self, ide_id: str) -> bool:
|
|
104
|
+
"""Configure a specific IDE for SLM integration.
|
|
105
|
+
|
|
106
|
+
Returns True if configuration was written successfully.
|
|
107
|
+
Idempotent — safe to run multiple times.
|
|
108
|
+
Does NOT overwrite existing user rules.
|
|
109
|
+
"""
|
|
110
|
+
if ide_id not in IDE_CONFIGS:
|
|
111
|
+
logger.warning("Unknown IDE: %s", ide_id)
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
config = IDE_CONFIGS[ide_id]
|
|
115
|
+
config_type = config["type"]
|
|
116
|
+
|
|
117
|
+
for config_file in config["config_files"]:
|
|
118
|
+
path = self._home / config_file
|
|
119
|
+
|
|
120
|
+
if config_type == "markdown_append":
|
|
121
|
+
return self._append_markdown(path)
|
|
122
|
+
elif config_type == "json_merge":
|
|
123
|
+
return self._merge_json(path)
|
|
124
|
+
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def connect_all(self) -> dict[str, str]:
|
|
128
|
+
"""Detect and configure all installed IDEs.
|
|
129
|
+
|
|
130
|
+
Returns dict of {ide_id: status} where status is
|
|
131
|
+
"connected", "not_installed", or "error".
|
|
132
|
+
"""
|
|
133
|
+
detected = self.detect_ides()
|
|
134
|
+
results: dict[str, str] = {}
|
|
135
|
+
|
|
136
|
+
for ide_id, is_installed in detected.items():
|
|
137
|
+
if not is_installed:
|
|
138
|
+
results[ide_id] = "not_installed"
|
|
139
|
+
continue
|
|
140
|
+
try:
|
|
141
|
+
success = self.connect(ide_id)
|
|
142
|
+
results[ide_id] = "connected" if success else "error"
|
|
143
|
+
except Exception as exc:
|
|
144
|
+
logger.debug("Failed to connect %s: %s", ide_id, exc)
|
|
145
|
+
results[ide_id] = "error"
|
|
146
|
+
|
|
147
|
+
return results
|
|
148
|
+
|
|
149
|
+
def get_status(self) -> list[dict[str, Any]]:
|
|
150
|
+
"""Get connection status for all known IDEs."""
|
|
151
|
+
detected = self.detect_ides()
|
|
152
|
+
status: list[dict[str, Any]] = []
|
|
153
|
+
for ide_id, config in IDE_CONFIGS.items():
|
|
154
|
+
status.append({
|
|
155
|
+
"id": ide_id,
|
|
156
|
+
"name": config["name"],
|
|
157
|
+
"installed": detected.get(ide_id, False),
|
|
158
|
+
"config_path": str(self._home / config["config_files"][0]),
|
|
159
|
+
})
|
|
160
|
+
return status
|
|
161
|
+
|
|
162
|
+
def _append_markdown(self, path: Path) -> bool:
|
|
163
|
+
"""Append SLM section to a markdown file. Idempotent."""
|
|
164
|
+
content = ""
|
|
165
|
+
if path.exists():
|
|
166
|
+
content = path.read_text()
|
|
167
|
+
|
|
168
|
+
# Check if already configured (idempotent)
|
|
169
|
+
if SLM_MARKER in content:
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
# Append SLM section
|
|
173
|
+
section = MARKDOWN_TEMPLATE.format(
|
|
174
|
+
marker=SLM_MARKER,
|
|
175
|
+
marker_start=SLM_MARKER_START,
|
|
176
|
+
marker_end=SLM_MARKER_END,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
path.write_text(content + "\n" + section)
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
def _merge_json(self, path: Path) -> bool:
|
|
184
|
+
"""Merge SLM config into a JSON config file."""
|
|
185
|
+
data: dict[str, Any] = {}
|
|
186
|
+
if path.exists():
|
|
187
|
+
try:
|
|
188
|
+
data = json.loads(path.read_text())
|
|
189
|
+
except json.JSONDecodeError:
|
|
190
|
+
data = {}
|
|
191
|
+
|
|
192
|
+
# Add MCP server config
|
|
193
|
+
if "mcpServers" not in data:
|
|
194
|
+
data["mcpServers"] = {}
|
|
195
|
+
|
|
196
|
+
data["mcpServers"]["superlocalmemory"] = {
|
|
197
|
+
"command": "python3",
|
|
198
|
+
"args": ["-m", "superlocalmemory.mcp.server"],
|
|
199
|
+
"enabled": True,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
203
|
+
path.write_text(json.dumps(data, indent=2))
|
|
204
|
+
return True
|