superlocalmemory 4.1.24 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/CHANGELOG.md +297 -0
  3. package/README.md +206 -271
  4. package/antigravity-plugin/_GENERATED.md +2 -1
  5. package/antigravity-plugin/agents/slm-governance-advisor.md +10 -4
  6. package/antigravity-plugin/agents/slm-loop-runner.md +1 -1
  7. package/antigravity-plugin/agents/slm-memory-advisor.md +8 -4
  8. package/antigravity-plugin/agents/slm-optimize-advisor.md +1 -1
  9. package/antigravity-plugin/plugin.json +1 -1
  10. package/antigravity-plugin/skills/slm-bot-memory/SKILL.md +36 -1
  11. package/antigravity-plugin/skills/slm-cache/SKILL.md +1 -1
  12. package/antigravity-plugin/skills/slm-compress/SKILL.md +1 -1
  13. package/antigravity-plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
  14. package/antigravity-plugin/skills/slm-governance/SKILL.md +61 -5
  15. package/antigravity-plugin/skills/slm-graph/SKILL.md +1 -1
  16. package/antigravity-plugin/skills/slm-loop/SKILL.md +22 -2
  17. package/antigravity-plugin/skills/slm-media/SKILL.md +334 -0
  18. package/antigravity-plugin/skills/slm-mesh/SKILL.md +110 -8
  19. package/antigravity-plugin/skills/slm-profile/SKILL.md +17 -12
  20. package/antigravity-plugin/skills/slm-recall/SKILL.md +91 -3
  21. package/antigravity-plugin/skills/slm-remember/SKILL.md +6 -1
  22. package/antigravity-plugin/skills/slm-scope/SKILL.md +1 -1
  23. package/antigravity-plugin/skills/slm-session/SKILL.md +1 -1
  24. package/antigravity-plugin/skills/slm-status/SKILL.md +48 -2
  25. package/antigravity-plugin/skills/slm-web-access/SKILL.md +92 -6
  26. package/bin/slm-npm +1 -1
  27. package/codex-plugin/.claude-plugin/plugin.json +1 -1
  28. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  29. package/codex-plugin/AGENTS.md +38 -9
  30. package/codex-plugin/README.md +8 -6
  31. package/codex-plugin/_GENERATED.md +2 -1
  32. package/codex-plugin/agents/slm-governance-advisor.md +10 -4
  33. package/codex-plugin/agents/slm-loop-runner.md +1 -1
  34. package/codex-plugin/agents/slm-memory-advisor.md +8 -4
  35. package/codex-plugin/agents/slm-optimize-advisor.md +1 -1
  36. package/codex-plugin/skills/slm-bot-memory/SKILL.md +36 -1
  37. package/codex-plugin/skills/slm-cache/SKILL.md +1 -1
  38. package/codex-plugin/skills/slm-compress/SKILL.md +1 -1
  39. package/codex-plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
  40. package/codex-plugin/skills/slm-governance/SKILL.md +61 -5
  41. package/codex-plugin/skills/slm-graph/SKILL.md +1 -1
  42. package/codex-plugin/skills/slm-loop/SKILL.md +22 -2
  43. package/codex-plugin/skills/slm-media/SKILL.md +334 -0
  44. package/codex-plugin/skills/slm-mesh/SKILL.md +110 -8
  45. package/codex-plugin/skills/slm-profile/SKILL.md +17 -12
  46. package/codex-plugin/skills/slm-recall/SKILL.md +91 -3
  47. package/codex-plugin/skills/slm-remember/SKILL.md +6 -1
  48. package/codex-plugin/skills/slm-scope/SKILL.md +1 -1
  49. package/codex-plugin/skills/slm-session/SKILL.md +1 -1
  50. package/codex-plugin/skills/slm-status/SKILL.md +48 -2
  51. package/codex-plugin/skills/slm-web-access/SKILL.md +92 -6
  52. package/copilot-plugin/.github/agents/slm-governance-advisor.agent.md +11 -5
  53. package/copilot-plugin/.github/agents/slm-loop-runner.agent.md +2 -2
  54. package/copilot-plugin/.github/agents/slm-memory-advisor.agent.md +9 -5
  55. package/copilot-plugin/.github/agents/slm-optimize-advisor.agent.md +2 -2
  56. package/copilot-plugin/.github/copilot-instructions.md +14 -5
  57. package/copilot-plugin/.github/prompts/slm-bot-memory.prompt.md +35 -2
  58. package/copilot-plugin/.github/prompts/slm-cache.prompt.md +2 -2
  59. package/copilot-plugin/.github/prompts/slm-compress.prompt.md +2 -2
  60. package/copilot-plugin/.github/prompts/slm-getting-started-bot.prompt.md +57 -8
  61. package/copilot-plugin/.github/prompts/slm-governance.prompt.md +60 -5
  62. package/copilot-plugin/.github/prompts/slm-graph.prompt.md +2 -2
  63. package/copilot-plugin/.github/prompts/slm-loop.prompt.md +28 -2
  64. package/copilot-plugin/.github/prompts/slm-media.prompt.md +335 -0
  65. package/copilot-plugin/.github/prompts/slm-mesh.prompt.md +108 -8
  66. package/copilot-plugin/.github/prompts/slm-profile.prompt.md +18 -13
  67. package/copilot-plugin/.github/prompts/slm-recall.prompt.md +92 -3
  68. package/copilot-plugin/.github/prompts/slm-remember.prompt.md +7 -2
  69. package/copilot-plugin/.github/prompts/slm-scope.prompt.md +2 -2
  70. package/copilot-plugin/.github/prompts/slm-session.prompt.md +2 -2
  71. package/copilot-plugin/.github/prompts/slm-status.prompt.md +48 -2
  72. package/copilot-plugin/.github/prompts/slm-web-access.prompt.md +92 -6
  73. package/copilot-plugin/README.md +3 -3
  74. package/copilot-plugin/_GENERATED.md +2 -1
  75. package/hermes-plugin/__init__.py +15 -5
  76. package/hermes-plugin/agents/slm-governance-advisor.md +10 -4
  77. package/hermes-plugin/agents/slm-loop-runner.md +1 -1
  78. package/hermes-plugin/agents/slm-memory-advisor.md +8 -4
  79. package/hermes-plugin/agents/slm-optimize-advisor.md +1 -1
  80. package/hermes-plugin/command-inventory.json +3 -3
  81. package/hermes-plugin/plugin.yaml +1 -1
  82. package/hermes-plugin/skills/slm-bot-memory/SKILL.md +36 -1
  83. package/hermes-plugin/skills/slm-cache/SKILL.md +1 -1
  84. package/hermes-plugin/skills/slm-compress/SKILL.md +1 -1
  85. package/hermes-plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
  86. package/hermes-plugin/skills/slm-governance/SKILL.md +61 -5
  87. package/hermes-plugin/skills/slm-graph/SKILL.md +1 -1
  88. package/hermes-plugin/skills/slm-loop/SKILL.md +22 -2
  89. package/hermes-plugin/skills/slm-media/SKILL.md +334 -0
  90. package/hermes-plugin/skills/slm-mesh/SKILL.md +110 -8
  91. package/hermes-plugin/skills/slm-profile/SKILL.md +17 -12
  92. package/hermes-plugin/skills/slm-recall/SKILL.md +91 -3
  93. package/hermes-plugin/skills/slm-remember/SKILL.md +6 -1
  94. package/hermes-plugin/skills/slm-scope/SKILL.md +1 -1
  95. package/hermes-plugin/skills/slm-session/SKILL.md +1 -1
  96. package/hermes-plugin/skills/slm-status/SKILL.md +48 -2
  97. package/hermes-plugin/skills/slm-web-access/SKILL.md +92 -6
  98. package/ide/configs/cody-commands.json +1 -1
  99. package/ide/configs/continue-mcp.yaml +9 -14
  100. package/ide/configs/continue-skills.yaml +8 -3
  101. package/ide/configs/opencode-mcp.json +4 -5
  102. package/package.json +38 -21
  103. package/plugin/.claude-plugin/plugin.json +2 -2
  104. package/plugin/.cursor-plugin/plugin.json +1 -1
  105. package/plugin/CLAUDE.md +13 -4
  106. package/plugin/agents/slm-governance-advisor.md +10 -4
  107. package/plugin/agents/slm-loop-runner.md +1 -1
  108. package/plugin/agents/slm-memory-advisor.md +8 -4
  109. package/plugin/agents/slm-optimize-advisor.md +1 -1
  110. package/plugin/cursor-skills/slm-bot-memory/SKILL.md +36 -1
  111. package/plugin/cursor-skills/slm-getting-started-bot/SKILL.md +58 -7
  112. package/plugin/cursor-skills/slm-recall/SKILL.md +91 -3
  113. package/plugin/cursor-skills/slm-remember/SKILL.md +6 -1
  114. package/plugin/cursor-skills/slm-scope/SKILL.md +1 -1
  115. package/plugin/cursor-skills/slm-session/SKILL.md +1 -1
  116. package/plugin/mcp.cursor.json +1 -1
  117. package/plugin/requirements.txt +1 -1
  118. package/plugin/skills/slm-bot-memory/SKILL.md +36 -1
  119. package/plugin/skills/slm-cache/SKILL.md +1 -1
  120. package/plugin/skills/slm-compress/SKILL.md +1 -1
  121. package/plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
  122. package/plugin/skills/slm-governance/SKILL.md +61 -5
  123. package/plugin/skills/slm-graph/SKILL.md +1 -1
  124. package/plugin/skills/slm-loop/SKILL.md +22 -2
  125. package/plugin/skills/slm-media/SKILL.md +334 -0
  126. package/plugin/skills/slm-mesh/SKILL.md +110 -8
  127. package/plugin/skills/slm-profile/SKILL.md +17 -12
  128. package/plugin/skills/slm-recall/SKILL.md +91 -3
  129. package/plugin/skills/slm-remember/SKILL.md +6 -1
  130. package/plugin/skills/slm-scope/SKILL.md +1 -1
  131. package/plugin/skills/slm-session/SKILL.md +1 -1
  132. package/plugin/skills/slm-status/SKILL.md +48 -2
  133. package/plugin/skills/slm-web-access/SKILL.md +92 -6
  134. package/plugin-src/agents/slm-governance-advisor.md +10 -4
  135. package/plugin-src/agents/slm-loop-runner.md +1 -1
  136. package/plugin-src/agents/slm-memory-advisor.md +8 -4
  137. package/plugin-src/agents/slm-optimize-advisor.md +1 -1
  138. package/plugin-src/rules/AGENTS.md +38 -9
  139. package/plugin-src/rules/CLAUDE.md.fragment +13 -4
  140. package/plugin-src/skills/slm-bot-memory/SKILL.md +36 -1
  141. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  142. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  143. package/plugin-src/skills/slm-getting-started-bot/SKILL.md +58 -7
  144. package/plugin-src/skills/slm-governance/SKILL.md +61 -5
  145. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  146. package/plugin-src/skills/slm-loop/SKILL.md +22 -2
  147. package/plugin-src/skills/slm-media/SKILL.md +334 -0
  148. package/plugin-src/skills/slm-mesh/SKILL.md +110 -8
  149. package/plugin-src/skills/slm-profile/SKILL.md +17 -12
  150. package/plugin-src/skills/slm-recall/SKILL.md +91 -3
  151. package/plugin-src/skills/slm-remember/SKILL.md +6 -1
  152. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  153. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  154. package/plugin-src/skills/slm-status/SKILL.md +48 -2
  155. package/plugin-src/skills/slm-web-access/SKILL.md +92 -6
  156. package/scripts/postinstall/engine-upgrade.js +112 -0
  157. package/scripts/postinstall/media-request.js +203 -0
  158. package/scripts/postinstall/node-arch.js +27 -0
  159. package/scripts/postinstall-interactive.js +15 -19
  160. package/scripts/postinstall.js +99 -13
  161. package/scripts/preuninstall.js +55 -3
