opencode-codebase-index 0.2.4 → 0.3.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.
package/dist/index.cjs CHANGED
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
491
491
  // path matching.
492
492
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
493
493
  // @returns {TestResult} true if a file is ignored
494
- test(path8, checkUnignored, mode) {
494
+ test(path9, checkUnignored, mode) {
495
495
  let ignored = false;
496
496
  let unignored = false;
497
497
  let matchedRule;
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
500
500
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
501
501
  return;
502
502
  }
503
- const matched = rule[mode].test(path8);
503
+ const matched = rule[mode].test(path9);
504
504
  if (!matched) {
505
505
  return;
506
506
  }
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
521
521
  var throwError = (message, Ctor) => {
522
522
  throw new Ctor(message);
523
523
  };
524
- var checkPath = (path8, originalPath, doThrow) => {
525
- if (!isString(path8)) {
524
+ var checkPath = (path9, originalPath, doThrow) => {
525
+ if (!isString(path9)) {
526
526
  return doThrow(
527
527
  `path must be a string, but got \`${originalPath}\``,
528
528
  TypeError
529
529
  );
530
530
  }
531
- if (!path8) {
531
+ if (!path9) {
532
532
  return doThrow(`path must not be empty`, TypeError);
533
533
  }
534
- if (checkPath.isNotRelative(path8)) {
534
+ if (checkPath.isNotRelative(path9)) {
535
535
  const r = "`path.relative()`d";
536
536
  return doThrow(
537
537
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
540
540
  }
541
541
  return true;
542
542
  };
543
- var isNotRelative = (path8) => REGEX_TEST_INVALID_PATH.test(path8);
543
+ var isNotRelative = (path9) => REGEX_TEST_INVALID_PATH.test(path9);
544
544
  checkPath.isNotRelative = isNotRelative;
545
545
  checkPath.convert = (p) => p;
546
546
  var Ignore2 = class {
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
570
570
  }
571
571
  // @returns {TestResult}
572
572
  _test(originalPath, cache, checkUnignored, slices) {
573
- const path8 = originalPath && checkPath.convert(originalPath);
573
+ const path9 = originalPath && checkPath.convert(originalPath);
574
574
  checkPath(
575
- path8,
575
+ path9,
576
576
  originalPath,
577
577
  this._strictPathCheck ? throwError : RETURN_FALSE
578
578
  );
579
- return this._t(path8, cache, checkUnignored, slices);
579
+ return this._t(path9, cache, checkUnignored, slices);
580
580
  }
581
- checkIgnore(path8) {
582
- if (!REGEX_TEST_TRAILING_SLASH.test(path8)) {
583
- return this.test(path8);
581
+ checkIgnore(path9) {
582
+ if (!REGEX_TEST_TRAILING_SLASH.test(path9)) {
583
+ return this.test(path9);
584
584
  }
585
- const slices = path8.split(SLASH2).filter(Boolean);
585
+ const slices = path9.split(SLASH2).filter(Boolean);
586
586
  slices.pop();
587
587
  if (slices.length) {
588
588
  const parent = this._t(
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
595
595
  return parent;
596
596
  }
597
597
  }
598
- return this._rules.test(path8, false, MODE_CHECK_IGNORE);
598
+ return this._rules.test(path9, false, MODE_CHECK_IGNORE);
599
599
  }
600
- _t(path8, cache, checkUnignored, slices) {
601
- if (path8 in cache) {
602
- return cache[path8];
600
+ _t(path9, cache, checkUnignored, slices) {
601
+ if (path9 in cache) {
602
+ return cache[path9];
603
603
  }
604
604
  if (!slices) {
605
- slices = path8.split(SLASH2).filter(Boolean);
605
+ slices = path9.split(SLASH2).filter(Boolean);
606
606
  }
607
607
  slices.pop();
608
608
  if (!slices.length) {
609
- return cache[path8] = this._rules.test(path8, checkUnignored, MODE_IGNORE);
609
+ return cache[path9] = this._rules.test(path9, checkUnignored, MODE_IGNORE);
610
610
  }
611
611
  const parent = this._t(
612
612
  slices.join(SLASH2) + SLASH2,
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
614
614
  checkUnignored,
615
615
  slices
616
616
  );
617
- return cache[path8] = parent.ignored ? parent : this._rules.test(path8, checkUnignored, MODE_IGNORE);
617
+ return cache[path9] = parent.ignored ? parent : this._rules.test(path9, checkUnignored, MODE_IGNORE);
618
618
  }
619
- ignores(path8) {
620
- return this._test(path8, this._ignoreCache, false).ignored;
619
+ ignores(path9) {
620
+ return this._test(path9, this._ignoreCache, false).ignored;
621
621
  }
622
622
  createFilter() {
623
- return (path8) => !this.ignores(path8);
623
+ return (path9) => !this.ignores(path9);
624
624
  }
625
625
  filter(paths) {
626
626
  return makeArray(paths).filter(this.createFilter());
627
627
  }
628
628
  // @returns {TestResult}
629
- test(path8) {
630
- return this._test(path8, this._testCache, true);
629
+ test(path9) {
630
+ return this._test(path9, this._testCache, true);
631
631
  }
632
632
  };
633
633
  var factory = (options) => new Ignore2(options);
634
- var isPathValid = (path8) => checkPath(path8 && checkPath.convert(path8), path8, RETURN_FALSE);
634
+ var isPathValid = (path9) => checkPath(path9 && checkPath.convert(path9), path9, RETURN_FALSE);
635
635
  var setupWindows = () => {
636
636
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
637
637
  checkPath.convert = makePosix;
638
638
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
639
- checkPath.isNotRelative = (path8) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path8) || isNotRelative(path8);
639
+ checkPath.isNotRelative = (path9) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path9) || isNotRelative(path9);
640
640
  };
641
641
  if (
642
642
  // Detect `process` so that it can run in browsers.
@@ -657,9 +657,10 @@ __export(index_exports, {
657
657
  default: () => index_default
658
658
  });
659
659
  module.exports = __toCommonJS(index_exports);
660
- var import_fs5 = require("fs");
661
- var path7 = __toESM(require("path"), 1);
660
+ var import_fs6 = require("fs");
661
+ var path8 = __toESM(require("path"), 1);
662
662
  var os3 = __toESM(require("os"), 1);
663
+ var import_url2 = require("url");
663
664
 
664
665
  // src/config/schema.ts
665
666
  var DEFAULT_INCLUDE = [
@@ -697,7 +698,10 @@ function getDefaultIndexingConfig() {
697
698
  maxChunksPerFile: 100,
698
699
  semanticOnly: false,
699
700
  retries: 3,
700
- retryDelayMs: 1e3
701
+ retryDelayMs: 1e3,
702
+ autoGc: true,
703
+ gcIntervalDays: 7,
704
+ gcOrphanThreshold: 100
701
705
  };
702
706
  }
703
707
  function getDefaultSearchConfig() {
@@ -732,7 +736,10 @@ function parseConfig(raw) {
732
736
  maxChunksPerFile: typeof rawIndexing.maxChunksPerFile === "number" ? Math.max(1, rawIndexing.maxChunksPerFile) : defaultIndexing.maxChunksPerFile,
733
737
  semanticOnly: typeof rawIndexing.semanticOnly === "boolean" ? rawIndexing.semanticOnly : defaultIndexing.semanticOnly,
734
738
  retries: typeof rawIndexing.retries === "number" ? rawIndexing.retries : defaultIndexing.retries,
735
- retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs
739
+ retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs,
740
+ autoGc: typeof rawIndexing.autoGc === "boolean" ? rawIndexing.autoGc : defaultIndexing.autoGc,
741
+ gcIntervalDays: typeof rawIndexing.gcIntervalDays === "number" ? Math.max(1, rawIndexing.gcIntervalDays) : defaultIndexing.gcIntervalDays,
742
+ gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold
736
743
  };
737
744
  const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
738
745
  const search = {
@@ -2071,34 +2078,36 @@ var GoogleEmbeddingProvider = class {
2071
2078
  };
2072
2079
  }
2073
2080
  async embedBatch(texts) {
2074
- const embeddings = [];
2075
- let totalTokens = 0;
2076
- for (const text of texts) {
2077
- const response = await fetch(
2078
- `${this.credentials.baseUrl}/models/${this.modelInfo.model}:embedContent?key=${this.credentials.apiKey}`,
2079
- {
2080
- method: "POST",
2081
- headers: {
2082
- "Content-Type": "application/json"
2083
- },
2084
- body: JSON.stringify({
2085
- content: {
2086
- parts: [{ text }]
2087
- }
2088
- })
2081
+ const results = await Promise.all(
2082
+ texts.map(async (text) => {
2083
+ const response = await fetch(
2084
+ `${this.credentials.baseUrl}/models/${this.modelInfo.model}:embedContent?key=${this.credentials.apiKey}`,
2085
+ {
2086
+ method: "POST",
2087
+ headers: {
2088
+ "Content-Type": "application/json"
2089
+ },
2090
+ body: JSON.stringify({
2091
+ content: {
2092
+ parts: [{ text }]
2093
+ }
2094
+ })
2095
+ }
2096
+ );
2097
+ if (!response.ok) {
2098
+ const error = await response.text();
2099
+ throw new Error(`Google embedding API error: ${response.status} - ${error}`);
2089
2100
  }
2090
- );
2091
- if (!response.ok) {
2092
- const error = await response.text();
2093
- throw new Error(`Google embedding API error: ${response.status} - ${error}`);
2094
- }
2095
- const data = await response.json();
2096
- embeddings.push(data.embedding.values);
2097
- totalTokens += Math.ceil(text.length / 4);
2098
- }
2101
+ const data = await response.json();
2102
+ return {
2103
+ embedding: data.embedding.values,
2104
+ tokensUsed: Math.ceil(text.length / 4)
2105
+ };
2106
+ })
2107
+ );
2099
2108
  return {
2100
- embeddings,
2101
- totalTokensUsed: totalTokens
2109
+ embeddings: results.map((r) => r.embedding),
2110
+ totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
2102
2111
  };
2103
2112
  }
2104
2113
  getModelInfo() {
@@ -2132,16 +2141,10 @@ var OllamaEmbeddingProvider = class {
2132
2141
  };
2133
2142
  }
2134
2143
  async embedBatch(texts) {
2135
- const embeddings = [];
2136
- let totalTokens = 0;
2137
- for (const text of texts) {
2138
- const result = await this.embed(text);
2139
- embeddings.push(result.embedding);
2140
- totalTokens += result.tokensUsed;
2141
- }
2144
+ const results = await Promise.all(texts.map((text) => this.embed(text)));
2142
2145
  return {
2143
- embeddings,
2144
- totalTokensUsed: totalTokens
2146
+ embeddings: results.map((r) => r.embedding),
2147
+ totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
2145
2148
  };
2146
2149
  }
2147
2150
  getModelInfo() {
@@ -2683,12 +2686,20 @@ var Database = class {
2683
2686
  upsertEmbedding(contentHash, embedding, chunkText, model) {
2684
2687
  this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
2685
2688
  }
2689
+ upsertEmbeddingsBatch(items) {
2690
+ if (items.length === 0) return;
2691
+ this.inner.upsertEmbeddingsBatch(items);
2692
+ }
2686
2693
  getMissingEmbeddings(contentHashes) {
2687
2694
  return this.inner.getMissingEmbeddings(contentHashes);
2688
2695
  }
2689
2696
  upsertChunk(chunk) {
2690
2697
  this.inner.upsertChunk(chunk);
2691
2698
  }
2699
+ upsertChunksBatch(chunks) {
2700
+ if (chunks.length === 0) return;
2701
+ this.inner.upsertChunksBatch(chunks);
2702
+ }
2692
2703
  getChunk(chunkId) {
2693
2704
  return this.inner.getChunk(chunkId) ?? null;
2694
2705
  }
@@ -2701,6 +2712,10 @@ var Database = class {
2701
2712
  addChunksToBranch(branch, chunkIds) {
2702
2713
  this.inner.addChunksToBranch(branch, chunkIds);
2703
2714
  }
2715
+ addChunksToBranchBatch(branch, chunkIds) {
2716
+ if (chunkIds.length === 0) return;
2717
+ this.inner.addChunksToBranchBatch(branch, chunkIds);
2718
+ }
2704
2719
  clearBranch(branch) {
2705
2720
  return this.inner.clearBranch(branch);
2706
2721
  }
@@ -2905,6 +2920,20 @@ var Indexer = class {
2905
2920
  });
2906
2921
  this.saveFailedBatches(existing);
2907
2922
  }
2923
+ getProviderRateLimits(provider) {
2924
+ switch (provider) {
2925
+ case "github-copilot":
2926
+ return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
2927
+ case "openai":
2928
+ return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
2929
+ case "google":
2930
+ return { concurrency: 5, intervalMs: 200, minRetryMs: 1e3, maxRetryMs: 3e4 };
2931
+ case "ollama":
2932
+ return { concurrency: 5, intervalMs: 0, minRetryMs: 500, maxRetryMs: 5e3 };
2933
+ default:
2934
+ return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
2935
+ }
2936
+ }
2908
2937
  async initialize() {
2909
2938
  this.detectedProvider = await detectEmbeddingProvider(this.config.embeddingProvider);
2910
2939
  if (!this.detectedProvider) {
@@ -2947,11 +2976,45 @@ var Indexer = class {
2947
2976
  this.currentBranch = "default";
2948
2977
  this.baseBranch = "default";
2949
2978
  }
2979
+ if (this.config.indexing.autoGc) {
2980
+ await this.maybeRunAutoGc();
2981
+ }
2982
+ }
2983
+ async maybeRunAutoGc() {
2984
+ if (!this.database) return;
2985
+ const lastGcTimestamp = this.database.getMetadata("lastGcTimestamp");
2986
+ const now = Date.now();
2987
+ const intervalMs = this.config.indexing.gcIntervalDays * 24 * 60 * 60 * 1e3;
2988
+ let shouldRunGc = false;
2989
+ if (!lastGcTimestamp) {
2990
+ shouldRunGc = true;
2991
+ } else {
2992
+ const lastGcTime = parseInt(lastGcTimestamp, 10);
2993
+ if (!isNaN(lastGcTime) && now - lastGcTime > intervalMs) {
2994
+ shouldRunGc = true;
2995
+ }
2996
+ }
2997
+ if (shouldRunGc) {
2998
+ await this.healthCheck();
2999
+ this.database.setMetadata("lastGcTimestamp", now.toString());
3000
+ }
3001
+ }
3002
+ async maybeRunOrphanGc() {
3003
+ if (!this.database) return;
3004
+ const stats = this.database.getStats();
3005
+ if (!stats) return;
3006
+ const orphanCount = stats.embeddingCount - stats.chunkCount;
3007
+ if (orphanCount > this.config.indexing.gcOrphanThreshold) {
3008
+ this.database.gcOrphanEmbeddings();
3009
+ this.database.gcOrphanChunks();
3010
+ this.database.setMetadata("lastGcTimestamp", Date.now().toString());
3011
+ }
2950
3012
  }
2951
3013
  migrateFromLegacyIndex() {
2952
3014
  if (!this.store || !this.database) return;
2953
3015
  const allMetadata = this.store.getAllMetadata();
2954
3016
  const chunkIds = [];
3017
+ const chunkDataBatch = [];
2955
3018
  for (const { key, metadata } of allMetadata) {
2956
3019
  const chunkData = {
2957
3020
  chunkId: key,
@@ -2963,10 +3026,13 @@ var Indexer = class {
2963
3026
  name: metadata.name,
2964
3027
  language: metadata.language
2965
3028
  };
2966
- this.database.upsertChunk(chunkData);
3029
+ chunkDataBatch.push(chunkData);
2967
3030
  chunkIds.push(key);
2968
3031
  }
2969
- this.database.addChunksToBranch(this.currentBranch || "default", chunkIds);
3032
+ if (chunkDataBatch.length > 0) {
3033
+ this.database.upsertChunksBatch(chunkDataBatch);
3034
+ }
3035
+ this.database.addChunksToBranchBatch(this.currentBranch || "default", chunkIds);
2970
3036
  }
2971
3037
  async ensureInitialized() {
2972
3038
  if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider || !this.database) {
@@ -3062,6 +3128,7 @@ var Indexer = class {
3062
3128
  }
3063
3129
  }
3064
3130
  }
3131
+ const chunkDataBatch = [];
3065
3132
  for (const parsed of parsedFiles) {
3066
3133
  currentFilePaths.add(parsed.path);
3067
3134
  if (parsed.chunks.length === 0) {
@@ -3089,7 +3156,7 @@ var Indexer = class {
3089
3156
  name: chunk.name,
3090
3157
  language: chunk.language
3091
3158
  };
3092
- database.upsertChunk(chunkData);
3159
+ chunkDataBatch.push(chunkData);
3093
3160
  if (existingChunks.get(id) === contentHash) {
3094
3161
  fileChunkCount++;
3095
3162
  continue;
@@ -3108,6 +3175,9 @@ var Indexer = class {
3108
3175
  fileChunkCount++;
3109
3176
  }
3110
3177
  }
3178
+ if (chunkDataBatch.length > 0) {
3179
+ database.upsertChunksBatch(chunkDataBatch);
3180
+ }
3111
3181
  let removedCount = 0;
3112
3182
  for (const [chunkId] of existingChunks) {
3113
3183
  if (!currentChunkIds.has(chunkId)) {
@@ -3121,7 +3191,7 @@ var Indexer = class {
3121
3191
  stats.removedChunks = removedCount;
3122
3192
  if (pendingChunks.length === 0 && removedCount === 0) {
3123
3193
  database.clearBranch(this.currentBranch);
3124
- database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
3194
+ database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
3125
3195
  this.fileHashCache = currentFileHashes;
3126
3196
  this.saveFileHashCache();
3127
3197
  stats.durationMs = Date.now() - startTime;
@@ -3136,7 +3206,7 @@ var Indexer = class {
3136
3206
  }
3137
3207
  if (pendingChunks.length === 0) {
3138
3208
  database.clearBranch(this.currentBranch);
3139
- database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
3209
+ database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
3140
3210
  store.save();
3141
3211
  invertedIndex.save();
3142
3212
  this.fileHashCache = currentFileHashes;
@@ -3172,7 +3242,12 @@ var Indexer = class {
3172
3242
  stats.indexedChunks++;
3173
3243
  }
3174
3244
  }
3175
- const queue = new PQueue({ concurrency: 1, interval: 4e3, intervalCap: 1 });
3245
+ const providerRateLimits = this.getProviderRateLimits(detectedProvider.provider);
3246
+ const queue = new PQueue({
3247
+ concurrency: providerRateLimits.concurrency,
3248
+ interval: providerRateLimits.intervalMs,
3249
+ intervalCap: providerRateLimits.concurrency
3250
+ });
3176
3251
  const dynamicBatches = createDynamicBatches(chunksNeedingEmbedding);
3177
3252
  let rateLimitBackoffMs = 0;
3178
3253
  for (const batch of dynamicBatches) {
@@ -3188,15 +3263,13 @@ var Indexer = class {
3188
3263
  },
3189
3264
  {
3190
3265
  retries: this.config.indexing.retries,
3191
- minTimeout: Math.max(this.config.indexing.retryDelayMs, 5e3),
3192
- // Minimum 5s between retries
3193
- maxTimeout: 6e4,
3194
- // Max 60s backoff
3266
+ minTimeout: Math.max(this.config.indexing.retryDelayMs, providerRateLimits.minRetryMs),
3267
+ maxTimeout: providerRateLimits.maxRetryMs,
3195
3268
  factor: 2,
3196
3269
  onFailedAttempt: (error) => {
3197
3270
  const message = getErrorMessage(error);
3198
3271
  if (isRateLimitError(error)) {
3199
- rateLimitBackoffMs = Math.min(6e4, (rateLimitBackoffMs || 5e3) * 2);
3272
+ rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
3200
3273
  console.error(
3201
3274
  `Rate limited (attempt ${error.attemptNumber}/${error.retriesLeft + error.attemptNumber}): waiting ${rateLimitBackoffMs / 1e3}s before retry...`
3202
3275
  );
@@ -3217,15 +3290,14 @@ var Indexer = class {
3217
3290
  metadata: chunk.metadata
3218
3291
  }));
3219
3292
  store.addBatch(items);
3220
- for (let i = 0; i < batch.length; i++) {
3221
- const chunk = batch[i];
3222
- const embedding = result.embeddings[i];
3223
- database.upsertEmbedding(
3224
- chunk.contentHash,
3225
- float32ArrayToBuffer(embedding),
3226
- chunk.text,
3227
- detectedProvider.modelInfo.model
3228
- );
3293
+ const embeddingBatchItems = batch.map((chunk, i) => ({
3294
+ contentHash: chunk.contentHash,
3295
+ embedding: float32ArrayToBuffer(result.embeddings[i]),
3296
+ chunkText: chunk.text,
3297
+ model: detectedProvider.modelInfo.model
3298
+ }));
3299
+ database.upsertEmbeddingsBatch(embeddingBatchItems);
3300
+ for (const chunk of batch) {
3229
3301
  invertedIndex.removeChunk(chunk.id);
3230
3302
  invertedIndex.addChunk(chunk.id, chunk.content);
3231
3303
  }
@@ -3254,11 +3326,14 @@ var Indexer = class {
3254
3326
  totalChunks: pendingChunks.length
3255
3327
  });
3256
3328
  database.clearBranch(this.currentBranch);
3257
- database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
3329
+ database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
3258
3330
  store.save();
3259
3331
  invertedIndex.save();
3260
3332
  this.fileHashCache = currentFileHashes;
3261
3333
  this.saveFileHashCache();
3334
+ if (this.config.indexing.autoGc && stats.removedChunks > 0) {
3335
+ await this.maybeRunOrphanGc();
3336
+ }
3262
3337
  stats.durationMs = Date.now() - startTime;
3263
3338
  if (stats.failedChunks > 0) {
3264
3339
  stats.failedBatchesPath = this.failedBatchesPath;
@@ -3398,11 +3473,14 @@ var Indexer = class {
3398
3473
  };
3399
3474
  }
3400
3475
  async clearIndex() {
3401
- const { store, invertedIndex } = await this.ensureInitialized();
3476
+ const { store, invertedIndex, database } = await this.ensureInitialized();
3402
3477
  store.clear();
3403
3478
  store.save();
3404
3479
  invertedIndex.clear();
3405
3480
  invertedIndex.save();
3481
+ this.fileHashCache.clear();
3482
+ this.saveFileHashCache();
3483
+ database.clearBranch(this.currentBranch);
3406
3484
  }
3407
3485
  async healthCheck() {
3408
3486
  const { store, invertedIndex, database } = await this.ensureInitialized();
@@ -3594,7 +3672,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3594
3672
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
3595
3673
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
3596
3674
  if (wantBigintFsStats) {
3597
- this._stat = (path8) => statMethod(path8, { bigint: true });
3675
+ this._stat = (path9) => statMethod(path9, { bigint: true });
3598
3676
  } else {
3599
3677
  this._stat = statMethod;
3600
3678
  }
@@ -3619,8 +3697,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3619
3697
  const par = this.parent;
3620
3698
  const fil = par && par.files;
3621
3699
  if (fil && fil.length > 0) {
3622
- const { path: path8, depth } = par;
3623
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
3700
+ const { path: path9, depth } = par;
3701
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
3624
3702
  const awaited = await Promise.all(slice);
3625
3703
  for (const entry of awaited) {
3626
3704
  if (!entry)
@@ -3660,21 +3738,21 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3660
3738
  this.reading = false;
3661
3739
  }
3662
3740
  }
3663
- async _exploreDir(path8, depth) {
3741
+ async _exploreDir(path9, depth) {
3664
3742
  let files;
3665
3743
  try {
3666
- files = await (0, import_promises.readdir)(path8, this._rdOptions);
3744
+ files = await (0, import_promises.readdir)(path9, this._rdOptions);
3667
3745
  } catch (error) {
3668
3746
  this._onError(error);
3669
3747
  }
3670
- return { files, depth, path: path8 };
3748
+ return { files, depth, path: path9 };
3671
3749
  }
3672
- async _formatEntry(dirent, path8) {
3750
+ async _formatEntry(dirent, path9) {
3673
3751
  let entry;
3674
- const basename3 = this._isDirent ? dirent.name : dirent;
3752
+ const basename4 = this._isDirent ? dirent.name : dirent;
3675
3753
  try {
3676
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path8, basename3));
3677
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename3 };
3754
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path9, basename4));
3755
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename4 };
3678
3756
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
3679
3757
  } catch (err) {
3680
3758
  this._onError(err);
@@ -4073,16 +4151,16 @@ var delFromSet = (main, prop, item) => {
4073
4151
  };
4074
4152
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
4075
4153
  var FsWatchInstances = /* @__PURE__ */ new Map();
4076
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
4154
+ function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
4077
4155
  const handleEvent = (rawEvent, evPath) => {
4078
- listener(path8);
4079
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
4080
- if (evPath && path8 !== evPath) {
4081
- fsWatchBroadcast(sp.resolve(path8, evPath), KEY_LISTENERS, sp.join(path8, evPath));
4156
+ listener(path9);
4157
+ emitRaw(rawEvent, evPath, { watchedPath: path9 });
4158
+ if (evPath && path9 !== evPath) {
4159
+ fsWatchBroadcast(sp.resolve(path9, evPath), KEY_LISTENERS, sp.join(path9, evPath));
4082
4160
  }
4083
4161
  };
4084
4162
  try {
4085
- return (0, import_node_fs.watch)(path8, {
4163
+ return (0, import_node_fs.watch)(path9, {
4086
4164
  persistent: options.persistent
4087
4165
  }, handleEvent);
4088
4166
  } catch (error) {
@@ -4098,12 +4176,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
4098
4176
  listener(val1, val2, val3);
4099
4177
  });
4100
4178
  };
4101
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
4179
+ var setFsWatchListener = (path9, fullPath, options, handlers) => {
4102
4180
  const { listener, errHandler, rawEmitter } = handlers;
4103
4181
  let cont = FsWatchInstances.get(fullPath);
4104
4182
  let watcher;
4105
4183
  if (!options.persistent) {
4106
- watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
4184
+ watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
4107
4185
  if (!watcher)
4108
4186
  return;
4109
4187
  return watcher.close.bind(watcher);
@@ -4114,7 +4192,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4114
4192
  addAndConvert(cont, KEY_RAW, rawEmitter);
4115
4193
  } else {
4116
4194
  watcher = createFsWatchInstance(
4117
- path8,
4195
+ path9,
4118
4196
  options,
4119
4197
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
4120
4198
  errHandler,
@@ -4129,7 +4207,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4129
4207
  cont.watcherUnusable = true;
4130
4208
  if (isWindows && error.code === "EPERM") {
4131
4209
  try {
4132
- const fd = await (0, import_promises2.open)(path8, "r");
4210
+ const fd = await (0, import_promises2.open)(path9, "r");
4133
4211
  await fd.close();
4134
4212
  broadcastErr(error);
4135
4213
  } catch (err) {
@@ -4160,7 +4238,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4160
4238
  };
4161
4239
  };
4162
4240
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
4163
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
4241
+ var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
4164
4242
  const { listener, rawEmitter } = handlers;
4165
4243
  let cont = FsWatchFileInstances.get(fullPath);
4166
4244
  const copts = cont && cont.options;
@@ -4182,7 +4260,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
4182
4260
  });
4183
4261
  const currmtime = curr.mtimeMs;
4184
4262
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
4185
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
4263
+ foreach(cont.listeners, (listener2) => listener2(path9, curr));
4186
4264
  }
4187
4265
  })
4188
4266
  };
@@ -4212,13 +4290,13 @@ var NodeFsHandler = class {
4212
4290
  * @param listener on fs change
4213
4291
  * @returns closer for the watcher instance
4214
4292
  */
4215
- _watchWithNodeFs(path8, listener) {
4293
+ _watchWithNodeFs(path9, listener) {
4216
4294
  const opts = this.fsw.options;
4217
- const directory = sp.dirname(path8);
4218
- const basename3 = sp.basename(path8);
4295
+ const directory = sp.dirname(path9);
4296
+ const basename4 = sp.basename(path9);
4219
4297
  const parent = this.fsw._getWatchedDir(directory);
4220
- parent.add(basename3);
4221
- const absolutePath = sp.resolve(path8);
4298
+ parent.add(basename4);
4299
+ const absolutePath = sp.resolve(path9);
4222
4300
  const options = {
4223
4301
  persistent: opts.persistent
4224
4302
  };
@@ -4227,13 +4305,13 @@ var NodeFsHandler = class {
4227
4305
  let closer;
4228
4306
  if (opts.usePolling) {
4229
4307
  const enableBin = opts.interval !== opts.binaryInterval;
4230
- options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
4231
- closer = setFsWatchFileListener(path8, absolutePath, options, {
4308
+ options.interval = enableBin && isBinaryPath(basename4) ? opts.binaryInterval : opts.interval;
4309
+ closer = setFsWatchFileListener(path9, absolutePath, options, {
4232
4310
  listener,
4233
4311
  rawEmitter: this.fsw._emitRaw
4234
4312
  });
4235
4313
  } else {
4236
- closer = setFsWatchListener(path8, absolutePath, options, {
4314
+ closer = setFsWatchListener(path9, absolutePath, options, {
4237
4315
  listener,
4238
4316
  errHandler: this._boundHandleError,
4239
4317
  rawEmitter: this.fsw._emitRaw
@@ -4249,13 +4327,13 @@ var NodeFsHandler = class {
4249
4327
  if (this.fsw.closed) {
4250
4328
  return;
4251
4329
  }
4252
- const dirname4 = sp.dirname(file);
4253
- const basename3 = sp.basename(file);
4254
- const parent = this.fsw._getWatchedDir(dirname4);
4330
+ const dirname5 = sp.dirname(file);
4331
+ const basename4 = sp.basename(file);
4332
+ const parent = this.fsw._getWatchedDir(dirname5);
4255
4333
  let prevStats = stats;
4256
- if (parent.has(basename3))
4334
+ if (parent.has(basename4))
4257
4335
  return;
4258
- const listener = async (path8, newStats) => {
4336
+ const listener = async (path9, newStats) => {
4259
4337
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
4260
4338
  return;
4261
4339
  if (!newStats || newStats.mtimeMs === 0) {
@@ -4269,18 +4347,18 @@ var NodeFsHandler = class {
4269
4347
  this.fsw._emit(EV.CHANGE, file, newStats2);
4270
4348
  }
4271
4349
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
4272
- this.fsw._closeFile(path8);
4350
+ this.fsw._closeFile(path9);
4273
4351
  prevStats = newStats2;
4274
4352
  const closer2 = this._watchWithNodeFs(file, listener);
4275
4353
  if (closer2)
4276
- this.fsw._addPathCloser(path8, closer2);
4354
+ this.fsw._addPathCloser(path9, closer2);
4277
4355
  } else {
4278
4356
  prevStats = newStats2;
4279
4357
  }
4280
4358
  } catch (error) {
4281
- this.fsw._remove(dirname4, basename3);
4359
+ this.fsw._remove(dirname5, basename4);
4282
4360
  }
4283
- } else if (parent.has(basename3)) {
4361
+ } else if (parent.has(basename4)) {
4284
4362
  const at = newStats.atimeMs;
4285
4363
  const mt = newStats.mtimeMs;
4286
4364
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -4305,7 +4383,7 @@ var NodeFsHandler = class {
4305
4383
  * @param item basename of this item
4306
4384
  * @returns true if no more processing is needed for this entry.
4307
4385
  */
4308
- async _handleSymlink(entry, directory, path8, item) {
4386
+ async _handleSymlink(entry, directory, path9, item) {
4309
4387
  if (this.fsw.closed) {
4310
4388
  return;
4311
4389
  }
@@ -4315,7 +4393,7 @@ var NodeFsHandler = class {
4315
4393
  this.fsw._incrReadyCount();
4316
4394
  let linkPath;
4317
4395
  try {
4318
- linkPath = await (0, import_promises2.realpath)(path8);
4396
+ linkPath = await (0, import_promises2.realpath)(path9);
4319
4397
  } catch (e) {
4320
4398
  this.fsw._emitReady();
4321
4399
  return true;
@@ -4325,12 +4403,12 @@ var NodeFsHandler = class {
4325
4403
  if (dir.has(item)) {
4326
4404
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
4327
4405
  this.fsw._symlinkPaths.set(full, linkPath);
4328
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
4406
+ this.fsw._emit(EV.CHANGE, path9, entry.stats);
4329
4407
  }
4330
4408
  } else {
4331
4409
  dir.add(item);
4332
4410
  this.fsw._symlinkPaths.set(full, linkPath);
4333
- this.fsw._emit(EV.ADD, path8, entry.stats);
4411
+ this.fsw._emit(EV.ADD, path9, entry.stats);
4334
4412
  }
4335
4413
  this.fsw._emitReady();
4336
4414
  return true;
@@ -4360,9 +4438,9 @@ var NodeFsHandler = class {
4360
4438
  return;
4361
4439
  }
4362
4440
  const item = entry.path;
4363
- let path8 = sp.join(directory, item);
4441
+ let path9 = sp.join(directory, item);
4364
4442
  current.add(item);
4365
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
4443
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
4366
4444
  return;
4367
4445
  }
4368
4446
  if (this.fsw.closed) {
@@ -4371,8 +4449,8 @@ var NodeFsHandler = class {
4371
4449
  }
4372
4450
  if (item === target || !target && !previous.has(item)) {
4373
4451
  this.fsw._incrReadyCount();
4374
- path8 = sp.join(dir, sp.relative(dir, path8));
4375
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
4452
+ path9 = sp.join(dir, sp.relative(dir, path9));
4453
+ this._addToNodeFs(path9, initialAdd, wh, depth + 1);
4376
4454
  }
4377
4455
  }).on(EV.ERROR, this._boundHandleError);
4378
4456
  return new Promise((resolve4, reject) => {
@@ -4441,13 +4519,13 @@ var NodeFsHandler = class {
4441
4519
  * @param depth Child path actually targeted for watch
4442
4520
  * @param target Child path actually targeted for watch
4443
4521
  */
4444
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
4522
+ async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
4445
4523
  const ready = this.fsw._emitReady;
4446
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
4524
+ if (this.fsw._isIgnored(path9) || this.fsw.closed) {
4447
4525
  ready();
4448
4526
  return false;
4449
4527
  }
4450
- const wh = this.fsw._getWatchHelpers(path8);
4528
+ const wh = this.fsw._getWatchHelpers(path9);
4451
4529
  if (priorWh) {
4452
4530
  wh.filterPath = (entry) => priorWh.filterPath(entry);
4453
4531
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -4463,8 +4541,8 @@ var NodeFsHandler = class {
4463
4541
  const follow = this.fsw.options.followSymlinks;
4464
4542
  let closer;
4465
4543
  if (stats.isDirectory()) {
4466
- const absPath = sp.resolve(path8);
4467
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
4544
+ const absPath = sp.resolve(path9);
4545
+ const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
4468
4546
  if (this.fsw.closed)
4469
4547
  return;
4470
4548
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -4474,29 +4552,29 @@ var NodeFsHandler = class {
4474
4552
  this.fsw._symlinkPaths.set(absPath, targetPath);
4475
4553
  }
4476
4554
  } else if (stats.isSymbolicLink()) {
4477
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
4555
+ const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
4478
4556
  if (this.fsw.closed)
4479
4557
  return;
4480
4558
  const parent = sp.dirname(wh.watchPath);
4481
4559
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
4482
4560
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
4483
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
4561
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
4484
4562
  if (this.fsw.closed)
4485
4563
  return;
4486
4564
  if (targetPath !== void 0) {
4487
- this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
4565
+ this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
4488
4566
  }
4489
4567
  } else {
4490
4568
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
4491
4569
  }
4492
4570
  ready();
4493
4571
  if (closer)
4494
- this.fsw._addPathCloser(path8, closer);
4572
+ this.fsw._addPathCloser(path9, closer);
4495
4573
  return false;
4496
4574
  } catch (error) {
4497
4575
  if (this.fsw._handleError(error)) {
4498
4576
  ready();
4499
- return path8;
4577
+ return path9;
4500
4578
  }
4501
4579
  }
4502
4580
  }
@@ -4539,24 +4617,24 @@ function createPattern(matcher) {
4539
4617
  }
4540
4618
  return () => false;
4541
4619
  }
4542
- function normalizePath(path8) {
4543
- if (typeof path8 !== "string")
4620
+ function normalizePath(path9) {
4621
+ if (typeof path9 !== "string")
4544
4622
  throw new Error("string expected");
4545
- path8 = sp2.normalize(path8);
4546
- path8 = path8.replace(/\\/g, "/");
4623
+ path9 = sp2.normalize(path9);
4624
+ path9 = path9.replace(/\\/g, "/");
4547
4625
  let prepend = false;
4548
- if (path8.startsWith("//"))
4626
+ if (path9.startsWith("//"))
4549
4627
  prepend = true;
4550
- path8 = path8.replace(DOUBLE_SLASH_RE, "/");
4628
+ path9 = path9.replace(DOUBLE_SLASH_RE, "/");
4551
4629
  if (prepend)
4552
- path8 = "/" + path8;
4553
- return path8;
4630
+ path9 = "/" + path9;
4631
+ return path9;
4554
4632
  }
4555
4633
  function matchPatterns(patterns, testString, stats) {
4556
- const path8 = normalizePath(testString);
4634
+ const path9 = normalizePath(testString);
4557
4635
  for (let index = 0; index < patterns.length; index++) {
4558
4636
  const pattern = patterns[index];
4559
- if (pattern(path8, stats)) {
4637
+ if (pattern(path9, stats)) {
4560
4638
  return true;
4561
4639
  }
4562
4640
  }
@@ -4594,19 +4672,19 @@ var toUnix = (string) => {
4594
4672
  }
4595
4673
  return str;
4596
4674
  };
4597
- var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
4598
- var normalizeIgnored = (cwd = "") => (path8) => {
4599
- if (typeof path8 === "string") {
4600
- return normalizePathToUnix(sp2.isAbsolute(path8) ? path8 : sp2.join(cwd, path8));
4675
+ var normalizePathToUnix = (path9) => toUnix(sp2.normalize(toUnix(path9)));
4676
+ var normalizeIgnored = (cwd = "") => (path9) => {
4677
+ if (typeof path9 === "string") {
4678
+ return normalizePathToUnix(sp2.isAbsolute(path9) ? path9 : sp2.join(cwd, path9));
4601
4679
  } else {
4602
- return path8;
4680
+ return path9;
4603
4681
  }
4604
4682
  };
4605
- var getAbsolutePath = (path8, cwd) => {
4606
- if (sp2.isAbsolute(path8)) {
4607
- return path8;
4683
+ var getAbsolutePath = (path9, cwd) => {
4684
+ if (sp2.isAbsolute(path9)) {
4685
+ return path9;
4608
4686
  }
4609
- return sp2.join(cwd, path8);
4687
+ return sp2.join(cwd, path9);
4610
4688
  };
4611
4689
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
4612
4690
  var DirEntry = class {
@@ -4671,10 +4749,10 @@ var WatchHelper = class {
4671
4749
  dirParts;
4672
4750
  followSymlinks;
4673
4751
  statMethod;
4674
- constructor(path8, follow, fsw) {
4752
+ constructor(path9, follow, fsw) {
4675
4753
  this.fsw = fsw;
4676
- const watchPath = path8;
4677
- this.path = path8 = path8.replace(REPLACER_RE, "");
4754
+ const watchPath = path9;
4755
+ this.path = path9 = path9.replace(REPLACER_RE, "");
4678
4756
  this.watchPath = watchPath;
4679
4757
  this.fullWatchPath = sp2.resolve(watchPath);
4680
4758
  this.dirParts = [];
@@ -4814,20 +4892,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4814
4892
  this._closePromise = void 0;
4815
4893
  let paths = unifyPaths(paths_);
4816
4894
  if (cwd) {
4817
- paths = paths.map((path8) => {
4818
- const absPath = getAbsolutePath(path8, cwd);
4895
+ paths = paths.map((path9) => {
4896
+ const absPath = getAbsolutePath(path9, cwd);
4819
4897
  return absPath;
4820
4898
  });
4821
4899
  }
4822
- paths.forEach((path8) => {
4823
- this._removeIgnoredPath(path8);
4900
+ paths.forEach((path9) => {
4901
+ this._removeIgnoredPath(path9);
4824
4902
  });
4825
4903
  this._userIgnored = void 0;
4826
4904
  if (!this._readyCount)
4827
4905
  this._readyCount = 0;
4828
4906
  this._readyCount += paths.length;
4829
- Promise.all(paths.map(async (path8) => {
4830
- const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
4907
+ Promise.all(paths.map(async (path9) => {
4908
+ const res = await this._nodeFsHandler._addToNodeFs(path9, !_internal, void 0, 0, _origAdd);
4831
4909
  if (res)
4832
4910
  this._emitReady();
4833
4911
  return res;
@@ -4849,17 +4927,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4849
4927
  return this;
4850
4928
  const paths = unifyPaths(paths_);
4851
4929
  const { cwd } = this.options;
4852
- paths.forEach((path8) => {
4853
- if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
4930
+ paths.forEach((path9) => {
4931
+ if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
4854
4932
  if (cwd)
4855
- path8 = sp2.join(cwd, path8);
4856
- path8 = sp2.resolve(path8);
4933
+ path9 = sp2.join(cwd, path9);
4934
+ path9 = sp2.resolve(path9);
4857
4935
  }
4858
- this._closePath(path8);
4859
- this._addIgnoredPath(path8);
4860
- if (this._watched.has(path8)) {
4936
+ this._closePath(path9);
4937
+ this._addIgnoredPath(path9);
4938
+ if (this._watched.has(path9)) {
4861
4939
  this._addIgnoredPath({
4862
- path: path8,
4940
+ path: path9,
4863
4941
  recursive: true
4864
4942
  });
4865
4943
  }
@@ -4923,38 +5001,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4923
5001
  * @param stats arguments to be passed with event
4924
5002
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
4925
5003
  */
4926
- async _emit(event, path8, stats) {
5004
+ async _emit(event, path9, stats) {
4927
5005
  if (this.closed)
4928
5006
  return;
4929
5007
  const opts = this.options;
4930
5008
  if (isWindows)
4931
- path8 = sp2.normalize(path8);
5009
+ path9 = sp2.normalize(path9);
4932
5010
  if (opts.cwd)
4933
- path8 = sp2.relative(opts.cwd, path8);
4934
- const args = [path8];
5011
+ path9 = sp2.relative(opts.cwd, path9);
5012
+ const args = [path9];
4935
5013
  if (stats != null)
4936
5014
  args.push(stats);
4937
5015
  const awf = opts.awaitWriteFinish;
4938
5016
  let pw;
4939
- if (awf && (pw = this._pendingWrites.get(path8))) {
5017
+ if (awf && (pw = this._pendingWrites.get(path9))) {
4940
5018
  pw.lastChange = /* @__PURE__ */ new Date();
4941
5019
  return this;
4942
5020
  }
4943
5021
  if (opts.atomic) {
4944
5022
  if (event === EVENTS.UNLINK) {
4945
- this._pendingUnlinks.set(path8, [event, ...args]);
5023
+ this._pendingUnlinks.set(path9, [event, ...args]);
4946
5024
  setTimeout(() => {
4947
- this._pendingUnlinks.forEach((entry, path9) => {
5025
+ this._pendingUnlinks.forEach((entry, path10) => {
4948
5026
  this.emit(...entry);
4949
5027
  this.emit(EVENTS.ALL, ...entry);
4950
- this._pendingUnlinks.delete(path9);
5028
+ this._pendingUnlinks.delete(path10);
4951
5029
  });
4952
5030
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
4953
5031
  return this;
4954
5032
  }
4955
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
5033
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
4956
5034
  event = EVENTS.CHANGE;
4957
- this._pendingUnlinks.delete(path8);
5035
+ this._pendingUnlinks.delete(path9);
4958
5036
  }
4959
5037
  }
4960
5038
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -4972,16 +5050,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4972
5050
  this.emitWithAll(event, args);
4973
5051
  }
4974
5052
  };
4975
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
5053
+ this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
4976
5054
  return this;
4977
5055
  }
4978
5056
  if (event === EVENTS.CHANGE) {
4979
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
5057
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
4980
5058
  if (isThrottled)
4981
5059
  return this;
4982
5060
  }
4983
5061
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
4984
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
5062
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
4985
5063
  let stats2;
4986
5064
  try {
4987
5065
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -5012,23 +5090,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5012
5090
  * @param timeout duration of time to suppress duplicate actions
5013
5091
  * @returns tracking object or false if action should be suppressed
5014
5092
  */
5015
- _throttle(actionType, path8, timeout) {
5093
+ _throttle(actionType, path9, timeout) {
5016
5094
  if (!this._throttled.has(actionType)) {
5017
5095
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
5018
5096
  }
5019
5097
  const action = this._throttled.get(actionType);
5020
5098
  if (!action)
5021
5099
  throw new Error("invalid throttle");
5022
- const actionPath = action.get(path8);
5100
+ const actionPath = action.get(path9);
5023
5101
  if (actionPath) {
5024
5102
  actionPath.count++;
5025
5103
  return false;
5026
5104
  }
5027
5105
  let timeoutObject;
5028
5106
  const clear = () => {
5029
- const item = action.get(path8);
5107
+ const item = action.get(path9);
5030
5108
  const count = item ? item.count : 0;
5031
- action.delete(path8);
5109
+ action.delete(path9);
5032
5110
  clearTimeout(timeoutObject);
5033
5111
  if (item)
5034
5112
  clearTimeout(item.timeoutObject);
@@ -5036,7 +5114,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5036
5114
  };
5037
5115
  timeoutObject = setTimeout(clear, timeout);
5038
5116
  const thr = { timeoutObject, clear, count: 0 };
5039
- action.set(path8, thr);
5117
+ action.set(path9, thr);
5040
5118
  return thr;
5041
5119
  }
5042
5120
  _incrReadyCount() {
@@ -5050,44 +5128,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5050
5128
  * @param event
5051
5129
  * @param awfEmit Callback to be called when ready for event to be emitted.
5052
5130
  */
5053
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
5131
+ _awaitWriteFinish(path9, threshold, event, awfEmit) {
5054
5132
  const awf = this.options.awaitWriteFinish;
5055
5133
  if (typeof awf !== "object")
5056
5134
  return;
5057
5135
  const pollInterval = awf.pollInterval;
5058
5136
  let timeoutHandler;
5059
- let fullPath = path8;
5060
- if (this.options.cwd && !sp2.isAbsolute(path8)) {
5061
- fullPath = sp2.join(this.options.cwd, path8);
5137
+ let fullPath = path9;
5138
+ if (this.options.cwd && !sp2.isAbsolute(path9)) {
5139
+ fullPath = sp2.join(this.options.cwd, path9);
5062
5140
  }
5063
5141
  const now = /* @__PURE__ */ new Date();
5064
5142
  const writes = this._pendingWrites;
5065
5143
  function awaitWriteFinishFn(prevStat) {
5066
5144
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
5067
- if (err || !writes.has(path8)) {
5145
+ if (err || !writes.has(path9)) {
5068
5146
  if (err && err.code !== "ENOENT")
5069
5147
  awfEmit(err);
5070
5148
  return;
5071
5149
  }
5072
5150
  const now2 = Number(/* @__PURE__ */ new Date());
5073
5151
  if (prevStat && curStat.size !== prevStat.size) {
5074
- writes.get(path8).lastChange = now2;
5152
+ writes.get(path9).lastChange = now2;
5075
5153
  }
5076
- const pw = writes.get(path8);
5154
+ const pw = writes.get(path9);
5077
5155
  const df = now2 - pw.lastChange;
5078
5156
  if (df >= threshold) {
5079
- writes.delete(path8);
5157
+ writes.delete(path9);
5080
5158
  awfEmit(void 0, curStat);
5081
5159
  } else {
5082
5160
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
5083
5161
  }
5084
5162
  });
5085
5163
  }
5086
- if (!writes.has(path8)) {
5087
- writes.set(path8, {
5164
+ if (!writes.has(path9)) {
5165
+ writes.set(path9, {
5088
5166
  lastChange: now,
5089
5167
  cancelWait: () => {
5090
- writes.delete(path8);
5168
+ writes.delete(path9);
5091
5169
  clearTimeout(timeoutHandler);
5092
5170
  return event;
5093
5171
  }
@@ -5098,8 +5176,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5098
5176
  /**
5099
5177
  * Determines whether user has asked to ignore this path.
5100
5178
  */
5101
- _isIgnored(path8, stats) {
5102
- if (this.options.atomic && DOT_RE.test(path8))
5179
+ _isIgnored(path9, stats) {
5180
+ if (this.options.atomic && DOT_RE.test(path9))
5103
5181
  return true;
5104
5182
  if (!this._userIgnored) {
5105
5183
  const { cwd } = this.options;
@@ -5109,17 +5187,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5109
5187
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
5110
5188
  this._userIgnored = anymatch(list, void 0);
5111
5189
  }
5112
- return this._userIgnored(path8, stats);
5190
+ return this._userIgnored(path9, stats);
5113
5191
  }
5114
- _isntIgnored(path8, stat4) {
5115
- return !this._isIgnored(path8, stat4);
5192
+ _isntIgnored(path9, stat4) {
5193
+ return !this._isIgnored(path9, stat4);
5116
5194
  }
5117
5195
  /**
5118
5196
  * Provides a set of common helpers and properties relating to symlink handling.
5119
5197
  * @param path file or directory pattern being watched
5120
5198
  */
5121
- _getWatchHelpers(path8) {
5122
- return new WatchHelper(path8, this.options.followSymlinks, this);
5199
+ _getWatchHelpers(path9) {
5200
+ return new WatchHelper(path9, this.options.followSymlinks, this);
5123
5201
  }
5124
5202
  // Directory helpers
5125
5203
  // -----------------
@@ -5151,63 +5229,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5151
5229
  * @param item base path of item/directory
5152
5230
  */
5153
5231
  _remove(directory, item, isDirectory) {
5154
- const path8 = sp2.join(directory, item);
5155
- const fullPath = sp2.resolve(path8);
5156
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
5157
- if (!this._throttle("remove", path8, 100))
5232
+ const path9 = sp2.join(directory, item);
5233
+ const fullPath = sp2.resolve(path9);
5234
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
5235
+ if (!this._throttle("remove", path9, 100))
5158
5236
  return;
5159
5237
  if (!isDirectory && this._watched.size === 1) {
5160
5238
  this.add(directory, item, true);
5161
5239
  }
5162
- const wp = this._getWatchedDir(path8);
5240
+ const wp = this._getWatchedDir(path9);
5163
5241
  const nestedDirectoryChildren = wp.getChildren();
5164
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
5242
+ nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
5165
5243
  const parent = this._getWatchedDir(directory);
5166
5244
  const wasTracked = parent.has(item);
5167
5245
  parent.remove(item);
5168
5246
  if (this._symlinkPaths.has(fullPath)) {
5169
5247
  this._symlinkPaths.delete(fullPath);
5170
5248
  }
5171
- let relPath = path8;
5249
+ let relPath = path9;
5172
5250
  if (this.options.cwd)
5173
- relPath = sp2.relative(this.options.cwd, path8);
5251
+ relPath = sp2.relative(this.options.cwd, path9);
5174
5252
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
5175
5253
  const event = this._pendingWrites.get(relPath).cancelWait();
5176
5254
  if (event === EVENTS.ADD)
5177
5255
  return;
5178
5256
  }
5179
- this._watched.delete(path8);
5257
+ this._watched.delete(path9);
5180
5258
  this._watched.delete(fullPath);
5181
5259
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
5182
- if (wasTracked && !this._isIgnored(path8))
5183
- this._emit(eventName, path8);
5184
- this._closePath(path8);
5260
+ if (wasTracked && !this._isIgnored(path9))
5261
+ this._emit(eventName, path9);
5262
+ this._closePath(path9);
5185
5263
  }
5186
5264
  /**
5187
5265
  * Closes all watchers for a path
5188
5266
  */
5189
- _closePath(path8) {
5190
- this._closeFile(path8);
5191
- const dir = sp2.dirname(path8);
5192
- this._getWatchedDir(dir).remove(sp2.basename(path8));
5267
+ _closePath(path9) {
5268
+ this._closeFile(path9);
5269
+ const dir = sp2.dirname(path9);
5270
+ this._getWatchedDir(dir).remove(sp2.basename(path9));
5193
5271
  }
5194
5272
  /**
5195
5273
  * Closes only file-specific watchers
5196
5274
  */
5197
- _closeFile(path8) {
5198
- const closers = this._closers.get(path8);
5275
+ _closeFile(path9) {
5276
+ const closers = this._closers.get(path9);
5199
5277
  if (!closers)
5200
5278
  return;
5201
5279
  closers.forEach((closer) => closer());
5202
- this._closers.delete(path8);
5280
+ this._closers.delete(path9);
5203
5281
  }
5204
- _addPathCloser(path8, closer) {
5282
+ _addPathCloser(path9, closer) {
5205
5283
  if (!closer)
5206
5284
  return;
5207
- let list = this._closers.get(path8);
5285
+ let list = this._closers.get(path9);
5208
5286
  if (!list) {
5209
5287
  list = [];
5210
- this._closers.set(path8, list);
5288
+ this._closers.set(path9, list);
5211
5289
  }
5212
5290
  list.push(closer);
5213
5291
  }
@@ -5302,7 +5380,7 @@ var FileWatcher = class {
5302
5380
  return;
5303
5381
  }
5304
5382
  const changes = Array.from(this.pendingChanges.entries()).map(
5305
- ([path8, type]) => ({ path: path8, type })
5383
+ ([path9, type]) => ({ path: path9, type })
5306
5384
  );
5307
5385
  this.pendingChanges.clear();
5308
5386
  try {
@@ -5482,7 +5560,7 @@ var index_codebase = (0, import_plugin.tool)({
5482
5560
  estimateOnly: z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
5483
5561
  verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
5484
5562
  },
5485
- async execute(args) {
5563
+ async execute(args, context) {
5486
5564
  const indexer = getIndexer();
5487
5565
  if (args.estimateOnly) {
5488
5566
  const estimate = await indexer.estimateCost();
@@ -5491,7 +5569,19 @@ var index_codebase = (0, import_plugin.tool)({
5491
5569
  if (args.force) {
5492
5570
  await indexer.clearIndex();
5493
5571
  }
5494
- const stats = await indexer.index();
5572
+ const stats = await indexer.index((progress) => {
5573
+ context.metadata({
5574
+ title: formatProgressTitle(progress),
5575
+ metadata: {
5576
+ phase: progress.phase,
5577
+ filesProcessed: progress.filesProcessed,
5578
+ totalFiles: progress.totalFiles,
5579
+ chunksProcessed: progress.chunksProcessed,
5580
+ totalChunks: progress.totalChunks,
5581
+ percentage: calculatePercentage(progress)
5582
+ }
5583
+ });
5584
+ });
5495
5585
  return formatIndexStats(stats, args.verbose ?? false);
5496
5586
  }
5497
5587
  });
@@ -5590,12 +5680,91 @@ function formatStatus(status) {
5590
5680
  }
5591
5681
  return lines.join("\n");
5592
5682
  }
5683
+ function formatProgressTitle(progress) {
5684
+ switch (progress.phase) {
5685
+ case "scanning":
5686
+ return "Scanning files...";
5687
+ case "parsing":
5688
+ return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
5689
+ case "embedding":
5690
+ return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
5691
+ case "storing":
5692
+ return "Storing index...";
5693
+ case "complete":
5694
+ return "Indexing complete";
5695
+ default:
5696
+ return "Indexing...";
5697
+ }
5698
+ }
5699
+ function calculatePercentage(progress) {
5700
+ if (progress.phase === "scanning") return 0;
5701
+ if (progress.phase === "complete") return 100;
5702
+ if (progress.phase === "parsing") {
5703
+ if (progress.totalFiles === 0) return 5;
5704
+ return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
5705
+ }
5706
+ if (progress.phase === "embedding") {
5707
+ if (progress.totalChunks === 0) return 20;
5708
+ return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
5709
+ }
5710
+ if (progress.phase === "storing") return 95;
5711
+ return 0;
5712
+ }
5713
+
5714
+ // src/commands/loader.ts
5715
+ var import_fs5 = require("fs");
5716
+ var path7 = __toESM(require("path"), 1);
5717
+ function parseFrontmatter(content) {
5718
+ const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
5719
+ const match = content.match(frontmatterRegex);
5720
+ if (!match) {
5721
+ return { frontmatter: {}, body: content.trim() };
5722
+ }
5723
+ const frontmatterLines = match[1].split("\n");
5724
+ const frontmatter = {};
5725
+ for (const line of frontmatterLines) {
5726
+ const colonIndex = line.indexOf(":");
5727
+ if (colonIndex > 0) {
5728
+ const key = line.slice(0, colonIndex).trim();
5729
+ const value = line.slice(colonIndex + 1).trim();
5730
+ frontmatter[key] = value;
5731
+ }
5732
+ }
5733
+ return { frontmatter, body: match[2].trim() };
5734
+ }
5735
+ function loadCommandsFromDirectory(commandsDir) {
5736
+ const commands = /* @__PURE__ */ new Map();
5737
+ if (!(0, import_fs5.existsSync)(commandsDir)) {
5738
+ return commands;
5739
+ }
5740
+ const files = (0, import_fs5.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
5741
+ for (const file of files) {
5742
+ const filePath = path7.join(commandsDir, file);
5743
+ const content = (0, import_fs5.readFileSync)(filePath, "utf-8");
5744
+ const { frontmatter, body } = parseFrontmatter(content);
5745
+ const name = path7.basename(file, ".md");
5746
+ const description = frontmatter.description || `Run the ${name} command`;
5747
+ commands.set(name, {
5748
+ description,
5749
+ template: body
5750
+ });
5751
+ }
5752
+ return commands;
5753
+ }
5593
5754
 
5594
5755
  // src/index.ts
5756
+ var import_meta2 = {};
5757
+ function getCommandsDir() {
5758
+ let currentDir = process.cwd();
5759
+ if (typeof import_meta2 !== "undefined" && import_meta2.url) {
5760
+ currentDir = path8.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
5761
+ }
5762
+ return path8.join(currentDir, "..", "commands");
5763
+ }
5595
5764
  function loadJsonFile(filePath) {
5596
5765
  try {
5597
- if ((0, import_fs5.existsSync)(filePath)) {
5598
- const content = (0, import_fs5.readFileSync)(filePath, "utf-8");
5766
+ if ((0, import_fs6.existsSync)(filePath)) {
5767
+ const content = (0, import_fs6.readFileSync)(filePath, "utf-8");
5599
5768
  return JSON.parse(content);
5600
5769
  }
5601
5770
  } catch {
@@ -5603,11 +5772,11 @@ function loadJsonFile(filePath) {
5603
5772
  return null;
5604
5773
  }
5605
5774
  function loadPluginConfig(projectRoot) {
5606
- const projectConfig = loadJsonFile(path7.join(projectRoot, ".opencode", "codebase-index.json"));
5775
+ const projectConfig = loadJsonFile(path8.join(projectRoot, ".opencode", "codebase-index.json"));
5607
5776
  if (projectConfig) {
5608
5777
  return projectConfig;
5609
5778
  }
5610
- const globalConfigPath = path7.join(os3.homedir(), ".config", "opencode", "codebase-index.json");
5779
+ const globalConfigPath = path8.join(os3.homedir(), ".config", "opencode", "codebase-index.json");
5611
5780
  const globalConfig = loadJsonFile(globalConfigPath);
5612
5781
  if (globalConfig) {
5613
5782
  return globalConfig;
@@ -5639,36 +5808,11 @@ var plugin = async ({ directory }) => {
5639
5808
  },
5640
5809
  async config(cfg) {
5641
5810
  cfg.command = cfg.command ?? {};
5642
- cfg.command["search"] = {
5643
- description: "Search codebase by meaning using semantic search",
5644
- template: `Use the \`codebase_search\` tool to find code related to: $ARGUMENTS
5645
-
5646
- If the index doesn't exist yet, run \`index_codebase\` first.
5647
-
5648
- Return the most relevant results with file paths and line numbers.`
5649
- };
5650
- cfg.command["find"] = {
5651
- description: "Find code using hybrid approach (semantic + grep)",
5652
- template: `Find code related to: $ARGUMENTS
5653
-
5654
- Strategy:
5655
- 1. First use \`codebase_search\` to find semantically related code
5656
- 2. From the results, identify specific function/class names
5657
- 3. Use grep to find all occurrences of those identifiers
5658
- 4. Combine findings into a comprehensive answer
5659
-
5660
- If the semantic index doesn't exist, run \`index_codebase\` first.`
5661
- };
5662
- cfg.command["index"] = {
5663
- description: "Index the codebase for semantic search",
5664
- template: `Run the \`index_codebase\` tool to create or update the semantic search index.
5665
-
5666
- Show progress and final statistics including:
5667
- - Number of files processed
5668
- - Number of chunks indexed
5669
- - Tokens used
5670
- - Duration`
5671
- };
5811
+ const commandsDir = getCommandsDir();
5812
+ const commands = loadCommandsFromDirectory(commandsDir);
5813
+ for (const [name, definition] of commands) {
5814
+ cfg.command[name] = definition;
5815
+ }
5672
5816
  }
5673
5817
  };
5674
5818
  };