@softerist/heuristic-mcp 3.0.15 → 3.0.16

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 (49) hide show
  1. package/README.md +104 -104
  2. package/config.jsonc +173 -173
  3. package/features/ann-config.js +131 -0
  4. package/features/clear-cache.js +84 -0
  5. package/features/find-similar-code.js +291 -0
  6. package/features/hybrid-search.js +544 -0
  7. package/features/index-codebase.js +3268 -0
  8. package/features/lifecycle.js +1189 -0
  9. package/features/package-version.js +302 -0
  10. package/features/register.js +408 -0
  11. package/features/resources.js +156 -0
  12. package/features/set-workspace.js +265 -0
  13. package/index.js +96 -96
  14. package/lib/cache-ops.js +22 -22
  15. package/lib/cache-utils.js +565 -565
  16. package/lib/cache.js +1870 -1870
  17. package/lib/call-graph.js +396 -396
  18. package/lib/cli.js +1 -1
  19. package/lib/config.js +517 -517
  20. package/lib/constants.js +39 -39
  21. package/lib/embed-query-process.js +7 -7
  22. package/lib/embedding-process.js +7 -7
  23. package/lib/embedding-worker.js +299 -299
  24. package/lib/ignore-patterns.js +316 -316
  25. package/lib/json-worker.js +14 -14
  26. package/lib/json-writer.js +337 -337
  27. package/lib/logging.js +164 -164
  28. package/lib/memory-logger.js +13 -13
  29. package/lib/onnx-backend.js +193 -193
  30. package/lib/project-detector.js +84 -84
  31. package/lib/server-lifecycle.js +165 -165
  32. package/lib/settings-editor.js +754 -754
  33. package/lib/tokenizer.js +256 -256
  34. package/lib/utils.js +428 -428
  35. package/lib/vector-store-binary.js +627 -627
  36. package/lib/vector-store-sqlite.js +95 -95
  37. package/lib/workspace-env.js +28 -28
  38. package/mcp_config.json +9 -9
  39. package/package.json +86 -75
  40. package/scripts/clear-cache.js +20 -0
  41. package/scripts/download-model.js +43 -0
  42. package/scripts/mcp-launcher.js +49 -0
  43. package/scripts/postinstall.js +12 -0
  44. package/search-configs.js +36 -36
  45. package/.prettierrc +0 -7
  46. package/debug-pids.js +0 -30
  47. package/eslint.config.js +0 -36
  48. package/specs/plan.md +0 -23
  49. package/vitest.config.js +0 -39
