@softerist/heuristic-mcp 2.1.39 → 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 +143 -166
- 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,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
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
}
|
|
215
|
-
|
|
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
|
-
|
|
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
|
|
234
|
-
npmBin = path.join(
|
|
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
|
-
//
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
258
|
+
console.error(`[Lifecycle] Failed to check status: ${error.message}`);
|
|
282
259
|
}
|
|
283
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",
|