opencode-codebase-index 0.2.5 → 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
  }
@@ -2961,11 +2976,45 @@ var Indexer = class {
2961
2976
  this.currentBranch = "default";
2962
2977
  this.baseBranch = "default";
2963
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
+ }
2964
3012
  }
2965
3013
  migrateFromLegacyIndex() {
2966
3014
  if (!this.store || !this.database) return;
2967
3015
  const allMetadata = this.store.getAllMetadata();
2968
3016
  const chunkIds = [];
3017
+ const chunkDataBatch = [];
2969
3018
  for (const { key, metadata } of allMetadata) {
2970
3019
  const chunkData = {
2971
3020
  chunkId: key,
@@ -2977,10 +3026,13 @@ var Indexer = class {
2977
3026
  name: metadata.name,
2978
3027
  language: metadata.language
2979
3028
  };
2980
- this.database.upsertChunk(chunkData);
3029
+ chunkDataBatch.push(chunkData);
2981
3030
  chunkIds.push(key);
2982
3031
  }
2983
- 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);
2984
3036
  }
2985
3037
  async ensureInitialized() {
2986
3038
  if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider || !this.database) {
@@ -3076,6 +3128,7 @@ var Indexer = class {
3076
3128
  }
3077
3129
  }
3078
3130
  }
