opencode-codebase-index 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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(path7, 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(path7);
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 = (path7, originalPath, doThrow) => {
525
+ if (!isString(path7)) {
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 (!path7) {
532
532
  return doThrow(`path must not be empty`, TypeError);
533
533
  }
534
- if (checkPath.isNotRelative(path8)) {
534
+ if (checkPath.isNotRelative(path7)) {
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 = (path7) => REGEX_TEST_INVALID_PATH.test(path7);
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 path7 = originalPath && checkPath.convert(originalPath);
574
574
  checkPath(
575
- path8,
575
+ path7,
576
576
  originalPath,
577
577
  this._strictPathCheck ? throwError : RETURN_FALSE
578
578
  );
579
- return this._t(path8, cache, checkUnignored, slices);
579
+ return this._t(path7, cache, checkUnignored, slices);
580
580
  }
581
- checkIgnore(path8) {
582
- if (!REGEX_TEST_TRAILING_SLASH.test(path8)) {
583
- return this.test(path8);
581
+ checkIgnore(path7) {
582
+ if (!REGEX_TEST_TRAILING_SLASH.test(path7)) {
583
+ return this.test(path7);
584
584
  }
585
- const slices = path8.split(SLASH2).filter(Boolean);
585
+ const slices = path7.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(path7, false, MODE_CHECK_IGNORE);
599
599
  }
600
- _t(path8, cache, checkUnignored, slices) {
601
- if (path8 in cache) {
602
- return cache[path8];
600
+ _t(path7, cache, checkUnignored, slices) {
601
+ if (path7 in cache) {
602
+ return cache[path7];
603
603
  }
604
604
  if (!slices) {
605
- slices = path8.split(SLASH2).filter(Boolean);
605
+ slices = path7.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[path7] = this._rules.test(path7, 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[path7] = parent.ignored ? parent : this._rules.test(path7, checkUnignored, MODE_IGNORE);
618
618
  }
619
- ignores(path8) {
620
- return this._test(path8, this._ignoreCache, false).ignored;
619
+ ignores(path7) {
620
+ return this._test(path7, this._ignoreCache, false).ignored;
621
621
  }
622
622
  createFilter() {
623
- return (path8) => !this.ignores(path8);
623
+ return (path7) => !this.ignores(path7);
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(path7) {
630
+ return this._test(path7, 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 = (path7) => checkPath(path7 && checkPath.convert(path7), path7, 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 = (path7) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path7) || isNotRelative(path7);
640
640
  };
641
641
  if (
642
642
  // Detect `process` so that it can run in browsers.
@@ -657,8 +657,8 @@ __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_fs4 = require("fs");
661
+ var path6 = __toESM(require("path"), 1);
662
662
 
663
663
  // src/config/schema.ts
664
664
  var DEFAULT_INCLUDE = [
@@ -693,6 +693,8 @@ function getDefaultIndexingConfig() {
693
693
  autoIndex: false,
694
694
  watchFiles: true,
695
695
  maxFileSize: 1048576,
696
+ maxChunksPerFile: 100,
697
+ semanticOnly: false,
696
698
  retries: 3,
697
699
  retryDelayMs: 1e3
698
700
  };
@@ -726,6 +728,8 @@ function parseConfig(raw) {
726
728
  autoIndex: typeof rawIndexing.autoIndex === "boolean" ? rawIndexing.autoIndex : defaultIndexing.autoIndex,
727
729
  watchFiles: typeof rawIndexing.watchFiles === "boolean" ? rawIndexing.watchFiles : defaultIndexing.watchFiles,
728
730
  maxFileSize: typeof rawIndexing.maxFileSize === "number" ? rawIndexing.maxFileSize : defaultIndexing.maxFileSize,
731
+ maxChunksPerFile: typeof rawIndexing.maxChunksPerFile === "number" ? Math.max(1, rawIndexing.maxChunksPerFile) : defaultIndexing.maxChunksPerFile,
732
+ semanticOnly: typeof rawIndexing.semanticOnly === "boolean" ? rawIndexing.semanticOnly : defaultIndexing.semanticOnly,
729
733
  retries: typeof rawIndexing.retries === "number" ? rawIndexing.retries : defaultIndexing.retries,
730
734
  retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs
731
735
  };
@@ -807,8 +811,8 @@ function getDefaultModelForProvider(provider) {
807
811
  }
808
812
 
809
813
  // src/indexer/index.ts
810
- var import_fs4 = require("fs");
811
- var path5 = __toESM(require("path"), 1);
814
+ var import_fs3 = require("fs");
815
+ var path4 = __toESM(require("path"), 1);
812
816
 
813
817
  // node_modules/eventemitter3/index.mjs
814
818
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -2626,139 +2630,39 @@ function generateChunkId(filePath, chunk) {
2626
2630
  function generateChunkHash(chunk) {
2627
2631
  return hashContent(chunk.content);
2628
2632
  }
2629
-
2630
- // src/indexer/inverted-index.ts
2631
- var import_fs3 = require("fs");
2632
- var path4 = __toESM(require("path"), 1);
2633
2633
  var InvertedIndex = class {
2634
- indexPath;
2635
- termToChunks = /* @__PURE__ */ new Map();
2636
- chunkTokens = /* @__PURE__ */ new Map();
2637
- totalTokenCount = 0;
2634
+ inner;
2638
2635
  constructor(indexPath) {
2639
- this.indexPath = path4.join(indexPath, "inverted-index.json");
2636
+ this.inner = new native.InvertedIndex(indexPath);
2640
2637
  }
2641
2638
  load() {
2642
- if (!(0, import_fs3.existsSync)(this.indexPath)) {
2643
- return;
2644
- }
2645
- try {
2646
- const content = (0, import_fs3.readFileSync)(this.indexPath, "utf-8");
2647
- const data = JSON.parse(content);
2648
- for (const [term, chunkIds] of Object.entries(data.termToChunks)) {
2649
- this.termToChunks.set(term, new Set(chunkIds));
2650
- }
2651
- for (const [chunkId, tokens] of Object.entries(data.chunkTokens)) {
2652
- const tokenMap = new Map(Object.entries(tokens).map(([k, v]) => [k, v]));
2653
- this.chunkTokens.set(chunkId, tokenMap);
2654
- for (const count of tokenMap.values()) {
2655
- this.totalTokenCount += count;
2656
- }
2657
- }
2658
- } catch {
2659
- this.termToChunks.clear();
2660
- this.chunkTokens.clear();
2661
- this.totalTokenCount = 0;
2662
- }
2639
+ this.inner.load();
2663
2640
  }
2664
2641
  save() {
2665
- const data = {
2666
- termToChunks: {},
2667
- chunkTokens: {},
2668
- avgDocLength: this.getAvgDocLength()
2669
- };
2670
- for (const [term, chunkIds] of this.termToChunks) {
2671
- data.termToChunks[term] = Array.from(chunkIds);
2672
- }
2673
- for (const [chunkId, tokens] of this.chunkTokens) {
2674
- data.chunkTokens[chunkId] = Object.fromEntries(tokens);
2675
- }
2676
- (0, import_fs3.writeFileSync)(this.indexPath, JSON.stringify(data));
2642
+ this.inner.save();
2677
2643
  }
2678
2644
  addChunk(chunkId, content) {
2679
- const tokens = this.tokenize(content);
2680
- const termFreq = /* @__PURE__ */ new Map();
2681
- for (const token of tokens) {
2682
- termFreq.set(token, (termFreq.get(token) || 0) + 1);
2683
- const chunks = this.termToChunks.get(token) || /* @__PURE__ */ new Set();
2684
- chunks.add(chunkId);
2685
- this.termToChunks.set(token, chunks);
2686
- }
2687
- this.chunkTokens.set(chunkId, termFreq);
2688
- this.totalTokenCount += tokens.length;
2645
+ this.inner.addChunk(chunkId, content);
2689
2646
  }
2690
2647
  removeChunk(chunkId) {
2691
- const tokens = this.chunkTokens.get(chunkId);
2692
- if (!tokens) return;
2693
- for (const [token, count] of tokens) {
2694
- this.totalTokenCount -= count;
2695
- const chunks = this.termToChunks.get(token);
2696
- if (chunks) {
2697
- chunks.delete(chunkId);
2698
- if (chunks.size === 0) {
2699
- this.termToChunks.delete(token);
2700
- }
2701
- }
2702
- }
2703
- this.chunkTokens.delete(chunkId);
2648
+ return this.inner.removeChunk(chunkId);
2704
2649
  }
2705
- search(query) {
2706
- const queryTokens = this.tokenize(query);
2707
- if (queryTokens.length === 0) {
2708
- return /* @__PURE__ */ new Map();
2709
- }
2710
- const candidateChunks = /* @__PURE__ */ new Set();
2711
- for (const token of queryTokens) {
2712
- const chunks = this.termToChunks.get(token);
2713
- if (chunks) {
2714
- for (const chunkId of chunks) {
2715
- candidateChunks.add(chunkId);
2716
- }
2717
- }
2650
+ search(query, limit) {
2651
+ const results = this.inner.search(query, limit ?? 100);
2652
+ const map = /* @__PURE__ */ new Map();
2653
+ for (const r of results) {
2654
+ map.set(r.chunkId, r.score);
2718
2655
  }
2719
- const scores = /* @__PURE__ */ new Map();
2720
- const k1 = 1.2;
2721
- const b = 0.75;
2722
- const N = this.chunkTokens.size;
2723
- const avgDocLength = this.getAvgDocLength();
2724
- for (const chunkId of candidateChunks) {
2725
- const termFreq = this.chunkTokens.get(chunkId);
2726
- if (!termFreq) continue;
2727
- const docLength = Array.from(termFreq.values()).reduce((a, b2) => a + b2, 0);
2728
- let score = 0;
2729
- for (const term of queryTokens) {
2730
- const tf = termFreq.get(term) || 0;
2731
- if (tf === 0) continue;
2732
- const df = this.termToChunks.get(term)?.size || 0;
2733
- const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
2734
- const tfNorm = tf * (k1 + 1) / (tf + k1 * (1 - b + b * (docLength / avgDocLength)));
2735
- score += idf * tfNorm;
2736
- }
2737
- scores.set(chunkId, score);
2738
- }
2739
- const maxScore = Math.max(...scores.values(), 1);
2740
- for (const [chunkId, score] of scores) {
2741
- scores.set(chunkId, score / maxScore);
2742
- }
2743
- return scores;
2656
+ return map;
2744
2657
  }
2745
2658
  hasChunk(chunkId) {
2746
- return this.chunkTokens.has(chunkId);
2659
+ return this.inner.hasChunk(chunkId);
2747
2660
  }
2748
2661
  clear() {
2749
- this.termToChunks.clear();
2750
- this.chunkTokens.clear();
2751
- this.totalTokenCount = 0;
2662
+ this.inner.clear();
2752
2663
  }
2753
2664
  getDocumentCount() {
2754
- return this.chunkTokens.size;
2755
- }
2756
- getAvgDocLength() {
2757
- const count = this.chunkTokens.size;
2758
- return count > 0 ? this.totalTokenCount / count : 100;
2759
- }
2760
- tokenize(text) {
2761
- return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 2);
2665
+ return this.inner.documentCount();
2762
2666
  }
2763
2667
  };
2764
2668
 
@@ -2773,23 +2677,25 @@ var Indexer = class {
2773
2677
  detectedProvider = null;
2774
2678
  fileHashCache = /* @__PURE__ */ new Map();
2775
2679
  fileHashCachePath = "";
2680
+ failedBatchesPath = "";
2776
2681
  constructor(projectRoot, config) {
2777
2682
  this.projectRoot = projectRoot;
2778
2683
  this.config = config;
2779
2684
  this.indexPath = this.getIndexPath();
2780
- this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
2685
+ this.fileHashCachePath = path4.join(this.indexPath, "file-hashes.json");
2686
+ this.failedBatchesPath = path4.join(this.indexPath, "failed-batches.json");
2781
2687
  }
2782
2688
  getIndexPath() {
2783
2689
  if (this.config.scope === "global") {
2784
2690
  const homeDir = process.env.HOME || process.env.USERPROFILE || "";
2785
- return path5.join(homeDir, ".opencode", "global-index");
2691
+ return path4.join(homeDir, ".opencode", "global-index");
2786
2692
  }
2787
- return path5.join(this.projectRoot, ".opencode", "index");
2693
+ return path4.join(this.projectRoot, ".opencode", "index");
2788
2694
  }
2789
2695
  loadFileHashCache() {
2790
2696
  try {
2791
- if ((0, import_fs4.existsSync)(this.fileHashCachePath)) {
2792
- const data = (0, import_fs4.readFileSync)(this.fileHashCachePath, "utf-8");
2697
+ if ((0, import_fs3.existsSync)(this.fileHashCachePath)) {
2698
+ const data = (0, import_fs3.readFileSync)(this.fileHashCachePath, "utf-8");
2793
2699
  const parsed = JSON.parse(data);
2794
2700
  this.fileHashCache = new Map(Object.entries(parsed));
2795
2701
  }
@@ -2802,7 +2708,38 @@ var Indexer = class {
2802
2708
  for (const [k, v] of this.fileHashCache) {
2803
2709
  obj[k] = v;
2804
2710
  }
2805
- (0, import_fs4.writeFileSync)(this.fileHashCachePath, JSON.stringify(obj));
2711
+ (0, import_fs3.writeFileSync)(this.fileHashCachePath, JSON.stringify(obj));
2712
+ }
2713
+ loadFailedBatches() {
2714
+ try {
2715
+ if ((0, import_fs3.existsSync)(this.failedBatchesPath)) {
2716
+ const data = (0, import_fs3.readFileSync)(this.failedBatchesPath, "utf-8");
2717
+ return JSON.parse(data);
2718
+ }
2719
+ } catch {
2720
+ return [];
2721
+ }
2722
+ return [];
2723
+ }
2724
+ saveFailedBatches(batches) {
2725
+ if (batches.length === 0) {
2726
+ if ((0, import_fs3.existsSync)(this.failedBatchesPath)) {
2727
+ import_fs3.promises.unlink(this.failedBatchesPath).catch(() => {
2728
+ });
2729
+ }
2730
+ return;
2731
+ }
2732
+ (0, import_fs3.writeFileSync)(this.failedBatchesPath, JSON.stringify(batches, null, 2));
2733
+ }
2734
+ addFailedBatch(batch, error) {
2735
+ const existing = this.loadFailedBatches();
2736
+ existing.push({
2737
+ chunks: batch,
2738
+ error,
2739
+ attemptCount: 1,
2740
+ lastAttempt: (/* @__PURE__ */ new Date()).toISOString()
2741
+ });
2742
+ this.saveFailedBatches(existing);
2806
2743
  }
2807
2744
  async initialize() {
2808
2745
  this.detectedProvider = await detectEmbeddingProvider(this.config.embeddingProvider);
@@ -2815,15 +2752,16 @@ var Indexer = class {
2815
2752
  this.detectedProvider.credentials,
2816
2753
  this.detectedProvider.modelInfo
2817
2754
  );
2818
- await import_fs4.promises.mkdir(this.indexPath, { recursive: true });
2755
+ await import_fs3.promises.mkdir(this.indexPath, { recursive: true });
2819
2756
  const dimensions = this.detectedProvider.modelInfo.dimensions;
2820
- const storePath = path5.join(this.indexPath, "vectors");
2757
+ const storePath = path4.join(this.indexPath, "vectors");
2821
2758
  this.store = new VectorStore(storePath, dimensions);
2822
- const indexFilePath = path5.join(this.indexPath, "vectors.usearch");
2823
- if ((0, import_fs4.existsSync)(indexFilePath)) {
2759
+ const indexFilePath = path4.join(this.indexPath, "vectors.usearch");
2760
+ if ((0, import_fs3.existsSync)(indexFilePath)) {
2824
2761
  this.store.load();
2825
2762
  }
2826
- this.invertedIndex = new InvertedIndex(this.indexPath);
2763
+ const invertedIndexPath = path4.join(this.indexPath, "inverted-index.json");
2764
+ this.invertedIndex = new InvertedIndex(invertedIndexPath);
2827
2765
  this.invertedIndex.load();
2828
2766
  }
2829
2767
  async ensureInitialized() {
@@ -2887,7 +2825,7 @@ var Indexer = class {
2887
2825
  if (this.fileHashCache.get(f.path) === currentHash) {
2888
2826
  unchangedFilePaths.add(f.path);
2889
2827
  } else {
2890
- const content = await import_fs4.promises.readFile(f.path, "utf-8");
2828
+ const content = await import_fs3.promises.readFile(f.path, "utf-8");
2891
2829
  changedFiles.push({ path: f.path, content, hash: currentHash });
2892
2830
  }
2893
2831
  }
@@ -2922,14 +2860,22 @@ var Indexer = class {
2922
2860
  for (const parsed of parsedFiles) {
2923
2861
  currentFilePaths.add(parsed.path);
2924
2862
  if (parsed.chunks.length === 0) {
2925
- const relativePath = path5.relative(this.projectRoot, parsed.path);
2863
+ const relativePath = path4.relative(this.projectRoot, parsed.path);
2926
2864
  stats.parseFailures.push(relativePath);
2927
2865
  }
2866
+ let fileChunkCount = 0;
2928
2867
  for (const chunk of parsed.chunks) {
2868
+ if (fileChunkCount >= this.config.indexing.maxChunksPerFile) {
2869
+ break;
2870
+ }
2871
+ if (this.config.indexing.semanticOnly && chunk.chunkType === "other") {
2872
+ continue;
2873
+ }
2929
2874
  const id = generateChunkId(parsed.path, chunk);
2930
2875
  const contentHash = generateChunkHash(chunk);
2931
2876
  currentChunkIds.add(id);
2932
2877
  if (existingChunks.get(id) === contentHash) {
2878
+ fileChunkCount++;
2933
2879
  continue;
2934
2880
  }
2935
2881
  const text = createEmbeddingText(chunk, parsed.path);
@@ -2943,6 +2889,7 @@ var Indexer = class {
2943
2889
  hash: contentHash
2944
2890
  };
2945
2891
  pendingChunks.push({ id, text, content: chunk.content, metadata });
2892
+ fileChunkCount++;
2946
2893
  }
2947
2894
  }
2948
2895
  let removedCount = 0;
@@ -3032,6 +2979,7 @@ var Indexer = class {
3032
2979
  });
3033
2980
  } catch (error) {
3034
2981
  stats.failedChunks += batch.length;
2982
+ this.addFailedBatch(batch, String(error));
3035
2983
  console.error(`Failed to embed batch after retries: ${error}`);
3036
2984
  }
3037
2985
  });
@@ -3049,6 +2997,9 @@ var Indexer = class {
3049
2997
  this.fileHashCache = currentFileHashes;
3050
2998
  this.saveFileHashCache();
3051
2999
  stats.durationMs = Date.now() - startTime;
3000
+ if (stats.failedChunks > 0) {
3001
+ stats.failedBatchesPath = this.failedBatchesPath;
3002
+ }
3052
3003
  onProgress?.({
3053
3004
  phase: "complete",
3054
3005
  filesProcessed: files.length,
@@ -3091,7 +3042,7 @@ var Indexer = class {
3091
3042
  let contextEndLine = r.metadata.endLine;
3092
3043
  if (this.config.search.includeContext) {
3093
3044
  try {
3094
- const fileContent = await import_fs4.promises.readFile(
3045
+ const fileContent = await import_fs3.promises.readFile(
3095
3046
  r.metadata.filePath,
3096
3047
  "utf-8"
3097
3048
  );
@@ -3194,7 +3145,7 @@ var Indexer = class {
3194
3145
  const removedFilePaths = [];
3195
3146
  let removedCount = 0;
3196
3147
  for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
3197
- if (!(0, import_fs4.existsSync)(filePath)) {
3148
+ if (!(0, import_fs3.existsSync)(filePath)) {
3198
3149
  for (const key of chunkKeys) {
3199
3150
  store.remove(key);
3200
3151
  invertedIndex.removeChunk(key);
@@ -3209,6 +3160,58 @@ var Indexer = class {
3209
3160
  }
3210
3161
  return { removed: removedCount, filePaths: removedFilePaths };
3211
3162
  }
3163
+ async retryFailedBatches() {
3164
+ const { store, provider, invertedIndex } = await this.ensureInitialized();
3165
+ const failedBatches = this.loadFailedBatches();
3166
+ if (failedBatches.length === 0) {
3167
+ return { succeeded: 0, failed: 0, remaining: 0 };
3168
+ }
3169
+ let succeeded = 0;
3170
+ let failed = 0;
3171
+ const stillFailing = [];
3172
+ for (const batch of failedBatches) {
3173
+ try {
3174
+ const result = await pRetry(
3175
+ async () => {
3176
+ const texts = batch.chunks.map((c) => c.text);
3177
+ return provider.embedBatch(texts);
3178
+ },
3179
+ {
3180
+ retries: this.config.indexing.retries,
3181
+ minTimeout: this.config.indexing.retryDelayMs
3182
+ }
3183
+ );
3184
+ const items = batch.chunks.map((chunk, idx) => ({
3185
+ id: chunk.id,
3186
+ vector: result.embeddings[idx],
3187
+ metadata: chunk.metadata
3188
+ }));
3189
+ store.addBatch(items);
3190
+ for (const chunk of batch.chunks) {
3191
+ invertedIndex.removeChunk(chunk.id);
3192
+ invertedIndex.addChunk(chunk.id, chunk.content);
3193
+ }
3194
+ succeeded += batch.chunks.length;
3195
+ } catch (error) {
3196
+ failed += batch.chunks.length;
3197
+ stillFailing.push({
3198
+ ...batch,
3199
+ attemptCount: batch.attemptCount + 1,
3200
+ lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
3201
+ error: String(error)
3202
+ });
3203
+ }
3204
+ }
3205
+ this.saveFailedBatches(stillFailing);
3206
+ if (succeeded > 0) {
3207
+ store.save();
3208
+ invertedIndex.save();
3209
+ }
3210
+ return { succeeded, failed, remaining: stillFailing.length };
3211
+ }
3212
+ getFailedBatchesCount() {
3213
+ return this.loadFailedBatches().length;
3214
+ }
3212
3215
  };
3213
3216
 
3214
3217
  // node_modules/chokidar/index.js
@@ -3301,7 +3304,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3301
3304
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
3302
3305
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
3303
3306
  if (wantBigintFsStats) {
3304
- this._stat = (path8) => statMethod(path8, { bigint: true });
3307
+ this._stat = (path7) => statMethod(path7, { bigint: true });
3305
3308
  } else {
3306
3309
  this._stat = statMethod;
3307
3310
  }
@@ -3326,8 +3329,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3326
3329
  const par = this.parent;
3327
3330
  const fil = par && par.files;
3328
3331
  if (fil && fil.length > 0) {
3329
- const { path: path8, depth } = par;
3330
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
3332
+ const { path: path7, depth } = par;
3333
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path7));
3331
3334
  const awaited = await Promise.all(slice);
3332
3335
  for (const entry of awaited) {
3333
3336
  if (!entry)
@@ -3367,20 +3370,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
3367
3370
  this.reading = false;
3368
3371
  }
3369
3372
  }
3370
- async _exploreDir(path8, depth) {
3373
+ async _exploreDir(path7, depth) {
3371
3374
  let files;
3372
3375
  try {
3373
- files = await (0, import_promises.readdir)(path8, this._rdOptions);
3376
+ files = await (0, import_promises.readdir)(path7, this._rdOptions);
3374
3377
  } catch (error) {
3375
3378
  this._onError(error);
3376
3379
  }
3377
- return { files, depth, path: path8 };
3380
+ return { files, depth, path: path7 };
3378
3381
  }
3379
- async _formatEntry(dirent, path8) {
3382
+ async _formatEntry(dirent, path7) {
3380
3383
  let entry;
3381
3384
  const basename3 = this._isDirent ? dirent.name : dirent;
3382
3385
  try {
3383
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path8, basename3));
3386
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path7, basename3));
3384
3387
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename3 };
3385
3388
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
3386
3389
  } catch (err) {
@@ -3780,16 +3783,16 @@ var delFromSet = (main, prop, item) => {
3780
3783
  };
3781
3784
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
3782
3785
  var FsWatchInstances = /* @__PURE__ */ new Map();
3783
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
3786
+ function createFsWatchInstance(path7, options, listener, errHandler, emitRaw) {
3784
3787
  const handleEvent = (rawEvent, evPath) => {
3785
- listener(path8);
3786
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
3787
- if (evPath && path8 !== evPath) {
3788
- fsWatchBroadcast(sp.resolve(path8, evPath), KEY_LISTENERS, sp.join(path8, evPath));
3788
+ listener(path7);
3789
+ emitRaw(rawEvent, evPath, { watchedPath: path7 });
3790
+ if (evPath && path7 !== evPath) {
3791
+ fsWatchBroadcast(sp.resolve(path7, evPath), KEY_LISTENERS, sp.join(path7, evPath));
3789
3792
  }
3790
3793
  };
3791
3794
  try {
3792
- return (0, import_node_fs.watch)(path8, {
3795
+ return (0, import_node_fs.watch)(path7, {
3793
3796
  persistent: options.persistent
3794
3797
  }, handleEvent);
3795
3798
  } catch (error) {
@@ -3805,12 +3808,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
3805
3808
  listener(val1, val2, val3);
3806
3809
  });
3807
3810
  };
3808
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
3811
+ var setFsWatchListener = (path7, fullPath, options, handlers) => {
3809
3812
  const { listener, errHandler, rawEmitter } = handlers;
3810
3813
  let cont = FsWatchInstances.get(fullPath);
3811
3814
  let watcher;
3812
3815
  if (!options.persistent) {
3813
- watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
3816
+ watcher = createFsWatchInstance(path7, options, listener, errHandler, rawEmitter);
3814
3817
  if (!watcher)
3815
3818
  return;
3816
3819
  return watcher.close.bind(watcher);
@@ -3821,7 +3824,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
3821
3824
  addAndConvert(cont, KEY_RAW, rawEmitter);
3822
3825
  } else {
3823
3826
  watcher = createFsWatchInstance(
3824
- path8,
3827
+ path7,
3825
3828
  options,
3826
3829
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
3827
3830
  errHandler,
@@ -3836,7 +3839,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
3836
3839
  cont.watcherUnusable = true;
3837
3840
  if (isWindows && error.code === "EPERM") {
3838
3841
  try {
3839
- const fd = await (0, import_promises2.open)(path8, "r");
3842
+ const fd = await (0, import_promises2.open)(path7, "r");
3840
3843
  await fd.close();
3841
3844
  broadcastErr(error);
3842
3845
  } catch (err) {
@@ -3867,7 +3870,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
3867
3870
  };
3868
3871
  };
3869
3872
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
3870
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
3873
+ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
3871
3874
  const { listener, rawEmitter } = handlers;
3872
3875
  let cont = FsWatchFileInstances.get(fullPath);
3873
3876
  const copts = cont && cont.options;
@@ -3889,7 +3892,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
3889
3892
  });
3890
3893
  const currmtime = curr.mtimeMs;
3891
3894
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
3892
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
3895
+ foreach(cont.listeners, (listener2) => listener2(path7, curr));
3893
3896
  }
3894
3897
  })
3895
3898
  };
@@ -3919,13 +3922,13 @@ var NodeFsHandler = class {
3919
3922
  * @param listener on fs change
3920
3923
  * @returns closer for the watcher instance
3921
3924
  */
3922
- _watchWithNodeFs(path8, listener) {
3925
+ _watchWithNodeFs(path7, listener) {
3923
3926
  const opts = this.fsw.options;
3924
- const directory = sp.dirname(path8);
3925
- const basename3 = sp.basename(path8);
3927
+ const directory = sp.dirname(path7);
3928
+ const basename3 = sp.basename(path7);
3926
3929
  const parent = this.fsw._getWatchedDir(directory);
3927
3930
  parent.add(basename3);
3928
- const absolutePath = sp.resolve(path8);
3931
+ const absolutePath = sp.resolve(path7);
3929
3932
  const options = {
3930
3933
  persistent: opts.persistent
3931
3934
  };
@@ -3935,12 +3938,12 @@ var NodeFsHandler = class {
3935
3938
  if (opts.usePolling) {
3936
3939
  const enableBin = opts.interval !== opts.binaryInterval;
3937
3940
  options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
3938
- closer = setFsWatchFileListener(path8, absolutePath, options, {
3941
+ closer = setFsWatchFileListener(path7, absolutePath, options, {
3939
3942
  listener,
3940
3943
  rawEmitter: this.fsw._emitRaw
3941
3944
  });
3942
3945
  } else {
3943
- closer = setFsWatchListener(path8, absolutePath, options, {
3946
+ closer = setFsWatchListener(path7, absolutePath, options, {
3944
3947
  listener,
3945
3948
  errHandler: this._boundHandleError,
3946
3949
  rawEmitter: this.fsw._emitRaw
@@ -3962,7 +3965,7 @@ var NodeFsHandler = class {
3962
3965
  let prevStats = stats;
3963
3966
  if (parent.has(basename3))
3964
3967
  return;
3965
- const listener = async (path8, newStats) => {
3968
+ const listener = async (path7, newStats) => {
3966
3969
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
3967
3970
  return;
3968
3971
  if (!newStats || newStats.mtimeMs === 0) {
@@ -3976,11 +3979,11 @@ var NodeFsHandler = class {
3976
3979
  this.fsw._emit(EV.CHANGE, file, newStats2);
3977
3980
  }
3978
3981
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
3979
- this.fsw._closeFile(path8);
3982
+ this.fsw._closeFile(path7);
3980
3983
  prevStats = newStats2;
3981
3984
  const closer2 = this._watchWithNodeFs(file, listener);
3982
3985
  if (closer2)
3983
- this.fsw._addPathCloser(path8, closer2);
3986
+ this.fsw._addPathCloser(path7, closer2);
3984
3987
  } else {
3985
3988
  prevStats = newStats2;
3986
3989
  }
@@ -4012,7 +4015,7 @@ var NodeFsHandler = class {
4012
4015
  * @param item basename of this item
4013
4016
  * @returns true if no more processing is needed for this entry.
4014
4017
  */
4015
- async _handleSymlink(entry, directory, path8, item) {
4018
+ async _handleSymlink(entry, directory, path7, item) {
4016
4019
  if (this.fsw.closed) {
4017
4020
  return;
4018
4021
  }
@@ -4022,7 +4025,7 @@ var NodeFsHandler = class {
4022
4025
  this.fsw._incrReadyCount();
4023
4026
  let linkPath;
4024
4027
  try {
4025
- linkPath = await (0, import_promises2.realpath)(path8);
4028
+ linkPath = await (0, import_promises2.realpath)(path7);
4026
4029
  } catch (e) {
4027
4030
  this.fsw._emitReady();
4028
4031
  return true;
@@ -4032,12 +4035,12 @@ var NodeFsHandler = class {
4032
4035
  if (dir.has(item)) {
4033
4036
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
4034
4037
  this.fsw._symlinkPaths.set(full, linkPath);
4035
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
4038
+ this.fsw._emit(EV.CHANGE, path7, entry.stats);
4036
4039
  }
4037
4040
  } else {
4038
4041
  dir.add(item);
4039
4042
  this.fsw._symlinkPaths.set(full, linkPath);
4040
- this.fsw._emit(EV.ADD, path8, entry.stats);
4043
+ this.fsw._emit(EV.ADD, path7, entry.stats);
4041
4044
  }
4042
4045
  this.fsw._emitReady();
4043
4046
  return true;
@@ -4067,9 +4070,9 @@ var NodeFsHandler = class {
4067
4070
  return;
4068
4071
  }
4069
4072
  const item = entry.path;
4070
- let path8 = sp.join(directory, item);
4073
+ let path7 = sp.join(directory, item);
4071
4074
  current.add(item);
4072
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
4075
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path7, item)) {
4073
4076
  return;
4074
4077
  }
4075
4078
  if (this.fsw.closed) {
@@ -4078,8 +4081,8 @@ var NodeFsHandler = class {
4078
4081
  }
4079
4082
  if (item === target || !target && !previous.has(item)) {
4080
4083
  this.fsw._incrReadyCount();
4081
- path8 = sp.join(dir, sp.relative(dir, path8));
4082
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
4084
+ path7 = sp.join(dir, sp.relative(dir, path7));
4085
+ this._addToNodeFs(path7, initialAdd, wh, depth + 1);
4083
4086
  }
4084
4087
  }).on(EV.ERROR, this._boundHandleError);
4085
4088
  return new Promise((resolve4, reject) => {
@@ -4148,13 +4151,13 @@ var NodeFsHandler = class {
4148
4151
  * @param depth Child path actually targeted for watch
4149
4152
  * @param target Child path actually targeted for watch
4150
4153
  */
4151
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
4154
+ async _addToNodeFs(path7, initialAdd, priorWh, depth, target) {
4152
4155
  const ready = this.fsw._emitReady;
4153
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
4156
+ if (this.fsw._isIgnored(path7) || this.fsw.closed) {
4154
4157
  ready();
4155
4158
  return false;
4156
4159
  }
4157
- const wh = this.fsw._getWatchHelpers(path8);
4160
+ const wh = this.fsw._getWatchHelpers(path7);
4158
4161
  if (priorWh) {
4159
4162
  wh.filterPath = (entry) => priorWh.filterPath(entry);
4160
4163
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -4170,8 +4173,8 @@ var NodeFsHandler = class {
4170
4173
  const follow = this.fsw.options.followSymlinks;
4171
4174
  let closer;
4172
4175
  if (stats.isDirectory()) {
4173
- const absPath = sp.resolve(path8);
4174
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
4176
+ const absPath = sp.resolve(path7);
4177
+ const targetPath = follow ? await (0, import_promises2.realpath)(path7) : path7;
4175
4178
  if (this.fsw.closed)
4176
4179
  return;
4177
4180
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -4181,29 +4184,29 @@ var NodeFsHandler = class {
4181
4184
  this.fsw._symlinkPaths.set(absPath, targetPath);
4182
4185
  }
4183
4186
  } else if (stats.isSymbolicLink()) {
4184
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
4187
+ const targetPath = follow ? await (0, import_promises2.realpath)(path7) : path7;
4185
4188
  if (this.fsw.closed)
4186
4189
  return;
4187
4190
  const parent = sp.dirname(wh.watchPath);
4188
4191
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
4189
4192
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
4190
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
4193
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path7, wh, targetPath);
4191
4194
  if (this.fsw.closed)
4192
4195
  return;
4193
4196
  if (targetPath !== void 0) {
4194
- this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
4197
+ this.fsw._symlinkPaths.set(sp.resolve(path7), targetPath);
4195
4198
  }
4196
4199
  } else {
4197
4200
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
4198
4201
  }
4199
4202
  ready();
4200
4203
  if (closer)
4201
- this.fsw._addPathCloser(path8, closer);
4204
+ this.fsw._addPathCloser(path7, closer);
4202
4205
  return false;
4203
4206
  } catch (error) {
4204
4207
  if (this.fsw._handleError(error)) {
4205
4208
  ready();
4206
- return path8;
4209
+ return path7;
4207
4210
  }
4208
4211
  }
4209
4212
  }
@@ -4246,24 +4249,24 @@ function createPattern(matcher) {
4246
4249
  }
4247
4250
  return () => false;
4248
4251
  }
4249
- function normalizePath(path8) {
4250
- if (typeof path8 !== "string")
4252
+ function normalizePath(path7) {
4253
+ if (typeof path7 !== "string")
4251
4254
  throw new Error("string expected");
4252
- path8 = sp2.normalize(path8);
4253
- path8 = path8.replace(/\\/g, "/");
4255
+ path7 = sp2.normalize(path7);
4256
+ path7 = path7.replace(/\\/g, "/");
4254
4257
  let prepend = false;
4255
- if (path8.startsWith("//"))
4258
+ if (path7.startsWith("//"))
4256
4259
  prepend = true;
4257
- path8 = path8.replace(DOUBLE_SLASH_RE, "/");
4260
+ path7 = path7.replace(DOUBLE_SLASH_RE, "/");
4258
4261
  if (prepend)
4259
- path8 = "/" + path8;
4260
- return path8;
4262
+ path7 = "/" + path7;
4263
+ return path7;
4261
4264
  }
4262
4265
  function matchPatterns(patterns, testString, stats) {
4263
- const path8 = normalizePath(testString);
4266
+ const path7 = normalizePath(testString);
4264
4267
  for (let index = 0; index < patterns.length; index++) {
4265
4268
  const pattern = patterns[index];
4266
- if (pattern(path8, stats)) {
4269
+ if (pattern(path7, stats)) {
4267
4270
  return true;
4268
4271
  }
4269
4272
  }
@@ -4301,19 +4304,19 @@ var toUnix = (string) => {
4301
4304
  }
4302
4305
  return str;
4303
4306
  };
4304
- var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
4305
- var normalizeIgnored = (cwd = "") => (path8) => {
4306
- if (typeof path8 === "string") {
4307
- return normalizePathToUnix(sp2.isAbsolute(path8) ? path8 : sp2.join(cwd, path8));
4307
+ var normalizePathToUnix = (path7) => toUnix(sp2.normalize(toUnix(path7)));
4308
+ var normalizeIgnored = (cwd = "") => (path7) => {
4309
+ if (typeof path7 === "string") {
4310
+ return normalizePathToUnix(sp2.isAbsolute(path7) ? path7 : sp2.join(cwd, path7));
4308
4311
  } else {
4309
- return path8;
4312
+ return path7;
4310
4313
  }
4311
4314
  };
4312
- var getAbsolutePath = (path8, cwd) => {
4313
- if (sp2.isAbsolute(path8)) {
4314
- return path8;
4315
+ var getAbsolutePath = (path7, cwd) => {
4316
+ if (sp2.isAbsolute(path7)) {
4317
+ return path7;
4315
4318
  }
4316
- return sp2.join(cwd, path8);
4319
+ return sp2.join(cwd, path7);
4317
4320
  };
4318
4321
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
4319
4322
  var DirEntry = class {
@@ -4378,10 +4381,10 @@ var WatchHelper = class {
4378
4381
  dirParts;
4379
4382
  followSymlinks;
4380
4383
  statMethod;
4381
- constructor(path8, follow, fsw) {
4384
+ constructor(path7, follow, fsw) {
4382
4385
  this.fsw = fsw;
4383
- const watchPath = path8;
4384
- this.path = path8 = path8.replace(REPLACER_RE, "");
4386
+ const watchPath = path7;
4387
+ this.path = path7 = path7.replace(REPLACER_RE, "");
4385
4388
  this.watchPath = watchPath;
4386
4389
  this.fullWatchPath = sp2.resolve(watchPath);
4387
4390
  this.dirParts = [];
@@ -4521,20 +4524,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4521
4524
  this._closePromise = void 0;
4522
4525
  let paths = unifyPaths(paths_);
4523
4526
  if (cwd) {
4524
- paths = paths.map((path8) => {
4525
- const absPath = getAbsolutePath(path8, cwd);
4527
+ paths = paths.map((path7) => {
4528
+ const absPath = getAbsolutePath(path7, cwd);
4526
4529
  return absPath;
4527
4530
  });
4528
4531
  }
4529
- paths.forEach((path8) => {
4530
- this._removeIgnoredPath(path8);
4532
+ paths.forEach((path7) => {
4533
+ this._removeIgnoredPath(path7);
4531
4534
  });
4532
4535
  this._userIgnored = void 0;
4533
4536
  if (!this._readyCount)
4534
4537
  this._readyCount = 0;
4535
4538
  this._readyCount += paths.length;
4536
- Promise.all(paths.map(async (path8) => {
4537
- const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
4539
+ Promise.all(paths.map(async (path7) => {
4540
+ const res = await this._nodeFsHandler._addToNodeFs(path7, !_internal, void 0, 0, _origAdd);
4538
4541
  if (res)
4539
4542
  this._emitReady();
4540
4543
  return res;
@@ -4556,17 +4559,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4556
4559
  return this;
4557
4560
  const paths = unifyPaths(paths_);
4558
4561
  const { cwd } = this.options;
4559
- paths.forEach((path8) => {
4560
- if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
4562
+ paths.forEach((path7) => {
4563
+ if (!sp2.isAbsolute(path7) && !this._closers.has(path7)) {
4561
4564
  if (cwd)
4562
- path8 = sp2.join(cwd, path8);
4563
- path8 = sp2.resolve(path8);
4565
+ path7 = sp2.join(cwd, path7);
4566
+ path7 = sp2.resolve(path7);
4564
4567
  }
4565
- this._closePath(path8);
4566
- this._addIgnoredPath(path8);
4567
- if (this._watched.has(path8)) {
4568
+ this._closePath(path7);
4569
+ this._addIgnoredPath(path7);
4570
+ if (this._watched.has(path7)) {
4568
4571
  this._addIgnoredPath({
4569
- path: path8,
4572
+ path: path7,
4570
4573
  recursive: true
4571
4574
  });
4572
4575
  }
@@ -4630,38 +4633,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4630
4633
  * @param stats arguments to be passed with event
4631
4634
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
4632
4635
  */
4633
- async _emit(event, path8, stats) {
4636
+ async _emit(event, path7, stats) {
4634
4637
  if (this.closed)
4635
4638
  return;
4636
4639
  const opts = this.options;
4637
4640
  if (isWindows)
4638
- path8 = sp2.normalize(path8);
4641
+ path7 = sp2.normalize(path7);
4639
4642
  if (opts.cwd)
4640
- path8 = sp2.relative(opts.cwd, path8);
4641
- const args = [path8];
4643
+ path7 = sp2.relative(opts.cwd, path7);
4644
+ const args = [path7];
4642
4645
  if (stats != null)
4643
4646
  args.push(stats);
4644
4647
  const awf = opts.awaitWriteFinish;
4645
4648
  let pw;
4646
- if (awf && (pw = this._pendingWrites.get(path8))) {
4649
+ if (awf && (pw = this._pendingWrites.get(path7))) {
4647
4650
  pw.lastChange = /* @__PURE__ */ new Date();
4648
4651
  return this;
4649
4652
  }
4650
4653
  if (opts.atomic) {
4651
4654
  if (event === EVENTS.UNLINK) {
4652
- this._pendingUnlinks.set(path8, [event, ...args]);
4655
+ this._pendingUnlinks.set(path7, [event, ...args]);
4653
4656
  setTimeout(() => {
4654
- this._pendingUnlinks.forEach((entry, path9) => {
4657
+ this._pendingUnlinks.forEach((entry, path8) => {
4655
4658
  this.emit(...entry);
4656
4659
  this.emit(EVENTS.ALL, ...entry);
4657
- this._pendingUnlinks.delete(path9);
4660
+ this._pendingUnlinks.delete(path8);
4658
4661
  });
4659
4662
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
4660
4663
  return this;
4661
4664
  }
4662
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
4665
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path7)) {
4663
4666
  event = EVENTS.CHANGE;
4664
- this._pendingUnlinks.delete(path8);
4667
+ this._pendingUnlinks.delete(path7);
4665
4668
  }
4666
4669
  }
4667
4670
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -4679,16 +4682,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4679
4682
  this.emitWithAll(event, args);
4680
4683
  }
4681
4684
  };
4682
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
4685
+ this._awaitWriteFinish(path7, awf.stabilityThreshold, event, awfEmit);
4683
4686
  return this;
4684
4687
  }
4685
4688
  if (event === EVENTS.CHANGE) {
4686
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
4689
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path7, 50);
4687
4690
  if (isThrottled)
4688
4691
  return this;
4689
4692
  }
4690
4693
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
4691
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
4694
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path7) : path7;
4692
4695
  let stats2;
4693
4696
  try {
4694
4697
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -4719,23 +4722,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4719
4722
  * @param timeout duration of time to suppress duplicate actions
4720
4723
  * @returns tracking object or false if action should be suppressed
4721
4724
  */
4722
- _throttle(actionType, path8, timeout) {
4725
+ _throttle(actionType, path7, timeout) {
4723
4726
  if (!this._throttled.has(actionType)) {
4724
4727
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
4725
4728
  }
4726
4729
  const action = this._throttled.get(actionType);
4727
4730
  if (!action)
4728
4731
  throw new Error("invalid throttle");
4729
- const actionPath = action.get(path8);
4732
+ const actionPath = action.get(path7);
4730
4733
  if (actionPath) {
4731
4734
  actionPath.count++;
4732
4735
  return false;
4733
4736
  }
4734
4737
  let timeoutObject;
4735
4738
  const clear = () => {
4736
- const item = action.get(path8);
4739
+ const item = action.get(path7);
4737
4740
  const count = item ? item.count : 0;
4738
- action.delete(path8);
4741
+ action.delete(path7);
4739
4742
  clearTimeout(timeoutObject);
4740
4743
  if (item)
4741
4744
  clearTimeout(item.timeoutObject);
@@ -4743,7 +4746,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4743
4746
  };
4744
4747
  timeoutObject = setTimeout(clear, timeout);
4745
4748
  const thr = { timeoutObject, clear, count: 0 };
4746
- action.set(path8, thr);
4749
+ action.set(path7, thr);
4747
4750
  return thr;
4748
4751
  }
4749
4752
  _incrReadyCount() {
@@ -4757,44 +4760,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4757
4760
  * @param event
4758
4761
  * @param awfEmit Callback to be called when ready for event to be emitted.
4759
4762
  */
4760
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
4763
+ _awaitWriteFinish(path7, threshold, event, awfEmit) {
4761
4764
  const awf = this.options.awaitWriteFinish;
4762
4765
  if (typeof awf !== "object")
4763
4766
  return;
4764
4767
  const pollInterval = awf.pollInterval;
4765
4768
  let timeoutHandler;
4766
- let fullPath = path8;
4767
- if (this.options.cwd && !sp2.isAbsolute(path8)) {
4768
- fullPath = sp2.join(this.options.cwd, path8);
4769
+ let fullPath = path7;
4770
+ if (this.options.cwd && !sp2.isAbsolute(path7)) {
4771
+ fullPath = sp2.join(this.options.cwd, path7);
4769
4772
  }
4770
4773
  const now = /* @__PURE__ */ new Date();
4771
4774
  const writes = this._pendingWrites;
4772
4775
  function awaitWriteFinishFn(prevStat) {
4773
4776
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
4774
- if (err || !writes.has(path8)) {
4777
+ if (err || !writes.has(path7)) {
4775
4778
  if (err && err.code !== "ENOENT")
4776
4779
  awfEmit(err);
4777
4780
  return;
4778
4781
  }
4779
4782
  const now2 = Number(/* @__PURE__ */ new Date());
4780
4783
  if (prevStat && curStat.size !== prevStat.size) {
4781
- writes.get(path8).lastChange = now2;
4784
+ writes.get(path7).lastChange = now2;
4782
4785
  }
4783
- const pw = writes.get(path8);
4786
+ const pw = writes.get(path7);
4784
4787
  const df = now2 - pw.lastChange;
4785
4788
  if (df >= threshold) {
4786
- writes.delete(path8);
4789
+ writes.delete(path7);
4787
4790
  awfEmit(void 0, curStat);
4788
4791
  } else {
4789
4792
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
4790
4793
  }
4791
4794
  });
4792
4795
  }
4793
- if (!writes.has(path8)) {
4794
- writes.set(path8, {
4796
+ if (!writes.has(path7)) {
4797
+ writes.set(path7, {
4795
4798
  lastChange: now,
4796
4799
  cancelWait: () => {
4797
- writes.delete(path8);
4800
+ writes.delete(path7);
4798
4801
  clearTimeout(timeoutHandler);
4799
4802
  return event;
4800
4803
  }
@@ -4805,8 +4808,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4805
4808
  /**
4806
4809
  * Determines whether user has asked to ignore this path.
4807
4810
  */
4808
- _isIgnored(path8, stats) {
4809
- if (this.options.atomic && DOT_RE.test(path8))
4811
+ _isIgnored(path7, stats) {
4812
+ if (this.options.atomic && DOT_RE.test(path7))
4810
4813
  return true;
4811
4814
  if (!this._userIgnored) {
4812
4815
  const { cwd } = this.options;
@@ -4816,17 +4819,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4816
4819
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
4817
4820
  this._userIgnored = anymatch(list, void 0);
4818
4821
  }
4819
- return this._userIgnored(path8, stats);
4822
+ return this._userIgnored(path7, stats);
4820
4823
  }
4821
- _isntIgnored(path8, stat4) {
4822
- return !this._isIgnored(path8, stat4);
4824
+ _isntIgnored(path7, stat4) {
4825
+ return !this._isIgnored(path7, stat4);
4823
4826
  }
4824
4827
  /**
4825
4828
  * Provides a set of common helpers and properties relating to symlink handling.
4826
4829
  * @param path file or directory pattern being watched
4827
4830
  */
4828
- _getWatchHelpers(path8) {
4829
- return new WatchHelper(path8, this.options.followSymlinks, this);
4831
+ _getWatchHelpers(path7) {
4832
+ return new WatchHelper(path7, this.options.followSymlinks, this);
4830
4833
  }
4831
4834
  // Directory helpers
4832
4835
  // -----------------
@@ -4858,63 +4861,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
4858
4861
  * @param item base path of item/directory
4859
4862
  */
4860
4863
  _remove(directory, item, isDirectory) {
4861
- const path8 = sp2.join(directory, item);
4862
- const fullPath = sp2.resolve(path8);
4863
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
4864
- if (!this._throttle("remove", path8, 100))
4864
+ const path7 = sp2.join(directory, item);
4865
+ const fullPath = sp2.resolve(path7);
4866
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path7) || this._watched.has(fullPath);
4867
+ if (!this._throttle("remove", path7, 100))
4865
4868
  return;
4866
4869
  if (!isDirectory && this._watched.size === 1) {
4867
4870
  this.add(directory, item, true);
4868
4871
  }
4869
- const wp = this._getWatchedDir(path8);
4872
+ const wp = this._getWatchedDir(path7);
4870
4873
  const nestedDirectoryChildren = wp.getChildren();
4871
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
4874
+ nestedDirectoryChildren.forEach((nested) => this._remove(path7, nested));
4872
4875
  const parent = this._getWatchedDir(directory);
4873
4876
  const wasTracked = parent.has(item);
4874
4877
  parent.remove(item);
4875
4878
  if (this._symlinkPaths.has(fullPath)) {
4876
4879
  this._symlinkPaths.delete(fullPath);
4877
4880
  }
4878
- let relPath = path8;
4881
+ let relPath = path7;
4879
4882
  if (this.options.cwd)
4880
- relPath = sp2.relative(this.options.cwd, path8);
4883
+ relPath = sp2.relative(this.options.cwd, path7);
4881
4884
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
4882
4885
  const event = this._pendingWrites.get(relPath).cancelWait();
4883
4886
  if (event === EVENTS.ADD)
4884
4887
  return;
4885
4888
  }
4886
- this._watched.delete(path8);
4889
+ this._watched.delete(path7);
4887
4890
  this._watched.delete(fullPath);
4888
4891
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
4889
- if (wasTracked && !this._isIgnored(path8))
4890
- this._emit(eventName, path8);
4891
- this._closePath(path8);
4892
+ if (wasTracked && !this._isIgnored(path7))
4893
+ this._emit(eventName, path7);
4894
+ this._closePath(path7);
4892
4895
  }
4893
4896
  /**
4894
4897
  * Closes all watchers for a path
4895
4898
  */
4896
- _closePath(path8) {
4897
- this._closeFile(path8);
4898
- const dir = sp2.dirname(path8);
4899
- this._getWatchedDir(dir).remove(sp2.basename(path8));
4899
+ _closePath(path7) {
4900
+ this._closeFile(path7);
4901
+ const dir = sp2.dirname(path7);
4902
+ this._getWatchedDir(dir).remove(sp2.basename(path7));
4900
4903
  }
4901
4904
  /**
4902
4905
  * Closes only file-specific watchers
4903
4906
  */
4904
- _closeFile(path8) {
4905
- const closers = this._closers.get(path8);
4907
+ _closeFile(path7) {
4908
+ const closers = this._closers.get(path7);
4906
4909
  if (!closers)
4907
4910
  return;
4908
4911
  closers.forEach((closer) => closer());
4909
- this._closers.delete(path8);
4912
+ this._closers.delete(path7);
4910
4913
  }
4911
- _addPathCloser(path8, closer) {
4914
+ _addPathCloser(path7, closer) {
4912
4915
  if (!closer)
4913
4916
  return;
4914
- let list = this._closers.get(path8);
4917
+ let list = this._closers.get(path7);
4915
4918
  if (!list) {
4916
4919
  list = [];
4917
- this._closers.set(path8, list);
4920
+ this._closers.set(path7, list);
4918
4921
  }
4919
4922
  list.push(closer);
4920
4923
  }
@@ -4944,7 +4947,7 @@ function watch(paths, options = {}) {
4944
4947
  var chokidar_default = { watch, FSWatcher };
4945
4948
 
4946
4949
  // src/watcher/index.ts
4947
- var path6 = __toESM(require("path"), 1);
4950
+ var path5 = __toESM(require("path"), 1);
4948
4951
  var FileWatcher = class {
4949
4952
  watcher = null;
4950
4953
  projectRoot;
@@ -4965,7 +4968,7 @@ var FileWatcher = class {
4965
4968
  const ignoreFilter = createIgnoreFilter(this.projectRoot);
4966
4969
  this.watcher = chokidar_default.watch(this.projectRoot, {
4967
4970
  ignored: (filePath) => {
4968
- const relativePath = path6.relative(this.projectRoot, filePath);
4971
+ const relativePath = path5.relative(this.projectRoot, filePath);
4969
4972
  if (!relativePath) return false;
4970
4973
  if (ignoreFilter.ignores(relativePath)) {
4971
4974
  return true;
@@ -5009,7 +5012,7 @@ var FileWatcher = class {
5009
5012
  return;
5010
5013
  }
5011
5014
  const changes = Array.from(this.pendingChanges.entries()).map(
5012
- ([path8, type]) => ({ path: path8, type })
5015
+ ([path7, type]) => ({ path: path7, type })
5013
5016
  );
5014
5017
  this.pendingChanges.clear();
5015
5018
  try {
@@ -5201,10 +5204,10 @@ function formatStatus(status) {
5201
5204
 
5202
5205
  // src/index.ts
5203
5206
  function loadPluginConfig(projectRoot) {
5204
- const configPath = path7.join(projectRoot, ".opencode", "codebase-index.json");
5207
+ const configPath = path6.join(projectRoot, ".opencode", "codebase-index.json");
5205
5208
  try {
5206
- if ((0, import_fs5.existsSync)(configPath)) {
5207
- const content = (0, import_fs5.readFileSync)(configPath, "utf-8");
5209
+ if ((0, import_fs4.existsSync)(configPath)) {
5210
+ const content = (0, import_fs4.readFileSync)(configPath, "utf-8");
5208
5211
  return JSON.parse(content);
5209
5212
  }
5210
5213
  } catch {
@@ -5233,6 +5236,39 @@ var plugin = async ({ directory }) => {
5233
5236
  index_codebase,
5234
5237
  index_status,
5235
5238
  index_health_check
5239
+ },
5240
+ async config(cfg) {
5241
+ cfg.command = cfg.command ?? {};
5242
+ cfg.command["search"] = {
5243
+ description: "Search codebase by meaning using semantic search",
5244
+ template: `Use the \`codebase_search\` tool to find code related to: $ARGUMENTS
5245
+
5246
+ If the index doesn't exist yet, run \`index_codebase\` first.
5247
+
5248
+ Return the most relevant results with file paths and line numbers.`
5249
+ };
5250
+ cfg.command["find"] = {
5251
+ description: "Find code using hybrid approach (semantic + grep)",
5252
+ template: `Find code related to: $ARGUMENTS
5253
+
5254
+ Strategy:
5255
+ 1. First use \`codebase_search\` to find semantically related code
5256
+ 2. From the results, identify specific function/class names
5257
+ 3. Use grep to find all occurrences of those identifiers
5258
+ 4. Combine findings into a comprehensive answer
5259
+
5260
+ If the semantic index doesn't exist, run \`index_codebase\` first.`
5261
+ };
5262
+ cfg.command["index"] = {
5263
+ description: "Index the codebase for semantic search",
5264
+ template: `Run the \`index_codebase\` tool to create or update the semantic search index.
5265
+
5266
+ Show progress and final statistics including:
5267
+ - Number of files processed
5268
+ - Number of chunks indexed
5269
+ - Tokens used
5270
+ - Duration`
5271
+ };
5236
5272
  }
5237
5273
  };
5238
5274
  };