@softerist/heuristic-mcp 3.0.16 → 3.0.17

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 (3) hide show
  1. package/index.js +1 -1
  2. package/lib/cli.js +251 -166
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { stop, start, status, logs } from './features/lifecycle.js';
4
4
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
package/lib/cli.js CHANGED
@@ -1,166 +1,251 @@
1
- export const DEFAULT_LOG_TAIL_LINES = 200;
2
-
3
- export function printHelp(defaultTailLines = DEFAULT_LOG_TAIL_LINES) {
4
- console.info(`Heuristic MCP Server
5
-
6
- Usage:
7
- heuristic-mcp [options]
8
-
9
- Options:
10
- --cache Show cache status and cleanup recommendations (dry-run)
11
- --cache --clean Remove stale cache directories (performs cleanup)
12
- --status Show server status and cache summary
13
- --clear <cache_id> Remove a specific cache by ID
14
- --clear-cache Remove cache for current workspace (and stale global caches)
15
- --logs Tail server logs (defaults to last 200 lines, follows)
16
- --mem Show last memory snapshot from logs (requires verbose logging)
17
- --tail <lines> Lines to show with --logs (default: ${defaultTailLines})
18
- --no-follow Do not follow log output with --logs
19
- --start [ide] Register + enable in IDE config (antigravity|codex|cursor|vscode|windsurf|warp|"Claude Desktop")
20
- --stop Stop running server instances
21
- --workspace <path> Workspace path (used by IDE launch / log viewer)
22
- --version, -v Show version
23
- --help, -h Show this help
24
- `);
25
- }
26
-
27
- export function parseWorkspaceDir(args) {
28
- const workspaceIndex = args.findIndex((arg) => arg.startsWith('--workspace'));
29
- if (workspaceIndex === -1) return null;
30
-
31
- const arg = args[workspaceIndex];
32
- let rawWorkspace = null;
33
-
34
- if (arg.includes('=')) {
35
- rawWorkspace = arg.split('=')[1];
36
- } else if (workspaceIndex + 1 < args.length) {
37
- rawWorkspace = args[workspaceIndex + 1];
38
- }
39
-
40
- // Check if IDE variable wasn't expanded (contains ${})
41
- if (rawWorkspace && rawWorkspace.includes('${')) {
42
- console.error(
43
- `[Server] IDE variable not expanded: ${rawWorkspace}, falling back to auto-detected workspace`
44
- );
45
- return null;
46
- }
47
-
48
- return rawWorkspace || null;
49
- }
50
-
51
- export function collectUnknownFlags(rawArgs, knownFlags, flagsWithValue) {
52
- const unknownFlags = [];
53
- for (let i = 0; i < rawArgs.length; i += 1) {
54
- const arg = rawArgs[i];
55
- if (flagsWithValue.has(arg)) {
56
- if (arg.includes('=')) continue;
57
- const next = rawArgs[i + 1];
58
- if (next && !next.startsWith('-')) {
59
- i += 1;
60
- }
61
- continue;
62
- }
63
- if (arg.startsWith('-') && !knownFlags.has(arg) && !arg.startsWith('--workspace=')) {
64
- unknownFlags.push(arg);
65
- }
66
- }
67
- return unknownFlags;
68
- }
69
-
70
- export function parseArgs(argv = process.argv) {
71
- const args = argv.slice(2);
72
- const rawArgs = [...args];
73
-
74
- const wantsVersion = args.includes('--version') || args.includes('-v');
75
- const wantsHelp = args.includes('--help') || args.includes('-h');
76
- const wantsCache = args.includes('--cache');
77
- const wantsClean = args.includes('--clean');
78
- const wantsStatus = args.includes('--status');
79
- const wantsClearCache = args.includes('--clear-cache');
80
- const wantsLogs = args.includes('--logs');
81
- const wantsMem = args.includes('--mem');
82
- const wantsRegister = args.includes('--register');
83
- const wantsStart = args.includes('--start') || wantsRegister;
84
- const wantsStop = args.includes('--stop');
85
- const wantsFix = args.includes('--fix');
86
- const wantsNoFollow = args.includes('--no-follow');
87
-
88
- const isServerMode = !(
89
- wantsCache ||
90
- wantsStatus ||
91
- wantsClearCache ||
92
- wantsLogs ||
93
- wantsMem ||
94
- wantsStart ||
95
- wantsStop ||
96
- wantsHelp ||
97
- wantsVersion
98
- );
99
-
100
- const workspaceDir = parseWorkspaceDir(args);
101
-
102
- let tailLines = DEFAULT_LOG_TAIL_LINES;
103
- if (wantsLogs) {
104
- const tailIndex = args.indexOf('--tail');
105
- if (tailIndex !== -1 && args[tailIndex + 1]) {
106
- const parsed = parseInt(args[tailIndex + 1], 10);
107
- if (!isNaN(parsed) && parsed > 0) {
108
- tailLines = parsed;
109
- }
110
- }
111
- }
112
-
113
- let startFilter = null;
114
- if (wantsStart) {
115
- const getFilter = (flag) => {
116
- const filterIndex = args.indexOf(flag);
117
- if (filterIndex === -1) return null;
118
- const value = args[filterIndex + 1];
119
- return value && !value.startsWith('-') ? value : null;
120
- };
121
- startFilter = getFilter('--start') ?? getFilter('--register');
122
- }
123
-
124
- const knownFlags = new Set([
125
- '--cache',
126
- '--clean',
127
- '--status',
128
- '--clear',
129
- '--clear-cache',
130
- '--logs',
131
- '--mem',
132
- '--tail',
133
- '--no-follow',
134
- '--start',
135
- '--stop',
136
- '--workspace',
137
- '--version',
138
- '-v',
139
- '--help',
140
- '-h',
141
- ]);
142
- const flagsWithValue = new Set(['--tail', '--workspace', '--start', '--register']);
143
- const unknownFlags = collectUnknownFlags(rawArgs, knownFlags, flagsWithValue);
144
-
145
- return {
146
- args,
147
- rawArgs,
148
- isServerMode,
149
- workspaceDir,
150
- wantsVersion,
151
- wantsHelp,
152
- wantsCache,
153
- wantsClean,
154
- wantsStatus,
155
- wantsClearCache,
156
- wantsLogs,
157
- wantsMem,
158
- wantsStart,
159
- wantsStop,
160
- wantsFix,
161
- wantsNoFollow,
162
- tailLines,
163
- startFilter,
164
- unknownFlags,
165
- };
166
- }
1
+ export const DEFAULT_LOG_TAIL_LINES = 200;
2
+ const FLAG_ARGS_WITH_VALUES = new Set(['--tail', '--workspace', '--start', '--register', '--clear']);
3
+ const COMMAND_ALIASES = Object.freeze({
4
+ status: '--status',
5
+ stat: '--status',
6
+ log: '--logs',
7
+ logs: '--logs',
8
+ start: '--start',
9
+ stop: '--stop',
10
+ cache: '--cache',
11
+ 'clear-cache': '--clear-cache',
12
+ clearcache: '--clear-cache',
13
+ clear: '--clear',
14
+ mem: '--mem',
15
+ memory: '--mem',
16
+ version: '--version',
17
+ help: '--help',
18
+ register: '--register',
19
+ });
20
+ const FLAG_ALIASES = Object.freeze({
21
+ '--log': '--logs',
22
+ });
23
+
24
+ export function printHelp(defaultTailLines = DEFAULT_LOG_TAIL_LINES) {
25
+ console.info(`Heuristic MCP Server
26
+
27
+ Usage:
28
+ heuristic-mcp [options]
29
+ heuristic-mcp <command> [args]
30
+
31
+ Options:
32
+ --cache Show cache status and cleanup recommendations (dry-run)
33
+ --cache --clean Remove stale cache directories (performs cleanup)
34
+ --status Show server status and cache summary
35
+ --clear <cache_id> Remove a specific cache by ID
36
+ --clear-cache Remove cache for current workspace (and stale global caches)
37
+ --logs Tail server logs (defaults to last 200 lines, follows)
38
+ --mem Show last memory snapshot from logs (requires verbose logging)
39
+ --tail <lines> Lines to show with --logs (default: ${defaultTailLines})
40
+ --no-follow Do not follow log output with --logs
41
+ --start [ide] Register + enable in IDE config (antigravity|codex|cursor|vscode|windsurf|warp|"Claude Desktop")
42
+ --stop Stop running server instances
43
+ --workspace <path> Workspace path (used by IDE launch / log viewer)
44
+ --version, -v Show version
45
+ --help, -h Show this help
46
+
47
+ `);
48
+ }
49
+
50
+ export function normalizeCliArgs(rawArgs = []) {
51
+ const normalized = [];
52
+ let expectsValue = false;
53
+
54
+ for (const token of rawArgs) {
55
+ if (expectsValue) {
56
+ normalized.push(token);
57
+ expectsValue = false;
58
+ continue;
59
+ }
60
+
61
+ if (token.startsWith('--')) {
62
+ const eqIdx = token.indexOf('=');
63
+ if (eqIdx !== -1) {
64
+ const flagPart = token.slice(0, eqIdx);
65
+ const valuePart = token.slice(eqIdx + 1);
66
+ const mappedFlag = FLAG_ALIASES[flagPart] || flagPart;
67
+ normalized.push(`${mappedFlag}=${valuePart}`);
68
+ continue;
69
+ }
70
+
71
+ const mappedFlag = FLAG_ALIASES[token] || token;
72
+ normalized.push(mappedFlag);
73
+ if (FLAG_ARGS_WITH_VALUES.has(mappedFlag)) {
74
+ expectsValue = true;
75
+ }
76
+ continue;
77
+ }
78
+
79
+ if (token.startsWith('-')) {
80
+ normalized.push(token);
81
+ continue;
82
+ }
83
+
84
+ const mappedCommand = COMMAND_ALIASES[token.toLowerCase()];
85
+ if (mappedCommand) {
86
+ normalized.push(mappedCommand);
87
+ if (FLAG_ARGS_WITH_VALUES.has(mappedCommand)) {
88
+ expectsValue = true;
89
+ }
90
+ continue;
91
+ }
92
+
93
+ normalized.push(token);
94
+ }
95
+
96
+ return normalized;
97
+ }
98
+
99
+ export function shouldDefaultToHelp(
100
+ args,
101
+ runtime = { stdinIsTTY: process.stdin.isTTY, stdoutIsTTY: process.stdout.isTTY }
102
+ ) {
103
+ return args.length === 0 && Boolean(runtime.stdinIsTTY && runtime.stdoutIsTTY);
104
+ }
105
+
106
+ export function parseWorkspaceDir(args) {
107
+ const workspaceIndex = args.findIndex((arg) => arg.startsWith('--workspace'));
108
+ if (workspaceIndex === -1) return null;
109
+
110
+ const arg = args[workspaceIndex];
111
+ let rawWorkspace = null;
112
+
113
+ if (arg.includes('=')) {
114
+ rawWorkspace = arg.split('=')[1];
115
+ } else if (workspaceIndex + 1 < args.length) {
116
+ rawWorkspace = args[workspaceIndex + 1];
117
+ }
118
+
119
+ // Check if IDE variable wasn't expanded (contains ${})
120
+ if (rawWorkspace && rawWorkspace.includes('${')) {
121
+ console.error(
122
+ `[Server] IDE variable not expanded: ${rawWorkspace}, falling back to auto-detected workspace`
123
+ );
124
+ return null;
125
+ }
126
+
127
+ return rawWorkspace || null;
128
+ }
129
+
130
+ export function collectUnknownFlags(rawArgs, knownFlags, flagsWithValue) {
131
+ const unknownFlags = [];
132
+ for (let i = 0; i < rawArgs.length; i += 1) {
133
+ const arg = rawArgs[i];
134
+ if (flagsWithValue.has(arg)) {
135
+ if (arg.includes('=')) continue;
136
+ const next = rawArgs[i + 1];
137
+ if (next && !next.startsWith('-')) {
138
+ i += 1;
139
+ }
140
+ continue;
141
+ }
142
+ if (arg.startsWith('-') && !knownFlags.has(arg) && !arg.startsWith('--workspace=')) {
143
+ unknownFlags.push(arg);
144
+ }
145
+ }
146
+ return unknownFlags;
147
+ }
148
+
149
+ export function parseArgs(
150
+ argv = process.argv,
151
+ runtime = { stdinIsTTY: process.stdin.isTTY, stdoutIsTTY: process.stdout.isTTY }
152
+ ) {
153
+ const args = normalizeCliArgs(argv.slice(2));
154
+ const rawArgs = [...args];
155
+
156
+ const wantsVersion = args.includes('--version') || args.includes('-v');
157
+ const wantsHelp = args.includes('--help') || args.includes('-h') || shouldDefaultToHelp(args, runtime);
158
+ const wantsCache = args.includes('--cache');
159
+ const wantsClean = args.includes('--clean');
160
+ const wantsStatus = args.includes('--status');
161
+ const wantsClearCache = args.includes('--clear-cache');
162
+ const wantsLogs = args.includes('--logs');
163
+ const wantsMem = args.includes('--mem');
164
+ const wantsRegister = args.includes('--register');
165
+ const wantsStart = args.includes('--start') || wantsRegister;
166
+ const wantsStop = args.includes('--stop');
167
+ const wantsFix = args.includes('--fix');
168
+ const wantsNoFollow = args.includes('--no-follow');
169
+
170
+ const isServerMode = !(
171
+ wantsCache ||
172
+ wantsStatus ||
173
+ wantsClearCache ||
174
+ wantsLogs ||
175
+ wantsMem ||
176
+ wantsStart ||
177
+ wantsStop ||
178
+ wantsHelp ||
179
+ wantsVersion
180
+ );
181
+
182
+ const workspaceDir = parseWorkspaceDir(args);
183
+
184
+ let tailLines = DEFAULT_LOG_TAIL_LINES;
185
+ if (wantsLogs) {
186
+ const tailIndex = args.indexOf('--tail');
187
+ if (tailIndex !== -1 && args[tailIndex + 1]) {
188
+ const parsed = parseInt(args[tailIndex + 1], 10);
189
+ if (!isNaN(parsed) && parsed > 0) {
190
+ tailLines = parsed;
191
+ }
192
+ }
193
+ }
194
+
195
+ let startFilter = null;
196
+ if (wantsStart) {
197
+ const getFilter = (flag) => {
198
+ const filterIndex = args.indexOf(flag);
199
+ if (filterIndex === -1) return null;
200
+ const value = args[filterIndex + 1];
201
+ return value && !value.startsWith('-') ? value : null;
202
+ };
203
+ startFilter = getFilter('--start') ?? getFilter('--register');
204
+ }
205
+
206
+ const knownFlags = new Set([
207
+ '--cache',
208
+ '--clean',
209
+ '--status',
210
+ '--clear',
211
+ '--clear-cache',
212
+ '--logs',
213
+ '--mem',
214
+ '--tail',
215
+ '--no-follow',
216
+ '--start',
217
+ '--register',
218
+ '--stop',
219
+ '--workspace',
220
+ '--fix',
221
+ '--version',
222
+ '-v',
223
+ '--help',
224
+ '-h',
225
+ ]);
226
+ const flagsWithValue = FLAG_ARGS_WITH_VALUES;
227
+ const unknownFlags = collectUnknownFlags(rawArgs, knownFlags, flagsWithValue);
228
+
229
+ return {
230
+ args,
231
+ rawArgs,
232
+ isServerMode,
233
+ workspaceDir,
234
+ wantsVersion,
235
+ wantsHelp,
236
+ wantsCache,
237
+ wantsClean,
238
+ wantsStatus,
239
+ wantsClearCache,
240
+ wantsLogs,
241
+ wantsMem,
242
+ wantsStart,
243
+ wantsStop,
244
+ wantsFix,
245
+ wantsNoFollow,
246
+ tailLines,
247
+ startFilter,
248
+ unknownFlags,
249
+ };
250
+ }
251
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softerist/heuristic-mcp",
3
- "version": "3.0.16",
3
+ "version": "3.0.17",
4
4
  "description": "An enhanced MCP server providing intelligent semantic code search with find-similar-code, recency ranking, and improved chunking. Fork of smart-coding-mcp.",
5
5
  "type": "module",
6
6
  "main": "index.js",