3131
+ const chunkDataBatch = [];
3079
3132
  for (const parsed of parsedFiles) {
3080
3133
  currentFilePaths.add(parsed.path);
3081
3134
  if (parsed.chunks.length === 0) {
@@ -3103,7 +3156,7 @@ var Indexer = class {
3103
3156
  name: chunk.name,
3104
3157
  language: chunk.language
3105
3158
  };
3106
- database.upsertChunk(chunkData);
3159
+ chunkDataBatch.push(chunkData);
3107
3160
  if (existingChunks.get(id) === contentHash) {
3108
3161
  fileChunkCount++;
3109
3162
  continue;
@@ -3122,6 +3175,9 @@ var Indexer = class {
3122
3175
  fileChunkCount++;
3123
3176
  }
3124
3177
  }
3178
+ if (chunkDataBatch.length > 0) {
3179
+ database.upsertChunksBatch(chunkDataBatch);
3180
+ }
3125
3181
  let removedCount = 0;
3126
3182
  for (const [chunkId] of existingChunks) {
3127
3183
  if (!currentChunkIds.has(chunkId)) {
@@ -3135,7 +3191,7 @@ var Indexer = class {
3135
3191
  stats.removedChunks = removedCount;
3136
3192
  if (pendingChunks.length === 0 && removedCount === 0) {
3137
3193
  database.clearBranch(this.currentBranch);
3138
- database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
3194
+ database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
3139
3195
  this.fileHashCache = currentFileHashes;
3140
3196
  this.saveFileHashCache();
3141
3197
  stats.durationMs = Date.now() - startTime;
@@ -3150,7 +3206,7 @@ var Indexer = class {
3150
3206
  }
3151
3207
  if (pendingChunks.length === 0) {
3152
3208
  database.clearBranch(this.currentBranch);
3153
- database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
3209
+ database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
3154
3210
  store.save();
3155
3211
  invertedIndex.save();
3156
3212
  this.fileHashCache = currentFileHashes;
@@ -3234,15 +3290,14 @@ var Indexer = class {
3234
3290
  metadata: chunk.metadata
3235
3291
  }));
3236
3292
  store.addBatch(items);
3237
- for (let i = 0; i < batch.length; i++) {
3238
- const chunk = batch[i];
3239
- const embedding = result.embeddings[i];
3240
- database.upsertEmbedding(
3241
- chunk.contentHash,
3242
- float32ArrayToBuffer(embedding),
3243
- chunk.text,
3244
- detectedProvider.modelInfo.model
3245
- );
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) {
3246
3301
  invertedIndex.removeChunk(chunk.id);
3247
3302
  invertedIndex.addChunk(chunk.id, chunk.content);
3248
3303
  }
@@ -3271,11 +3326,14 @@ var Indexer = class {
3271
3326
  totalChunks: pendingChunks.length
3272
3327
  });
3273
3328
  database.clearBranch(this.currentBranch);
3274
- database.addChunksToBranch(this.currentBranch, Array.from(currentChunkIds));
3329
+ database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
3275
3330
  store.save();
3276
3331
  invertedIndex.save();
3277
3332
  this.fileHashCache = currentFileHashes;
3278
3333
  this.saveFileHashCache();
3334
+ if (this.config.indexing.autoGc && stats.removedChunks > 0) {
3335
+ await this.maybeRunOrphanGc();
3336
+ }
3279
3337
  stats.durationMs = Date.now() - startTime;
3280
3338
  if (stats.failedChunks > 0) {
3281
3339
  stats.failedBatchesPath = this.failedBatchesPath;
@@ -3415,11 +3473,14 @@ var Indexer = class {
3415
3473
  };
3416
3474
  }
3417
3475
  async clearIndex() {
3418
- const { store, invertedIndex } = await this.ensureInitialized();
3476
+ const { store, invertedIndex, database } = await this.ensureInitialized();
3419
3477
  store.clear();
3420
3478
  store.save();
3421
3479
  invertedIndex.clear();
3422
3480
  invertedIndex.save();
3481
+ this.fileHashCache.clear();
3482
+ this.saveFileHashCache();
3483
+ database.clearBranch(this.currentBranch);
3423
3484
  }
3424
3485
  async healthCheck() {
3425
3486
  const { store, invertedIndex, database } = await this.ensureInitialized();
@@ -3611,7 +3672,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3611
3672
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
3612
3673
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
3613
3674
  if (wantBigintFsStats) {
3614
- this._stat = (path8) => statMethod(path8, { bigint: true });
3675
+ this._stat = (path9) => statMethod(path9, { bigint: true });
3615
3676
  } else {
3616
3677
  this._stat = statMethod;
3617
3678
  }
@@ -3636,8 +3697,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3636
3697
  const par = this.parent;
3637
3698
  const fil = par && par.files;
3638
3699
  if (fil && fil.length > 0) {
3639
- const { path: path8, depth } = par;
3640
- 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));
3641
3702
  const awaited = await Promise.all(slice);
3642
3703
  for (const entry of awaited) {
3643
3704
  if (!entry)
@@ -3677,21 +3738,21 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3677
3738
  this.reading = false;
3678
3739
  }
3679
3740
  }
3680
- async _exploreDir(path8, depth) {
3741
+ async _exploreDir(path9, depth) {
3681
3742
  let files;
3682
3743
  try {
3683
- files = await (0, import_promises.readdir)(path8, this._rdOptions);
3744
+ files = await (0, import_promises.readdir)(path9, this._rdOptions);
3684
3745
  } catch (error) {
3685
3746
  this._onError(error);
3686
3747
  }
3687
- return { files, depth, path: path8 };
3748
+ return { files, depth, path: path9 };
3688
3749
  }
3689
- async _formatEntry(dirent, path8) {
3750
+ async _formatEntry(dirent, path9) {
3690
3751
  let entry;
3691
- const basename3 = this._isDirent ? dirent.name : dirent;
3752
+ const basename4 = this._isDirent ? dirent.name : dirent;
3692
3753
  try {
3693
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path8, basename3));
3694
- 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 };
3695
3756
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
3696
3757
  } catch (err) {
3697
3758
  this._onError(err);
@@ -4090,16 +4151,16 @@ var delFromSet = (main, prop, item) => {
4090
4151
  };
4091
4152
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
4092
4153
  var FsWatchInstances = /* @__PURE__ */ new Map();
4093
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
4154
+ function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
4094
4155
  const handleEvent = (rawEvent, evPath) => {
4095
- listener(path8);
4096
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
4097
- if (evPath && path8 !== evPath) {
4098
- 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));
4099
4160
  }
4100
4161
  };
4101
4162
  try {
4102
- return (0, import_node_fs.watch)(path8, {
4163
+ return (0, import_node_fs.watch)(path9, {
4103
4164
  persistent: options.persistent
4104
4165
  }, handleEvent);
4105
4166
  } catch (error) {
@@ -4115,12 +4176,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
4115
4176
  listener(val1, val2, val3);
4116
4177
  });
4117
4178
  };
4118
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
4179
+ var setFsWatchListener = (path9, fullPath, options, handlers) => {
4119
4180
  const { listener, errHandler, rawEmitter } = handlers;
4120
4181
  let cont = FsWatchInstances.get(fullPath);
4121
4182
  let watcher;
4122
4183
  if (!options.persistent) {
4123
- watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
4184
+ watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
4124
4185
  if (!watcher)
4125
4186
  return;
4126
4187
  return watcher.close.bind(watcher);
@@ -4131,7 +4192,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4131
4192
  addAndConvert(cont, KEY_RAW, rawEmitter);
4132
4193
  } else {
4133
4194
  watcher = createFsWatchInstance(
4134
- path8,
4195
+ path9,
4135
4196
  options,
4136
4197
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
4137
4198
  errHandler,
@@ -4146,7 +4207,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4146
4207
  cont.watcherUnusable = true;
4147
4208
  if (isWindows && error.code === "EPERM") {
4148
4209
  try {
4149
- const fd = await (0, import_promises2.open)(path8, "r");
4210
+ const fd = await (0, import_promises2.open)(path9, "r");
4150
4211
  await fd.close();
4151
4212
  broadcastErr(error);
4152
4213
  } catch (err) {
@@ -4177,7 +4238,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4177
4238
  };
4178
4239
  };
4179
4240
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
4180
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
4241
+ var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
4181
4242
  const { listener, rawEmitter } = handlers;
4182
4243
  let cont = FsWatchFileInstances.get(fullPath);
4183
4244
  const copts = cont && cont.options;
@@ -4199,7 +4260,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
4199
4260
  });
4200
4261
  const currmtime = curr.mtimeMs;
4201
4262
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
4202
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
4263
+ foreach(cont.listeners, (listener2) => listener2(path9, curr));
4203
4264
  }
4204
4265
  })
