superlocalmemory 4.1.18 → 4.1.20
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 +3 -3
- package/CHANGELOG.md +183 -0
- package/README.md +132 -661
- package/bin/slm-npm +1 -1
- package/ide/configs/cursor-mcp.json +4 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.mcp.json +7 -2
- package/plugin/CLAUDE.md +4 -4
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/hooks/hooks.json +9 -9
- package/plugin/requirements.txt +1 -1
- package/plugin/scripts/ensure-venv.bat +5 -5
- package/plugin/scripts/ensure-venv.sh +27 -5
- package/plugin/scripts/slm-launch +60 -68
- package/plugin/scripts/slm-launch.bat +10 -1
- package/plugin/scripts/slm-resolve.sh +128 -0
- package/plugin/scripts/slm-run +76 -0
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +4 -4
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +1 -1
- package/plugin-src/agents/slm-optimize-advisor.md +1 -1
- package/plugin-src/rules/AGENTS.md +22 -11
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/SKILL.md +4 -4
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/scripts/postinstall.js +47 -16
package/bin/slm-npm
CHANGED
|
@@ -28,7 +28,7 @@ if (!fs.existsSync(runtimePython)) {
|
|
|
28
28
|
console.error(`Expected the package-owned interpreter at: ${runtimePython}`);
|
|
29
29
|
console.error('Repair it without touching system Python by running:');
|
|
30
30
|
console.error(' npm rebuild superlocalmemory');
|
|
31
|
-
console.error('If Python is missing, install Python 3.
|
|
31
|
+
console.error('If Python is missing, install Python 3.12-3.14 first.');
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Qualixar",
|
|
4
4
|
"url": "https://github.com/qualixar/superlocalmemory"
|
|
5
5
|
},
|
|
6
|
-
"description": "Local-first agent memory + reversible context compression and KV cache, as an MCP server.
|
|
6
|
+
"description": "Local-first agent memory + reversible context compression and KV cache, as an MCP server. 38-tool code profile with graph intelligence.",
|
|
7
7
|
"displayName": "SuperLocalMemory",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"memory",
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
"mcpServers": "./.mcp.json",
|
|
16
16
|
"name": "superlocalmemory",
|
|
17
17
|
"repository": "https://github.com/qualixar/superlocalmemory",
|
|
18
|
-
"version": "4.1.
|
|
18
|
+
"version": "4.1.20"
|
|
19
19
|
}
|
package/plugin/.mcp.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"superlocalmemory": {
|
|
4
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/slm-launch",
|
|
5
|
-
"args": [
|
|
4
|
+
"command": "${ComSpec:-${CLAUDE_PLUGIN_ROOT}/scripts/slm-launch}",
|
|
5
|
+
"args": [
|
|
6
|
+
"/d",
|
|
7
|
+
"/s",
|
|
8
|
+
"/c",
|
|
9
|
+
"where.exe /q slm || (echo SLM plugin: slm is not on PATH. On Windows the plugin runs the SuperLocalMemory you installed. Install it first with: pipx install superlocalmemory or: pip install superlocalmemory then restart your editor. 1>&2 & exit 1) & slm serve start 1>&2 & slm mcp"
|
|
10
|
+
],
|
|
6
11
|
"env": {
|
|
7
12
|
"SLM_AGENT_ID": "claude_code"
|
|
8
13
|
}
|
package/plugin/CLAUDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<!-- BEGIN SuperLocalMemory v4.1.
|
|
1
|
+
<!-- BEGIN SuperLocalMemory v4.1.20 -->
|
|
2
2
|
|
|
3
3
|
## SuperLocalMemory (SLM) — Agent Rules
|
|
4
4
|
|
|
5
|
-
SLM is local-first memory for agents. All tools run on the user's machine; nothing goes to the cloud unless the user chose Mode C
|
|
5
|
+
SLM is local-first memory for agents. All tools run on the user's machine; nothing goes to the cloud unless the user chose Mode C, turned on the online answer check, or separately opted into Jev memory-kind typing (its own consent, on top of the answer check already using Jev).
|
|
6
6
|
|
|
7
7
|
### Session start
|
|
8
8
|
Call `session_init(project_path, query)` ONCE per fresh session before any recall/remember. Never twice.
|
|
@@ -40,6 +40,6 @@ slm-recall · slm-remember · slm-session · slm-status · slm-cache · slm-comp
|
|
|
40
40
|
### Subagents
|
|
41
41
|
slm-memory-advisor (memory decisions, session hygiene, scope/profile guidance) · slm-optimize-advisor (context compression + KV cache) · slm-governance-advisor (scope/roles/compliance/GDPR)
|
|
42
42
|
|
|
43
|
-
<!-- END SuperLocalMemory v4.1.
|
|
43
|
+
<!-- END SuperLocalMemory v4.1.20 -->
|
|
44
44
|
|
|
45
|
-
SuperLocalMemory v4.1.
|
|
45
|
+
SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
|
|
@@ -77,4 +77,4 @@ slm-scope · slm-governance · slm-profile · slm-remember · slm-recall
|
|
|
77
77
|
# What NOT to do
|
|
78
78
|
Never session_init twice; never forget without dry-run preview; never store secrets; never bypass role checks; never claim an erasure succeeded without verifying via recall.
|
|
79
79
|
|
|
80
|
-
SuperLocalMemory v4.1.
|
|
80
|
+
SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
|
|
@@ -47,4 +47,4 @@ slm-recall · slm-remember · slm-session · slm-scope · slm-profile · slm-gov
|
|
|
47
47
|
# What NOT to do
|
|
48
48
|
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; never bypass scope or governance restrictions.
|
|
49
49
|
|
|
50
|
-
SuperLocalMemory v4.1.
|
|
50
|
+
SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
|
|
@@ -41,4 +41,4 @@ slm-compress · slm-cache · slm-status · slm-profile
|
|
|
41
41
|
# What NOT to do
|
|
42
42
|
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 %; never carry ccr_ids across profile switches.
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v4.1.
|
|
44
|
+
SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
|
package/plugin/hooks/hooks.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "slm hook mandate
|
|
8
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook mandate || true",
|
|
9
9
|
"timeout": 5000
|
|
10
10
|
}
|
|
11
11
|
]
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"hooks": [
|
|
23
23
|
{
|
|
24
24
|
"type": "command",
|
|
25
|
-
"command": "slm hook start
|
|
25
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook start || true",
|
|
26
26
|
"timeout": 15000
|
|
27
27
|
}
|
|
28
28
|
]
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"hooks": [
|
|
35
35
|
{
|
|
36
36
|
"type": "command",
|
|
37
|
-
"command": "slm hook checkpoint
|
|
37
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook checkpoint || true",
|
|
38
38
|
"timeout": 5000
|
|
39
39
|
}
|
|
40
40
|
]
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"hooks": [
|
|
45
45
|
{
|
|
46
46
|
"type": "command",
|
|
47
|
-
"command": "slm hook post_tool_outcome
|
|
47
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook post_tool_outcome || true",
|
|
48
48
|
"timeout": 5000
|
|
49
49
|
}
|
|
50
50
|
]
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"hooks": [
|
|
56
56
|
{
|
|
57
57
|
"type": "command",
|
|
58
|
-
"command": "slm hook user_prompt_rehash
|
|
58
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook user_prompt_rehash || true",
|
|
59
59
|
"timeout": 5000
|
|
60
60
|
}
|
|
61
61
|
]
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"hooks": [
|
|
65
65
|
{
|
|
66
66
|
"type": "command",
|
|
67
|
-
"command": "slm hook topic_shift
|
|
67
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook topic_shift || true",
|
|
68
68
|
"timeout": 3000
|
|
69
69
|
}
|
|
70
70
|
]
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
"hooks": [
|
|
76
76
|
{
|
|
77
77
|
"type": "command",
|
|
78
|
-
"command": "slm hook stop
|
|
78
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook stop || true",
|
|
79
79
|
"timeout": 10000
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
"type": "command",
|
|
83
|
-
"command": "slm hook stop_outcome
|
|
83
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook stop_outcome || true",
|
|
84
84
|
"timeout": 10000
|
|
85
85
|
}
|
|
86
86
|
]
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"hooks": [
|
|
93
93
|
{
|
|
94
94
|
"type": "command",
|
|
95
|
-
"command": "slm hook before_web
|
|
95
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/slm-run\" hook before_web || true",
|
|
96
96
|
"timeout": 5000
|
|
97
97
|
}
|
|
98
98
|
]
|
package/plugin/requirements.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
superlocalmemory==4.1.
|
|
1
|
+
superlocalmemory==4.1.20
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
:: Exit codes: 0 = venv ready non-0 = failure (logged to stderr)
|
|
13
13
|
:: All informational output goes to stderr (stdout reserved for MCP stdio protocol).
|
|
14
14
|
::
|
|
15
|
-
:: Requires: Python 3.
|
|
15
|
+
:: Requires: Python 3.12+ on PATH, pip, standard Windows cmd.exe
|
|
16
16
|
|
|
17
17
|
setlocal EnableDelayedExpansion
|
|
18
18
|
|
|
@@ -29,13 +29,13 @@ if not defined CLAUDE_PLUGIN_DATA (
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
:: ---------------------------------------------------------------------------
|
|
32
|
-
:: Python >= 3.
|
|
32
|
+
:: Python >= 3.12 guard
|
|
33
33
|
:: ---------------------------------------------------------------------------
|
|
34
|
-
python -c "import sys; sys.exit(0 if sys.version_info >= (3,
|
|
34
|
+
python -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)" 2>nul
|
|
35
35
|
if errorlevel 1 (
|
|
36
36
|
for /f "tokens=*" %%v in ('python --version 2^>^&1') do set PY_VER=%%v
|
|
37
|
-
echo ERROR: SuperLocalMemory plugin requires Python ^>= 3.
|
|
38
|
-
echo Install Python 3.
|
|
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
39
|
exit /b 1
|
|
40
40
|
)
|
|
41
41
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# CLAUDE_PLUGIN_ROOT — plugin installation dir (ephemeral, contains scripts/ + requirements.txt)
|
|
9
9
|
# CLAUDE_PLUGIN_DATA — persistent data dir (venv lives here, survives plugin updates)
|
|
10
10
|
#
|
|
11
|
-
# Exit codes: 0 = venv ready non-0 = failure (logged to stderr)
|
|
11
|
+
# Exit codes: 0 = venv ready, or not needed non-0 = failure (logged to stderr)
|
|
12
12
|
# All output goes to stderr only (stdout reserved for MCP stdio protocol).
|
|
13
13
|
|
|
14
14
|
set -euo pipefail
|
|
@@ -25,12 +25,34 @@ set -euo pipefail
|
|
|
25
25
|
exec 1>&2
|
|
26
26
|
|
|
27
27
|
# ---------------------------------------------------------------------------
|
|
28
|
-
#
|
|
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
|
|
29
51
|
# ---------------------------------------------------------------------------
|
|
30
|
-
if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3,
|
|
52
|
+
if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)" 2>/dev/null; then
|
|
31
53
|
PY_VER=$(python3 --version 2>&1 || echo "unknown")
|
|
32
|
-
echo "ERROR: SuperLocalMemory plugin requires Python >= 3.
|
|
33
|
-
echo "Install Python 3.
|
|
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
|
|
34
56
|
exit 1
|
|
35
57
|
fi
|
|
36
58
|
|
|
@@ -31,7 +31,12 @@
|
|
|
31
31
|
# user's decision, and a plugin that narrows it takes away tools they configured.
|
|
32
32
|
|
|
33
33
|
_SLM_DATA="${SLM_DATA_DIR:-$HOME/.superlocalmemory}"
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
# Which slm to run is decided in ONE place, shared with the hooks and the venv
|
|
36
|
+
# bootstrap, so the three can never disagree about which install is in play.
|
|
37
|
+
# shellcheck source=slm-resolve.sh
|
|
38
|
+
. "$(dirname "${BASH_SOURCE[0]}")/slm-resolve.sh"
|
|
39
|
+
_PLUGIN_BIN="$(slm_plugin_bin)"
|
|
35
40
|
|
|
36
41
|
# Is a daemon already up for this data dir? If so we join it rather than boot a
|
|
37
42
|
# second one — one writer for every session that shares the store.
|
|
@@ -41,91 +46,78 @@ if [ -f "${_SLM_DATA}/daemon.pid" ] \
|
|
|
41
46
|
_daemon_running=1
|
|
42
47
|
fi
|
|
43
48
|
|
|
44
|
-
# Expand a leading ~ WITHOUT eval — eval on an environment value is a
|
|
45
|
-
# command-injection foot-gun. Only tilde expansion; pass expanded paths for vars.
|
|
46
|
-
_expand_tilde() {
|
|
47
|
-
case "$1" in
|
|
48
|
-
"~"*) printf '%s' "${HOME}${1#\~}" ;;
|
|
49
|
-
*) printf '%s' "$1" ;;
|
|
50
|
-
esac
|
|
51
|
-
}
|
|
52
|
-
|
|
53
49
|
# Start a daemon with the chosen binary unless one is already serving.
|
|
54
50
|
_ensure_daemon() {
|
|
55
51
|
[ "${_daemon_running}" -eq 1 ] && return 0
|
|
56
52
|
"$1" serve start >&2
|
|
57
53
|
}
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
echo " One store, one version. Remove the venv to stop it drifting: rm -rf \"${CLAUDE_PLUGIN_DATA}/venv\"" >&2
|
|
82
|
-
fi
|
|
83
|
-
fi
|
|
84
|
-
_ensure_daemon "$(command -v slm)" || \
|
|
85
|
-
echo "SLM plugin: daemon did not start; continuing as a direct MCP writer." >&2
|
|
86
|
-
exec slm mcp
|
|
87
|
-
fi
|
|
88
|
-
# 2. No system install. Use the plugin's own venv if this host gave us
|
|
89
|
-
# somewhere to keep one.
|
|
55
|
+
if ! slm_choose; then
|
|
56
|
+
echo "${SLM_RESOLVE_ERROR}" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
case "${SLM_LAUNCHER:-auto}:${SLM_CHOICE}" in
|
|
61
|
+
auto:system)
|
|
62
|
+
# An slm already on PATH — the pip, pipx or npm install the user has.
|
|
63
|
+
# This is the common case and the one that must not be forked.
|
|
64
|
+
#
|
|
65
|
+
# Both orderings end up here, and both must converge on ONE
|
|
66
|
+
# environment:
|
|
67
|
+
# installed first, plugin second -> nothing was ever forked;
|
|
68
|
+
# plugin first, installed second -> the plugin venv is now the
|
|
69
|
+
# older copy and must stop being used.
|
|
70
|
+
# They already share one store, because this launcher sets no
|
|
71
|
+
# SLM_DATA_DIR and both resolve ~/.superlocalmemory. What is left to
|
|
72
|
+
# go wrong is two DIFFERENT VERSIONS serving that one store: whoever
|
|
73
|
+
# starts the daemon first decides which code the other one talks to.
|
|
74
|
+
# So when a leftover plugin venv is found beside a real install, say
|
|
75
|
+
# so, with both versions named. Silent duplication is how a store
|
|
76
|
+
# ends up served by a release nobody thinks they are running.
|
|
90
77
|
if [ -n "${_PLUGIN_BIN}" ] && [ -x "${_PLUGIN_BIN}" ]; then
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
_sys_v="$(slm --version 2>/dev/null | awk '{print $NF}')"
|
|
79
|
+
_plg_v="$("${_PLUGIN_BIN}" --version 2>/dev/null | awk '{print $NF}')"
|
|
80
|
+
if [ -n "${_plg_v}" ] && [ "${_sys_v}" != "${_plg_v}" ]; then
|
|
81
|
+
echo "SLM plugin: using the installed slm ${_sys_v:-?} and IGNORING a leftover plugin venv at ${_PLUGIN_BIN} (${_plg_v})." >&2
|
|
82
|
+
echo " One store, one version. Remove the venv to stop it drifting: rm -rf \"${CLAUDE_PLUGIN_DATA}/venv\"" >&2
|
|
94
83
|
fi
|
|
95
|
-
exec "${_PLUGIN_BIN}" mcp
|
|
96
84
|
fi
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
echo " Looked for: 'slm' on PATH, and ${_PLUGIN_BIN:-a plugin venv (this host sets no plugin data dir)}." >&2
|
|
100
|
-
echo " Install it with: pipx install superlocalmemory" >&2
|
|
101
|
-
exit 1
|
|
102
|
-
;;
|
|
103
|
-
system)
|
|
104
|
-
if ! command -v slm >/dev/null 2>&1; then
|
|
105
|
-
echo "SLM_LAUNCHER=system but no 'slm' on PATH." >&2
|
|
106
|
-
exit 1
|
|
107
|
-
fi
|
|
108
|
-
_ensure_daemon "$(command -v slm)" || true
|
|
85
|
+
_ensure_daemon "${SLM_CHOICE_BIN}" || \
|
|
86
|
+
echo "SLM plugin: daemon did not start; continuing as a direct MCP writer." >&2
|
|
109
87
|
exec slm mcp
|
|
110
88
|
;;
|
|
111
|
-
plugin)
|
|
112
|
-
|
|
113
|
-
|
|
89
|
+
auto:plugin)
|
|
90
|
+
# No system install. Use the plugin's own venv, once SessionStart has
|
|
91
|
+
# built it. If it is not there either, say which two things were looked
|
|
92
|
+
# for, so the fix is obvious.
|
|
93
|
+
if [ "${SLM_CHOICE_READY}" -ne 1 ]; then
|
|
94
|
+
slm_not_found_message >&2
|
|
95
|
+
echo >&2
|
|
114
96
|
exit 1
|
|
115
97
|
fi
|
|
116
|
-
if ! _ensure_daemon "${
|
|
98
|
+
if ! _ensure_daemon "${SLM_CHOICE_BIN}"; then
|
|
117
99
|
echo "SLM plugin: unable to start the owned daemon; refusing a direct MCP writer." >&2
|
|
118
100
|
exit 1
|
|
119
101
|
fi
|
|
120
|
-
exec "${
|
|
102
|
+
exec "${SLM_CHOICE_BIN}" mcp
|
|
121
103
|
;;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
104
|
+
system:system)
|
|
105
|
+
_ensure_daemon "${SLM_CHOICE_BIN}" || true
|
|
106
|
+
exec slm mcp
|
|
107
|
+
;;
|
|
108
|
+
plugin:plugin)
|
|
109
|
+
if ! _ensure_daemon "${SLM_CHOICE_BIN}"; then
|
|
110
|
+
echo "SLM plugin: unable to start the owned daemon; refusing a direct MCP writer." >&2
|
|
126
111
|
exit 1
|
|
127
112
|
fi
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
exec "${SLM_CHOICE_BIN}" mcp
|
|
114
|
+
;;
|
|
115
|
+
*:explicit)
|
|
116
|
+
_ensure_daemon "${SLM_CHOICE_BIN}" || true
|
|
117
|
+
exec "${SLM_CHOICE_BIN}" mcp
|
|
118
|
+
;;
|
|
119
|
+
*)
|
|
120
|
+
echo "SLM plugin: internal error: unexpected launcher choice '${SLM_LAUNCHER:-auto}:${SLM_CHOICE}'." >&2
|
|
121
|
+
exit 1
|
|
130
122
|
;;
|
|
131
123
|
esac
|
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
:: slm-launch.bat — WP-F SuperLocalMemory MCP launcher (Windows)
|
|
3
3
|
::
|
|
4
4
|
:: Cross-platform counterpart: slm-launch (POSIX bash)
|
|
5
|
-
::
|
|
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
|
|
6
15
|
::
|
|
7
16
|
:: Resolves the correct venv binary for Windows and joins the namespace daemon
|
|
8
17
|
:: before opening MCP, preserving one writer for parallel Claude sessions.
|
|
@@ -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
|
+
}
|