grepmax 0.7.29 → 0.7.31

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.
@@ -50,6 +50,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
50
50
  };
51
51
  Object.defineProperty(exports, "__esModule", { value: true });
52
52
  exports.generateSummaries = generateSummaries;
53
+ exports.computeStaleFiles = computeStaleFiles;
53
54
  exports.initialSync = initialSync;
54
55
  const fs = __importStar(require("node:fs"));
55
56
  const path = __importStar(require("node:path"));
@@ -178,6 +179,9 @@ function createNoopMetaCache() {
178
179
  close: () => __awaiter(this, void 0, void 0, function* () { }),
179
180
  };
180
181
  }
182
+ function computeStaleFiles(cachedPaths, seenPaths) {
183
+ return Array.from(cachedPaths).filter((p) => !seenPaths.has(p));
184
+ }
181
185
  function initialSync(options) {
182
186
  return __awaiter(this, void 0, void 0, function* () {
183
187
  var _a, e_1, _b, _c;
@@ -465,7 +469,7 @@ function initialSync(options) {
465
469
  : new Error(String(flushError));
466
470
  }
467
471
  // Stale cleanup: only remove paths scoped to this project's root
468
- const stale = Array.from(cachedPaths).filter((p) => !seenPaths.has(p));
472
+ const stale = computeStaleFiles(cachedPaths, seenPaths);
469
473
  if (!dryRun && stale.length > 0 && !shouldSkipCleanup) {
470
474
  (0, logger_1.log)("index", `Stale cleanup: ${stale.length} paths`);
471
475
  yield vectorDb.deletePaths(stale);
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.processBatchCore = processBatchCore;
46
+ exports.flushBatchToDb = flushBatchToDb;
47
+ exports.computeRetryAction = computeRetryAction;
48
+ const fs = __importStar(require("node:fs"));
49
+ const cache_check_1 = require("../utils/cache-check");
50
+ const file_utils_1 = require("../utils/file-utils");
51
+ function processBatchCore(batch, metaCache, pool) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ let reindexed = 0;
54
+ const changedIds = [];
55
+ const deletes = [];
56
+ const vectors = [];
57
+ const metaUpdates = new Map();
58
+ const metaDeletes = [];
59
+ for (const [absPath, event] of batch) {
60
+ if (event === "unlink") {
61
+ deletes.push(absPath);
62
+ metaDeletes.push(absPath);
63
+ reindexed++;
64
+ continue;
65
+ }
66
+ try {
67
+ const stats = yield fs.promises.stat(absPath);
68
+ if (!(0, file_utils_1.isIndexableFile)(absPath, stats.size))
69
+ continue;
70
+ const cached = metaCache.get(absPath);
71
+ if ((0, cache_check_1.isFileCached)(cached, stats)) {
72
+ continue;
73
+ }
74
+ const result = yield pool.processFile({
75
+ path: absPath,
76
+ absolutePath: absPath,
77
+ });
78
+ const metaEntry = {
79
+ hash: result.hash,
80
+ mtimeMs: result.mtimeMs,
81
+ size: result.size,
82
+ };
83
+ if (cached && cached.hash === result.hash) {
84
+ metaUpdates.set(absPath, metaEntry);
85
+ continue;
86
+ }
87
+ if (result.shouldDelete) {
88
+ deletes.push(absPath);
89
+ metaUpdates.set(absPath, metaEntry);
90
+ reindexed++;
91
+ continue;
92
+ }
93
+ deletes.push(absPath);
94
+ if (result.vectors.length > 0) {
95
+ vectors.push(...result.vectors);
96
+ for (const v of result.vectors) {
97
+ changedIds.push(v.id);
98
+ }
99
+ }
100
+ metaUpdates.set(absPath, metaEntry);
101
+ reindexed++;
102
+ }
103
+ catch (err) {
104
+ const code = err === null || err === void 0 ? void 0 : err.code;
105
+ if (code === "ENOENT") {
106
+ deletes.push(absPath);
107
+ metaDeletes.push(absPath);
108
+ reindexed++;
109
+ }
110
+ }
111
+ }
112
+ return { reindexed, changedIds, vectors, deletes, metaUpdates, metaDeletes };
113
+ });
114
+ }
115
+ function flushBatchToDb(result, vectorDb) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ const newIds = result.vectors.map((v) => v.id);
118
+ if (result.vectors.length > 0) {
119
+ yield vectorDb.insertBatch(result.vectors);
120
+ }
121
+ if (result.deletes.length > 0) {
122
+ if (newIds.length > 0) {
123
+ yield vectorDb.deletePathsExcludingIds(result.deletes, newIds);
124
+ }
125
+ else {
126
+ yield vectorDb.deletePaths(result.deletes);
127
+ }
128
+ }
129
+ });
130
+ }
131
+ function computeRetryAction(batch, retryCount, maxRetries, isLockError, consecutiveLockFailures, debounceMs) {
132
+ var _a;
133
+ const requeued = new Map();
134
+ let dropped = 0;
135
+ for (const [absPath, event] of batch) {
136
+ const count = ((_a = retryCount.get(absPath)) !== null && _a !== void 0 ? _a : 0) + 1;
137
+ if (count >= maxRetries) {
138
+ retryCount.delete(absPath);
139
+ dropped++;
140
+ }
141
+ else {
142
+ requeued.set(absPath, event);
143
+ retryCount.set(absPath, count);
144
+ }
145
+ }
146
+ const effectiveFailures = isLockError ? consecutiveLockFailures + 1 : 0;
147
+ const backoffMs = Math.min(debounceMs * Math.pow(2, effectiveFailures), 30000);
148
+ return { requeued, dropped, backoffMs };
149
+ }
@@ -103,11 +103,18 @@ function startWatcher(opts) {
103
103
  clearTimeout(debounceTimer);
104
104
  debounceTimer = setTimeout(() => processBatch(), DEBOUNCE_MS);
105
105
  };
