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