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.js 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.
@@ -652,8 +652,8 @@ var require_ignore = __commonJS({
652
652
  });
653
653
 
654
654
  // src/index.ts
655
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
656
- import * as path7 from "path";
655
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
656
+ import * as path6 from "path";
657
657
 
658
658
  // src/config/schema.ts
659
659
  var DEFAULT_INCLUDE = [
@@ -688,6 +688,8 @@ function getDefaultIndexingConfig() {
688
688
  autoIndex: false,
689
689
  watchFiles: true,
690
690
  maxFileSize: 1048576,
691
+ maxChunksPerFile: 100,
692
+ semanticOnly: false,
691
693
  retries: 3,
692
694
  retryDelayMs: 1e3
693
695
  };
@@ -721,6 +723,8 @@ function parseConfig(raw) {
721
723
  autoIndex: typeof rawIndexing.autoIndex === "boolean" ? rawIndexing.autoIndex : defaultIndexing.autoIndex,
722
724
  watchFiles: typeof rawIndexing.watchFiles === "boolean" ? rawIndexing.watchFiles : defaultIndexing.watchFiles,
723
725
  maxFileSize: typeof rawIndexing.maxFileSize === "number" ? rawIndexing.maxFileSize : defaultIndexing.maxFileSize,
726
+ maxChunksPerFile: typeof rawIndexing.maxChunksPerFile === "number" ? Math.max(1, rawIndexing.maxChunksPerFile) : defaultIndexing.maxChunksPerFile,
727
+ semanticOnly: typeof rawIndexing.semanticOnly === "boolean" ? rawIndexing.semanticOnly : defaultIndexing.semanticOnly,
724
728
  retries: typeof rawIndexing.retries === "number" ? rawIndexing.retries : defaultIndexing.retries,
725
729
  retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs
726
730
  };
@@ -802,8 +806,8 @@ function getDefaultModelForProvider(provider) {
802
806
  }
803
807
 
804
808
  // src/indexer/index.ts
805
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, promises as fsPromises2 } from "fs";
806
- import * as path5 from "path";
809
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync, promises as fsPromises2 } from "fs";
810
+ import * as path4 from "path";
807
811
 
808
812
  // node_modules/eventemitter3/index.mjs
