grepmax 0.7.2 → 0.7.4

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.
@@ -33,7 +33,8 @@ function installPlugin() {
33
33
  return __awaiter(this, void 0, void 0, function* () {
34
34
  try {
35
35
  yield runClaudeCommand(["marketplace", "add", "reowens/grepmax"]);
36
- console.log(" Successfully added the gmax marketplace");
36
+ yield runClaudeCommand(["marketplace", "update", "grepmax"]);
37
+ console.log("✅ Marketplace updated");
37
38
  yield runClaudeCommand(["install", "grepmax"]);
38
39
  console.log("✅ Successfully installed the gmax plugin for Claude Code");
39
40
  console.log("\nNext steps:");
@@ -304,10 +304,39 @@ exports.mcp = new commander_1.Command("mcp")
304
304
  });
305
305
  }
306
306
  // --- Index sync ---
307
+ let _indexChildPid = null;
308
+ function isIndexProcessRunning() {
309
+ if (!_indexChildPid)
310
+ return false;
311
+ try {
312
+ process.kill(_indexChildPid, 0);
313
+ return true;
314
+ }
315
+ catch (_a) {
316
+ return false;
317
+ }
318
+ }
307
319
  function ensureIndexReady() {
308
320
  return __awaiter(this, void 0, void 0, function* () {
321
+ var _a;
309
322
  if (_indexReady)
310
323
  return;
324
+ // Check if a previously spawned index process finished
325
+ if (_indexing && !isIndexProcessRunning()) {
326
+ _indexing = false;
327
+ _indexProgress = "";
328
+ _indexChildPid = null;
329
+ // Re-check if index now exists
330
+ try {
331
+ const db = getVectorDb();
332
+ if (yield db.hasRowsForPath(projectRoot)) {
333
+ _indexReady = true;
334
+ console.log("[MCP] Background indexing complete.");
335
+ return;
336
+ }
337
+ }
338
+ catch (_b) { }
339
+ }
311
340
  try {
312
341
  const db = getVectorDb();
313
342
  const hasIndex = yield db.hasRowsForPath(projectRoot);
@@ -316,23 +345,25 @@ exports.mcp = new commander_1.Command("mcp")
316
345
  return; // Already indexing in background
317
346
  _indexing = true;
318
347
  _indexProgress = "starting...";
319
- console.log("[MCP] No index found, running initial sync...");
320
- (0, syncer_1.initialSync)({
321
- projectRoot,
322
- onProgress: (info) => {
323
- _indexProgress = `${info.processed}/${info.total || "?"} files`;
324
- },
325
- })
326
- .then(() => {
327
- _indexReady = true;
328
- _indexing = false;
329
- _indexProgress = "";
330
- console.log("[MCP] Initial sync complete.");
331
- })
332
- .catch((e) => {
348
+ console.log("[MCP] No index found, spawning background index...");
349
+ // Spawn gmax index as a detached child process — doesn't hold
350
+ // the lock inside this MCP process, so CLI `gmax index` won't conflict.
351
+ const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "index", "--path", projectRoot], { detached: true, stdio: "ignore" });
352
+ _indexChildPid = (_a = child.pid) !== null && _a !== void 0 ? _a : null;
353
+ child.unref();
354
+ _indexProgress = `PID ${_indexChildPid}`;
355
+ console.log(`[MCP] Background index started (PID: ${_indexChildPid})`);
356
+ child.on("exit", (code) => {
333
357
  _indexing = false;
334
358
  _indexProgress = "";
335
- console.error("[MCP] Index sync failed:", e);
359
+ _indexChildPid = null;
360
+ if (code === 0) {
361
+ _indexReady = true;
362
+ console.log("[MCP] Background indexing complete.");
363
+ }
364
+ else {
365
+ console.error(`[MCP] Background indexing failed (exit code: ${code})`);
366
+ }
336
367
  });
337
368
  }
338
369
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",
@@ -1,7 +1,7 @@
1
1
  const fs = require("node:fs");
2
2
  const _path = require("node:path");
3
3
  const http = require("node:http");
4
- const { spawn } = require("node:child_process");
4
+ const { spawn, execFileSync } = require("node:child_process");
5
5
 
6
6
  function isServerRunning(port) {
7
7
  return new Promise((resolve) => {
@@ -20,12 +20,30 @@ function isServerRunning(port) {
20
20
  });
21
21
  }
22
22
 
23
- function startPythonServer(scriptName, logName) {
23
+ function findMlxServerDir() {
24
+ // Try to find mlx-embed-server relative to the gmax binary (npm install location)
25
+ try {
26
+ const gmaxPath = execFileSync("which gmax", {
27
+ encoding: "utf-8",
28
+ }).trim();
29
+ // gmax binary is a symlink in .bin/ → resolve to package root
30
+ const realPath = fs.realpathSync(gmaxPath);
31
+ const pkgRoot = _path.resolve(_path.dirname(realPath), "..");
32
+ const serverDir = _path.join(pkgRoot, "mlx-embed-server");
33
+ if (fs.existsSync(_path.join(serverDir, "server.py"))) return serverDir;
34
+ } catch {}
35
+
36
+ // Fallback: dev mode — relative to plugin root
24
37
  const pluginRoot = __dirname.replace(/\/hooks$/, "");
25
- const gmaxRoot = _path.resolve(pluginRoot, "../..");
26
- const serverDir = _path.join(gmaxRoot, "mlx-embed-server");
38
+ const devRoot = _path.resolve(pluginRoot, "../..");
39
+ const devDir = _path.join(devRoot, "mlx-embed-server");
40
+ if (fs.existsSync(_path.join(devDir, "server.py"))) return devDir;
27
41
 
28
- if (!fs.existsSync(_path.join(serverDir, scriptName))) return;
42
+ return null;
43
+ }
44
+
45
+ function startPythonServer(serverDir, scriptName, logName) {
46
+ if (!serverDir) return;
29
47
 
30
48
  const logPath = `/tmp/${logName}.log`;
31
49
  const out = fs.openSync(logPath, "a");
@@ -41,7 +59,6 @@ function startPythonServer(scriptName, logName) {
41
59
 
42
60
  function startWatcher() {
43
61
  try {
44
- const { execFileSync } = require("node:child_process");
45
62
  execFileSync("gmax", ["watch", "-b"], { timeout: 5000, stdio: "ignore" });
46
63
  } catch {
47
64
  // Watcher may already be running or gmax not in PATH — ignore
@@ -49,18 +66,19 @@ function startWatcher() {
49
66
  }
50
67
 
51
68
  async function main() {
52
- const embedMode =
53
- process.env.GMAX_EMBED_MODE || process.env.OSGREP_EMBED_MODE || "auto";
69
+ const embedMode = process.env.GMAX_EMBED_MODE || "auto";
54
70
 
55
71
  if (embedMode !== "cpu") {
72
+ const serverDir = findMlxServerDir();
73
+
56
74
  // Start MLX embed server (port 8100)
57
- if (!(await isServerRunning(8100))) {
58
- startPythonServer("server.py", "mlx-embed-server");
75
+ if (serverDir && !(await isServerRunning(8100))) {
76
+ startPythonServer(serverDir, "server.py", "mlx-embed-server");
59
77
  }
60
78
 
61
79
  // Start LLM summarizer server (port 8101)
62
- if (!(await isServerRunning(8101))) {
63
- startPythonServer("summarizer.py", "mlx-summarizer");
80
+ if (serverDir && !(await isServerRunning(8101))) {
81
+ startPythonServer(serverDir, "summarizer.py", "mlx-summarizer");
64
82
  }
65
83
  }
66
84
 
@@ -71,7 +89,7 @@ async function main() {
71
89
  hookSpecificOutput: {
72
90
  hookEventName: "SessionStart",
73
91
  additionalContext:
74
- 'gmax MCP ready; prefer `gmax "<complete question>"` over grep (plain output is agent-friendly).',
92
+ "gmax MCP ready. Use semantic_search for concept-based code search (5+ words recommended). Use code_skeleton before reading large files. index_status to check health.",
75
93
  },
76
94
  };
77
95
  process.stdout.write(JSON.stringify(response));