@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,192 +1,170 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
import path from 'path';
|
|
4
2
|
import fs from 'fs/promises';
|
|
5
3
|
import { acquireWorkspaceLock, releaseWorkspaceLock } from '../lib/server-lifecycle.js';
|
|
6
4
|
import { getWorkspaceCachePath } from '../lib/workspace-cache-key.js';
|
|
7
5
|
import { cleanupStaleBinaryArtifacts } from '../lib/vector-store-binary.js';
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
|
|
10
7
|
function getWorkspaceCacheDir(workspacePath, globalCacheDir) {
|
|
11
8
|
return getWorkspaceCachePath(workspacePath, globalCacheDir);
|
|
12
9
|
}
|
|
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
|
-
this.
|
|
49
|
-
this.
|
|
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
|
-
const
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
} catch (rollbackErr) {
|
|
173
|
-
console.warn(
|
|
174
|
-
`[SetWorkspace] Failed to rollback indexer state: ${rollbackErr.message}`
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
success: false,
|
|
180
|
-
error: `Failed to update workspace state: ${indexerUpdateError.message}`,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (previousCache) {
|
|
186
|
-
await releaseWorkspaceLock({ cacheDirectory: previousCache });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
10
|
+
|
|
11
|
+
export function getToolDefinition() {
|
|
12
|
+
return {
|
|
13
|
+
name: 'f_set_workspace',
|
|
14
|
+
description:
|
|
15
|
+
'Changes the current workspace path at runtime. This updates the search directory and cache, and optionally triggers a full reindex. Useful for multi-project workflows.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
workspacePath: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Absolute path to the new workspace directory',
|
|
22
|
+
},
|
|
23
|
+
reindex: {
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
description: 'Whether to trigger a full reindex after switching (default: true)',
|
|
26
|
+
default: true,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ['workspacePath'],
|
|
30
|
+
},
|
|
31
|
+
annotations: {
|
|
32
|
+
title: 'Set Workspace',
|
|
33
|
+
readOnlyHint: false,
|
|
34
|
+
destructiveHint: false,
|
|
35
|
+
idempotentHint: true,
|
|
36
|
+
openWorldHint: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class SetWorkspaceFeature {
|
|
42
|
+
constructor(config, cache, indexer, getGlobalCacheDir) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.cache = cache;
|
|
45
|
+
this.indexer = indexer;
|
|
46
|
+
this.getGlobalCacheDir = getGlobalCacheDir;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async execute({ workspacePath, reindex = true }) {
|
|
50
|
+
if (!workspacePath || typeof workspacePath !== 'string') {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: 'workspacePath is required and must be a string',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const normalizedPath = path.resolve(workspacePath);
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const stat = await fs.stat(normalizedPath);
|
|
61
|
+
if (!stat.isDirectory()) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: `Path is not a directory: ${normalizedPath}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: `Cannot access directory: ${normalizedPath} (${err.message})`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const previousWorkspace = this.config.searchDirectory;
|
|
75
|
+
const previousCache = this.config.cacheDirectory;
|
|
76
|
+
|
|
77
|
+
this.config.searchDirectory = normalizedPath;
|
|
78
|
+
|
|
79
|
+
const globalCacheDir = this.getGlobalCacheDir();
|
|
80
|
+
let newCacheDir = getWorkspaceCacheDir(normalizedPath, globalCacheDir);
|
|
81
|
+
|
|
82
|
+
const legacyPath = path.join(normalizedPath, '.smart-coding-cache');
|
|
83
|
+
try {
|
|
84
|
+
const legacyStats = await fs.stat(legacyPath);
|
|
85
|
+
if (legacyStats.isDirectory()) {
|
|
86
|
+
newCacheDir = legacyPath;
|
|
87
|
+
}
|
|
88
|
+
} catch {}
|
|
89
|
+
this.config.cacheDirectory = newCacheDir;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await fs.mkdir(newCacheDir, { recursive: true });
|
|
93
|
+
} catch (err) {
|
|
94
|
+
this.config.searchDirectory = previousWorkspace;
|
|
95
|
+
this.config.cacheDirectory = previousCache;
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error: `Failed to create cache directory: ${err.message}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const lock = await acquireWorkspaceLock({
|
|
103
|
+
cacheDirectory: newCacheDir,
|
|
104
|
+
workspaceDir: normalizedPath,
|
|
105
|
+
});
|
|
106
|
+
if (!lock.acquired) {
|
|
107
|
+
this.config.searchDirectory = previousWorkspace;
|
|
108
|
+
this.config.cacheDirectory = previousCache;
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
error: `Workspace is already locked by another server (pid ${lock.ownerPid ?? 'unknown'})`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
let indexerUpdateError = null;
|
|
115
|
+
|
|
116
|
+
if (this.indexer) {
|
|
117
|
+
if (typeof this.indexer.terminateWorkers === 'function') {
|
|
118
|
+
try {
|
|
119
|
+
await this.indexer.terminateWorkers();
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.warn(`[SetWorkspace] Failed to terminate workers: ${err.message}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
if (typeof this.indexer.updateWorkspaceState === 'function') {
|
|
126
|
+
await this.indexer.updateWorkspaceState({ restartWatcher: true });
|
|
127
|
+
} else {
|
|
128
|
+
this.indexer.workspaceRoot = normalizedPath;
|
|
129
|
+
this.indexer.workspaceRootReal = null;
|
|
130
|
+
if (this.config.watchFiles && typeof this.indexer.setupFileWatcher === 'function') {
|
|
131
|
+
await this.indexer.setupFileWatcher();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
indexerUpdateError = err;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (indexerUpdateError) {
|
|
140
|
+
this.config.searchDirectory = previousWorkspace;
|
|
141
|
+
this.config.cacheDirectory = previousCache;
|
|
142
|
+
await releaseWorkspaceLock({ cacheDirectory: newCacheDir });
|
|
143
|
+
if (this.indexer) {
|
|
144
|
+
try {
|
|
145
|
+
if (typeof this.indexer.updateWorkspaceState === 'function') {
|
|
146
|
+
await this.indexer.updateWorkspaceState({ restartWatcher: true });
|
|
147
|
+
} else {
|
|
148
|
+
this.indexer.workspaceRoot = previousWorkspace;
|
|
149
|
+
this.indexer.workspaceRootReal = null;
|
|
150
|
+
if (this.config.watchFiles && typeof this.indexer.setupFileWatcher === 'function') {
|
|
151
|
+
await this.indexer.setupFileWatcher();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch (rollbackErr) {
|
|
155
|
+
console.warn(`[SetWorkspace] Failed to rollback indexer state: ${rollbackErr.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
error: `Failed to update workspace state: ${indexerUpdateError.message}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (previousCache) {
|
|
165
|
+
await releaseWorkspaceLock({ cacheDirectory: previousCache });
|
|
166
|
+
}
|
|
167
|
+
|
|
190
168
|
if (this.cache && typeof this.cache.load === 'function') {
|
|
191
169
|
try {
|
|
192
170
|
if (this.config.vectorStoreFormat === 'binary') {
|
|
@@ -196,62 +174,58 @@ export class SetWorkspaceFeature {
|
|
|
196
174
|
} catch (err) {
|
|
197
175
|
console.warn(`[SetWorkspace] Failed to load cache: ${err.message}`);
|
|
198
176
|
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
reindexStatus =
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
export { getWorkspaceCacheDir };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let reindexStatus = null;
|
|
180
|
+
if (reindex && this.indexer && typeof this.indexer.indexAll === 'function') {
|
|
181
|
+
try {
|
|
182
|
+
this.indexer.indexAll().catch((err) => {
|
|
183
|
+
console.warn(`[SetWorkspace] Reindex failed: ${err.message}`);
|
|
184
|
+
});
|
|
185
|
+
reindexStatus = 'started';
|
|
186
|
+
} catch (err) {
|
|
187
|
+
reindexStatus = `failed: ${err.message}`;
|
|
188
|
+
}
|
|
189
|
+
} else if (!reindex) {
|
|
190
|
+
reindexStatus = 'skipped';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
previousWorkspace,
|
|
196
|
+
newWorkspace: normalizedPath,
|
|
197
|
+
cacheDirectory: newCacheDir,
|
|
198
|
+
reindexStatus,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function createHandleToolCall(featureInstance) {
|
|
204
|
+
return async (request) => {
|
|
205
|
+
const args = request.params?.arguments || {};
|
|
206
|
+
const { workspacePath, reindex } = args;
|
|
207
|
+
|
|
208
|
+
const result = await featureInstance.execute({
|
|
209
|
+
workspacePath,
|
|
210
|
+
reindex: reindex !== false,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (result.success) {
|
|
214
|
+
let message = `✓ Workspace switched to: **${result.newWorkspace}**\n`;
|
|
215
|
+
message += `\n- Previous: \`${result.previousWorkspace || '(none)'}\``;
|
|
216
|
+
message += `\n- Cache: \`${result.cacheDirectory}\``;
|
|
217
|
+
if (result.reindexStatus) {
|
|
218
|
+
message += `\n- Reindex: ${result.reindexStatus}`;
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
content: [{ type: 'text', text: message }],
|
|
222
|
+
};
|
|
223
|
+
} else {
|
|
224
|
+
return {
|
|
225
|
+
content: [{ type: 'text', text: `Error: ${result.error}` }],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { getWorkspaceCacheDir };
|