809
813
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -2620,139 +2624,39 @@ function generateChunkId(filePath, chunk) {
2620
2624
  function generateChunkHash(chunk) {
2621
2625
  return hashContent(chunk.content);
2622
2626
  }
2623
-
2624
- // src/indexer/inverted-index.ts
2625
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
2626
- import * as path4 from "path";
2627
2627
  var InvertedIndex = class {
2628
- indexPath;
2629
- termToChunks = /* @__PURE__ */ new Map();
2630
- chunkTokens = /* @__PURE__ */ new Map();
2631
- totalTokenCount = 0;
2628
+ inner;
2632
2629
  constructor(indexPath) {
2633
- this.indexPath = path4.join(indexPath, "inverted-index.json");
2630
+ this.inner = new native.InvertedIndex(indexPath);
2634
2631
  }
2635
2632
  load() {
2636
- if (!existsSync3(this.indexPath)) {
2637
- return;
2638
- }
2639
- try {
2640
- const content = readFileSync3(this.indexPath, "utf-8");
2641
- const data = JSON.parse(content);
2642
- for (const [term, chunkIds] of Object.entries(data.termToChunks)) {
2643
- this.termToChunks.set(term, new Set(chunkIds));
2644
- }
2645
- for (const [chunkId, tokens] of Object.entries(data.chunkTokens)) {
2646
- const tokenMap = new Map(Object.entries(tokens).map(([k, v]) => [k, v]));
2647
- this.chunkTokens.set(chunkId, tokenMap);
2648
- for (const count of tokenMap.values()) {
2649
- this.totalTokenCount += count;
2650
- }
2651
- }
2652
- } catch {
2653
- this.termToChunks.clear();
2654
- this.chunkTokens.clear();
2655
- this.totalTokenCount = 0;
2656
- }
2633
+ this.inner.load();
2657
2634
  }
2658
2635
  save() {
2659
- const data = {
2660
- termToChunks: {},
2661
- chunkTokens: {},
2662
- avgDocLength: this.getAvgDocLength()
2663
- };
2664
- for (const [term, chunkIds] of this.termToChunks) {
2665
- data.termToChunks[term] = Array.from(chunkIds);
2666
- }
2667
- for (const [chunkId, tokens] of this.chunkTokens) {
2668
- data.chunkTokens[chunkId] = Object.fromEntries(tokens);
2669
- }
2670
- writeFileSync(this.indexPath, JSON.stringify(data));
2636
+ this.inner.save();
2671
2637
  }
2672
2638
  addChunk(chunkId, content) {
2673
- const tokens = this.tokenize(content);
2674
- const termFreq = /* @__PURE__ */ new Map();
2675
- for (const token of tokens) {
2676
- termFreq.set(token, (termFreq.get(token) || 0) + 1);
2677
- const chunks = this.termToChunks.get(token) || /* @__PURE__ */ new Set();
2678
- chunks.add(chunkId);
2679
- this.termToChunks.set(token, chunks);
2680
- }
2681
- this.chunkTokens.set(chunkId, termFreq);
2682
- this.totalTokenCount += tokens.length;
2639
+ this.inner.addChunk(chunkId, content);
2683
2640
  }
2684
2641
  removeChunk(chunkId) {
2685
- const tokens = this.chunkTokens.get(chunkId);
2686
- if (!tokens) return;
2687
- for (const [token, count] of tokens) {
2688
- this.totalTokenCount -= count;
2689
- const chunks = this.termToChunks.get(token);
2690
- if (chunks) {
2691
- chunks.delete(chunkId);
2692
- if (chunks.size === 0) {
2693
- this.termToChunks.delete(token);
2694
- }
2695
- }
2696
- }
2697
- this.chunkTokens.delete(chunkId);
2642
+ return this.inner.removeChunk(chunkId);
2698
2643
  }
2699
- search(query) {
2700
- const queryTokens = this.tokenize(query);
2701
- if (queryTokens.length === 0) {
2702
- return /* @__PURE__ */ new Map();
2703
- }
2704
- const candidateChunks = /* @__PURE__ */ new Set();
2705
- for (const token of queryTokens) {
2706
- const chunks = this.termToChunks.get(token);
2707
- if (chunks) {
2708
- for (const chunkId of chunks) {
2709
- candidateChunks.add(chunkId);
2710
- }
2711
- }
2644
+ search(query, limit) {
2645
+ const results = this.inner.search(query, limit ?? 100);
2646
+ const map = /* @__PURE__ */ new Map();
2647
+ for (const r of results) {
2648
+ map.set(r.chunkId, r.score);
2712
2649
  }
2713
- const scores = /* @__PURE__ */ new Map();
2714
- const k1 = 1.2;
2715
- const b = 0.75;
2716
- const N = this.chunkTokens.size;
2717
- const avgDocLength = this.getAvgDocLength();
2718
- for (const chunkId of candidateChunks) {
2719
- const termFreq = this.chunkTokens.get(chunkId);
2720
- if (!termFreq) continue;
2721
- const docLength = Array.from(termFreq.values()).reduce((a, b2) => a + b2, 0);
2722
- let score = 0;
2723
- for (const term of queryTokens) {
2724
- const tf = termFreq.get(term) || 0;
2725
- if (tf === 0) continue;
2726
- const df = this.termToChunks.get(term)?.size || 0;
2727
- const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
2728
- const tfNorm = tf * (k1 + 1) / (tf + k1 * (1 - b + b * (docLength / avgDocLength)));
2729
- score += idf * tfNorm;
2730
- }
2731
- scores.set(chunkId, score);
2732
- }
2733
- const maxScore = Math.max(...scores.values(), 1);
2734
- for (const [chunkId, score] of scores) {
2735
- scores.set(chunkId, score / maxScore);
2736
- }
2737
- return scores;
2650
+ return map;
2738
2651
  }
2739
2652
  hasChunk(chunkId) {
2740
- return this.chunkTokens.has(chunkId);
2653
+ return this.inner.hasChunk(chunkId);
2741
2654
  }
2742
2655
  clear() {
2743
- this.termToChunks.clear();
2744
- this.chunkTokens.clear();
2745
- this.totalTokenCount = 0;
2656
+ this.inner.clear();
2746
2657
  }
2747
2658
  getDocumentCount() {
2748
- return this.chunkTokens.size;
2749
- }
2750
- getAvgDocLength() {
2751
- const count = this.chunkTokens.size;
2752
- return count > 0 ? this.totalTokenCount / count : 100;
2753
- }
2754
- tokenize(text) {
2755
- return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 2);
2659
+ return this.inner.documentCount();
2756
2660
  }
2757
2661
  };
2758
2662
 
@@ -2767,23 +2671,25 @@ var Indexer = class {
2767
2671
  detectedProvider = null;
2768
2672
  fileHashCache = /* @__PURE__ */ new Map();
2769
2673
  fileHashCachePath = "";
2674
+ failedBatchesPath = "";
2770
2675
  constructor(projectRoot, config) {
2771
2676
  this.projectRoot = projectRoot;
2772
2677
  this.config = config;
2773
2678
  this.indexPath = this.getIndexPath();
2774
- this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
2679
+ this.fileHashCachePath = path4.join(this.indexPath, "file-hashes.json");
2680
+ this.failedBatchesPath = path4.join(this.indexPath, "failed-batches.json");
2775
2681
  }
2776
2682
  getIndexPath() {
2777
2683
  if (this.config.scope === "global") {
2778
2684
  const homeDir = process.env.HOME || process.env.USERPROFILE || "";
2779
- return path5.join(homeDir, ".opencode", "global-index");
2685
+ return path4.join(homeDir, ".opencode", "global-index");
2780
2686
  }
2781
- return path5.join(this.projectRoot, ".opencode", "index");
2687
+ return path4.join(this.projectRoot, ".opencode", "index");
2782
2688
  }
2783
2689
  loadFileHashCache() {
2784
2690
  try {
2785
- if (existsSync4(this.fileHashCachePath)) {
2786
- const data = readFileSync4(this.fileHashCachePath, "utf-8");
2691
+ if (existsSync3(this.fileHashCachePath)) {
2692
+ const data = readFileSync3(this.fileHashCachePath, "utf-8");
2787
2693
  const parsed = JSON.parse(data);
2788
2694
  this.fileHashCache = new Map(Object.entries(parsed));
2789
2695
  }
@@ -2796,7 +2702,38 @@ var Indexer = class {
2796
2702
  for (const [k, v] of this.fileHashCache) {
2797
2703
  obj[k] = v;
2798
2704
  }
2799
- writeFileSync2(this.fileHashCachePath, JSON.stringify(obj));
2705
+ writeFileSync(this.fileHashCachePath, JSON.stringify(obj));
2706
+ }
2707
+ loadFailedBatches() {
2708
+ try {
2709
+ if (existsSync3(this.failedBatchesPath)) {
2710
+ const data = readFileSync3(this.failedBatchesPath, "utf-8");
2711
+ return JSON.parse(data);
2712
+ }
2713
+ } catch {
2714
+ return [];
2715
+ }
2716
+ return [];
2717
+ }
2718
+ saveFailedBatches(batches) {
2719
+ if (batches.length === 0) {
2720
+ if (existsSync3(this.failedBatchesPath)) {
2721
+ fsPromises2.unlink(this.failedBatchesPath).catch(() => {
2722
+ });
2723
+ }
2724
+ return;
2725
+ }
2726
+ writeFileSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
2727
+ }
2728
+ addFailedBatch(batch, error) {
2729
+ const existing = this.loadFailedBatches();
2730
+ existing.push({
2731
+ chunks: batch,
2732
+ error,
2733
+ attemptCount: 1,
2734
+ lastAttempt: (/* @__PURE__ */ new Date()).toISOString()
2735
+ });
2736
+ this.saveFailedBatches(existing);
2800
2737
  }
2801
2738
  async initialize() {
2802
2739
  this.detectedProvider = await detectEmbeddingProvider(this.config.embeddingProvider);
@@ -2811,13 +2748,14 @@ var Indexer = class {
2811
2748
  );
2812
2749
  await fsPromises2.mkdir(this.indexPath, { recursive: true });
2813
2750
  const dimensions = this.detectedProvider.modelInfo.dimensions;
2814
- const storePath = path5.join(this.indexPath, "vectors");
2751
+ const storePath = path4.join(this.indexPath, "vectors");
2815
2752
  this.store = new VectorStore(storePath, dimensions);
2816
- const indexFilePath = path5.join(this.indexPath, "vectors.usearch");
2817
- if (existsSync4(indexFilePath)) {
2753
+ const indexFilePath = path4.join(this.indexPath, "vectors.usearch");
2754
+ if (existsSync3(indexFilePath)) {
2818
2755
  this.store.load();
2819
2756
  }
2820
- this.invertedIndex = new InvertedIndex(this.indexPath);
2757
+ const invertedIndexPath = path4.join(this.indexPath, "inverted-index.json");
2758
+ this.invertedIndex = new InvertedIndex(invertedIndexPath);
2821
2759
  this.invertedIndex.load();
2822
2760
  }
2823
2761
  async ensureInitialized() {
@@ -2916,14 +2854,22 @@ var Indexer = class {
2916
2854
  for (const parsed of parsedFiles) {
2917
2855
  currentFilePaths.add(parsed.path);
2918
2856
  if (parsed.chunks.length === 0) {
2919
- const relativePath = path5.relative(this.projectRoot, parsed.path);
2857
+ const relativePath = path4.relative(this.projectRoot, parsed.path);
2920
2858
  stats.parseFailures.push(relativePath);
2921
2859
  }
2860
+ let fileChunkCount = 0;
2922
2861
  for (const chunk of parsed.chunks) {
2862
+ if (fileChunkCount >= this.config.indexing.maxChunksPerFile) {
2863
+ break;
2864
+ }
2865
+ if (this.config.indexing.semanticOnly && chunk.chunkType === "other") {
2866
+ continue;
2867
+ }
2923
2868
  const id = generateChunkId(parsed.path, chunk);
2924
2869
  const contentHash = generateChunkHash(chunk);
2925
2870
  currentChunkIds.add(id);
2926
2871
  if (existingChunks.get(id) === contentHash) {
2872
+ fileChunkCount++;
2927
2873
  continue;
2928
2874
  }
2929
2875
  const text = createEmbeddingText(chunk, parsed.path);
@@ -2937,6 +2883,7 @@ var Indexer = class {
2937
2883
  hash: contentHash
2938
2884
  };
2939
2885
  pendingChunks.push({ id, text, content: chunk.content, metadata });
2886
+ fileChunkCount++;
2940
2887
  }
2941
2888
  }
2942
2889
  let removedCount = 0;
@@ -3026,6 +2973,7 @@ var Indexer = class {
3026
2973
  });
3027
2974
  } catch (error) {
3028
2975
  stats.failedChunks += batch.length;
2976
+ this.addFailedBatch(batch, String(error));
3029
2977
  console.error(`Failed to embed batch after retries: ${error}`);
3030
2978
  }
3031
2979
  });
@@ -3043,6 +2991,9 @@ var Indexer = class {
3043
2991
  this.fileHashCache = currentFileHashes;
3044
2992
  this.saveFileHashCache();
3045
2993
  stats.durationMs = Date.now() - startTime;
2994
+ if (stats.failedChunks > 0) {
2995
+ stats.failedBatchesPath = this.failedBatchesPath;
2996
+ }
3046
2997
  onProgress?.({
3047
2998
  phase: "complete",
3048
2999
  filesProcessed: files.length,
@@ -3188,7 +3139,7 @@ var Indexer = class {
3188
3139
  const removedFilePaths = [];
3189
3140
  let removedCount = 0;
3190
3141
  for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
3191
- if (!existsSync4(filePath)) {
3142
+ if (!existsSync3(filePath)) {
3192
3143
  for (const key of chunkKeys) {
3193
3144
  store.remove(key);
3194
3145
  invertedIndex.removeChunk(key);
@@ -3203,6 +3154,58 @@ var Indexer = class {
3203
3154
  }
3204
3155
  return { removed: removedCount, filePaths: removedFilePaths };
3205
3156
  }
3157
+ async retryFailedBatches() {
3158
+ const { store, provider, invertedIndex } = await this.ensureInitialized();
3159
+ const failedBatches = this.loadFailedBatches();
3160
+ if (failedBatches.length === 0) {
3161
+ return { succeeded: 0, failed: 0, remaining: 0 };
3162
+ }
3163
+ let succeeded = 0;
3164
+ let failed = 0;
3165
+ const stillFailing = [];
3166
+ for (const batch of failedBatches) {
3167
+ try {
3168
+ const result = await pRetry(
3169
+ async () => {
3170
+ const texts = batch.chunks.map((c) => c.text);
3171
+ return provider.embedBatch(texts);
3172
+ },
3173
+ {
3174
+ retries: this.config.indexing.retries,
3175
+ minTimeout: this.config.indexing.retryDelayMs
3176
+ }
3177
+ );
3178
+ const items = batch.chunks.map((chunk, idx) => ({
3179
+ id: chunk.id,
3180
+ vector: result.embeddings[idx],
3181
+ metadata: chunk.metadata
3182
+ }));
3183
+ store.addBatch(items);
3184
+ for (const chunk of batch.chunks) {
3185
+ invertedIndex.removeChunk(chunk.id);
3186
+ invertedIndex.addChunk(chunk.id, chunk.content);
3187
+ }
3188
+ succeeded += batch.chunks.length;
3189
+ } catch (error) {
3190
+ failed += batch.chunks.length;
3191
+ stillFailing.push({
3192
+ ...batch,
3193
+ attemptCount: batch.attemptCount + 1,
3194
+ lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
3195
+ error: String(error)
3196
+ });
3197
+ }
3198
+ }
3199
+ this.saveFailedBatches(stillFailing);
3200
+ if (succeeded > 0) {
3201
+ store.save();
3202
+ invertedIndex.save();
3203
+ }
3204
+ return { succeeded, failed, remaining: stillFailing.length };
3205
+ }
3206
+ getFailedBatchesCount() {
3207
+ return this.loadFailedBatches().length;
3208
+ }
3206
3209
  };
3207
3210
 
3208
3211
  // node_modules/chokidar/index.js
@@ -3295,7 +3298,7 @@ var ReaddirpStream = class extends Readable {
3295
3298
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
3296
3299
  const statMethod = opts.lstat ? lstat : stat;
3297
3300
  if (wantBigintFsStats) {
3298
- this._stat = (path8) => statMethod(path8, { bigint: true });
3301
+ this._stat = (path7) => statMethod(path7, { bigint: true });
3299
3302
  } else {
3300
3303
  this._stat = statMethod;
3301
3304
  }
@@ -3320,8 +3323,8 @@ var ReaddirpStream = class extends Readable {
3320
3323
  const par = this.parent;
3321
3324
  const fil = par && par.files;
3322
3325
  if (fil && fil.length > 0) {
3323
- const { path: path8, depth } = par;
3324
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
3326
+ const { path: path7, depth } = par;
3327
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path7));
3325
3328
  const awaited = await Promise.all(slice);
3326
3329
  for (const entry of awaited) {
3327
3330
  if (!entry)
@@ -3361,20 +3364,20 @@ var ReaddirpStream = class extends Readable {
3361
3364
  this.reading = false;
3362
3365
  }
3363
3366
  }
3364
- async _exploreDir(path8, depth) {
3367
+ async _exploreDir(path7, depth) {
3365
3368
  let files;
3366
3369
  try {
3367
- files = await readdir(path8, this._rdOptions);
3370
+ files = await readdir(path7, this._rdOptions);
3368
3371
  } catch (error) {
3369
3372
  this._onError(error);
3370
3373
  }
3371
- return { files, depth, path: path8 };
3374
+ return { files, depth, path: path7 };
3372
3375
  }
3373
- async _formatEntry(dirent, path8) {
3376
+ async _formatEntry(dirent, path7) {
3374
3377
  let entry;
3375
3378
  const basename3 = this._isDirent ? dirent.name : dirent;
3376
3379
  try {
3377
- const fullPath = presolve(pjoin(path8, basename3));
3380
+ const fullPath = presolve(pjoin(path7, basename3));
3378
3381
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
3379
3382
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
3380
3383
  } catch (err) {
@@ -3774,16 +3777,16 @@ var delFromSet = (main, prop, item) => {
3774
3777
  };
3775
3778
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
3776
3779
  var FsWatchInstances = /* @__PURE__ */ new Map();
3777
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
3780
+ function createFsWatchInstance(path7, options, listener, errHandler, emitRaw) {
3778
3781
  const handleEvent = (rawEvent, evPath) => {
3779
- listener(path8);
3780
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
3781
- if (evPath && path8 !== evPath) {
3782
- fsWatchBroadcast(sp.resolve(path8, evPath), KEY_LISTENERS, sp.join(path8, evPath));
3782
+ listener(path7);
3783
+ emitRaw(rawEvent, evPath, { watchedPath: path7 });
3784
+ if (evPath && path7 !== evPath) {
3785
+ fsWatchBroadcast(sp.resolve(path7, evPath), KEY_LISTENERS, sp.join(path7, evPath));
3783
3786
  }
3784
3787
  };
3785
3788
  try {
3786
- return fs_watch(path8, {
3789
+ return fs_watch(path7, {
3787
3790
  persistent: options.persistent
3788
3791
  }, handleEvent);
3789
3792
  } catch (error) {
@@ -3799,12 +3802,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
3799
3802
  listener(val1, val2, val3);
3800
3803
  });
3801
3804
  };
3802
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
3805
+ var setFsWatchListener = (path7, fullPath, options, handlers) => {
3803
3806
  const { listener, errHandler, rawEmitter } = handlers;
3804
3807
  let cont = FsWatchInstances.get(fullPath);
3805
3808
  let watcher;
3806
3809
  if (!options.persistent) {
3807
- watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
3810
+ watcher = createFsWatchInstance(path7, options, listener, errHandler, rawEmitter);
3808
3811
  if (!watcher)
3809
3812
  return;
3810
3813
  return watcher.close.bind(watcher);
@@ -3815,7 +3818,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
3815
3818
  addAndConvert(cont, KEY_RAW, rawEmitter);
3816
3819
  } else {
3817
3820
  watcher = createFsWatchInstance(
3818
- path8,
3821
+ path7,
3819
3822
  options,
3820
3823
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
3821
3824
  errHandler,
@@ -3830,7 +3833,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
3830
3833
  cont.watcherUnusable = true;
3831
3834
  if (isWindows && error.code === "EPERM") {
3832
3835
  try {
3833
- const fd = await open(path8, "r");
3836
+ const fd = await open(path7, "r");
3834
3837
  await fd.close();
3835
3838
  broadcastErr(error);
3836
3839
  } catch (err) {
@@ -3861,7 +3864,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
3861
3864
  };
3862
3865
  };
3863
3866
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
3864
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
3867
+ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
3865
3868
  const { listener, rawEmitter } = handlers;
3866
3869
  let cont = FsWatchFileInstances.get(fullPath);
3867
3870
  const copts = cont && cont.options;
@@ -3883,7 +3886,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
3883
3886
  });
3884
3887
  const currmtime = curr.mtimeMs;
3885
3888
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
3886
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
3889
+ foreach(cont.listeners, (listener2) => listener2(path7, curr));
3887
3890
  }
3888
3891
  })
3889
3892
  };
@@ -3913,13 +3916,13 @@ var NodeFsHandler = class {
3913
3916
  * @param listener on fs change
3914
3917
  * @returns closer for the watcher instance
3915
3918
  */
3916
- _watchWithNodeFs(path8, listener) {
3919
+ _watchWithNodeFs(path7, listener) {
3917
3920
  const opts = this.fsw.options;
3918
- const directory = sp.dirname(path8);
3919
- const basename3 = sp.basename(path8);
3921
+ const directory = sp.dirname(path7);
3922
+ const basename3 = sp.basename(path7);
3920
3923
  const parent = this.fsw._getWatchedDir(directory);
3921
3924
  parent.add(basename3);
3922
- const absolutePath = sp.resolve(path8);
3925
+ const absolutePath = sp.resolve(path7);
3923
3926
  const options = {
3924
3927
  persistent: opts.persistent
3925
3928
  };
@@ -3929,12 +3932,12 @@ var NodeFsHandler = class {
3929
3932
  if (opts.usePolling) {
3930
3933
  const enableBin = opts.interval !== opts.binaryInterval;
3931
3934
  options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
3932
- closer = setFsWatchFileListener(path8, absolutePath, options, {
3935
+ closer = setFsWatchFileListener(path7, absolutePath, options, {
3933
3936
  listener,
3934
3937
  rawEmitter: this.fsw._emitRaw
3935
3938
  });
3936
3939
  } else {
3937
- closer = setFsWatchListener(path8, absolutePath, options, {
3940
+ closer = setFsWatchListener(path7, absolutePath, options, {
3938
3941
  listener,
3939
3942
  errHandler: this._boundHandleError,
3940
3943
  rawEmitter: this.fsw._emitRaw
@@ -3956,7 +3959,7 @@ var NodeFsHandler = class {
3956
3959
  let prevStats = stats;
3957
3960
  if (parent.has(basename3))
3958
3961
  return;
3959
- const listener = async (path8, newStats) => {
3962
+ const listener = async (path7, newStats) => {
3960
3963
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
3961
3964
  return;
3962
3965
  if (!newStats || newStats.mtimeMs === 0) {
@@ -3970,11 +3973,11 @@ var NodeFsHandler = class {
3970
3973
  this.fsw._emit(EV.CHANGE, file, newStats2);
3971
3974
  }
3972
3975
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
3973
- this.fsw._closeFile(path8);
3976
+ this.fsw._closeFile(path7);
3974
3977
  prevStats = newStats2;
3975
3978
  const closer2 = this._watchWithNodeFs(file, listener);
3976
3979
  if (closer2)
3977
- this.fsw._addPathCloser(path8, closer2);
3980
+ this.fsw._addPathCloser(path7, closer2);
3978
3981
  } else {
3979
3982
  prevStats = newStats2;
3980
3983
  }
@@ -4006,7 +4009,7 @@ var NodeFsHandler = class {
4006
4009
  * @param item basename of this item
4007
4010
  * @returns true if no more processing is needed for this entry.
4008
4011
  */
4009
- async _handleSymlink(entry, directory, path8, item) {
4012
+ async _handleSymlink(entry, directory, path7, item) {
4010
4013
  if (this.fsw.closed) {
4011
4014
  return;
4012
4015
  }
@@ -4016,7 +4019,7 @@ var NodeFsHandler = class {
4016
4019
  this.fsw._incrReadyCount();
4017
4020
  let linkPath;
4018
4021
  try {
4019
- linkPath = await fsrealpath(path8);
4022
+ linkPath = await fsrealpath(path7);
4020
4023
  } catch (e) {
4021
4024
  this.fsw._emitReady();
4022
4025
  return true;
@@ -4026,12 +4029,12 @@ var NodeFsHandler = class {
4026
4029
  if (dir.has(item)) {
4027
4030
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
4028
4031
  this.fsw._symlinkPaths.set(full, linkPath);
4029
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
4032
+ this.fsw._emit(EV.CHANGE, path7, entry.stats);
4030
4033
  }
4031
4034
  } else {
4032
4035
  dir.add(item);
4033
4036
  this.fsw._symlinkPaths.set(full, linkPath);
4034
- this.fsw._emit(EV.ADD, path8, entry.stats);
4037
+ this.fsw._emit(EV.ADD, path7, entry.stats);
4035
4038
  }
4036
4039
  this.fsw._emitReady();
4037
4040
  return true;
@@ -4061,9 +4064,9 @@ var NodeFsHandler = class {
4061
4064
  return;
4062
4065
  }
4063
4066
  const item = entry.path;
4064
- let path8 = sp.join(directory, item);
4067
+ let path7 = sp.join(directory, item);
4065
4068
  current.add(item);
4066
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
4069
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path7, item)) {
4067
4070
  return;
4068
4071
  }
4069
4072
  if (this.fsw.closed) {
@@ -4072,8 +4075,8 @@ var NodeFsHandler = class {
4072
4075
  }
4073
4076
  if (item === target || !target && !previous.has(item)) {
4074
4077
  this.fsw._incrReadyCount();
4075
- path8 = sp.join(dir, sp.relative(dir, path8));
4076
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
4078
+ path7 = sp.join(dir, sp.relative(dir, path7));
4079
+ this._addToNodeFs(path7, initialAdd, wh, depth + 1);
4077
4080
  }
4078
4081
  }).on(EV.ERROR, this._boundHandleError);
4079
4082
  return new Promise((resolve4, reject) => {
@@ -4142,13 +4145,13 @@ var NodeFsHandler = class {
4142
4145
  * @param depth Child path actually targeted for watch
4143
4146
  * @param target Child path actually targeted for watch
4144
4147
  */
4145
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
4148
+ async _addToNodeFs(path7, initialAdd, priorWh, depth, target) {
4146
4149
  const ready = this.fsw._emitReady;
4147
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
4150
+ if (this.fsw._isIgnored(path7) || this.fsw.closed) {
4148
4151
  ready();
4149
4152
  return false;
4150
4153
  }
4151
- const wh = this.fsw._getWatchHelpers(path8);
4154
+ const wh = this.fsw._getWatchHelpers(path7);
4152
4155
  if (priorWh) {
4153
4156
  wh.filterPath = (entry) => priorWh.filterPath(entry);
4154
4157
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -4164,8 +4167,8 @@ var NodeFsHandler = class {
4164
4167
  const follow = this.fsw.options.followSymlinks;
4165
4168
  let closer;
4166
4169
  if (stats.isDirectory()) {
4167
- const absPath = sp.resolve(path8);
4168
- const targetPath = follow ? await fsrealpath(path8) : path8;
4170
+ const absPath = sp.resolve(path7);
4171
+ const targetPath = follow ? await fsrealpath(path7) : path7;
4169
4172
  if (this.fsw.closed)
4170
4173
  return;
4171
4174
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -4175,29 +4178,29 @@ var NodeFsHandler = class {
4175
4178
  this.fsw._symlinkPaths.set(absPath, targetPath);
4176
4179
  }
4177
4180
  } else if (stats.isSymbolicLink()) {
4178
- const targetPath = follow ? await fsrealpath(path8) : path8;
4181
+ const targetPath = follow ? await fsrealpath(path7) : path7;
4179
4182
  if (this.fsw.closed)
4180
4183
  return;
4181
4184
  const parent = sp.dirname(wh.watchPath);
4182
4185
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
4183
4186
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
4184
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
4187
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path7, wh, targetPath);
4185
4188
  if (this.fsw.closed)
4186
4189
  return;
4187
4190
  if (targetPath !== void 0) {
4188
- this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
4191
+ this.fsw._symlinkPaths.set(sp.resolve(path7), targetPath);
4189
4192
  }
4190
4193
  } else {
4191
4194
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
4192
4195
  }
4193
4196
  ready();
4194
4197
  if (closer)
4195
- this.fsw._addPathCloser(path8, closer);
4198
+ this.fsw._addPathCloser(path7, closer);
4196
4199
  return false;
4197
4200
  } catch (error) {
4198
4201
  if (this.fsw._handleError(error)) {
4199
4202
  ready();
4200
- return path8;
4203
+ return path7;
4201
4204
  }
4202
4205
  }
4203
4206
  }
@@ -4240,24 +4243,24 @@ function createPattern(matcher) {
4240
4243
  }
4241
4244
  return () => false;
4242
4245
  }
4243
- function normalizePath(path8) {
4244
- if (typeof path8 !== "string")
4246
+ function normalizePath(path7) {
4247
+ if (typeof path7 !== "string")
4245
4248
  throw new Error("string expected");
4246
- path8 = sp2.normalize(path8);
4247
- path8 = path8.replace(/\\/g, "/");
4249
+ path7 = sp2.normalize(path7);
4250
+ path7 = path7.replace(/\\/g, "/");
4248
4251
  let prepend = false;
4249
- if (path8.startsWith("//"))
4252
+ if (path7.startsWith("//"))
4250
4253
  prepend = true;
4251
- path8 = path8.replace(DOUBLE_SLASH_RE, "/");
4254
+ path7 = path7.replace(DOUBLE_SLASH_RE, "/");
4252
4255
  if (prepend)
4253
- path8 = "/" + path8;
4254
- return path8;
4256
+ path7 = "/" + path7;
4257
+ return path7;
4255
4258
  }
4256
4259
  function matchPatterns(patterns, testString, stats) {
4257
- const path8 = normalizePath(testString);
4260
+ const path7 = normalizePath(testString);
4258
4261
  for (let index = 0; index < patterns.length; index++) {
4259
4262
  const pattern = patterns[index];
4260
- if (pattern(path8, stats)) {
4263
+ if (pattern(path7, stats)) {
4261
4264
  return true;
4262
4265
  }
4263
4266
  }
@@ -4295,19 +4298,19 @@ var toUnix = (string) => {
4295
4298
  }
4296
4299
  return str;
4297
4300
  };
4298
- var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
4299
- var normalizeIgnored = (cwd = "") => (path8) => {
4300
- if (typeof path8 === "string") {
4301
- return normalizePathToUnix(sp2.isAbsolute(path8) ? path8 : sp2.join(cwd, path8));
4301
+ var normalizePathToUnix = (path7) => toUnix(sp2.normalize(toUnix(path7)));
4302
+ var normalizeIgnored = (cwd = "") => (path7) => {
4303
+ if (typeof path7 === "string") {
4304
+ return normalizePathToUnix(sp2.isAbsolute(path7) ? path7 : sp2.join(cwd, path7));
4302
4305
  } else {
4303
- return path8;
4306
+ return path7;
4304
4307
  }
4305
4308
  };
4306
- var getAbsolutePath = (path8, cwd) => {
4307
- if (sp2.isAbsolute(path8)) {
4308
- return path8;
4309
+ var getAbsolutePath = (path7, cwd) => {
4310
+ if (sp2.isAbsolute(path7)) {
4311
+ return path7;
4309
4312
  }
4310
- return sp2.join(cwd, path8);
4313
+ return sp2.join(cwd, path7);
4311
4314
  };
4312
4315
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
4313
4316
  var DirEntry = class {
@@ -4372,10 +4375,10 @@ var WatchHelper = class {
4372
4375
  dirParts;
4373
4376
  followSymlinks;
4374
4377
  statMethod;
4375
- constructor(path8, follow, fsw) {
4378
+ constructor(path7, follow, fsw) {
4376
4379
  this.fsw = fsw;
4377
- const watchPath = path8;
4378
- this.path = path8 = path8.replace(REPLACER_RE, "");
4380
+ const watchPath = path7;
4381
+ this.path = path7 = path7.replace(REPLACER_RE, "");
4379
4382
  this.watchPath = watchPath;
4380
4383
  this.fullWatchPath = sp2.resolve(watchPath);
4381
4384
  this.dirParts = [];
@@ -4515,20 +4518,20 @@ var FSWatcher = class extends EventEmitter2 {
4515
4518
  this._closePromise = void 0;
4516
4519
  let paths = unifyPaths(paths_);
4517
4520
  if (cwd) {
4518
- paths = paths.map((path8) => {
4519
- const absPath = getAbsolutePath(path8, cwd);
4521
+ paths = paths.map((path7) => {
4522
+ const absPath = getAbsolutePath(path7, cwd);
4520
4523
  return absPath;
4521
4524
  });
4522
4525
  }
4523
- paths.forEach((path8) => {
4524
- this._removeIgnoredPath(path8);
4526
+ paths.forEach((path7) => {
4527
+ this._removeIgnoredPath(path7);
4525
4528
  });
4526
4529
  this._userIgnored = void 0;
4527
4530
  if (!this._readyCount)
4528
4531
  this._readyCount = 0;
4529
4532
  this._readyCount += paths.length;
4530
- Promise.all(paths.map(async (path8) => {
4531
- const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
4533
+ Promise.all(paths.map(async (path7) => {
4534
+ const res = await this._nodeFsHandler._addToNodeFs(path7, !_internal, void 0, 0, _origAdd);
4532
4535
  if (res)
4533
4536
  this._emitReady();
4534
4537
  return res;
@@ -4550,17 +4553,17 @@ var FSWatcher = class extends EventEmitter2 {
4550
4553
  return this;
4551
4554
  const paths = unifyPaths(paths_);
4552
4555
  const { cwd } = this.options;
4553
- paths.forEach((path8) => {
4554
- if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
4556
+ paths.forEach((path7) => {
4557
+ if (!sp2.isAbsolute(path7) && !this._closers.has(path7)) {
4555
4558
  if (cwd)
4556
- path8 = sp2.join(cwd, path8);
4557
- path8 = sp2.resolve(path8);
4559
+ path7 = sp2.join(cwd, path7);
4560
+ path7 = sp2.resolve(path7);
4558
4561
  }
4559
- this._closePath(path8);
4560
- this._addIgnoredPath(path8);
4561
- if (this._watched.has(path8)) {
4562
+ this._closePath(path7);
4563
+ this._addIgnoredPath(path7);
4564
+ if (this._watched.has(path7)) {
4562
4565
  this._addIgnoredPath({
4563
- path: path8,
4566
+ path: path7,
4564
4567
  recursive: true
4565
4568
  });
4566
4569
  }
@@ -4624,38 +4627,38 @@ var FSWatcher = class extends EventEmitter2 {
4624
4627
  * @param stats arguments to be passed with event
4625
4628
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
4626
4629
  */
4627
- async _emit(event, path8, stats) {
4630
+ async _emit(event, path7, stats) {
4628
4631
  if (this.closed)
4629
4632
  return;
4630
4633
  const opts = this.options;
4631
4634
  if (isWindows)
4632
- path8 = sp2.normalize(path8);
4635
+ path7 = sp2.normalize(path7);
4633
4636
  if (opts.cwd)
4634
- path8 = sp2.relative(opts.cwd, path8);
4635
- const args = [path8];
4637
+ path7 = sp2.relative(opts.cwd, path7);
4638
+ const args = [path7];
4636
4639
  if (stats != null)
4637
4640
  args.push(stats);
4638
4641
  const awf = opts.awaitWriteFinish;
4639
4642
  let pw;
4640
- if (awf && (pw = this._pendingWrites.get(path8))) {
4643
+ if (awf && (pw = this._pendingWrites.get(path7))) {
4641
4644
  pw.lastChange = /* @__PURE__ */ new Date();
4642
4645
  return this;
4643
4646
  }
4644
4647
  if (opts.atomic) {
4645
4648
  if (event === EVENTS.UNLINK) {
4646
- this._pendingUnlinks.set(path8, [event, ...args]);
4649
+ this._pendingUnlinks.set(path7, [event, ...args]);
4647
4650
  setTimeout(() => {
4648
- this._pendingUnlinks.forEach((entry, path9) => {
4651
+ this._pendingUnlinks.forEach((entry, path8) => {
4649
4652
  this.emit(...entry);
4650
4653
  this.emit(EVENTS.ALL, ...entry);
4651
- this._pendingUnlinks.delete(path9);
4654
+ this._pendingUnlinks.delete(path8);
4652
4655
  });
4653
4656
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
4654
4657
  return this;
4655
4658
  }
4656
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
4659
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path7)) {
4657
4660
  event = EVENTS.CHANGE;
4658
- this._pendingUnlinks.delete(path8);
4661
+ this._pendingUnlinks.delete(path7);
4659
4662
  }
4660
4663
  }
4661
4664
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -4673,16 +4676,16 @@ var FSWatcher = class extends EventEmitter2 {
4673
4676
  this.emitWithAll(event, args);
4674
4677
  }
4675
4678
  };
4676
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
4679
+ this._awaitWriteFinish(path7, awf.stabilityThreshold, event, awfEmit);
4677
4680
  return this;
4678
4681
  }
4679
4682
  if (event === EVENTS.CHANGE) {
4680
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
4683
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path7, 50);
4681
4684
  if (isThrottled)
4682
4685
  return this;
4683
4686
  }
4684
4687
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
4685
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
4688
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path7) : path7;
4686
4689
  let stats2;
4687
4690
  try {
4688
4691
  stats2 = await stat3(fullPath);
@@ -4713,23 +4716,23 @@ var FSWatcher = class extends EventEmitter2 {
4713
4716
  * @param timeout duration of time to suppress duplicate actions
4714
4717
  * @returns tracking object or false if action should be suppressed
4715
4718
  */
4716
- _throttle(actionType, path8, timeout) {
4719
+ _throttle(actionType, path7, timeout) {
4717
4720
  if (!this._throttled.has(actionType)) {
4718
4721
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
4719
4722
  }
4720
4723
  const action = this._throttled.get(actionType);
4721
4724
  if (!action)
4722
4725
  throw new Error("invalid throttle");
4723
- const actionPath = action.get(path8);
4726
+ const actionPath = action.get(path7);
4724
4727
  if (actionPath) {
4725
4728
  actionPath.count++;
4726
4729
  return false;
4727
4730
  }
4728
4731
  let timeoutObject;
4729
4732
  const clear = () => {
4730
- const item = action.get(path8);
4733
+ const item = action.get(path7);
4731
4734
  const count = item ? item.count : 0;
4732
- action.delete(path8);
4735
+ action.delete(path7);
4733
4736
  clearTimeout(timeoutObject);
4734
4737
  if (item)
4735
4738
  clearTimeout(item.timeoutObject);
@@ -4737,7 +4740,7 @@ var FSWatcher = class extends EventEmitter2 {
4737
4740
  };
4738
4741
  timeoutObject = setTimeout(clear, timeout);
4739
4742
  const thr = { timeoutObject, clear, count: 0 };
4740
- action.set(path8, thr);
4743
+ action.set(path7, thr);
4741
4744
  return thr;
4742
4745
  }
4743
4746
  _incrReadyCount() {
@@ -4751,44 +4754,44 @@ var FSWatcher = class extends EventEmitter2 {
4751
4754
  * @param event
4752
4755
  * @param awfEmit Callback to be called when ready for event to be emitted.
4753
4756
  */
4754
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
4757
+ _awaitWriteFinish(path7, threshold, event, awfEmit) {
4755
4758
  const awf = this.options.awaitWriteFinish;
4756
4759
  if (typeof awf !== "object")
4757
4760
  return;
4758
4761
  const pollInterval = awf.pollInterval;
4759
4762
  let timeoutHandler;
4760
- let fullPath = path8;
4761
- if (this.options.cwd && !sp2.isAbsolute(path8)) {
4762
- fullPath = sp2.join(this.options.cwd, path8);
4763
+ let fullPath = path7;
4764
+ if (this.options.cwd && !sp2.isAbsolute(path7)) {
4765
+ fullPath = sp2.join(this.options.cwd, path7);
4763
4766
  }
4764
4767
  const now = /* @__PURE__ */ new Date();
4765
4768
  const writes = this._pendingWrites;
4766
4769
  function awaitWriteFinishFn(prevStat) {
4767
4770
  statcb(fullPath, (err, curStat) => {
4768
- if (err || !writes.has(path8)) {
4771
+ if (err || !writes.has(path7)) {
4769
4772
  if (err && err.code !== "ENOENT")
4770
4773
  awfEmit(err);
4771
4774
  return;
4772
4775
  }
4773
4776
  const now2 = Number(/* @__PURE__ */ new Date());
4774
4777
  if (prevStat && curStat.size !== prevStat.size) {
4775
- writes.get(path8).lastChange = now2;
4778
+ writes.get(path7).lastChange = now2;
4776
4779
  }
4777
- const pw = writes.get(path8);
4780
+ const pw = writes.get(path7);
4778
4781
  const df = now2 - pw.lastChange;
4779
4782
  if (df >= threshold) {
4780
- writes.delete(path8);
4783
+ writes.delete(path7);
4781
4784
  awfEmit(void 0, curStat);
4782
4785
  } else {
4783
4786
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
4784
4787
  }
4785
4788
  });
4786
4789
  }
4787
- if (!writes.has(path8)) {
4788
- writes.set(path8, {
4790
+ if (!writes.has(path7)) {
4791
+ writes.set(path7, {
4789
4792
  lastChange: now,
4790
4793
  cancelWait: () => {
4791
- writes.delete(path8);
4794
+ writes.delete(path7);
4792
4795
  clearTimeout(timeoutHandler);
4793
4796
  return event;
4794
4797
  }
@@ -4799,8 +4802,8 @@ var FSWatcher = class extends EventEmitter2 {
4799
4802
  /**
4800
4803
  * Determines whether user has asked to ignore this path.
4801
4804
  */
4802
- _isIgnored(path8, stats) {
4803
- if (this.options.atomic && DOT_RE.test(path8))
4805
+ _isIgnored(path7, stats) {
4806
+ if (this.options.atomic && DOT_RE.test(path7))
4804
4807
  return true;
4805
4808
  if (!this._userIgnored) {
4806
4809
  const { cwd } = this.options;
@@ -4810,17 +4813,17 @@ var FSWatcher = class extends EventEmitter2 {
4810
4813
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
4811
4814
  this._userIgnored = anymatch(list, void 0);
4812
4815
  }
4813
- return this._userIgnored(path8, stats);
4816
+ return this._userIgnored(path7, stats);
4814
4817
  }
4815
- _isntIgnored(path8, stat4) {
4816
- return !this._isIgnored(path8, stat4);
4818
+ _isntIgnored(path7, stat4) {
4819
+ return !this._isIgnored(path7, stat4);
4817
4820
  }
4818
4821
  /**
4819
4822
  * Provides a set of common helpers and properties relating to symlink handling.
4820
4823
  * @param path file or directory pattern being watched
4821
4824
  */
4822
- _getWatchHelpers(path8) {
4823
- return new WatchHelper(path8, this.options.followSymlinks, this);
4825
+ _getWatchHelpers(path7) {
4826
+ return new WatchHelper(path7, this.options.followSymlinks, this);
4824
4827
  }
4825
4828
  // Directory helpers
4826
4829
  // -----------------
@@ -4852,63 +4855,63 @@ var FSWatcher = class extends EventEmitter2 {
4852
4855
  * @param item base path of item/directory
4853
4856
  */
4854
4857
  _remove(directory, item, isDirectory) {
4855
- const path8 = sp2.join(directory, item);
4856
- const fullPath = sp2.resolve(path8);
4857
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
4858
- if (!this._throttle("remove", path8, 100))
4858
+ const path7 = sp2.join(directory, item);
4859
+ const fullPath = sp2.resolve(path7);
4860
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path7) || this._watched.has(fullPath);
4861
+ if (!this._throttle("remove", path7, 100))
4859
4862
  return;
4860
4863
  if (!isDirectory && this._watched.size === 1) {
4861
4864
  this.add(directory, item, true);
4862
4865
  }
4863
- const wp = this._getWatchedDir(path8);
4866
+ const wp = this._getWatchedDir(path7);
4864
4867
  const nestedDirectoryChildren = wp.getChildren();
4865
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
4868
+ nestedDirectoryChildren.forEach((nested) => this._remove(path7, nested));
4866
4869
  const parent = this._getWatchedDir(directory);
4867
4870
  const wasTracked = parent.has(item);
4868
4871
  parent.remove(item);
4869
4872
  if (this._symlinkPaths.has(fullPath)) {
4870
4873
  this._symlinkPaths.delete(fullPath);
4871
4874
  }
4872
- let relPath = path8;
4875
+ let relPath = path7;
4873
4876
  if (this.options.cwd)
4874
- relPath = sp2.relative(this.options.cwd, path8);
4877
+ relPath = sp2.relative(this.options.cwd, path7);
4875
4878
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
4876
4879
  const event = this._pendingWrites.get(relPath).cancelWait();
4877
4880
  if (event === EVENTS.ADD)
4878
4881
  return;
4879
4882
  }
4880
- this._watched.delete(path8);
4883
+ this._watched.delete(path7);
4881
4884
  this._watched.delete(fullPath);
4882
4885
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
4883
- if (wasTracked && !this._isIgnored(path8))
4884
- this._emit(eventName, path8);
4885
- this._closePath(path8);
4886
+ if (wasTracked && !this._isIgnored(path7))
4887
+ this._emit(eventName, path7);
4888
+ this._closePath(path7);
4886
4889
  }
4887
4890
  /**
4888
4891
  * Closes all watchers for a path
4889
4892
  */
4890
- _closePath(path8) {
4891
- this._closeFile(path8);
4892
- const dir = sp2.dirname(path8);
4893
- this._getWatchedDir(dir).remove(sp2.basename(path8));
4893
+ _closePath(path7) {
4894
+ this._closeFile(path7);
4895
+ const dir = sp2.dirname(path7);
4896
+ this._getWatchedDir(dir).remove(sp2.basename(path7));
4894
4897
  }
4895
4898
  /**
4896
4899
  * Closes only file-specific watchers
4897
4900
  */
4898
- _closeFile(path8) {
4899
- const closers = this._closers.get(path8);
4901
+ _closeFile(path7) {
4902
+ const closers = this._closers.get(path7);
4900
4903
  if (!closers)
4901
4904
  return;
4902
4905
  closers.forEach((closer) => closer());
4903
- this._closers.delete(path8);
4906
+ this._closers.delete(path7);
4904
4907
  }
4905
- _addPathCloser(path8, closer) {
4908
+ _addPathCloser(path7, closer) {
4906
4909
  if (!closer)
4907
4910
  return;
4908
- let list = this._closers.get(path8);
4911
+ let list = this._closers.get(path7);
4909
4912
  if (!list) {
4910
4913
  list = [];
4911
- this._closers.set(path8, list);
4914
+ this._closers.set(path7, list);
4912
4915
  }
4913
4916
  list.push(closer);
4914
4917
  }
@@ -4938,7 +4941,7 @@ function watch(paths, options = {}) {
4938
4941
  var chokidar_default = { watch, FSWatcher };
4939
4942
 
4940
4943
  // src/watcher/index.ts
4941
- import * as path6 from "path";
4944
+ import * as path5 from "path";
4942
4945
  var FileWatcher = class {
4943
4946
  watcher = null;
4944
4947
  projectRoot;
@@ -4959,7 +4962,7 @@ var FileWatcher = class {
4959
4962
  const ignoreFilter = createIgnoreFilter(this.projectRoot);
4960
4963
  this.watcher = chokidar_default.watch(this.projectRoot, {
4961
4964
  ignored: (filePath) => {
4962
- const relativePath = path6.relative(this.projectRoot, filePath);
4965
+ const relativePath = path5.relative(this.projectRoot, filePath);
4963
4966
  if (!relativePath) return false;
4964
4967
  if (ignoreFilter.ignores(relativePath)) {
4965
4968
  return true;
@@ -5003,7 +5006,7 @@ var FileWatcher = class {
5003
5006
  return;
5004
5007
  }
5005
5008
  const changes = Array.from(this.pendingChanges.entries()).map(
5006
- ([path8, type]) => ({ path: path8, type })
5009
+ ([path7, type]) => ({ path: path7, type })
5007
5010
  );
5008
5011
  this.pendingChanges.clear();
5009
5012
  try {
@@ -5195,10 +5198,10 @@ function formatStatus(status) {
5195
5198
 
5196
5199
  // src/index.ts
5197
5200
  function loadPluginConfig(projectRoot) {
5198
- const configPath = path7.join(projectRoot, ".opencode", "codebase-index.json");
5201
+ const configPath = path6.join(projectRoot, ".opencode", "codebase-index.json");
5199
5202
  try {
5200
- if (existsSync5(configPath)) {
5201
- const content = readFileSync5(configPath, "utf-8");
5203
+ if (existsSync4(configPath)) {
5204
+ const content = readFileSync4(configPath, "utf-8");
5202
5205
  return JSON.parse(content);
5203
5206
  }
5204
5207
  } catch {
@@ -5227,6 +5230,39 @@ var plugin = async ({ directory }) => {
5227
5230
  index_codebase,
5228
5231
  index_status,
5229
5232
  index_health_check
5233
+ },
5234
+ async config(cfg) {
5235
+ cfg.command = cfg.command ?? {};
5236
+ cfg.command["search"] = {
5237
+ description: "Search codebase by meaning using semantic search",
5238
+ template: `Use the \`codebase_search\` tool to find code related to: $ARGUMENTS
5239
+
5240
+ If the index doesn't exist yet, run \`index_codebase\` first.
5241
+
5242
+ Return the most relevant results with file paths and line numbers.`
5243
+ };
5244
+ cfg.command["find"] = {
5245
+ description: "Find code using hybrid approach (semantic + grep)",
5246
+ template: `Find code related to: $ARGUMENTS
5247
+
5248
+ Strategy:
5249
+ 1. First use \`codebase_search\` to find semantically related code
5250
+ 2. From the results, identify specific function/class names
5251
+ 3. Use grep to find all occurrences of those identifiers
5252
+ 4. Combine findings into a comprehensive answer
5253
+
5254
+ If the semantic index doesn't exist, run \`index_codebase\` first.`
5255
+ };
5256
+ cfg.command["index"] = {
5257
+ description: "Index the codebase for semantic search",
5258
+ template: `Run the \`index_codebase\` tool to create or update the semantic search index.
5259
+
5260
+ Show progress and final statistics including:
5261
+ - Number of files processed
5262
+ - Number of chunks indexed
5263
+ - Tokens used
5264
+ - Duration`
5265
+ };
5230
5266
  }
5231
5267
  };
5232
5268
  };