106
+ const BATCH_TIMEOUT_MS = 120000;
106
107
  const processBatch = () => __awaiter(this, void 0, void 0, function* () {
107
108
  var _a;
108
109
  if (closed || processing || pending.size === 0)
109
110
  return;
110
111
  processing = true;
112
+ const batchTimeout = setTimeout(() => {
113
+ if (processing) {
114
+ console.error("[watch] Batch processing timed out after 120s, resetting");
115
+ processing = false;
116
+ }
117
+ }, BATCH_TIMEOUT_MS);
111
118
  const batch = new Map(pending);
112
119
  pending.clear();
113
120
  (0, logger_1.log)("watch", `Processing ${batch.size} changed files`);
@@ -209,39 +216,6 @@ function startWatcher(opts) {
209
216
  finally {
210
217
  yield lock.release();
211
218
  }
212
- // Summarize new/changed chunks outside the lock (sequential, no GPU contention)
213
- if (changedIds.length > 0) {
214
- try {
215
- const table = yield vectorDb.ensureTable();
216
- for (const id of changedIds) {
217
- const escaped = (0, filter_builder_1.escapeSqlString)(id);
218
- const rows = yield table
219
- .query()
220
- .select(["id", "path", "content"])
221
- .where(`id = '${escaped}'`)
222
- .limit(1)
223
- .toArray();
224
- if (rows.length === 0)
225
- continue;
226
- const r = rows[0];
227
- const lang = path.extname(String(r.path || "")).replace(/^\./, "") ||
228
- "unknown";
229
- const summaries = yield (0, llm_client_1.summarizeChunks)([
230
- {
231
- code: String(r.content || ""),
232
- language: lang,
233
- file: String(r.path || ""),
234
- },
235
- ]);
236
- if (summaries === null || summaries === void 0 ? void 0 : summaries[0]) {
237
- yield vectorDb.updateRows([id], "summary", [summaries[0]]);
238
- }
239
- }
240
- }
241
- catch (_b) {
242
- // Summarizer unavailable — skip silently
243
- }
244
- }
245
219
  if (reindexed > 0) {
246
220
  const duration = Date.now() - start;
247
221
  onReindex === null || onReindex === void 0 ? void 0 : onReindex(reindexed, duration);
@@ -280,12 +254,48 @@ function startWatcher(opts) {
280
254
  }
281
255
  }
282
256
  finally {
257
+ clearTimeout(batchTimeout);
283
258
  processing = false;
284
259
  // Process any events that came in while we were processing
285
260
  if (pending.size > 0) {
286
261
  scheduleBatch();
287
262
  }
288
263
  }
264
+ // Fire-and-forget summarization — doesn't block the next batch
265
+ if (changedIds.length > 0) {
266
+ (() => __awaiter(this, void 0, void 0, function* () {
267
+ try {
268
+ const table = yield vectorDb.ensureTable();
269
+ for (const id of changedIds) {
270
+ const escaped = (0, filter_builder_1.escapeSqlString)(id);
271
+ const rows = yield table
272
+ .query()
273
+ .select(["id", "path", "content"])
274
+ .where(`id = '${escaped}'`)
275
+ .limit(1)
276
+ .toArray();
277
+ if (rows.length === 0)
278
+ continue;
279
+ const r = rows[0];
280
+ const lang = path.extname(String(r.path || "")).replace(/^\./, "") ||
281
+ "unknown";
282
+ const summaries = yield (0, llm_client_1.summarizeChunks)([
283
+ {
284
+ code: String(r.content || ""),
285
+ language: lang,
286
+ file: String(r.path || ""),
287
+ },
288
+ ]);
289
+ if (summaries === null || summaries === void 0 ? void 0 : summaries[0]) {
290
+ yield vectorDb.updateRows([id], "summary", [summaries[0]]);
291
+ }
292
+ }
293
+ }
294
+ catch (_a) {
295
+ // Summarizer unavailable — skip
296
+ }
297
+ }))();
298
+ }
289
299
  });
290
300
  const onFileEvent = (event, absPath) => {
291
301
  if (closed)
@@ -304,7 +314,7 @@ function startWatcher(opts) {
304
314
  watcher.on("unlink", (p) => onFileEvent("unlink", p));
305
315
  // Periodic FTS rebuild
306
316
  const ftsInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
307
- if (closed)
317
+ if (closed || processing)
308
318
  return;
309
319
  try {
310
320
  yield vectorDb.createFTSIndex();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.29",
3
+ "version": "0.7.31",
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.29",
3
+ "version": "0.7.31",
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",