@softerist/heuristic-mcp 2.1.38 → 2.1.40

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,196 +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
- }
152
+ console.log(''); // spacer
118
153
 
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');
154
+ // APPEND LOGS INFO (Cache Status)
155
+ const globalCacheRoot = path.join(getGlobalCacheDir(), 'heuristic-mcp');
156
+ console.log('[Status] Inspecting cache status...\n');
124
157
 
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
- }
136
-
137
- const globalCacheRoot = path.join(getGlobalCacheDir(), 'heuristic-mcp');
138
-
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
+ // 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
+ }
191
199
 
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`);
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
+ }
214
+ } else {
215
+ console.log(` Status: ❌ Invalid or corrupted (${err.message})`);
216
+ }
199
217
  }
200
-
201
- } catch (e) {
202
- console.log(` Status: ❌ Invalid or corrupted (${e.message})`);
203
218
  }
219
+ console.log(`${'─'.repeat(60)}`);
204
220
  }
205
221
 
206
- console.log(`${'─'.repeat(60)}\n`);
207
- }
208
-
209
- // Show important paths
210
- console.log('[Paths] Important locations:');
222
+ // SHOW PATHS
223
+ console.log('\n[Paths] Important locations:');
211
224
 
212
225
  // Global npm bin
213
- const { execSync } = await import('child_process');
214
226
  let npmBin = 'unknown';
215
227
  try {
216
- const prefix = execSync('npm config get prefix', { encoding: 'utf-8' }).trim();
217
- npmBin = path.join(prefix, 'bin');
228
+ const { stdout } = await execPromise('npm config get prefix');
229
+ npmBin = path.join(stdout.trim(), 'bin');
218
230
  } catch {}
231
+ console.log(` 📦 Global npm bin: ${npmBin}`);
219
232
 
220
- // Check all known MCP config paths (same as register.js)
221
- const home = os.homedir();
222
- const mcpConfigs = [];
223
-
224
- // Antigravity
225
- const antigravityConfig = path.join(home, '.gemini', 'antigravity', 'mcp_config.json');
226
- const antigravityExists = await fs.access(antigravityConfig).then(() => true).catch(() => false);
227
- 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
+ ];
228
238
 
229
- // Claude Desktop
230
- let claudeConfig = null;
231
- if (process.platform === 'darwin') {
232
- 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');
233
242
  } else if (process.platform === 'win32') {
234
- claudeConfig = path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
235
- }
236
- if (claudeConfig) {
237
- const claudeExists = await fs.access(claudeConfig).then(() => true).catch(() => false);
238
- mcpConfigs.push({ name: 'Claude Desktop', path: claudeConfig, exists: claudeExists });
243
+ configLocations[1].path = path.join(process.env.APPDATA || '', 'Cursor', 'User', 'settings.json');
239
244
  }
240
245
 
241
- // Cursor (uses settings.json with mcpServers key)
242
- let cursorConfig = null;
243
- if (process.platform === 'darwin') {
244
- cursorConfig = path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'settings.json');
245
- } else if (process.platform === 'win32') {
246
- cursorConfig = path.join(process.env.APPDATA || '', 'Cursor', 'User', 'settings.json');
247
- } else {
248
- 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}`);
249
251
  }
250
- const cursorExists = await fs.access(cursorConfig).then(() => true).catch(() => false);
251
- mcpConfigs.push({ name: 'Cursor', path: cursorConfig, exists: cursorExists });
252
252
 
253
- console.log(` 📦 Global npm bin: ${npmBin}`);
254
- console.log(` ⚙️ MCP configs:`);
255
- for (const cfg of mcpConfigs) {
256
- const status = cfg.exists ? '\x1b[32m(exists)\x1b[0m' : '\x1b[90m(not found)\x1b[0m';
257
- console.log(` - ${cfg.name}: ${cfg.path} ${status}`);
258
- }
259
253
  console.log(` 💾 Cache root: ${globalCacheRoot}`);
260
254
  console.log(` 📁 Current dir: ${process.cwd()}`);
261
255
  console.log('');
262
256
 
263
257
  } catch (error) {
264
- console.error(`[Logs] Error reading cache: ${error.message}`);
258
+ console.error(`[Lifecycle] Failed to check status: ${error.message}`);
265
259
  }
266
260
  }
package/index.js CHANGED
@@ -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.38",
3
+ "version": "2.1.40",
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",