@softerist/heuristic-mcp 3.2.3 → 3.2.5
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 +387 -376
- package/config.jsonc +800 -800
- package/features/ann-config.js +102 -110
- package/features/clear-cache.js +81 -84
- package/features/find-similar-code.js +265 -286
- package/features/hybrid-search.js +487 -536
- package/features/index-codebase.js +3146 -3271
- package/features/lifecycle.js +1011 -1063
- package/features/package-version.js +277 -291
- package/features/register.js +351 -370
- package/features/resources.js +115 -130
- package/features/set-workspace.js +214 -240
- package/index.js +788 -781
- package/lib/cache-ops.js +22 -22
- package/lib/cache-utils.js +465 -519
- package/lib/cache.js +1749 -1849
- package/lib/call-graph.js +396 -396
- package/lib/cli.js +232 -226
- package/lib/config.js +1483 -1495
- package/lib/constants.js +511 -493
- package/lib/embed-query-process.js +206 -212
- package/lib/embedding-process.js +434 -451
- package/lib/embedding-worker.js +862 -934
- package/lib/ignore-patterns.js +276 -316
- package/lib/json-worker.js +14 -14
- package/lib/json-writer.js +302 -310
- package/lib/logging.js +133 -127
- package/lib/memory-logger.js +13 -13
- package/lib/onnx-backend.js +188 -193
- package/lib/path-utils.js +18 -23
- package/lib/project-detector.js +82 -84
- package/lib/server-lifecycle.js +164 -147
- package/lib/settings-editor.js +738 -739
- package/lib/slice-normalize.js +25 -31
- package/lib/tokenizer.js +168 -203
- package/lib/utils.js +364 -409
- package/lib/vector-store-binary.js +973 -991
- package/lib/vector-store-sqlite.js +377 -414
- package/lib/workspace-env.js +32 -34
- package/mcp_config.json +9 -9
- package/package.json +86 -86
- package/scripts/clear-cache.js +20 -20
- package/scripts/download-model.js +43 -43
- package/scripts/mcp-launcher.js +49 -49
- package/scripts/postinstall.js +12 -12
- package/search-configs.js +36 -36
|
@@ -1,212 +1,206 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import readline from 'readline';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let persistentChild = null;
|
|
11
|
-
let childReadline = null;
|
|
12
|
-
let idleTimer = null;
|
|
13
|
-
let currentConfig = null;
|
|
14
|
-
let pendingRequests = [];
|
|
15
|
-
let isProcessingRequest = false;
|
|
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
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
childReadline
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
persistentChild.
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
export function isEmbeddingPoolActive() {
|
|
211
|
-
return persistentChild !== null && !persistentChild.killed;
|
|
212
|
-
}
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
import { EMBEDDING_POOL_IDLE_TIMEOUT_MS } from './constants.js';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const EMBEDDING_PROCESS_PATH = path.join(__dirname, 'embedding-process.js');
|
|
9
|
+
|
|
10
|
+
let persistentChild = null;
|
|
11
|
+
let childReadline = null;
|
|
12
|
+
let idleTimer = null;
|
|
13
|
+
let currentConfig = null;
|
|
14
|
+
let pendingRequests = [];
|
|
15
|
+
let isProcessingRequest = false;
|
|
16
|
+
|
|
17
|
+
const DEFAULT_IDLE_TIMEOUT_MS = EMBEDDING_POOL_IDLE_TIMEOUT_MS;
|
|
18
|
+
|
|
19
|
+
function getOrCreateChild(config) {
|
|
20
|
+
if (persistentChild && !persistentChild.killed) {
|
|
21
|
+
resetIdleTimer(config);
|
|
22
|
+
return persistentChild;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const args = ['--expose-gc', EMBEDDING_PROCESS_PATH];
|
|
26
|
+
persistentChild = spawn(process.execPath, args, {
|
|
27
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
28
|
+
env: {
|
|
29
|
+
...process.env,
|
|
30
|
+
EMBEDDING_PROCESS_PERSISTENT: 'true',
|
|
31
|
+
EMBEDDING_PROCESS_RUN_MAIN: 'true',
|
|
32
|
+
EMBEDDING_PROCESS_VERBOSE: config.verbose ? 'true' : '',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
currentConfig = config;
|
|
37
|
+
|
|
38
|
+
childReadline = readline.createInterface({
|
|
39
|
+
input: persistentChild.stdout,
|
|
40
|
+
crlfDelay: Infinity,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
childReadline.on('line', (line) => {
|
|
44
|
+
if (!line.trim()) return;
|
|
45
|
+
|
|
46
|
+
if (pendingRequests.length > 0) {
|
|
47
|
+
const { resolve, reject, startTime } = pendingRequests.shift();
|
|
48
|
+
try {
|
|
49
|
+
const result = JSON.parse(line);
|
|
50
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
51
|
+
|
|
52
|
+
if (!result.results || result.results.length === 0) {
|
|
53
|
+
reject(new Error('Embedding child process returned no results'));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const embResult = result.results[0];
|
|
58
|
+
if (!embResult.success) {
|
|
59
|
+
reject(new Error(`Embedding failed: ${embResult.error}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const vector = new Float32Array(embResult.vector);
|
|
64
|
+
|
|
65
|
+
if (currentConfig?.verbose) {
|
|
66
|
+
console.info(`[Search] Query embedding (persistent child) completed in ${elapsed}s`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
resolve(vector);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
reject(new Error(`Failed to parse embedding result: ${err.message}`));
|
|
72
|
+
} finally {
|
|
73
|
+
isProcessingRequest = false;
|
|
74
|
+
processNextRequest();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
persistentChild.stderr.on('data', (data) => {
|
|
80
|
+
if (currentConfig?.verbose) {
|
|
81
|
+
process.stderr.write(data);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
persistentChild.on('error', (err) => {
|
|
86
|
+
console.error(`[EmbedPool] Child process error: ${err.message}`);
|
|
87
|
+
cleanupChild();
|
|
88
|
+
|
|
89
|
+
for (const { reject } of pendingRequests) {
|
|
90
|
+
reject(new Error(`Child process error: ${err.message}`));
|
|
91
|
+
}
|
|
92
|
+
pendingRequests = [];
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
persistentChild.on('close', (code) => {
|
|
96
|
+
if (currentConfig?.verbose) {
|
|
97
|
+
console.info(`[EmbedPool] Child process exited with code ${code}`);
|
|
98
|
+
}
|
|
99
|
+
cleanupChild();
|
|
100
|
+
|
|
101
|
+
for (const { reject } of pendingRequests) {
|
|
102
|
+
reject(new Error(`Child process exited unexpectedly with code ${code}`));
|
|
103
|
+
}
|
|
104
|
+
pendingRequests = [];
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
resetIdleTimer(config);
|
|
108
|
+
|
|
109
|
+
if (config.verbose) {
|
|
110
|
+
console.info(
|
|
111
|
+
`[EmbedPool] Started persistent embedding child process (PID: ${persistentChild.pid})`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return persistentChild;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function resetIdleTimer(config) {
|
|
119
|
+
if (idleTimer) {
|
|
120
|
+
clearTimeout(idleTimer);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const timeout = config.embeddingPoolIdleTimeoutMs || DEFAULT_IDLE_TIMEOUT_MS;
|
|
124
|
+
|
|
125
|
+
idleTimer = setTimeout(() => {
|
|
126
|
+
if (persistentChild && !persistentChild.killed) {
|
|
127
|
+
if (currentConfig?.verbose) {
|
|
128
|
+
console.info(
|
|
129
|
+
`[EmbedPool] Idle timeout reached, shutting down child process to free memory`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
shutdownChild();
|
|
133
|
+
}
|
|
134
|
+
}, timeout);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function cleanupChild() {
|
|
138
|
+
if (idleTimer) {
|
|
139
|
+
clearTimeout(idleTimer);
|
|
140
|
+
idleTimer = null;
|
|
141
|
+
}
|
|
142
|
+
if (childReadline) {
|
|
143
|
+
childReadline.close();
|
|
144
|
+
childReadline = null;
|
|
145
|
+
}
|
|
146
|
+
persistentChild = null;
|
|
147
|
+
isProcessingRequest = false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function shutdownChild() {
|
|
151
|
+
if (persistentChild && !persistentChild.killed) {
|
|
152
|
+
try {
|
|
153
|
+
persistentChild.stdin.write(JSON.stringify({ type: 'shutdown' }) + '\n');
|
|
154
|
+
} catch {
|
|
155
|
+
persistentChild.kill();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
cleanupChild();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function processNextRequest() {
|
|
162
|
+
if (isProcessingRequest || pendingRequests.length === 0) return;
|
|
163
|
+
|
|
164
|
+
const request = pendingRequests[0];
|
|
165
|
+
if (!request) return;
|
|
166
|
+
|
|
167
|
+
isProcessingRequest = true;
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const child = getOrCreateChild(request.config);
|
|
171
|
+
child.stdin.write(JSON.stringify(request.payload) + '\n');
|
|
172
|
+
} catch (err) {
|
|
173
|
+
const { reject } = pendingRequests.shift();
|
|
174
|
+
reject(err);
|
|
175
|
+
isProcessingRequest = false;
|
|
176
|
+
processNextRequest();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function embedQueryInChildProcess(query, config) {
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const payload = {
|
|
183
|
+
embeddingModel: config.embeddingModel,
|
|
184
|
+
numThreads: config.embeddingProcessNumThreads || 4,
|
|
185
|
+
chunks: [{ file: '__query__', startLine: 0, endLine: 0, text: query }],
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
pendingRequests.push({
|
|
189
|
+
payload,
|
|
190
|
+
config,
|
|
191
|
+
resolve,
|
|
192
|
+
reject,
|
|
193
|
+
startTime: Date.now(),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
processNextRequest();
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function forceShutdownEmbeddingPool() {
|
|
201
|
+
shutdownChild();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function isEmbeddingPoolActive() {
|
|
205
|
+
return persistentChild !== null && !persistentChild.killed;
|
|
206
|
+
}
|