grepmax 0.24.0 → 0.25.0

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.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +11 -6
  3. package/dist/commands/add.js +38 -31
  4. package/dist/commands/config.js +16 -15
  5. package/dist/commands/context.js +38 -13
  6. package/dist/commands/doctor.js +76 -83
  7. package/dist/commands/extract.js +12 -1
  8. package/dist/commands/index.js +23 -24
  9. package/dist/commands/list.js +23 -14
  10. package/dist/commands/mcp.js +278 -143
  11. package/dist/commands/peek.js +16 -5
  12. package/dist/commands/repair.js +54 -120
  13. package/dist/commands/search-output.js +30 -9
  14. package/dist/commands/search-run.js +75 -47
  15. package/dist/commands/search-skeletons.js +28 -18
  16. package/dist/commands/search.js +45 -49
  17. package/dist/commands/serve.js +415 -373
  18. package/dist/commands/setup.js +2 -2
  19. package/dist/commands/similar.js +5 -5
  20. package/dist/commands/skeleton.js +67 -41
  21. package/dist/commands/status.js +5 -2
  22. package/dist/commands/summarize.js +6 -0
  23. package/dist/commands/watch.js +102 -38
  24. package/dist/config.js +3 -0
  25. package/dist/lib/daemon/daemon.js +1101 -379
  26. package/dist/lib/daemon/ipc-handler.js +122 -13
  27. package/dist/lib/daemon/mlx-server-manager.js +268 -125
  28. package/dist/lib/daemon/process-manager.js +7 -4
  29. package/dist/lib/daemon/search-handler.js +23 -9
  30. package/dist/lib/daemon/watcher-manager.js +353 -110
  31. package/dist/lib/index/batch-processor.js +231 -67
  32. package/dist/lib/index/embedding-generation.js +109 -0
  33. package/dist/lib/index/embedding-status.js +23 -0
  34. package/dist/lib/index/file-policy.js +273 -0
  35. package/dist/lib/index/ignore-patterns.js +4 -0
  36. package/dist/lib/index/index-config.js +18 -4
  37. package/dist/lib/index/syncer.js +256 -79
  38. package/dist/lib/index/walker.js +66 -86
  39. package/dist/lib/index/watcher-batch.js +9 -0
  40. package/dist/lib/index/watcher.js +129 -3
  41. package/dist/lib/llm/server.js +6 -0
  42. package/dist/lib/search/searcher.js +30 -19
  43. package/dist/lib/store/store-lease.js +473 -0
  44. package/dist/lib/store/vector-db.js +302 -57
  45. package/dist/lib/utils/blocked-roots.js +63 -0
  46. package/dist/lib/utils/cross-project.js +7 -3
  47. package/dist/lib/utils/daemon-client.js +24 -1
  48. package/dist/lib/utils/daemon-launcher.js +38 -13
  49. package/dist/lib/utils/doctor-status.js +76 -0
  50. package/dist/lib/utils/file-utils.js +74 -4
  51. package/dist/lib/utils/keyed-mutex.js +101 -0
  52. package/dist/lib/utils/logger.js +57 -1
  53. package/dist/lib/utils/mlx-hf-cache.js +114 -0
  54. package/dist/lib/utils/operation-coordinator.js +146 -0
  55. package/dist/lib/utils/path-containment.js +106 -0
  56. package/dist/lib/utils/process.js +44 -0
  57. package/dist/lib/utils/project-registry.js +351 -3
  58. package/dist/lib/utils/scope-filter.js +3 -9
  59. package/dist/lib/utils/stale-hint.js +12 -19
  60. package/dist/lib/utils/watcher-launcher.js +5 -1
  61. package/dist/lib/utils/watcher-store.js +2 -2
  62. package/dist/lib/workers/colbert-math.js +15 -12
  63. package/dist/lib/workers/embeddings/colbert.js +3 -2
  64. package/dist/lib/workers/embeddings/granite.js +4 -3
  65. package/dist/lib/workers/embeddings/mlx-client.js +59 -16
  66. package/dist/lib/workers/orchestrator.js +39 -8
  67. package/dist/lib/workers/pool.js +146 -82
  68. package/dist/lib/workers/process-child.js +11 -2
  69. package/dist/lib/workers/serialized-handler.js +10 -0
  70. package/dist/lib/workers/worker.js +13 -4
  71. package/mlx-embed-server/server.py +21 -3
  72. package/mlx-embed-server/summarizer.py +8 -0
  73. package/package.json +4 -3
  74. package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
  75. package/plugins/grepmax/hooks/start.js +3 -170
