superlocalmemory 4.1.22 → 4.1.23
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +99 -0
- package/README.md +50 -27
- package/antigravity-plugin/_GENERATED.md +33 -0
- package/antigravity-plugin/agents/slm-governance-advisor.md +78 -0
- package/antigravity-plugin/agents/slm-loop-runner.md +82 -0
- package/antigravity-plugin/agents/slm-memory-advisor.md +50 -0
- package/antigravity-plugin/agents/slm-optimize-advisor.md +44 -0
- package/antigravity-plugin/commands/slm-loop.md +33 -0
- package/antigravity-plugin/hooks/hooks.json +59 -0
- package/antigravity-plugin/hooks.json +42 -0
- package/antigravity-plugin/mcp_config.json +14 -0
- package/antigravity-plugin/plugin.json +25 -0
- package/antigravity-plugin/scripts/antigravity_hook_adapter.py +85 -0
- package/antigravity-plugin/skills/slm-bot-memory/SKILL.md +146 -0
- package/antigravity-plugin/skills/slm-cache/SKILL.md +147 -0
- package/antigravity-plugin/skills/slm-compress/SKILL.md +150 -0
- package/antigravity-plugin/skills/slm-getting-started-bot/SKILL.md +146 -0
- package/antigravity-plugin/skills/slm-governance/SKILL.md +221 -0
- package/antigravity-plugin/skills/slm-graph/SKILL.md +327 -0
- package/antigravity-plugin/skills/slm-loop/SKILL.md +153 -0
- package/antigravity-plugin/skills/slm-mesh/SKILL.md +287 -0
- package/antigravity-plugin/skills/slm-profile/SKILL.md +159 -0
- package/antigravity-plugin/skills/slm-recall/SKILL.md +430 -0
- package/antigravity-plugin/skills/slm-remember/SKILL.md +370 -0
- package/antigravity-plugin/skills/slm-scope/SKILL.md +183 -0
- package/antigravity-plugin/skills/slm-session/SKILL.md +291 -0
- package/antigravity-plugin/skills/slm-status/SKILL.md +185 -0
- package/antigravity-plugin/skills/slm-web-access/SKILL.md +91 -0
- package/codex-plugin/.claude-plugin/plugin.json +19 -0
- package/codex-plugin/.codex/config.toml +40 -0
- package/codex-plugin/.codex-plugin/plugin.json +43 -0
- package/codex-plugin/.mcp.json +16 -0
- package/codex-plugin/AGENTS.md +161 -0
- package/codex-plugin/README.md +205 -0
- package/codex-plugin/_GENERATED.md +52 -0
- package/codex-plugin/agents/slm-governance-advisor.md +78 -0
- package/codex-plugin/agents/slm-loop-runner.md +82 -0
- package/codex-plugin/agents/slm-memory-advisor.md +50 -0
- package/codex-plugin/agents/slm-optimize-advisor.md +44 -0
- package/codex-plugin/commands/slm-loop.md +33 -0
- package/codex-plugin/hooks/hooks.json +59 -0
- package/codex-plugin/scripts/ensure-venv.bat +108 -0
- package/codex-plugin/scripts/ensure-venv.sh +100 -0
- package/codex-plugin/scripts/slm-launch +55 -0
- package/codex-plugin/scripts/slm-launch.bat +30 -0
- package/codex-plugin/skills/slm-bot-memory/SKILL.md +146 -0
- package/codex-plugin/skills/slm-cache/SKILL.md +147 -0
- package/codex-plugin/skills/slm-compress/SKILL.md +150 -0
- package/codex-plugin/skills/slm-getting-started-bot/SKILL.md +146 -0
- package/codex-plugin/skills/slm-governance/SKILL.md +221 -0
- package/codex-plugin/skills/slm-graph/SKILL.md +327 -0
- package/codex-plugin/skills/slm-loop/SKILL.md +153 -0
- package/codex-plugin/skills/slm-mesh/SKILL.md +287 -0
- package/codex-plugin/skills/slm-profile/SKILL.md +159 -0
- package/codex-plugin/skills/slm-recall/SKILL.md +430 -0
- package/codex-plugin/skills/slm-remember/SKILL.md +370 -0
- package/codex-plugin/skills/slm-scope/SKILL.md +183 -0
- package/codex-plugin/skills/slm-session/SKILL.md +291 -0
- package/codex-plugin/skills/slm-status/SKILL.md +185 -0
- package/codex-plugin/skills/slm-web-access/SKILL.md +91 -0
- package/copilot-plugin/.github/agents/slm-governance-advisor.agent.md +80 -0
- package/copilot-plugin/.github/agents/slm-loop-runner.agent.md +84 -0
- package/copilot-plugin/.github/agents/slm-memory-advisor.agent.md +52 -0
- package/copilot-plugin/.github/agents/slm-optimize-advisor.agent.md +46 -0
- package/copilot-plugin/.github/copilot-instructions.md +52 -0
- package/copilot-plugin/.github/hooks/slm-hooks.json +44 -0
- package/copilot-plugin/.github/prompts/slm-bot-memory.prompt.md +147 -0
- package/copilot-plugin/.github/prompts/slm-cache.prompt.md +151 -0
- package/copilot-plugin/.github/prompts/slm-compress.prompt.md +154 -0
- package/copilot-plugin/.github/prompts/slm-getting-started-bot.prompt.md +147 -0
- package/copilot-plugin/.github/prompts/slm-governance.prompt.md +224 -0
- package/copilot-plugin/.github/prompts/slm-graph.prompt.md +322 -0
- package/copilot-plugin/.github/prompts/slm-loop.prompt.md +159 -0
- package/copilot-plugin/.github/prompts/slm-mesh.prompt.md +290 -0
- package/copilot-plugin/.github/prompts/slm-profile.prompt.md +155 -0
- package/copilot-plugin/.github/prompts/slm-recall.prompt.md +434 -0
- package/copilot-plugin/.github/prompts/slm-remember.prompt.md +375 -0
- package/copilot-plugin/.github/prompts/slm-scope.prompt.md +183 -0
- package/copilot-plugin/.github/prompts/slm-session.prompt.md +291 -0
- package/copilot-plugin/.github/prompts/slm-status.prompt.md +190 -0
- package/copilot-plugin/.github/prompts/slm-web-access.prompt.md +95 -0
- package/copilot-plugin/.vscode/mcp.json +11 -0
- package/copilot-plugin/README.md +26 -0
- package/copilot-plugin/_GENERATED.md +49 -0
- package/copilot-plugin/scripts/ensure-venv.bat +122 -0
- package/copilot-plugin/scripts/ensure-venv.sh +169 -0
- package/copilot-plugin/scripts/slm-launch +207 -0
- package/copilot-plugin/scripts/slm-launch.bat +32 -0
- package/copilot-plugin/scripts/slm-resolve.sh +128 -0
- package/copilot-plugin/scripts/slm-run +76 -0
- package/copilot-plugin/scripts/torch-cpu-resolve.sh +86 -0
- package/docs/pi-dev-integration.md +0 -2
- package/hermes-plugin/README.md +30 -0
- package/hermes-plugin/_GENERATED.md +3 -0
- package/hermes-plugin/__init__.py +417 -0
- package/hermes-plugin/agents/slm-governance-advisor.md +78 -0
- package/hermes-plugin/agents/slm-loop-runner.md +82 -0
- package/hermes-plugin/agents/slm-memory-advisor.md +50 -0
- package/hermes-plugin/agents/slm-optimize-advisor.md +44 -0
- package/hermes-plugin/command-inventory.json +7 -0
- package/hermes-plugin/plugin.yaml +24 -0
- package/hermes-plugin/remote.py +390 -0
- package/hermes-plugin/skills/slm-bot-memory/SKILL.md +146 -0
- package/hermes-plugin/skills/slm-cache/SKILL.md +147 -0
- package/hermes-plugin/skills/slm-compress/SKILL.md +150 -0
- package/hermes-plugin/skills/slm-getting-started-bot/SKILL.md +146 -0
- package/hermes-plugin/skills/slm-governance/SKILL.md +221 -0
- package/hermes-plugin/skills/slm-graph/SKILL.md +327 -0
- package/hermes-plugin/skills/slm-loop/SKILL.md +153 -0
- package/hermes-plugin/skills/slm-mesh/SKILL.md +287 -0
- package/hermes-plugin/skills/slm-profile/SKILL.md +159 -0
- package/hermes-plugin/skills/slm-recall/SKILL.md +430 -0
- package/hermes-plugin/skills/slm-remember/SKILL.md +370 -0
- package/hermes-plugin/skills/slm-scope/SKILL.md +183 -0
- package/hermes-plugin/skills/slm-session/SKILL.md +291 -0
- package/hermes-plugin/skills/slm-status/SKILL.md +185 -0
- package/hermes-plugin/skills/slm-web-access/SKILL.md +91 -0
- package/package.json +9 -18
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +14 -12
- package/plugin/agents/slm-governance-advisor.md +14 -16
- package/plugin/agents/slm-loop-runner.md +22 -11
- package/plugin/agents/slm-memory-advisor.md +14 -14
- package/plugin/agents/slm-optimize-advisor.md +5 -5
- package/plugin/commands/slm-loop.md +5 -3
- package/plugin/cursor-skills/slm-bot-memory/SKILL.md +19 -12
- package/plugin/cursor-skills/slm-getting-started-bot/SKILL.md +17 -9
- package/plugin/cursor-skills/slm-recall/SKILL.md +151 -54
- package/plugin/cursor-skills/slm-remember/SKILL.md +132 -35
- package/plugin/cursor-skills/slm-scope/SKILL.md +24 -17
- package/plugin/cursor-skills/slm-session/SKILL.md +77 -57
- package/plugin/mcp.cursor.json +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-bot-memory/SKILL.md +19 -12
- package/plugin/skills/slm-cache/SKILL.md +6 -7
- package/plugin/skills/slm-compress/SKILL.md +3 -3
- package/plugin/skills/slm-getting-started-bot/SKILL.md +17 -9
- package/plugin/skills/slm-governance/SKILL.md +126 -153
- package/plugin/skills/slm-graph/SKILL.md +27 -15
- package/plugin/skills/slm-loop/SKILL.md +74 -20
- package/plugin/skills/slm-mesh/SKILL.md +17 -12
- package/plugin/skills/slm-profile/SKILL.md +101 -92
- package/plugin/skills/slm-recall/SKILL.md +151 -54
- package/plugin/skills/slm-remember/SKILL.md +132 -35
- package/plugin/skills/slm-scope/SKILL.md +24 -17
- package/plugin/skills/slm-session/SKILL.md +77 -57
- package/plugin/skills/slm-status/SKILL.md +54 -35
- package/plugin/skills/slm-web-access/SKILL.md +91 -0
- package/plugin-src/agents/slm-governance-advisor.md +78 -0
- package/plugin-src/agents/slm-loop-runner.md +82 -0
- package/plugin-src/agents/slm-memory-advisor.md +14 -14
- package/plugin-src/agents/slm-optimize-advisor.md +5 -5
- package/plugin-src/commands/slm-loop.md +33 -0
- package/plugin-src/rules/AGENTS.md +45 -36
- package/plugin-src/rules/CLAUDE.md.fragment +47 -0
- package/plugin-src/skills/slm-bot-memory/SKILL.md +19 -12
- package/plugin-src/skills/slm-cache/SKILL.md +6 -7
- package/plugin-src/skills/slm-compress/SKILL.md +3 -3
- package/plugin-src/skills/slm-getting-started-bot/SKILL.md +17 -9
- package/plugin-src/skills/slm-governance/SKILL.md +126 -153
- package/plugin-src/skills/slm-graph/SKILL.md +27 -15
- package/plugin-src/skills/slm-loop/SKILL.md +74 -20
- package/plugin-src/skills/slm-mesh/SKILL.md +17 -12
- package/plugin-src/skills/slm-profile/SKILL.md +101 -92
- package/plugin-src/skills/slm-recall/SKILL.md +151 -54
- package/plugin-src/skills/slm-remember/SKILL.md +132 -35
- package/plugin-src/skills/slm-scope/SKILL.md +24 -17
- package/plugin-src/skills/slm-session/SKILL.md +77 -57
- package/plugin-src/skills/slm-status/SKILL.md +54 -35
- package/plugin-src/skills/slm-web-access/SKILL.md +91 -0
|
@@ -0,0 +1,122 @@
|
|
|
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.12+ 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.12 guard
|
|
33
|
+
:: ---------------------------------------------------------------------------
|
|
34
|
+
python -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) 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.12, found: !PY_VER! >&2
|
|
38
|
+
echo Install Python 3.12+ 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
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ensure-venv.sh — 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, or not needed 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
|
+
# Only build a venv that will be used.
|
|
29
|
+
#
|
|
30
|
+
# slm-launch prefers an slm that is already installed, and SLM_LAUNCHER can name
|
|
31
|
+
# one outright. In those cases the plugin venv is never run, so neither its
|
|
32
|
+
# Python requirement nor its install cost should land on this session. The
|
|
33
|
+
# question is answered by slm-resolve.sh, the same code the launcher and the
|
|
34
|
+
# hooks use, so the three can never disagree about which slm is in play.
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# shellcheck source=slm-resolve.sh
|
|
37
|
+
. "$(dirname "${BASH_SOURCE[0]}")/slm-resolve.sh"
|
|
38
|
+
if ! slm_choose; then
|
|
39
|
+
# A venv cannot fix a launcher that is configured to use something else.
|
|
40
|
+
# Report it, and leave the session alone: the MCP server reports it again.
|
|
41
|
+
echo "SLM plugin: venv not needed — ${SLM_RESOLVE_ERROR}" >&2
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
if [ "${SLM_CHOICE}" != "plugin" ]; then
|
|
45
|
+
echo "SLM plugin: venv not needed — using ${SLM_CHOICE_BIN} (${SLM_CHOICE})." >&2
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# Python >= 3.12 guard
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)" 2>/dev/null; then
|
|
53
|
+
PY_VER=$(python3 --version 2>&1 || echo "unknown")
|
|
54
|
+
echo "ERROR: SuperLocalMemory plugin requires Python >= 3.12, found: ${PY_VER}" >&2
|
|
55
|
+
echo "Install Python 3.12+ and ensure it is first on PATH." >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Paths
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
REQ="${CLAUDE_PLUGIN_ROOT}/requirements.txt"
|
|
63
|
+
TORCH_CPU_PIN_FILE="${CLAUDE_PLUGIN_ROOT}/requirements-cpu-torch.txt"
|
|
64
|
+
VENV="${CLAUDE_PLUGIN_DATA}/venv"
|
|
65
|
+
SENTINEL="${CLAUDE_PLUGIN_DATA}/.venv-reqs.sha256"
|
|
66
|
+
VENV_TMP="${CLAUDE_PLUGIN_DATA}/venv.tmp"
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Compute sha256 of requirements.txt, and of the CPU-torch pin file when it
|
|
70
|
+
# exists (a pin bump alone must also trigger a rebuild).
|
|
71
|
+
# (cross-platform: prefer sha256sum, fall back to shasum -a 256)
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
_sha256_of() {
|
|
74
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
75
|
+
sha256sum "$1" | awk '{print $1}'
|
|
76
|
+
else
|
|
77
|
+
shasum -a 256 "$1" | awk '{print $1}'
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
hash_req() {
|
|
82
|
+
local combined
|
|
83
|
+
combined="$(_sha256_of "${REQ}")"
|
|
84
|
+
if [ -f "${TORCH_CPU_PIN_FILE}" ]; then
|
|
85
|
+
combined="${combined}:$(_sha256_of "${TORCH_CPU_PIN_FILE}")"
|
|
86
|
+
fi
|
|
87
|
+
# Fold the (possibly combined) digest down to one hash again so the
|
|
88
|
+
# sentinel stays a single opaque token either way.
|
|
89
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
90
|
+
printf '%s' "${combined}" | sha256sum | awk '{print $1}'
|
|
91
|
+
else
|
|
92
|
+
printf '%s' "${combined}" | shasum -a 256 | awk '{print $1}'
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
NEW_HASH=$(hash_req)
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# Fast-path: venv python exists AND sentinel matches current requirements hash
|
|
100
|
+
# venv/bin/python3 (or python) is always present after `python3 -m venv`;
|
|
101
|
+
# the sentinel guards against stale requirements.
|
|
102
|
+
# In production, superlocalmemory also installs venv/bin/slm; both checks pass.
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
VENV_PYTHON="${VENV}/bin/python3"
|
|
105
|
+
if [ ! -x "${VENV_PYTHON}" ]; then
|
|
106
|
+
VENV_PYTHON="${VENV}/bin/python"
|
|
107
|
+
fi
|
|
108
|
+
if [ -x "${VENV_PYTHON}" ] && [ -f "${SENTINEL}" ] && [ "$(cat "${SENTINEL}")" = "${NEW_HASH}" ]; then
|
|
109
|
+
echo "SLM plugin: venv up-to-date (sha256=${NEW_HASH:0:12}…), skipping install." >&2
|
|
110
|
+
exit 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# Rebuild venv atomically: install to venv.tmp, rename to venv, write sentinel LAST
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
echo "SLM plugin: bootstrapping Python venv at ${VENV} …" >&2
|
|
117
|
+
echo " requirements: ${REQ}" >&2
|
|
118
|
+
echo " python3: $(python3 --version 2>&1)" >&2
|
|
119
|
+
|
|
120
|
+
# Clean up any partial previous attempt
|
|
121
|
+
rm -rf "${VENV_TMP}"
|
|
122
|
+
|
|
123
|
+
# Create fresh venv
|
|
124
|
+
python3 -m venv "${VENV_TMP}"
|
|
125
|
+
|
|
126
|
+
# Upgrade pip first (prefer binary to avoid source builds)
|
|
127
|
+
"${VENV_TMP}/bin/pip" install --upgrade pip --prefer-binary --quiet
|
|
128
|
+
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
# CPU-only torch on CPU-only Linux. Plain PyPI resolution of the
|
|
131
|
+
# `torch==X.Y.Z` requirement that requirements.txt pulls in transitively
|
|
132
|
+
# installs the CUDA build on Linux (torch + triton + ~18 nvidia-* packages,
|
|
133
|
+
# about 2.6 GiB) even when there is no GPU to use it. Pre-installing the
|
|
134
|
+
# pinned version from the official CPU index first means the later
|
|
135
|
+
# `pip install -r requirements.txt` below finds it already satisfied and
|
|
136
|
+
# never touches the CUDA wheels. macOS, Windows, a host with a GPU, and a
|
|
137
|
+
# user who already set SLM_TORCH_BACKEND or their own pip index are all left
|
|
138
|
+
# exactly as before (see torch-cpu-resolve.sh).
|
|
139
|
+
# shellcheck source=torch-cpu-resolve.sh
|
|
140
|
+
. "$(dirname "${BASH_SOURCE[0]}")/torch-cpu-resolve.sh"
|
|
141
|
+
if torch_cpu_should_force; then
|
|
142
|
+
if TORCH_PIN="$(torch_cpu_pin "${TORCH_CPU_PIN_FILE}")"; then
|
|
143
|
+
echo "SLM plugin: Linux, no GPU detected — installing ${TORCH_PIN} from ${TORCH_CPU_INDEX_URL} (set SLM_TORCH_BACKEND=cuda to opt out)." >&2
|
|
144
|
+
"${VENV_TMP}/bin/pip" install \
|
|
145
|
+
--require-virtualenv \
|
|
146
|
+
--prefer-binary \
|
|
147
|
+
--quiet \
|
|
148
|
+
--index-url "${TORCH_CPU_INDEX_URL}" \
|
|
149
|
+
"${TORCH_PIN}"
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# Install requirements
|
|
154
|
+
"${VENV_TMP}/bin/pip" install \
|
|
155
|
+
--require-virtualenv \
|
|
156
|
+
--prefer-binary \
|
|
157
|
+
--quiet \
|
|
158
|
+
-r "${REQ}"
|
|
159
|
+
|
|
160
|
+
echo "SLM plugin: install complete, activating venv." >&2
|
|
161
|
+
|
|
162
|
+
# Atomic swap: remove old venv (if any), rename tmp into place
|
|
163
|
+
rm -rf "${VENV}"
|
|
164
|
+
mv "${VENV_TMP}" "${VENV}"
|
|
165
|
+
|
|
166
|
+
# Write sentinel LAST — guarantees that a crash before this line triggers rebuild
|
|
167
|
+
echo "${NEW_HASH}" > "${SENTINEL}"
|
|
168
|
+
|
|
169
|
+
echo "SLM plugin: venv ready at ${VENV}/bin/slm" >&2
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# slm-launch — SuperLocalMemory MCP launcher (POSIX)
|
|
3
|
+
#
|
|
4
|
+
# Cross-platform counterpart: slm-launch.bat (Windows)
|
|
5
|
+
# Referenced by .mcp.json as the MCP server command.
|
|
6
|
+
#
|
|
7
|
+
# WHAT THIS RESOLVES, AND WHY THE DEFAULT CHANGED
|
|
8
|
+
#
|
|
9
|
+
# It used to default to a venv the plugin owns:
|
|
10
|
+
# "${CLAUDE_PLUGIN_DATA}/venv/bin/slm". So anyone who already had SuperLocalMemory
|
|
11
|
+
# — from pip, pipx or npm — got a SECOND installation the moment they installed
|
|
12
|
+
# the plugin, on its own schedule, upgraded separately, and easy to leave behind
|
|
13
|
+
# a release. Installing a plugin should not fork your installation.
|
|
14
|
+
#
|
|
15
|
+
# It was also Claude-Code-only. ${CLAUDE_PLUGIN_DATA} is set by Claude Code and
|
|
16
|
+
# by nothing else, so on Codex or Antigravity the path collapsed to
|
|
17
|
+
# "/venv/bin/slm" and the server simply did not start.
|
|
18
|
+
#
|
|
19
|
+
# The default is now: USE THE SLM THAT IS ALREADY INSTALLED. A plugin-owned venv
|
|
20
|
+
# is the fallback for a machine that has none, not the first choice.
|
|
21
|
+
#
|
|
22
|
+
# Environment (all optional):
|
|
23
|
+
# SLM_LAUNCHER auto (default) — prefer an installed slm, else the plugin venv
|
|
24
|
+
# system — require the slm on PATH
|
|
25
|
+
# plugin — require the plugin's own venv
|
|
26
|
+
# <path> — an explicit slm binary (leading ~ expanded)
|
|
27
|
+
# SLM_DATA_DIR SLM data dir (default: ~/.superlocalmemory)
|
|
28
|
+
# CLAUDE_PLUGIN_DATA where a plugin-owned venv lives (then CURSOR_PLUGIN_DATA,
|
|
29
|
+
# PLUGIN_DATA, then ~/.local/share/superlocalmemory-plugin)
|
|
30
|
+
# SLM_LAUNCHER_BOOTSTRAP=1 build that venv here when nothing else can run
|
|
31
|
+
#
|
|
32
|
+
# HOSTS THAT DO NOT EXPAND THEIR VARIABLES
|
|
33
|
+
#
|
|
34
|
+
# Some hosts (Grok Bot's plugin computer) pass ${CLAUDE_PLUGIN_ROOT} and
|
|
35
|
+
# ${CLAUDE_PLUGIN_DATA} through literally. So this script finds itself from
|
|
36
|
+
# BASH_SOURCE, and treats any value still containing "${" as unset.
|
|
37
|
+
#
|
|
38
|
+
# Which slm runs, in order: SLM_LAUNCHER; an slm on PATH; the plugin venv, if
|
|
39
|
+
# built; uvx, pinned to this plugin's release (CPU-only torch on Linux); and,
|
|
40
|
+
# only with SLM_LAUNCHER_BOOTSTRAP=1, a plugin venv built on the spot.
|
|
41
|
+
# The daemon is started first and `slm serve start` waits (up to 60 s) for its
|
|
42
|
+
# /health, so the first remember after a cold start is not "daemon unavailable".
|
|
43
|
+
#
|
|
44
|
+
# Deliberately NOT set here: the MCP profile. Which tools a host exposes is the
|
|
45
|
+
# user's decision, and a plugin that narrows it takes away tools they configured.
|
|
46
|
+
|
|
47
|
+
# Where this script lives. Never from ${CLAUDE_PLUGIN_ROOT}: not every host sets it.
|
|
48
|
+
_slm_self="${BASH_SOURCE[0]}"
|
|
49
|
+
while [ -h "${_slm_self}" ]; do
|
|
50
|
+
_slm_link="$(readlink "${_slm_self}")"
|
|
51
|
+
case "${_slm_link}" in
|
|
52
|
+
/*) _slm_self="${_slm_link}" ;;
|
|
53
|
+
*) _slm_self="$(dirname "${_slm_self}")/${_slm_link}" ;;
|
|
54
|
+
esac
|
|
55
|
+
done
|
|
56
|
+
_SLM_SCRIPTS="$(cd "$(dirname "${_slm_self}")" && pwd)"
|
|
57
|
+
_SLM_PLUGIN_ROOT="$(dirname "${_SLM_SCRIPTS}")"
|
|
58
|
+
|
|
59
|
+
# A value that is empty, or a placeholder the host never replaced, is not a value.
|
|
60
|
+
_slm_usable() {
|
|
61
|
+
case "$1" in
|
|
62
|
+
''|*'${'*) return 1 ;;
|
|
63
|
+
esac
|
|
64
|
+
return 0
|
|
65
|
+
}
|
|
66
|
+
[ -n "${HOME:-}" ] || { HOME="$(cd ~ 2>/dev/null && pwd)"; export HOME; }
|
|
67
|
+
_slm_usable "${SLM_DATA_DIR:-}" || unset SLM_DATA_DIR
|
|
68
|
+
_slm_usable "${SLM_LAUNCHER:-}" || unset SLM_LAUNCHER
|
|
69
|
+
if ! _slm_usable "${CLAUDE_PLUGIN_DATA:-}"; then
|
|
70
|
+
unset CLAUDE_PLUGIN_DATA
|
|
71
|
+
for _slm_d in "${CURSOR_PLUGIN_DATA:-}" "${PLUGIN_DATA:-}" \
|
|
72
|
+
"${XDG_DATA_HOME:-${HOME}/.local/share}/superlocalmemory-plugin"; do
|
|
73
|
+
if _slm_usable "${_slm_d}"; then CLAUDE_PLUGIN_DATA="${_slm_d}"; break; fi
|
|
74
|
+
done
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
_SLM_DATA="${SLM_DATA_DIR:-$HOME/.superlocalmemory}"
|
|
78
|
+
|
|
79
|
+
# Which slm to run is decided in ONE place, shared with the hooks and the venv
|
|
80
|
+
# bootstrap, so the three can never disagree about which install is in play.
|
|
81
|
+
# shellcheck source=slm-resolve.sh
|
|
82
|
+
. "${_SLM_SCRIPTS}/slm-resolve.sh"
|
|
83
|
+
_PLUGIN_BIN="$(slm_plugin_bin)"
|
|
84
|
+
|
|
85
|
+
# Is a daemon already up for this data dir? If so we join it rather than boot a
|
|
86
|
+
# second one — one writer for every session that shares the store.
|
|
87
|
+
_daemon_running=0
|
|
88
|
+
if [ -f "${_SLM_DATA}/daemon.pid" ] \
|
|
89
|
+
&& kill -0 "$(cat "${_SLM_DATA}/daemon.pid" 2>/dev/null)" 2>/dev/null; then
|
|
90
|
+
_daemon_running=1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Start a daemon with the chosen command unless one is already serving.
|
|
94
|
+
_ensure_daemon() {
|
|
95
|
+
[ "${_daemon_running}" -eq 1 ] && return 0
|
|
96
|
+
"$@" serve start >&2
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# The release this plugin tree ships (requirements.txt is stamped every release).
|
|
100
|
+
_slm_pin() {
|
|
101
|
+
sed -n 's/^\(superlocalmemory==[0-9][0-9A-Za-z.+!-]*\)[[:space:]]*$/\1/p' \
|
|
102
|
+
"${_SLM_PLUGIN_ROOT}/requirements.txt" 2>/dev/null | head -n 1
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# No installed slm and no plugin venv: run this release through uvx. Returns
|
|
106
|
+
# only when it cannot (no uvx, or no pin); otherwise it becomes the server.
|
|
107
|
+
_slm_run_uvx() {
|
|
108
|
+
local _pin
|
|
109
|
+
command -v uvx >/dev/null 2>&1 || return 1
|
|
110
|
+
_pin="$(_slm_pin)"
|
|
111
|
+
if [ -z "${_pin}" ]; then
|
|
112
|
+
echo "SLM plugin: not using uvx: no superlocalmemory pin in ${_SLM_PLUGIN_ROOT}/requirements.txt." >&2
|
|
113
|
+
return 1
|
|
114
|
+
fi
|
|
115
|
+
# PyPI's Linux torch is the CUDA build; macOS and Windows get CPU wheels anyway.
|
|
116
|
+
if [ "$(uname -s 2>/dev/null)" = "Linux" ]; then
|
|
117
|
+
export UV_TORCH_BACKEND="${UV_TORCH_BACKEND:-cpu}"
|
|
118
|
+
fi
|
|
119
|
+
export TOKENIZERS_PARALLELISM="${TOKENIZERS_PARALLELISM:-false}"
|
|
120
|
+
echo "SLM plugin: no installed slm; running ${_pin} through uvx (downloaded once, then cached)." >&2
|
|
121
|
+
_ensure_daemon uvx --from "${_pin}" slm || \
|
|
122
|
+
echo "SLM plugin: daemon did not start; the MCP server will retry on first use." >&2
|
|
123
|
+
exec uvx --from "${_pin}" slm mcp
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Opt-in: build the plugin venv now, with the same bootstrap SessionStart runs.
|
|
127
|
+
_slm_bootstrap() {
|
|
128
|
+
[ "${SLM_LAUNCHER_BOOTSTRAP:-0}" = "1" ] || return 1
|
|
129
|
+
CLAUDE_PLUGIN_ROOT="${_SLM_PLUGIN_ROOT}" CLAUDE_PLUGIN_DATA="${CLAUDE_PLUGIN_DATA}" \
|
|
130
|
+
bash "${_SLM_SCRIPTS}/ensure-venv.sh" >&2 || return 1
|
|
131
|
+
[ -x "${SLM_CHOICE_BIN}" ]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if ! slm_choose; then
|
|
135
|
+
echo "${SLM_RESOLVE_ERROR}" >&2
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
case "${SLM_LAUNCHER:-auto}:${SLM_CHOICE}" in
|
|
140
|
+
auto:system)
|
|
141
|
+
# An slm already on PATH — the pip, pipx or npm install the user has.
|
|
142
|
+
# This is the common case and the one that must not be forked.
|
|
143
|
+
#
|
|
144
|
+
# Both orderings end up here, and both must converge on ONE
|
|
145
|
+
# environment:
|
|
146
|
+
# installed first, plugin second -> nothing was ever forked;
|
|
147
|
+
# plugin first, installed second -> the plugin venv is now the
|
|
148
|
+
# older copy and must stop being used.
|
|
149
|
+
# They already share one store, because this launcher sets no
|
|
150
|
+
# SLM_DATA_DIR and both resolve ~/.superlocalmemory. What is left to
|
|
151
|
+
# go wrong is two DIFFERENT VERSIONS serving that one store: whoever
|
|
152
|
+
# starts the daemon first decides which code the other one talks to.
|
|
153
|
+
# So when a leftover plugin venv is found beside a real install, say
|
|
154
|
+
# so, with both versions named. Silent duplication is how a store
|
|
155
|
+
# ends up served by a release nobody thinks they are running.
|
|
156
|
+
if [ -n "${_PLUGIN_BIN}" ] && [ -x "${_PLUGIN_BIN}" ]; then
|
|
157
|
+
_sys_v="$(slm --version 2>/dev/null | awk '{print $NF}')"
|
|
158
|
+
_plg_v="$("${_PLUGIN_BIN}" --version 2>/dev/null | awk '{print $NF}')"
|
|
159
|
+
if [ -n "${_plg_v}" ] && [ "${_sys_v}" != "${_plg_v}" ]; then
|
|
160
|
+
echo "SLM plugin: using the installed slm ${_sys_v:-?} and IGNORING a leftover plugin venv at ${_PLUGIN_BIN} (${_plg_v})." >&2
|
|
161
|
+
echo " One store, one version. Remove the venv to stop it drifting: rm -rf \"${CLAUDE_PLUGIN_DATA}/venv\"" >&2
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
_ensure_daemon "${SLM_CHOICE_BIN}" || \
|
|
165
|
+
echo "SLM plugin: daemon did not start; continuing as a direct MCP writer." >&2
|
|
166
|
+
exec slm mcp
|
|
167
|
+
;;
|
|
168
|
+
auto:plugin)
|
|
169
|
+
# No system install. Use the plugin's own venv, once SessionStart has
|
|
170
|
+
# built it. A host that runs no hooks never builds it: then uvx, then
|
|
171
|
+
# (opt-in) a venv built here. If none of that works, say what was
|
|
172
|
+
# looked for, so the fix is obvious.
|
|
173
|
+
if [ "${SLM_CHOICE_READY}" -ne 1 ]; then
|
|
174
|
+
_slm_run_uvx
|
|
175
|
+
if ! _slm_bootstrap; then
|
|
176
|
+
slm_not_found_message >&2
|
|
177
|
+
echo >&2
|
|
178
|
+
echo " Or install uv (https://docs.astral.sh/uv/): the plugin then runs SuperLocalMemory through uvx." >&2
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
181
|
+
fi
|
|
182
|
+
if ! _ensure_daemon "${SLM_CHOICE_BIN}"; then
|
|
183
|
+
echo "SLM plugin: unable to start the owned daemon; refusing a direct MCP writer." >&2
|
|
184
|
+
exit 1
|
|
185
|
+
fi
|
|
186
|
+
exec "${SLM_CHOICE_BIN}" mcp
|
|
187
|
+
;;
|
|
188
|
+
system:system)
|
|
189
|
+
_ensure_daemon "${SLM_CHOICE_BIN}" || true
|
|
190
|
+
exec slm mcp
|
|
191
|
+
;;
|
|
192
|
+
plugin:plugin)
|
|
193
|
+
if ! _ensure_daemon "${SLM_CHOICE_BIN}"; then
|
|
194
|
+
echo "SLM plugin: unable to start the owned daemon; refusing a direct MCP writer." >&2
|
|
195
|
+
exit 1
|
|
196
|
+
fi
|
|
197
|
+
exec "${SLM_CHOICE_BIN}" mcp
|
|
198
|
+
;;
|
|
199
|
+
*:explicit)
|
|
200
|
+
_ensure_daemon "${SLM_CHOICE_BIN}" || true
|
|
201
|
+
exec "${SLM_CHOICE_BIN}" mcp
|
|
202
|
+
;;
|
|
203
|
+
*)
|
|
204
|
+
echo "SLM plugin: internal error: unexpected launcher choice '${SLM_LAUNCHER:-auto}:${SLM_CHOICE}'." >&2
|
|
205
|
+
exit 1
|
|
206
|
+
;;
|
|
207
|
+
esac
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
:: slm-launch.bat — WP-F SuperLocalMemory MCP launcher (Windows)
|
|
3
|
+
::
|
|
4
|
+
:: Cross-platform counterpart: slm-launch (POSIX bash)
|
|
5
|
+
::
|
|
6
|
+
:: NOT reached through plugin/.mcp.json. That file names the extensionless
|
|
7
|
+
:: "${CLAUDE_PLUGIN_ROOT}/scripts/slm-launch", and a host that spawns it without
|
|
8
|
+
:: a shell (CreateProcess, Node spawn without shell) never resolves it to this
|
|
9
|
+
:: .bat: CreateProcess cannot start a batch file and only appends .exe to a
|
|
10
|
+
:: name without an extension. This file runs only when started through cmd.exe
|
|
11
|
+
:: (for example a hand-written MCP entry using "cmd /c"). It does not read
|
|
12
|
+
:: SLM_LAUNCHER; see MCP_JSON_NOTES.md and issue #139. Since 4.1.20 the plugin's
|
|
13
|
+
:: .mcp.json starts the INSTALLED slm on Windows through %ComSpec% instead.
|
|
14
|
+
:: Proven on the Windows CI runner: tests/test_plugin/test_windows_mcp_spawn_premise.py
|
|
15
|
+
::
|
|
16
|
+
:: Resolves the correct venv binary for Windows and joins the namespace daemon
|
|
17
|
+
:: before opening MCP, preserving one writer for parallel Claude sessions.
|
|
18
|
+
::
|
|
19
|
+
:: On Windows, Python venv places entry points in Scripts\ (not bin\ like POSIX).
|
|
20
|
+
:: This launcher bridges the path difference so ONE .mcp.json command field works
|
|
21
|
+
:: cross-platform.
|
|
22
|
+
::
|
|
23
|
+
:: Environment:
|
|
24
|
+
:: CLAUDE_PLUGIN_DATA — persistent data dir where venv lives
|
|
25
|
+
|
|
26
|
+
"%CLAUDE_PLUGIN_DATA%\venv\Scripts\slm.exe" serve start 1>&2
|
|
27
|
+
if errorlevel 1 (
|
|
28
|
+
echo SLM plugin: unable to start the owned daemon; refusing a direct MCP writer. 1>&2
|
|
29
|
+
exit /b 1
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
"%CLAUDE_PLUGIN_DATA%\venv\Scripts\slm.exe" mcp
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# slm-resolve.sh — which SuperLocalMemory install does this plugin use?
|
|
3
|
+
#
|
|
4
|
+
# Sourced, never run. ONE answer to that question, shared by every script that
|
|
5
|
+
# needs it:
|
|
6
|
+
# slm-launch the MCP server
|
|
7
|
+
# slm-run every lifecycle hook
|
|
8
|
+
# ensure-venv.sh the plugin venv bootstrap (it only builds what will be used)
|
|
9
|
+
# Before this file each of them decided on its own, and they disagreed: the
|
|
10
|
+
# launcher fell back to the plugin venv, the hooks only ever tried a bare `slm`,
|
|
11
|
+
# and the bootstrap built a venv even when nothing would run it.
|
|
12
|
+
#
|
|
13
|
+
# Environment (all optional):
|
|
14
|
+
# SLM_LAUNCHER auto (default) — prefer an installed slm, else the plugin venv
|
|
15
|
+
# system — require the slm on PATH
|
|
16
|
+
# plugin — require the plugin's own venv
|
|
17
|
+
# <path> — an explicit slm binary (leading ~ expanded)
|
|
18
|
+
# CLAUDE_PLUGIN_DATA where a plugin-owned venv lives, when there is one
|
|
19
|
+
#
|
|
20
|
+
# After slm_choose returns 0:
|
|
21
|
+
# SLM_CHOICE system | plugin | explicit
|
|
22
|
+
# SLM_CHOICE_BIN the binary that choice names
|
|
23
|
+
# SLM_CHOICE_READY 1 when SLM_CHOICE_BIN is executable now, else 0
|
|
24
|
+
# (a plugin venv that SessionStart has not built yet is 0)
|
|
25
|
+
# After slm_choose returns 1:
|
|
26
|
+
# SLM_RESOLVE_ERROR what was asked for and why it cannot be honoured
|
|
27
|
+
#
|
|
28
|
+
# Safe under `set -euo pipefail`: every variable read has a default.
|
|
29
|
+
|
|
30
|
+
# Expand a leading ~ WITHOUT eval — eval on an environment value is a
|
|
31
|
+
# command-injection foot-gun. Only tilde expansion; pass expanded paths for vars.
|
|
32
|
+
_slm_expand_tilde() {
|
|
33
|
+
case "$1" in
|
|
34
|
+
"~"*) printf '%s' "${HOME}${1#\~}" ;;
|
|
35
|
+
*) printf '%s' "$1" ;;
|
|
36
|
+
esac
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Where the plugin venv keeps slm. Empty when the host gives the plugin no data
|
|
40
|
+
# directory (only Claude Code sets CLAUDE_PLUGIN_DATA). A venv built on Windows
|
|
41
|
+
# keeps entry points in Scripts\ with an .exe suffix, and Git Bash is where these
|
|
42
|
+
# scripts run there.
|
|
43
|
+
#
|
|
44
|
+
# _slm_plugin_bin_var sets SLM_PLUGIN_BIN without a subshell (hooks run this on
|
|
45
|
+
# every tool call); slm_plugin_bin prints it, for callers that want a value.
|
|
46
|
+
_slm_plugin_bin_var() {
|
|
47
|
+
SLM_PLUGIN_BIN=""
|
|
48
|
+
[ -n "${CLAUDE_PLUGIN_DATA:-}" ] || return 0
|
|
49
|
+
case "${OSTYPE:-}" in
|
|
50
|
+
msys*|cygwin*|win32*) SLM_PLUGIN_BIN="${CLAUDE_PLUGIN_DATA}/venv/Scripts/slm.exe" ;;
|
|
51
|
+
*) SLM_PLUGIN_BIN="${CLAUDE_PLUGIN_DATA}/venv/bin/slm" ;;
|
|
52
|
+
esac
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
slm_plugin_bin() {
|
|
56
|
+
_slm_plugin_bin_var
|
|
57
|
+
printf '%s' "${SLM_PLUGIN_BIN}"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# The message for "nothing to run", naming both places that were looked in.
|
|
61
|
+
slm_not_found_message() {
|
|
62
|
+
local _pbin
|
|
63
|
+
_pbin="$(slm_plugin_bin)"
|
|
64
|
+
printf '%s\n' "SLM plugin: no SuperLocalMemory found."
|
|
65
|
+
printf '%s\n' " Looked for: 'slm' on PATH, and ${_pbin:-a plugin venv (this host sets no plugin data dir)}."
|
|
66
|
+
printf '%s' " Install it with: pipx install superlocalmemory"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_slm_set_choice() {
|
|
70
|
+
SLM_CHOICE="$1"
|
|
71
|
+
SLM_CHOICE_BIN="$2"
|
|
72
|
+
if [ -n "$2" ] && [ -x "$2" ]; then SLM_CHOICE_READY=1; else SLM_CHOICE_READY=0; fi
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
slm_choose() {
|
|
76
|
+
SLM_CHOICE=""
|
|
77
|
+
SLM_CHOICE_BIN=""
|
|
78
|
+
SLM_CHOICE_READY=0
|
|
79
|
+
SLM_RESOLVE_ERROR=""
|
|
80
|
+
local _pbin _sys
|
|
81
|
+
_slm_plugin_bin_var
|
|
82
|
+
_pbin="${SLM_PLUGIN_BIN}"
|
|
83
|
+
|
|
84
|
+
case "${SLM_LAUNCHER:-auto}" in
|
|
85
|
+
auto)
|
|
86
|
+
# 1. An slm already on PATH — the pip, pipx or npm install the user
|
|
87
|
+
# has. The common case, and the one that must not be forked.
|
|
88
|
+
if _sys="$(command -v slm 2>/dev/null)" && [ -n "${_sys}" ]; then
|
|
89
|
+
_slm_set_choice system "${_sys}"
|
|
90
|
+
return 0
|
|
91
|
+
fi
|
|
92
|
+
# 2. No system install: the plugin's own venv, if this host gives the
|
|
93
|
+
# plugin somewhere to keep one. It may not be built yet.
|
|
94
|
+
if [ -n "${_pbin}" ]; then
|
|
95
|
+
_slm_set_choice plugin "${_pbin}"
|
|
96
|
+
return 0
|
|
97
|
+
fi
|
|
98
|
+
# 3. Neither.
|
|
99
|
+
SLM_RESOLVE_ERROR="$(slm_not_found_message)"
|
|
100
|
+
return 1
|
|
101
|
+
;;
|
|
102
|
+
system)
|
|
103
|
+
if _sys="$(command -v slm 2>/dev/null)" && [ -n "${_sys}" ]; then
|
|
104
|
+
_slm_set_choice system "${_sys}"
|
|
105
|
+
return 0
|
|
106
|
+
fi
|
|
107
|
+
SLM_RESOLVE_ERROR="SLM_LAUNCHER=system but no 'slm' on PATH."
|
|
108
|
+
return 1
|
|
109
|
+
;;
|
|
110
|
+
plugin)
|
|
111
|
+
if [ -z "${_pbin}" ]; then
|
|
112
|
+
SLM_RESOLVE_ERROR="SLM_LAUNCHER=plugin but this host sets no CLAUDE_PLUGIN_DATA."
|
|
113
|
+
return 1
|
|
114
|
+
fi
|
|
115
|
+
_slm_set_choice plugin "${_pbin}"
|
|
116
|
+
return 0
|
|
117
|
+
;;
|
|
118
|
+
*)
|
|
119
|
+
_sys="$(_slm_expand_tilde "${SLM_LAUNCHER}")"
|
|
120
|
+
if [ ! -x "${_sys}" ]; then
|
|
121
|
+
SLM_RESOLVE_ERROR="SLM_LAUNCHER is not an executable slm binary: ${_sys}"
|
|
122
|
+
return 1
|
|
123
|
+
fi
|
|
124
|
+
_slm_set_choice explicit "${_sys}"
|
|
125
|
+
return 0
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
}
|