@zvec/zvec-grep 0.1.3 → 0.1.5

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 (135) hide show
  1. package/README.md +127 -42
  2. package/README_CN.md +124 -42
  3. package/dist/cli/args.d.ts +3 -0
  4. package/dist/cli/args.js +490 -192
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/commands.d.ts +3 -1
  7. package/dist/cli/commands.js +590 -56
  8. package/dist/cli/commands.js.map +1 -1
  9. package/dist/cli/errors.js +6 -5
  10. package/dist/cli/errors.js.map +1 -1
  11. package/dist/cli/format/context.js +33 -29
  12. package/dist/cli/format/context.js.map +1 -1
  13. package/dist/cli/format/debug.js +12 -14
  14. package/dist/cli/format/debug.js.map +1 -1
  15. package/dist/cli/format/highlight.js.map +1 -1
  16. package/dist/cli/format/progress.js +8 -3
  17. package/dist/cli/format/progress.js.map +1 -1
  18. package/dist/cli/format/range.js.map +1 -1
  19. package/dist/cli/format/status.js +88 -26
  20. package/dist/cli/format/status.js.map +1 -1
  21. package/dist/cli/help.d.ts +1 -1
  22. package/dist/cli/help.js +170 -85
  23. package/dist/cli/help.js.map +1 -1
  24. package/dist/cli/index.js +4 -4
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/mcp.d.ts +2 -0
  27. package/dist/cli/mcp.js +354 -0
  28. package/dist/cli/mcp.js.map +1 -0
  29. package/dist/cli/types.d.ts +22 -9
  30. package/dist/cli/types.js.map +1 -1
  31. package/dist/cli/version.js +3 -1
  32. package/dist/cli/version.js.map +1 -1
  33. package/dist/engine/collection/index.js +15 -15
  34. package/dist/engine/collection/index.js.map +1 -1
  35. package/dist/engine/config.d.ts +28 -0
  36. package/dist/engine/config.js +195 -0
  37. package/dist/engine/config.js.map +1 -0
  38. package/dist/engine/errors/details.js.map +1 -1
  39. package/dist/engine/errors/index.js.map +1 -1
  40. package/dist/engine/extraction/code/adapter.js.map +1 -1
  41. package/dist/engine/extraction/code/extractor.js +87 -73
  42. package/dist/engine/extraction/code/extractor.js.map +1 -1
  43. package/dist/engine/extraction/code/families/c-family.js +12 -6
  44. package/dist/engine/extraction/code/families/c-family.js.map +1 -1
  45. package/dist/engine/extraction/code/families/js-ts.js +24 -18
  46. package/dist/engine/extraction/code/families/js-ts.js.map +1 -1
  47. package/dist/engine/extraction/code/families/metadata.js +11 -8
  48. package/dist/engine/extraction/code/families/metadata.js.map +1 -1
  49. package/dist/engine/extraction/code/families/name-field.js +5 -5
  50. package/dist/engine/extraction/code/families/name-field.js.map +1 -1
  51. package/dist/engine/extraction/code/languages/c.js +1 -4
  52. package/dist/engine/extraction/code/languages/c.js.map +1 -1
  53. package/dist/engine/extraction/code/languages/cpp.js.map +1 -1
  54. package/dist/engine/extraction/code/languages/go.js +1 -3
  55. package/dist/engine/extraction/code/languages/go.js.map +1 -1
  56. package/dist/engine/extraction/code/languages/java.js.map +1 -1
  57. package/dist/engine/extraction/code/languages/javascript.js +1 -3
  58. package/dist/engine/extraction/code/languages/javascript.js.map +1 -1
  59. package/dist/engine/extraction/code/languages/python.js +5 -6
  60. package/dist/engine/extraction/code/languages/python.js.map +1 -1
  61. package/dist/engine/extraction/code/languages/rust.js +1 -5
  62. package/dist/engine/extraction/code/languages/rust.js.map +1 -1
  63. package/dist/engine/extraction/code/languages/typescript.js.map +1 -1
  64. package/dist/engine/extraction/ids.js.map +1 -1
  65. package/dist/engine/extraction/index.js +3 -3
  66. package/dist/engine/extraction/index.js.map +1 -1
  67. package/dist/engine/extraction/markdown/extractor.js +4 -2
  68. package/dist/engine/extraction/markdown/extractor.js.map +1 -1
  69. package/dist/engine/extraction/tree-sitter/grammar.js.map +1 -1
  70. package/dist/engine/extraction/tree-sitter/nodes.js +2 -1
  71. package/dist/engine/extraction/tree-sitter/nodes.js.map +1 -1
  72. package/dist/engine/extraction/tree-sitter/parser.js.map +1 -1
  73. package/dist/engine/files/file-type.js.map +1 -1
  74. package/dist/engine/models/catalog.js +7 -23
  75. package/dist/engine/models/catalog.js.map +1 -1
  76. package/dist/engine/models/embeddings.js +3 -3
  77. package/dist/engine/models/embeddings.js.map +1 -1
  78. package/dist/engine/models/factory.js.map +1 -1
  79. package/dist/engine/models/index.d.ts +1 -1
  80. package/dist/engine/models/index.js.map +1 -1
  81. package/dist/engine/models/providers/index.d.ts +1 -1
  82. package/dist/engine/models/providers/index.js +1 -1
  83. package/dist/engine/models/providers/index.js.map +1 -1
  84. package/dist/engine/models/providers/llama-cpp/embedding.js +49 -18
  85. package/dist/engine/models/providers/llama-cpp/embedding.js.map +1 -1
  86. package/dist/engine/models/providers/qwen/embedding.js +16 -11
  87. package/dist/engine/models/providers/qwen/embedding.js.map +1 -1
  88. package/dist/engine/models/providers/qwen/index.d.ts +1 -1
  89. package/dist/engine/models/providers/qwen/index.js +1 -1
  90. package/dist/engine/models/providers/qwen/index.js.map +1 -1
  91. package/dist/engine/pipeline/indexing/index.js +46 -33
  92. package/dist/engine/pipeline/indexing/index.js.map +1 -1
  93. package/dist/engine/pipeline/indexing/root-paths.js +10 -13
  94. package/dist/engine/pipeline/indexing/root-paths.js.map +1 -1
  95. package/dist/engine/pipeline/indexing/scanner/index.js +121 -46
  96. package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -1
  97. package/dist/engine/pipeline/search/index.js +68 -24
  98. package/dist/engine/pipeline/search/index.js.map +1 -1
  99. package/dist/engine/service/index.d.ts +1 -1
  100. package/dist/engine/service/lexical.d.ts +14 -15
  101. package/dist/engine/service/lexical.js +78 -114
  102. package/dist/engine/service/lexical.js.map +1 -1
  103. package/dist/engine/service/root.js +2 -2
  104. package/dist/engine/service/root.js.map +1 -1
  105. package/dist/engine/service/structure-enrichment.js +17 -11
  106. package/dist/engine/service/structure-enrichment.js.map +1 -1
  107. package/dist/engine/service/types.d.ts +39 -8
  108. package/dist/engine/service/zvec-grep.js +303 -160
  109. package/dist/engine/service/zvec-grep.js.map +1 -1
  110. package/dist/engine/storage/index.js.map +1 -1
  111. package/dist/engine/storage/zvec.js +49 -28
  112. package/dist/engine/storage/zvec.js.map +1 -1
  113. package/dist/engine/types.d.ts +14 -0
  114. package/dist/engine/types.js.map +1 -1
  115. package/dist/engine/utils/file-selection.d.ts +10 -0
  116. package/dist/engine/utils/file-selection.js +120 -0
  117. package/dist/engine/utils/file-selection.js.map +1 -0
  118. package/dist/engine/utils/glob.d.ts +3 -0
  119. package/dist/engine/utils/glob.js +116 -18
  120. package/dist/engine/utils/glob.js.map +1 -1
  121. package/dist/engine/utils/hash.js.map +1 -1
  122. package/dist/engine/utils/json.d.ts +6 -2
  123. package/dist/engine/utils/json.js +50 -12
  124. package/dist/engine/utils/json.js.map +1 -1
  125. package/dist/engine/utils/lock.js +6 -6
  126. package/dist/engine/utils/lock.js.map +1 -1
  127. package/dist/engine/utils/path.js +2 -2
  128. package/dist/engine/utils/path.js.map +1 -1
  129. package/dist/engine/utils/timing.js.map +1 -1
  130. package/dist/index.d.ts +3 -3
  131. package/dist/index.js +2 -2
  132. package/dist/index.js.map +1 -1
  133. package/package.json +34 -7
  134. package/skills/zvec-grep/SKILL.md +58 -48
  135. package/skills/zvec-grep/agents/openai.yaml +1 -1
