@softerist/heuristic-mcp 2.1.47 → 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 -75
  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
@@ -0,0 +1,13 @@
1
+ function formatMb(bytes) {
2
+ return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
3
+ }
4
+
5
+ export function logMemory(prefix) {
6
+ const { rss, heapUsed, heapTotal } = process.memoryUsage();
7
+ console.info(`${prefix} rss=${formatMb(rss)} heap=${formatMb(heapUsed)}/${formatMb(heapTotal)}`);
8
+ }
9
+
10
+ export function startMemoryLogger(prefix, intervalMs) {
11
+ const timer = setInterval(() => logMemory(prefix), intervalMs);
12
+ return () => clearInterval(timer);
13
+ }
@@ -1,6 +1,6 @@
1
- import fs from "fs/promises";
2
- import path from "path";
3
- import { FILE_TYPE_MAP, IGNORE_PATTERNS } from "./ignore-patterns.js";
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { FILE_TYPE_MAP, IGNORE_PATTERNS, SKIP_DIRECTORIES } from './ignore-patterns.js';
4
4
 
5
5
  export class ProjectDetector {
6
6
  constructor(searchDirectory) {
@@ -8,22 +8,24 @@ export class ProjectDetector {
8
8
  this.detectedTypes = new Set();
9
9
  }
10
10
 
11
- async detectProjectTypes() {
11
+ async detectProjectTypes(options = {}) {
12
+ const maxDepth = typeof options.maxDepth === 'number' ? options.maxDepth : 2;
13
+ const startDepth = typeof options.startDepth === 'number' ? options.startDepth : 0;
12
14
  const markerFiles = Object.keys(FILE_TYPE_MAP);
13
15
  const discoveredTypes = new Map(); // type -> first marker found
14
16
 
15
17
  const checkDir = async (dir, depth) => {
16
- if (depth > 2) return;
18
+ if (depth > maxDepth) return;
17
19
 
18
20
  const items = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
19
- const itemNames = items.map(i => i.name);
21
+ const itemNames = items.map((i) => i.name);
20
22
  const itemSet = new Set(itemNames);
21
23
 
22
24
  for (const marker of markerFiles) {
23
25
  let found = false;
24
26
  if (marker.includes('*')) {
25
27
  const regex = new RegExp('^' + marker.replace('*', '.*') + '$');
26
- found = itemNames.some(file => regex.test(file));
28
+ found = itemNames.some((file) => regex.test(file));
27
29
  } else {
28
30
  found = itemSet.has(marker);
29
31
  }
@@ -37,17 +39,11 @@ export class ProjectDetector {
37
39
  }
38
40
 
39
41
  // Recurse into subdirectories
40
- if (depth < 2) {
42
+ if (depth < maxDepth) {
41
43
  for (const item of items) {
42
44
  if (item.isDirectory()) {
43
45
  const name = item.name;
44
- if (name.startsWith('.') || [
45
- 'node_modules', 'dist', 'build', 'target', 'vendor', // Build outputs
46
- 'coverage', 'htmlcov', // Test coverage
47
- 'typings', 'nltk_data', 'secrets', // Data/secrets
48
- 'venv', 'env', // Python envs (non-dot)
49
- '__pycache__', 'eggs', '.eggs' // Python artifacts
50
- ].includes(name)) {
46
+ if (name.startsWith('.') || SKIP_DIRECTORIES.includes(name)) {
51
47
  continue;
52
48
  }
53
49
  await checkDir(path.join(dir, name), depth + 1);
@@ -56,7 +52,7 @@ export class ProjectDetector {
56
52
  }
57
53
  };
58
54
 
59
- await checkDir(this.searchDirectory, 0);
55
+ await checkDir(this.searchDirectory, startDepth);
60
56
 
61
57
  for (const [type, marker] of discoveredTypes) {
62
58
  this.detectedTypes.add(type);
@@ -82,7 +78,7 @@ export class ProjectDetector {
82
78
  getSummary() {
83
79
  return {
84
80
  detectedTypes: Array.from(this.detectedTypes),
85
- patternCount: this.getSmartIgnorePatterns().length
81
+ patternCount: this.getSmartIgnorePatterns().length,
86
82
  };
87
83
  }
88
84
  }
@@ -0,0 +1,38 @@
1
+ import fs from 'fs/promises';
2
+ import fsSync from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+
6
+ function isTestEnv() {
7
+ return process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';
8
+ }
9
+
10
+ export async function setupPidFile({ pidFileName = '.heuristic-mcp.pid' } = {}) {
11
+ if (isTestEnv()) {
12
+ return null;
13
+ }
14
+
15
+ const pidPath = path.join(os.homedir(), pidFileName);
16
+ try {
17
+ await fs.writeFile(pidPath, `${process.pid}`, 'utf-8');
18
+ } catch (err) {
19
+ console.error(`[Server] Warning: Failed to write PID file: ${err.message}`);
20
+ return null;
21
+ }
22
+
23
+ const cleanup = () => {
24
+ try {
25
+ fsSync.unlinkSync(pidPath);
26
+ } catch {
27
+ // ignore
28
+ }
29
+ };
30
+
31
+ process.on('exit', cleanup);
32
+ return pidPath;
33
+ }
34
+
35
+ export function registerSignalHandlers(handler) {
36
+ process.on('SIGINT', () => handler('SIGINT'));
37
+ process.on('SIGTERM', () => handler('SIGTERM'));
38
+ }