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.
Files changed (49) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/CHANGELOG.md +183 -0
  3. package/README.md +132 -661
  4. package/bin/slm-npm +1 -1
  5. package/ide/configs/cursor-mcp.json +4 -1
  6. package/package.json +1 -1
  7. package/plugin/.claude-plugin/plugin.json +2 -2
  8. package/plugin/.mcp.json +7 -2
  9. package/plugin/CLAUDE.md +4 -4
  10. package/plugin/agents/slm-governance-advisor.md +1 -1
  11. package/plugin/agents/slm-loop-runner.md +1 -1
  12. package/plugin/agents/slm-memory-advisor.md +1 -1
  13. package/plugin/agents/slm-optimize-advisor.md +1 -1
  14. package/plugin/hooks/hooks.json +9 -9
  15. package/plugin/requirements.txt +1 -1
  16. package/plugin/scripts/ensure-venv.bat +5 -5
  17. package/plugin/scripts/ensure-venv.sh +27 -5
  18. package/plugin/scripts/slm-launch +60 -68
  19. package/plugin/scripts/slm-launch.bat +10 -1
  20. package/plugin/scripts/slm-resolve.sh +128 -0
  21. package/plugin/scripts/slm-run +76 -0
  22. package/plugin/skills/slm-cache/SKILL.md +1 -1
  23. package/plugin/skills/slm-compress/SKILL.md +1 -1
  24. package/plugin/skills/slm-governance/SKILL.md +1 -1
  25. package/plugin/skills/slm-graph/SKILL.md +1 -1
  26. package/plugin/skills/slm-loop/SKILL.md +1 -1
  27. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  28. package/plugin/skills/slm-profile/SKILL.md +4 -4
  29. package/plugin/skills/slm-recall/SKILL.md +1 -1
  30. package/plugin/skills/slm-remember/SKILL.md +1 -1
  31. package/plugin/skills/slm-scope/SKILL.md +1 -1
  32. package/plugin/skills/slm-session/SKILL.md +1 -1
  33. package/plugin/skills/slm-status/SKILL.md +1 -1
  34. package/plugin-src/agents/slm-memory-advisor.md +1 -1
  35. package/plugin-src/agents/slm-optimize-advisor.md +1 -1
  36. package/plugin-src/rules/AGENTS.md +22 -11
  37. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  38. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  39. package/plugin-src/skills/slm-governance/SKILL.md +1 -1
  40. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  41. package/plugin-src/skills/slm-loop/SKILL.md +1 -1
  42. package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
  43. package/plugin-src/skills/slm-profile/SKILL.md +4 -4
  44. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  45. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  46. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  47. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  48. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  49. package/scripts/postinstall.js +47 -16
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # slm-run — run slm for a plugin hook, using the slm the MCP server uses.
3
+ #
4
+ # "${CLAUDE_PLUGIN_ROOT}/scripts/slm-run" hook <name>
5
+ #
6
+ # Hooks used to call a bare `slm`, so on a machine where SuperLocalMemory lives
7
+ # only in the plugin venv every hook was "command not found" — and the error was
8
+ # thrown away, so memory capture stopped with nothing to say why. This wrapper
9
+ # resolves slm with the launcher's own code (slm-resolve.sh), honours the same
10
+ # SLM_LAUNCHER, and passes stdin, stdout and the arguments straight through.
11
+ #
12
+ # A hook must never break the session that runs it, so this ALWAYS exits 0
13
+ # (Claude Code reads exit 2 as "block"). When slm cannot be found, or fails, the
14
+ # reason goes to
15
+ # ${SLM_DATA_DIR:-~/.superlocalmemory}/logs/plugin-hooks.log
16
+ # and one line naming that file goes to stderr. A hook that succeeds is silent:
17
+ # whatever slm wrote to stderr is discarded, exactly as before.
18
+
19
+ _slm_args="$*"
20
+ _log_dir="${SLM_DATA_DIR:-${HOME}/.superlocalmemory}/logs"
21
+ _log="${_log_dir}/plugin-hooks.log"
22
+ _LOG_MAX_BYTES=1048576
23
+
24
+ # Append one entry. Best effort: an unwritable log must not fail the hook.
25
+ _log_entry() {
26
+ {
27
+ mkdir -p "${_log_dir}" || return 1
28
+ if [ -f "${_log}" ] && [ "$(wc -c < "${_log}")" -gt "${_LOG_MAX_BYTES}" ]; then
29
+ mv -f "${_log}" "${_log}.1"
30
+ fi
31
+ printf '%s slm %s: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "${_slm_args}" "$1" >> "${_log}"
32
+ } 2>/dev/null
33
+ }
34
+
35
+ # Record a failure, tell stderr where to look, and leave the session alone.
36
+ _give_up() {
37
+ if _log_entry "$1"; then
38
+ echo "SLM plugin: hook 'slm ${_slm_args}' did not run cleanly; see ${_log}" >&2
39
+ else
40
+ echo "SLM plugin: hook 'slm ${_slm_args}' did not run cleanly: $1" >&2
41
+ fi
42
+ exit 0
43
+ }
44
+
45
+ _here="${BASH_SOURCE[0]%/*}"
46
+ [ "${_here}" = "${BASH_SOURCE[0]}" ] && _here=.
47
+ # shellcheck source=slm-resolve.sh
48
+ if ! . "${_here}/slm-resolve.sh" 2>/dev/null; then
49
+ _give_up "the plugin is incomplete: ${_here}/slm-resolve.sh is missing"
50
+ fi
51
+
52
+ if ! slm_choose; then
53
+ _give_up "$(printf '%s' "${SLM_RESOLVE_ERROR}" | tr '\n' ' ')"
54
+ fi
55
+
56
+ if [ "${SLM_CHOICE_READY}" -ne 1 ]; then
57
+ if [ "${SLM_LAUNCHER:-auto}" = "auto" ]; then
58
+ _give_up "$(slm_not_found_message | tr '\n' ' ') (A plugin venv is built by SessionStart; the first session can run its hooks before it exists.)"
59
+ fi
60
+ _give_up "${SLM_CHOICE} slm is not executable: ${SLM_CHOICE_BIN}"
61
+ fi
62
+
63
+ # slm's stderr is kept only long enough to report a failure. The trap removes
64
+ # it however this script ends, including a host timeout killing the hook.
65
+ _err="$(mktemp "${TMPDIR:-/tmp}/slm-hook.XXXXXX" 2>/dev/null)" || _err=/dev/null
66
+ trap '[ "${_err}" = /dev/null ] || rm -f "${_err}"' EXIT
67
+ trap 'exit 0' HUP INT TERM
68
+
69
+ "${SLM_CHOICE_BIN}" "$@" 2>"${_err}"
70
+ _rc=$?
71
+ if [ "${_rc}" -ne 0 ]; then
72
+ _detail=""
73
+ [ "${_err}" = /dev/null ] || _detail="$(tail -n 5 "${_err}" 2>/dev/null | tr '\n' ' ')"
74
+ _give_up "exit ${_rc} from ${SLM_CHOICE_BIN} (${SLM_CHOICE})${_detail:+: ${_detail}}"
75
+ fi
76
+ exit 0
@@ -145,4 +145,4 @@ These subcommands control daemon-level cache settings. They do not read or write
145
145
 
