grepmax 0.26.9 → 0.26.11

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.
@@ -10,7 +10,7 @@
10
10
  "name": "grepmax",
11
11
  "source": "./plugins/grepmax",
12
12
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
13
- "version": "0.26.9",
13
+ "version": "0.26.11",
14
14
  "author": {
15
15
  "name": "Robert Owens",
16
16
  "email": "robowens@me.com"
@@ -261,9 +261,12 @@ exports.doctor = new commander_1.Command("doctor")
261
261
  const bloatRatio = logicalSize > 0 ? diskSize / logicalSize : 0;
262
262
  if (bloatRatio > 2.0)
263
263
  needsOptimize = true;
264
- if (numSmallFragments > 10)
264
+ // Track the compactor's own threshold. Warning (and worse, --fix forcing a
265
+ // full optimize) at a lower count than the compactor uses would re-trigger
266
+ // the whole-table rewrite that raising the threshold exists to avoid.
267
+ if (numSmallFragments > config_1.FRAGMENT_COMPACT_THRESHOLD)
265
268
  needsOptimize = true;
266
- if (versions.length > 50)
269
+ if (versions.length > config_1.VERSION_PRUNE_THRESHOLD)
267
270
  needsOptimize = true;
268
271
  // Disk space check
269
272
  let availBytes = 0;
@@ -384,14 +387,14 @@ exports.doctor = new commander_1.Command("doctor")
384
387
  console.log(`ok Storage: ${totalChunks.toLocaleString()} rows, ${formatSize(logicalSize)} logical, ${formatSize(diskSize)} disk`);
385
388
  }
386
389
  // Fragments
