@softerist/heuristic-mcp 2.1.46 → 3.0.0

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 (109) hide show
  1. package/.agent/workflows/code-review.md +60 -0
  2. package/.prettierrc +7 -0
  3. package/ARCHITECTURE.md +105 -170
  4. package/CONTRIBUTING.md +32 -113
  5. package/GEMINI.md +73 -0
  6. package/LICENSE +21 -21
  7. package/README.md +161 -54
  8. package/config.json +876 -76
  9. package/debug-pids.js +27 -0
  10. package/eslint.config.js +36 -0
  11. package/features/ann-config.js +37 -26
  12. package/features/clear-cache.js +28 -19
  13. package/features/find-similar-code.js +142 -66
  14. package/features/hybrid-search.js +253 -93
  15. package/features/index-codebase.js +1455 -394
  16. package/features/lifecycle.js +813 -180
  17. package/features/register.js +58 -52
  18. package/index.js +450 -306
  19. package/lib/cache-ops.js +22 -0
  20. package/lib/cache-utils.js +68 -0
  21. package/lib/cache.js +1392 -587
  22. package/lib/call-graph.js +165 -50
  23. package/lib/cli.js +154 -0
  24. package/lib/config.js +462 -121
  25. package/lib/embedding-process.js +77 -0
  26. package/lib/embedding-worker.js +545 -30
  27. package/lib/ignore-patterns.js +61 -59
  28. package/lib/json-worker.js +14 -0
  29. package/lib/json-writer.js +344 -0
  30. package/lib/logging.js +88 -0
  31. package/lib/memory-logger.js +13 -0
  32. package/lib/project-detector.js +13 -17
  33. package/lib/server-lifecycle.js +38 -0
  34. package/lib/settings-editor.js +645 -0
  35. package/lib/tokenizer.js +207 -104
  36. package/lib/utils.js +273 -198
  37. package/lib/vector-store-binary.js +592 -0
  38. package/mcp_config.example.json +13 -0
  39. package/package.json +13 -2
  40. package/scripts/clear-cache.js +6 -17
  41. package/scripts/download-model.js +14 -9
  42. package/scripts/postinstall.js +5 -5
  43. package/search-configs.js +36 -0
  44. package/test/ann-config.test.js +179 -0
  45. package/test/ann-fallback.test.js +6 -6
  46. package/test/binary-store.test.js +69 -0
  47. package/test/cache-branches.test.js +120 -0
  48. package/test/cache-errors.test.js +264 -0
  49. package/test/cache-extra.test.js +300 -0
  50. package/test/cache-helpers.test.js +205 -0
  51. package/test/cache-hnsw-failure.test.js +40 -0
  52. package/test/cache-json-worker.test.js +190 -0
  53. package/test/cache-worker.test.js +102 -0
  54. package/test/cache.test.js +443 -0
  55. package/test/call-graph.test.js +103 -4
  56. package/test/clear-cache.test.js +69 -68
  57. package/test/code-review-workflow.test.js +50 -0
  58. package/test/config.test.js +418 -0
  59. package/test/coverage-gap.test.js +497 -0
  60. package/test/coverage-maximizer.test.js +236 -0
  61. package/test/debug-analysis.js +107 -0
  62. package/test/embedding-model.test.js +173 -103
  63. package/test/embedding-worker-extra.test.js +272 -0
  64. package/test/embedding-worker.test.js +158 -0
  65. package/test/features.test.js +139 -0
  66. package/test/final-boost.test.js +271 -0
  67. package/test/final-polish.test.js +183 -0
  68. package/test/final.test.js +95 -0
  69. package/test/find-similar-code.test.js +191 -0
  70. package/test/helpers.js +92 -11
  71. package/test/helpers.test.js +46 -0
  72. package/test/hybrid-search-basic.test.js +62 -0
  73. package/test/hybrid-search-branch.test.js +202 -0
  74. package/test/hybrid-search-callgraph.test.js +229 -0
  75. package/test/hybrid-search-extra.test.js +81 -0
  76. package/test/hybrid-search.test.js +484 -71
  77. package/test/index-cli.test.js +520 -0
  78. package/test/index-codebase-batch.test.js +119 -0
  79. package/test/index-codebase-branches.test.js +585 -0
  80. package/test/index-codebase-core.test.js +1032 -0
  81. package/test/index-codebase-edge-cases.test.js +254 -0
  82. package/test/index-codebase-errors.test.js +132 -0
  83. package/test/index-codebase-gap.test.js +239 -0
  84. package/test/index-codebase-lines.test.js +151 -0
  85. package/test/index-codebase-watcher.test.js +259 -0
  86. package/test/index-codebase-zone.test.js +259 -0
  87. package/test/index-codebase.test.js +371 -69
  88. package/test/index-memory.test.js +220 -0
  89. package/test/indexer-detailed.test.js +176 -0
  90. package/test/integration.test.js +148 -92
  91. package/test/json-worker.test.js +50 -0
  92. package/test/lifecycle.test.js +541 -0
  93. package/test/master.test.js +198 -0
  94. package/test/perfection.test.js +349 -0
  95. package/test/project-detector.test.js +65 -0
  96. package/test/register.test.js +262 -0
  97. package/test/tokenizer.test.js +55 -93
  98. package/test/ultra-maximizer.test.js +116 -0
  99. package/test/utils-branches.test.js +161 -0
  100. package/test/utils-extra.test.js +116 -0
  101. package/test/utils.test.js +131 -0
  102. package/test/verify_fixes.js +76 -0
  103. package/test/worker-errors.test.js +96 -0
  104. package/test/worker-init.test.js +102 -0
  105. package/test/worker_throttling.test.js +93 -0
  106. package/tools/scripts/benchmark-search.js +95 -0
  107. package/tools/scripts/cache-stats.js +71 -0
  108. package/tools/scripts/manual-search.js +34 -0
  109. package/vitest.config.js +19 -9
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Tests for CacheClearer feature
3
- *
3
+ *
4
4
  * Tests the cache clearing functionality including:
