@softerist/heuristic-mcp 3.2.3 → 3.2.4

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 +3139 -3270
  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 +693 -758
  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 +116 -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 +133 -145
  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,291 +1,277 @@
1
-
2
-
3
-
4
- const REGISTRIES = {
5
- npm: {
6
- name: 'npm',
7
- pattern: /^(?:npm:)?(.+)$/,
8
- url: (pkg) => `https://registry.npmjs.org/${encodeURIComponent(pkg)}/latest`,
9
- parse: (data) => data.version,
10
- detect: (pkg) =>
11
- pkg.startsWith('@') || /^[a-z0-9][-a-z0-9._]*$/i.test(pkg.replace(/^npm:/, '')),
12
- },
13
- pypi: {
14
- name: 'PyPI',
15
- pattern: /^(?:pip:|pypi:)(.+)$/,
16
- url: (pkg) => `https://pypi.org/pypi/${encodeURIComponent(pkg)}/json`,
17
- parse: (data) => data.info.version,
18
- detect: () => false,
19
- },
20
- crates: {
21
- name: 'crates.io',
22
- pattern: /^(?:cargo:|crates:|rust:)(.+)$/,
23
- url: (pkg) => `https://crates.io/api/v1/crates/${encodeURIComponent(pkg)}`,
24
- parse: (data) => data.crate.max_version,
25
- headers: { 'User-Agent': 'heuristic-mcp/1.0' },
26
- detect: () => false,
27
- },
28
- go: {
29
- name: 'Go',
30
- pattern: /^(?:go:)(.+)$/,
31
- url: (pkg) => `https://proxy.golang.org/${encodeURIComponent(pkg)}/@latest`,
32
- parse: (data) => data.Version,
33
- detect: (pkg) => pkg.includes('/') && pkg.includes('.'),
34
- },
35
- rubygems: {
36
- name: 'RubyGems',
37
- pattern: /^(?:gem:|ruby:)(.+)$/,
38
- url: (pkg) => `https://rubygems.org/api/v1/gems/${encodeURIComponent(pkg)}.json`,
39
- parse: (data) => data.version,
40
- detect: () => false,
41
- },
42
- nuget: {
43
- name: 'NuGet',
44
- pattern: /^(?:nuget:|dotnet:)(.+)$/,
45
- url: (pkg) =>
46
- `https://api.nuget.org/v3-flatcontainer/${encodeURIComponent(pkg.toLowerCase())}/index.json`,
47
- parse: (data) => data.versions[data.versions.length - 1],
48
- detect: () => false,
49
- },
50
- packagist: {
51
- name: 'Packagist',
52
- pattern: /^(?:composer:|php:)(.+)$/,
53
- url: (pkg) => `https://repo.packagist.org/p2/${encodeURIComponent(pkg)}.json`,
54
- parse: (data) => {
55
- const pkgName = Object.keys(data.packages)[0];
56
- const versions = data.packages[pkgName];
57
-
58
- const stable = versions.find((v) => !v.version.includes('dev'));
59
- return stable ? stable.version : versions[0].version;
60
- },
61
- detect: (pkg) => pkg.includes('/'),
62
- },
63
- hex: {
64
- name: 'Hex',
65
- pattern: /^(?:hex:|elixir:|mix:)(.+)$/,
66
- url: (pkg) => `https://hex.pm/api/packages/${encodeURIComponent(pkg)}`,
67
- parse: (data) => {
68
- const releases = data.releases;
69
- return releases.length > 0 ? releases[0].version : null;
70
- },
71
- detect: () => false,
72
- },
73
- pub: {
74
- name: 'pub.dev',
75
- pattern: /^(?:pub:|dart:|flutter:)(.+)$/,
76
- url: (pkg) => `https://pub.dev/api/packages/${encodeURIComponent(pkg)}`,
77
- parse: (data) => data.latest.version,
78
- detect: () => false,
79
- },
80
- maven: {
81
- name: 'Maven Central',
82
- pattern: /^(?:maven:|java:)(.+)$/,
83
- url: (pkg) => {
84
-
85
- const [group, artifact] = pkg.includes(':') ? pkg.split(':') : pkg.split('/');
86
- if (!artifact) return null;
87
- return `https://search.maven.org/solrsearch/select?q=g:${encodeURIComponent(group)}+AND+a:${encodeURIComponent(artifact)}&rows=1&wt=json`;
88
- },
89
- parse: (data) => {
90
- if (data.response.docs.length === 0) return null;
91
- return data.response.docs[0].latestVersion;
92
- },
93
- detect: (pkg) => pkg.includes(':') || (pkg.includes('.') && pkg.includes('/')),
94
- },
95
- homebrew: {
96
- name: 'Homebrew',
97
- pattern: /^(?:brew:|homebrew:)(.+)$/,
98
- url: (pkg) => `https://formulae.brew.sh/api/formula/${encodeURIComponent(pkg)}.json`,
99
- parse: (data) => data.versions.stable,
100
- detect: () => false,
101
- },
102
- conda: {
103
- name: 'Conda',
104
- pattern: /^(?:conda:)(.+)$/,
105
- url: (pkg) =>
106
- `https://api.anaconda.org/package/conda-forge/${encodeURIComponent(pkg)}`,
107
- parse: (data) => data.latest_version,
108
- detect: () => false,
109
- },
110
- };
111
-
112
-
113
- function detectRegistry(packageName) {
114
-
115
- for (const [key, registry] of Object.entries(REGISTRIES)) {
116
- if (registry.pattern.test(packageName) && key !== 'npm') {
117
- const match = packageName.match(registry.pattern);
118
- if (match) {
119
- return { registry, cleanName: match[1] };
120
- }
121
- }
122
- }
123
-
124
-
125
- for (const registry of Object.values(REGISTRIES)) {
126
- if (registry.detect(packageName)) {
127
- const match = packageName.match(registry.pattern);
128
- return { registry, cleanName: match ? match[1] : packageName };
129
- }
130
- }
131
-
132
-
133
- const npmMatch = packageName.match(REGISTRIES.npm.pattern);
134
- return { registry: REGISTRIES.npm, cleanName: npmMatch ? npmMatch[1] : packageName };
135
- }
136
-
137
-
138
- async function fetchPackageVersion(packageName, timeoutMs = 10000) {
139
- const { registry, cleanName } = detectRegistry(packageName);
140
-
141
- const url = registry.url(cleanName);
142
- if (!url) {
143
- return {
144
- success: false,
145
- error: `Invalid package format for ${registry.name}: ${cleanName}`,
146
- registry: registry.name,
147
- };
148
- }
149
-
150
- const controller = new AbortController();
151
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
152
-
153
- try {
154
- const headers = {
155
- Accept: 'application/json',
156
- ...(registry.headers || {}),
157
- };
158
-
159
- const response = await fetch(url, {
160
- headers,
161
- signal: controller.signal,
162
- });
163
-
164
- if (!response.ok) {
165
- if (response.status === 404) {
166
- return {
167
- success: false,
168
- error: `Package "${cleanName}" not found on ${registry.name}`,
169
- registry: registry.name,
170
- };
171
- }
172
- return {
173
- success: false,
174
- error: `${registry.name} returned status ${response.status}`,
175
- registry: registry.name,
176
- };
177
- }
178
-
179
- const data = await response.json();
180
- const version = registry.parse(data);
181
-
182
- if (!version) {
183
- return {
184
- success: false,
185
- error: `Could not parse version from ${registry.name} response`,
186
- registry: registry.name,
187
- };
188
- }
189
-
190
- return {
191
- success: true,
192
- package: cleanName,
193
- version,
194
- registry: registry.name,
195
- };
196
- } catch (error) {
197
- if (error.name === 'AbortError') {
198
- return {
199
- success: false,
200
- error: `Request to ${registry.name} timed out`,
201
- registry: registry.name,
202
- };
203
- }
204
- return {
205
- success: false,
206
- error: `Failed to fetch from ${registry.name}: ${error.message}`,
207
- registry: registry.name,
208
- };
209
- } finally {
210
- clearTimeout(timeout);
211
- }
212
- }
213
-
214
-
215
- function getSupportedRegistries() {
216
- return Object.entries(REGISTRIES).map(([key, reg]) => ({
217
- key,
218
- name: reg.name,
219
- prefix: reg.pattern.source.match(/\?:([^)]+)\)/)?.[1] || key + ':',
220
- }));
221
- }
222
-
223
-
224
- export function getToolDefinition() {
225
- return {
226
- name: 'e_check_package_version',
227
- description:
228
- 'Fetches the latest version of a package from its official registry. Supports npm, PyPI, crates.io, Maven, Go, RubyGems, NuGet, Packagist, Hex, pub.dev, Homebrew, and Conda. Use prefix like "pip:requests" for non-npm packages.',
229
- inputSchema: {
230
- type: 'object',
231
- properties: {
232
- package: {
233
- type: 'string',
234
- description:
235
- 'Package name, optionally prefixed with registry (e.g., "lodash", "pip:requests", "cargo:serde", "go:github.com/gin-gonic/gin")',
236
- },
237
- },
238
- required: ['package'],
239
- },
240
- annotations: {
241
- title: 'Check Package Version',
242
- readOnlyHint: true,
243
- destructiveHint: false,
244
- idempotentHint: true,
245
- openWorldHint: true,
246
- },
247
- };
248
- }
249
-
250
-
251
- export async function handleToolCall(request) {
252
- const args = request.params?.arguments || {};
253
- const packageName = args.package;
254
-
255
- if (!packageName || typeof packageName !== 'string' || packageName.trim() === '') {
256
- return {
257
- content: [
258
- {
259
- type: 'text',
260
- text: 'Error: Please provide a package name.',
261
- },
262
- ],
263
- isError: true,
264
- };
265
- }
266
-
267
- const result = await fetchPackageVersion(packageName.trim());
268
-
269
- if (result.success) {
270
- return {
271
- content: [
272
- {
273
- type: 'text',
274
- text: `**${result.package}** (${result.registry})\n\nLatest version: \`${result.version}\``,
275
- },
276
- ],
277
- };
278
- } else {
279
- return {
280
- content: [
281
- {
282
- type: 'text',
283
- text: `Error: ${result.error}`,
284
- },
285
- ],
286
- };
287
- }
288
- }
289
-
290
-
291
- export { fetchPackageVersion, detectRegistry, getSupportedRegistries, REGISTRIES };
1
+ const REGISTRIES = {
2
+ npm: {
3
+ name: 'npm',
4
+ pattern: /^(?:npm:)?(.+)$/,
5
+ url: (pkg) => `https://registry.npmjs.org/${encodeURIComponent(pkg)}/latest`,
6
+ parse: (data) => data.version,
7
+ detect: (pkg) =>
8
+ pkg.startsWith('@') || /^[a-z0-9][-a-z0-9._]*$/i.test(pkg.replace(/^npm:/, '')),
9
+ },
10
+ pypi: {
11
+ name: 'PyPI',
12
+ pattern: /^(?:pip:|pypi:)(.+)$/,
13
+ url: (pkg) => `https://pypi.org/pypi/${encodeURIComponent(pkg)}/json`,
14
+ parse: (data) => data.info.version,
15
+ detect: () => false,
16
+ },
17
+ crates: {
18
+ name: 'crates.io',
19
+ pattern: /^(?:cargo:|crates:|rust:)(.+)$/,
20
+ url: (pkg) => `https://crates.io/api/v1/crates/${encodeURIComponent(pkg)}`,
21
+ parse: (data) => data.crate.max_version,
22
+ headers: { 'User-Agent': 'heuristic-mcp/1.0' },
23
+ detect: () => false,
24
+ },
25
+ go: {
26
+ name: 'Go',
27
+ pattern: /^(?:go:)(.+)$/,
28
+ url: (pkg) => `https://proxy.golang.org/${encodeURIComponent(pkg)}/@latest`,
29
+ parse: (data) => data.Version,
30
+ detect: (pkg) => pkg.includes('/') && pkg.includes('.'),
31
+ },
32
+ rubygems: {
33
+ name: 'RubyGems',
34
+ pattern: /^(?:gem:|ruby:)(.+)$/,
35
+ url: (pkg) => `https://rubygems.org/api/v1/gems/${encodeURIComponent(pkg)}.json`,
36
+ parse: (data) => data.version,
37
+ detect: () => false,
38
+ },
39
+ nuget: {
40
+ name: 'NuGet',
41
+ pattern: /^(?:nuget:|dotnet:)(.+)$/,
42
+ url: (pkg) =>
43
+ `https://api.nuget.org/v3-flatcontainer/${encodeURIComponent(pkg.toLowerCase())}/index.json`,
44
+ parse: (data) => data.versions[data.versions.length - 1],
45
+ detect: () => false,
46
+ },
47
+ packagist: {
48
+ name: 'Packagist',
49
+ pattern: /^(?:composer:|php:)(.+)$/,
50
+ url: (pkg) => `https://repo.packagist.org/p2/${encodeURIComponent(pkg)}.json`,
51
+ parse: (data) => {
52
+ const pkgName = Object.keys(data.packages)[0];
53
+ const versions = data.packages[pkgName];
54
+
55
+ const stable = versions.find((v) => !v.version.includes('dev'));
56
+ return stable ? stable.version : versions[0].version;
57
+ },
58
+ detect: (pkg) => pkg.includes('/'),
59
+ },
60
+ hex: {
61
+ name: 'Hex',
62
+ pattern: /^(?:hex:|elixir:|mix:)(.+)$/,
63
+ url: (pkg) => `https://hex.pm/api/packages/${encodeURIComponent(pkg)}`,
64
+ parse: (data) => {
65
+ const releases = data.releases;
66
+ return releases.length > 0 ? releases[0].version : null;
67
+ },
68
+ detect: () => false,
69
+ },
70
+ pub: {
71
+ name: 'pub.dev',
72
+ pattern: /^(?:pub:|dart:|flutter:)(.+)$/,
73
+ url: (pkg) => `https://pub.dev/api/packages/${encodeURIComponent(pkg)}`,
74
+ parse: (data) => data.latest.version,
75
+ detect: () => false,
76
+ },
77
+ maven: {
78
+ name: 'Maven Central',
79
+ pattern: /^(?:maven:|java:)(.+)$/,
80
+ url: (pkg) => {
81
+ const [group, artifact] = pkg.includes(':') ? pkg.split(':') : pkg.split('/');
82
+ if (!artifact) return null;
83
+ return `https://search.maven.org/solrsearch/select?q=g:${encodeURIComponent(group)}+AND+a:${encodeURIComponent(artifact)}&rows=1&wt=json`;
84
+ },
85
+ parse: (data) => {
86
+ if (data.response.docs.length === 0) return null;
87
+ return data.response.docs[0].latestVersion;
88
+ },
89
+ detect: (pkg) => pkg.includes(':') || (pkg.includes('.') && pkg.includes('/')),
90
+ },
91
+ homebrew: {
92
+ name: 'Homebrew',
93
+ pattern: /^(?:brew:|homebrew:)(.+)$/,
94
+ url: (pkg) => `https://formulae.brew.sh/api/formula/${encodeURIComponent(pkg)}.json`,
95
+ parse: (data) => data.versions.stable,
96
+ detect: () => false,
97
+ },
98
+ conda: {
99
+ name: 'Conda',
100
+ pattern: /^(?:conda:)(.+)$/,
101
+ url: (pkg) => `https://api.anaconda.org/package/conda-forge/${encodeURIComponent(pkg)}`,
102
+ parse: (data) => data.latest_version,
103
+ detect: () => false,
104
+ },
105
+ };
106
+
107
+ function detectRegistry(packageName) {
108
+ for (const [key, registry] of Object.entries(REGISTRIES)) {
109
+ if (registry.pattern.test(packageName) && key !== 'npm') {
110
+ const match = packageName.match(registry.pattern);
111
+ if (match) {
112
+ return { registry, cleanName: match[1] };
113
+ }
114
+ }
115
+ }
116
+
117
+ for (const registry of Object.values(REGISTRIES)) {
118
+ if (registry.detect(packageName)) {
119
+ const match = packageName.match(registry.pattern);
120
+ return { registry, cleanName: match ? match[1] : packageName };
121
+ }
122
+ }
123
+
124
+ const npmMatch = packageName.match(REGISTRIES.npm.pattern);
125
+ return { registry: REGISTRIES.npm, cleanName: npmMatch ? npmMatch[1] : packageName };
126
+ }
127
+
128
+ async function fetchPackageVersion(packageName, timeoutMs = 10000) {
129
+ const { registry, cleanName } = detectRegistry(packageName);
130
+
131
+ const url = registry.url(cleanName);
132
+ if (!url) {
133
+ return {
134
+ success: false,
135
+ error: `Invalid package format for ${registry.name}: ${cleanName}`,
136
+ registry: registry.name,
137
+ };
138
+ }
139
+
140
+ const controller = new AbortController();
141
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
142
+
143
+ try {
144
+ const headers = {
145
+ Accept: 'application/json',
146
+ ...(registry.headers || {}),
147
+ };
148
+
149
+ const response = await fetch(url, {
150
+ headers,
151
+ signal: controller.signal,
152
+ });
153
+
154
+ if (!response.ok) {
155
+ if (response.status === 404) {
156
+ return {
157
+ success: false,
158
+ error: `Package "${cleanName}" not found on ${registry.name}`,
159
+ registry: registry.name,
160
+ };
161
+ }
162
+ return {
163
+ success: false,
164
+ error: `${registry.name} returned status ${response.status}`,
165
+ registry: registry.name,
166
+ };
167
+ }
168
+
169
+ const data = await response.json();
170
+ const version = registry.parse(data);
171
+
172
+ if (!version) {
173
+ return {
174
+ success: false,
175
+ error: `Could not parse version from ${registry.name} response`,
176
+ registry: registry.name,
177
+ };
178
+ }
179
+
180
+ return {
181
+ success: true,
182
+ package: cleanName,
183
+ version,
184
+ registry: registry.name,
185
+ };
186
+ } catch (error) {
187
+ if (error.name === 'AbortError') {
188
+ return {
189
+ success: false,
190
+ error: `Request to ${registry.name} timed out`,
191
+ registry: registry.name,
192
+ };
193
+ }
194
+ return {
195
+ success: false,
196
+ error: `Failed to fetch from ${registry.name}: ${error.message}`,
197
+ registry: registry.name,
198
+ };
199
+ } finally {
200
+ clearTimeout(timeout);
201
+ }
202
+ }
203
+
204
+ function getSupportedRegistries() {
205
+ return Object.entries(REGISTRIES).map(([key, reg]) => ({
206
+ key,
207
+ name: reg.name,
208
+ prefix: reg.pattern.source.match(/\?:([^)]+)\)/)?.[1] || key + ':',
209
+ }));
210
+ }
211
+
212
+ export function getToolDefinition() {
213
+ return {
214
+ name: 'e_check_package_version',
215
+ description:
216
+ 'Fetches the latest version of a package from its official registry. Supports npm, PyPI, crates.io, Maven, Go, RubyGems, NuGet, Packagist, Hex, pub.dev, Homebrew, and Conda. Use prefix like "pip:requests" for non-npm packages.',
217
+ inputSchema: {
218
+ type: 'object',
219
+ properties: {
220
+ package: {
221
+ type: 'string',
222
+ description:
223
+ 'Package name, optionally prefixed with registry (e.g., "lodash", "pip:requests", "cargo:serde", "go:github.com/gin-gonic/gin")',
224
+ },
225
+ },
226
+ required: ['package'],
227
+ },
228
+ annotations: {
229
+ title: 'Check Package Version',
230
+ readOnlyHint: true,
231
+ destructiveHint: false,
232
+ idempotentHint: true,
233
+ openWorldHint: true,
234
+ },
235
+ };
236
+ }
237
+
238
+ export async function handleToolCall(request) {
239
+ const args = request.params?.arguments || {};
240
+ const packageName = args.package;
241
+
242
+ if (!packageName || typeof packageName !== 'string' || packageName.trim() === '') {
243
+ return {
244
+ content: [
245
+ {
246
+ type: 'text',
247
+ text: 'Error: Please provide a package name.',
248
+ },
249
+ ],
250
+ isError: true,
251
+ };
252
+ }
253
+
254
+ const result = await fetchPackageVersion(packageName.trim());
255
+
256
+ if (result.success) {
257
+ return {
258
+ content: [
259
+ {
260
+ type: 'text',
261
+ text: `**${result.package}** (${result.registry})\n\nLatest version: \`${result.version}\``,
262
+ },
263
+ ],
264
+ };
265
+ } else {
266
+ return {
267
+ content: [
268
+ {
269
+ type: 'text',
270
+ text: `Error: ${result.error}`,
271
+ },
272
+ ],
273
+ };
274
+ }
275
+ }
276
+
277
+ export { fetchPackageVersion, detectRegistry, getSupportedRegistries, REGISTRIES };