specmem-hardwicksoftware 3.7.19 â 3.7.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/README.md +87 -8
- package/dist/codebase/codeAnalyzer.js +1155 -0
- package/dist/codebase/codebaseIndexer.js +1 -1
- package/dist/tools/goofy/findWhatISaid.js +1 -1
- package/embedding-sandbox/frankenstein-embeddings.py +32 -16
- package/embedding-sandbox/server.mjs +40 -7
- package/mcp-proxy.cjs +18 -1
- package/package.json +14 -3
|
@@ -900,7 +900,7 @@ export class CodebaseIndexer {
|
|
|
900
900
|
const analyzableLanguages = [
|
|
901
901
|
'typescript', 'typescript-react', 'javascript', 'javascript-react',
|
|
902
902
|
'python', 'go', 'rust', 'java', 'kotlin', 'scala',
|
|
903
|
-
'ruby', 'php', 'c', 'cpp', 'swift'
|
|
903
|
+
'ruby', 'php', 'c', 'cpp', 'swift', 'html'
|
|
904
904
|
];
|
|
905
905
|
return analyzableLanguages.includes(language);
|
|
906
906
|
}
|
|
@@ -821,7 +821,7 @@ export class FindWhatISaid {
|
|
|
821
821
|
});
|
|
822
822
|
}
|
|
823
823
|
catch (embeddingError) {
|
|
824
|
-
|
|
824
|
+
// embeddingTimeoutId is scoped inside withEmbeddingRetry â already cleared there
|
|
825
825
|
const embeddingDuration = Date.now() - embeddingStartTime;
|
|
826
826
|
const err = embeddingError;
|
|
827
827
|
// ============================================================================
|
|
@@ -293,32 +293,48 @@ def _detect_best_onnx_file():
|
|
|
293
293
|
"""
|
|
294
294
|
Detect CPU features and return the best ONNX model file name.
|
|
295
295
|
Priority: avx512_vnni > avx512 > avx2 > default
|
|
296
|
+
Falls back to whatever .onnx file exists if the optimal one isn't found.
|
|
296
297
|
"""
|
|
298
|
+
# Ordered by preference (best first)
|
|
299
|
+
candidates = []
|
|
300
|
+
|
|
297
301
|
try:
|
|
298
302
|
with open('/proc/cpuinfo', 'r') as f:
|
|
299
303
|
cpuinfo = f.read().lower()
|
|
300
304
|
|
|
301
|
-
# Check for AVX512 VNNI (best for INT8)
|
|
302
305
|
if 'avx512_vnni' in cpuinfo or 'avx512vnni' in cpuinfo:
|
|
303
|
-
|
|
304
|
-
return "onnx/model_qint8_avx512_vnni.onnx"
|
|
305
|
-
|
|
306
|
-
# Check for AVX512 (good INT8 support)
|
|
306
|
+
candidates.append(("onnx/model_qint8_avx512_vnni.onnx", "AVX512-VNNI"))
|
|
307
307
|
if 'avx512f' in cpuinfo or 'avx512' in cpuinfo:
|
|
308
|
-
|
|
309
|
-
return "onnx/model_qint8_avx512.onnx"
|
|
310
|
-
|
|
311
|
-
# Check for AVX2 (common, decent performance)
|
|
308
|
+
candidates.append(("onnx/model_qint8_avx512.onnx", "AVX512"))
|
|
312
309
|
if 'avx2' in cpuinfo:
|
|
313
|
-
|
|
314
|
-
return "onnx/model_quint8_avx2.onnx"
|
|
315
|
-
|
|
316
|
-
# Fallback to unoptimized
|
|
317
|
-
print("âšī¸ Using default ONNX model (no AVX optimization)", file=sys.stderr)
|
|
318
|
-
return "onnx/model.onnx"
|
|
310
|
+
candidates.append(("onnx/model_quint8_avx2.onnx", "AVX2"))
|
|
319
311
|
except Exception as e:
|
|
320
312
|
print(f"â ī¸ Could not detect CPU features: {e}", file=sys.stderr)
|
|
321
|
-
|
|
313
|
+
|
|
314
|
+
# Always add standard fallbacks
|
|
315
|
+
candidates.append(("onnx/model_quantized.onnx", "quantized"))
|
|
316
|
+
candidates.append(("onnx/model.onnx", "default"))
|
|
317
|
+
|
|
318
|
+
# Check which files actually exist in the bundled model dir
|
|
319
|
+
bundled_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'models', 'all-MiniLM-L6-v2')
|
|
320
|
+
for onnx_file, label in candidates:
|
|
321
|
+
full_path = os.path.join(bundled_dir, onnx_file)
|
|
322
|
+
if os.path.isfile(full_path):
|
|
323
|
+
print(f"đ Using {label} ONNX model: {onnx_file}", file=sys.stderr)
|
|
324
|
+
return onnx_file
|
|
325
|
+
|
|
326
|
+
# Last resort: find ANY .onnx file in the bundled dir
|
|
327
|
+
onnx_dir = os.path.join(bundled_dir, 'onnx')
|
|
328
|
+
if os.path.isdir(onnx_dir):
|
|
329
|
+
for f in os.listdir(onnx_dir):
|
|
330
|
+
if f.endswith('.onnx'):
|
|
331
|
+
result = f"onnx/{f}"
|
|
332
|
+
print(f"đ Auto-detected ONNX model: {result}", file=sys.stderr)
|
|
333
|
+
return result
|
|
334
|
+
|
|
335
|
+
# Nothing found - return default and let SentenceTransformer handle it
|
|
336
|
+
print("âšī¸ No bundled ONNX model found - using default", file=sys.stderr)
|
|
337
|
+
return "onnx/model.onnx"
|
|
322
338
|
|
|
323
339
|
_BEST_ONNX_FILE = _detect_best_onnx_file()
|
|
324
340
|
|
|
@@ -57,6 +57,13 @@ const getMachineSocketPath = () => {
|
|
|
57
57
|
const SOCKET_PATH = process.env.SOCKET_PATH || getMachineSocketPath();
|
|
58
58
|
const MODEL_NAME = 'Xenova/all-MiniLM-L6-v2';
|
|
59
59
|
|
|
60
|
+
// Bundled model: shipped with npm package, used as fallback when HF cache unavailable
|
|
61
|
+
import { fileURLToPath } from 'url';
|
|
62
|
+
import { dirname } from 'path';
|
|
63
|
+
const __filename_esm = fileURLToPath(import.meta.url);
|
|
64
|
+
const __dirname_esm = dirname(__filename_esm);
|
|
65
|
+
const BUNDLED_MODEL_DIR = join(__dirname_esm, 'models', 'all-MiniLM-L6-v2');
|
|
66
|
+
|
|
60
67
|
// Dynamic dimensions - detected from model and database
|
|
61
68
|
let NATIVE_DIM = null;
|
|
62
69
|
let TARGET_DIM = null;
|
|
@@ -78,13 +85,39 @@ async function loadModel() {
|
|
|
78
85
|
try {
|
|
79
86
|
console.log('[Sandbox] Loading model from local cache...');
|
|
80
87
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
// Try HF cache first, fall back to bundled model
|
|
89
|
+
let modelSource = MODEL_NAME;
|
|
90
|
+
try {
|
|
91
|
+
extractor = await pipeline('feature-extraction', MODEL_NAME, {
|
|
92
|
+
local_files_only: true,
|
|
93
|
+
device: 'cpu'
|
|
94
|
+
});
|
|
95
|
+
} catch (hfErr) {
|
|
96
|
+
// HF cache miss â try bundled model shipped with npm package
|
|
97
|
+
if (existsSync(BUNDLED_MODEL_DIR)) {
|
|
98
|
+
console.log(`[Sandbox] HF cache miss, loading bundled model: ${BUNDLED_MODEL_DIR}`);
|
|
99
|
+
// Ensure model.onnx exists (bundled may only have model_quint8_avx2.onnx)
|
|
100
|
+
const onnxDir = join(BUNDLED_MODEL_DIR, 'onnx');
|
|
101
|
+
const modelOnnx = join(onnxDir, 'model.onnx');
|
|
102
|
+
if (!existsSync(modelOnnx) && existsSync(onnxDir)) {
|
|
103
|
+
// Find any .onnx file and symlink as model.onnx
|
|
104
|
+
const { readdirSync, symlinkSync } = await import('fs');
|
|
105
|
+
const onnxFiles = readdirSync(onnxDir).filter(f => f.endsWith('.onnx'));
|
|
106
|
+
if (onnxFiles.length > 0) {
|
|
107
|
+
try { symlinkSync(onnxFiles[0], modelOnnx); } catch {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
extractor = await pipeline('feature-extraction', BUNDLED_MODEL_DIR, {
|
|
111
|
+
local_files_only: true,
|
|
112
|
+
device: 'cpu'
|
|
113
|
+
});
|
|
114
|
+
modelSource = BUNDLED_MODEL_DIR;
|
|
115
|
+
} else {
|
|
116
|
+
throw hfErr;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Skip the duplicate pipeline call below â extractor is already loaded
|
|
88
121
|
|
|
89
122
|
modelReady = true;
|
|
90
123
|
|
package/mcp-proxy.cjs
CHANGED
|
@@ -261,7 +261,15 @@ function spawnServer() {
|
|
|
261
261
|
stopHeartbeat();
|
|
262
262
|
|
|
263
263
|
if (!shuttingDown) {
|
|
264
|
-
|
|
264
|
+
// If bootstrap was intentionally killed (SIGTERM/SIGKILL from init or system),
|
|
265
|
+
// don't restart â exit the proxy too. Init will start a fresh bootstrap.
|
|
266
|
+
// Only restart on crashes (non-zero exit code without signal).
|
|
267
|
+
if (signal === 'SIGTERM' || signal === 'SIGKILL') {
|
|
268
|
+
log(`Bootstrap was intentionally killed (${signal}) â proxy exiting`);
|
|
269
|
+
shutdown();
|
|
270
|
+
} else {
|
|
271
|
+
scheduleRestart();
|
|
272
|
+
}
|
|
265
273
|
}
|
|
266
274
|
});
|
|
267
275
|
|
|
@@ -386,6 +394,15 @@ function shutdown() {
|
|
|
386
394
|
process.on('SIGTERM', shutdown);
|
|
387
395
|
process.on('SIGINT', shutdown);
|
|
388
396
|
|
|
397
|
+
// Orphan detection: if parent (Claude) dies, proxy gets reparented to PID 1
|
|
398
|
+
// Check every 10s and exit if orphaned
|
|
399
|
+
setInterval(() => {
|
|
400
|
+
if (process.ppid === 1) {
|
|
401
|
+
log('Parent died (PPID=1), proxy shutting down');
|
|
402
|
+
shutdown();
|
|
403
|
+
}
|
|
404
|
+
}, 10000);
|
|
405
|
+
|
|
389
406
|
// ============================================================================
|
|
390
407
|
// Start
|
|
391
408
|
// ============================================================================
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specmem-hardwicksoftware",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.20",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
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",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
@@ -44,7 +44,18 @@
|
|
|
44
44
|
"anthropic",
|
|
45
45
|
"embeddings",
|
|
46
46
|
"context",
|
|
47
|
-
"hooks"
|
|
47
|
+
"hooks",
|
|
48
|
+
"code-indexing",
|
|
49
|
+
"typescript",
|
|
50
|
+
"javascript",
|
|
51
|
+
"python",
|
|
52
|
+
"java",
|
|
53
|
+
"cpp",
|
|
54
|
+
"rust",
|
|
55
|
+
"html",
|
|
56
|
+
"codebase-analysis",
|
|
57
|
+
"multi-language",
|
|
58
|
+
"team-coordination"
|
|
48
59
|
],
|
|
49
60
|
"author": {
|
|
50
61
|
"name": "Jonathan Hardwick",
|