387
- if (numSmallFragments > 10) {
390
+ if (numSmallFragments > config_1.FRAGMENT_COMPACT_THRESHOLD) {
388
391
  console.log(`WARN Fragments: ${numFragments} total, ${numSmallFragments} small — needs compaction`);
389
392
  }
390
393
  else {
391
394
  console.log(`ok Fragments: ${numFragments} total, ${numSmallFragments} small`);
392
395
  }
393
396
  // Versions
394
- if (versions.length > 50) {
397
+ if (versions.length > config_1.VERSION_PRUNE_THRESHOLD) {
395
398
  console.log(`WARN Versions: ${versions.length} — pruning recommended`);
396
399
  }
397
400
  else {
package/dist/config.js CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.INDEXABLE_EXTENSIONS = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.REBUILD_COMMAND = exports.CHUNKER_VERSION_HISTORY = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
36
+ exports.INDEXABLE_EXTENSIONS = exports.VERSION_PRUNE_THRESHOLD = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.REBUILD_COMMAND = exports.CHUNKER_VERSION_HISTORY = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
37
37
  exports.describeChunkerGap = describeChunkerGap;
38
38
  exports.describeEmbeddingGap = describeEmbeddingGap;
39
39
  exports.describeSchemaDimGap = describeSchemaDimGap;
@@ -273,6 +273,14 @@ exports.DISK_LOW_BYTES = (() => {
273
273
  // Caveat: measured on the no-ANN path — revisit if GMAX_ANN=1 becomes default,
274
274
  // since an ANN index partitions independently of fragments.
275
275
  exports.FRAGMENT_COMPACT_THRESHOLD = 400;
276
+ // Version count that counts as anomalous rather than normal accumulation.
277
+ // Versions pile up with writes and are pruned by compaction, so this has to
278
+ // track FRAGMENT_COMPACT_THRESHOLD: historically ~160 versions accumulated per
279
+ // 52 fragments (~3 per fragment), so 400 fragments implies ~1200 versions
280
+ // between compactions as ordinary steady state. A threshold below that would
281
+ // fire on every healthy store — and make `doctor --fix` force the whole-table
282
+ // rewrite that the fragment threshold exists to avoid.
283
+ exports.VERSION_PRUNE_THRESHOLD = 2000;
276
284
  // Extensions we consider for indexing to avoid binary noise and improve relevance.
277
285
  exports.INDEXABLE_EXTENSIONS = new Set([
278
286
  ".ts",
@@ -635,7 +635,7 @@ class Daemon {
635
635
  * on every search to annotate partial-result responses (Phase 6).
636
636
  */
637
637
  indexState(root) {
638
- var _a, _b, _c;
638
+ var _a, _b, _c, _d;
639
639
  const processor = this.processors.get(root);
640
640
  const batchPending = (_a = processor === null || processor === void 0 ? void 0 : processor.progress.pendingFiles) !== null && _a !== void 0 ? _a : 0;
641
641
  const processing = (_b = processor === null || processor === void 0 ? void 0 : processor.progress.processing) !== null && _b !== void 0 ? _b : false;
@@ -648,9 +648,23 @@ class Daemon {
648
648
  if (fullIdx && fullIdx.total > 0) {
649
649
  pendingFiles = Math.max(pendingFiles, fullIdx.total - fullIdx.processed);
650
650
  }
651
+ // Distinguish a real backlog from a queue draining as cache hits. A full
652
+ // index genuinely has work outstanding; a post-FSEvents-drop catchup mostly
653
+ // re-verifies unchanged files. Only claim "verifying" with a real sample and
654
+ // no initial-index work pending.
655
+ const { recentFiles, recentReindexed } = (_d = processor === null || processor === void 0 ? void 0 : processor.progress) !== null && _d !== void 0 ? _d : {
656
+ recentFiles: 0,
657
+ recentReindexed: 0,
658
+ };
659
+ const verifying = !fullIdx &&
660
+ !initialPending &&
661
+ recentFiles >= 50 &&
662
+ recentReindexed === 0 &&
663
+ pendingFiles > 0;
651
664
  return {
652
665
  indexing: !!fullIdx || processing || batchPending > 0 || initialPending,
653
666
  pendingFiles,
667
+ verifying,
654
668
  };
655
669
  }
656
670
  search(payload, signal) {
@@ -76,6 +76,8 @@ class ProjectBatchProcessor {
76
76
  this.currentBatchAc = null;
77
77
  this.lastCorruptionLogMs = 0;
78
78
  this.policyChangedDuringBatch = false;
79
+ /** Rolling window of recent batch outcomes — see `progress`. */
80
+ this.recentBatches = [];
79
81
  this.projectRoot = opts.projectRoot;
80
82
  this.vectorDb = opts.vectorDb;
81
83
  this.metaCache = opts.metaCache;
@@ -129,12 +131,27 @@ class ProjectBatchProcessor {
129
131
  (_b = this.onActivity) === null || _b === void 0 ? void 0 : _b.call(this);
130
132
  this.scheduleBatch();
131
133
  }
132
- /** Live (re)index progress: files queued + whether a batch is running. */
134
+ /** Live (re)index progress: files queued + whether a batch is running.
135
+ *
136
+ * recentFiles/recentReindexed summarize the last few batches so callers can
137
+ * tell a real backlog from a queue draining as cache hits. A catchup after an
138
+ * FSEvents drop queues the whole project — on a large repo that is 10k+ files
139
+ * clearing at ~0.0s per 50-file batch with 0 reindexed, which is verification,
140
+ * not pending work. Without this, a large pendingFiles alone reads as "the
141
+ * index is far behind" and search callers caveat correct results. */
133
142
  get progress() {
143
+ let recentFiles = 0;
144
+ let recentReindexed = 0;
145
+ for (const b of this.recentBatches) {
146
+ recentFiles += b.files;
147
+ recentReindexed += b.reindexed;
148
+ }
134
149
  return {
135
150
  pendingFiles: this.pending.size,
136
151
  processing: this.processing,
137
152
  failedFiles: this.terminalFailures.size,
153
+ recentFiles,
154
+ recentReindexed,
138
155
  };
139
156
  }
140
157
  close() {
@@ -484,6 +501,10 @@ class ProjectBatchProcessor {
484
501
  if (reindexed > 0) {
485
502
  (_b = this.onReindex) === null || _b === void 0 ? void 0 : _b.call(this, reindexed, duration);
486
503
  }
504
+ this.recentBatches.push({ files: batch.size, reindexed });
505
+ if (this.recentBatches.length > ProjectBatchProcessor.RECENT_BATCH_WINDOW) {
506
+ this.recentBatches.shift();
507
+ }
487
508
  const remaining = this.pending.size;
488
509
  (0, logger_1.log)(this.wtag, `Batch complete: ${batch.size} files, ${reindexed} reindexed (${(duration / 1000).toFixed(1)}s)${remaining > 0 ? ` — ${remaining} remaining` : ""}`);
489
510
  for (const absPath of completed) {
@@ -609,3 +630,4 @@ class ProjectBatchProcessor {
609
630
  }
610
631
  }
611
632
  exports.ProjectBatchProcessor = ProjectBatchProcessor;
633
+ ProjectBatchProcessor.RECENT_BATCH_WINDOW = 5;
@@ -16,6 +16,16 @@ exports.formatIndexStateFooter = formatIndexStateFooter;
16
16
  function formatIndexStateFooter(state, opts) {
17
17
  if (!(state === null || state === void 0 ? void 0 : state.indexing))
18
18
  return null;
19
+ // Re-verification is not incompleteness. Saying "results may be incomplete"
20
+ // while a catchup re-checks thousands of unchanged files trains agents to
21
+ // distrust and retry correct answers, which is worse than saying nothing.
22
+ if (state.verifying) {
23
+ const n = state.pendingFiles;
24
+ if (opts.agent) {
25
+ return `[index: verifying ~${n} unchanged files · results current]`;
26
+ }
27
+ return `Index verifying ${n} unchanged file${n === 1 ? "" : "s"} — results are current.`;
28
+ }
19
29
  const count = state.pendingFiles > 0 ? `~${state.pendingFiles} files pending` : null;
20
30
  if (opts.agent) {
21
31
  const parts = ["index: syncing"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.26.9",
3
+ "version": "0.26.11",
4
4
  "author": "Robert Owens <78518764+reowens@users.noreply.github.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.26.9",
3
+ "version": "0.26.11",
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",