superlocalmemory 3.6.23 → 3.7.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/CHANGELOG.md +52 -0
- package/README.md +271 -71
- package/bin/slm-npm +43 -89
- package/ide/configs/antigravity-mcp.json +2 -2
- package/ide/configs/chatgpt-desktop-mcp.json +1 -1
- package/ide/configs/claude-desktop-mcp.json +2 -2
- package/ide/configs/windsurf-mcp.json +2 -2
- package/ide/hooks/context-hook.js +6 -2
- package/ide/hooks/post-recall-hook.js +7 -3
- package/ide/hooks/tool-event-hook.sh +2 -1
- package/package.json +18 -10
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/_GENERATED.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/pyproject.toml +40 -8
- package/scripts/postinstall-interactive.js +17 -94
- package/scripts/postinstall.js +185 -258
- package/scripts/preuninstall.js +9 -50
- package/src/superlocalmemory/__init__.py +2 -2
- package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
- package/src/superlocalmemory/attribution/signer.py +34 -19
- package/src/superlocalmemory/attribution/watermark.py +1 -1
- package/src/superlocalmemory/cli/_lazy_init.py +3 -5
- package/src/superlocalmemory/cli/commands.py +453 -191
- package/src/superlocalmemory/cli/context_commands.py +5 -4
- package/src/superlocalmemory/cli/daemon.py +282 -187
- package/src/superlocalmemory/cli/db_migrate.py +3 -1
- package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
- package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
- package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
- package/src/superlocalmemory/cli/main.py +128 -31
- package/src/superlocalmemory/cli/pending_store.py +54 -38
- package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
- package/src/superlocalmemory/cli/service_installer.py +57 -52
- package/src/superlocalmemory/cli/setup_wizard.py +142 -88
- package/src/superlocalmemory/cli/version_banner.py +2 -1
- package/src/superlocalmemory/code_graph/config.py +3 -1
- package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
- package/src/superlocalmemory/core/config.py +65 -20
- package/src/superlocalmemory/core/consolidation_engine.py +9 -7
- package/src/superlocalmemory/core/context_cache.py +56 -8
- package/src/superlocalmemory/core/derivation_lineage.py +246 -0
- package/src/superlocalmemory/core/embedding_worker.py +32 -20
- package/src/superlocalmemory/core/embeddings.py +54 -18
- package/src/superlocalmemory/core/engine.py +150 -104
- package/src/superlocalmemory/core/engine_ingestion.py +513 -0
- package/src/superlocalmemory/core/engine_wiring.py +2 -0
- package/src/superlocalmemory/core/evidence_bundle.py +526 -0
- package/src/superlocalmemory/core/fact_consolidator.py +5 -11
- package/src/superlocalmemory/core/graph_analyzer.py +2 -2
- package/src/superlocalmemory/core/health_monitor.py +4 -2
- package/src/superlocalmemory/core/ingestion_command.py +636 -0
- package/src/superlocalmemory/core/injection.py +69 -18
- package/src/superlocalmemory/core/lifecycle_state.py +153 -0
- package/src/superlocalmemory/core/maintenance.py +1 -1
- package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
- package/src/superlocalmemory/core/mutations.py +143 -0
- package/src/superlocalmemory/core/platform_utils.py +7 -4
- package/src/superlocalmemory/core/ram_lock.py +16 -5
- package/src/superlocalmemory/core/rate_limit.py +1 -1
- package/src/superlocalmemory/core/recall_pipeline.py +60 -101
- package/src/superlocalmemory/core/recall_worker.py +76 -59
- package/src/superlocalmemory/core/registry.py +1 -1
- package/src/superlocalmemory/core/scale_engine.py +293 -0
- package/src/superlocalmemory/core/score_contract.py +62 -0
- package/src/superlocalmemory/core/security_primitives.py +3 -1
- package/src/superlocalmemory/core/slm_disabled.py +3 -5
- package/src/superlocalmemory/core/store_pipeline.py +172 -40
- package/src/superlocalmemory/core/tier_manager.py +32 -20
- package/src/superlocalmemory/core/worker_pool.py +13 -4
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
- package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
- package/src/superlocalmemory/encoding/auto_linker.py +1 -1
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
- package/src/superlocalmemory/encoding/consolidator.py +22 -5
- package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
- package/src/superlocalmemory/encoding/foresight.py +2 -0
- package/src/superlocalmemory/encoding/graph_builder.py +1 -1
- package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
- package/src/superlocalmemory/evaluation/__init__.py +13 -0
- package/src/superlocalmemory/evaluation/calibration.py +308 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
- package/src/superlocalmemory/graph/cozo_backend.py +256 -23
- package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
- package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
- package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
- package/src/superlocalmemory/hooks/auto_recall.py +31 -6
- package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
- package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
- package/src/superlocalmemory/hooks/claude_code_hooks.py +123 -35
- package/src/superlocalmemory/hooks/codex_assets.py +59 -0
- package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
- package/src/superlocalmemory/hooks/context_payload.py +1 -1
- package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
- package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
- package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
- package/src/superlocalmemory/hooks/hook_handlers.py +219 -32
- package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
- package/src/superlocalmemory/hooks/session_registry.py +15 -8
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
- package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
- package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
- package/src/superlocalmemory/infra/auth_middleware.py +38 -5
- package/src/superlocalmemory/infra/backup.py +7 -5
- package/src/superlocalmemory/infra/cloud_backup.py +18 -8
- package/src/superlocalmemory/infra/daemon_identity.py +248 -0
- package/src/superlocalmemory/infra/data_root.py +199 -0
- package/src/superlocalmemory/infra/event_bus.py +3 -1
- package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
- package/src/superlocalmemory/infra/process_reaper.py +23 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
- package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
- package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/credentials.py +14 -7
- package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
- package/src/superlocalmemory/learning/ensemble.py +11 -0
- package/src/superlocalmemory/learning/entity_compiler.py +1 -1
- package/src/superlocalmemory/learning/feedback.py +1 -1
- package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
- package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
- package/src/superlocalmemory/learning/ranker.py +4 -1
- package/src/superlocalmemory/learning/source_quality.py +1 -1
- package/src/superlocalmemory/learning/trigram_index.py +3 -2
- package/src/superlocalmemory/llm/backbone.py +1 -1
- package/src/superlocalmemory/math/ebbinghaus.py +1 -1
- package/src/superlocalmemory/math/fisher.py +1 -1
- package/src/superlocalmemory/math/fisher_quantized.py +1 -1
- package/src/superlocalmemory/math/hopfield.py +1 -1
- package/src/superlocalmemory/math/langevin.py +1 -1
- package/src/superlocalmemory/math/polar_quant.py +3 -4
- package/src/superlocalmemory/math/qjl.py +1 -1
- package/src/superlocalmemory/math/sheaf.py +1 -1
- package/src/superlocalmemory/math/turbo_quant.py +3 -2
- package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
- package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
- package/src/superlocalmemory/mcp/http_transport.py +53 -0
- package/src/superlocalmemory/mcp/server.py +39 -13
- package/src/superlocalmemory/mcp/shared.py +69 -3
- package/src/superlocalmemory/mcp/tools_active.py +141 -31
- package/src/superlocalmemory/mcp/tools_core.py +128 -29
- package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
- package/src/superlocalmemory/mcp/tools_learning.py +42 -2
- package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
- package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
- package/src/superlocalmemory/mcp/tools_v28.py +23 -2
- package/src/superlocalmemory/mcp/tools_v3.py +26 -1
- package/src/superlocalmemory/mcp/tools_v33.py +56 -17
- package/src/superlocalmemory/mesh/broker.py +2 -0
- package/src/superlocalmemory/mesh/remote_sync.py +50 -12
- package/src/superlocalmemory/optimize/cache/manager.py +77 -1
- package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
- package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
- package/src/superlocalmemory/optimize/compress/router.py +6 -1
- package/src/superlocalmemory/optimize/config/__init__.py +5 -0
- package/src/superlocalmemory/optimize/config/store.py +6 -4
- package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
- package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
- package/src/superlocalmemory/optimize/proxy/server.py +2 -2
- package/src/superlocalmemory/optimize/storage/db.py +12 -12
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/ann_index.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
- package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
- package/src/superlocalmemory/retrieval/engine.py +169 -79
- package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
- package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
- package/src/superlocalmemory/retrieval/fusion.py +1 -1
- package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
- package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
- package/src/superlocalmemory/retrieval/reranker.py +56 -20
- package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
- package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
- package/src/superlocalmemory/retrieval/strategy.py +1 -1
- package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
- package/src/superlocalmemory/retrieval/vector_store.py +1 -1
- package/src/superlocalmemory/server/api.py +10 -7
- package/src/superlocalmemory/server/bandit_loops.py +4 -2
- package/src/superlocalmemory/server/recall_serializer.py +24 -0
- package/src/superlocalmemory/server/route_mutations.py +84 -0
- package/src/superlocalmemory/server/routes/agents.py +8 -6
- package/src/superlocalmemory/server/routes/brain.py +14 -12
- package/src/superlocalmemory/server/routes/chat.py +29 -12
- package/src/superlocalmemory/server/routes/data_io.py +55 -24
- package/src/superlocalmemory/server/routes/helpers.py +8 -63
- package/src/superlocalmemory/server/routes/ingest.py +53 -36
- package/src/superlocalmemory/server/routes/memories.py +104 -43
- package/src/superlocalmemory/server/routes/mesh.py +31 -0
- package/src/superlocalmemory/server/routes/profiles.py +26 -4
- package/src/superlocalmemory/server/routes/tiers.py +43 -11
- package/src/superlocalmemory/server/routes/timeline.py +5 -1
- package/src/superlocalmemory/server/routes/v3_api.py +76 -21
- package/src/superlocalmemory/server/security_middleware.py +1 -1
- package/src/superlocalmemory/server/unified_daemon.py +680 -293
- package/src/superlocalmemory/server/write_identity.py +147 -0
- package/src/superlocalmemory/storage/access_log.py +4 -3
- package/src/superlocalmemory/storage/database.py +118 -25
- package/src/superlocalmemory/storage/migration_runner.py +84 -1
- package/src/superlocalmemory/storage/migration_v33.py +1 -1
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
- package/src/superlocalmemory/storage/models.py +16 -0
- package/src/superlocalmemory/storage/quantized_store.py +20 -3
- package/src/superlocalmemory/storage/v2_migrator.py +5 -3
- package/src/superlocalmemory/ui/favicon.svg +5 -0
- package/src/superlocalmemory/ui/index.html +1 -0
- package/src/superlocalmemory/ui/js/compliance.js +1 -1
- package/src/superlocalmemory/ui/js/core.js +49 -8
- package/src/superlocalmemory/ui/js/dashboard.js +23 -2
- package/src/superlocalmemory/ui/js/feedback.js +1 -1
- package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
- package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
- package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
- package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
- package/src/superlocalmemory/ui/js/settings.js +4 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
- package/bin/slm +0 -59
- package/bin/slm.bat +0 -77
- package/bin/slm.cmd +0 -5
- package/ide/integrations/langchain/README.md +0 -106
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
- package/ide/integrations/langchain/pyproject.toml +0 -38
- package/ide/integrations/langchain/tests/__init__.py +0 -3
- package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
- package/ide/integrations/langchain/tests/test_security.py +0 -117
- package/ide/integrations/llamaindex/README.md +0 -81
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
- package/ide/integrations/llamaindex/pyproject.toml +0 -43
- package/ide/integrations/llamaindex/tests/__init__.py +0 -3
- package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
- package/ide/integrations/llamaindex/tests/test_security.py +0 -241
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-memory-advisor.md +0 -44
- package/plugin-src/agents/slm-optimize-advisor.md +0 -38
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -23
- package/plugin-src/manifest.json +0 -25
- package/plugin-src/requirements.txt +0 -1
- package/plugin-src/rules/CLAUDE.md.fragment +0 -44
- package/plugin-src/scripts/ensure-venv.bat +0 -122
- package/plugin-src/scripts/ensure-venv.sh +0 -105
- package/plugin-src/scripts/slm-launch +0 -15
- package/plugin-src/scripts/slm-launch.bat +0 -17
- package/plugin-src/settings.json +0 -16
- package/plugin-src/skills/slm-cache/SKILL.md +0 -140
- package/plugin-src/skills/slm-compress/SKILL.md +0 -143
- package/plugin-src/skills/slm-graph/SKILL.md +0 -300
- package/plugin-src/skills/slm-recall/SKILL.md +0 -204
- package/plugin-src/skills/slm-remember/SKILL.md +0 -194
- package/plugin-src/skills/slm-session/SKILL.md +0 -207
- package/plugin-src/skills/slm-status/SKILL.md +0 -149
- package/scripts/__tests__/build-plugin.test.mjs +0 -613
- package/scripts/_savings_math.py +0 -270
- package/scripts/build-dmg.sh +0 -417
- package/scripts/build-plugin.js +0 -742
- package/scripts/build-slm-hook.ps1 +0 -40
- package/scripts/build-slm-hook.sh +0 -45
- package/scripts/build_entry.py +0 -452
- package/scripts/ci/stage5b_gate.sh +0 -50
- package/scripts/dogfood_savings.py +0 -490
- package/scripts/generate-thumbnails.py +0 -218
- package/scripts/install-skills.ps1 +0 -4
- package/scripts/install-skills.sh +0 -5
- package/scripts/install.ps1 +0 -701
- package/scripts/install.sh +0 -1015
- package/scripts/postinstall_binary.js +0 -287
- package/scripts/prepack.js +0 -33
- package/scripts/release_manifest.py +0 -273
- package/scripts/slm-hook.spec +0 -56
- package/scripts/start-dashboard.ps1 +0 -52
- package/scripts/start-dashboard.sh +0 -41
- package/scripts/sync-wiki.ps1 +0 -127
- package/scripts/sync-wiki.sh +0 -82
- package/scripts/test-dmg.sh +0 -161
- package/scripts/test-npm-package.ps1 +0 -252
- package/scripts/test-npm-package.sh +0 -207
- package/scripts/verify-install.ps1 +0 -294
- package/scripts/verify-install.sh +0 -266
- package/scripts/verify-v27.ps1 +0 -301
- package/scripts/verify-v27.sh +0 -233
- package/src/superlocalmemory.egg-info/PKG-INFO +0 -516
- package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
- package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
- package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
- package/src/superlocalmemory.egg-info/requires.txt +0 -71
- package/src/superlocalmemory.egg-info/top_level.txt +0 -1
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""SuperLocalMemory V2 - LlamaIndex Chat Store Security Tests
|
|
5
|
-
|
|
6
|
-
Security-focused tests: SQL injection, XSS, oversized payloads, null bytes,
|
|
7
|
-
and other adversarial inputs that a production chat store must handle safely.
|
|
8
|
-
"""
|
|
9
|
-
import os
|
|
10
|
-
import sys
|
|
11
|
-
import tempfile
|
|
12
|
-
|
|
13
|
-
import pytest
|
|
14
|
-
|
|
15
|
-
# Ensure the SLM source tree is importable for tests
|
|
16
|
-
_SLM_SRC = os.path.join(
|
|
17
|
-
os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, "src"
|
|
18
|
-
)
|
|
19
|
-
_SLM_SRC = os.path.abspath(_SLM_SRC)
|
|
20
|
-
if _SLM_SRC not in sys.path:
|
|
21
|
-
sys.path.insert(0, _SLM_SRC)
|
|
22
|
-
|
|
23
|
-
from llama_index.core.base.llms.types import ChatMessage, MessageRole
|
|
24
|
-
from llama_index.storage.chat_store.superlocalmemory import SuperLocalMemoryChatStore
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# ---------------------------------------------------------------------------
|
|
28
|
-
# Fixtures
|
|
29
|
-
# ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@pytest.fixture
|
|
33
|
-
def tmp_db():
|
|
34
|
-
with tempfile.TemporaryDirectory() as d:
|
|
35
|
-
yield os.path.join(d, "test_security.db")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@pytest.fixture
|
|
39
|
-
def store(tmp_db):
|
|
40
|
-
return SuperLocalMemoryChatStore(db_path=tmp_db)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# ---------------------------------------------------------------------------
|
|
44
|
-
# SQL Injection Tests
|
|
45
|
-
# ---------------------------------------------------------------------------
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class TestSQLInjection:
|
|
49
|
-
"""Verify that SQL injection attempts in keys and content are harmless."""
|
|
50
|
-
|
|
51
|
-
SQL_PAYLOADS = [
|
|
52
|
-
"'; DROP TABLE memories; --",
|
|
53
|
-
"' OR '1'='1",
|
|
54
|
-
"'; DELETE FROM memories WHERE '1'='1",
|
|
55
|
-
"1; SELECT * FROM memories --",
|
|
56
|
-
"Robert'); DROP TABLE memories;--",
|
|
57
|
-
"' UNION SELECT * FROM memories --",
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
def test_sql_injection_in_key(self, store):
|
|
61
|
-
"""SQL injection attempts in the session key must not corrupt the store."""
|
|
62
|
-
for payload in self.SQL_PAYLOADS:
|
|
63
|
-
msg = ChatMessage(role=MessageRole.USER, content="safe content")
|
|
64
|
-
store.add_message(payload, msg)
|
|
65
|
-
|
|
66
|
-
# The store should still function — retrieve the message back
|
|
67
|
-
msgs = store.get_messages(payload)
|
|
68
|
-
assert len(msgs) == 1
|
|
69
|
-
assert msgs[0].content == "safe content"
|
|
70
|
-
|
|
71
|
-
# Clean up
|
|
72
|
-
store.delete_messages(payload)
|
|
73
|
-
|
|
74
|
-
def test_sql_injection_in_content(self, store):
|
|
75
|
-
"""SQL injection attempts in message content must be stored literally."""
|
|
76
|
-
for payload in self.SQL_PAYLOADS:
|
|
77
|
-
msg = ChatMessage(role=MessageRole.USER, content=payload)
|
|
78
|
-
store.add_message("safe-key", msg)
|
|
79
|
-
|
|
80
|
-
msgs = store.get_messages("safe-key")
|
|
81
|
-
assert len(msgs) == len(self.SQL_PAYLOADS)
|
|
82
|
-
|
|
83
|
-
# Verify content is stored literally, not executed
|
|
84
|
-
for i, payload in enumerate(self.SQL_PAYLOADS):
|
|
85
|
-
assert msgs[i].content == payload
|
|
86
|
-
|
|
87
|
-
# Clean up
|
|
88
|
-
store.delete_messages("safe-key")
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# ---------------------------------------------------------------------------
|
|
92
|
-
# XSS Tests
|
|
93
|
-
# ---------------------------------------------------------------------------
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class TestXSS:
|
|
97
|
-
"""Verify that XSS payloads in content are stored literally (not executed)."""
|
|
98
|
-
|
|
99
|
-
XSS_PAYLOADS = [
|
|
100
|
-
"<script>alert('xss')</script>",
|
|
101
|
-
'<img src=x onerror=alert(1)>',
|
|
102
|
-
"javascript:alert(document.cookie)",
|
|
103
|
-
"<svg/onload=alert('xss')>",
|
|
104
|
-
'"><script>alert(String.fromCharCode(88,83,83))</script>',
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
def test_xss_in_content(self, store):
|
|
108
|
-
"""XSS payloads must be stored and retrieved as literal strings."""
|
|
109
|
-
for payload in self.XSS_PAYLOADS:
|
|
110
|
-
msg = ChatMessage(role=MessageRole.USER, content=payload)
|
|
111
|
-
store.add_message("xss-key", msg)
|
|
112
|
-
|
|
113
|
-
msgs = store.get_messages("xss-key")
|
|
114
|
-
assert len(msgs) == len(self.XSS_PAYLOADS)
|
|
115
|
-
|
|
116
|
-
for i, payload in enumerate(self.XSS_PAYLOADS):
|
|
117
|
-
assert msgs[i].content == payload
|
|
118
|
-
|
|
119
|
-
store.delete_messages("xss-key")
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
# ---------------------------------------------------------------------------
|
|
123
|
-
# Oversized Content Tests
|
|
124
|
-
# ---------------------------------------------------------------------------
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class TestOversizedContent:
|
|
128
|
-
"""Verify that oversized payloads are handled without crashing."""
|
|
129
|
-
|
|
130
|
-
def test_oversized_content_1mb(self, store):
|
|
131
|
-
"""A 1MB message should be rejected by MemoryStoreV2's validation."""
|
|
132
|
-
huge = "A" * 1_000_001 # Just over the 1MB limit
|
|
133
|
-
msg = ChatMessage(role=MessageRole.USER, content=huge)
|
|
134
|
-
|
|
135
|
-
# MemoryStoreV2 raises ValueError for content > 1MB
|
|
136
|
-
with pytest.raises((ValueError, Exception)):
|
|
137
|
-
store.add_message("huge-key", msg)
|
|
138
|
-
|
|
139
|
-
def test_large_but_valid_content(self, store):
|
|
140
|
-
"""A 500KB message should be stored successfully (under 1MB limit)."""
|
|
141
|
-
large = "B" * 500_000
|
|
142
|
-
msg = ChatMessage(role=MessageRole.USER, content=large)
|
|
143
|
-
store.add_message("large-key", msg)
|
|
144
|
-
|
|
145
|
-
msgs = store.get_messages("large-key")
|
|
146
|
-
assert len(msgs) == 1
|
|
147
|
-
assert len(msgs[0].content) == 500_000
|
|
148
|
-
|
|
149
|
-
store.delete_messages("large-key")
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# ---------------------------------------------------------------------------
|
|
153
|
-
# Edge Case Key Tests
|
|
154
|
-
# ---------------------------------------------------------------------------
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class TestEdgeCaseKeys:
|
|
158
|
-
"""Verify safe handling of unusual session key values."""
|
|
159
|
-
|
|
160
|
-
def test_empty_key(self, store):
|
|
161
|
-
"""An empty string key should still work (tags allow empty strings)."""
|
|
162
|
-
msg = ChatMessage(role=MessageRole.USER, content="empty key test")
|
|
163
|
-
store.add_message("", msg)
|
|
164
|
-
|
|
165
|
-
msgs = store.get_messages("")
|
|
166
|
-
assert len(msgs) == 1
|
|
167
|
-
assert msgs[0].content == "empty key test"
|
|
168
|
-
|
|
169
|
-
store.delete_messages("")
|
|
170
|
-
|
|
171
|
-
def test_null_bytes_in_key(self, store):
|
|
172
|
-
"""Null bytes in a key should be handled without crash."""
|
|
173
|
-
key_with_null = "key\x00with\x00nulls"
|
|
174
|
-
msg = ChatMessage(role=MessageRole.USER, content="null byte key")
|
|
175
|
-
store.add_message(key_with_null, msg)
|
|
176
|
-
|
|
177
|
-
msgs = store.get_messages(key_with_null)
|
|
178
|
-
assert len(msgs) == 1
|
|
179
|
-
assert msgs[0].content == "null byte key"
|
|
180
|
-
|
|
181
|
-
store.delete_messages(key_with_null)
|
|
182
|
-
|
|
183
|
-
def test_very_long_key(self, store):
|
|
184
|
-
"""A very long key (1000 chars) should work."""
|
|
185
|
-
long_key = "k" * 1000
|
|
186
|
-
msg = ChatMessage(role=MessageRole.USER, content="long key test")
|
|
187
|
-
store.add_message(long_key, msg)
|
|
188
|
-
|
|
189
|
-
msgs = store.get_messages(long_key)
|
|
190
|
-
assert len(msgs) == 1
|
|
191
|
-
|
|
192
|
-
store.delete_messages(long_key)
|
|
193
|
-
|
|
194
|
-
def test_special_characters_in_key(self, store):
|
|
195
|
-
"""Keys with special characters should work."""
|
|
196
|
-
special_keys = [
|
|
197
|
-
"user@example.com",
|
|
198
|
-
"path/to/session",
|
|
199
|
-
"key with spaces",
|
|
200
|
-
"key-with-dashes_and_underscores",
|
|
201
|
-
"unicode-key",
|
|
202
|
-
]
|
|
203
|
-
for key in special_keys:
|
|
204
|
-
store.add_message(
|
|
205
|
-
key, ChatMessage(role=MessageRole.USER, content=f"test {key}")
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
for key in special_keys:
|
|
209
|
-
msgs = store.get_messages(key)
|
|
210
|
-
assert len(msgs) == 1, f"Failed for key: {key}"
|
|
211
|
-
|
|
212
|
-
# Verify all keys are discoverable
|
|
213
|
-
all_keys = store.get_keys()
|
|
214
|
-
for key in special_keys:
|
|
215
|
-
assert key in all_keys, f"Key '{key}' not found in get_keys()"
|
|
216
|
-
|
|
217
|
-
for key in special_keys:
|
|
218
|
-
store.delete_messages(key)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
# ---------------------------------------------------------------------------
|
|
222
|
-
# Null / None Content Tests
|
|
223
|
-
# ---------------------------------------------------------------------------
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
class TestNullContent:
|
|
227
|
-
"""Verify that null bytes and None-like content are handled safely."""
|
|
228
|
-
|
|
229
|
-
def test_null_bytes_in_content(self, store):
|
|
230
|
-
"""Null bytes in content should be stored and retrieved literally."""
|
|
231
|
-
content_with_null = "before\x00after"
|
|
232
|
-
msg = ChatMessage(role=MessageRole.USER, content=content_with_null)
|
|
233
|
-
store.add_message("null-content-key", msg)
|
|
234
|
-
|
|
235
|
-
msgs = store.get_messages("null-content-key")
|
|
236
|
-
assert len(msgs) == 1
|
|
237
|
-
# The content may or may not preserve null bytes depending on SQLite/JSON
|
|
238
|
-
# The key requirement is that it doesn't crash
|
|
239
|
-
assert msgs[0].content is not None
|
|
240
|
-
|
|
241
|
-
store.delete_messages("null-content-key")
|
package/plugin-src/.mcp.json
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: slm-memory-advisor
|
|
3
|
-
description: >
|
|
4
|
-
Advises the main agent on using SuperLocalMemory well — when to call
|
|
5
|
-
session_init, remember, recall, and search; how to phrase queries; and how
|
|
6
|
-
to keep memory clean. Delegate here for any "should I save/recall this?"
|
|
7
|
-
decision or when memory results look wrong.
|
|
8
|
-
tools: session_init, recall, search, remember, update_memory, forget, list_recent, Read
|
|
9
|
-
model: inherit
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Role
|
|
13
|
-
You are the SuperLocalMemory (SLM) memory advisor. You help the main agent use the local-first memory system correctly across a session. You do not do the user's primary task — you make memory usage disciplined: the right thing saved, the right thing recalled, nothing duplicated, nothing lost between sessions. SLM is 100% local; every tool runs on the user's machine.
|
|
14
|
-
|
|
15
|
-
# When to act
|
|
16
|
-
When the main agent: starts a session and hasn't loaded project context; is about to or just made a decision worth persisting; asks "what did we decide about X"; gets recall results that look irrelevant/empty.
|
|
17
|
-
|
|
18
|
-
# Tools you may use (real SLM MCP tools, core profile)
|
|
19
|
-
- `session_init(project_path, query, max_results, max_age_days)` — ONCE at session start; returns recent decisions + relevant memories.
|
|
20
|
-
- `recall(query, limit, session_id, fast, include_global, include_shared)` — multi-channel semantic retrieval (default limit 10). Leave `include_global`/`include_shared` unset — recall is private-by-default (v3.6.15).
|
|
21
|
-
- `search(query, limit, profile_id)` — exact keyword / FTS5 BM25.
|
|
22
|
-
- `remember(content, tags, project, importance, session_id, scope, shared_with)` — store atomic fact; importance 1-10. Leave `scope` unset (defaults to `personal`/private).
|
|
23
|
-
- `update_memory(fact_id, content)` — correct by exact id.
|
|
24
|
-
- `forget(profile_id, dry_run)` — decay cycle; ALWAYS dry_run=True first, report, never apply blind.
|
|
25
|
-
- `list_recent(limit)` — newest first.
|
|
26
|
-
- `Read` — inspect a file before deciding what to remember.
|
|
27
|
-
|
|
28
|
-
# Decision rules
|
|
29
|
-
1. SESSION_INIT FIRST — once, before any recall/remember in a fresh session. Never skip; never twice.
|
|
30
|
-
2. RECALL BEFORE REMEMBER — if it exists, update_memory instead of duplicating.
|
|
31
|
-
3. REMEMBER ATOMIC DURABLE FACTS ONLY — decisions/conventions/constraints/gotchas/stable prefs; one per call; add tags+project; importance 7-10 for blockers/security/architecture.
|
|
32
|
-
4. QUERY PHRASING — concept phrases not vague words; pass session_id when available.
|
|
33
|
-
5. recall vs search — recall for conceptual; search for literal keyword.
|
|
34
|
-
6. EMPTY/LOW results → broaden, try search, or list_recent; never fabricate.
|
|
35
|
-
7. SESSION END — close_session(session_id) when work meaningfully complete.
|
|
36
|
-
8. SCOPE IS OPT-IN (v3.6.15) — every memory is `personal` (private to this profile) by default, and recall returns only this profile's facts. Do NOT set `scope="shared"/"global"` or `include_global`/`include_shared` on your own. Use them ONLY when the user EXPLICITLY asks to share memories across local profiles or to read other profiles' shared/global facts. Default behaviour is identical to single-profile SLM.
|
|
37
|
-
|
|
38
|
-
# CLI fallback (MCP unavailable)
|
|
39
|
-
recall→`slm recall "<q>" --limit N` (add `--include-global`/`--include-shared` only on explicit user request) · search→`slm search "<q>"` · remember→`slm remember "<c>" --tags a,b` (project/importance are MCP-only, NOT CLI flags; `--scope shared --shared-with a,b` only when the user asks to share) · list→`slm list --limit N` · forget→`slm forget` (preview first) · status→`slm status`. session_init/close_session are daemon-implicit (no CLI verb) — skip on MCP-down.
|
|
40
|
-
|
|
41
|
-
# What NOT to do
|
|
42
|
-
Never session_init twice; never forget dry_run=False without reporting preview; never dump a whole file into remember; never invent a memory; never claim "saved" without success:true / clean CLI exit.
|
|
43
|
-
|
|
44
|
-
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: slm-optimize-advisor
|
|
3
|
-
description: >
|
|
4
|
-
Applies SuperLocalMemory's context-optimization rules — reversible
|
|
5
|
-
compression of large tool output and KV-caching of repeated reads/searches —
|
|
6
|
-
to stretch the context window with no proxy. Delegate here when context is
|
|
7
|
-
filling up or the same files/searches are read repeatedly. Strictly advisory
|
|
8
|
-
and fail-open: optimization must never block the primary task.
|
|
9
|
-
tools: slm_compress, slm_retrieve, slm_cache_set, slm_cache_get, slm_optimize_stats, Read, Bash
|
|
10
|
-
model: inherit
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Role
|
|
14
|
-
You are the SLM optimize advisor. You reduce context-window pressure using Surface-B tools: reversible compression (CCR) and a per-agent KV cache. No proxy; full 1M window preserved on any plan. You apply the same rules as the slm-compress and slm-cache skills. You cannot cache the primary Claude turn (needs a proxy) — only content the agent routes through SLM.
|
|
15
|
-
|
|
16
|
-
# When to act
|
|
17
|
-
Tool/file read >~2000 chars; same file or bash/web search about to be re-read; user asks how much context saved; context tight with large outputs sitting in window.
|
|
18
|
-
|
|
19
|
-
# Tools (real SLM MCP tools, code profile)
|
|
20
|
-
slm_compress(content,mode,reversible,ttl_seconds)→compressed,lossy,ccr_id (mode normalize|auto|aggressive); slm_retrieve(ccr_id); slm_cache_set(key,value,ttl_seconds); slm_cache_get(key)→hit,value; slm_optimize_stats(); Read/Bash to fetch content.
|
|
21
|
-
|
|
22
|
-
# Decision rules (the 8 — apply automatically)
|
|
23
|
-
1. CLAUDE.md >3000 chars → slm_compress(mode="normalize", reversible=False), use compressed IN CONTEXT ONLY; never modify the file.
|
|
24
|
-
2. Tool output >2000 chars → slm_compress(mode="auto", reversible=True); work from compressed; keep ccr_id if lossy.
|
|
25
|
-
3. Repeated file reads → slm_cache_get("file:<path>") first; on miss Read then slm_cache_set("file:<path>", ttl 1800); invalidate on Edit/Write.
|
|
26
|
-
4. Repeated bash/web → slm_cache_set("bash:<cmd[:64]>", ttl 600); web ttl 3600.
|
|
27
|
-
5. Retrieve original only when byte-identical needed.
|
|
28
|
-
6. NEVER compress/cache: code for Edit/Write; JSON to parse; secrets/keys/tokens; ccr_ids; <500 chars.
|
|
29
|
-
7. Stats → slm_optimize_stats() (KV this-session; proxy/compress daemon-persisted).
|
|
30
|
-
8. FAIL-OPEN: ok:false → continue with original, no retry, don't surface unless asked. Never block the task.
|
|
31
|
-
|
|
32
|
-
# CLI fallback
|
|
33
|
-
stats→`slm optimize status`/`savings` · compress→`slm compress` · cache→`slm cache ...`. If CLI also fails, drop optimization silently (rule 8).
|
|
34
|
-
|
|
35
|
-
# What NOT to do
|
|
36
|
-
Never compress code-for-edit/JSON-to-parse/<500 chars; never store secrets/ccr_ids; never let optimize failure block/alter the task; never claim a specific savings %.
|
|
37
|
-
|
|
38
|
-
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
File without changes
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"hooks": {
|
|
3
|
-
"SessionStart": [
|
|
4
|
-
{
|
|
5
|
-
"hooks": [
|
|
6
|
-
{
|
|
7
|
-
"type": "command",
|
|
8
|
-
"command": "slm hook mandate 2>/dev/null || true",
|
|
9
|
-
"timeout": 5000
|
|
10
|
-
}
|
|
11
|
-
]
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"hooks": [
|
|
15
|
-
{
|
|
16
|
-
"type": "command",
|
|
17
|
-
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/ensure-venv.sh\""
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
}
|
|
21
|
-
]
|
|
22
|
-
}
|
|
23
|
-
}
|
package/plugin-src/manifest.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "3.6.23",
|
|
3
|
-
"pluginName": "superlocalmemory",
|
|
4
|
-
"displayName": "SuperLocalMemory",
|
|
5
|
-
"repository": "https://github.com/qualixar/superlocalmemory",
|
|
6
|
-
"keywords": ["memory", "mcp", "agents", "local-first", "context-compression"],
|
|
7
|
-
"skills": [
|
|
8
|
-
{ "name": "slm-cache", "hasReadme": false },
|
|
9
|
-
{ "name": "slm-compress", "hasReadme": false },
|
|
10
|
-
{ "name": "slm-graph", "hasReadme": false },
|
|
11
|
-
{ "name": "slm-recall", "hasReadme": false },
|
|
12
|
-
{ "name": "slm-remember", "hasReadme": false },
|
|
13
|
-
{ "name": "slm-session", "hasReadme": false },
|
|
14
|
-
{ "name": "slm-status", "hasReadme": false }
|
|
15
|
-
],
|
|
16
|
-
"targets": {
|
|
17
|
-
"plugin": "plugin/.claude-plugin/plugin.json"
|
|
18
|
-
},
|
|
19
|
-
"marketplace": {
|
|
20
|
-
"owner": "qualixar",
|
|
21
|
-
"ownerEmail": "varun.pratap.bhardwaj@gmail.com",
|
|
22
|
-
"repo": "superlocalmemory",
|
|
23
|
-
"description": "Local-first agent memory + reversible context compression and KV cache, as an MCP server. 20-tool code profile with graph intelligence."
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
superlocalmemory==3.6.23
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
<!-- BEGIN SuperLocalMemory v3.6.18 -->
|
|
2
|
-
|
|
3
|
-
## SuperLocalMemory (SLM) — Agent Rules
|
|
4
|
-
|
|
5
|
-
SLM is local-first memory for agents. All tools run on the user's machine; no cloud calls.
|
|
6
|
-
|
|
7
|
-
### Session start
|
|
8
|
-
Call `session_init(project_path, query)` ONCE per fresh session before any recall/remember. Never twice.
|
|
9
|
-
|
|
10
|
-
### Remember
|
|
11
|
-
- Atomic durable facts only (decisions, conventions, constraints, gotchas). One fact per call.
|
|
12
|
-
- Recall-before-remember: `recall(query, 5)` first; use `update_memory` if near-match found.
|
|
13
|
-
- Always supply tags + project + importance (7–10 for blockers/security/architecture).
|
|
14
|
-
- Never dump a whole file; never claim "saved" without success:true.
|
|
15
|
-
- Scope is opt-in (v3.6.15): writes are `personal`/private by default. Only use `scope="shared"/"global"` (or recall's `include_global`/`include_shared`) when the user explicitly asks to share across local profiles.
|
|
16
|
-
|
|
17
|
-
### Recall
|
|
18
|
-
- `recall` for conceptual/semantic queries; `search` for exact keywords.
|
|
19
|
-
- Phrase as concepts; pass session_id when available. Never fabricate.
|
|
20
|
-
|
|
21
|
-
### Optimize (fail-open)
|
|
22
|
-
- Output >2000 chars → `slm_compress(mode="auto", reversible=True)`; keep ccr_id if lossy.
|
|
23
|
-
- Repeated reads → `slm_cache_get("file:<path>")` first; on miss cache with ttl=1800.
|
|
24
|
-
- NEVER compress/cache: code-for-edit, JSON-to-parse, secrets, ccr_ids, <500 chars.
|
|
25
|
-
- If ok:false → continue with original; never block the task.
|
|
26
|
-
|
|
27
|
-
### Session end
|
|
28
|
-
`close_session(session_id)` when work is meaningfully complete.
|
|
29
|
-
|
|
30
|
-
### CLI fallback (MCP down)
|
|
31
|
-
`slm recall "<q>" --limit N` · `slm search "<q>"` · `slm remember "<c>" --tags t` · `slm list --limit N` · `slm forget` (preview first) · `slm status` · `slm optimize status`
|
|
32
|
-
|
|
33
|
-
### Skills
|
|
34
|
-
slm-recall · slm-remember · slm-session · slm-status · slm-cache · slm-compress · slm-graph
|
|
35
|
-
|
|
36
|
-
### Commands
|
|
37
|
-
/superlocalmemory:slm-recall · /superlocalmemory:slm-remember · /superlocalmemory:slm-session · /superlocalmemory:slm-status · /superlocalmemory:slm-cache · /superlocalmemory:slm-compress · /superlocalmemory:slm-graph
|
|
38
|
-
|
|
39
|
-
### Subagents
|
|
40
|
-
slm-memory-advisor (memory decisions, session hygiene) · slm-optimize-advisor (context compression + KV cache)
|
|
41
|
-
|
|
42
|
-
<!-- END SuperLocalMemory v3.6.18 -->
|
|
43
|
-
|
|
44
|
-
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
:: ensure-venv.bat — WP-F SuperLocalMemory plugin venv bootstrap (Windows)
|
|
3
|
-
::
|
|
4
|
-
:: Windows equivalent of ensure-venv.sh. Called by Claude Code SessionStart hook
|
|
5
|
-
:: on Windows hosts. Idempotent: fast-path exits quickly when requirements.txt
|
|
6
|
-
:: is unchanged (sentinel file guards reinstall).
|
|
7
|
-
::
|
|
8
|
-
:: Environment (set by Claude Code plugin runtime):
|
|
9
|
-
:: CLAUDE_PLUGIN_ROOT — plugin installation dir (ephemeral, contains scripts\ + requirements.txt)
|
|
10
|
-
:: CLAUDE_PLUGIN_DATA — persistent data dir (venv lives here, survives plugin updates)
|
|
11
|
-
::
|
|
12
|
-
:: Exit codes: 0 = venv ready non-0 = failure (logged to stderr)
|
|
13
|
-
:: All informational output goes to stderr (stdout reserved for MCP stdio protocol).
|
|
14
|
-
::
|
|
15
|
-
:: Requires: Python 3.11+ on PATH, pip, standard Windows cmd.exe
|
|
16
|
-
|
|
17
|
-
setlocal EnableDelayedExpansion
|
|
18
|
-
|
|
19
|
-
:: ---------------------------------------------------------------------------
|
|
20
|
-
:: Guard: required env vars must be set
|
|
21
|
-
:: ---------------------------------------------------------------------------
|
|
22
|
-
if not defined CLAUDE_PLUGIN_ROOT (
|
|
23
|
-
echo ERROR: CLAUDE_PLUGIN_ROOT must be set ^(plugin installation directory^) >&2
|
|
24
|
-
exit /b 1
|
|
25
|
-
)
|
|
26
|
-
if not defined CLAUDE_PLUGIN_DATA (
|
|
27
|
-
echo ERROR: CLAUDE_PLUGIN_DATA must be set ^(plugin persistent data directory^) >&2
|
|
28
|
-
exit /b 1
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
:: ---------------------------------------------------------------------------
|
|
32
|
-
:: Python >= 3.11 guard
|
|
33
|
-
:: ---------------------------------------------------------------------------
|
|
34
|
-
python -c "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)" 2>nul
|
|
35
|
-
if errorlevel 1 (
|
|
36
|
-
for /f "tokens=*" %%v in ('python --version 2^>^&1') do set PY_VER=%%v
|
|
37
|
-
echo ERROR: SuperLocalMemory plugin requires Python ^>= 3.11, found: !PY_VER! >&2
|
|
38
|
-
echo Install Python 3.11+ and ensure it is first on PATH. >&2
|
|
39
|
-
exit /b 1
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
:: ---------------------------------------------------------------------------
|
|
43
|
-
:: Paths
|
|
44
|
-
:: ---------------------------------------------------------------------------
|
|
45
|
-
set "REQ=%CLAUDE_PLUGIN_ROOT%\requirements.txt"
|
|
46
|
-
set "VENV=%CLAUDE_PLUGIN_DATA%\venv"
|
|
47
|
-
set "VENV_SCRIPTS=%VENV%\Scripts"
|
|
48
|
-
set "SENTINEL=%CLAUDE_PLUGIN_DATA%\.venv-reqs.sha256"
|
|
49
|
-
set "VENV_TMP=%CLAUDE_PLUGIN_DATA%\venv.tmp"
|
|
50
|
-
|
|
51
|
-
:: ---------------------------------------------------------------------------
|
|
52
|
-
:: Compute sha256 of requirements.txt using PowerShell (built-in on Windows 10+)
|
|
53
|
-
:: Output: hex string of SHA256 hash
|
|
54
|
-
:: ---------------------------------------------------------------------------
|
|
55
|
-
for /f "usebackq tokens=*" %%h in (
|
|
56
|
-
`powershell -NoProfile -Command "(Get-FileHash -Algorithm SHA256 '%REQ%').Hash.ToLower()"`
|
|
57
|
-
) do set "NEW_HASH=%%h"
|
|
58
|
-
|
|
59
|
-
if "!NEW_HASH!"=="" (
|
|
60
|
-
echo ERROR: Could not compute sha256 of requirements.txt >&2
|
|
61
|
-
exit /b 1
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
:: ---------------------------------------------------------------------------
|
|
65
|
-
:: Fast-path: venv Scripts\python.exe exists AND sentinel matches
|
|
66
|
-
:: ---------------------------------------------------------------------------
|
|
67
|
-
set "VENV_PYTHON=%VENV_SCRIPTS%\python.exe"
|
|
68
|
-
if exist "%VENV_PYTHON%" (
|
|
69
|
-
if exist "%SENTINEL%" (
|
|
70
|
-
set /p "CURRENT_HASH=" < "%SENTINEL%"
|
|
71
|
-
if "!CURRENT_HASH!"=="!NEW_HASH!" (
|
|
72
|
-
echo SLM plugin: venv up-to-date, skipping install. >&2
|
|
73
|
-
exit /b 0
|
|
74
|
-
)
|
|
75
|
-
)
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
:: ---------------------------------------------------------------------------
|
|
79
|
-
:: Rebuild venv atomically: install to venv.tmp, rename to venv, write sentinel LAST
|
|
80
|
-
:: ---------------------------------------------------------------------------
|
|
81
|
-
echo SLM plugin: bootstrapping Python venv at %VENV% ... >&2
|
|
82
|
-
echo requirements: %REQ% >&2
|
|
83
|
-
|
|
84
|
-
:: Clean up any partial previous attempt
|
|
85
|
-
if exist "%VENV_TMP%" rmdir /s /q "%VENV_TMP%"
|
|
86
|
-
|
|
87
|
-
:: Create fresh venv
|
|
88
|
-
python -m venv "%VENV_TMP%"
|
|
89
|
-
if errorlevel 1 (
|
|
90
|
-
echo ERROR: python -m venv failed >&2
|
|
91
|
-
exit /b 1
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
:: Upgrade pip first
|
|
95
|
-
"%VENV_TMP%\Scripts\pip.exe" install --upgrade pip --prefer-binary --quiet
|
|
96
|
-
if errorlevel 1 (
|
|
97
|
-
echo ERROR: pip upgrade failed >&2
|
|
98
|
-
exit /b 1
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
:: Install requirements
|
|
102
|
-
"%VENV_TMP%\Scripts\pip.exe" install --require-virtualenv --prefer-binary --quiet -r "%REQ%"
|
|
103
|
-
if errorlevel 1 (
|
|
104
|
-
echo ERROR: pip install requirements failed >&2
|
|
105
|
-
exit /b 1
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
echo SLM plugin: install complete, activating venv. >&2
|
|
109
|
-
|
|
110
|
-
:: Atomic swap: remove old venv (if any), rename tmp into place
|
|
111
|
-
if exist "%VENV%" rmdir /s /q "%VENV%"
|
|
112
|
-
move "%VENV_TMP%" "%VENV%"
|
|
113
|
-
if errorlevel 1 (
|
|
114
|
-
echo ERROR: Could not move venv.tmp to venv >&2
|
|
115
|
-
exit /b 1
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
:: Write sentinel LAST — guarantees that a crash before this line triggers rebuild
|
|
119
|
-
echo !NEW_HASH!>"%SENTINEL%"
|
|
120
|
-
|
|
121
|
-
echo SLM plugin: venv ready at %VENV%\Scripts\slm.exe >&2
|
|
122
|
-
exit /b 0
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ensure-venv.sh — WP-06 SuperLocalMemory plugin venv bootstrap
|
|
3
|
-
#
|
|
4
|
-
# Called by Claude Code SessionStart hook. Idempotent: fast-path exits in <100ms
|
|
5
|
-
# on repeat invocations when requirements.txt is unchanged.
|
|
6
|
-
#
|
|
7
|
-
# Environment (set by Claude Code plugin runtime):
|
|
8
|
-
# CLAUDE_PLUGIN_ROOT — plugin installation dir (ephemeral, contains scripts/ + requirements.txt)
|
|
9
|
-
# CLAUDE_PLUGIN_DATA — persistent data dir (venv lives here, survives plugin updates)
|
|
10
|
-
#
|
|
11
|
-
# Exit codes: 0 = venv ready non-0 = failure (logged to stderr)
|
|
12
|
-
# All output goes to stderr only (stdout reserved for MCP stdio protocol).
|
|
13
|
-
|
|
14
|
-
set -euo pipefail
|
|
15
|
-
|
|
16
|
-
# ---------------------------------------------------------------------------
|
|
17
|
-
# :? guard — fail loudly if required env vars are unset or empty
|
|
18
|
-
# ---------------------------------------------------------------------------
|
|
19
|
-
: "${CLAUDE_PLUGIN_ROOT:?CLAUDE_PLUGIN_ROOT must be set (plugin installation directory)}"
|
|
20
|
-
: "${CLAUDE_PLUGIN_DATA:?CLAUDE_PLUGIN_DATA must be set (plugin persistent data directory)}"
|
|
21
|
-
|
|
22
|
-
# ---------------------------------------------------------------------------
|
|
23
|
-
# Redirect all output to stderr (MCP uses stdout for protocol messages)
|
|
24
|
-
# ---------------------------------------------------------------------------
|
|
25
|
-
exec 1>&2
|
|
26
|
-
|
|
27
|
-
# ---------------------------------------------------------------------------
|
|
28
|
-
# Python >= 3.11 guard
|
|
29
|
-
# ---------------------------------------------------------------------------
|
|
30
|
-
if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)" 2>/dev/null; then
|
|
31
|
-
PY_VER=$(python3 --version 2>&1 || echo "unknown")
|
|
32
|
-
echo "ERROR: SuperLocalMemory plugin requires Python >= 3.11, found: ${PY_VER}" >&2
|
|
33
|
-
echo "Install Python 3.11+ and ensure it is first on PATH." >&2
|
|
34
|
-
exit 1
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
# ---------------------------------------------------------------------------
|
|
38
|
-
# Paths
|
|
39
|
-
# ---------------------------------------------------------------------------
|
|
40
|
-
REQ="${CLAUDE_PLUGIN_ROOT}/requirements.txt"
|
|
41
|
-
VENV="${CLAUDE_PLUGIN_DATA}/venv"
|
|
42
|
-
SENTINEL="${CLAUDE_PLUGIN_DATA}/.venv-reqs.sha256"
|
|
43
|
-
VENV_TMP="${CLAUDE_PLUGIN_DATA}/venv.tmp"
|
|
44
|
-
|
|
45
|
-
# ---------------------------------------------------------------------------
|
|
46
|
-
# Compute sha256 of requirements.txt (cross-platform: prefer sha256sum, fall back to shasum -a 256)
|
|
47
|
-
# ---------------------------------------------------------------------------
|
|
48
|
-
hash_req() {
|
|
49
|
-
if command -v sha256sum >/dev/null 2>&1; then
|
|
50
|
-
sha256sum "${REQ}" | awk '{print $1}'
|
|
51
|
-
else
|
|
52
|
-
shasum -a 256 "${REQ}" | awk '{print $1}'
|
|
53
|
-
fi
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
NEW_HASH=$(hash_req)
|
|
57
|
-
|
|
58
|
-
# ---------------------------------------------------------------------------
|
|
59
|
-
# Fast-path: venv python exists AND sentinel matches current requirements hash
|
|
60
|
-
# venv/bin/python3 (or python) is always present after `python3 -m venv`;
|
|
61
|
-
# the sentinel guards against stale requirements.
|
|
62
|
-
# In production, superlocalmemory also installs venv/bin/slm; both checks pass.
|
|
63
|
-
# ---------------------------------------------------------------------------
|
|
64
|
-
VENV_PYTHON="${VENV}/bin/python3"
|
|
65
|
-
if [ ! -x "${VENV_PYTHON}" ]; then
|
|
66
|
-
VENV_PYTHON="${VENV}/bin/python"
|
|
67
|
-
fi
|
|
68
|
-
if [ -x "${VENV_PYTHON}" ] && [ -f "${SENTINEL}" ] && [ "$(cat "${SENTINEL}")" = "${NEW_HASH}" ]; then
|
|
69
|
-
echo "SLM plugin: venv up-to-date (sha256=${NEW_HASH:0:12}…), skipping install." >&2
|
|
70
|
-
exit 0
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
# ---------------------------------------------------------------------------
|
|
74
|
-
# Rebuild venv atomically: install to venv.tmp, rename to venv, write sentinel LAST
|
|
75
|
-
# ---------------------------------------------------------------------------
|
|
76
|
-
echo "SLM plugin: bootstrapping Python venv at ${VENV} …" >&2
|
|
77
|
-
echo " requirements: ${REQ}" >&2
|
|
78
|
-
echo " python3: $(python3 --version 2>&1)" >&2
|
|
79
|
-
|
|
80
|
-
# Clean up any partial previous attempt
|
|
81
|
-
rm -rf "${VENV_TMP}"
|
|
82
|
-
|
|
83
|
-
# Create fresh venv
|
|
84
|
-
python3 -m venv "${VENV_TMP}"
|
|
85
|
-
|
|
86
|
-
# Upgrade pip first (prefer binary to avoid source builds)
|
|
87
|
-
"${VENV_TMP}/bin/pip" install --upgrade pip --prefer-binary --quiet
|
|
88
|
-
|
|
89
|
-
# Install requirements
|
|
90
|
-
"${VENV_TMP}/bin/pip" install \
|
|
91
|
-
--require-virtualenv \
|
|
92
|
-
--prefer-binary \
|
|
93
|
-
--quiet \
|
|
94
|
-
-r "${REQ}"
|
|
95
|
-
|
|
96
|
-
echo "SLM plugin: install complete, activating venv." >&2
|
|
97
|
-
|
|
98
|
-
# Atomic swap: remove old venv (if any), rename tmp into place
|
|
99
|
-
rm -rf "${VENV}"
|
|
100
|
-
mv "${VENV_TMP}" "${VENV}"
|
|
101
|
-
|
|
102
|
-
# Write sentinel LAST — guarantees that a crash before this line triggers rebuild
|
|
103
|
-
echo "${NEW_HASH}" > "${SENTINEL}"
|
|
104
|
-
|
|
105
|
-
echo "SLM plugin: venv ready at ${VENV}/bin/slm" >&2
|