@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.
- package/features/lifecycle.js +145 -151
- package/index.js +2 -1
- package/package.json +1 -1
package/features/lifecycle.js
CHANGED
|
@@ -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
|
|
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
|
|
77
|
-
const
|
|
78
|
-
let pids = [];
|
|
91
|
+
const home = os.homedir();
|
|
92
|
+
const pids = [];
|
|
79
93
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
console.error(`[Lifecycle] Failed to check status: ${error.message}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
152
|
+
console.log(''); // spacer
|
|
118
153
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
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
|
|
217
|
-
npmBin = path.join(
|
|
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
|
-
//
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
258
|
+
console.error(`[Lifecycle] Failed to check status: ${error.message}`);
|
|
265
259
|
}
|
|
266
260
|
}
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softerist/heuristic-mcp",
|
|
3
|
-
"version": "2.1.
|
|
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",
|