specmem-hardwicksoftware 3.7.40 → 3.7.42
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/package.json +1 -1
- package/scripts/specmem-init.cjs +75 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specmem-hardwicksoftware",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.42",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Your Claude Code sessions don't have to start from scratch anymore — SpecMem gives your AI real memory. It won't forget your conversations, your code, or your architecture decisions between sessions. That's the whole point. Semantic code indexing that actually works: TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, C, C++, HTML and more. It doesn't just track functions — it gets classes, methods, fields, constants, enums, macros, imports, structs, the whole codebase graph. There's chat memory too, powered by pgvector embeddings. You've also got token compression, team coordination, multi-agent comms, and file watching built in. 74+ MCP tools. Runs on PostgreSQL + Docker. It's kind of a big deal. justcalljon.pro",
|
|
6
6
|
"main": "dist/index.js",
|
package/scripts/specmem-init.cjs
CHANGED
|
@@ -9193,14 +9193,46 @@ async function ensureModels(ui) {
|
|
|
9193
9193
|
// Permanent models release — version-independent, all npm versions pull from here
|
|
9194
9194
|
const releaseUrl = 'https://github.com/jonhardwick-spec/specmem/releases/download/models/specmem-models.tar.gz';
|
|
9195
9195
|
const tmpTarball = path.join(os.tmpdir(), 'specmem-models.tar.gz');
|
|
9196
|
+
const expectedSizeMB = 562; // approximate tarball size
|
|
9196
9197
|
|
|
9197
9198
|
initLog(`[MODELS] Downloading: ${releaseUrl}`);
|
|
9198
9199
|
|
|
9200
|
+
// Use spawn (async) instead of execSync so blessed TUI can render during download
|
|
9199
9201
|
try {
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9202
|
+
await new Promise((resolve, reject) => {
|
|
9203
|
+
const args = hasCurl
|
|
9204
|
+
? ['curl', ['-fSL', '-o', tmpTarball, releaseUrl]]
|
|
9205
|
+
: ['wget', ['-O', tmpTarball, releaseUrl]];
|
|
9206
|
+
const dl = spawn(args[0], args[1], { stdio: 'ignore' });
|
|
9207
|
+
|
|
9208
|
+
// Poll file size for progress updates
|
|
9209
|
+
const progressInterval = setInterval(() => {
|
|
9210
|
+
try {
|
|
9211
|
+
const stat = fs.statSync(tmpTarball);
|
|
9212
|
+
const mb = (stat.size / (1024 * 1024)).toFixed(0);
|
|
9213
|
+
const pct = Math.min(99, Math.round((stat.size / (expectedSizeMB * 1024 * 1024)) * 100));
|
|
9214
|
+
if (ui) ui.setSubStatus(`↓ Downloading ML models... ${mb}MB / ~${expectedSizeMB}MB (${pct}%)`);
|
|
9215
|
+
} catch {}
|
|
9216
|
+
}, 2000);
|
|
9217
|
+
|
|
9218
|
+
const timeout = setTimeout(() => {
|
|
9219
|
+
clearInterval(progressInterval);
|
|
9220
|
+
dl.kill('SIGTERM');
|
|
9221
|
+
reject(new Error('Download timed out after 15 minutes'));
|
|
9222
|
+
}, 900000);
|
|
9223
|
+
|
|
9224
|
+
dl.on('close', (code) => {
|
|
9225
|
+
clearInterval(progressInterval);
|
|
9226
|
+
clearTimeout(timeout);
|
|
9227
|
+
if (code === 0) resolve();
|
|
9228
|
+
else reject(new Error(`Download exited with code ${code}`));
|
|
9229
|
+
});
|
|
9230
|
+
dl.on('error', (err) => {
|
|
9231
|
+
clearInterval(progressInterval);
|
|
9232
|
+
clearTimeout(timeout);
|
|
9233
|
+
reject(err);
|
|
9234
|
+
});
|
|
9235
|
+
});
|
|
9204
9236
|
} catch (e) {
|
|
9205
9237
|
initLog(`[MODELS] Download failed: ${e.message}`);
|
|
9206
9238
|
if (ui) ui.setSubStatus(`⚠ Model download failed — mini-COT/translation disabled`);
|
|
@@ -10125,6 +10157,42 @@ ${lastOutput}
|
|
|
10125
10157
|
try { execSync('which podman 2>/dev/null', { stdio: 'pipe' }); return 'podman'; } catch { return 'docker'; }
|
|
10126
10158
|
})();
|
|
10127
10159
|
|
|
10160
|
+
// Helper: run a long command async so blessed TUI stays responsive
|
|
10161
|
+
const spawnAsync = (cmd, args, label, timeoutMs = 600000) => {
|
|
10162
|
+
return new Promise((resolve, reject) => {
|
|
10163
|
+
const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
10164
|
+
let lastLine = '';
|
|
10165
|
+
const onData = (chunk) => {
|
|
10166
|
+
const lines = chunk.toString().split('\n').filter(Boolean);
|
|
10167
|
+
if (lines.length) lastLine = lines[lines.length - 1].slice(0, 80);
|
|
10168
|
+
};
|
|
10169
|
+
if (child.stdout) child.stdout.on('data', onData);
|
|
10170
|
+
if (child.stderr) child.stderr.on('data', onData);
|
|
10171
|
+
|
|
10172
|
+
const statusInterval = setInterval(() => {
|
|
10173
|
+
if (ui && lastLine) ui.setSubStatus(lastLine);
|
|
10174
|
+
}, 2000);
|
|
10175
|
+
|
|
10176
|
+
const timer = setTimeout(() => {
|
|
10177
|
+
clearInterval(statusInterval);
|
|
10178
|
+
child.kill('SIGTERM');
|
|
10179
|
+
reject(new Error(`${label} timed out`));
|
|
10180
|
+
}, timeoutMs);
|
|
10181
|
+
|
|
10182
|
+
child.on('close', (code) => {
|
|
10183
|
+
clearInterval(statusInterval);
|
|
10184
|
+
clearTimeout(timer);
|
|
10185
|
+
if (code === 0) resolve();
|
|
10186
|
+
else reject(new Error(`${label} exited with code ${code}`));
|
|
10187
|
+
});
|
|
10188
|
+
child.on('error', (err) => {
|
|
10189
|
+
clearInterval(statusInterval);
|
|
10190
|
+
clearTimeout(timer);
|
|
10191
|
+
reject(err);
|
|
10192
|
+
});
|
|
10193
|
+
});
|
|
10194
|
+
};
|
|
10195
|
+
|
|
10128
10196
|
let imageExists = false;
|
|
10129
10197
|
try {
|
|
10130
10198
|
execSync(`${rtCmd} image inspect ${containerImage} 2>/dev/null`, { stdio: 'pipe' });
|
|
@@ -10133,7 +10201,7 @@ ${lastOutput}
|
|
|
10133
10201
|
} catch {
|
|
10134
10202
|
ui.setStatus(`Pulling ${containerImage}...`);
|
|
10135
10203
|
try {
|
|
10136
|
-
|
|
10204
|
+
await spawnAsync(rtCmd, ['pull', containerImage], 'Image pull', 600000);
|
|
10137
10205
|
imageExists = true;
|
|
10138
10206
|
ui.setStatus('Image pulled');
|
|
10139
10207
|
} catch (pullErr) {
|
|
@@ -10142,13 +10210,9 @@ ${lastOutput}
|
|
|
10142
10210
|
ui.setStatus('Pull failed, building locally...');
|
|
10143
10211
|
const dockerfilePath = path.join(path.dirname(__dirname), 'container', 'Dockerfile');
|
|
10144
10212
|
if (fs.existsSync(dockerfilePath)) {
|
|
10145
|
-
|
|
10146
|
-
// Without this, Node's default 1MB maxBuffer causes ERR_CHILD_PROCESS_STDIO_MAXBUFFER
|
|
10147
|
-
// which crashes init before the catch can fire cleanly
|
|
10213
|
+
ui.setStatus('Building container image locally (this may take 10-20 min)...');
|
|
10148
10214
|
try {
|
|
10149
|
-
|
|
10150
|
-
stdio: 'ignore', timeout: 1200000 // 20 min for local build
|
|
10151
|
-
});
|
|
10215
|
+
await spawnAsync(rtCmd, ['build', '-t', containerImage, '-f', dockerfilePath, path.dirname(__dirname)], 'Image build', 1200000);
|
|
10152
10216
|
imageExists = true;
|
|
10153
10217
|
ui.setStatus('Image built locally');
|
|
10154
10218
|
} catch (buildErr) {
|
|
@@ -10157,7 +10221,6 @@ ${lastOutput}
|
|
|
10157
10221
|
}
|
|
10158
10222
|
}
|
|
10159
10223
|
} catch (fallbackErr) {
|
|
10160
|
-
// Catch-all: even if ui.setStatus or fs.existsSync throws, don't crash init
|
|
10161
10224
|
initLog(`[CONTAINER] Pull fallback error: ${fallbackErr.message}`);
|
|
10162
10225
|
ui.setStatus('Image unavailable');
|
|
10163
10226
|
}
|