@softerist/heuristic-mcp 3.0.15 → 3.0.16
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 +104 -104
- package/config.jsonc +173 -173
- package/features/ann-config.js +131 -0
- package/features/clear-cache.js +84 -0
- package/features/find-similar-code.js +291 -0
- package/features/hybrid-search.js +544 -0
- package/features/index-codebase.js +3268 -0
- package/features/lifecycle.js +1189 -0
- package/features/package-version.js +302 -0
- package/features/register.js +408 -0
- package/features/resources.js +156 -0
- package/features/set-workspace.js +265 -0
- package/index.js +96 -96
- package/lib/cache-ops.js +22 -22
- package/lib/cache-utils.js +565 -565
- package/lib/cache.js +1870 -1870
- package/lib/call-graph.js +396 -396
- package/lib/cli.js +1 -1
- package/lib/config.js +517 -517
- package/lib/constants.js +39 -39
- package/lib/embed-query-process.js +7 -7
- package/lib/embedding-process.js +7 -7
- package/lib/embedding-worker.js +299 -299
- package/lib/ignore-patterns.js +316 -316
- package/lib/json-worker.js +14 -14
- package/lib/json-writer.js +337 -337
- package/lib/logging.js +164 -164
- package/lib/memory-logger.js +13 -13
- package/lib/onnx-backend.js +193 -193
- package/lib/project-detector.js +84 -84
- package/lib/server-lifecycle.js +165 -165
- package/lib/settings-editor.js +754 -754
- package/lib/tokenizer.js +256 -256
- package/lib/utils.js +428 -428
- package/lib/vector-store-binary.js +627 -627
- package/lib/vector-store-sqlite.js +95 -95
- package/lib/workspace-env.js +28 -28
- package/mcp_config.json +9 -9
- package/package.json +86 -75
- package/scripts/clear-cache.js +20 -0
- package/scripts/download-model.js +43 -0
- package/scripts/mcp-launcher.js +49 -0
- package/scripts/postinstall.js +12 -0
- package/search-configs.js +36 -36
- package/.prettierrc +0 -7
- package/debug-pids.js +0 -30
- package/eslint.config.js +0 -36
- package/specs/plan.md +0 -23
- package/vitest.config.js +0 -39
|
@@ -224,18 +224,18 @@ export class SqliteVectorStore {
|
|
|
224
224
|
view.content = row.content;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
if (includeVector && row.vector) {
|
|
228
|
-
const expectedBytes = this.dim * Float32Array.BYTES_PER_ELEMENT;
|
|
229
|
-
if (row.vector.byteLength >= expectedBytes) {
|
|
230
|
-
// Copy the buffer to avoid issues with SQLite reusing internal buffers
|
|
231
|
-
const bufferView = new Float32Array(
|
|
232
|
-
row.vector.buffer,
|
|
233
|
-
row.vector.byteOffset,
|
|
234
|
-
this.dim
|
|
235
|
-
);
|
|
236
|
-
view.vector = new Float32Array(bufferView);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
227
|
+
if (includeVector && row.vector) {
|
|
228
|
+
const expectedBytes = this.dim * Float32Array.BYTES_PER_ELEMENT;
|
|
229
|
+
if (row.vector.byteLength >= expectedBytes) {
|
|
230
|
+
// Copy the buffer to avoid issues with SQLite reusing internal buffers
|
|
231
|
+
const bufferView = new Float32Array(
|
|
232
|
+
row.vector.buffer,
|
|
233
|
+
row.vector.byteOffset,
|
|
234
|
+
this.dim
|
|
235
|
+
);
|
|
236
|
+
view.vector = new Float32Array(bufferView);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
239
|
|
|
240
240
|
views.push(view);
|
|
241
241
|
}
|
|
@@ -264,7 +264,7 @@ export class SqliteVectorStore {
|
|
|
264
264
|
* @param {Array} chunks - Array of chunk objects with vector, file, startLine, endLine, content
|
|
265
265
|
* @param {Object} options - { getContent, preRename }
|
|
266
266
|
*/
|
|
267
|
-
static async write(cacheDir, chunks, { getContent, getVector, preRename } = {}) {
|
|
267
|
+
static async write(cacheDir, chunks, { getContent, getVector, preRename } = {}) {
|
|
268
268
|
if (!chunks || chunks.length === 0) {
|
|
269
269
|
return null;
|
|
270
270
|
}
|
|
@@ -286,10 +286,10 @@ export class SqliteVectorStore {
|
|
|
286
286
|
denseSourceIndices.push(i);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
const resolveContent = async (chunk, sourceIndex) => {
|
|
290
|
-
if (chunk.content !== undefined && chunk.content !== null) {
|
|
291
|
-
return chunk.content;
|
|
292
|
-
}
|
|
289
|
+
const resolveContent = async (chunk, sourceIndex) => {
|
|
290
|
+
if (chunk.content !== undefined && chunk.content !== null) {
|
|
291
|
+
return chunk.content;
|
|
292
|
+
}
|
|
293
293
|
if (typeof getContent === 'function') {
|
|
294
294
|
const value = getContent(chunk, sourceIndex);
|
|
295
295
|
if (value && typeof value.then === 'function') {
|
|
@@ -297,39 +297,39 @@ export class SqliteVectorStore {
|
|
|
297
297
|
}
|
|
298
298
|
return value;
|
|
299
299
|
}
|
|
300
|
-
return null;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
const resolveVector = async (chunk, sourceIndex) => {
|
|
304
|
-
let vectorSource = chunk.vector;
|
|
305
|
-
if (
|
|
306
|
-
(vectorSource === undefined || vectorSource === null) &&
|
|
307
|
-
typeof getVector === 'function'
|
|
308
|
-
) {
|
|
309
|
-
vectorSource = getVector(chunk, sourceIndex);
|
|
310
|
-
if (vectorSource && typeof vectorSource.then === 'function') {
|
|
311
|
-
vectorSource = await vectorSource;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (vectorSource === undefined || vectorSource === null) {
|
|
315
|
-
throw new Error(`Missing vector data for sqlite cache write at index ${sourceIndex}`);
|
|
316
|
-
}
|
|
317
|
-
const vector =
|
|
318
|
-
vectorSource instanceof Float32Array
|
|
319
|
-
? vectorSource
|
|
320
|
-
: ArrayBuffer.isView(vectorSource)
|
|
321
|
-
? Float32Array.from(vectorSource)
|
|
322
|
-
: Float32Array.from(vectorSource);
|
|
323
|
-
if (!vector || vector.length === 0) {
|
|
324
|
-
throw new Error(`Empty vector data for sqlite cache write at index ${sourceIndex}`);
|
|
325
|
-
}
|
|
326
|
-
return vector;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
const dim =
|
|
330
|
-
denseChunks.length > 0
|
|
331
|
-
? (await resolveVector(denseChunks[0], denseSourceIndices[0])).length
|
|
332
|
-
: 0;
|
|
300
|
+
return null;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const resolveVector = async (chunk, sourceIndex) => {
|
|
304
|
+
let vectorSource = chunk.vector;
|
|
305
|
+
if (
|
|
306
|
+
(vectorSource === undefined || vectorSource === null) &&
|
|
307
|
+
typeof getVector === 'function'
|
|
308
|
+
) {
|
|
309
|
+
vectorSource = getVector(chunk, sourceIndex);
|
|
310
|
+
if (vectorSource && typeof vectorSource.then === 'function') {
|
|
311
|
+
vectorSource = await vectorSource;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (vectorSource === undefined || vectorSource === null) {
|
|
315
|
+
throw new Error(`Missing vector data for sqlite cache write at index ${sourceIndex}`);
|
|
316
|
+
}
|
|
317
|
+
const vector =
|
|
318
|
+
vectorSource instanceof Float32Array
|
|
319
|
+
? vectorSource
|
|
320
|
+
: ArrayBuffer.isView(vectorSource)
|
|
321
|
+
? Float32Array.from(vectorSource)
|
|
322
|
+
: Float32Array.from(vectorSource);
|
|
323
|
+
if (!vector || vector.length === 0) {
|
|
324
|
+
throw new Error(`Empty vector data for sqlite cache write at index ${sourceIndex}`);
|
|
325
|
+
}
|
|
326
|
+
return vector;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const dim =
|
|
330
|
+
denseChunks.length > 0
|
|
331
|
+
? (await resolveVector(denseChunks[0], denseSourceIndices[0])).length
|
|
332
|
+
: 0;
|
|
333
333
|
|
|
334
334
|
// Create new database
|
|
335
335
|
const db = new Database(writePath);
|
|
@@ -364,51 +364,51 @@ export class SqliteVectorStore {
|
|
|
364
364
|
CREATE INDEX idx_chunks_file ON chunks(file);
|
|
365
365
|
`);
|
|
366
366
|
|
|
367
|
-
// Insert metadata
|
|
368
|
-
const insertMeta = db.prepare(`INSERT INTO metadata (key, value) VALUES (?, ?)`);
|
|
369
|
-
insertMeta.run('version', String(STORE_VERSION));
|
|
370
|
-
insertMeta.run('dim', String(dim));
|
|
371
|
-
insertMeta.run('count', String(denseChunks.length));
|
|
372
|
-
insertMeta.run('createdAt', new Date().toISOString());
|
|
373
|
-
|
|
374
|
-
// Insert chunks in a transaction for speed without pre-materializing all vectors/content.
|
|
375
|
-
const insertChunk = db.prepare(`
|
|
376
|
-
INSERT INTO chunks (id, file, startLine, endLine, content, vector)
|
|
377
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
378
|
-
`);
|
|
379
|
-
|
|
380
|
-
db.exec('BEGIN');
|
|
381
|
-
try {
|
|
382
|
-
for (let i = 0; i < denseChunks.length; i += 1) {
|
|
383
|
-
const chunk = denseChunks[i];
|
|
384
|
-
const sourceIndex = denseSourceIndices[i];
|
|
385
|
-
const vector = await resolveVector(chunk, sourceIndex);
|
|
386
|
-
if (vector.length !== dim) {
|
|
387
|
-
throw new Error('Vector dimension mismatch in sqlite cache write');
|
|
388
|
-
}
|
|
389
|
-
let content = await resolveContent(chunk, sourceIndex);
|
|
390
|
-
if (content === undefined) content = null;
|
|
391
|
-
if (content !== null && typeof content !== 'string') {
|
|
392
|
-
content = String(content);
|
|
393
|
-
}
|
|
394
|
-
insertChunk.run(
|
|
395
|
-
i,
|
|
396
|
-
chunk.file,
|
|
397
|
-
chunk.startLine ?? 0,
|
|
398
|
-
chunk.endLine ?? 0,
|
|
399
|
-
content,
|
|
400
|
-
Buffer.from(vector.buffer, vector.byteOffset, vector.byteLength)
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
db.exec('COMMIT');
|
|
404
|
-
} catch (error) {
|
|
405
|
-
try {
|
|
406
|
-
db.exec('ROLLBACK');
|
|
407
|
-
} catch {
|
|
408
|
-
// ignore rollback errors
|
|
409
|
-
}
|
|
410
|
-
throw error;
|
|
411
|
-
}
|
|
367
|
+
// Insert metadata
|
|
368
|
+
const insertMeta = db.prepare(`INSERT INTO metadata (key, value) VALUES (?, ?)`);
|
|
369
|
+
insertMeta.run('version', String(STORE_VERSION));
|
|
370
|
+
insertMeta.run('dim', String(dim));
|
|
371
|
+
insertMeta.run('count', String(denseChunks.length));
|
|
372
|
+
insertMeta.run('createdAt', new Date().toISOString());
|
|
373
|
+
|
|
374
|
+
// Insert chunks in a transaction for speed without pre-materializing all vectors/content.
|
|
375
|
+
const insertChunk = db.prepare(`
|
|
376
|
+
INSERT INTO chunks (id, file, startLine, endLine, content, vector)
|
|
377
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
378
|
+
`);
|
|
379
|
+
|
|
380
|
+
db.exec('BEGIN');
|
|
381
|
+
try {
|
|
382
|
+
for (let i = 0; i < denseChunks.length; i += 1) {
|
|
383
|
+
const chunk = denseChunks[i];
|
|
384
|
+
const sourceIndex = denseSourceIndices[i];
|
|
385
|
+
const vector = await resolveVector(chunk, sourceIndex);
|
|
386
|
+
if (vector.length !== dim) {
|
|
387
|
+
throw new Error('Vector dimension mismatch in sqlite cache write');
|
|
388
|
+
}
|
|
389
|
+
let content = await resolveContent(chunk, sourceIndex);
|
|
390
|
+
if (content === undefined) content = null;
|
|
391
|
+
if (content !== null && typeof content !== 'string') {
|
|
392
|
+
content = String(content);
|
|
393
|
+
}
|
|
394
|
+
insertChunk.run(
|
|
395
|
+
i,
|
|
396
|
+
chunk.file,
|
|
397
|
+
chunk.startLine ?? 0,
|
|
398
|
+
chunk.endLine ?? 0,
|
|
399
|
+
content,
|
|
400
|
+
Buffer.from(vector.buffer, vector.byteOffset, vector.byteLength)
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
db.exec('COMMIT');
|
|
404
|
+
} catch (error) {
|
|
405
|
+
try {
|
|
406
|
+
db.exec('ROLLBACK');
|
|
407
|
+
} catch {
|
|
408
|
+
// ignore rollback errors
|
|
409
|
+
}
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
412
412
|
|
|
413
413
|
// Optimize the database
|
|
414
414
|
db.exec('ANALYZE');
|
package/lib/workspace-env.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DYNAMIC_WORKSPACE_ENV_PREFIX,
|
|
3
|
-
WORKSPACE_ENV_KEY_PATTERN,
|
|
4
|
-
WORKSPACE_ENV_VARS,
|
|
5
|
-
} from './constants.js';
|
|
6
|
-
|
|
7
|
-
export function scoreWorkspaceEnvKey(key) {
|
|
8
|
-
const upper = String(key || '').toUpperCase();
|
|
9
|
-
let score = 0;
|
|
10
|
-
if (upper.includes('WORKSPACE')) score += 8;
|
|
11
|
-
if (upper.includes('PROJECT')) score += 4;
|
|
12
|
-
if (upper.includes('ROOT')) score += 3;
|
|
13
|
-
if (upper.includes('CWD')) score += 2;
|
|
14
|
-
if (upper.includes('DIR')) score += 1;
|
|
15
|
-
return score;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getDynamicWorkspaceEnvKeys(env = process.env) {
|
|
19
|
-
return Object.keys(env)
|
|
20
|
-
.filter((key) => key.startsWith(DYNAMIC_WORKSPACE_ENV_PREFIX))
|
|
21
|
-
.filter((key) => WORKSPACE_ENV_KEY_PATTERN.test(key))
|
|
22
|
-
.filter((key) => !WORKSPACE_ENV_VARS.includes(key))
|
|
23
|
-
.sort((a, b) => scoreWorkspaceEnvKey(b) - scoreWorkspaceEnvKey(a));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getWorkspaceEnvKeys(env = process.env) {
|
|
27
|
-
return [...WORKSPACE_ENV_VARS, ...getDynamicWorkspaceEnvKeys(env)];
|
|
28
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
DYNAMIC_WORKSPACE_ENV_PREFIX,
|
|
3
|
+
WORKSPACE_ENV_KEY_PATTERN,
|
|
4
|
+
WORKSPACE_ENV_VARS,
|
|
5
|
+
} from './constants.js';
|
|
6
|
+
|
|
7
|
+
export function scoreWorkspaceEnvKey(key) {
|
|
8
|
+
const upper = String(key || '').toUpperCase();
|
|
9
|
+
let score = 0;
|
|
10
|
+
if (upper.includes('WORKSPACE')) score += 8;
|
|
11
|
+
if (upper.includes('PROJECT')) score += 4;
|
|
12
|
+
if (upper.includes('ROOT')) score += 3;
|
|
13
|
+
if (upper.includes('CWD')) score += 2;
|
|
14
|
+
if (upper.includes('DIR')) score += 1;
|
|
15
|
+
return score;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getDynamicWorkspaceEnvKeys(env = process.env) {
|
|
19
|
+
return Object.keys(env)
|
|
20
|
+
.filter((key) => key.startsWith(DYNAMIC_WORKSPACE_ENV_PREFIX))
|
|
21
|
+
.filter((key) => WORKSPACE_ENV_KEY_PATTERN.test(key))
|
|
22
|
+
.filter((key) => !WORKSPACE_ENV_VARS.includes(key))
|
|
23
|
+
.sort((a, b) => scoreWorkspaceEnvKey(b) - scoreWorkspaceEnvKey(a));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getWorkspaceEnvKeys(env = process.env) {
|
|
27
|
+
return [...WORKSPACE_ENV_VARS, ...getDynamicWorkspaceEnvKeys(env)];
|
|
28
|
+
}
|
package/mcp_config.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"mcpServers": {
|
|
3
|
-
"heuristic-mcp": {
|
|
4
|
-
"command": "node",
|
|
5
|
-
"args": ["scripts\\mcp-launcher.js"],
|
|
6
|
-
"disabled": false
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"heuristic-mcp": {
|
|
4
|
+
"command": "node",
|
|
5
|
+
"args": ["scripts\\mcp-launcher.js"],
|
|
6
|
+
"disabled": false
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,75 +1,86 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@softerist/heuristic-mcp",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "An enhanced MCP server providing intelligent semantic code search with find-similar-code, recency ranking, and improved chunking. Fork of smart-coding-mcp.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"heuristic-mcp": "index.js"
|
|
9
|
-
},
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
},
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
-
}
|
|
75
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@softerist/heuristic-mcp",
|
|
3
|
+
"version": "3.0.16",
|
|
4
|
+
"description": "An enhanced MCP server providing intelligent semantic code search with find-similar-code, recency ranking, and improved chunking. Fork of smart-coding-mcp.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"heuristic-mcp": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"config.jsonc",
|
|
13
|
+
"mcp_config.json",
|
|
14
|
+
"search-configs.js",
|
|
15
|
+
"lib/",
|
|
16
|
+
"features/",
|
|
17
|
+
"scripts/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "node --expose-gc index.js",
|
|
23
|
+
"dev": "node --expose-gc --watch index.js",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"clean": "node scripts/clear-cache.js",
|
|
27
|
+
"lint": "eslint .",
|
|
28
|
+
"format": "prettier --write .",
|
|
29
|
+
"postinstall": "node scripts/postinstall.js && node scripts/download-model.js"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mcp",
|
|
33
|
+
"semantic-search",
|
|
34
|
+
"code-search",
|
|
35
|
+
"embeddings",
|
|
36
|
+
"ai",
|
|
37
|
+
"model-context-protocol",
|
|
38
|
+
"hybrid-search",
|
|
39
|
+
"code-intelligence",
|
|
40
|
+
"cursor",
|
|
41
|
+
"vscode",
|
|
42
|
+
"claude",
|
|
43
|
+
"codex",
|
|
44
|
+
"openai",
|
|
45
|
+
"gemini",
|
|
46
|
+
"anthropic",
|
|
47
|
+
"antigravity",
|
|
48
|
+
"heuristic"
|
|
49
|
+
],
|
|
50
|
+
"author": {
|
|
51
|
+
"name": "Softerist",
|
|
52
|
+
"url": "https://github.com/softerist"
|
|
53
|
+
},
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "git+https://github.com/softerist/heuristic-mcp.git"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/softerist/heuristic-mcp#readme",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@huggingface/transformers": "^3.8.1",
|
|
62
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
63
|
+
"better-sqlite3": "^12.6.2",
|
|
64
|
+
"chokidar": "^3.5.3",
|
|
65
|
+
"fdir": "^6.5.0",
|
|
66
|
+
"ignore": "^7.0.5",
|
|
67
|
+
"punycode": "^2.3.1"
|
|
68
|
+
},
|
|
69
|
+
"optionalDependencies": {
|
|
70
|
+
"hnswlib-node": "^3.0.0"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=18.0.0"
|
|
74
|
+
},
|
|
75
|
+
"overrides": {
|
|
76
|
+
"punycode": "^2.3.1"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^9.39.2",
|
|
80
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
81
|
+
"eslint": "^9.39.2",
|
|
82
|
+
"globals": "^17.1.0",
|
|
83
|
+
"prettier": "^3.8.1",
|
|
84
|
+
"vitest": "^4.0.16"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import { loadConfig } from '../lib/config.js';
|
|
4
|
+
|
|
5
|
+
async function clearCache() {
|
|
6
|
+
try {
|
|
7
|
+
const config = await loadConfig(process.cwd());
|
|
8
|
+
const cacheDir = config.cacheDirectory;
|
|
9
|
+
|
|
10
|
+
// Remove cache directory
|
|
11
|
+
await fs.rm(cacheDir, { recursive: true, force: true });
|
|
12
|
+
console.info(`Cache cleared successfully: ${cacheDir}`);
|
|
13
|
+
console.info('Next startup will perform a full reindex.');
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error(`Error clearing cache: ${error.message}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
clearCache();
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { getGlobalCacheDir } from '../lib/config.js';
|
|
4
|
+
|
|
5
|
+
async function downloadModel() {
|
|
6
|
+
const globalCacheDir = path.join(getGlobalCacheDir(), 'xenova');
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const transformers = await import('@huggingface/transformers');
|
|
10
|
+
const { pipeline, env } = transformers;
|
|
11
|
+
|
|
12
|
+
// Force cache directory to global location
|
|
13
|
+
env.cacheDir = globalCacheDir;
|
|
14
|
+
|
|
15
|
+
console.info(`[Model Setup] Pre-caching model to: ${globalCacheDir}`);
|
|
16
|
+
// Check if network is available by pinging HF (simple check)
|
|
17
|
+
// Actually, pipeline() will fail fast if network is down
|
|
18
|
+
console.info(`[Model Setup] Downloading 'jinaai/jina-embeddings-v2-base-code'...`);
|
|
19
|
+
|
|
20
|
+
// This will download the model to the cache directory
|
|
21
|
+
await pipeline('feature-extraction', 'jinaai/jina-embeddings-v2-base-code');
|
|
22
|
+
|
|
23
|
+
console.info(`[Model Setup] ✅ Model cached successfully!`);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error && error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
26
|
+
console.warn(
|
|
27
|
+
'[Model Setup] ⚠️ Transformers not available yet; skipping model pre-download.'
|
|
28
|
+
);
|
|
29
|
+
console.warn(
|
|
30
|
+
'[Model Setup] This is okay! The server will attempt to download it when started.'
|
|
31
|
+
);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.warn(`[Model Setup] ⚠️ Constructive warning: Failed to pre-download model.`);
|
|
35
|
+
console.warn(
|
|
36
|
+
'[Model Setup] This is okay! The server will attempt to download it when started.'
|
|
37
|
+
);
|
|
38
|
+
console.warn(`[Model Setup] Error details: ${error.message}`);
|
|
39
|
+
// Don't fail the install, just warn
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
downloadModel();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { getWorkspaceEnvKeys } from '../lib/workspace-env.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
8
|
+
const indexPath = path.join(repoRoot, 'index.js');
|
|
9
|
+
|
|
10
|
+
let workspace;
|
|
11
|
+
const passthrough = [];
|
|
12
|
+
|
|
13
|
+
function readWorkspaceFromEnv() {
|
|
14
|
+
for (const key of getWorkspaceEnvKeys()) {
|
|
15
|
+
const value = process.env[key];
|
|
16
|
+
if (!value || value.includes('${')) continue;
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (let i = 2; i < process.argv.length; i += 1) {
|
|
23
|
+
const arg = process.argv[i];
|
|
24
|
+
if (arg === '--workspace' && i + 1 < process.argv.length) {
|
|
25
|
+
workspace = process.argv[i + 1];
|
|
26
|
+
i += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
passthrough.push(arg);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!workspace) {
|
|
33
|
+
workspace = readWorkspaceFromEnv();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!workspace) {
|
|
37
|
+
workspace = process.cwd();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const args = ['--expose-gc', indexPath, '--workspace', workspace, ...passthrough];
|
|
41
|
+
const child = spawn(process.execPath, args, { stdio: 'inherit' });
|
|
42
|
+
|
|
43
|
+
child.on('exit', (code, signal) => {
|
|
44
|
+
if (signal) {
|
|
45
|
+
process.kill(process.pid, signal);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
process.exit(code ?? 1);
|
|
49
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { register } from '../features/register.js';
|
|
2
|
+
|
|
3
|
+
// Run the registration process - MUST await to ensure file writes complete
|
|
4
|
+
console.info('[PostInstall] Running Heuristic MCP registration...');
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
await register();
|
|
8
|
+
console.info('[PostInstall] Registration complete.');
|
|
9
|
+
} catch (err) {
|
|
10
|
+
console.error('[PostInstall] Registration failed:', err.message);
|
|
11
|
+
// Don't fail the install if registration fails, just warn
|
|
12
|
+
}
|
package/search-configs.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { loadConfig } from './lib/config.js';
|
|
2
|
-
import { EmbeddingsCache } from './lib/cache.js';
|
|
3
|
-
import { HybridSearch } from './features/hybrid-search.js';
|
|
4
|
-
import { pipeline, env } from '@huggingface/transformers';
|
|
5
|
-
|
|
6
|
-
// Force same thread config as server
|
|
7
|
-
if (env?.backends?.onnx) {
|
|
8
|
-
env.backends.onnx.numThreads = 2;
|
|
9
|
-
if (env.backends.onnx.wasm) {
|
|
10
|
-
env.backends.onnx.wasm.numThreads = 2;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function searchConfigs() {
|
|
15
|
-
const config = await loadConfig(process.cwd());
|
|
16
|
-
const cache = new EmbeddingsCache(config);
|
|
17
|
-
await cache.load();
|
|
18
|
-
|
|
19
|
-
const embedder = async (text) => {
|
|
20
|
-
const pipe = await pipeline('feature-extraction', config.embeddingModel, {
|
|
21
|
-
session_options: { numThreads: 2 },
|
|
22
|
-
dtype: 'fp32',
|
|
23
|
-
});
|
|
24
|
-
return pipe(text, { pooling: 'mean', normalize: true });
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const searcher = new HybridSearch(embedder, cache, config);
|
|
28
|
-
const { results } = await searcher.search('configuration files, config, settings');
|
|
29
|
-
|
|
30
|
-
console.info(JSON.stringify(results, null, 2));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
searchConfigs().catch((err) => {
|
|
34
|
-
console.error(err);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
});
|
|
1
|
+
import { loadConfig } from './lib/config.js';
|
|
2
|
+
import { EmbeddingsCache } from './lib/cache.js';
|
|
3
|
+
import { HybridSearch } from './features/hybrid-search.js';
|
|
4
|
+
import { pipeline, env } from '@huggingface/transformers';
|
|
5
|
+
|
|
6
|
+
// Force same thread config as server
|
|
7
|
+
if (env?.backends?.onnx) {
|
|
8
|
+
env.backends.onnx.numThreads = 2;
|
|
9
|
+
if (env.backends.onnx.wasm) {
|
|
10
|
+
env.backends.onnx.wasm.numThreads = 2;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function searchConfigs() {
|
|
15
|
+
const config = await loadConfig(process.cwd());
|
|
16
|
+
const cache = new EmbeddingsCache(config);
|
|
17
|
+
await cache.load();
|
|
18
|
+
|
|
19
|
+
const embedder = async (text) => {
|
|
20
|
+
const pipe = await pipeline('feature-extraction', config.embeddingModel, {
|
|
21
|
+
session_options: { numThreads: 2 },
|
|
22
|
+
dtype: 'fp32',
|
|
23
|
+
});
|
|
24
|
+
return pipe(text, { pooling: 'mean', normalize: true });
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const searcher = new HybridSearch(embedder, cache, config);
|
|
28
|
+
const { results } = await searcher.search('configuration files, config, settings');
|
|
29
|
+
|
|
30
|
+
console.info(JSON.stringify(results, null, 2));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
searchConfigs().catch((err) => {
|
|
34
|
+
console.error(err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|