superlocalmemory 4.1.22 → 4.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +145 -0
  3. package/README.md +72 -46
  4. package/antigravity-plugin/_GENERATED.md +33 -0
  5. package/antigravity-plugin/agents/slm-governance-advisor.md +78 -0
  6. package/antigravity-plugin/agents/slm-loop-runner.md +82 -0
  7. package/antigravity-plugin/agents/slm-memory-advisor.md +50 -0
  8. package/antigravity-plugin/agents/slm-optimize-advisor.md +44 -0
  9. package/antigravity-plugin/commands/slm-loop.md +33 -0
  10. package/antigravity-plugin/hooks/hooks.json +59 -0
  11. package/antigravity-plugin/hooks.json +42 -0
  12. package/antigravity-plugin/mcp_config.json +14 -0
  13. package/antigravity-plugin/plugin.json +25 -0
  14. package/antigravity-plugin/scripts/antigravity_hook_adapter.py +85 -0
  15. package/antigravity-plugin/skills/slm-bot-memory/SKILL.md +146 -0
  16. package/antigravity-plugin/skills/slm-cache/SKILL.md +147 -0
  17. package/antigravity-plugin/skills/slm-compress/SKILL.md +150 -0
  18. package/antigravity-plugin/skills/slm-getting-started-bot/SKILL.md +146 -0
  19. package/antigravity-plugin/skills/slm-governance/SKILL.md +221 -0
  20. package/antigravity-plugin/skills/slm-graph/SKILL.md +327 -0
  21. package/antigravity-plugin/skills/slm-loop/SKILL.md +153 -0
  22. package/antigravity-plugin/skills/slm-mesh/SKILL.md +287 -0
  23. package/antigravity-plugin/skills/slm-profile/SKILL.md +159 -0
  24. package/antigravity-plugin/skills/slm-recall/SKILL.md +430 -0
  25. package/antigravity-plugin/skills/slm-remember/SKILL.md +370 -0
  26. package/antigravity-plugin/skills/slm-scope/SKILL.md +183 -0
  27. package/antigravity-plugin/skills/slm-session/SKILL.md +291 -0
  28. package/antigravity-plugin/skills/slm-status/SKILL.md +185 -0
  29. package/antigravity-plugin/skills/slm-web-access/SKILL.md +93 -0
  30. package/codex-plugin/.claude-plugin/plugin.json +19 -0
  31. package/codex-plugin/.codex/config.toml +40 -0
  32. package/codex-plugin/.codex-plugin/plugin.json +43 -0
  33. package/codex-plugin/.mcp.json +16 -0
  34. package/codex-plugin/AGENTS.md +161 -0
  35. package/codex-plugin/README.md +205 -0
  36. package/codex-plugin/_GENERATED.md +52 -0
  37. package/codex-plugin/agents/slm-governance-advisor.md +78 -0
  38. package/codex-plugin/agents/slm-loop-runner.md +82 -0
  39. package/codex-plugin/agents/slm-memory-advisor.md +50 -0
  40. package/codex-plugin/agents/slm-optimize-advisor.md +44 -0
  41. package/codex-plugin/commands/slm-loop.md +33 -0
  42. package/codex-plugin/hooks/hooks.json +59 -0
  43. package/codex-plugin/scripts/ensure-venv.bat +108 -0
  44. package/codex-plugin/scripts/ensure-venv.sh +100 -0
  45. package/codex-plugin/scripts/slm-launch +55 -0
  46. package/codex-plugin/scripts/slm-launch.bat +30 -0
  47. package/codex-plugin/skills/slm-bot-memory/SKILL.md +146 -0
  48. package/codex-plugin/skills/slm-cache/SKILL.md +147 -0
  49. package/codex-plugin/skills/slm-compress/SKILL.md +150 -0
  50. package/codex-plugin/skills/slm-getting-started-bot/SKILL.md +146 -0
  51. package/codex-plugin/skills/slm-governance/SKILL.md +221 -0
  52. package/codex-plugin/skills/slm-graph/SKILL.md +327 -0
  53. package/codex-plugin/skills/slm-loop/SKILL.md +153 -0
  54. package/codex-plugin/skills/slm-mesh/SKILL.md +287 -0
  55. package/codex-plugin/skills/slm-profile/SKILL.md +159 -0
  56. package/codex-plugin/skills/slm-recall/SKILL.md +430 -0
  57. package/codex-plugin/skills/slm-remember/SKILL.md +370 -0
  58. package/codex-plugin/skills/slm-scope/SKILL.md +183 -0
  59. package/codex-plugin/skills/slm-session/SKILL.md +291 -0
  60. package/codex-plugin/skills/slm-status/SKILL.md +185 -0
  61. package/codex-plugin/skills/slm-web-access/SKILL.md +93 -0
  62. package/copilot-plugin/.github/agents/slm-governance-advisor.agent.md +80 -0
  63. package/copilot-plugin/.github/agents/slm-loop-runner.agent.md +84 -0
  64. package/copilot-plugin/.github/agents/slm-memory-advisor.agent.md +52 -0
  65. package/copilot-plugin/.github/agents/slm-optimize-advisor.agent.md +46 -0
  66. package/copilot-plugin/.github/copilot-instructions.md +52 -0
  67. package/copilot-plugin/.github/hooks/slm-hooks.json +44 -0
  68. package/copilot-plugin/.github/prompts/slm-bot-memory.prompt.md +147 -0
  69. package/copilot-plugin/.github/prompts/slm-cache.prompt.md +151 -0
  70. package/copilot-plugin/.github/prompts/slm-compress.prompt.md +154 -0
  71. package/copilot-plugin/.github/prompts/slm-getting-started-bot.prompt.md +147 -0
  72. package/copilot-plugin/.github/prompts/slm-governance.prompt.md +224 -0
  73. package/copilot-plugin/.github/prompts/slm-graph.prompt.md +322 -0
  74. package/copilot-plugin/.github/prompts/slm-loop.prompt.md +159 -0
  75. package/copilot-plugin/.github/prompts/slm-mesh.prompt.md +290 -0
  76. package/copilot-plugin/.github/prompts/slm-profile.prompt.md +155 -0
  77. package/copilot-plugin/.github/prompts/slm-recall.prompt.md +434 -0
  78. package/copilot-plugin/.github/prompts/slm-remember.prompt.md +375 -0
  79. package/copilot-plugin/.github/prompts/slm-scope.prompt.md +183 -0
  80. package/copilot-plugin/.github/prompts/slm-session.prompt.md +291 -0
  81. package/copilot-plugin/.github/prompts/slm-status.prompt.md +190 -0
  82. package/copilot-plugin/.github/prompts/slm-web-access.prompt.md +97 -0
  83. package/copilot-plugin/.vscode/mcp.json +11 -0
  84. package/copilot-plugin/README.md +26 -0
  85. package/copilot-plugin/_GENERATED.md +49 -0
  86. package/copilot-plugin/scripts/ensure-venv.bat +122 -0
  87. package/copilot-plugin/scripts/ensure-venv.sh +169 -0
  88. package/copilot-plugin/scripts/slm-launch +207 -0
  89. package/copilot-plugin/scripts/slm-launch.bat +32 -0
  90. package/copilot-plugin/scripts/slm-resolve.sh +128 -0
  91. package/copilot-plugin/scripts/slm-run +76 -0
  92. package/copilot-plugin/scripts/torch-cpu-resolve.sh +86 -0
  93. package/docs/pi-dev-integration.md +0 -2
  94. package/hermes-plugin/README.md +30 -0
  95. package/hermes-plugin/_GENERATED.md +3 -0
  96. package/hermes-plugin/__init__.py +417 -0
  97. package/hermes-plugin/agents/slm-governance-advisor.md +78 -0
  98. package/hermes-plugin/agents/slm-loop-runner.md +82 -0
  99. package/hermes-plugin/agents/slm-memory-advisor.md +50 -0
  100. package/hermes-plugin/agents/slm-optimize-advisor.md +44 -0
  101. package/hermes-plugin/command-inventory.json +7 -0
  102. package/hermes-plugin/plugin.yaml +24 -0
  103. package/hermes-plugin/remote.py +390 -0
  104. package/hermes-plugin/skills/slm-bot-memory/SKILL.md +146 -0
  105. package/hermes-plugin/skills/slm-cache/SKILL.md +147 -0
  106. package/hermes-plugin/skills/slm-compress/SKILL.md +150 -0
  107. package/hermes-plugin/skills/slm-getting-started-bot/SKILL.md +146 -0
  108. package/hermes-plugin/skills/slm-governance/SKILL.md +221 -0
  109. package/hermes-plugin/skills/slm-graph/SKILL.md +327 -0
  110. package/hermes-plugin/skills/slm-loop/SKILL.md +153 -0
  111. package/hermes-plugin/skills/slm-mesh/SKILL.md +287 -0
  112. package/hermes-plugin/skills/slm-profile/SKILL.md +159 -0
  113. package/hermes-plugin/skills/slm-recall/SKILL.md +430 -0
  114. package/hermes-plugin/skills/slm-remember/SKILL.md +370 -0
  115. package/hermes-plugin/skills/slm-scope/SKILL.md +183 -0
  116. package/hermes-plugin/skills/slm-session/SKILL.md +291 -0
  117. package/hermes-plugin/skills/slm-status/SKILL.md +185 -0
  118. package/hermes-plugin/skills/slm-web-access/SKILL.md +93 -0
  119. package/package.json +9 -18
  120. package/plugin/.claude-plugin/plugin.json +1 -1
  121. package/plugin/.cursor-plugin/plugin.json +1 -1
  122. package/plugin/CLAUDE.md +14 -12
  123. package/plugin/agents/slm-governance-advisor.md +14 -16
  124. package/plugin/agents/slm-loop-runner.md +22 -11
  125. package/plugin/agents/slm-memory-advisor.md +14 -14
  126. package/plugin/agents/slm-optimize-advisor.md +5 -5
  127. package/plugin/commands/slm-loop.md +5 -3
  128. package/plugin/cursor-skills/slm-bot-memory/SKILL.md +19 -12
  129. package/plugin/cursor-skills/slm-getting-started-bot/SKILL.md +17 -9
  130. package/plugin/cursor-skills/slm-recall/SKILL.md +151 -54
  131. package/plugin/cursor-skills/slm-remember/SKILL.md +132 -35
  132. package/plugin/cursor-skills/slm-scope/SKILL.md +24 -17
  133. package/plugin/cursor-skills/slm-session/SKILL.md +77 -57
  134. package/plugin/mcp.cursor.json +1 -1
  135. package/plugin/requirements.txt +1 -1
  136. package/plugin/skills/slm-bot-memory/SKILL.md +19 -12
  137. package/plugin/skills/slm-cache/SKILL.md +6 -7
  138. package/plugin/skills/slm-compress/SKILL.md +3 -3
  139. package/plugin/skills/slm-getting-started-bot/SKILL.md +17 -9
  140. package/plugin/skills/slm-governance/SKILL.md +126 -153
  141. package/plugin/skills/slm-graph/SKILL.md +27 -15
  142. package/plugin/skills/slm-loop/SKILL.md +74 -20
  143. package/plugin/skills/slm-mesh/SKILL.md +17 -12
  144. package/plugin/skills/slm-profile/SKILL.md +101 -92
  145. package/plugin/skills/slm-recall/SKILL.md +151 -54
  146. package/plugin/skills/slm-remember/SKILL.md +132 -35
  147. package/plugin/skills/slm-scope/SKILL.md +24 -17
  148. package/plugin/skills/slm-session/SKILL.md +77 -57
  149. package/plugin/skills/slm-status/SKILL.md +54 -35
  150. package/plugin/skills/slm-web-access/SKILL.md +93 -0
  151. package/plugin-src/agents/slm-governance-advisor.md +78 -0
  152. package/plugin-src/agents/slm-loop-runner.md +82 -0
  153. package/plugin-src/agents/slm-memory-advisor.md +14 -14
  154. package/plugin-src/agents/slm-optimize-advisor.md +5 -5
  155. package/plugin-src/commands/slm-loop.md +33 -0
  156. package/plugin-src/rules/AGENTS.md +45 -36
  157. package/plugin-src/rules/CLAUDE.md.fragment +47 -0
  158. package/plugin-src/skills/slm-bot-memory/SKILL.md +19 -12
  159. package/plugin-src/skills/slm-cache/SKILL.md +6 -7
  160. package/plugin-src/skills/slm-compress/SKILL.md +3 -3
  161. package/plugin-src/skills/slm-getting-started-bot/SKILL.md +17 -9
  162. package/plugin-src/skills/slm-governance/SKILL.md +126 -153
  163. package/plugin-src/skills/slm-graph/SKILL.md +27 -15
  164. package/plugin-src/skills/slm-loop/SKILL.md +74 -20
  165. package/plugin-src/skills/slm-mesh/SKILL.md +17 -12
  166. package/plugin-src/skills/slm-profile/SKILL.md +101 -92
  167. package/plugin-src/skills/slm-recall/SKILL.md +151 -54
  168. package/plugin-src/skills/slm-remember/SKILL.md +132 -35
  169. package/plugin-src/skills/slm-scope/SKILL.md +24 -17
  170. package/plugin-src/skills/slm-session/SKILL.md +77 -57
  171. package/plugin-src/skills/slm-status/SKILL.md +54 -35
  172. package/plugin-src/skills/slm-web-access/SKILL.md +93 -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
+ }