package/lib/logging.js CHANGED
@@ -1,164 +1,164 @@
1
- import fs from 'fs/promises';
2
- import { createWriteStream } from 'fs';
3
- import path from 'path';
4
- import util from 'util';
5
-
6
- let logStream = null;
7
- const originalConsole = {
8
- log: console.info,
9
- warn: console.warn,
10
- error: console.error,
11
- info: console.info,
12
- };
13
-
14
- export function enableStderrOnlyLogging() {
15
- // Keep MCP stdout clean by routing all console output to stderr.
16
- const redirect = (...args) => originalConsole.error(...args);
17
- // eslint-disable-next-line no-console
18
- console.log = redirect;
19
- console.info = redirect;
20
- console.warn = redirect;
21
- console.error = redirect;
22
- // eslint-disable-next-line no-console
23
- console.log = redirect;
24
- console.info = redirect;
25
- }
26
-
27
- export async function setupFileLogging(config) {
28
- if (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') {
29
- return null;
30
- }
31
-
32
- try {
33
- const logPath = await ensureLogDirectory(config);
34
- logStream = createWriteStream(logPath, { flags: 'a' });
35
-
36
- const writeLine = (level, args) => {
37
- if (!logStream) return;
38
- const message = util.format(...args);
39
- // Skip empty lines (spacers) in log files
40
- if (!message.trim()) return;
41
-
42
- const timestamp = new Date().toISOString();
43
- const lines = message
44
- .split(/\r?\n/)
45
- .map((line) => line.trimEnd())
46
- .filter((line) => line.length > 0);
47
- if (lines.length === 0) return;
48
- const payload = lines.map((line) => `${timestamp} [${level}] ${line}`).join('\n') + '\n';
49
- logStream.write(payload);
50
- };
51
-
52
- const wrap = (method, level) => {
53
- const originalError = originalConsole.error;
54
- // eslint-disable-next-line no-console
55
- console[method] = (...args) => {
56
- // Always send to original stderr to avoid MCP protocol pollution on stdout
57
- originalError(...args);
58
- writeLine(level, args);
59
- };
60
- };
61
-
62
- wrap('log', 'INFO');
63
- wrap('warn', 'WARN');
64
- wrap('error', 'ERROR');
65
- wrap('info', 'INFO');
66
-
67
- logStream.on('error', (err) => {
68
- originalConsole.error(`[Logs] Failed to write log file: ${err.message}`);
69
- });
70
-
71
- process.on('exit', () => {
72
- if (logStream) logStream.end();
73
- });
74
-
75
- return logPath;
76
- } catch (err) {
77
- originalConsole.error(`[Logs] Failed to initialize log file: ${err.message}`);
78
- return null;
79
- }
80
- }
81
-
82
- export function getLogFilePath(config) {
83
- return path.join(config.cacheDirectory, 'logs', 'server.log');
84
- }
85
-
86
- export async function ensureLogDirectory(config) {
87
- const logPath = getLogFilePath(config);
88
- await fs.mkdir(path.dirname(logPath), { recursive: true });
89
- return logPath;
90
- }
91
-
92
- // ============================================================================
93
- // Error Handling Utilities
94
- // ============================================================================
95
-
96
- /*
97
- * Error handling patterns used in this codebase:
98
- *
99
- * SILENT_EXPECTED: Empty catch for expected failures (file not found, cleanup)
100
- * Use for: fs.stat on optional files, cleanup on exit, optional features
101
- *
102
- * LOG_AND_CONTINUE: Warn but continue execution
103
- * Use for: Non-critical features, fallback scenarios
104
- *
105
- * LOG_AND_RETHROW: Log context then propagate to caller
106
- * Use for: Fatal errors that caller must handle
107
- *
108
- * VERBOSE_ONLY: Only log when verbose mode is enabled
109
- * Use for: Performance diagnostics, debug information
110
- */
111
- export const ERROR_PATTERNS = {
112
- SILENT_EXPECTED: 'silent_expected',
113
- LOG_AND_CONTINUE: 'log_and_continue',
114
- LOG_AND_RETHROW: 'log_and_rethrow',
115
- VERBOSE_ONLY: 'verbose_only',
116
- };
117
-
118
- /**
119
- * Log message only when verbose mode is enabled.
120
- * @param {object|boolean} configOrVerbose - Config object with verbose property, or boolean
121
- * @param {...any} args - Arguments to log
122
- */
123
- export function logVerbose(configOrVerbose, ...args) {
124
- const isVerbose = typeof configOrVerbose === 'boolean'
125
- ? configOrVerbose
126
- : configOrVerbose?.verbose;
127
- if (isVerbose) {
128
- console.info(...args);
129
- }
130
- }
131
-
132
- /**
133
- * Log a recoverable error with consistent formatting.
134
- * Use when the error is non-fatal and execution can continue.
135
- * @param {string} context - Where the error occurred
136
- * @param {Error} error - The caught error
137
- * @param {object} options - Optional configuration
138
- * @param {boolean} options.verbose - If true, log full stack trace
139
- * @param {string} options.fallbackAction - Description of fallback behavior
140
- */
141
- export function logRecoverableError(context, error, options = {}) {
142
- const message = error?.message || String(error);
143
- const prefix = `[${context}]`;
144
-
145
- if (options.fallbackAction) {
146
- console.warn(`${prefix} ${message}. Fallback: ${options.fallbackAction}`);
147
- } else {
148
- console.warn(`${prefix} ${message}`);
149
- }
150
-
151
- if (options.verbose && error?.stack) {
152
- console.warn(`${prefix} Stack trace:`, error.stack);
153
- }
154
- }
155
-
156
- /**
157
- * Check if an error is expected and should be silently ignored.
158
- * @param {Error} error - The caught error
159
- * @param {string[]} expectedCodes - Expected error codes
160
- * @returns {boolean} True if error matches an expected code
161
- */
162
- export function isExpectedError(error, expectedCodes = ['ENOENT', 'ENOTDIR']) {
163
- return error && typeof error.code === 'string' && expectedCodes.includes(error.code);
164
- }
1
+ import fs from 'fs/promises';
2
+ import { createWriteStream } from 'fs';
3
+ import path from 'path';
4
+ import util from 'util';
5
+
6
+ let logStream = null;
7
+ const originalConsole = {
8
+ log: console.info,
9
+ warn: console.warn,
10
+ error: console.error,
11
+ info: console.info,
12
+ };
13
+
14
+ export function enableStderrOnlyLogging() {
15
+ // Keep MCP stdout clean by routing all console output to stderr.
16
+ const redirect = (...args) => originalConsole.error(...args);
17
+ // eslint-disable-next-line no-console
18
+ console.log = redirect;
19
+ console.info = redirect;
20
+ console.warn = redirect;
21
+ console.error = redirect;
22
+ // eslint-disable-next-line no-console
23
+ console.log = redirect;
24
+ console.info = redirect;
25
+ }
26
+
27
+ export async function setupFileLogging(config) {
28
+ if (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test') {
29
+ return null;
30
+ }
31
+
32
+ try {
33
+ const logPath = await ensureLogDirectory(config);
34
+ logStream = createWriteStream(logPath, { flags: 'a' });
35
+
36
+ const writeLine = (level, args) => {
37
+ if (!logStream) return;
38
+ const message = util.format(...args);
39
+ // Skip empty lines (spacers) in log files
40
+ if (!message.trim()) return;
41
+
42
+ const timestamp = new Date().toISOString();
43
+ const lines = message
44
+ .split(/\r?\n/)
45
+ .map((line) => line.trimEnd())
46
+ .filter((line) => line.length > 0);
47
+ if (lines.length === 0) return;
48
+ const payload = lines.map((line) => `${timestamp} [${level}] ${line}`).join('\n') + '\n';
49
+ logStream.write(payload);
50
+ };
51
+
52
+ const wrap = (method, level) => {
53
+ const originalError = originalConsole.error;
54
+ // eslint-disable-next-line no-console
55
+ console[method] = (...args) => {
56
+ // Always send to original stderr to avoid MCP protocol pollution on stdout
57
+ originalError(...args);
58
+ writeLine(level, args);
59
+ };
60
+ };
61
+
62
+ wrap('log', 'INFO');
63
+ wrap('warn', 'WARN');
64
+ wrap('error', 'ERROR');
65
+ wrap('info', 'INFO');
66
+
67
+ logStream.on('error', (err) => {
68
+ originalConsole.error(`[Logs] Failed to write log file: ${err.message}`);
69
+ });
70
+
71
+ process.on('exit', () => {
72
+ if (logStream) logStream.end();
73
+ });
74
+
75
+ return logPath;
76
+ } catch (err) {
77
+ originalConsole.error(`[Logs] Failed to initialize log file: ${err.message}`);
78
+ return null;
79
+ }
80
+ }
81
+
82
+ export function getLogFilePath(config) {
83
+ return path.join(config.cacheDirectory, 'logs', 'server.log');
84
+ }
85
+
86
+ export async function ensureLogDirectory(config) {
87
+ const logPath = getLogFilePath(config);
88
+ await fs.mkdir(path.dirname(logPath), { recursive: true });
89
+ return logPath;
90
+ }
91
+
92
+ // ============================================================================
93
+ // Error Handling Utilities
94
+ // ============================================================================
95
+
96
+ /*
97
+ * Error handling patterns used in this codebase:
98
+ *
99
+ * SILENT_EXPECTED: Empty catch for expected failures (file not found, cleanup)
100
+ * Use for: fs.stat on optional files, cleanup on exit, optional features
101
+ *
102
+ * LOG_AND_CONTINUE: Warn but continue execution
103
+ * Use for: Non-critical features, fallback scenarios
104
+ *
105
+ * LOG_AND_RETHROW: Log context then propagate to caller
106
+ * Use for: Fatal errors that caller must handle
107
+ *
108
+ * VERBOSE_ONLY: Only log when verbose mode is enabled
109
+ * Use for: Performance diagnostics, debug information
110
+ */
111
+ export const ERROR_PATTERNS = {
112
+ SILENT_EXPECTED: 'silent_expected',
113
+ LOG_AND_CONTINUE: 'log_and_continue',
114
+ LOG_AND_RETHROW: 'log_and_rethrow',
115
+ VERBOSE_ONLY: 'verbose_only',
116
+ };
117
+
118
+ /**
119
+ * Log message only when verbose mode is enabled.
120
+ * @param {object|boolean} configOrVerbose - Config object with verbose property, or boolean
121
+ * @param {...any} args - Arguments to log
122
+ */
123
+ export function logVerbose(configOrVerbose, ...args) {
124
+ const isVerbose = typeof configOrVerbose === 'boolean'
125
+ ? configOrVerbose
126
+ : configOrVerbose?.verbose;
127
+ if (isVerbose) {
128
+ console.info(...args);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Log a recoverable error with consistent formatting.
134
+ * Use when the error is non-fatal and execution can continue.
135
+ * @param {string} context - Where the error occurred
136
+ * @param {Error} error - The caught error
137
+ * @param {object} options - Optional configuration
138
+ * @param {boolean} options.verbose - If true, log full stack trace
139
+ * @param {string} options.fallbackAction - Description of fallback behavior
140
+ */
141
+ export function logRecoverableError(context, error, options = {}) {
142
+ const message = error?.message || String(error);
143
+ const prefix = `[${context}]`;
144
+
145
+ if (options.fallbackAction) {
146
+ console.warn(`${prefix} ${message}. Fallback: ${options.fallbackAction}`);
147
+ } else {
148
+ console.warn(`${prefix} ${message}`);
149
+ }
150
+
151
+ if (options.verbose && error?.stack) {
152
+ console.warn(`${prefix} Stack trace:`, error.stack);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Check if an error is expected and should be silently ignored.
158
+ * @param {Error} error - The caught error
159
+ * @param {string[]} expectedCodes - Expected error codes
160
+ * @returns {boolean} True if error matches an expected code
161
+ */
162
+ export function isExpectedError(error, expectedCodes = ['ENOENT', 'ENOTDIR']) {
163
+ return error && typeof error.code === 'string' && expectedCodes.includes(error.code);
164
+ }
@@ -1,13 +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
+ 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
+ }