@softerist/heuristic-mcp 2.1.39 → 2.1.41

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.
@@ -1,5 +1,10 @@
1
+
1
2
  import { exec } from 'child_process';
2
3
  import util from 'util';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import fs from 'fs/promises';
7
+
3
8
  const execPromise = util.promisify(exec);
4
9
 
5
10
  export async function stop() {
@@ -15,7 +20,7 @@ export async function stop() {
15
20
  } else {
16
21
  // Unix: Use pgrep to get all matching PIDs
17
22
  try {
18
- const { stdout } = await execPromise(`pgrep -f \"heuristic-mcp.*index.js\"`);
23
+ const { stdout } = await execPromise(`pgrep -f "heuristic-mcp.*index.js"`);
19
24
  const allPids = stdout.trim().split(/\s+/).filter(p => p && !isNaN(p));
20
25
 
21
26
  // Filter out current PID and dead processes
@@ -71,213 +76,185 @@ export async function start() {
71
76
  }
72
77
  }
73
78
 
79
+ // Helper to get global cache dir
80
+ function getGlobalCacheDir() {
81
+ if (process.platform === 'win32') {
82
+ return process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
83
+ } else if (process.platform === 'darwin') {
84
+ return path.join(os.homedir(), 'Library', 'Caches');
85
+ }
86
+ return process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
87
+ }
88
+
74
89
  export async function status() {
75
90
  try {
76
- const platform = process.platform;
77
- const currentPid = process.pid;
78
- let pids = [];
91
+ const home = os.homedir();
92
+ const pids = [];
79
93
 
80
- if (platform === 'win32') {
81
- const { stdout } = await execPromise(`wmic process where "CommandLine like '%heuristic-mcp/index.js%'" get ProcessId`);
82
- pids = stdout.trim().split(/\s+/).filter(p => p && !isNaN(p) && parseInt(p) !== currentPid);
83
- } else {
84
- try {
85
- const { stdout } = await execPromise(`pgrep -f "heuristic-mcp.*index.js"`);
86
- const allPids = stdout.trim().split(/\s+/).filter(p => p && !isNaN(p));
87
-
88
- // Filter out current PID and dead processes (e.g. ephemeral shell wrappers)
89
- const validPids = [];
90
- for (const p of allPids) {
91
- const pid = parseInt(p);
92
- if (pid === currentPid) continue;
93
-
94
- try {
95
- // Check if process is still alive
96
- process.kill(pid, 0);
97
- validPids.push(p);
98
- } catch (e) {
99
- // Process is dead or access denied
94
+ // 1. Check PID file first
95
+ const pidFile = path.join(home, '.heuristic-mcp.pid');
96
+
97
+ try {
98
+ const content = await fs.readFile(pidFile, 'utf-8');
99
+ const pid = parseInt(content.trim(), 10);
100
+ if (pid && !isNaN(pid)) {
101
+ // Check if running
102
+ try {
103
+ process.kill(pid, 0);
104
+ pids.push(pid);
105
+ } catch (e) {
106
+ // Stale PID file
107
+ await fs.unlink(pidFile).catch(() => {});
108
+ }
109
+ }
110
+ } catch (e) {
111
+ // No pid file, ignore
112
+ }
113
+
114
+ // 2. Fallback to process list if no PID file found or process dead
115
+ if (pids.length === 0) {
116
+ try {
117
+ // Simplified ps check for Linux/Mac
118
+ let cmd = 'ps aux';
119
+ if (process.platform === 'win32') {
120
+ // Skip Win32 complex ps logic for now
121
+ } else {
122
+ const { stdout } = await execPromise('ps aux');
123
+ const lines = stdout.split('\n');
124
+
125
+ const validPids = [];
126
+ const myPid = process.pid;
127
+
128
+ for (const line of lines) {
129
+ if (line.includes('heuristic-mcp/index.js') || line.includes('heuristic-mcp')) {
130
+ const parts = line.trim().split(/\s+/);
131
+ const pid = parseInt(parts[1], 10);
132
+ if (pid && !isNaN(pid) && pid !== myPid && !line.includes(' grep ')) {
133
+ validPids.push(pid);
134
+ }
135
+ }
136
+ }
137
+ // Merge validPids into pids if not already present
138
+ for (const p of validPids) {
139
+ if (!pids.includes(p)) pids.push(p);
100
140
  }
101
141
  }
102
- pids = validPids;
103
- } catch (e) {
104
- if (e.code === 1) pids = [];
105
- else throw e;
106
- }
142
+ } catch (e) {}
107
143
  }
108
144
 
145
+ // STATUS OUTPUT
146
+ console.log(''); // spacer
109
147
  if (pids.length > 0) {
110
148
  console.log(`[Lifecycle] 🟢 Server is RUNNING. PID(s): ${pids.join(', ')}`);
111
149
  } else {
112
150
  console.log('[Lifecycle] ⚪ Server is STOPPED.');
113
151
  }
114
- } catch (error) {
115
- console.error(`[Lifecycle] Failed to check status: ${error.message}`);
116
- }
117
- }
118
-
119
- export async function logs() {
120
- const fs = await import('fs/promises');
121
- const path = await import('path');
122
- const os = await import('os');
123
- const crypto = await import('crypto');
124
-
125
- console.log('[Logs] Searching for cache directories...\n');
126
-
127
- // Determine global cache root
128
- function getGlobalCacheDir() {
129
- if (process.platform === 'win32') {
130
- return process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
131
- } else if (process.platform === 'darwin') {
132
- return path.join(os.homedir(), 'Library', 'Caches');
133
- }
134
- return process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
135
- }
152
+ console.log(''); // spacer
136
153
 
137
- const globalCacheRoot = path.join(getGlobalCacheDir(), 'heuristic-mcp');
154
+ // APPEND LOGS INFO (Cache Status)
155
+ const globalCacheRoot = path.join(getGlobalCacheDir(), 'heuristic-mcp');
156
+ console.log('[Status] Inspecting cache status...\n');
138
157
 
139
- try {
140
- // List all cache directories
141
158
  const cacheDirs = await fs.readdir(globalCacheRoot).catch(() => []);
142
159
 
143
160
  if (cacheDirs.length === 0) {
144
- console.log('[Logs] No cache directories found.');
145
- console.log(`[Logs] Expected location: ${globalCacheRoot}`);
146
- console.log('');
147
- // Don't return - fall through to show paths section
161
+ console.log('[Status] No cache directories found.');
162
+ console.log(`[Status] Expected location: ${globalCacheRoot}`);
148
163
  } else {
164
+ console.log(`[Status] Found ${cacheDirs.length} cache director${cacheDirs.length === 1 ? 'y' : 'ies'} in ${globalCacheRoot}`);
165
+
166
+ for (const dir of cacheDirs) {
167
+ const cacheDir = path.join(globalCacheRoot, dir);
168
+ const metaFile = path.join(cacheDir, 'meta.json');
169
+
170
+ console.log(`${'─'.repeat(60)}`);
171
+ console.log(`📁 Cache: ${dir}`);
172
+ console.log(` Path: ${cacheDir}`);
173
+
174
+ try {
175
+ const metaData = JSON.parse(await fs.readFile(metaFile, 'utf-8'));
176
+
177
+ console.log(` Status: ✅ Valid cache`);
178
+ console.log(` Workspace: ${metaData.workspace || 'Unknown'}`);
179
+ console.log(` Files indexed: ${metaData.filesIndexed ?? 'N/A'}`);
180
+ console.log(` Chunks stored: ${metaData.chunksStored ?? 'N/A'}`);
181
+
182
+ if (metaData.lastSaveTime) {
183
+ const saveDate = new Date(metaData.lastSaveTime);
184
+ const now = new Date();
185
+ const ageMs = now - saveDate;
186
+ const ageHours = Math.floor(ageMs / (1000 * 60 * 60));
187
+ const ageMins = Math.floor((ageMs % (1000 * 60 * 60)) / (1000 * 60));
188
+ console.log(` Last saved: ${saveDate.toLocaleString()} (${ageHours}h ${ageMins}m ago)`);
189
+ }
149
190
 
150
- console.log(`[Logs] Found ${cacheDirs.length} cache director${cacheDirs.length === 1 ? 'y' : 'ies'} in ${globalCacheRoot}\n`);
151
-
152
- for (const dir of cacheDirs) {
153
- const cacheDir = path.join(globalCacheRoot, dir);
154
- const metaFile = path.join(cacheDir, 'meta.json');
155
-
156
- console.log(`${'─'.repeat(60)}`);
157
- console.log(`📁 Cache: ${dir}`);
158
- console.log(` Path: ${cacheDir}`);
159
-
160
- try {
161
- const metaData = JSON.parse(await fs.readFile(metaFile, 'utf-8'));
162
-
163
- console.log(` Status: ✅ Valid cache`);
164
- console.log(` Workspace: ${metaData.workspace || 'Unknown'}`);
165
- console.log(` Files indexed: ${metaData.filesIndexed ?? 'N/A'}`);
166
- console.log(` Chunks stored: ${metaData.chunksStored ?? 'N/A'}`);
167
- console.log(` Embedding model: ${metaData.embeddingModel}`);
168
-
169
- if (metaData.lastSaveTime) {
170
- const saveDate = new Date(metaData.lastSaveTime);
171
- const now = new Date();
172
- const ageMs = now - saveDate;
173
- const ageHours = Math.floor(ageMs / (1000 * 60 * 60));
174
- const ageMins = Math.floor((ageMs % (1000 * 60 * 60)) / (1000 * 60));
175
-
176
- console.log(` Last saved: ${saveDate.toLocaleString()} (${ageHours}h ${ageMins}m ago)`);
177
- }
178
-
179
- // Check file sizes
180
- const files = ['embeddings.json', 'file-hashes.json', 'call-graph.json', 'ann-index.bin'];
181
- const sizes = [];
182
- for (const file of files) {
183
- try {
184
- const stat = await fs.stat(path.join(cacheDir, file));
185
- sizes.push(`${file}: ${(stat.size / 1024).toFixed(1)}KB`);
186
- } catch {}
187
- }
188
- if (sizes.length > 0) {
189
- console.log(` Files: ${sizes.join(', ')}`);
190
- }
191
-
192
- // Verify indexing completion
193
- if (metaData.filesIndexed && metaData.filesIndexed > 0 && metaData.chunksStored && metaData.chunksStored > 0) {
194
- console.log(` Indexing: ✅ COMPLETE (${metaData.filesIndexed} files → ${metaData.chunksStored} chunks)`);
195
- } else if (metaData.filesIndexed === 0) {
196
- console.log(` Indexing: ⚠️ NO FILES (check excludePatterns in config)`);
197
- } else {
198
- console.log(` Indexing: ⚠️ INCOMPLETE or UNKNOWN`);
199
- }
191
+ // Verify indexing completion
192
+ if (metaData.filesIndexed && metaData.filesIndexed > 0) {
193
+ console.log(` Indexing: COMPLETE (${metaData.filesIndexed} files)`);
194
+ } else if (metaData.filesIndexed === 0) {
195
+ console.log(` Indexing: ⚠️ NO FILES (check excludePatterns)`);
196
+ } else {
197
+ console.log(` Indexing: ⚠️ INCOMPLETE`);
198
+ }
200
199
 
201
- } catch (err) {
202
- if (err.code === 'ENOENT') {
203
- // Meta file missing - check if directory is fresh (indexing in progress)
204
- try {
205
- const stats = await fs.stat(cacheDir);
206
- const ageMs = new Date() - stats.mtime;
207
- // If less than 10 minutes old, assume indexing
208
- if (ageMs < 10 * 60 * 1000) {
209
- console.log(` Status: Initializing / Indexing in progress...`);
210
- console.log(` (Metadata file has not been written yet using ID ${dir})`);
211
- } else {
212
- console.log(` Status: ⚠️ Incomplete cache (stale)`);
200
+ } catch (err) {
201
+ if (err.code === 'ENOENT') {
202
+ try {
203
+ const stats = await fs.stat(cacheDir);
204
+ const ageMs = new Date() - stats.mtime;
205
+ if (ageMs < 10 * 60 * 1000) {
206
+ console.log(` Status: Initializing / Indexing in progress...`);
207
+ console.log(` (Metadata file has not been written yet using ID ${dir})`);
208
+ } else {
209
+ console.log(` Status: ⚠️ Incomplete cache (stale)`);
210
+ }
211
+ } catch {
212
+ console.log(` Status: ❌ Invalid cache directory`);
213
213
  }
214
- } catch {
215
- console.log(` Status: ❌ Invalid cache directory`);
214
+ } else {
215
+ console.log(` Status: ❌ Invalid or corrupted (${err.message})`);
216
216
  }
217
- } else {
218
- console.log(` Status: ❌ Invalid or corrupted (${err.message})`);
219
217
  }
220
218
  }
219
+ console.log(`${'─'.repeat(60)}`);
221
220
  }
222
221
 
223
- console.log(`${'─'.repeat(60)}\n`);
224
- }
225
-
226
- // Show important paths
227
- console.log('[Paths] Important locations:');
222
+ // SHOW PATHS
223
+ console.log('\n[Paths] Important locations:');
228
224
 
229
225
  // Global npm bin
230
- const { execSync } = await import('child_process');
231
226
  let npmBin = 'unknown';
232
227
  try {
233
- const prefix = execSync('npm config get prefix', { encoding: 'utf-8' }).trim();
234
- npmBin = path.join(prefix, 'bin');
228
+ const { stdout } = await execPromise('npm config get prefix');
229
+ npmBin = path.join(stdout.trim(), 'bin');
235
230
  } catch {}
231
+ console.log(` 📦 Global npm bin: ${npmBin}`);
236
232
 
237
- // Check all known MCP config paths (same as register.js)
238
- const home = os.homedir();
239
- const mcpConfigs = [];
240
-
241
- // Antigravity
242
- const antigravityConfig = path.join(home, '.gemini', 'antigravity', 'mcp_config.json');
243
- const antigravityExists = await fs.access(antigravityConfig).then(() => true).catch(() => false);
244
- mcpConfigs.push({ name: 'Antigravity', path: antigravityConfig, exists: antigravityExists });
233
+ // Configs
234
+ const configLocations = [
235
+ { name: 'Antigravity', path: path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json') },
236
+ { name: 'Cursor', path: path.join(os.homedir(), '.config', 'Cursor', 'User', 'settings.json') }
237
+ ];
245
238
 
246
- // Claude Desktop
247
- let claudeConfig = null;
248
- if (process.platform === 'darwin') {
249
- claudeConfig = path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
239
+ // Platform specific logic for Cursor
240
+ if (process.platform === 'darwin') {
241
+ configLocations[1].path = path.join(os.homedir(), 'Library', 'Application Support', 'Cursor', 'User', 'settings.json');
250
242
  } else if (process.platform === 'win32') {
251
- claudeConfig = path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
252
- }
253
- if (claudeConfig) {
254
- const claudeExists = await fs.access(claudeConfig).then(() => true).catch(() => false);
255
- mcpConfigs.push({ name: 'Claude Desktop', path: claudeConfig, exists: claudeExists });
243
+ configLocations[1].path = path.join(process.env.APPDATA || '', 'Cursor', 'User', 'settings.json');
256
244
  }
257
245
 
258
- // Cursor (uses settings.json with mcpServers key)
259
- let cursorConfig = null;
260
- if (process.platform === 'darwin') {
261
- cursorConfig = path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'settings.json');
262
- } else if (process.platform === 'win32') {
263
- cursorConfig = path.join(process.env.APPDATA || '', 'Cursor', 'User', 'settings.json');
264
- } else {
265
- cursorConfig = path.join(home, '.config', 'Cursor', 'User', 'settings.json');
246
+ console.log(' ⚙️ MCP configs:');
247
+ for (const loc of configLocations) {
248
+ let status = '(not found)';
249
+ try { await fs.access(loc.path); status = '(exists)'; } catch {}
250
+ console.log(` - ${loc.name}: ${loc.path} ${status}`);
266
251
  }
267
- const cursorExists = await fs.access(cursorConfig).then(() => true).catch(() => false);
268
- mcpConfigs.push({ name: 'Cursor', path: cursorConfig, exists: cursorExists });
269
252
 
270
- console.log(` 📦 Global npm bin: ${npmBin}`);
271
- console.log(` ⚙️ MCP configs:`);
272
- for (const cfg of mcpConfigs) {
273
- const status = cfg.exists ? '\x1b[32m(exists)\x1b[0m' : '\x1b[90m(not found)\x1b[0m';
274
- console.log(` - ${cfg.name}: ${cfg.path} ${status}`);
275
- }
276
253
  console.log(` 💾 Cache root: ${globalCacheRoot}`);
277
254
  console.log(` 📁 Current dir: ${process.cwd()}`);
278
255
  console.log('');
279
256
 
280
257
  } catch (error) {
281
- console.error(`[Logs] Error reading cache: ${error.message}`);
258
+ console.error(`[Lifecycle] Failed to check status: ${error.message}`);
282
259
  }
283
260
  }
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
- import { stop, start, status, logs } from "./features/lifecycle.js";
3
+ import { stop, start, status } from "./features/lifecycle.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
6
6
  import { pipeline } from "@xenova/transformers";
@@ -54,7 +54,8 @@ if (args.includes('--status')) {
54
54
  }
55
55
 
56
56
  if (args.includes('--logs')) {
57
- await logs();
57
+ console.log("⚠️ Note: '--logs' is deprecated. Please use '--status' to see cache information.");
58
+ await status();
58
59
  process.exit(0);
59
60
  }
60
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softerist/heuristic-mcp",
3
- "version": "2.1.39",
3
+ "version": "2.1.41",
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",