146
146
  ---
147
147
 
148
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -147,4 +147,4 @@ Content over 1 MB (1 000 000 bytes UTF-8) is processed but `reversible` is force
147
147
 
148
148
  ---
149
149
 
150
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -245,4 +245,4 @@ Before running any destructive operation (`forget`, `compact_memories`):
245
245
 
246
246
  ---
247
247
 
248
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -312,4 +312,4 @@ profile. See `slm-profile` for the full profile switching workflow.
312
312
 
313
313
  ---
314
314
 
315
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -96,4 +96,4 @@ paused, name the approval needed; when errored, quote the short detail.
96
96
 
97
97
  ---
98
98
 
99
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -31,9 +31,9 @@ project, a client engagement, a production vs staging environment.
31
31
  | Profile | Tools | When to use |
32
32
  |---------|-------|-------------|
33
33
  | `core` | 18 tools — remember, recall, search, session, optimize | Minimal footprint, no code tools |
34
- | `code` | 34 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
- | `full` | 50 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
- | `power` | 62 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
34
+ | `code` | 38 tools — core + portable Brain evidence + code graph + profile switching + bounded loops + memory-kind management | Default for IDE/coding agents |
35
+ | `full` | 54 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
+ | `power` | 66 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
37
37
  | `mesh` | 8 tools — mesh coordination only | Lightweight cross-session signalling |