5
5
  * - Basic cache clearing
6
6
  * - Protection during indexing
@@ -10,28 +10,27 @@
10
10
  */
11
11
 
12
12
  import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
13
- import {
14
- createTestFixtures,
15
- cleanupFixtures,
13
+ import {
14
+ createTestFixtures,
15
+ cleanupFixtures,
16
16
  clearTestCache,
17
- createMockRequest
17
+ createMockRequest,
18
18
  } from './helpers.js';
19
19
  import * as ClearCacheFeature from '../features/clear-cache.js';
20
20
  import { CacheClearer } from '../features/clear-cache.js';
21
21
  import fs from 'fs/promises';
22
- import path from 'path';
23
22
 
24
23
  describe('CacheClearer', () => {
25
24
  let fixtures;
26
-
25
+
27
26
  beforeAll(async () => {
28
27
  fixtures = await createTestFixtures({ workerThreads: 1 });
29
28
  });
30
-
29
+
31
30
  afterAll(async () => {
32
31
  await cleanupFixtures(fixtures);
33
32
  });
34
-
33
+
35
34
  beforeEach(async () => {
36
35
  // Reset state
37
36
  fixtures.indexer.isIndexing = false;
@@ -41,42 +40,44 @@ describe('CacheClearer', () => {
41
40
 
42
41
  describe('Basic Cache Clearing', () => {
43
42
  it('should clear cache successfully', async () => {
43
+ expect(fixtures.cacheClearer).toBeInstanceOf(CacheClearer);
44
+
44
45
  // First ensure we have a cache
45
46
  await fixtures.indexer.indexAll(true);
46
-
47
+
47
48
  // Verify cache exists
48
49
  expect(fixtures.cache.getVectorStore().length).toBeGreaterThan(0);
49
-
50
+
50
51
  // Clear cache
51
52
  const result = await fixtures.cacheClearer.execute();
52
-
53
+
53
54
  expect(result.success).toBe(true);
54
55
  expect(result.message).toContain('Cache cleared successfully');
55
56
  expect(result.cacheDirectory).toBe(fixtures.config.cacheDirectory);
56
57
  });
57
-
58
+
58
59
  it('should empty vectorStore and fileHashes', async () => {
59
60
  // Create some cache
60
61
  await fixtures.indexer.indexAll(true);
61
-
62
+
62
63
  // Clear
63
64
  await fixtures.cacheClearer.execute();
64
-
65
+
65
66
  // Both should be empty
66
67
  expect(fixtures.cache.getVectorStore().length).toBe(0);
67
- expect(fixtures.cache.fileHashes.size).toBe(0);
68
+ expect(fixtures.cache.getFileHashCount()).toBe(0);
68
69
  });
69
-
70
+
70
71
  it('should delete cache directory', async () => {
71
72
  // Create cache
72
73
  await fixtures.indexer.indexAll(true);
73
-
74
+
74
75
  // Verify cache directory exists
75
76
  await expect(fs.access(fixtures.config.cacheDirectory)).resolves.not.toThrow();
76
-
77
+
77
78
  // Clear
78
79
  await fixtures.cacheClearer.execute();
79
-
80
+
80
81
  // Directory should not exist
81
82
  await expect(fs.access(fixtures.config.cacheDirectory)).rejects.toThrow();
82
83
  });
@@ -87,24 +88,24 @@ describe('CacheClearer', () => {
87
88
  // Simulate indexing in progress
88
89
  await clearTestCache(fixtures.config);
89
90
  fixtures.cache.setVectorStore([]);
90
- fixtures.cache.fileHashes = new Map();
91
-
91
+ fixtures.cache.clearFileHashes();
92
+
92
93
  const indexPromise = fixtures.indexer.indexAll(true);
93
94
  expect(fixtures.indexer.isIndexing).toBe(true);
94
-
95
+
95
96
  // Try to clear - should fail
96
97
  await expect(fixtures.cacheClearer.execute()).rejects.toThrow(
97
98
  'Cannot clear cache while indexing is in progress'
98
99
  );
99
-
100
+
100
101
  await indexPromise;
101
102
  });
102
-
103
+
103
104
  it('should allow clear after indexing completes', async () => {
104
105
  // Complete indexing
105
106
  await fixtures.indexer.indexAll(true);
106
107
  expect(fixtures.indexer.isIndexing).toBe(false);
107
-
108
+
108
109
  // Clear should work
109
110
  const result = await fixtures.cacheClearer.execute();
110
111
  expect(result.success).toBe(true);
@@ -115,23 +116,23 @@ describe('CacheClearer', () => {
115
116
  it('should prevent clear while cache is being saved', async () => {
116
117
  // Simulate save in progress
117
118
  fixtures.cache.isSaving = true;
118
-
119
+
119
120
  // Try to clear - should fail
120
121
  await expect(fixtures.cacheClearer.execute()).rejects.toThrow(
121
122
  'Cannot clear cache while cache is being saved'
122
123
  );
123
-
124
+
124
125
  // Reset
125
126
  fixtures.cache.isSaving = false;
126
127
  });
127
-
128
+
128
129
  it('should allow clear after save completes', async () => {
129
130
  // Index first
130
131
  await fixtures.indexer.indexAll(true);
131
-
132
+
132
133
  // isSaving should be false after indexing
133
134
  expect(fixtures.cache.isSaving).toBe(false);
134
-
135
+
135
136
  // Clear should work
136
137
  const result = await fixtures.cacheClearer.execute();
137
138
  expect(result.success).toBe(true);
@@ -142,58 +143,58 @@ describe('CacheClearer', () => {
142
143
  it('should prevent multiple concurrent clears', async () => {
143
144
  // Index first
144
145
  await fixtures.indexer.indexAll(true);
145
-
146
+
146
147
  // Reset the isClearing flag
147
148
  fixtures.cacheClearer.isClearing = false;
148
-
149
+
149
150
  // Start multiple concurrent clears
150
151
  const promises = [
151
152
  fixtures.cacheClearer.execute(),
152
153
  fixtures.cacheClearer.execute(),
153
- fixtures.cacheClearer.execute()
154
+ fixtures.cacheClearer.execute(),
154
155
  ];
155
-
156
+
156
157
  const results = await Promise.allSettled(promises);
157
-
158
+
158
159
  // Exactly one should succeed
159
- const successes = results.filter(r => r.status === 'fulfilled');
160
- const failures = results.filter(r => r.status === 'rejected');
161
-
160
+ const successes = results.filter((r) => r.status === 'fulfilled');
161
+ const failures = results.filter((r) => r.status === 'rejected');
162
+
162
163
  expect(successes.length).toBe(1);
163
164
  expect(failures.length).toBe(2);
164
-
165
+
165
166
  // Failures should have correct error message
166
167
  for (const failure of failures) {
167
168
  expect(failure.reason.message).toContain('already in progress');
168
169
  }
169
170
  });
170
-
171
+
171
172
  it('should reset isClearing flag after completion', async () => {
172
173
  // Index first
173
174
  await fixtures.indexer.indexAll(true);
174
-
175
+
175
176
  expect(fixtures.cacheClearer.isClearing).toBe(false);
176
-
177
+
177
178
  // Clear
178
179
  await fixtures.cacheClearer.execute();
179
-
180
+
180
181
  // Flag should be reset
181
182
  expect(fixtures.cacheClearer.isClearing).toBe(false);
182
183
  });
183
-
184
+
184
185
  it('should reset isClearing flag even on error', async () => {
185
186
  // Set up for failure
186
187
  fixtures.cache.isSaving = true;
187
-
188
+
188
189
  try {
189
190
  await fixtures.cacheClearer.execute();
190
191
  } catch {
191
192
  // Expected to fail
192
193
  }
193
-
194
+
194
195
  // isClearing should not have been set (failed before setting)
195
196
  expect(fixtures.cacheClearer.isClearing).toBe(false);
196
-
197
+
197
198
  // Reset
198
199
  fixtures.cache.isSaving = false;
199
200
  });
@@ -202,15 +203,15 @@ describe('CacheClearer', () => {
202
203
 
203
204
  describe('Clear Cache Tool Handler', () => {
204
205
  let fixtures;
205
-
206
+
206
207
  beforeAll(async () => {
207
208
  fixtures = await createTestFixtures({ workerThreads: 1 });
208
209
  });
209
-
210
+
210
211
  afterAll(async () => {
211
212
  await cleanupFixtures(fixtures);
212
213
  });
213
-
214
+
214
215
  beforeEach(async () => {
215
216
  fixtures.indexer.isIndexing = false;
216
217
  fixtures.cache.isSaving = false;
@@ -220,7 +221,7 @@ describe('Clear Cache Tool Handler', () => {
220
221
  describe('Tool Definition', () => {
221
222
  it('should have correct tool definition', () => {
222
223
  const toolDef = ClearCacheFeature.getToolDefinition();
223
-
224
+
224
225
  expect(toolDef.name).toBe('c_clear_cache');
225
226
  expect(toolDef.description).toContain('cache');
226
227
  expect(toolDef.annotations.destructiveHint).toBe(true);
@@ -232,53 +233,53 @@ describe('Clear Cache Tool Handler', () => {
232
233
  it('should return success message on cleared cache', async () => {
233
234
  // Index first
234
235
  await fixtures.indexer.indexAll(true);
235
-
236
+
236
237
  const request = createMockRequest('c_clear_cache', {});
237
238
  const result = await ClearCacheFeature.handleToolCall(request, fixtures.cacheClearer);
238
-
239
+
239
240
  expect(result.content[0].text).toContain('Cache cleared successfully');
240
241
  expect(result.content[0].text).toContain('Cache directory:');
241
242
  });
242
-
243
+
243
244
  it('should return error message when indexing is in progress', async () => {
244
245
  // Simulate indexing
245
246
  await clearTestCache(fixtures.config);
246
247
  fixtures.cache.setVectorStore([]);
247
- fixtures.cache.fileHashes = new Map();
248
-
248
+ fixtures.cache.clearFileHashes();
249
+
249
250
  const indexPromise = fixtures.indexer.indexAll(true);
250
251
  expect(fixtures.indexer.isIndexing).toBe(true);
251
-
252
+
252
253
  const request = createMockRequest('c_clear_cache', {});
253
254
  const result = await ClearCacheFeature.handleToolCall(request, fixtures.cacheClearer);
254
-
255
+
255
256
  expect(result.content[0].text).toContain('Failed to clear cache');
256
257
  expect(result.content[0].text).toContain('indexing is in progress');
257
-
258
+
258
259
  await indexPromise;
259
260
  });
260
-
261
+
261
262
  it('should return error message when save is in progress', async () => {
262
263
  fixtures.cache.isSaving = true;
263
-
264
+
264
265
  const request = createMockRequest('c_clear_cache', {});
265
266
  const result = await ClearCacheFeature.handleToolCall(request, fixtures.cacheClearer);
266
-
267
+
267
268
  expect(result.content[0].text).toContain('Failed to clear cache');
268
269
  expect(result.content[0].text).toContain('being saved');
269
-
270
+
270
271
  fixtures.cache.isSaving = false;
271
272
  });
272
-
273
+
273
274
  it('should return error message when clear is already in progress', async () => {
274
275
  fixtures.cacheClearer.isClearing = true;
275
-
276
+
276
277
  const request = createMockRequest('c_clear_cache', {});
277
278
  const result = await ClearCacheFeature.handleToolCall(request, fixtures.cacheClearer);
278
-
279
+
279
280
  expect(result.content[0].text).toContain('Failed to clear cache');
280
281
  expect(result.content[0].text).toContain('already in progress');
281
-
282
+
282
283
  fixtures.cacheClearer.isClearing = false;
283
284
  });
284
285
  });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Ensure the code-review workflow includes the required production-ready guidance.
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const testDir = path.dirname(fileURLToPath(import.meta.url));
11
+ const repoRoot = path.resolve(testDir, '..');
12
+ const workflowPath = path.join(repoRoot, '.agent', 'workflows', 'code-review.md');
13
+
14
+ async function readWorkflow() {
15
+ return fs.readFile(workflowPath, 'utf8');
16
+ }
17
+
18
+ describe('Code Review Workflow', () => {
19
+ it('includes scope control and line-by-line expectations', async () => {
20
+ const content = await readWorkflow();
21
+
22
+ expect(content).toContain('Review Scope & Context');
23
+ expect(content).toContain('For code <100 lines');
24
+ expect(content).toContain('For code >500 lines or critical systems');
25
+ expect(content).toContain('Review every function and critical code path');
26
+ expect(content).toContain('Group line-by-line findings by severity');
27
+ });
28
+
29
+ it('includes fix plan effort scale and dependency tracking', async () => {
30
+ const content = await readWorkflow();
31
+
32
+ expect(content).toContain('Estimated effort per item');
33
+ expect(content).toContain('S (Small)');
34
+ expect(content).toContain('M (Medium)');
35
+ expect(content).toContain('L (Large)');
36
+ expect(content).toContain('Requires #N');
37
+ });
38
+
39
+ it('includes patch, tests, length control, and follow-up guidance', async () => {
40
+ const content = await readWorkflow();
41
+
42
+ expect(content).toContain('unified diff format');
43
+ expect(content).toContain('ALL critical severity issues');
44
+ expect(content).toContain('unit tests');
45
+ expect(content).toContain('integration tests');
46
+ expect(content).toContain('Length Control');
47
+ expect(content).toContain('Follow-up Reviews');
48
+ expect(content).toContain('regression verification');
49
+ });
50
+ });