@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.
- package/README.md +104 -104
- package/config.jsonc +173 -173
- package/features/ann-config.js +131 -0
- package/features/clear-cache.js +84 -0
- package/features/find-similar-code.js +291 -0
- package/features/hybrid-search.js +544 -0
- package/features/index-codebase.js +3268 -0
- package/features/lifecycle.js +1189 -0
- package/features/package-version.js +302 -0
- package/features/register.js +408 -0
- package/features/resources.js +156 -0
- package/features/set-workspace.js +265 -0
- package/index.js +96 -96
- package/lib/cache-ops.js +22 -22
- package/lib/cache-utils.js +565 -565
- package/lib/cache.js +1870 -1870
- package/lib/call-graph.js +396 -396
- package/lib/cli.js +1 -1
- package/lib/config.js +517 -517
- package/lib/constants.js +39 -39
- package/lib/embed-query-process.js +7 -7
- package/lib/embedding-process.js +7 -7
- package/lib/embedding-worker.js +299 -299
- package/lib/ignore-patterns.js +316 -316
- package/lib/json-worker.js +14 -14
- package/lib/json-writer.js +337 -337
- package/lib/logging.js +164 -164
- package/lib/memory-logger.js +13 -13
- package/lib/onnx-backend.js +193 -193
- package/lib/project-detector.js +84 -84
- package/lib/server-lifecycle.js +165 -165
- package/lib/settings-editor.js +754 -754
- package/lib/tokenizer.js +256 -256
- package/lib/utils.js +428 -428
- package/lib/vector-store-binary.js +627 -627
- package/lib/vector-store-sqlite.js +95 -95
- package/lib/workspace-env.js +28 -28
- package/mcp_config.json +9 -9
- package/package.json +86 -75
- package/scripts/clear-cache.js +20 -0
- package/scripts/download-model.js +43 -0
- package/scripts/mcp-launcher.js +49 -0
- package/scripts/postinstall.js +12 -0
- package/search-configs.js +36 -36
- package/.prettierrc +0 -7
- package/debug-pids.js +0 -30
- package/eslint.config.js +0 -36
- package/specs/plan.md +0 -23
- 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
|
+
}
|
package/lib/memory-logger.js
CHANGED
|
@@ -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
|
+
}
|