superlocalmemory 2.8.6 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +9 -1
- package/NOTICE +63 -0
- package/README.md +165 -480
- package/bin/slm +17 -449
- package/bin/slm-npm +1 -1
- 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
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
3
|
-
"""Tests for v2.8 learning.db schema extensions — outcome and behavioral tables.
|
|
4
|
-
"""
|
|
5
|
-
import pytest
|
|
6
|
-
import json
|
|
7
|
-
import sys
|
|
8
|
-
import os
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture(autouse=True)
|
|
15
|
-
def reset_singleton():
|
|
16
|
-
from learning.learning_db import LearningDB
|
|
17
|
-
LearningDB.reset_instance()
|
|
18
|
-
yield
|
|
19
|
-
LearningDB.reset_instance()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@pytest.fixture
|
|
23
|
-
def learning_db(tmp_path):
|
|
24
|
-
from learning.learning_db import LearningDB
|
|
25
|
-
db_path = tmp_path / "learning.db"
|
|
26
|
-
return LearningDB(db_path=db_path)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class TestActionOutcomesTable:
|
|
30
|
-
def test_table_exists(self, learning_db):
|
|
31
|
-
conn = learning_db._get_connection()
|
|
32
|
-
tables = [r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()]
|
|
33
|
-
conn.close()
|
|
34
|
-
assert "action_outcomes" in tables
|
|
35
|
-
|
|
36
|
-
def test_store_outcome(self, learning_db):
|
|
37
|
-
oid = learning_db.store_outcome([1, 2], "success", action_type="code_written", project="myproject")
|
|
38
|
-
assert isinstance(oid, int)
|
|
39
|
-
assert oid > 0
|
|
40
|
-
|
|
41
|
-
def test_get_outcomes(self, learning_db):
|
|
42
|
-
learning_db.store_outcome([1], "success", project="proj1")
|
|
43
|
-
learning_db.store_outcome([2], "failure", project="proj1")
|
|
44
|
-
results = learning_db.get_outcomes(project="proj1")
|
|
45
|
-
assert len(results) == 2
|
|
46
|
-
|
|
47
|
-
def test_get_outcomes_by_memory_id(self, learning_db):
|
|
48
|
-
learning_db.store_outcome([1, 2], "success")
|
|
49
|
-
learning_db.store_outcome([3], "failure")
|
|
50
|
-
results = learning_db.get_outcomes(memory_id=1)
|
|
51
|
-
assert len(results) == 1
|
|
52
|
-
assert 1 in results[0]["memory_ids"]
|
|
53
|
-
|
|
54
|
-
def test_outcome_has_profile(self, learning_db):
|
|
55
|
-
learning_db.store_outcome([1], "success")
|
|
56
|
-
results = learning_db.get_outcomes()
|
|
57
|
-
assert results[0]["profile"] == "default"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class TestBehavioralPatternsTable:
|
|
61
|
-
def test_table_exists(self, learning_db):
|
|
62
|
-
conn = learning_db._get_connection()
|
|
63
|
-
tables = [r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()]
|
|
64
|
-
conn.close()
|
|
65
|
-
assert "behavioral_patterns" in tables
|
|
66
|
-
|
|
67
|
-
def test_store_pattern(self, learning_db):
|
|
68
|
-
pid = learning_db.store_behavioral_pattern("tag_success", "python", success_rate=0.85, evidence_count=10, confidence=0.8)
|
|
69
|
-
assert isinstance(pid, int)
|
|
70
|
-
|
|
71
|
-
def test_get_patterns(self, learning_db):
|
|
72
|
-
learning_db.store_behavioral_pattern("tag_success", "python", confidence=0.8)
|
|
73
|
-
learning_db.store_behavioral_pattern("tag_success", "javascript", confidence=0.6)
|
|
74
|
-
results = learning_db.get_behavioral_patterns(pattern_type="tag_success")
|
|
75
|
-
assert len(results) == 2
|
|
76
|
-
|
|
77
|
-
def test_get_patterns_min_confidence(self, learning_db):
|
|
78
|
-
learning_db.store_behavioral_pattern("tag_success", "python", confidence=0.8)
|
|
79
|
-
learning_db.store_behavioral_pattern("tag_success", "javascript", confidence=0.3)
|
|
80
|
-
results = learning_db.get_behavioral_patterns(min_confidence=0.5)
|
|
81
|
-
assert len(results) == 1
|
|
82
|
-
assert results[0]["pattern_key"] == "python"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class TestCrossProjectTable:
|
|
86
|
-
def test_table_exists(self, learning_db):
|
|
87
|
-
conn = learning_db._get_connection()
|
|
88
|
-
tables = [r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()]
|
|
89
|
-
conn.close()
|
|
90
|
-
assert "cross_project_behaviors" in tables
|
|
91
|
-
|
|
92
|
-
def test_store_transfer(self, learning_db):
|
|
93
|
-
pid = learning_db.store_behavioral_pattern("tag_success", "python", confidence=0.8)
|
|
94
|
-
tid = learning_db.store_cross_project("project_a", "project_b", pid, confidence=0.7)
|
|
95
|
-
assert isinstance(tid, int)
|
|
96
|
-
|
|
97
|
-
def test_get_transfers(self, learning_db):
|
|
98
|
-
pid = learning_db.store_behavioral_pattern("tag_success", "python", confidence=0.8)
|
|
99
|
-
learning_db.store_cross_project("proj_a", "proj_b", pid, confidence=0.7)
|
|
100
|
-
results = learning_db.get_cross_project_transfers(source_project="proj_a")
|
|
101
|
-
assert len(results) == 1
|
|
102
|
-
assert results[0]["target_project"] == "proj_b"
|
|
103
|
-
|
|
104
|
-
def test_existing_tables_untouched(self, learning_db):
|
|
105
|
-
"""Existing 6 tables should still exist."""
|
|
106
|
-
conn = learning_db._get_connection()
|
|
107
|
-
tables = {r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
|
|
108
|
-
conn.close()
|
|
109
|
-
for expected in ["transferable_patterns", "workflow_patterns", "ranking_feedback", "ranking_models", "source_quality", "engagement_metrics"]:
|
|
110
|
-
assert expected in tables
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
3
|
-
"""Tests for unified learning status with v2.8 engines.
|
|
4
|
-
"""
|
|
5
|
-
import sys
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TestUnifiedLearningStatus:
|
|
11
|
-
def test_get_status_has_v28_engines(self):
|
|
12
|
-
"""Status should include lifecycle, behavioral, compliance info."""
|
|
13
|
-
from learning import get_status
|
|
14
|
-
status = get_status()
|
|
15
|
-
assert "v28_engines" in status
|
|
16
|
-
|
|
17
|
-
def test_v28_engines_structure(self):
|
|
18
|
-
from learning import get_status
|
|
19
|
-
status = get_status()
|
|
20
|
-
engines = status["v28_engines"]
|
|
21
|
-
assert "lifecycle" in engines
|
|
22
|
-
assert "behavioral" in engines
|
|
23
|
-
assert "compliance" in engines
|
|
24
|
-
|
|
25
|
-
def test_lifecycle_status_included(self):
|
|
26
|
-
from learning import get_status
|
|
27
|
-
status = get_status()
|
|
28
|
-
lifecycle = status["v28_engines"]["lifecycle"]
|
|
29
|
-
assert "available" in lifecycle
|
|
30
|
-
|
|
31
|
-
def test_behavioral_status_included(self):
|
|
32
|
-
from learning import get_status
|
|
33
|
-
status = get_status()
|
|
34
|
-
behavioral = status["v28_engines"]["behavioral"]
|
|
35
|
-
assert "available" in behavioral
|
|
36
|
-
|
|
37
|
-
def test_compliance_status_included(self):
|
|
38
|
-
from learning import get_status
|
|
39
|
-
status = get_status()
|
|
40
|
-
compliance = status["v28_engines"]["compliance"]
|
|
41
|
-
assert "available" in compliance
|
|
42
|
-
|
|
43
|
-
def test_graceful_when_engines_unavailable(self):
|
|
44
|
-
"""Status should not crash even if engine imports fail."""
|
|
45
|
-
from learning import get_status
|
|
46
|
-
status = get_status()
|
|
47
|
-
# Should always return a dict with v28_engines
|
|
48
|
-
assert isinstance(status["v28_engines"], dict)
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
3
|
-
"""Tests for v2.8 outcome signal types in feedback collector.
|
|
4
|
-
"""
|
|
5
|
-
import pytest
|
|
6
|
-
import sys
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestOutcomeSignalTypes:
|
|
13
|
-
"""Verify outcome signal types are registered and have correct values."""
|
|
14
|
-
|
|
15
|
-
def test_outcome_success_registered(self):
|
|
16
|
-
from learning.feedback_collector import FeedbackCollector
|
|
17
|
-
assert "outcome_success" in FeedbackCollector.SIGNAL_VALUES
|
|
18
|
-
assert FeedbackCollector.SIGNAL_VALUES["outcome_success"] == 1.0
|
|
19
|
-
|
|
20
|
-
def test_outcome_partial_registered(self):
|
|
21
|
-
from learning.feedback_collector import FeedbackCollector
|
|
22
|
-
assert "outcome_partial" in FeedbackCollector.SIGNAL_VALUES
|
|
23
|
-
assert FeedbackCollector.SIGNAL_VALUES["outcome_partial"] == 0.5
|
|
24
|
-
|
|
25
|
-
def test_outcome_failure_registered(self):
|
|
26
|
-
from learning.feedback_collector import FeedbackCollector
|
|
27
|
-
assert "outcome_failure" in FeedbackCollector.SIGNAL_VALUES
|
|
28
|
-
assert FeedbackCollector.SIGNAL_VALUES["outcome_failure"] == 0.0
|
|
29
|
-
|
|
30
|
-
def test_outcome_retry_registered(self):
|
|
31
|
-
from learning.feedback_collector import FeedbackCollector
|
|
32
|
-
assert "outcome_retry" in FeedbackCollector.SIGNAL_VALUES
|
|
33
|
-
assert FeedbackCollector.SIGNAL_VALUES["outcome_retry"] == 0.2
|
|
34
|
-
|
|
35
|
-
def test_existing_signals_unchanged(self):
|
|
36
|
-
"""All 17 original signal types still present with correct values."""
|
|
37
|
-
from learning.feedback_collector import FeedbackCollector
|
|
38
|
-
SV = FeedbackCollector.SIGNAL_VALUES
|
|
39
|
-
assert SV["mcp_used_high"] == 1.0
|
|
40
|
-
assert SV["dashboard_thumbs_up"] == 1.0
|
|
41
|
-
assert SV["implicit_positive_timegap"] == 0.6
|
|
42
|
-
assert SV["passive_decay"] == 0.0
|
|
43
|
-
assert len(SV) == 21 # 17 original + 4 new
|
|
44
|
-
|
|
45
|
-
def test_total_signal_count(self):
|
|
46
|
-
"""Should have exactly 21 signal types (17 + 4)."""
|
|
47
|
-
from learning.feedback_collector import FeedbackCollector
|
|
48
|
-
assert len(FeedbackCollector.SIGNAL_VALUES) == 21
|
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
import sqlite3
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
import pytest
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# ---------------------------------------------------------------------------
|
|
11
|
-
# Fixtures
|
|
12
|
-
# ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def memory_db(tmp_path):
|
|
16
|
-
"""Create a minimal memory.db with the required schema."""
|
|
17
|
-
db_path = tmp_path / "memory.db"
|
|
18
|
-
conn = sqlite3.connect(str(db_path))
|
|
19
|
-
cursor = conn.cursor()
|
|
20
|
-
cursor.execute('''
|
|
21
|
-
CREATE TABLE IF NOT EXISTS memories (
|
|
22
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23
|
-
content TEXT NOT NULL,
|
|
24
|
-
summary TEXT,
|
|
25
|
-
project_path TEXT,
|
|
26
|
-
project_name TEXT,
|
|
27
|
-
tags TEXT DEFAULT '[]',
|
|
28
|
-
category TEXT,
|
|
29
|
-
parent_id INTEGER,
|
|
30
|
-
tree_path TEXT DEFAULT '/',
|
|
31
|
-
depth INTEGER DEFAULT 0,
|
|
32
|
-
memory_type TEXT DEFAULT 'session',
|
|
33
|
-
importance INTEGER DEFAULT 5,
|
|
34
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
35
|
-
last_accessed TIMESTAMP,
|
|
36
|
-
access_count INTEGER DEFAULT 0,
|
|
37
|
-
content_hash TEXT,
|
|
38
|
-
cluster_id INTEGER,
|
|
39
|
-
profile TEXT DEFAULT 'default',
|
|
40
|
-
created_by TEXT,
|
|
41
|
-
source_protocol TEXT,
|
|
42
|
-
trust_score REAL DEFAULT 1.0
|
|
43
|
-
)
|
|
44
|
-
''')
|
|
45
|
-
conn.commit()
|
|
46
|
-
conn.close()
|
|
47
|
-
return db_path
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@pytest.fixture
|
|
51
|
-
def pcm(memory_db):
|
|
52
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
53
|
-
return ProjectContextManager(memory_db_path=memory_db)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _insert_memories(db_path, memories):
|
|
57
|
-
conn = sqlite3.connect(str(db_path))
|
|
58
|
-
cursor = conn.cursor()
|
|
59
|
-
for m in memories:
|
|
60
|
-
cursor.execute('''
|
|
61
|
-
INSERT INTO memories (content, project_name, project_path,
|
|
62
|
-
profile, cluster_id, created_at)
|
|
63
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
64
|
-
''', (
|
|
65
|
-
m.get('content', 'test'),
|
|
66
|
-
m.get('project_name'),
|
|
67
|
-
m.get('project_path'),
|
|
68
|
-
m.get('profile', 'default'),
|
|
69
|
-
m.get('cluster_id'),
|
|
70
|
-
m.get('created_at', '2026-02-16 10:00:00'),
|
|
71
|
-
))
|
|
72
|
-
conn.commit()
|
|
73
|
-
conn.close()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# ---------------------------------------------------------------------------
|
|
77
|
-
# Path Extraction
|
|
78
|
-
# ---------------------------------------------------------------------------
|
|
79
|
-
|
|
80
|
-
class TestExtractProjectFromPath:
|
|
81
|
-
"""Test the static _extract_project_from_path method."""
|
|
82
|
-
|
|
83
|
-
def test_projects_parent(self):
|
|
84
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
85
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
86
|
-
"/Users/varun/projects/MY_PROJECT/src/main.py"
|
|
87
|
-
)
|
|
88
|
-
assert result == "MY_PROJECT"
|
|
89
|
-
|
|
90
|
-
def test_repos_parent(self):
|
|
91
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
92
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
93
|
-
"/home/dev/repos/my-app/lib/util.js"
|
|
94
|
-
)
|
|
95
|
-
assert result == "my-app"
|
|
96
|
-
|
|
97
|
-
def test_documents_parent(self):
|
|
98
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
99
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
100
|
-
"/Users/varun/Documents/AGENTIC_Official/SuperLocalMemoryV2-repo/src/learning/foo.py"
|
|
101
|
-
)
|
|
102
|
-
assert result == "SuperLocalMemoryV2-repo"
|
|
103
|
-
|
|
104
|
-
def test_workspace_services(self):
|
|
105
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
106
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
107
|
-
"/workspace/services/auth-service/index.ts"
|
|
108
|
-
)
|
|
109
|
-
assert result == "auth-service"
|
|
110
|
-
|
|
111
|
-
def test_empty_path(self):
|
|
112
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
113
|
-
assert ProjectContextManager._extract_project_from_path("") is None
|
|
114
|
-
|
|
115
|
-
def test_none_path(self):
|
|
116
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
117
|
-
assert ProjectContextManager._extract_project_from_path(None) is None
|
|
118
|
-
|
|
119
|
-
def test_short_path(self):
|
|
120
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
121
|
-
assert ProjectContextManager._extract_project_from_path("/") is None
|
|
122
|
-
|
|
123
|
-
def test_github_parent(self):
|
|
124
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
125
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
126
|
-
"/home/user/github/cool-project/README.md"
|
|
127
|
-
)
|
|
128
|
-
assert result == "cool-project"
|
|
129
|
-
|
|
130
|
-
def test_skip_dirs_not_returned(self):
|
|
131
|
-
"""Directories like src, lib, node_modules should not be project names."""
|
|
132
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
133
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
134
|
-
"/Users/dev/projects/myapp/src/lib/util.py"
|
|
135
|
-
)
|
|
136
|
-
assert result == "myapp"
|
|
137
|
-
|
|
138
|
-
def test_code_parent(self):
|
|
139
|
-
from src.learning.project_context_manager import ProjectContextManager
|
|
140
|
-
result = ProjectContextManager._extract_project_from_path(
|
|
141
|
-
"/Users/dev/code/awesome-tool/main.py"
|
|
142
|
-
)
|
|
143
|
-
assert result == "awesome-tool"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
# ---------------------------------------------------------------------------
|
|
147
|
-
# Project Detection
|
|
148
|
-
# ---------------------------------------------------------------------------
|
|
149
|
-
|
|
150
|
-
class TestDetectCurrentProject:
|
|
151
|
-
def test_with_explicit_project_tags(self, pcm, memory_db):
|
|
152
|
-
"""Signal 1: project_name tag should dominate."""
|
|
153
|
-
memories = [
|
|
154
|
-
{"content": "test", "project_name": "MyProject"},
|
|
155
|
-
{"content": "test", "project_name": "MyProject"},
|
|
156
|
-
{"content": "test", "project_name": "MyProject"},
|
|
157
|
-
]
|
|
158
|
-
recent = [
|
|
159
|
-
{"project_name": "MyProject", "project_path": None, "cluster_id": None,
|
|
160
|
-
"profile": "default", "content": "test"}
|
|
161
|
-
for _ in range(3)
|
|
162
|
-
]
|
|
163
|
-
result = pcm.detect_current_project(recent_memories=recent)
|
|
164
|
-
assert result == "MyProject"
|
|
165
|
-
|
|
166
|
-
def test_with_project_paths(self, pcm):
|
|
167
|
-
"""Signal 2: project_path analysis."""
|
|
168
|
-
recent = [
|
|
169
|
-
{"project_name": None,
|
|
170
|
-
"project_path": "/Users/dev/projects/SLM/src/main.py",
|
|
171
|
-
"cluster_id": None, "profile": "default", "content": "test"}
|
|
172
|
-
for _ in range(5)
|
|
173
|
-
]
|
|
174
|
-
result = pcm.detect_current_project(recent_memories=recent)
|
|
175
|
-
assert result == "SLM"
|
|
176
|
-
|
|
177
|
-
def test_with_profiles(self, pcm):
|
|
178
|
-
"""Signal 3: active profile as weak signal."""
|
|
179
|
-
recent = [
|
|
180
|
-
{"project_name": None, "project_path": None,
|
|
181
|
-
"cluster_id": None, "profile": "work", "content": "test"}
|
|
182
|
-
]
|
|
183
|
-
# Profile is weak (weight=1), may not win alone due to 40% threshold
|
|
184
|
-
# with just 1 memory. But adding it to existing signal helps.
|
|
185
|
-
recent_with_name = [
|
|
186
|
-
{"project_name": "work", "project_path": None,
|
|
187
|
-
"cluster_id": None, "profile": "work", "content": "test"}
|
|
188
|
-
]
|
|
189
|
-
result = pcm.detect_current_project(recent_memories=recent_with_name)
|
|
190
|
-
assert result == "work"
|
|
191
|
-
|
|
192
|
-
def test_empty_memories(self, pcm):
|
|
193
|
-
result = pcm.detect_current_project(recent_memories=[])
|
|
194
|
-
assert result is None
|
|
195
|
-
|
|
196
|
-
def test_ambiguous_results_none(self, pcm):
|
|
197
|
-
"""When no project clears 40% threshold, return None."""
|
|
198
|
-
recent = [
|
|
199
|
-
{"project_name": "A", "project_path": None, "cluster_id": None,
|
|
200
|
-
"profile": "default", "content": "test"},
|
|
201
|
-
{"project_name": "B", "project_path": None, "cluster_id": None,
|
|
202
|
-
"profile": "default", "content": "test"},
|
|
203
|
-
{"project_name": "C", "project_path": None, "cluster_id": None,
|
|
204
|
-
"profile": "default", "content": "test"},
|
|
205
|
-
]
|
|
206
|
-
# Each gets 3 points (weight 3 for project_tag), total = 9
|
|
207
|
-
# 3/9 = 33% < 40% threshold, so None
|
|
208
|
-
result = pcm.detect_current_project(recent_memories=recent)
|
|
209
|
-
assert result is None
|
|
210
|
-
|
|
211
|
-
def test_mixed_signals(self, pcm):
|
|
212
|
-
"""Both project_name and project_path pointing to same project."""
|
|
213
|
-
recent = [
|
|
214
|
-
{"project_name": "SLM", "project_path": "/projects/SLM/src/a.py",
|
|
215
|
-
"cluster_id": None, "profile": "default", "content": "test"},
|
|
216
|
-
{"project_name": "SLM", "project_path": "/projects/SLM/src/b.py",
|
|
217
|
-
"cluster_id": None, "profile": "default", "content": "test"},
|
|
218
|
-
]
|
|
219
|
-
result = pcm.detect_current_project(recent_memories=recent)
|
|
220
|
-
assert result == "SLM"
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
# ---------------------------------------------------------------------------
|
|
224
|
-
# Project Boost
|
|
225
|
-
# ---------------------------------------------------------------------------
|
|
226
|
-
|
|
227
|
-
class TestGetProjectBoost:
|
|
228
|
-
def test_match_returns_1_0(self, pcm):
|
|
229
|
-
memory = {"project_name": "MyProject"}
|
|
230
|
-
assert pcm.get_project_boost(memory, "MyProject") == 1.0
|
|
231
|
-
|
|
232
|
-
def test_case_insensitive_match(self, pcm):
|
|
233
|
-
memory = {"project_name": "myproject"}
|
|
234
|
-
assert pcm.get_project_boost(memory, "MyProject") == 1.0
|
|
235
|
-
|
|
236
|
-
def test_mismatch_returns_0_3(self, pcm):
|
|
237
|
-
memory = {"project_name": "OtherProject"}
|
|
238
|
-
assert pcm.get_project_boost(memory, "MyProject") == 0.3
|
|
239
|
-
|
|
240
|
-
def test_no_current_project_returns_0_6(self, pcm):
|
|
241
|
-
memory = {"project_name": "Anything"}
|
|
242
|
-
assert pcm.get_project_boost(memory, None) == 0.6
|
|
243
|
-
|
|
244
|
-
def test_no_project_info_returns_0_6(self, pcm):
|
|
245
|
-
memory = {"content": "no project info"}
|
|
246
|
-
assert pcm.get_project_boost(memory, "MyProject") == 0.6
|
|
247
|
-
|
|
248
|
-
def test_path_match(self, pcm):
|
|
249
|
-
memory = {"project_path": "/projects/SLM/src/main.py"}
|
|
250
|
-
assert pcm.get_project_boost(memory, "SLM") == 1.0
|
|
251
|
-
|
|
252
|
-
def test_path_mismatch(self, pcm):
|
|
253
|
-
memory = {"project_path": "/projects/OTHER/src/main.py"}
|
|
254
|
-
assert pcm.get_project_boost(memory, "SLM") == 0.3
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
# ---------------------------------------------------------------------------
|
|
258
|
-
# safe_get
|
|
259
|
-
# ---------------------------------------------------------------------------
|
|
260
|
-
|
|
261
|
-
class TestSafeGet:
|
|
262
|
-
def test_normal_value(self, pcm):
|
|
263
|
-
assert pcm._safe_get({"key": "value"}, "key") == "value"
|
|
264
|
-
|
|
265
|
-
def test_missing_key(self, pcm):
|
|
266
|
-
assert pcm._safe_get({"key": "value"}, "other") is None
|
|
267
|
-
|
|
268
|
-
def test_none_value(self, pcm):
|
|
269
|
-
assert pcm._safe_get({"key": None}, "key") is None
|
|
270
|
-
|
|
271
|
-
def test_empty_string(self, pcm):
|
|
272
|
-
assert pcm._safe_get({"key": ""}, "key") is None
|
|
273
|
-
|
|
274
|
-
def test_whitespace_string(self, pcm):
|
|
275
|
-
assert pcm._safe_get({"key": " "}, "key") is None
|
|
276
|
-
|
|
277
|
-
def test_integer_value(self, pcm):
|
|
278
|
-
assert pcm._safe_get({"key": 42}, "key") == 42
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
# ---------------------------------------------------------------------------
|
|
282
|
-
# Cache Invalidation
|
|
283
|
-
# ---------------------------------------------------------------------------
|
|
284
|
-
|
|
285
|
-
class TestCacheInvalidation:
|
|
286
|
-
def test_invalidate_cache(self, pcm):
|
|
287
|
-
# Force column cache to be populated
|
|
288
|
-
pcm._get_available_columns()
|
|
289
|
-
assert pcm._available_columns is not None
|
|
290
|
-
|
|
291
|
-
pcm.invalidate_cache()
|
|
292
|
-
assert pcm._available_columns is None
|