@@ -0,0 +1,203 @@
1
+ /**
2
+ * What's-new banner and the "turn on images and documents" request.
3
+ *
4
+ * The installer never downloads anything. It only records a request in
5
+ * <data root>/features.json; the SLM daemon acts on it the next time it
6
+ * starts. Nothing is written unless the person asked (a flag, the
7
+ * SLM_ENABLE_MEDIA=1 environment variable, or a "yes" at a terminal).
8
+ *
9
+ * Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
10
+ * Licensed under AGPL-3.0-or-later.
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const os = require('os');
18
+ const readline = require('readline');
19
+ const { nodeRunsTranslated } = require('./node-arch.js');
20
+
21
+ const FEATURES_FILE = 'features.json';
22
+
23
+ const GIB = 1024 ** 3;
24
+ // Same threshold and override as the Python side (MEDIA_MIN_RAM_BYTES and
25
+ // LOW_RAM_OVERRIDE_ENV in runtimes/media_env.py): 15 GiB, because machines sold as
26
+ // 16 GB report a little less.
27
+ const MIN_RAM_BYTES = 15 * GIB;
28
+ const LOW_RAM_OVERRIDE_ENV = 'SLM_MEDIA_ALLOW_LOW_RAM';
29
+
30
+ /**
31
+ * Why images and documents cannot be turned on here, or '' when they can.
32
+ * Unknown memory (0) is allowed; SLM_MEDIA_ALLOW_LOW_RAM=1 is the developer override.
33
+ */
34
+ function ramRefusal(totalBytes, env = process.env) {
35
+ if (!(totalBytes > 0 && totalBytes < MIN_RAM_BYTES)) return '';
36
+ if (env && env[LOW_RAM_OVERRIDE_ENV] === '1') return '';
37
+ return 'Images and documents need a computer with at least 16 GB of memory; this one has '
38
+ + (totalBytes / GIB).toFixed(1) + ' GB. Your text memories keep working.';
39
+ }
40
+
41
+ // Where the managed picture and document environment has a build (the same list as
42
+ // runtimes/locks): Apple Silicon Macs, x86_64 and ARM64 Linux, and x64 Windows. `translated` is a
43
+ // Node that runs as an Intel program on an Apple Silicon Mac.
44
+ const NOT_SUPPORTED = 'Images and documents are not supported on this computer yet. Your text memories keep working.';
45
+
46
+ function mediaPlatformRefusal({ platform, arch, translated } = {}) {
47
+ if (platform === 'darwin' && arch === 'x64') {
48
+ if (translated) {
49
+ return 'This Node.js runs as an Intel program on an Apple Silicon Mac, so SLM cannot tell that your '
50
+ + 'Mac can use images and documents. Run Node natively (the Apple Silicon build), then try again.';
51
+ }
52
+ return NOT_SUPPORTED;
53
+ }
54
+ if (platform === 'linux' && arch !== 'x64' && arch !== 'arm64') return NOT_SUPPORTED;
55
+ if (platform === 'win32' && arch !== 'x64') return NOT_SUPPORTED;
56
+ return '';
57
+ }
58
+
59
+ function currentMachine() {
60
+ return { platform: process.platform, arch: process.arch, translated: nodeRunsTranslated(process.platform) };
61
+ }
62
+
63
+ function printWhatsNew(log = console.log) {
64
+ log('');
65
+ log("What's new in 4.1.25:");
66
+ log(' + Images & documents: off until you turn them on (about 1.5 GB of models). Run: slm media enable');
67
+ log(' + Bots on the web can message each other through the mesh (remote access is still off).');
68
+ log(' + See what is on and what you can turn on: slm features');
69
+ }
70
+
71
+ // Same alias order as every other entry point.
72
+ function resolveDataRoot(env = process.env) {
73
+ return env.SLM_DATA_DIR
74
+ || env.SL_MEMORY_PATH
75
+ || env.SLM_HOME
76
+ || path.join(env.HOME || os.homedir(), '.superlocalmemory');
77
+ }
78
+
79
+ function mediaRequested(args, env) {
80
+ const viaNpm = env && ['true', '1'].includes(String(env.npm_config_media || '').toLowerCase());
81
+ return Boolean((args && args.media) || (env && env.SLM_ENABLE_MEDIA === '1') || viaNpm);
82
+ }
83
+
84
+ /**
85
+ * Merge {"media": {"requested": true, ...}} into features.json (mode 0600).
86
+ * Never throws; returns { ok, note }.
87
+ */
88
+ function recordMediaRequest(slmDir, now = new Date()) {
89
+ const file = path.join(slmDir, FEATURES_FILE);
90
+ try {
91
+ let data = { schema: 1 };
92
+ if (fs.existsSync(file)) {
93
+ try {
94
+ data = JSON.parse(fs.readFileSync(file, 'utf8'));
95
+ } catch (_e) {
96
+ return { ok: false, note: 'features.json could not be read; left untouched. Run: slm media enable' };
97
+ }
98
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
99
+ return { ok: false, note: 'features.json has an unexpected shape; left untouched. Run: slm media enable' };
100
+ }
101
+ }
102
+ const media = data.media && typeof data.media === 'object' ? data.media : {};
103
+ if (media.enabled) return { ok: true, note: 'images and documents are already on' };
104
+ data.media = Object.assign({}, media, {
105
+ requested: true,
106
+ requested_at: now.toISOString(),
107
+ choice_source: 'npm',
108
+ });
109
+ fs.mkdirSync(slmDir, { recursive: true });
110
+ const tmp = file + '.tmp';
111
+ const fd = fs.openSync(tmp, 'w', 0o600);
112
+ try {
113
+ fs.writeSync(fd, JSON.stringify(data), 0, 'utf8');
114
+ } finally {
115
+ fs.closeSync(fd);
116
+ }
117
+ fs.renameSync(tmp, file);
118
+ if (process.platform !== 'win32') {
119
+ try { fs.chmodSync(file, 0o600); } catch (_e) { /* best-effort */ }
120
+ }
121
+ return { ok: true, note: 'recorded' };
122
+ } catch (e) {
123
+ return { ok: false, note: 'could not record the request (' + e.message + '). Run: slm media enable' };
124
+ }
125
+ }
126
+
127
+ function askYesNo(question) {
128
+ return new Promise((resolve) => {
129
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
130
+ rl.question(question, (answer) => {
131
+ rl.close();
132
+ resolve(/^\s*y(es)?\s*$/i.test(answer || ''));
133
+ });
134
+ });
135
+ }
136
+
137
+ /**
138
+ * Decide and (unless dry-run) record. `interactive` allows the question,
139
+ * which defaults to No. Returns true when a request was recorded. On a computer
140
+ * with less than 16 GB of memory it says so once and neither asks nor records.
141
+ */
142
+ async function handleMediaChoice({ args, env, slmDir, interactive, ask = askYesNo, log = console.log,
143
+ totalMem = os.totalmem(), machine = currentMachine() }) {
144
+ let wanted = mediaRequested(args, env);
145
+ const unsupported = mediaPlatformRefusal(machine);
146
+ if ((wanted || interactive) && unsupported) {
147
+ log('');
148
+ log('SLM: ' + unsupported);
149
+ return false; // not asked, nothing recorded: the daemon would refuse it anyway
150
+ }
151
+ const refusal = ramRefusal(totalMem, env);
152
+ if ((wanted || interactive) && refusal) {
153
+ log('');
154
+ log('SLM: ' + refusal);
155
+ return false; // not asked, nothing recorded: the daemon would refuse it anyway
156
+ }
157
+ if ((wanted || interactive) && totalMem > 0 && totalMem < MIN_RAM_BYTES) {
158
+ log('SLM: ' + LOW_RAM_OVERRIDE_ENV + '=1 is set; allowing images and documents on '
159
+ + (totalMem / GIB).toFixed(1) + ' GB of memory (16 GB needed).');
160
+ }
161
+ if (!wanted && interactive) {
162
+ log('');
163
+ wanted = await ask('Turn on images and documents later? It downloads about 1.5 GB when SLM next starts. [y/N] ');
164
+ }
165
+ if (!wanted) return false;
166
+ if (args && args.dryRun) {
167
+ log('SLM dry-run: would record a request to turn on images and documents.');
168
+ return false;
169
+ }
170
+ const result = recordMediaRequest(slmDir);
171
+ if (result.ok) {
172
+ log('SLM: images and documents will start setting up the next time SLM starts (' + result.note + ').');
173
+ } else {
174
+ log('SLM: ' + result.note);
175
+ }
176
+ return result.ok;
177
+ }
178
+
179
+ /**
180
+ * The step npm's own postinstall runs: --media / npm_config_media /
181
+ * SLM_ENABLE_MEDIA=1 record a request; on a real terminal (never in CI) a
182
+ * default-No question is asked. Never downloads, never throws.
183
+ */
184
+ async function runMediaStep({ argv = [], env = process.env, tty, ask, log = console.log } = {}) {
185
+ try {
186
+ const isTty = tty !== undefined ? tty
187
+ : Boolean(process.stdout.isTTY && process.stdin.isTTY);
188
+ const ci = env.CI === 'true' || env.CI === '1';
189
+ return await handleMediaChoice({
190
+ args: { media: argv.includes('--media'), dryRun: argv.includes('--dry-run') },
191
+ env,
192
+ slmDir: resolveDataRoot(env),
193
+ interactive: isTty && !ci,
194
+ ask: ask || askYesNo,
195
+ log,
196
+ });
197
+ } catch (e) {
198
+ log('SLM: could not record the images and documents choice (' + e.message + '). Run: slm media enable');
199
+ return false;
200
+ }
201
+ }
202
+
203
+ module.exports = { mediaPlatformRefusal, ramRefusal, MIN_RAM_BYTES, runMediaStep, resolveDataRoot, printWhatsNew, mediaRequested, recordMediaRequest, handleMediaChoice, FEATURES_FILE };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Whether this Node.js is an Intel program run by Rosetta on an Apple Silicon Mac.
3
+ *
4
+ * Both the Python check and the pictures request need to tell that apart from a
5
+ * real Intel Mac: a universal2 Python started by a translated Node reports x86_64.
6
+ *
7
+ * Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
8
+ * Licensed under AGPL-3.0-or-later.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const os = require('os');
14
+ const { spawnSync } = require('child_process');
15
+
16
+ // Only macOS has the question; any failure to ask answers false.
17
+ function nodeRunsTranslated(platform = os.platform(), run = spawnSync) {
18
+ if (platform !== 'darwin') return false;
19
+ try {
20
+ const result = run('sysctl', ['-n', 'sysctl.proc_translated'], { stdio: 'pipe', timeout: 3000, encoding: 'utf8' });
21
+ return Boolean(result) && !result.error && String(result.stdout || '').trim() === '1';
22
+ } catch (_) {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ module.exports = { nodeRunsTranslated };
@@ -33,9 +33,11 @@
33
33
  * --reconfigure Allow overwrite of existing config.toml.
34
34
  * --home=<path> Override $HOME (test hook).
35
35
  * --reply-file=<json> JSON file providing custom-knob answers.
36
+ * --media Record a request to turn on images and documents.
36
37
  *
37
38
  * Environment variables (test/CI hooks):
38
39
  * CI=true Force non-TTY path.
40
+ * SLM_ENABLE_MEDIA=1 Same as --media (records a request; never downloads).
39
41
  * SLM_INSTALL_FREE_RAM_MB=<int> Override free-RAM probe (benchmark).
40
42
  * SLM_INSTALL_COLD_START_MS=<n> Override Python cold-start probe.
41
43
  * SLM_INSTALL_DISK_FREE_GB=<n> Override disk-free probe.
@@ -144,6 +146,7 @@ function parseArgs(argv) {
144
146
  home: null,
145
147
  replyFile: null,
146
148
  homeOutsideHome: false, // H-10: opt-in flag for --home outside $HOME
149
+ media: false, // --media: record a request to turn on images and documents
147
150
  // v3.8.0: deployment mode — "personal" | "enterprise" (default: personal)
148
151
  deployment: null,
149
152
  };
@@ -151,6 +154,7 @@ function parseArgs(argv) {
151
154
  if (a === '--dry-run') args.dryRun = true;
152
155
  else if (a === '--reconfigure') args.reconfigure = true;
153
156
  else if (a === '--home-outside-home') args.homeOutsideHome = true; // H-10
157
+ else if (a === '--media') args.media = true;
154
158
  else if (a.startsWith('--profile=')) args.profile = a.slice('--profile='.length);
155
159
  else if (a.startsWith('--home=')) args.home = a.slice('--home='.length);
156
160
  else if (a.startsWith('--reply-file=')) args.replyFile = a.slice('--reply-file='.length);
@@ -188,6 +192,9 @@ const {
188
192
  executeIdeConnections,
189
193
  } = require('./postinstall/ide-multiselect.js');
190
194
 
195
+ // 4.1.25 — what's-new banner + the (never-downloading) media request
196
+ const { printWhatsNew, handleMediaChoice } = require('./postinstall/media-request.js');
197
+
191
198
  // ------------------------------------------------------------------------
192
199
  // TTY detection
193
200
  // ------------------------------------------------------------------------
@@ -526,22 +533,6 @@ async function runInteractiveFlow(rl, recommendedProfile) {
526
533
  // First-run checklist
527
534
  // ------------------------------------------------------------------------
528
535
 
529
- // UX-G2 — one-screen "what's new in v3.4.21" banner for upgraders so
530
- // existing users see the headline before the first-run checklist. Kept
531
- // under 60 LOC per Stage-8 G2 scope.
532
- function printLivingBrainDelta() {
533
- console.log('');
534
- console.log('Current setup guarantees:');
535
- console.log(' + session_init mandate hook — Claude calls ToolSearch→session_init first, every session');
536
- console.log(' + External IDE integrations are installed only after explicit consent in `slm setup`');
537
- console.log(' + M017 migration — ccq_consolidated_blocks gets scope column (no more silent CCQ scope drop)');
538
- console.log(' + GC-safe test flags baked into pyproject.toml + Makefile (no more macOS ARM SIGSEGV)');
539
- console.log('What\'s unchanged:');
540
- console.log(' * Your memory.db — zero deletes, zero rewrites');
541
- console.log(' * Your profile settings');
542
- console.log(' * All CLI commands you already use');
543
- }
544
-
545
536
  function printFirstRunChecklist(config) {
546
537
  console.log('');
547
538
  console.log('SuperLocalMemory is configured.');
@@ -608,6 +599,8 @@ async function main() {
608
599
  if (cfgExists && !args.reconfigure) {
609
600
  console.log('SLM: existing config.toml detected at ' + cfgPath);
610
601
  console.log('SLM: skipping installer. Use --reconfigure to change settings.');
602
+ printWhatsNew();
603
+ await handleMediaChoice({ args, env: process.env, slmDir, interactive: false });
611
604
  return 0;
612
605
  }
613
606
 
@@ -710,6 +703,8 @@ async function main() {
710
703
  if (args.dryRun) {
711
704
  console.log('SLM dry-run: would write profile=' + config.profile +
712
705
  ' to ' + cfgPath);
706
+ printWhatsNew();
707
+ await handleMediaChoice({ args, env: process.env, slmDir, interactive: false });
713
708
  printFirstRunChecklist(config);
714
709
  return 0;
715
710
  }
@@ -764,8 +759,9 @@ async function main() {
764
759
  // v3.8.0: Execute IDE connections after config write so state is consistent.
765
760
  if (config._pendingIdes) executeIdeConnections(config._pendingIdes, slmDir);
766
761
 
767
- // UX-G2: show the one-screen delta banner so upgraders see what shipped.
768
- printLivingBrainDelta();
762
+ // Show what shipped, then the (default-No) question about images and documents.
763
+ printWhatsNew();
764
+ await handleMediaChoice({ args, env: process.env, slmDir, interactive: !nonInteractive });
769
765
 
770
766
  printFirstRunChecklist(config);
771
767
  return 0;
@@ -800,7 +796,7 @@ module.exports = {
800
796
  LLM_MODEL_CHOICES,
801
797
  PROFILES,
802
798
  CUSTOM_KNOB_ENUMS, // UX-M3
803
- printLivingBrainDelta, // UX-G2 (exposed for the test harness only)
799
+ printWhatsNew, // shared with scripts/postinstall.js (exposed for the test harness)
804
800
  // v3.8.0 — deployment
805
801
  DEPLOYMENT_PRESETS,
806
802
  DEFAULT_DEPLOYMENT_MODE,
@@ -16,6 +16,9 @@ const { spawnSync } = require('child_process');
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
18
  const os = require('os');
19
+ const { printWhatsNew, runMediaStep } = require('./postinstall/media-request.js');
20
+ const { nodeRunsTranslated } = require('./postinstall/node-arch.js');
21
+ const { runUpgradeStep } = require('./postinstall/engine-upgrade.js');
19
22
 
20
23
  const MIN_PYTHON = Object.freeze([3, 12]);
21
24
  const MAX_PYTHON_EXCLUSIVE = Object.freeze([3, 15]);
@@ -242,13 +245,82 @@ function validateRuntimeLocation(venvRoot) {
242
245
  }
243
246
  }
244
247
 
248
+ function pythonGuidance(platform = os.platform()) {
249
+ const lines = ['', 'SuperLocalMemory requires Python 3.12, 3.13, or 3.14.'];
250
+ if (platform === 'darwin') {
251
+ lines.push('Install it with Homebrew: brew install python@3.13');
252
+ lines.push('or the installer from https://www.python.org/downloads/macos/');
253
+ } else if (platform === 'win32') {
254
+ lines.push('Install it with: winget install Python.Python.3.13');
255
+ lines.push('or from https://www.python.org/downloads/windows/ (the py launcher is found automatically).');
256
+ } else if (platform === 'linux') {
257
+ lines.push('Debian 13 / Ubuntu 24.04 and later: sudo apt install python3 python3-venv');
258
+ lines.push('Ubuntu 22.04: sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt install python3.12 python3.12-venv');
259
+ lines.push('Fedora / RHEL 9: sudo dnf install python3.12');
260
+ } else {
261
+ lines.push('Install it from https://www.python.org/downloads/');
262
+ }
263
+ lines.push('Then finish the install: npm rebuild -g superlocalmemory');
264
+ lines.push('(or set SLM_PYTHON to the interpreter if it is somewhere unusual).');
265
+ lines.push('The npm installer creates a private virtual environment;');
266
+ lines.push('it never installs packages into your system Python.');
267
+ return lines;
268
+ }
269
+
270
+ // Machines SuperLocalMemory cannot run on: an Intel (x86_64) Python on macOS (the
271
+ // pinned security library, cryptography 50, has no Intel Mac build), a 32-bit Python
272
+ // on Windows or Linux, and Windows on ARM. pip would fail deep in dependency
273
+ // resolution instead. An Intel answer on a Mac can be false: a universal2 Python
274
+ // started by a Node that runs translated (Rosetta) reports x86_64 although the Mac
275
+ // is Apple Silicon, so `translated` (see nodeRunsTranslated) changes the advice.
276
+ function unsupportedMachine(platform, machine, is64, translated = false) {
277
+ const m = String(machine || '').toLowerCase();
278
+ if (platform === 'darwin' && m === 'x86_64') {
279
+ if (translated === true) {
280
+ return [
281
+ 'This Node.js is running as an Intel program on an Apple Silicon Mac (translated by Rosetta),',
282
+ 'so the Python it starts reports Intel too. SuperLocalMemory needs everything to run natively.',
283
+ 'Install the Apple Silicon (arm64) Node.js (nodejs.org installer, or Homebrew in /opt/homebrew),',
284
+ 'open a new terminal, and run Node natively, then: npm rebuild -g superlocalmemory',
285
+ ].join('\n');
286
+ }
287
+ return [
288
+ 'This Python is an Intel (x86_64) build, and SuperLocalMemory needs an Apple Silicon (arm64) one.',
289
+ 'On an Apple Silicon Mac, install the arm64 Python (Homebrew in /opt/homebrew: brew install python@3.13,',
290
+ 'or the python.org installer run natively, not under Rosetta), then: npm rebuild -g superlocalmemory',
291
+ 'Intel Macs are not supported: the security library SuperLocalMemory pins has no Intel Mac build.',
292
+ ].join('\n');
293
+ }
294
+ if (platform === 'win32' && m === 'arm64') {
295
+ return 'Windows on ARM is not supported yet: the security library SuperLocalMemory pins has no '
296
+ + 'Windows ARM build. Use a 64-bit Intel or AMD Windows computer, or WSL with an x86_64 Linux.';
297
+ }
298
+ if (platform === 'win32' && is64 === false) {
299
+ return 'This Python is 32-bit. SuperLocalMemory needs 64-bit Python 3.12-3.14 on Windows '
300
+ + '(winget install Python.Python.3.13), then: npm rebuild -g superlocalmemory';
301
+ }
302
+ if (platform === 'linux' && (is64 === false || ['i386', 'i486', 'i586', 'i686', 'armv6l', 'armv7l'].includes(m))) {
303
+ return 'This computer or this Python is 32-bit. SuperLocalMemory needs a 64-bit Linux '
304
+ + '(x86_64 or aarch64) with 64-bit Python 3.12-3.14, then: npm rebuild -g superlocalmemory';
305
+ }
306
+ return null;
307
+ }
308
+
309
+ function pythonMachine(python) {
310
+ try {
311
+ const result = spawnSync(python.command, [...python.prefixArgs, '-c',
312
+ 'import platform, sys; print(sys.platform, platform.machine(), sys.maxsize > 2**32)'], {
313
+ stdio: 'pipe', timeout: 5000, env: process.env,
314
+ });
315
+ const [platform, machine, wide] = String(result.stdout || '').trim().split(/\s+/);
316
+ return { platform: platform || '', machine: machine || '', is64: wide === 'True' ? true : wide === 'False' ? false : null };
317
+ } catch (_) {
318
+ return { platform: '', machine: '', is64: null };
319
+ }
320
+ }
321
+
245
322
  function printPythonGuidance() {
246
- console.error('');
247
- console.error('SuperLocalMemory requires Python 3.12, 3.13, or 3.14.');
248
- console.error('Install Python from https://www.python.org/downloads/ and rerun:');
249
- console.error(' npm rebuild superlocalmemory');
250
- console.error('The npm installer will create a private virtual environment;');
251
- console.error('it will not install packages into your system Python.');
323
+ for (const line of pythonGuidance()) console.error(line);
252
324
  }
253
325
 
254
326
  function failureDetail(result) {
@@ -277,12 +349,19 @@ function main(argv = process.argv.slice(2)) {
277
349
  printPythonGuidance();
278
350
  return 1;
279
351
  }
352
+ const machine = pythonMachine(python);
353
+ const refusal = unsupportedMachine(machine.platform, machine.machine, machine.is64, nodeRunsTranslated());
354
+ if (refusal) {
355
+ console.error('');
356
+ console.error(refusal);
357
+ return 1;
358
+ }
280
359
 
281
360
  const locationCheck = validateRuntimeLocation(venvRoot);
282
361
  if (!locationCheck.ok) {
283
362
  console.error(`SuperLocalMemory: refusing unsafe runtime location: ${locationCheck.error}.`);
284
363
  console.error('Move that path aside manually, verify it contains no needed files, then run:');
285
- console.error(' npm rebuild superlocalmemory');
364
+ console.error(' npm rebuild -g superlocalmemory');
286
365
  return 1;
287
366
  }
288
367
 
@@ -311,7 +390,7 @@ function main(argv = process.argv.slice(2)) {
311
390
  } else {
312
391
  console.error('Repair the selected Python installation so the stdlib venv module is available, then run:');
313
392
  }
314
- console.error(' npm rebuild superlocalmemory');
393
+ console.error(' npm rebuild -g superlocalmemory');
315
394
  return 1;
316
395
  }
317
396
 
@@ -361,10 +440,9 @@ function main(argv = process.argv.slice(2)) {
361
440
  if (installPackage.status !== 0) {
362
441
  console.error(`SuperLocalMemory: private-runtime installation failed (${failureDetail(installPackage)}).`);
363
442
  console.error(`Tried Python source: ${packageSource}`);
364
- console.error('This install needs network access to PyPI, or set SLM_LOCAL_WHEEL=/path/to/superlocalmemory-<version>-py3-none-any.whl for air-gapped installs, then run:');
365
- console.error(' npm rebuild superlocalmemory');
443
+ console.error('This install needs network access to PyPI (or set SLM_LOCAL_WHEEL=/path/to/superlocalmemory-<version>-py3-none-any.whl for air-gapped installs).');
366
444
  console.error('Check network access, available disk space, and Python build prerequisites, then run:');
367
- console.error(' npm rebuild superlocalmemory');
445
+ console.error(' npm rebuild -g superlocalmemory');
368
446
  console.error('No system Python packages or SLM durable data were modified.');
369
447
  return 1;
370
448
  }
@@ -391,7 +469,7 @@ function main(argv = process.argv.slice(2)) {
391
469
  console.error(
392
470
  `SuperLocalMemory: runtime identity check failed (npm=${packageVersion}, python=${installedVersion || 'unavailable'}).`,
393
471
  );
394
- console.error('Run `npm rebuild superlocalmemory` to repair the package-owned runtime.');
472
+ console.error('Run `npm rebuild -g superlocalmemory` to repair the package-owned runtime.');
395
473
  return 1;
396
474
  }
397
475
 
@@ -416,12 +494,16 @@ function main(argv = process.argv.slice(2)) {
416
494
  console.log(' slm upgrade-hosts');
417
495
  console.log('');
418
496
  console.log(' Prefer to tune performance profiles? Use: slm reconfigure');
497
+ printWhatsNew();
419
498
  console.log('');
420
499
  return 0;
421
500
  }
422
501
 
423
502
  if (require.main === module) {
424
- process.exit(main());
503
+ const code = main();
504
+ const done = () => process.exit(code);
505
+ if (code !== 0) done();
506
+ else runMediaStep({ argv: process.argv.slice(2) }).then(() => runUpgradeStep()).then(done, done);
425
507
  }
426
508
 
427
509
  module.exports = {
@@ -430,10 +512,14 @@ module.exports = {
430
512
  hasNvidiaGpu,
431
513
  isSupportedPython,
432
514
  main,
515
+ runMediaStep,
433
516
  parsePythonVersion,
434
517
  probePython,
435
518
  pypiSpecifier,
436
519
  pythonCandidates,
520
+ pythonGuidance,
521
+ unsupportedMachine,
522
+ nodeRunsTranslated,
437
523
  runtimePythonPath,
438
524
  shouldForceCpuTorch,
439
525
  TORCH_CPU_INDEX_URL,
@@ -3,8 +3,10 @@
3
3
  * SuperLocalMemory NPM pre-uninstall notice.
4
4
  *
5
5
  * npm owns and removes the package directory, including its private Python
6
- * runtime. Durable SLM data has separate ownership and is never inspected or
7
- * modified by an application-code uninstall.
6
+ * runtime. Durable SLM data has separate ownership and is never modified or
7
+ * deleted by an application-code uninstall. The one thing worth saying: the
8
+ * images-and-documents models (about 1.5 GB) live in the data folder, so this
9
+ * names where they are, how big they are, and the command that removes them.
8
10
  *
9
11
  * Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
10
12
  * Licensed under AGPL-3.0-or-later.
@@ -12,5 +14,55 @@
12
14
 
13
15
  'use strict';
14
16
 
17
+ const fs = require('fs');
18
+ const os = require('os');
19
+ const path = require('path');
20
+
21
+ function dataRoot(env = process.env) {
22
+ return env.SLM_DATA_DIR
23
+ || env.SL_MEMORY_PATH
24
+ || env.SLM_HOME
25
+ || path.join(os.homedir(), '.superlocalmemory');
26
+ }
27
+
28
+ // Total bytes under dir; read-only, bounded, never follows links, never throws.
29
+ function folderBytes(dir, limit = 200000) {
30
+ let total = 0;
31
+ let seen = 0;
32
+ const stack = [dir];
33
+ while (stack.length && seen < limit) {
34
+ const current = stack.pop();
35
+ let entries = [];
36
+ try { entries = fs.readdirSync(current, { withFileTypes: true }); } catch (_e) { continue; }
37
+ for (const entry of entries) {
38
+ seen += 1;
39
+ const full = path.join(current, entry.name);
40
+ try {
41
+ if (entry.isSymbolicLink()) continue;
42
+ if (entry.isDirectory()) stack.push(full);
43
+ else total += fs.lstatSync(full).size;
44
+ } catch (_e) { /* skip unreadable entries */ }
45
+ }
46
+ }
47
+ return total;
48
+ }
49
+
50
+ function humanSize(bytes) {
51
+ if (bytes >= 1024 ** 3) return (bytes / 1024 ** 3).toFixed(1) + ' GB';
52
+ return Math.max(1, Math.round(bytes / 1024 ** 2)) + ' MB';
53
+ }
54
+
55
+ function mediaEnvNotice(env = process.env, log = console.log) {
56
+ try {
57
+ const mediaDir = path.join(dataRoot(env), 'runtimes', 'media');
58
+ if (!fs.existsSync(mediaDir)) return;
59
+ log('Images & documents models are kept at ' + mediaDir + ' (' + humanSize(folderBytes(mediaDir)) + ').');
60
+ log('To remove them, run: slm media disable --remove-files');
61
+ } catch (_e) { /* a notice must never fail an uninstall */ }
62
+ }
63
+
15
64
  console.log('SuperLocalMemory application code is being removed.');
16
- console.log('Memory data is preserved. No database, profile, backup, or configuration was inspected or changed.');
65
+ console.log('Memory data is preserved. No database, profile, backup, or configuration was changed.');
66
+ mediaEnvNotice();
67
+
68
+ module.exports = { mediaEnvNotice, folderBytes, dataRoot };