4205
4266
  };
@@ -4229,13 +4290,13 @@ var NodeFsHandler = class {
4229
4290
  * @param listener on fs change
4230
4291
  * @returns closer for the watcher instance
4231
4292
  */
4232
- _watchWithNodeFs(path8, listener) {
4293
+ _watchWithNodeFs(path9, listener) {
4233
4294
  const opts = this.fsw.options;
4234
- const directory = sp.dirname(path8);
4235
- const basename3 = sp.basename(path8);
4295
+ const directory = sp.dirname(path9);
4296
+ const basename4 = sp.basename(path9);
4236
4297
  const parent = this.fsw._getWatchedDir(directory);
4237
- parent.add(basename3);
4238
- const absolutePath = sp.resolve(path8);
4298
+ parent.add(basename4);
4299
+ const absolutePath = sp.resolve(path9);
4239
4300
  const options = {
4240
4301
  persistent: opts.persistent
4241
4302
  };
@@ -4244,13 +4305,13 @@ var NodeFsHandler = class {
4244
4305
  let closer;
4245
4306
  if (opts.usePolling) {
4246
4307
  const enableBin = opts.interval !== opts.binaryInterval;
4247
- options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
4248
- closer = setFsWatchFileListener(path8, absolutePath, options, {
4308
+ options.interval = enableBin && isBinaryPath(basename4) ? opts.binaryInterval : opts.interval;
4309
+ closer = setFsWatchFileListener(path9, absolutePath, options, {
4249
4310
  listener,
4250
4311
  rawEmitter: this.fsw._emitRaw
4251
4312
  });
4252
4313
  } else {
4253
- closer = setFsWatchListener(path8, absolutePath, options, {
4314
+ closer = setFsWatchListener(path9, absolutePath, options, {
4254
4315
  listener,
4255
4316
  errHandler: this._boundHandleError,
4256
4317
  rawEmitter: this.fsw._emitRaw
@@ -4266,13 +4327,13 @@ var NodeFsHandler = class {
4266
4327
  if (this.fsw.closed) {
4267
4328
  return;
4268
4329
  }
4269
- const dirname4 = sp.dirname(file);
4270
- const basename3 = sp.basename(file);
4271
- 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);
4272
4333
  let prevStats = stats;
4273
- if (parent.has(basename3))
4334
+ if (parent.has(basename4))
4274
4335
  return;
4275
- const listener = async (path8, newStats) => {
4336
+ const listener = async (path9, newStats) => {
4276
4337
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
4277
4338
  return;
4278
4339
  if (!newStats || newStats.mtimeMs === 0) {
@@ -4286,18 +4347,18 @@ var NodeFsHandler = class {
4286
4347
  this.fsw._emit(EV.CHANGE, file, newStats2);
4287
4348
  }
4288
4349
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
4289
- this.fsw._closeFile(path8);
4350
+ this.fsw._closeFile(path9);
4290
4351
  prevStats = newStats2;
4291
4352
  const closer2 = this._watchWithNodeFs(file, listener);
4292
4353
  if (closer2)
4293
- this.fsw._addPathCloser(path8, closer2);
4354
+ this.fsw._addPathCloser(path9, closer2);
4294
4355
  } else {
4295
4356
  prevStats = newStats2;
4296
4357
  }
4297
4358
  } catch (error) {
4298
- this.fsw._remove(dirname4, basename3);
4359
+ this.fsw._remove(dirname5, basename4);
4299
4360
  }
4300
- } else if (parent.has(basename3)) {
4361
+ } else if (parent.has(basename4)) {
4301
4362
  const at = newStats.atimeMs;
4302
4363
  const mt = newStats.mtimeMs;
4303
4364
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -4322,7 +4383,7 @@ var NodeFsHandler = class {
4322
4383
  * @param item basename of this item
4323
4384
  * @returns true if no more processing is needed for this entry.
4324
4385
  */
4325
- async _handleSymlink(entry, directory, path8, item) {
4386
+ async _handleSymlink(entry, directory, path9, item) {
4326
4387
  if (this.fsw.closed) {
4327
4388
  return;
4328
4389
  }
@@ -4332,7 +4393,7 @@ var NodeFsHandler = class {
4332
4393
  this.fsw._incrReadyCount();
4333
4394
  let linkPath;
4334
4395
  try {
4335
- linkPath = await (0, import_promises2.realpath)(path8);
4396
+ linkPath = await (0, import_promises2.realpath)(path9);
4336
4397
  } catch (e) {
4337
4398
  this.fsw._emitReady();
4338
4399
  return true;
@@ -4342,12 +4403,12 @@ var NodeFsHandler = class {
4342
4403
  if (dir.has(item)) {
4343
4404
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
4344
4405
  this.fsw._symlinkPaths.set(full, linkPath);
4345
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
4406
+ this.fsw._emit(EV.CHANGE, path9, entry.stats);
4346
4407
  }
4347
4408
  } else {
4348
4409
  dir.add(item);
4349
4410
  this.fsw._symlinkPaths.set(full, linkPath);
4350
- this.fsw._emit(EV.ADD, path8, entry.stats);
4411
+ this.fsw._emit(EV.ADD, path9, entry.stats);
4351
4412
  }
4352
4413
  this.fsw._emitReady();
4353
4414
  return true;
@@ -4377,9 +4438,9 @@ var NodeFsHandler = class {
4377
4438
  return;
4378
4439
  }
4379
4440
  const item = entry.path;
4380
- let path8 = sp.join(directory, item);
4441
+ let path9 = sp.join(directory, item);
4381
4442
  current.add(item);
4382
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
4443
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
4383
4444
  return;
4384
4445
  }
4385
4446
  if (this.fsw.closed) {
@@ -4388,8 +4449,8 @@ var NodeFsHandler = class {
4388
4449
  }
4389
4450
  if (item === target || !target && !previous.has(item)) {
4390
4451
  this.fsw._incrReadyCount();
4391
- path8 = sp.join(dir, sp.relative(dir, path8));
4392
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
4452
+ path9 = sp.join(dir, sp.relative(dir, path9));
4453
+ this._addToNodeFs(path9, initialAdd, wh, depth + 1);
4393
4454
  }
4394
4455
  }).on(EV.ERROR, this._boundHandleError);
4395
4456
  return new Promise((resolve4, reject) => {
@@ -4458,13 +4519,13 @@ var NodeFsHandler = class {
4458
4519
  * @param depth Child path actually targeted for watch
4459
4520
  * @param target Child path actually targeted for watch
4460
4521
  */
4461
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
4522
+ async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
4462
4523
  const ready = this.fsw._emitReady;
4463
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
4524
+ if (this.fsw._isIgnored(path9) || this.fsw.closed) {
4464
4525
  ready();
4465
4526
  return false;
4466
4527
  }
4467
- const wh = this.fsw._getWatchHelpers(path8);
4528
+ const wh = this.fsw._getWatchHelpers(path9);
4468
4529
  if (priorWh) {
4469
4530
  wh.filterPath = (entry) => priorWh.filterPath(entry);
4470
4531
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -4480,8 +4541,8 @@ var NodeFsHandler = class {
4480
4541
  const follow = this.fsw.options.followSymlinks;
4481
4542
  let closer;
4482
4543
  if (stats.isDirectory()) {
4483
- const absPath = sp.resolve(path8);
4484
- 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;
4485
4546
  if (this.fsw.closed)
4486
4547
  return;
4487
4548
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -4491,29 +4552,29 @@ var NodeFsHandler = class {
4491
4552
  this.fsw._symlinkPaths.set(absPath, targetPath);
4492
4553
  }
4493
4554
  } else if (stats.isSymbolicLink()) {
4494
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
4555
+ const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
4495
4556
  if (this.fsw.closed)
4496
4557
  return;
4497
4558
  const parent = sp.dirname(wh.watchPath);
4498
4559
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
4499
4560
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
4500
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
4561
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
4501
4562
  if (this.fsw.closed)
4502
4563
  return;
4503
4564
  if (targetPath !== void 0) {
4504
- this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
4565
+ this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
4505
4566
  }
4506
4567
  } else {
4507
4568
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
4508
4569
  }
4509
4570
  ready();
4510
4571
  if (closer)
4511
- this.fsw._addPathCloser(path8, closer);
4572
+ this.fsw._addPathCloser(path9, closer);
4512
4573
  return false;
4513
4574
  } catch (error) {
4514
4575
  if (this.fsw._handleError(error)) {
4515
4576
  ready();
4516
- return path8;
4577
+ return path9;
4517
4578
  }
4518
4579
  }
4519
4580
  }
@@ -4556,24 +4617,24 @@ function createPattern(matcher) {
4556
4617
  }
4557
4618
  return () => false;
4558
4619
  }
4559
- function normalizePath(path8) {
4560
- if (typeof path8 !== "string")
4620
+ function normalizePath(path9) {
4621
+ if (typeof path9 !== "string")
4561
4622
  throw new Error("string expected");
4562
- path8 = sp2.normalize(path8);
4563
- path8 = path8.replace(/\\/g, "/");
4623
+ path9 = sp2.normalize(path9);
4624
+ path9 = path9.replace(/\\/g, "/");
4564
4625
  let prepend = false;
4565
- if (path8.startsWith("//"))
4626
+ if (path9.startsWith("//"))
4566
4627
  prepend = true;
4567
- path8 = path8.replace(DOUBLE_SLASH_RE, "/");
4628
+ path9 = path9.replace(DOUBLE_SLASH_RE, "/");
4568
4629
  if (prepend)
4569
- path8 = "/" + path8;
4570
- return path8;
4630
+ path9 = "/" + path9;
4631
+ return path9;
4571
4632
  }
4572
4633
  function matchPatterns(patterns, testString, stats) {
4573
- const path8 = normalizePath(testString);
4634
+ const path9 = normalizePath(testString);
4574
4635
  for (let index = 0; index < patterns.length; index++) {
4575
4636
  const pattern = patterns[index];
4576
- if (pattern(path8, stats)) {
4637
+ if (pattern(path9, stats)) {
4577
4638
  return true;
4578
4639
  }
4579
4640
  }
@@ -4611,19 +4672,19 @@ var toUnix = (string) => {
4611
4672
  }
4612
4673
  return str;
4613
4674
  };
4614
- var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
4615
- var normalizeIgnored = (cwd = "") => (path8) => {
4616
- if (typeof path8 === "string") {
4617
- 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));
4618
4679
  } else {
4619
- return path8;
4680
+ return path9;
4620
4681
  }
4621
4682
  };
4622
- var getAbsolutePath = (path8, cwd) => {
4623
- if (sp2.isAbsolute(path8)) {
4624
- return path8;
4683
+ var getAbsolutePath = (path9, cwd) => {
4684
+ if (sp2.isAbsolute(path9)) {
4685
+ return path9;
4625
4686
  }
4626
- return sp2.join(cwd, path8);
4687
+ return sp2.join(cwd, path9);
4627
4688
  };
4628
4689
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
4629
4690
  var DirEntry = class {
@@ -4688,10 +4749,10 @@ var WatchHelper = class {
4688
4749
  dirParts;
4689
4750
  followSymlinks;
4690
4751
  statMethod;
4691
- constructor(path8, follow, fsw) {
4752
+ constructor(path9, follow, fsw) {
4692
4753
  this.fsw = fsw;
4693
- const watchPath = path8;
4694
- this.path = path8 = path8.replace(REPLACER_RE, "");
4754
+ const watchPath = path9;
4755
+ this.path = path9 = path9.replace(REPLACER_RE, "");
4695
4756
  this.watchPath = watchPath;
4696
4757
  this.fullWatchPath = sp2.resolve(watchPath);
4697
4758
  this.dirParts = [];
@@ -4831,20 +4892,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4831
4892
  this._closePromise = void 0;
4832
4893
  let paths = unifyPaths(paths_);
4833
4894
  if (cwd) {
4834
- paths = paths.map((path8) => {
4835
- const absPath = getAbsolutePath(path8, cwd);
4895
+ paths = paths.map((path9) => {
4896
+ const absPath = getAbsolutePath(path9, cwd);
4836
4897
  return absPath;
4837
4898
  });
4838
4899
  }
4839
- paths.forEach((path8) => {
4840
- this._removeIgnoredPath(path8);
4900
+ paths.forEach((path9) => {
4901
+ this._removeIgnoredPath(path9);
4841
4902
  });
4842
4903
  this._userIgnored = void 0;
4843
4904
  if (!this._readyCount)
4844
4905
  this._readyCount = 0;
4845
4906
  this._readyCount += paths.length;
4846
- Promise.all(paths.map(async (path8) => {
4847
- 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);
4848
4909
  if (res)
4849
4910
  this._emitReady();
4850
4911
  return res;
@@ -4866,17 +4927,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4866
4927
  return this;
4867
4928
  const paths = unifyPaths(paths_);
4868
4929
  const { cwd } = this.options;
4869
- paths.forEach((path8) => {
4870
- if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
4930
+ paths.forEach((path9) => {
4931
+ if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
4871
4932
  if (cwd)
4872
- path8 = sp2.join(cwd, path8);
4873
- path8 = sp2.resolve(path8);
4933
+ path9 = sp2.join(cwd, path9);
4934
+ path9 = sp2.resolve(path9);
4874
4935
  }
4875
- this._closePath(path8);
4876
- this._addIgnoredPath(path8);
4877
- if (this._watched.has(path8)) {
4936
+ this._closePath(path9);
4937
+ this._addIgnoredPath(path9);
4938
+ if (this._watched.has(path9)) {
4878
4939
  this._addIgnoredPath({
4879
- path: path8,
4940
+ path: path9,
4880
4941
  recursive: true
4881
4942
  });
4882
4943
  }
@@ -4940,38 +5001,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4940
5001
  * @param stats arguments to be passed with event
4941
5002
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
4942
5003
  */
4943
- async _emit(event, path8, stats) {
5004
+ async _emit(event, path9, stats) {
4944
5005
  if (this.closed)
4945
5006
  return;
4946
5007
  const opts = this.options;
4947
5008
  if (isWindows)
4948
- path8 = sp2.normalize(path8);
5009
+ path9 = sp2.normalize(path9);
4949
5010
  if (opts.cwd)
4950
- path8 = sp2.relative(opts.cwd, path8);
4951
- const args = [path8];
5011
+ path9 = sp2.relative(opts.cwd, path9);
5012
+ const args = [path9];
4952
5013
  if (stats != null)
4953
5014
  args.push(stats);
4954
5015
  const awf = opts.awaitWriteFinish;
4955
5016
  let pw;
4956
- if (awf && (pw = this._pendingWrites.get(path8))) {
5017
+ if (awf && (pw = this._pendingWrites.get(path9))) {
4957
5018
  pw.lastChange = /* @__PURE__ */ new Date();
4958
5019
  return this;
4959
5020
  }
4960
5021
  if (opts.atomic) {
4961
5022
  if (event === EVENTS.UNLINK) {
4962
- this._pendingUnlinks.set(path8, [event, ...args]);
5023
+ this._pendingUnlinks.set(path9, [event, ...args]);
4963
5024
  setTimeout(() => {
4964
- this._pendingUnlinks.forEach((entry, path9) => {
5025
+ this._pendingUnlinks.forEach((entry, path10) => {
4965
5026
  this.emit(...entry);
4966
5027
  this.emit(EVENTS.ALL, ...entry);
4967
- this._pendingUnlinks.delete(path9);
5028
+ this._pendingUnlinks.delete(path10);
4968
5029
  });
4969
5030
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
4970
5031
  return this;
4971
5032
  }
4972
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
5033
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
4973
5034
  event = EVENTS.CHANGE;
4974
- this._pendingUnlinks.delete(path8);
5035
+ this._pendingUnlinks.delete(path9);
4975
5036
  }
4976
5037
  }
4977
5038
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -4989,16 +5050,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4989
5050
  this.emitWithAll(event, args);
4990
5051
  }
4991
5052
  };
4992
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
5053
+ this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
4993
5054
  return this;
4994
5055
  }
4995
5056
  if (event === EVENTS.CHANGE) {
4996
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
5057
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
4997
5058
  if (isThrottled)
4998
5059
  return this;
4999
5060
  }
5000
5061
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
5001
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
5062
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
5002
5063
  let stats2;
5003
5064
  try {
5004
5065
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -5029,23 +5090,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5029
5090
  * @param timeout duration of time to suppress duplicate actions
5030
5091
  * @returns tracking object or false if action should be suppressed
5031
5092
  */
5032
- _throttle(actionType, path8, timeout) {
5093
+ _throttle(actionType, path9, timeout) {
5033
5094
  if (!this._throttled.has(actionType)) {
5034
5095
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
5035
5096
  }
5036
5097
  const action = this._throttled.get(actionType);
5037
5098
  if (!action)
5038
5099
  throw new Error("invalid throttle");
5039
- const actionPath = action.get(path8);
5100
+ const actionPath = action.get(path9);
5040
5101
  if (actionPath) {
5041
5102
  actionPath.count++;
5042
5103
  return false;
5043
5104
  }
5044
5105
  let timeoutObject;
5045
5106
  const clear = () => {
5046
- const item = action.get(path8);
5107
+ const item = action.get(path9);
5047
5108
  const count = item ? item.count : 0;
5048
- action.delete(path8);
5109
+ action.delete(path9);
5049
5110
  clearTimeout(timeoutObject);
5050
5111
  if (item)
5051
5112
  clearTimeout(item.timeoutObject);
@@ -5053,7 +5114,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5053
5114
  };
5054
5115
  timeoutObject = setTimeout(clear, timeout);
5055
5116
  const thr = { timeoutObject, clear, count: 0 };
5056
- action.set(path8, thr);
5117
+ action.set(path9, thr);
5057
5118
  return thr;
5058
5119
  }
5059
5120
  _incrReadyCount() {
@@ -5067,44 +5128,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5067
5128
  * @param event
5068
5129
  * @param awfEmit Callback to be called when ready for event to be emitted.
5069
5130
  */
5070
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
5131
+ _awaitWriteFinish(path9, threshold, event, awfEmit) {
5071
5132
  const awf = this.options.awaitWriteFinish;
5072
5133
  if (typeof awf !== "object")
5073
5134
  return;
5074
5135
  const pollInterval = awf.pollInterval;
5075
5136
  let timeoutHandler;
5076
- let fullPath = path8;
5077
- if (this.options.cwd && !sp2.isAbsolute(path8)) {
5078
- 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);
5079
5140
  }
5080
5141
  const now = /* @__PURE__ */ new Date();
5081
5142
  const writes = this._pendingWrites;
5082
5143
  function awaitWriteFinishFn(prevStat) {
5083
5144
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
5084
- if (err || !writes.has(path8)) {
5145
+ if (err || !writes.has(path9)) {
5085
5146
  if (err && err.code !== "ENOENT")
5086
5147
  awfEmit(err);
5087
5148
  return;
5088
5149
  }
5089
5150
  const now2 = Number(/* @__PURE__ */ new Date());
5090
5151
  if (prevStat && curStat.size !== prevStat.size) {
5091
- writes.get(path8).lastChange = now2;
5152
+ writes.get(path9).lastChange = now2;
5092
5153
  }
5093
- const pw = writes.get(path8);
5154
+ const pw = writes.get(path9);
5094
5155
  const df = now2 - pw.lastChange;
5095
5156
  if (df >= threshold) {
5096
- writes.delete(path8);
5157
+ writes.delete(path9);
5097
5158
  awfEmit(void 0, curStat);
5098
5159
  } else {
5099
5160
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
5100
5161
  }
5101
5162
  });
5102
5163
  }
5103
- if (!writes.has(path8)) {
5104
- writes.set(path8, {
5164
+ if (!writes.has(path9)) {
5165
+ writes.set(path9, {
5105
5166
  lastChange: now,
5106
5167
  cancelWait: () => {
5107
- writes.delete(path8);
5168
+ writes.delete(path9);
5108
5169
  clearTimeout(timeoutHandler);
5109
5170
  return event;
5110
5171
  }
@@ -5115,8 +5176,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5115
5176
  /**
5116
5177
  * Determines whether user has asked to ignore this path.
5117
5178
  */
5118
- _isIgnored(path8, stats) {
5119
- if (this.options.atomic && DOT_RE.test(path8))
5179
+ _isIgnored(path9, stats) {
5180
+ if (this.options.atomic && DOT_RE.test(path9))
5120
5181
  return true;
5121
5182
  if (!this._userIgnored) {
5122
5183
  const { cwd } = this.options;
@@ -5126,17 +5187,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5126
5187
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
5127
5188
  this._userIgnored = anymatch(list, void 0);
5128
5189
  }
5129
- return this._userIgnored(path8, stats);
5190
+ return this._userIgnored(path9, stats);
5130
5191
  }
5131
- _isntIgnored(path8, stat4) {
5132
- return !this._isIgnored(path8, stat4);
5192
+ _isntIgnored(path9, stat4) {
5193
+ return !this._isIgnored(path9, stat4);
5133
5194
  }
5134
5195
  /**
5135
5196
  * Provides a set of common helpers and properties relating to symlink handling.
5136
5197
  * @param path file or directory pattern being watched
5137
5198
  */
5138
- _getWatchHelpers(path8) {
5139
- return new WatchHelper(path8, this.options.followSymlinks, this);
5199
+ _getWatchHelpers(path9) {
5200
+ return new WatchHelper(path9, this.options.followSymlinks, this);
5140
5201
  }
5141
5202
  // Directory helpers
5142
5203
  // -----------------
@@ -5168,63 +5229,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
5168
5229
  * @param item base path of item/directory
5169
5230
  */
5170
5231
  _remove(directory, item, isDirectory) {
5171
- const path8 = sp2.join(directory, item);
5172
- const fullPath = sp2.resolve(path8);
5173
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
5174
- 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))
5175
5236
  return;
5176
5237
  if (!isDirectory && this._watched.size === 1) {
5177
5238
  this.add(directory, item, true);
5178
5239
  }
5179
- const wp = this._getWatchedDir(path8);
5240
+ const wp = this._getWatchedDir(path9);
5180
5241
  const nestedDirectoryChildren = wp.getChildren();
5181
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
5242
+ nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
5182
5243
  const parent = this._getWatchedDir(directory);
5183
5244
  const wasTracked = parent.has(item);
5184
5245
  parent.remove(item);
5185
5246
  if (this._symlinkPaths.has(fullPath)) {
5186
5247
  this._symlinkPaths.delete(fullPath);
5187
5248
  }
5188
- let relPath = path8;
5249
+ let relPath = path9;
5189
5250
  if (this.options.cwd)
5190
- relPath = sp2.relative(this.options.cwd, path8);
5251
+ relPath = sp2.relative(this.options.cwd, path9);
5191
5252
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
5192
5253
  const event = this._pendingWrites.get(relPath).cancelWait();
5193
5254
  if (event === EVENTS.ADD)
5194
5255
  return;
5195
5256
  }
5196
- this._watched.delete(path8);
5257
+ this._watched.delete(path9);
5197
5258
  this._watched.delete(fullPath);
5198
5259
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
5199
- if (wasTracked && !this._isIgnored(path8))
5200
- this._emit(eventName, path8);
5201
- this._closePath(path8);
5260
+ if (wasTracked && !this._isIgnored(path9))
5261
+ this._emit(eventName, path9);
5262
+ this._closePath(path9);
5202
5263
  }
5203
5264
  /**
5204
5265
  * Closes all watchers for a path
5205
5266
  */
5206
- _closePath(path8) {
5207
- this._closeFile(path8);
5208
- const dir = sp2.dirname(path8);
5209
- 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));
5210
5271
  }
5211
5272
  /**
5212
5273
  * Closes only file-specific watchers
5213
5274
  */
5214
- _closeFile(path8) {
5215
- const closers = this._closers.get(path8);
5275
+ _closeFile(path9) {
5276
+ const closers = this._closers.get(path9);
5216
5277
  if (!closers)
5217
5278
  return;
5218
5279
  closers.forEach((closer) => closer());
5219
- this._closers.delete(path8);
5280
+ this._closers.delete(path9);
5220
5281
  }
5221
- _addPathCloser(path8, closer) {
5282
+ _addPathCloser(path9, closer) {
5222
5283
  if (!closer)
5223
5284
  return;
5224
- let list = this._closers.get(path8);
5285
+ let list = this._closers.get(path9);
5225
5286
  if (!list) {
5226
5287
  list = [];
5227
- this._closers.set(path8, list);
5288
+ this._closers.set(path9, list);
5228
5289
  }
5229
5290
  list.push(closer);
5230
5291
  }
@@ -5319,7 +5380,7 @@ var FileWatcher = class {
5319
5380
  return;
5320
5381
  }
5321
5382
  const changes = Array.from(this.pendingChanges.entries()).map(
5322
- ([path8, type]) => ({ path: path8, type })
5383
+ ([path9, type]) => ({ path: path9, type })
5323
5384
  );
5324
5385
  this.pendingChanges.clear();
5325
5386
  try {
@@ -5499,7 +5560,7 @@ var index_codebase = (0, import_plugin.tool)({
5499
5560
  estimateOnly: z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
5500
5561
  verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
5501
5562
  },
5502
- async execute(args) {
5563
+ async execute(args, context) {
5503
5564
  const indexer = getIndexer();
5504
5565
  if (args.estimateOnly) {
5505
5566
  const estimate = await indexer.estimateCost();
@@ -5508,7 +5569,19 @@ var index_codebase = (0, import_plugin.tool)({
5508
5569
  if (args.force) {
5509
5570
  await indexer.clearIndex();
5510
5571
  }
5511
- 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
+ });
5512
5585
  return formatIndexStats(stats, args.verbose ?? false);
5513
5586
  }
5514
5587
  });
@@ -5607,12 +5680,91 @@ function formatStatus(status) {
5607
5680
  }
5608
5681
  return lines.join("\n");
5609
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
+ }
5610
5754
 
5611
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
+ }
5612
5764
  function loadJsonFile(filePath) {
5613
5765
  try {
5614
- if ((0, import_fs5.existsSync)(filePath)) {
5615
- 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");
5616
5768
  return JSON.parse(content);
5617
5769
  }
5618
5770
  } catch {
@@ -5620,11 +5772,11 @@ function loadJsonFile(filePath) {
5620
5772
  return null;
5621
5773
  }
5622
5774
  function loadPluginConfig(projectRoot) {
5623
- const projectConfig = loadJsonFile(path7.join(projectRoot, ".opencode", "codebase-index.json"));
5775
+ const projectConfig = loadJsonFile(path8.join(projectRoot, ".opencode", "codebase-index.json"));
5624
5776
  if (projectConfig) {
5625
5777
  return projectConfig;
5626
5778
  }
5627
- const globalConfigPath = path7.join(os3.homedir(), ".config", "opencode", "codebase-index.json");
5779
+ const globalConfigPath = path8.join(os3.homedir(), ".config", "opencode", "codebase-index.json");
5628
5780
  const globalConfig = loadJsonFile(globalConfigPath);
5629
5781
  if (globalConfig) {
5630
5782
  return globalConfig;
@@ -5656,36 +5808,11 @@ var plugin = async ({ directory }) => {
5656
5808
  },
5657
5809
  async config(cfg) {
5658
5810
  cfg.command = cfg.command ?? {};
5659
- cfg.command["search"] = {
5660
- description: "Search codebase by meaning using semantic search",
5661
- template: `Use the \`codebase_search\` tool to find code related to: $ARGUMENTS
5662
-
5663
- If the index doesn't exist yet, run \`index_codebase\` first.
5664
-
5665
- Return the most relevant results with file paths and line numbers.`
5666
- };
5667
- cfg.command["find"] = {
5668
- description: "Find code using hybrid approach (semantic + grep)",
5669
- template: `Find code related to: $ARGUMENTS
5670
-
5671
- Strategy:
5672
- 1. First use \`codebase_search\` to find semantically related code
5673
- 2. From the results, identify specific function/class names
5674
- 3. Use grep to find all occurrences of those identifiers
5675
- 4. Combine findings into a comprehensive answer
5676
-
5677
- If the semantic index doesn't exist, run \`index_codebase\` first.`
5678
- };
5679
- cfg.command["index"] = {
5680
- description: "Index the codebase for semantic search",
5681
- template: `Run the \`index_codebase\` tool to create or update the semantic search index.
5682
-
5683
- Show progress and final statistics including:
5684
- - Number of files processed
5685
- - Number of chunks indexed
5686
- - Tokens used
5687
- - Duration`
5688
- };
5811
+ const commandsDir = getCommandsDir();
5812
+ const commands = loadCommandsFromDirectory(commandsDir);
5813
+ for (const [name, definition] of commands) {
5814
+ cfg.command[name] = definition;
5815
+ }
5689
5816
  }
5690
5817
  };
5691
5818
  };