38
38
 
39
39
  The profile is set at MCP server startup via `SLM_MCP_PROFILE` in the MCP config.
@@ -146,4 +146,4 @@ Name them differently in your MCP config (e.g. `superlocalmemory-personal` and
146
146
 
147
147
  ---
148
148
 
149
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -330,4 +330,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
330
330
 
331
331
  ---
332
332
 
333
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
333
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -270,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
270
270
 
271
271
  ---
272
272
 
273
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -173,4 +173,4 @@ to review the impact. See `slm-remember` for the full deletion discipline.
173
173
 
174
174
  ---
175
175
 
176
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -268,4 +268,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
268
268
 
269
269
  ---
270
270
 
271
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
271
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -163,4 +163,4 @@ multi-profile setup. To switch the active profile, see `slm-profile`.
163
163
 
164
164
  ---
165
165
 
166
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
166
+ 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.18 · Qualixar · AGPL-3.0-or-later
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.18 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -83,16 +83,27 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
83
83
 
84
84
  ## Tool reference (core profile — 18 tools)
85
85
 
86
- > The MCP config ships `SLM_MCP_PROFILE=code` and `SLM_AGENT_ID=claude_code` (34 tools):
87
- > the 18 core tools below **plus** 6 code-graph tools (`build_code_graph`,
88
- > `get_blast_radius`, `query_graph`, `semantic_search_code`, `get_review_context`,
89
- > `detect_changes`), 5 portable-evidence tools (`get_brain_evidence_status`,
90
- > `record_agent_experience`, `record_cognitive_turn`, `finalize_cognitive_turn`,
91
- > `observe_bounded_loop_evidence`), 3 bounded-loop tools (`slm_loop_run`,
92
- > `slm_loop_history`, `slm_loop_show`) and 2 usefulness reports (`report_outcome`,
93
- > `report_feedback`).
94
- > Use `full` (50 tools) to add mesh coordination. Use `power` (62 tools) for governance
95
- > and audit tools. See slm-profile for profile switching.
86
+ > The MCP config ships only `SLM_AGENT_ID=claude_code` — no `SLM_MCP_PROFILE` —
87
+ > so it falls back to the same no-profile default every install gets: the
88
+ > 54-tool `full` surface. That is the 18 core tools below **plus** mesh
89
+ > coordination (8: `mesh_summary`, `mesh_peers`, `mesh_send`, `mesh_inbox`,
90
+ > `mesh_state`, `mesh_lock`, `mesh_events`, `mesh_status`), portable-evidence
91
+ > tools (5: `get_brain_evidence_status`, `record_agent_experience`,
92
+ > `record_cognitive_turn`, `finalize_cognitive_turn`,
93
+ > `observe_bounded_loop_evidence`), memory-kind tools (4: `set_memory_kind`,
94
+ > `memory_kinds_status`, `review_memory_kinds`, `confirm_memory_kinds`),
95
+ > bounded-loop tools (3: `slm_loop_run`, `slm_loop_history`, `slm_loop_show`),
96
+ > usefulness reports (2: `report_outcome`, `report_feedback`) and 14 more
97
+ > administration/learning tools (`delete_memory`, `get_status`, `observe`,
98
+ > `run_maintenance`, `consolidate_cognitive`, `get_soft_prompts`, `set_mode`,
99
+ > `log_tool_event`, `get_assertions`, `reinforce_assertion`,
100
+ > `contradict_assertion`, `evolve_skill`, `skill_health`, `skill_lineage`).
101
+ > Set `SLM_MCP_PROFILE=code` yourself for the narrower 38-tool `code` surface,
102
+ > which trades mesh and administration tools for 6 code-graph tools
103
+ > (`build_code_graph`, `get_blast_radius`, `query_graph`,
104
+ > `semantic_search_code`, `get_review_context`, `detect_changes`). Use
105
+ > `power` (66 tools) for governance and audit tools. See slm-profile for
106
+ > profile switching.
96
107
 
97
108
  | Tool | Signature (key params) | Notes |
98
109
  |--------------------|----------------------------------------------------------------------------------------------|----------------------------------------|
@@ -138,4 +149,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
138
149
  - **slm-optimize-advisor** — context compression and KV cache
139
150
  - **slm-governance-advisor** — scope/role compliance, retention policies, GDPR
140
151
 
141
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
152
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -145,4 +145,4 @@ These subcommands control daemon-level cache settings. They do not read or write
145
145
 
146
146
  ---
147
147
 
148
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -147,4 +147,4 @@ Content over 1 MB (1 000 000 bytes UTF-8) is processed but `reversible` is force
147
147
 
148
148
  ---
149
149
 
150
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -245,4 +245,4 @@ Before running any destructive operation (`forget`, `compact_memories`):
245
245
 
246
246
  ---
247
247
 
248
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -312,4 +312,4 @@ profile. See `slm-profile` for the full profile switching workflow.
312
312
 
313
313
  ---
314
314
 
315
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -96,4 +96,4 @@ paused, name the approval needed; when errored, quote the short detail.
96
96
 
97
97
  ---
98
98
 
99
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -31,9 +31,9 @@ project, a client engagement, a production vs staging environment.
31
31
  | Profile | Tools | When to use |
32
32
  |---------|-------|-------------|
33
33
  | `core` | 18 tools — remember, recall, search, session, optimize | Minimal footprint, no code tools |
34
- | `code` | 34 tools — core + portable Brain evidence + code graph + profile switching + bounded loops | Default for IDE/coding agents |
35
- | `full` | 50 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
- | `power` | 62 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
34
+ | `code` | 38 tools — core + portable Brain evidence + code graph + profile switching + bounded loops + memory-kind management | Default for IDE/coding agents |
35
+ | `full` | 54 tools — code + all memory ops + mesh + bounded loops | Multi-session, team workflows |
36
+ | `power` | 66 tools — full + governance + behavioral tools | Enterprise, admin, audit use cases |
37
37
  | `mesh` | 8 tools — mesh coordination only | Lightweight cross-session signalling |
38
38
 
39
39
  The profile is set at MCP server startup via `SLM_MCP_PROFILE` in the MCP config.
@@ -146,4 +146,4 @@ Name them differently in your MCP config (e.g. `superlocalmemory-personal` and
146
146
 
147
147
  ---
148
148
 
149
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -330,4 +330,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
330
330
 
331
331
  ---
332
332
 
333
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
333
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -270,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
270
270
 
271
271
  ---
272
272
 
273
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -173,4 +173,4 @@ to review the impact. See `slm-remember` for the full deletion discipline.
173
173
 
174
174
  ---
175
175
 
176
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -268,4 +268,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
268
268
 
269
269
  ---
270
270
 
271
- *SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later*
271
+ *SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later*
@@ -163,4 +163,4 @@ multi-profile setup. To switch the active profile, see `slm-profile`.
163
163
 
164
164
  ---
165
165
 
166
- SuperLocalMemory v4.1.18 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.1.20 · Qualixar · AGPL-3.0-or-later
@@ -17,7 +17,7 @@ const fs = require('fs');
17
17
  const path = require('path');
18
18
  const os = require('os');
19
19
 
20
- const MIN_PYTHON = Object.freeze([3, 11]);
20
+ const MIN_PYTHON = Object.freeze([3, 12]);
21
21
  const MAX_PYTHON_EXCLUSIVE = Object.freeze([3, 15]);
22
22
  const INSTALL_TIMEOUT_MS = 15 * 60 * 1000;
23
23
 
@@ -43,12 +43,19 @@ function pythonCandidates(platform = os.platform()) {
43
43
  ['py', '-3.14'],
44
44
  ['py', '-3.13'],
45
45
  ['py', '-3.12'],
46
- ['py', '-3.11'],
47
46
  ['python3'],
48
47
  ['python'],
49
48
  ];
50
49
  }
50
+ // Debian 12, Ubuntu 22.04 (with deadsnakes) and RHEL 9 all ship a system
51
+ // `python3` older than 3.12, with the supported versioned interpreter
52
+ // installed alongside it, unused. Try the versioned names first so that
53
+ // interpreter is found before falling back to the bare names and the
54
+ // fixed absolute paths below.
51
55
  return [
56
+ ['python3.14'],
57
+ ['python3.13'],
58
+ ['python3.12'],
52
59
  ['python3'],
53
60
  ['python'],
54
61
  ['/opt/homebrew/bin/python3'],
@@ -57,21 +64,44 @@ function pythonCandidates(platform = os.platform()) {
57
64
  ];
58
65
  }
59
66
 
67
+ function probePython(command, prefixArgs) {
68
+ try {
69
+ const result = spawnSync(command, [...prefixArgs, '--version'], {
70
+ stdio: 'pipe',
71
+ timeout: 5000,
72
+ env: process.env,
73
+ });
74
+ const output = `${(result.stdout || '').toString()} ${(result.stderr || '').toString()}`;
75
+ return { status: result.status, version: parsePythonVersion(output) };
76
+ } catch (error) {
77
+ return { status: null, version: null, error };
78
+ }
79
+ }
80
+
60
81
  function findSupportedPython() {
82
+ const override = String(process.env.SLM_PYTHON || '').trim();
83
+ if (override) {
84
+ // The user named this interpreter explicitly (docs/getting-started.md's
85
+ // escape hatch for a system where no supported `python3` is on PATH, for
86
+ // example a deadsnakes or pyenv install at a custom path). Honour it,
87
+ // but still version-check it — and if it fails, say so plainly instead
88
+ // of silently trying a different interpreter the user did not choose.
89
+ const probe = probePython(override, []);
90
+ if (probe.status === 0 && isSupportedPython(probe.version)) {
91
+ return { command: override, prefixArgs: [], version: probe.version };
92
+ }
93
+ const detail = probe.status === 0 && probe.version
94
+ ? `found Python ${probe.version.join('.')}, which is not supported (need 3.12-3.14)`
95
+ : 'could not be run (check the path and that it is executable)';
96
+ console.error(`SuperLocalMemory: SLM_PYTHON=${override} ${detail}.`);
97
+ console.error('Point SLM_PYTHON at a Python 3.12-3.14 interpreter, or unset it to let the installer search automatically.');
98
+ return null;
99
+ }
100
+
61
101
  for (const candidate of pythonCandidates()) {
62
- try {
63
- const result = spawnSync(candidate[0], [...candidate.slice(1), '--version'], {
64
- stdio: 'pipe',
65
- timeout: 5000,
66
- env: process.env,
67
- });
68
- const output = `${(result.stdout || '').toString()} ${(result.stderr || '').toString()}`;
69
- const version = parsePythonVersion(output);
70
- if (result.status === 0 && isSupportedPython(version)) {
71
- return { command: candidate[0], prefixArgs: candidate.slice(1), version };
72
- }
73
- } catch (_error) {
74
- // Try the next interpreter without mutating PATH or the machine.
102
+ const probe = probePython(candidate[0], candidate.slice(1));
103
+ if (probe.status === 0 && isSupportedPython(probe.version)) {
104
+ return { command: candidate[0], prefixArgs: candidate.slice(1), version: probe.version };
75
105
  }
76
106
  }
77
107
  return null;
@@ -142,7 +172,7 @@ function validateRuntimeLocation(venvRoot) {
142
172
 
143
173
  function printPythonGuidance() {
144
174
  console.error('');
145
- console.error('SuperLocalMemory requires Python 3.11, 3.12, 3.13, or 3.14.');
175
+ console.error('SuperLocalMemory requires Python 3.12, 3.13, or 3.14.');
146
176
  console.error('Install Python from https://www.python.org/downloads/ and rerun:');
147
177
  console.error(' npm rebuild superlocalmemory');
148
178
  console.error('The npm installer will create a private virtual environment;');
@@ -296,6 +326,7 @@ module.exports = {
296
326
  isSupportedPython,
297
327
  main,
298
328
  parsePythonVersion,
329
+ probePython,
299
330
  pypiSpecifier,
300
331
  pythonCandidates,
301
332
  runtimePythonPath,