@@ -1,18 +1,20 @@
1
- import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
2
2
  import { createHash } from "node:crypto";
3
3
  import { dirname, join, relative, resolve } from "node:path";
4
+ import { globalConfigPath, readGlobalConfig, } from "../config.js";
4
5
  import { Collection, CollectionRegistry, isCollectionIndexed, } from "../collection/index.js";
5
6
  import { collectionDetail, detail, EngineError, errorDetails, } from "../errors/index.js";
6
7
  import { createEmbeddingModel, createEmbeddingModelFromReference, } from "../models/index.js";
7
8
  import { ANONYMOUS_COLLECTION_NAME, anonymousCollectionPath, anonymousHome, anonymousIndexLocation, resolveZvecGrepRoot, } from "./root.js";
8
- import { runLexicalFallback, runRgSearch } from "./lexical.js";
9
- import { defaultHome } from "../utils/path.js";
10
- import { acquireReadWriteLock, assertNoWriteLock } from "../utils/lock.js";
9
+ import { runRgSearch } from "./lexical.js";
10
+ import { defaultHome, normalizePath } from "../utils/path.js";
11
+ import { acquireReadWriteLock, assertNoWriteLock, } from "../utils/lock.js";
11
12
  import { TimingCollector } from "../utils/timing.js";
12
13
  import { enrichLexicalItemsWithStructure } from "./structure-enrichment.js";
13
14
  const DEFAULT_CONTEXT_LIMIT = 10;
14
15
  const DEFAULT_CONTEXT_TOTAL_LIMIT = 30;
15
16
  const DEFAULT_LOCAL_EMBEDDING = "local/embeddinggemma-300m";
17
+ const MAX_RECOVERED_EMBEDDING_MODELS = 4;
16
18
  export async function createZvecGrep(options = {}) {
17
19
  return new ZvecGrepService(options);
18
20
  }