@@ -42,6 +42,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  });
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.EXCLUSIVE_GENERATION_REBUILD_PROTOCOL = void 0;
45
46
  exports.writeProgress = writeProgress;
46
47
  exports.writeDone = writeDone;
47
48
  exports.startHeartbeat = startHeartbeat;
@@ -49,6 +50,9 @@ exports.handleCommand = handleCommand;
49
50
  const fs = __importStar(require("node:fs"));
50
51
  const path = __importStar(require("node:path"));
51
52
  const logger_1 = require("../utils/logger");
53
+ const operation_coordinator_1 = require("../utils/operation-coordinator");
54
+ const path_containment_1 = require("../utils/path-containment");
55
+ const project_registry_1 = require("../utils/project-registry");
52
56
  const DAEMON_VERSION = (() => {
53
57
  try {
54
58
  return JSON.parse(fs.readFileSync(path.join(__dirname, "../../../package.json"), "utf-8")).version;
@@ -57,6 +61,7 @@ const DAEMON_VERSION = (() => {
57
61
  return "unknown";
58
62
  }
59
63
  })();
64
+ exports.EXCLUSIVE_GENERATION_REBUILD_PROTOCOL = 1;
60
65
  /**
61
66
  * Write a streaming progress line to the IPC connection.
62
67
  */
@@ -74,6 +79,19 @@ function writeDone(conn, data) {
74
79
  conn.write(`${JSON.stringify(Object.assign({ type: "done" }, data))}\n`);
75
80
  conn.end();
76
81
  }
82
+ function errorResponse(err) {
83
+ if (err instanceof operation_coordinator_1.OperationBusyError ||
84
+ err instanceof operation_coordinator_1.OperationClosedError) {
85
+ return { ok: false, error: err.message, code: err.code };
86
+ }
87
+ return {
88
+ ok: false,
89
+ error: err instanceof Error ? err.message : String(err),
90
+ };
91
+ }
92
+ function startStreaming(conn, operation) {
93
+ void operation().catch((err) => writeDone(conn, errorResponse(err)));
94
+ }
77
95
  const HEARTBEAT_INTERVAL_MS = 5000;
78
96
  /**
79
97
  * Emit periodic heartbeat lines so the client's watchdog timer resets even
@@ -102,9 +120,15 @@ function startHeartbeat(conn, intervalMs = HEARTBEAT_INTERVAL_MS) {
102
120
  */
103
121
  function handleCommand(daemon, cmd, conn) {
104
122
  return __awaiter(this, void 0, void 0, function* () {
105
- var _a, _b, _c, _d, _e;
123
+ var _a, _b, _c, _d, _e, _f, _g;
106
124
  try {
107
125
  (0, logger_1.debug)("daemon", `ipc cmd=${cmd.cmd}${cmd.root ? ` root=${cmd.root}` : ""}`);
126
+ if (cmd.cmd !== "ping" &&
127
+ cmd.cmd !== "shutdown" &&
128
+ (daemon.operationStatus() === "closing" ||
129
+ daemon.operationStatus() === "closed")) {
130
+ return errorResponse(new operation_coordinator_1.OperationClosedError());
131
+ }
108
132
  // The socket listens before LanceDB/MetaCache are open so liveness probes
109
133
  // succeed during slow startup. Gate resource-dependent commands until those
110
134
  // stores exist; ping/shutdown must always work (probes + restart).
@@ -119,6 +143,12 @@ function handleCommand(daemon, cmd, conn) {
119
143
  pid: process.pid,
120
144
  uptime: daemon.uptime(),
121
145
  version: DAEMON_VERSION,
146
+ operationStatus: daemon.operationStatus(),
147
+ resourceGeneration: daemon.resourceGenerationId(),
148
+ rebuildPending: daemon.hasUnfinishedRebuild(),
149
+ capabilities: {
150
+ exclusiveGenerationRebuild: exports.EXCLUSIVE_GENERATION_REBUILD_PROTOCOL,
151
+ },
122
152
  };
123
153
  case "watch": {
124
154
  const root = String(cmd.root || "");
@@ -141,7 +171,17 @@ function handleCommand(daemon, cmd, conn) {
141
171
  uptime: daemon.uptime(),
142
172
  projects: daemon.listProjects(),
143
173
  diskPressure: daemon.getDiskPressure(),
174
+ mlx: daemon.getMlxStatus(),
144
175
  };
176
+ case "project-stats": {
177
+ const root = String(cmd.root || "");
178
+ if (!root)
179
+ return { ok: false, error: "missing root" };
180
+ const project = (0, project_registry_1.listProjects)().find((entry) => entry.root === root);
181
+ if (!project)
182
+ return { ok: false, error: "project not registered" };
183
+ return Object.assign({ ok: true }, (yield daemon.projectStats(root)));
184
+ }
145
185
  case "shutdown": {
146
186
  const reason = String((_a = cmd.reason) !== null && _a !== void 0 ? _a : "unknown");
147
187
  const fromPid = (_b = cmd.from_pid) !== null && _b !== void 0 ? _b : "?";
@@ -155,7 +195,9 @@ function handleCommand(daemon, cmd, conn) {
155
195
  setImmediate(() => daemon.shutdown());
156
196
  return { ok: true };
157
197
  }
158
- case "search": {
198
+ case "search":
199
+ case "search-v2": {
200
+ const isV2 = cmd.cmd === "search-v2";
159
201
  const projectRoot = String(cmd.projectRoot || "");
160
202
  if (!projectRoot)
161
203
  return { ok: false, error: "missing projectRoot" };
@@ -169,6 +211,9 @@ function handleCommand(daemon, cmd, conn) {
169
211
  conn.on("close", onClose);
170
212
  try {
171
213
  const limitRaw = typeof cmd.limit === "number" ? cmd.limit : 10;
214
+ if (!Number.isInteger(limitRaw) || limitRaw < 1 || limitRaw > 1000) {
215
+ return { ok: false, error: "invalid limit" };
216
+ }
172
217
  const skeletonLimitRaw = typeof cmd.skeletonLimit === "number"
173
218
  ? cmd.skeletonLimit
174
219
  : undefined;
@@ -181,12 +226,57 @@ function handleCommand(daemon, cmd, conn) {
181
226
  !Array.isArray(cmd.filters)
182
227
  ? cmd.filters
183
228
  : undefined;
229
+ const projectRoots = filters === null || filters === void 0 ? void 0 : filters.projectRoots;
230
+ const eligibleProjects = new Set((0, project_registry_1.listProjects)()
231
+ .filter((project) => project.status !== "error")
232
+ .map((project) => project.root));
233
+ if (isV2) {
234
+ if (!Array.isArray(projectRoots) || projectRoots.length === 0) {
235
+ return { ok: false, error: "missing projectRoots" };
236
+ }
237
+ if (projectRoots.some((root) => typeof root !== "string" || !eligibleProjects.has(root))) {
238
+ return { ok: false, error: "invalid projectRoots" };
239
+ }
240
+ if ((filters === null || filters === void 0 ? void 0 : filters.inPrefixes) !== undefined ||
241
+ (filters === null || filters === void 0 ? void 0 : filters.excludePrefixes) !== undefined) {
242
+ return {
243
+ ok: false,
244
+ error: "project path filters are not valid for cross-project search",
245
+ };
246
+ }
247
+ }
248
+ else if (projectRoots !== undefined) {
249
+ return { ok: false, error: "projectRoots requires search-v2" };
250
+ }
251
+ else {
252
+ const legacyRoots = filters === null || filters === void 0 ? void 0 : filters.project_roots;
253
+ if (typeof legacyRoots === "string" &&
254
+ legacyRoots.split(",").some((root) => !eligibleProjects.has(root))) {
255
+ return { ok: false, error: "invalid project_roots" };
256
+ }
257
+ for (const prefix of [
258
+ ...((_f = filters === null || filters === void 0 ? void 0 : filters.inPrefixes) !== null && _f !== void 0 ? _f : []),
259
+ ...((_g = filters === null || filters === void 0 ? void 0 : filters.excludePrefixes) !== null && _g !== void 0 ? _g : []),
260
+ ]) {
261
+ (0, path_containment_1.resolveContainedPath)(projectRoot, prefix);
262
+ }
263
+ }
264
+ let pathPrefix;
265
+ if (typeof cmd.pathPrefix === "string") {
266
+ if (isV2) {
267
+ return {
268
+ ok: false,
269
+ error: "pathPrefix is not valid for cross-project search",
270
+ };
271
+ }
272
+ pathPrefix = `${(0, path_containment_1.resolveContainedPath)(projectRoot, cmd.pathPrefix).replace(/\/$/, "")}/`;
273
+ }
184
274
  const resp = yield daemon.search({
185
275
  projectRoot,
186
276
  query,
187
277
  limit: limitRaw,
188
278
  filters,
189
- pathPrefix: typeof cmd.pathPrefix === "string" ? cmd.pathPrefix : undefined,
279
+ pathPrefix,
190
280
  rerank: cmd.rerank === true,
191
281
  explain: cmd.explain === true,
192
282
  seeds: cmd.seeds &&
@@ -209,40 +299,60 @@ function handleCommand(daemon, cmd, conn) {
209
299
  const root = String(cmd.root || "");
210
300
  if (!root)
211
301
  return { ok: false, error: "missing root" };
212
- daemon.addProject(root, conn);
302
+ startStreaming(conn, () => daemon.addProject(root, conn));
303
+ return null;
304
+ }
305
+ case "ensure-project": {
306
+ const root = String(cmd.root || "");
307
+ if (!root)
308
+ return { ok: false, error: "missing root" };
309
+ startStreaming(conn, () => daemon.ensureProject(root, conn));
213
310
  return null;
214
311
  }
215
312
  case "index": {
216
313
  const root = String(cmd.root || "");
217
314
  if (!root)
218
315
  return { ok: false, error: "missing root" };
219
- daemon.indexProject(root, conn, {
316
+ startStreaming(conn, () => daemon.indexProject(root, conn, {
220
317
  reset: !!cmd.reset,
221
318
  dryRun: !!cmd.dryRun,
222
- });
319
+ }));
223
320
  return null;
224
321
  }
225
322
  case "remove": {
226
323
  const root = String(cmd.root || "");
227
324
  if (!root)
228
325
  return { ok: false, error: "missing root" };
229
- daemon.removeProject(root, conn);
326
+ startStreaming(conn, () => daemon.removeProject(root, conn));
230
327
  return null;
231
328
  }
232
- case "repair": {
329
+ case "repair":
330
+ return {
331
+ ok: false,
332
+ error: "legacy repair protocol is disabled; restart gmax and use repair-v2",
333
+ code: "REBUILD_PROTOCOL_REQUIRED",
334
+ };
335
+ case "repair-v2": {
336
+ if (cmd.protocol !== exports.EXCLUSIVE_GENERATION_REBUILD_PROTOCOL) {
337
+ return {
338
+ ok: false,
339
+ error: "unsupported exclusive rebuild protocol",
340
+ code: "REBUILD_PROTOCOL_REQUIRED",
341
+ };
342
+ }
233
343
  // Global drop-and-rebuild recovery for a physical table-width mismatch.
234
344
  // Streams per-project progress; daemon manages the connection.
235
- daemon.repairRebuild(conn);
345
+ startStreaming(conn, () => daemon.repairRebuild(conn));
236
346
  return null;
237
347
  }
238
348
  case "summarize": {
239
349
  const root = String(cmd.root || "");
240
350
  if (!root)
241
351
  return { ok: false, error: "missing root" };
242
- daemon.summarizeProject(root, conn, {
352
+ startStreaming(conn, () => daemon.summarizeProject(root, conn, {
243
353
  limit: typeof cmd.limit === "number" ? cmd.limit : undefined,
244
354
  pathPrefix: typeof cmd.pathPrefix === "string" ? cmd.pathPrefix : undefined,
245
- });
355
+ }));
246
356
  return null;
247
357
  }
248
358
  // --- LLM server management ---
@@ -265,8 +375,7 @@ function handleCommand(daemon, cmd, conn) {
265
375
  }
266
376
  }
267
377
  catch (err) {
268
- const msg = err instanceof Error ? err.message : String(err);
269
- return { ok: false, error: msg };
378
+ return errorResponse(err);
270
379
  }
271
380
  });
272
381
  }