@@ -21,14 +23,14 @@ class ZvecGrepService {
21
23
  root;
22
24
  collections;
23
25
  embeddingModel;
24
- embeddingModelExplicit;
25
26
  recoveredEmbeddingModels = new Map();
27
+ retiredEmbeddingModels = new Set();
28
+ activeEmbeddingModelOperations = 0;
26
29
  closed = false;
27
30
  constructor(options) {
28
31
  this.options = options;
29
32
  this.root = resolveZvecGrepRoot(options.root);
30
- this.embeddingModelExplicit = hasExplicitEmbeddingModel(options);
31
- this.embeddingModel = resolveEmbeddingModel(options, this.root);
33
+ this.embeddingModel = options.embeddingModel;
32
34
  this.collections = {
33
35
  list: () => this.listCollections(),
34
36
  info: (name) => this.collectionInfo(name),
@@ -41,7 +43,7 @@ class ZvecGrepService {
41
43
  this.ensureOpen();
42
44
  const root = resolveZvecGrepRoot(options.root ?? this.root);
43
45
  const location = anonymousIndexLocation(root);
44
- return await withHomeWriteLock(location.home, options.rebuild ? "index.rebuild" : "index", async () => {
46
+ return await this.withEmbeddingModelOperation(() => withHomeWriteLock(location.home, options.rebuild ? "index.rebuild" : "index", async () => {
45
47
  const existing = readCollectionInfo(location.home, ANONYMOUS_COLLECTION_NAME);
46
48
  const embeddingModel = this.embeddingModelForIndex(existing, location.home, "index");
47
49
  const registry = new CollectionRegistry(location.home, embeddingModel);
@@ -50,13 +52,23 @@ class ZvecGrepService {
50
52
  resetPaths: options.resetPaths === true,
51
53
  includePaths: options.includePaths,
52
54
  excludePaths: options.excludePaths,
55
+ globs: options.globs,
56
+ insensitiveGlobs: options.insensitiveGlobs,
57
+ fileTypes: options.fileTypes,
58
+ excludedFileTypes: options.excludedFileTypes,
59
+ hidden: options.hidden,
60
+ noIgnore: options.noIgnore,
61
+ ignoreFiles: options.ignoreFiles,
62
+ maxDepth: options.maxDepth,
63
+ maxFileSizeBytes: options.maxFileSizeBytes,
64
+ follow: options.follow,
53
65
  });
54
66
  if (options.rebuild) {
55
67
  registry.remove(ANONYMOUS_COLLECTION_NAME);
56
68
  }
57
69
  const existingAfterRebuild = registry.get(ANONYMOUS_COLLECTION_NAME);
58
70
  if (isCollectionIndexed(existingAfterRebuild)) {
59
- assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg --index --rebuild");
71
+ assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg index --rebuild");
60
72
  }
61
73
  registry.prepareIndex(ANONYMOUS_COLLECTION_NAME, rootPaths, anonymousCollectionPath(root));
62
74
  return await registry.open(ANONYMOUS_COLLECTION_NAME).index({
@@ -68,6 +80,23 @@ class ZvecGrepService {
68
80
  finally {
69
81
  registry.close();
70
82
  }
83
+ }));
84
+ }
85
+ async dropIndex(options = {}) {
86
+ this.ensureOpen();
87
+ const root = resolveZvecGrepRoot(options.root ?? this.root);
88
+ const location = anonymousIndexLocation(root);
89
+ if (!readCollectionInfo(location.home, ANONYMOUS_COLLECTION_NAME)) {
90
+ return false;
91
+ }
92
+ return await withHomeWriteLock(location.home, "index.drop", async () => {
93
+ const registry = new CollectionRegistry(location.home, undefined);
94
+ try {
95
+ return registry.remove(ANONYMOUS_COLLECTION_NAME);
96
+ }
97
+ finally {
98
+ registry.close();
99
+ }
71
100
  });
72
101
  }
73
102
  async disableIndex(options = {}) {
@@ -87,9 +116,11 @@ class ZvecGrepService {
87
116
  }
88
117
  async context(options) {
89
118
  this.ensureOpen();
90
- const timings = new TimingCollector();
91
- const result = await timings.time("total", () => this.contextWithTimings(options, timings));
92
- return withContextTimings(result, timings);
119
+ return await this.withEmbeddingModelOperation(async () => {
120
+ const timings = new TimingCollector();
121
+ const result = await timings.time("total", () => this.contextWithTimings(options, timings));
122
+ return withContextTimings(result, timings);
123
+ });
93
124
  }
94
125
  async contextWithTimings(options, timings) {
95
126
  const request = normalizeContextRequest(options);
@@ -131,16 +162,16 @@ class ZvecGrepService {
131
162
  home: location.home,
132
163
  indexPath: location.collectionPath,
133
164
  source: "unindexed",
134
- suggestion: "zg --index or zg --rg",
165
+ suggestion: "zg index or zg query --rg",
135
166
  };
136
167
  }
137
168
  return await withHomeReadLock(nearest.location.home, "info", async () => {
138
169
  const registry = new CollectionRegistry(nearest.location.home, undefined, true);
139
170
  try {
140
171
  const collection = registry.get(ANONYMOUS_COLLECTION_NAME);
141
- const indexed = collection !== null
142
- && collection.indexPolicy !== "disabled"
143
- && isCollectionIndexed(collection);
172
+ const indexed = collection !== null &&
173
+ collection.indexPolicy !== "disabled" &&
174
+ isCollectionIndexed(collection);
144
175
  return {
145
176
  root: nearest.location.root,
146
177
  indexed,
@@ -149,7 +180,9 @@ class ZvecGrepService {
149
180
  indexPath: nearest.location.collectionPath,
150
181
  source: indexed ? "index" : "unindexed",
151
182
  collection: collection ?? undefined,
152
- status: indexed ? await registry.status(ANONYMOUS_COLLECTION_NAME) : null,
183
+ status: indexed
184
+ ? await registry.status(ANONYMOUS_COLLECTION_NAME)
185
+ : null,
153
186
  suggestion: anonymousInfoSuggestion(collection),
154
187
  };
155
188
  }
@@ -162,8 +195,10 @@ class ZvecGrepService {
162
195
  const models = new Set([
163
196
  ...(this.embeddingModel ? [this.embeddingModel] : []),
164
197
  ...this.recoveredEmbeddingModels.values(),
198
+ ...this.retiredEmbeddingModels,
165
199
  ]);
166
200
  this.recoveredEmbeddingModels.clear();
201
+ this.retiredEmbeddingModels.clear();
167
202
  for (const model of models) {
168
203
  await model.dispose();
169
204
  }
@@ -208,7 +243,7 @@ class ZvecGrepService {
208
243
  async indexCollection(name, paths, options = {}) {
209
244
  this.ensureOpen();
210
245
  const home = serviceHome(this.options);
211
- return await withHomeWriteLock(home, options.rebuild ? "collections.index.rebuild" : "collections.index", async () => {
246
+ return await this.withEmbeddingModelOperation(() => withHomeWriteLock(home, options.rebuild ? "collections.index.rebuild" : "collections.index", async () => {
212
247
  const existing = readCollectionInfo(home, name);
213
248
  const embeddingModel = this.embeddingModelForIndex(existing, home, "collections.index");
214
249
  const registry = this.createRegistry(false, embeddingModel);
@@ -222,13 +257,23 @@ class ZvecGrepService {
222
257
  resetPaths: options.resetPaths === true,
223
258
  includePaths: options.includePaths,
224
259
  excludePaths: options.excludePaths,
260
+ globs: options.globs,
261
+ insensitiveGlobs: options.insensitiveGlobs,
262
+ fileTypes: options.fileTypes,
263
+ excludedFileTypes: options.excludedFileTypes,
264
+ hidden: options.hidden,
265
+ noIgnore: options.noIgnore,
266
+ ignoreFiles: options.ignoreFiles,
267
+ maxDepth: options.maxDepth,
268
+ maxFileSizeBytes: options.maxFileSizeBytes,
269
+ follow: options.follow,
225
270
  });
226
271
  if (options.rebuild) {
227
272
  registry.remove(name);
228
273
  }
229
274
  const existingAfterRebuild = registry.get(name);
230
275
  if (isCollectionIndexed(existingAfterRebuild)) {
231
- assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg --collections index <name> --rebuild");
276
+ assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg collections index <name> --rebuild");
232
277
  }
233
278
  registry.prepareIndex(name, rootPaths);
234
279
  return await registry.open(name).index({
@@ -239,7 +284,7 @@ class ZvecGrepService {
239
284
  finally {
240
285
  registry.close();
241
286
  }
242
- });
287
+ }));
243
288
  }
244
289
  async removeCollection(name) {
245
290
  this.ensureOpen();
@@ -305,7 +350,7 @@ class ZvecGrepService {
305
350
  return;
306
351
  }
307
352
  const embeddingModel = this.embeddingModelForIndex(existing, location.home, "context.refresh");
308
- assertCollectionEmbeddingMatchesCurrentModel(existing, embeddingModel, "zg --index --rebuild");
353
+ assertCollectionEmbeddingMatchesCurrentModel(existing, embeddingModel, "zg index --rebuild");
309
354
  const registry = new CollectionRegistry(location.home, embeddingModel);
310
355
  try {
311
356
  const result = await registry.open(ANONYMOUS_COLLECTION_NAME).index({
@@ -332,7 +377,8 @@ class ZvecGrepService {
332
377
  });
333
378
  }
334
379
  const collection = this.openCollectionForSearch(info, request, registry.home);
335
- const root = collection.info.rootPaths[0]?.absolutePath ?? resolveZvecGrepRoot(options.root ?? this.root);
380
+ const root = collection.info.rootPaths[0]?.absolutePath ??
381
+ resolveZvecGrepRoot(options.root ?? this.root);
336
382
  try {
337
383
  return await this.contextFromCollection({
338
384
  root,
@@ -341,7 +387,6 @@ class ZvecGrepService {
341
387
  anonymous: false,
342
388
  options: {
343
389
  ...options,
344
- fallback: "disabled",
345
390
  },
346
391
  timings,
347
392
  });
@@ -357,8 +402,11 @@ class ZvecGrepService {
357
402
  }
358
403
  async contextFromCollection(input) {
359
404
  const searches = [];
360
- const limit = contextGroupLimit(input.options.limit, input.request.groups.length);
361
- for (const group of input.request.groups) {
405
+ const groups = input.options.fuse
406
+ ? [{ routes: input.request.routes }]
407
+ : input.request.groups;
408
+ const limit = contextGroupLimit(input.options.limit, groups.length);
409
+ for (const group of groups) {
362
410
  const search = await input.collection.searchPlan({
363
411
  routes: group.routes,
364
412
  limit,
@@ -367,6 +415,10 @@ class ZvecGrepService {
367
415
  symbolTypes: input.options.symbolTypes,
368
416
  includePaths: input.options.includePaths,
369
417
  excludePaths: input.options.excludePaths,
418
+ globs: input.options.globs,
419
+ insensitiveGlobs: input.options.insensitiveGlobs,
420
+ fileTypes: input.options.fileTypes,
421
+ excludedFileTypes: input.options.excludedFileTypes,
370
422
  modifiedAfter: input.options.modifiedAfter,
371
423
  modifiedBefore: input.options.modifiedBefore,
372
424
  });
@@ -395,45 +447,26 @@ class ZvecGrepService {
395
447
  },
396
448
  };
397
449
  }
398
- async contextFromLexicalFallback(root, request, options, timings) {
399
- const fallbackResults = [];
400
- const limit = contextGroupLimit(options.limit, request.groups.length);
401
- for (const query of request.fallbackQueries) {
402
- fallbackResults.push(await timings.time("fallback_search", () => runLexicalFallback({
403
- root,
404
- query,
405
- limit,
406
- includePaths: options.includePaths,
407
- excludePaths: options.excludePaths,
408
- modifiedAfter: options.modifiedAfter,
409
- modifiedBefore: options.modifiedBefore,
410
- rgOptions: options.rgOptions,
411
- })));
412
- }
413
- const items = dedupeAndRerankContextItems(fallbackResults.flatMap((fallback) => fallback.items));
414
- const diagnostics = mergeFallbackDiagnostics(fallbackResults);
415
- return {
416
- query: request.displayQuery,
417
- root,
418
- source: "lexical_fallback",
419
- coverage: diagnostics.truncated ? "lexical_truncated" : "lexical_exhaustive",
420
- items,
421
- diagnostics: {
422
- emptyReason: items.length === 0 ? "no_matches" : undefined,
423
- fallback: diagnostics,
424
- },
425
- };
426
- }
427
450
  async contextFromRg(root, request, options, timings) {
428
451
  let rgResult;
429
452
  try {
430
453
  rgResult = await timings.time("rg_search", () => runRgSearch({
431
454
  root,
432
- patterns: request.fallbackQueries,
455
+ patterns: request.rgPatterns,
433
456
  paths: options.rgPaths,
434
457
  limit: options.limit,
435
458
  includePaths: options.includePaths,
436
459
  excludePaths: options.excludePaths,
460
+ globs: options.globs,
461
+ insensitiveGlobs: options.insensitiveGlobs,
462
+ fileTypes: options.fileTypes,
463
+ excludedFileTypes: options.excludedFileTypes,
464
+ hidden: options.hidden,
465
+ noIgnore: options.noIgnore,
466
+ ignoreFiles: options.ignoreFiles,
467
+ maxDepth: options.maxDepth,
468
+ maxFileSizeBytes: options.maxFileSizeBytes,
469
+ follow: options.follow,
437
470
  modifiedAfter: options.modifiedAfter,
438
471
  modifiedBefore: options.modifiedBefore,
439
472
  rgOptions: options.rgOptions,
@@ -442,27 +475,24 @@ class ZvecGrepService {
442
475
  catch (cause) {
443
476
  throw new EngineError("Search failed", {
444
477
  code: "ZVEC_GREP.ENGINE.SEARCH.FAILED",
445
- context: errorDetails([
446
- detail("source", "rg"),
447
- detail("root", root),
448
- ]),
478
+ context: errorDetails([detail("source", "rg"), detail("root", root)]),
449
479
  cause,
450
480
  });
451
481
  }
452
482
  const structuralEnrichment = await timings.time("structure_enrichment", () => enrichLexicalItemsWithStructure(root, rgResult.items));
453
483
  const items = dedupeAndRerankContextItems(structuralEnrichment.items);
454
- const emptyReason = items.length === 0
455
- ? rgEmptyReason(rgResult.diagnostics)
456
- : undefined;
484
+ const emptyReason = items.length === 0 ? rgEmptyReason(rgResult.diagnostics) : undefined;
457
485
  return {
458
486
  query: request.displayQuery,
459
487
  root,
460
488
  source: "rg",
461
- coverage: rgResult.diagnostics.truncated ? "lexical_truncated" : "lexical_exhaustive",
489
+ coverage: rgResult.diagnostics.truncated
490
+ ? "rg_truncated"
491
+ : "rg_exhaustive",
462
492
  items,
463
493
  diagnostics: {
464
494
  emptyReason,
465
- fallback: rgResult.diagnostics,
495
+ rg: rgResult.diagnostics,
466
496
  structure: structuralEnrichment.diagnostics,
467
497
  },
468
498
  };
@@ -483,31 +513,96 @@ class ZvecGrepService {
483
513
  return this.recoverEmbeddingModel(schema, registryHome);
484
514
  }
485
515
  embeddingModelForIndex(existing, registryHome, operation) {
486
- if (isCollectionIndexed(existing) && !this.embeddingModelExplicit) {
516
+ if (isCollectionIndexed(existing) &&
517
+ !this.embeddingModel &&
518
+ !this.options.embedding) {
487
519
  return this.recoverEmbeddingModel(existing.embedding, registryHome);
488
520
  }
489
- return this.requireEmbeddingModel(operation);
521
+ const reference = this.options.embedding ??
522
+ (this.options.defaultEmbedding === true
523
+ ? DEFAULT_LOCAL_EMBEDDING
524
+ : undefined);
525
+ return (this.embeddingModel ??
526
+ (reference
527
+ ? this.embeddingModelFromReference(reference, registryHome)
528
+ : undefined) ??
529
+ this.configuredEmbeddingModel(registryHome) ??
530
+ this.requireEmbeddingModel(operation));
490
531
  }
491
532
  recoverEmbeddingModel(schema, registryHome) {
492
- const key = `${schema.provider}/${schema.model}/${modelCacheDir(this.options, this.root, registryHome)}`;
533
+ const config = readGlobalConfig();
534
+ const options = providerOptions(this.options, this.root, registryHome, schema.provider, config);
535
+ const key = `${schema.provider}/${schema.model}/${providerOptionsFingerprint(options)}`;
536
+ return this.cachedEmbeddingModel(key, () => createEmbeddingModel({
537
+ provider: schema.provider,
538
+ model: schema.model,
539
+ }, options));
540
+ }
541
+ configuredEmbeddingModel(registryHome) {
542
+ const config = readGlobalConfig();
543
+ const reference = config.defaults?.embedding;
544
+ if (!reference) {
545
+ return undefined;
546
+ }
547
+ return this.embeddingModelFromReference(reference, registryHome, config);
548
+ }
549
+ embeddingModelFromReference(reference, registryHome, config = readGlobalConfig()) {
550
+ const provider = providerFromReference(reference);
551
+ const options = providerOptions(this.options, this.root, registryHome, provider, config);
552
+ const key = `configured/${reference}/${providerOptionsFingerprint(options)}`;
553
+ return this.cachedEmbeddingModel(key, () => createEmbeddingModelFromReference(reference, options));
554
+ }
555
+ cachedEmbeddingModel(key, create) {
493
556
  const cached = this.recoveredEmbeddingModels.get(key);
494
557
  if (cached) {
558
+ this.recoveredEmbeddingModels.delete(key);
559
+ this.recoveredEmbeddingModels.set(key, cached);
495
560
  return cached;
496
561
  }
497
- const model = createEmbeddingModel({
498
- provider: schema.provider,
499
- model: schema.model,
500
- }, providerOptions(this.options, this.root, registryHome));
562
+ const model = create();
501
563
  this.recoveredEmbeddingModels.set(key, model);
564
+ this.trimRecoveredEmbeddingModels();
502
565
  return model;
503
566
  }
567
+ trimRecoveredEmbeddingModels() {
568
+ while (this.recoveredEmbeddingModels.size > MAX_RECOVERED_EMBEDDING_MODELS) {
569
+ const oldestKey = this.recoveredEmbeddingModels.keys().next().value;
570
+ if (oldestKey === undefined) {
571
+ return;
572
+ }
573
+ const model = this.recoveredEmbeddingModels.get(oldestKey);
574
+ this.recoveredEmbeddingModels.delete(oldestKey);
575
+ if (model) {
576
+ this.retiredEmbeddingModels.add(model);
577
+ }
578
+ }
579
+ }
580
+ async withEmbeddingModelOperation(operation) {
581
+ this.activeEmbeddingModelOperations += 1;
582
+ try {
583
+ return await operation();
584
+ }
585
+ finally {
586
+ this.activeEmbeddingModelOperations -= 1;
587
+ if (this.activeEmbeddingModelOperations === 0) {
588
+ await this.disposeRetiredEmbeddingModels();
589
+ }
590
+ }
591
+ }
592
+ async disposeRetiredEmbeddingModels() {
593
+ const models = [...this.retiredEmbeddingModels];
594
+ this.retiredEmbeddingModels.clear();
595
+ for (const model of models) {
596
+ await model.dispose();
597
+ }
598
+ }
504
599
  requireEmbeddingModel(operation) {
505
600
  if (!this.embeddingModel) {
506
601
  throw new EngineError("zvec-grep operation requires an embedding model", {
507
602
  code: "ZVEC_GREP.ENGINE.SERVICE.EMBEDDING_MODEL_REQUIRED",
508
603
  context: errorDetails([
509
604
  detail("operation", operation),
510
- detail("hint", 'Pass "--embedding <model>" or set ZVEC_GREP_EMBEDDING. Existing indexes can rerun --index without --embedding to reuse the stored schema.'),
605
+ detail("hint", `Pass "--embedding <model>", set ZVEC_GREP_EMBEDDING, or configure defaults.embedding in ${globalConfigPath()}. Existing indexes can run "zg index" without --embedding to reuse the stored schema.`),
511
606
  detail("examples", "local/embeddinggemma-300m, qwen/text-embedding-v4"),
512
607
  ]),
513
608
  });
@@ -551,11 +646,11 @@ async function collectionNeedsRefresh(home, collectionName) {
551
646
  }
552
647
  }
553
648
  function collectionIndexStatusNeedsRefresh(status) {
554
- return status.filesAdded > 0
555
- || status.filesModified > 0
556
- || status.filesDeleted > 0
557
- || status.filesPending > 0
558
- || status.filesFailed > 0;
649
+ return (status.filesAdded > 0 ||
650
+ status.filesModified > 0 ||
651
+ status.filesDeleted > 0 ||
652
+ status.filesPending > 0 ||
653
+ status.filesFailed > 0);
559
654
  }
560
655
  function acquireHomeLock(home, mode, operation) {
561
656
  return acquireReadWriteLock(homeLockPath(home), mode, { operation });
@@ -581,9 +676,10 @@ function serviceHome(options) {
581
676
  return options.home ?? defaultHome();
582
677
  }
583
678
  function rgEmptyReason(diagnostics) {
584
- return diagnostics.missingPaths && diagnostics.missingPaths.length > 0
585
- && diagnostics.searchedPaths
586
- && diagnostics.searchedPaths.length === 0
679
+ return diagnostics.missingPaths &&
680
+ diagnostics.missingPaths.length > 0 &&
681
+ diagnostics.searchedPaths &&
682
+ diagnostics.searchedPaths.length === 0
587
683
  ? "no_searchable_files"
588
684
  : "no_matches";
589
685
  }
@@ -617,13 +713,13 @@ function findNearestAnonymousCollection(start) {
617
713
  }
618
714
  function anonymousInfoSuggestion(collection) {
619
715
  if (!collection) {
620
- return "zg --index or zg --rg";
716
+ return "zg index or zg query --rg";
621
717
  }
622
718
  if (collection.indexPolicy === "disabled") {
623
- return "zg --rg";
719
+ return "zg query --rg";
624
720
  }
625
721
  if (!isCollectionIndexed(collection)) {
626
- return "zg --index";
722
+ return "zg index";
627
723
  }
628
724
  return undefined;
629
725
  }
@@ -634,11 +730,11 @@ function anonymousIndexMissingError(root, policy) {
634
730
  detail("root", root),
635
731
  detail("policy", policy),
636
732
  detail("hint", policy === "undecided"
637
- ? "Ask the user whether to build an index with zg --index, or use zg --rg for no-index search."
638
- : "Run zg --index to build the enabled workspace index, or use zg --rg for no-index search."),
733
+ ? "Ask the user whether to build an index with zg index, or use zg query --rg for no-index search."
734
+ : "Run zg index to build the enabled workspace index, or use zg query --rg for no-index search."),
639
735
  detail("agent_prompt", policy === "undecided"
640
- ? "Ask the user whether this workspace should be indexed. If yes, run zg --index --embedding <model> with appropriate include/exclude filters; if no, run zg --disable-index. For immediate no-index search, use zg --rg."
641
- : "This workspace is marked index-enabled but has no built index. Ask before running zg --index if an embedding model or cost is involved; otherwise use zg --rg for immediate no-index search."),
736
+ ? "Ask the user whether this workspace should be indexed. If yes, run zg index --embedding <model> with appropriate -g/--glob and -t/--type filters; otherwise use zg query --rg for immediate no-index search."
737
+ : "This workspace is marked index-enabled but has no built index. Ask before running zg index if an embedding model or cost is involved; otherwise use zg query --rg for immediate no-index search."),
642
738
  ]),
643
739
  });
644
740
  }
@@ -648,23 +744,66 @@ function anonymousIndexDisabledError(root) {
648
744
  context: errorDetails([
649
745
  detail("root", root),
650
746
  detail("policy", "disabled"),
651
- detail("hint", "Use zg --rg for no-index search. Run zg --index only if the user explicitly decides to index this workspace."),
747
+ detail("hint", "Use zg query --rg for no-index search. Run zg index only if the user explicitly decides to index this workspace."),
652
748
  detail("agent_action", "do_not_build_index"),
653
749
  ]),
654
750
  });
655
751
  }
656
752
  function resolveIndexRootPaths(existing, requested, fallbackRoot, options) {
657
753
  let rootPaths = requested
658
- ?? existing?.rootPaths
659
- ?? [fallbackRoot];
754
+ ? inheritRequestedRootPathSettings(requested, existing?.rootPaths)
755
+ : (existing?.rootPaths ?? [fallbackRoot]);
660
756
  if (options.resetPaths) {
661
757
  rootPaths = rootPaths.map(resetRootPathFilters);
662
758
  }
663
- if (options.includePaths !== undefined || options.excludePaths !== undefined) {
664
- rootPaths = rootPaths.map((rootPath) => applyRootPathFilterOverrides(rootPath, options.includePaths, options.excludePaths));
759
+ if (options.includePaths !== undefined ||
760
+ options.excludePaths !== undefined ||
761
+ options.globs !== undefined ||
762
+ options.insensitiveGlobs !== undefined ||
763
+ options.fileTypes !== undefined ||
764
+ options.excludedFileTypes !== undefined ||
765
+ options.hidden !== undefined ||
766
+ options.noIgnore !== undefined ||
767
+ options.ignoreFiles !== undefined ||
768
+ options.maxDepth !== undefined ||
769
+ options.maxFileSizeBytes !== undefined ||
770
+ options.follow !== undefined) {
771
+ rootPaths = rootPaths.map((rootPath) => applyRootPathOverrides(rootPath, options));
665
772
  }
666
773
  return rootPaths;
667
774
  }
775
+ function inheritRequestedRootPathSettings(requested, existing) {
776
+ if (!existing?.length) {
777
+ return requested;
778
+ }
779
+ return requested.map((rootPath) => {
780
+ const absolutePath = normalizePath(typeof rootPath === "string" ? rootPath : rootPath.absolutePath);
781
+ const inherited = existing.find((candidate) => normalizePath(candidate.absolutePath) === absolutePath);
782
+ if (!inherited) {
783
+ return rootPath;
784
+ }
785
+ if (typeof rootPath === "string") {
786
+ return { ...inherited, absolutePath };
787
+ }
788
+ return {
789
+ ...inherited,
790
+ ...rootPath,
791
+ absolutePath,
792
+ include: rootPath.include ?? inherited.include,
793
+ exclude: rootPath.exclude ?? inherited.exclude,
794
+ globs: rootPath.globs ?? inherited.globs,
795
+ insensitiveGlobs: rootPath.insensitiveGlobs ?? inherited.insensitiveGlobs,
796
+ fileTypes: rootPath.fileTypes ?? inherited.fileTypes,
797
+ excludedFileTypes: rootPath.excludedFileTypes ?? inherited.excludedFileTypes,
798
+ hidden: rootPath.hidden ?? inherited.hidden,
799
+ noIgnore: rootPath.noIgnore ?? inherited.noIgnore,
800
+ ignoreFiles: rootPath.ignoreFiles ?? inherited.ignoreFiles,
801
+ maxDepth: rootPath.maxDepth ?? inherited.maxDepth,
802
+ maxFileSizeBytes: rootPath.maxFileSizeBytes ?? inherited.maxFileSizeBytes,
803
+ follow: rootPath.follow ?? inherited.follow,
804
+ };
805
+ });
806
+ }
668
807
  function resetRootPathFilters(rootPath) {
669
808
  if (typeof rootPath === "string") {
670
809
  return rootPath;
@@ -674,7 +813,7 @@ function resetRootPathFilters(rootPath) {
674
813
  recursive: rootPath.recursive,
675
814
  };
676
815
  }
677
- function applyRootPathFilterOverrides(rootPath, include, exclude) {
816
+ function applyRootPathOverrides(rootPath, options) {
678
817
  const normalized = typeof rootPath === "string"
679
818
  ? {
680
819
  absolutePath: rootPath,
@@ -683,26 +822,20 @@ function applyRootPathFilterOverrides(rootPath, include, exclude) {
683
822
  : rootPath;
684
823
  return {
685
824
  ...normalized,
686
- include: include ?? normalized.include,
687
- exclude: exclude ?? normalized.exclude,
825
+ include: options.includePaths ?? normalized.include,
826
+ exclude: options.excludePaths ?? normalized.exclude,
827
+ globs: options.globs ?? normalized.globs,
828
+ insensitiveGlobs: options.insensitiveGlobs ?? normalized.insensitiveGlobs,
829
+ fileTypes: options.fileTypes ?? normalized.fileTypes,
830
+ excludedFileTypes: options.excludedFileTypes ?? normalized.excludedFileTypes,
831
+ hidden: options.hidden ?? normalized.hidden,
832
+ noIgnore: options.noIgnore ?? normalized.noIgnore,
833
+ ignoreFiles: options.ignoreFiles ?? normalized.ignoreFiles,
834
+ maxDepth: options.maxDepth ?? normalized.maxDepth,
835
+ maxFileSizeBytes: options.maxFileSizeBytes ?? normalized.maxFileSizeBytes,
836
+ follow: options.follow ?? normalized.follow,
688
837
  };
689
838
  }
690
- function resolveEmbeddingModel(options, root) {
691
- if (options.embeddingModel) {
692
- return options.embeddingModel;
693
- }
694
- if (options.embedding) {
695
- return createEmbeddingModelFromReference(options.embedding, providerOptions(options, root));
696
- }
697
- if (options.defaultEmbedding === true) {
698
- return createEmbeddingModelFromReference(DEFAULT_LOCAL_EMBEDDING, providerOptions(options, root));
699
- }
700
- return undefined;
701
- }
702
- function hasExplicitEmbeddingModel(options) {
703
- return options.embeddingModel !== undefined
704
- || options.embedding !== undefined;
705
- }
706
839
  function readCollectionInfo(home, name) {
707
840
  const registry = new CollectionRegistry(home, undefined, true);
708
841
  try {
@@ -720,28 +853,47 @@ function indexedEmbeddingSchema(info) {
720
853
  code: "ZVEC_GREP.ENGINE.SERVICE.INDEX_MISSING",
721
854
  context: errorDetails([
722
855
  collectionDetail(info.name),
723
- detail("hint", "Run zg --index to build this index."),
856
+ detail("hint", "Run zg index to build this index."),
724
857
  ]),
725
858
  });
726
859
  }
727
- function providerOptions(options, root, registryHome) {
860
+ function providerOptions(options, root, registryHome, provider, config = readGlobalConfig()) {
861
+ const providerConfig = provider ? config.providers?.[provider] : undefined;
728
862
  return {
729
- apiKey: options.apiKey ?? "",
730
- endpoint: options.endpoint,
731
- modelCacheDir: modelCacheDir(options, root, registryHome),
732
- llamaGpu: options.llamaGpu,
733
- embeddingParallelism: options.embeddingParallelism,
863
+ apiKey: options.apiKey ?? providerConfig?.apiKey ?? "",
864
+ endpoint: options.endpoint ?? providerConfig?.endpoint,
865
+ modelCacheDir: options.modelCacheDir ??
866
+ process.env.ZVEC_GREP_MODEL_CACHE ??
867
+ config.defaults?.modelCacheDir ??
868
+ modelCacheDir(options, root, registryHome),
869
+ llamaGpu: options.llamaGpu ?? config.defaults?.llamaGpu,
870
+ embeddingParallelism: options.embeddingParallelism ?? config.defaults?.embeddingParallelism,
734
871
  };
735
872
  }
873
+ function providerFromReference(reference) {
874
+ const separator = reference.indexOf("/");
875
+ return separator > 0 ? reference.slice(0, separator) : undefined;
876
+ }
877
+ function providerOptionsFingerprint(options) {
878
+ return createHash("sha256")
879
+ .update(JSON.stringify([
880
+ options.apiKey,
881
+ options.endpoint,
882
+ options.modelCacheDir,
883
+ options.llamaGpu,
884
+ options.embeddingParallelism,
885
+ ]))
886
+ .digest("hex");
887
+ }
736
888
  function assertCollectionEmbeddingMatchesCurrentModel(info, model, rebuildCommand) {
737
889
  if (!isCollectionIndexed(info)) {
738
890
  return;
739
891
  }
740
892
  const expected = info.embedding;
741
- const changed = expected.provider !== model.ref.provider
742
- || expected.model !== model.ref.model
743
- || expected.dimension !== model.dimension
744
- || expected.metric !== model.metric;
893
+ const changed = expected.provider !== model.ref.provider ||
894
+ expected.model !== model.ref.model ||
895
+ expected.dimension !== model.dimension ||
896
+ expected.metric !== model.metric;
745
897
  if (!changed) {
746
898
  return;
747
899
  }
@@ -808,29 +960,36 @@ function contextGroupLimit(limit, groupCount) {
808
960
  function normalizeContextRequest(options) {
809
961
  const primaryQueries = normalizePrimaryQueries(options.query, options.queries);
810
962
  const extraRoutes = normalizeContextRoutes(options.routes ?? []);
811
- if (primaryQueries.length === 0 && extraRoutes.length === 0) {
963
+ const hasPatternFiles = options.rg === true && (options.rgOptions?.patternFiles?.length ?? 0) > 0;
964
+ if (primaryQueries.length === 0 &&
965
+ extraRoutes.length === 0 &&
966
+ !hasPatternFiles) {
812
967
  throw new EngineError("zvec-grep context requires a non-empty query or route", {
813
968
  code: "ZVEC_GREP.ENGINE.SERVICE.EMPTY_QUERY",
814
969
  });
815
970
  }
816
971
  const groups = contextGroups(primaryQueries, extraRoutes);
817
972
  const routes = groups.flatMap((group) => group.routes);
818
- const fallbackQueries = groups.map((group) => group.fallbackQuery);
973
+ const rgPatterns = [
974
+ ...primaryQueries,
975
+ ...extraRoutes.map((route) => route.query),
976
+ ];
819
977
  const displayQuery = primaryQueries.length > 0
820
978
  ? primaryQueries.join(" | ")
821
- : extraRoutes.map((route) => route.query).join(" | ");
979
+ : extraRoutes.length > 0
980
+ ? extraRoutes.map((route) => route.query).join(" | ")
981
+ : options
982
+ .rgOptions.patternFiles.map((path) => `@${path}`)
983
+ .join(" | ");
822
984
  return {
823
985
  displayQuery,
824
- fallbackQueries,
986
+ rgPatterns,
825
987
  routes,
826
988
  groups,
827
989
  };
828
990
  }
829
991
  function normalizePrimaryQueries(query, queries) {
830
- return [
831
- query,
832
- ...(queries ?? []),
833
- ].flatMap((value) => {
992
+ return [query, ...(queries ?? [])].flatMap((value) => {
834
993
  const normalized = normalizeOptionalQuery(value);
835
994
  return normalized ? [normalized] : [];
836
995
  });
@@ -867,11 +1026,9 @@ function contextGroups(primaryQueries, extraRoutes) {
867
1026
  { mode: "fts", query },
868
1027
  { mode: "vector", query },
869
1028
  ],
870
- fallbackQuery: query,
871
1029
  })),
872
1030
  ...extraRoutes.map((route) => ({
873
1031
  routes: [route],
874
- fallbackQuery: route.query,
875
1032
  })),
876
1033
  ];
877
1034
  }
@@ -920,23 +1077,7 @@ function contextItemDedupeKey(item) {
920
1077
  if (item.entityId) {
921
1078
  return `entity:${item.entityId}`;
922
1079
  }
923
- return [
924
- "range",
925
- item.file.absolutePath,
926
- JSON.stringify(item.range),
927
- ].join(":");
928
- }
929
- function mergeFallbackDiagnostics(results) {
930
- const [first] = results;
931
- if (!first) {
932
- throw new EngineError("zvec-grep lexical fallback requires at least one query", {
933
- code: "ZVEC_GREP.ENGINE.SERVICE.EMPTY_QUERY",
934
- });
935
- }
936
- return {
937
- ...first.diagnostics,
938
- truncated: results.some((result) => result.diagnostics.truncated),
939
- };
1080
+ return ["range", item.file.absolutePath, JSON.stringify(item.range)].join(":");
940
1081
  }
941
1082
  function contextItemTarget(hit) {
942
1083
  const evidence = hit.evidence.find((item) => !item.isEntity);
@@ -979,7 +1120,8 @@ function contextItemOutline(hit, evidence) {
979
1120
  if (!isUsefulOutline(hit.entity.metadata, outline)) {
980
1121
  return undefined;
981
1122
  }
982
- if (evidence?.content.kind === "text" && evidence.content.text.trim() === outline) {
1123
+ if (evidence?.content.kind === "text" &&
1124
+ evidence.content.text.trim() === outline) {
983
1125
  return undefined;
984
1126
  }
985
1127
  return outline;
@@ -988,23 +1130,23 @@ function isUsefulOutline(metadata, outline) {
988
1130
  if (!metadata || metadata.kind !== "code") {
989
1131
  return false;
990
1132
  }
991
- return metadata.symbolType === "class"
992
- || metadata.symbolType === "interface"
993
- || metadata.symbolType === "module"
994
- || outline.includes("\ncalls:");
1133
+ return (metadata.symbolType === "class" ||
1134
+ metadata.symbolType === "interface" ||
1135
+ metadata.symbolType === "module" ||
1136
+ outline.includes("\ncalls:"));
995
1137
  }
996
1138
  function sameDisplayedContent(entity, evidence) {
997
- return JSON.stringify(entity.range) === JSON.stringify(evidence.range)
998
- && contentEquals(entity.content, evidence.content);
1139
+ return (JSON.stringify(entity.range) === JSON.stringify(evidence.range) &&
1140
+ contentEquals(entity.content, evidence.content));
999
1141
  }
1000
1142
  function contentEquals(left, right) {
1001
1143
  if (left.kind !== right.kind) {
1002
1144
  return false;
1003
1145
  }
1004
1146
  if (left.kind === "image") {
1005
- return right.kind === "image"
1006
- && left.format === right.format
1007
- && left.data.byteLength === right.data.byteLength;
1147
+ return (right.kind === "image" &&
1148
+ left.format === right.format &&
1149
+ left.data.byteLength === right.data.byteLength);
1008
1150
  }
1009
1151
  return right.kind === "text" && left.text === right.text;
1010
1152
  }
@@ -1020,7 +1162,8 @@ function fileFreshnessStatus(file) {
1020
1162
  if (file.indexStatus.indexedTime >= info.mtimeMs) {
1021
1163
  return "fresh";
1022
1164
  }
1023
- if (file.contentHash && sha256File(file.absolutePath) === file.contentHash) {
1165
+ if (file.contentHash &&
1166
+ sha256File(file.absolutePath) === file.contentHash) {
1024
1167
  return "fresh";
1025
1168
  }
1026
1169
  }