opencode-codebase-index 0.1.8 → 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(path9, 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(path9);
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 = (path9, originalPath, doThrow) => {
525
- if (!isString(path9)) {
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 (!path9) {
531
+ if (!path7) {
532
532
  return doThrow(`path must not be empty`, TypeError);
533
533
  }
534
- if (checkPath.isNotRelative(path9)) {
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 = (path9) => REGEX_TEST_INVALID_PATH.test(path9);
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 path9 = originalPath && checkPath.convert(originalPath);
573
+ const path7 = originalPath && checkPath.convert(originalPath);
574
574
  checkPath(
575
- path9,
575
+ path7,
576
576
  originalPath,
577
577
  this._strictPathCheck ? throwError : RETURN_FALSE
578
578
  );
579
- return this._t(path9, cache, checkUnignored, slices);
579
+ return this._t(path7, cache, checkUnignored, slices);
580
580
  }
581
- checkIgnore(path9) {
582
- if (!REGEX_TEST_TRAILING_SLASH.test(path9)) {
583
- return this.test(path9);
581
+ checkIgnore(path7) {
582
+ if (!REGEX_TEST_TRAILING_SLASH.test(path7)) {
583
+ return this.test(path7);
584
584
  }
585
- const slices = path9.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(path9, false, MODE_CHECK_IGNORE);
598
+ return this._rules.test(path7, false, MODE_CHECK_IGNORE);
599
599
  }
600
- _t(path9, cache, checkUnignored, slices) {
601
- if (path9 in cache) {
602
- return cache[path9];
600
+ _t(path7, cache, checkUnignored, slices) {
601
+ if (path7 in cache) {
602
+ return cache[path7];
603
603
  }
604
604
  if (!slices) {
605
- slices = path9.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[path9] = this._rules.test(path9, 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[path9] = parent.ignored ? parent : this._rules.test(path9, checkUnignored, MODE_IGNORE);
617
+ return cache[path7] = parent.ignored ? parent : this._rules.test(path7, checkUnignored, MODE_IGNORE);
618
618
  }
619
- ignores(path9) {
620
- return this._test(path9, this._ignoreCache, false).ignored;
619
+ ignores(path7) {
620
+ return this._test(path7, this._ignoreCache, false).ignored;
621
621
  }
622
622
  createFilter() {
623
- return (path9) => !this.ignores(path9);
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(path9) {
630
- return this._test(path9, 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 = (path9) => checkPath(path9 && checkPath.convert(path9), path9, 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 = (path9) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path9) || isNotRelative(path9);
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 readFileSync6 } from "fs";
656
- import * as path8 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 readFileSync5, writeFileSync as writeFileSync3, promises as fsPromises2 } from "fs";
806
- import * as path6 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);
@@ -1874,7 +1878,7 @@ function getGitHubCopilotCredentials() {
1874
1878
  if (!copilotAuth || copilotAuth.type !== "oauth") {
1875
1879
  return null;
1876
1880
  }
1877
- const baseUrl = copilotAuth.enterpriseUrl ? `https://copilot-api.${copilotAuth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://api.githubcopilot.com";
1881
+ const baseUrl = copilotAuth.enterpriseUrl ? `https://copilot-api.${copilotAuth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
1878
1882
  return {
1879
1883
  provider: "github-copilot",
1880
1884
  baseUrl,
@@ -1949,9 +1953,6 @@ function getProviderDisplayName(provider) {
1949
1953
  }
1950
1954
 
1951
1955
  // src/embeddings/provider.ts
1952
- import { readFileSync as readFileSync2, writeFileSync } from "fs";
1953
- import * as path2 from "path";
1954
- import * as os2 from "os";
1955
1956
  function createEmbeddingProvider(credentials, modelInfo) {
1956
1957
  switch (credentials.provider) {
1957
1958
  case "github-copilot":
@@ -1966,56 +1967,16 @@ function createEmbeddingProvider(credentials, modelInfo) {
1966
1967
  throw new Error(`Unsupported embedding provider: ${credentials.provider}`);
1967
1968
  }
1968
1969
  }
1969
- var COPILOT_HEADERS = {
1970
- "User-Agent": "GitHubCopilotChat/0.35.0",
1971
- "Editor-Version": "vscode/1.107.0",
1972
- "Editor-Plugin-Version": "copilot-chat/0.35.0",
1973
- "Copilot-Integration-Id": "vscode-chat",
1974
- "Openai-Intent": "conversation-edits"
1975
- };
1976
1970
  var GitHubCopilotEmbeddingProvider = class {
1977
1971
  constructor(credentials, modelInfo) {
1978
1972
  this.credentials = credentials;
1979
1973
  this.modelInfo = modelInfo;
1980
- this.accessToken = credentials.accessToken || "";
1981
- this.tokenExpires = credentials.tokenExpires || 0;
1982
1974
  }
1983
- accessToken;
1984
- tokenExpires;
1985
- async ensureValidToken() {
1986
- if (this.accessToken && this.tokenExpires > Date.now()) {
1987
- return this.accessToken;
1988
- }
1975
+ getToken() {
1989
1976
  if (!this.credentials.refreshToken) {
1990
- throw new Error("No refresh token available for GitHub Copilot");
1991
- }
1992
- const response = await fetch("https://api.github.com/copilot_internal/v2/token", {
1993
- headers: {
1994
- Accept: "application/json",
1995
- Authorization: `Bearer ${this.credentials.refreshToken}`,
1996
- ...COPILOT_HEADERS
1997
- }
1998
- });
1999
- if (!response.ok) {
2000
- throw new Error(`Token refresh failed: ${response.status}`);
2001
- }
2002
- const tokenData = await response.json();
2003
- this.accessToken = tokenData.token;
2004
- this.tokenExpires = tokenData.expires_at * 1e3 - 5 * 60 * 1e3;
2005
- this.persistToken(tokenData.token, this.tokenExpires);
2006
- return this.accessToken;
2007
- }
2008
- persistToken(token, expires) {
2009
- try {
2010
- const authPath = path2.join(os2.homedir(), ".local", "share", "opencode", "auth.json");
2011
- const authData = JSON.parse(readFileSync2(authPath, "utf-8"));
2012
- if (authData["github-copilot"]) {
2013
- authData["github-copilot"].access = token;
2014
- authData["github-copilot"].expires = expires;
2015
- writeFileSync(authPath, JSON.stringify(authData, null, 2));
2016
- }
2017
- } catch {
1977
+ throw new Error("No OAuth token available for GitHub");
2018
1978
  }
1979
+ return this.credentials.refreshToken;
2019
1980
  }
2020
1981
  async embed(text) {
2021
1982
  const result = await this.embedBatch([text]);
@@ -2025,16 +1986,17 @@ var GitHubCopilotEmbeddingProvider = class {
2025
1986
  };
2026
1987
  }
2027
1988
  async embedBatch(texts) {
2028
- const token = await this.ensureValidToken();
2029
- const response = await fetch(`${this.credentials.baseUrl}/embeddings`, {
1989
+ const token = this.getToken();
1990
+ const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
2030
1991
  method: "POST",
2031
1992
  headers: {
2032
1993
  Authorization: `Bearer ${token}`,
2033
1994
  "Content-Type": "application/json",
2034
- ...COPILOT_HEADERS
1995
+ Accept: "application/vnd.github+json",
1996
+ "X-GitHub-Api-Version": "2022-11-28"
2035
1997
  },
2036
1998
  body: JSON.stringify({
2037
- model: this.modelInfo.model,
1999
+ model: `openai/${this.modelInfo.model}`,
2038
2000
  input: texts
2039
2001
  })
2040
2002
  });
@@ -2183,8 +2145,8 @@ var OllamaEmbeddingProvider = class {
2183
2145
 
2184
2146
  // src/utils/files.ts
2185
2147
  var import_ignore = __toESM(require_ignore(), 1);
2186
- import { existsSync as existsSync2, readFileSync as readFileSync3, promises as fsPromises } from "fs";
2187
- import * as path3 from "path";
2148
+ import { existsSync as existsSync2, readFileSync as readFileSync2, promises as fsPromises } from "fs";
2149
+ import * as path2 from "path";
2188
2150
  function createIgnoreFilter(projectRoot) {
2189
2151
  const ig = (0, import_ignore.default)();
2190
2152
  const defaultIgnores = [
@@ -2201,15 +2163,15 @@ function createIgnoreFilter(projectRoot) {
2201
2163
  ".opencode"
2202
2164
  ];
2203
2165
  ig.add(defaultIgnores);
2204
- const gitignorePath = path3.join(projectRoot, ".gitignore");
2166
+ const gitignorePath = path2.join(projectRoot, ".gitignore");
2205
2167
  if (existsSync2(gitignorePath)) {
2206
- const gitignoreContent = readFileSync3(gitignorePath, "utf-8");
2168
+ const gitignoreContent = readFileSync2(gitignorePath, "utf-8");
2207
2169
  ig.add(gitignoreContent);
2208
2170
  }
2209
2171
  return ig;
2210
2172
  }
2211
2173
  function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
2212
- const relativePath = path3.relative(projectRoot, filePath);
2174
+ const relativePath = path2.relative(projectRoot, filePath);
2213
2175
  if (ignoreFilter.ignores(relativePath)) {
2214
2176
  return false;
2215
2177
  }
@@ -2233,8 +2195,8 @@ function matchGlob(filePath, pattern) {
2233
2195
  async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped) {
2234
2196
  const entries = await fsPromises.readdir(dir, { withFileTypes: true });
2235
2197
  for (const entry of entries) {
2236
- const fullPath = path3.join(dir, entry.name);
2237
- const relativePath = path3.relative(projectRoot, fullPath);
2198
+ const fullPath = path2.join(dir, entry.name);
2199
+ const relativePath = path2.relative(projectRoot, fullPath);
2238
2200
  if (ignoreFilter.ignores(relativePath)) {
2239
2201
  if (entry.isFile()) {
2240
2202
  skipped.push({ path: relativePath, reason: "gitignore" });
@@ -2357,12 +2319,12 @@ function padRight(str, length) {
2357
2319
  }
2358
2320
 
2359
2321
  // src/native/index.ts
2360
- import * as path4 from "path";
2361
- import * as os3 from "os";
2322
+ import * as path3 from "path";
2323
+ import * as os2 from "os";
2362
2324
  import { fileURLToPath } from "url";
2363
2325
  function getNativeBinding() {
2364
- const platform2 = os3.platform();
2365
- const arch2 = os3.arch();
2326
+ const platform2 = os2.platform();
2327
+ const arch2 = os2.arch();
2366
2328
  let bindingName;
2367
2329
  if (platform2 === "darwin" && arch2 === "arm64") {
2368
2330
  bindingName = "codebase-index-native.darwin-arm64.node";
@@ -2379,15 +2341,15 @@ function getNativeBinding() {
2379
2341
  }
2380
2342
  let currentDir;
2381
2343
  if (typeof import.meta !== "undefined" && import.meta.url) {
2382
- currentDir = path4.dirname(fileURLToPath(import.meta.url));
2344
+ currentDir = path3.dirname(fileURLToPath(import.meta.url));
2383
2345
  } else if (typeof __dirname !== "undefined") {
2384
2346
  currentDir = __dirname;
2385
2347
  } else {
2386
2348
  currentDir = process.cwd();
2387
2349
  }
2388
2350
  const isDevMode = currentDir.includes("/src/native");
2389
- const packageRoot = isDevMode ? path4.resolve(currentDir, "../..") : path4.resolve(currentDir, "..");
2390
- const nativePath = path4.join(packageRoot, "native", bindingName);
2351
+ const packageRoot = isDevMode ? path3.resolve(currentDir, "../..") : path3.resolve(currentDir, "..");
2352
+ const nativePath = path3.join(packageRoot, "native", bindingName);
2391
2353
  return __require(nativePath);
2392
2354
  }
2393
2355
  var native = getNativeBinding();
@@ -2662,139 +2624,39 @@ function generateChunkId(filePath, chunk) {
2662
2624
  function generateChunkHash(chunk) {
2663
2625
  return hashContent(chunk.content);
2664
2626
  }
2665
-
2666
- // src/indexer/inverted-index.ts
2667
- import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
2668
- import * as path5 from "path";
2669
2627
  var InvertedIndex = class {
2670
- indexPath;
2671
- termToChunks = /* @__PURE__ */ new Map();
2672
- chunkTokens = /* @__PURE__ */ new Map();
2673
- totalTokenCount = 0;
2628
+ inner;
2674
2629
  constructor(indexPath) {
2675
- this.indexPath = path5.join(indexPath, "inverted-index.json");
2630
+ this.inner = new native.InvertedIndex(indexPath);
2676
2631
  }
2677
2632
  load() {
2678
- if (!existsSync3(this.indexPath)) {
2679
- return;
2680
- }
2681
- try {
2682
- const content = readFileSync4(this.indexPath, "utf-8");
2683
- const data = JSON.parse(content);
2684
- for (const [term, chunkIds] of Object.entries(data.termToChunks)) {
2685
- this.termToChunks.set(term, new Set(chunkIds));
2686
- }
2687
- for (const [chunkId, tokens] of Object.entries(data.chunkTokens)) {
2688
- const tokenMap = new Map(Object.entries(tokens).map(([k, v]) => [k, v]));
2689
- this.chunkTokens.set(chunkId, tokenMap);
2690
- for (const count of tokenMap.values()) {
2691
- this.totalTokenCount += count;
2692
- }
2693
- }
2694
- } catch {
2695
- this.termToChunks.clear();
2696
- this.chunkTokens.clear();
2697
- this.totalTokenCount = 0;
2698
- }
2633
+ this.inner.load();
2699
2634
  }
2700
2635
  save() {
2701
- const data = {
2702
- termToChunks: {},
2703
- chunkTokens: {},
2704
- avgDocLength: this.getAvgDocLength()
2705
- };
2706
- for (const [term, chunkIds] of this.termToChunks) {
2707
- data.termToChunks[term] = Array.from(chunkIds);
2708
- }
2709
- for (const [chunkId, tokens] of this.chunkTokens) {
2710
- data.chunkTokens[chunkId] = Object.fromEntries(tokens);
2711
- }
2712
- writeFileSync2(this.indexPath, JSON.stringify(data));
2636
+ this.inner.save();
2713
2637
  }
2714
2638
  addChunk(chunkId, content) {
2715
- const tokens = this.tokenize(content);
2716
- const termFreq = /* @__PURE__ */ new Map();
2717
- for (const token of tokens) {
2718
- termFreq.set(token, (termFreq.get(token) || 0) + 1);
2719
- const chunks = this.termToChunks.get(token) || /* @__PURE__ */ new Set();
2720
- chunks.add(chunkId);
2721
- this.termToChunks.set(token, chunks);
2722
- }
2723
- this.chunkTokens.set(chunkId, termFreq);
2724
- this.totalTokenCount += tokens.length;
2639
+ this.inner.addChunk(chunkId, content);
2725
2640
  }
2726
2641
  removeChunk(chunkId) {
2727
- const tokens = this.chunkTokens.get(chunkId);
2728
- if (!tokens) return;
2729
- for (const [token, count] of tokens) {
2730
- this.totalTokenCount -= count;
2731
- const chunks = this.termToChunks.get(token);
2732
- if (chunks) {
2733
- chunks.delete(chunkId);
2734
- if (chunks.size === 0) {
2735
- this.termToChunks.delete(token);
2736
- }
2737
- }
2738
- }
2739
- this.chunkTokens.delete(chunkId);
2642
+ return this.inner.removeChunk(chunkId);
2740
2643
  }
2741
- search(query) {
2742
- const queryTokens = this.tokenize(query);
2743
- if (queryTokens.length === 0) {
2744
- return /* @__PURE__ */ new Map();
2745
- }
2746
- const candidateChunks = /* @__PURE__ */ new Set();
2747
- for (const token of queryTokens) {
2748
- const chunks = this.termToChunks.get(token);
2749
- if (chunks) {
2750
- for (const chunkId of chunks) {
2751
- candidateChunks.add(chunkId);
2752
- }
2753
- }
2754
- }
2755
- const scores = /* @__PURE__ */ new Map();
2756
- const k1 = 1.2;
2757
- const b = 0.75;
2758
- const N = this.chunkTokens.size;
2759
- const avgDocLength = this.getAvgDocLength();
2760
- for (const chunkId of candidateChunks) {
2761
- const termFreq = this.chunkTokens.get(chunkId);
2762
- if (!termFreq) continue;
2763
- const docLength = Array.from(termFreq.values()).reduce((a, b2) => a + b2, 0);
2764
- let score = 0;
2765
- for (const term of queryTokens) {
2766
- const tf = termFreq.get(term) || 0;
2767
- if (tf === 0) continue;
2768
- const df = this.termToChunks.get(term)?.size || 0;
2769
- const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
2770
- const tfNorm = tf * (k1 + 1) / (tf + k1 * (1 - b + b * (docLength / avgDocLength)));
2771
- score += idf * tfNorm;
2772
- }
2773
- scores.set(chunkId, score);
2774
- }
2775
- const maxScore = Math.max(...scores.values(), 1);
2776
- for (const [chunkId, score] of scores) {
2777
- scores.set(chunkId, score / maxScore);
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);
2778
2649
  }
2779
- return scores;
2650
+ return map;
2780
2651
  }
2781
2652
  hasChunk(chunkId) {
2782
- return this.chunkTokens.has(chunkId);
2653
+ return this.inner.hasChunk(chunkId);
2783
2654
  }
2784
2655
  clear() {
2785
- this.termToChunks.clear();
2786
- this.chunkTokens.clear();
2787
- this.totalTokenCount = 0;
2656
+ this.inner.clear();
2788
2657
  }
2789
2658
  getDocumentCount() {
2790
- return this.chunkTokens.size;
2791
- }
2792
- getAvgDocLength() {
2793
- const count = this.chunkTokens.size;
2794
- return count > 0 ? this.totalTokenCount / count : 100;
2795
- }
2796
- tokenize(text) {
2797
- return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 2);
2659
+ return this.inner.documentCount();
2798
2660
  }
2799
2661
  };
2800
2662
 
@@ -2809,23 +2671,25 @@ var Indexer = class {
2809
2671
  detectedProvider = null;
2810
2672
  fileHashCache = /* @__PURE__ */ new Map();
2811
2673
  fileHashCachePath = "";
2674
+ failedBatchesPath = "";
2812
2675
  constructor(projectRoot, config) {
2813
2676
  this.projectRoot = projectRoot;
2814
2677
  this.config = config;
2815
2678
  this.indexPath = this.getIndexPath();
2816
- this.fileHashCachePath = path6.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");
2817
2681
  }
2818
2682
  getIndexPath() {
2819
2683
  if (this.config.scope === "global") {
2820
2684
  const homeDir = process.env.HOME || process.env.USERPROFILE || "";
2821
- return path6.join(homeDir, ".opencode", "global-index");
2685
+ return path4.join(homeDir, ".opencode", "global-index");
2822
2686
  }
2823
- return path6.join(this.projectRoot, ".opencode", "index");
2687
+ return path4.join(this.projectRoot, ".opencode", "index");
2824
2688
  }
2825
2689
  loadFileHashCache() {
2826
2690
  try {
2827
- if (existsSync4(this.fileHashCachePath)) {
2828
- const data = readFileSync5(this.fileHashCachePath, "utf-8");
2691
+ if (existsSync3(this.fileHashCachePath)) {
2692
+ const data = readFileSync3(this.fileHashCachePath, "utf-8");
2829
2693
  const parsed = JSON.parse(data);
2830
2694
  this.fileHashCache = new Map(Object.entries(parsed));
2831
2695
  }
@@ -2838,7 +2702,38 @@ var Indexer = class {
2838
2702
  for (const [k, v] of this.fileHashCache) {
2839
2703
  obj[k] = v;
2840
2704
  }
2841
- writeFileSync3(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);
2842
2737
  }
2843
2738
  async initialize() {
2844
2739
  this.detectedProvider = await detectEmbeddingProvider(this.config.embeddingProvider);
@@ -2853,13 +2748,14 @@ var Indexer = class {
2853
2748
  );
2854
2749
  await fsPromises2.mkdir(this.indexPath, { recursive: true });
2855
2750
  const dimensions = this.detectedProvider.modelInfo.dimensions;
2856
- const storePath = path6.join(this.indexPath, "vectors");
2751
+ const storePath = path4.join(this.indexPath, "vectors");
2857
2752
  this.store = new VectorStore(storePath, dimensions);
2858
- const indexFilePath = path6.join(this.indexPath, "vectors.usearch");
2859
- if (existsSync4(indexFilePath)) {
2753
+ const indexFilePath = path4.join(this.indexPath, "vectors.usearch");
2754
+ if (existsSync3(indexFilePath)) {
2860
2755
  this.store.load();
2861
2756
  }
2862
- this.invertedIndex = new InvertedIndex(this.indexPath);
2757
+ const invertedIndexPath = path4.join(this.indexPath, "inverted-index.json");
2758
+ this.invertedIndex = new InvertedIndex(invertedIndexPath);
2863
2759
  this.invertedIndex.load();
2864
2760
  }
2865
2761
  async ensureInitialized() {
@@ -2958,14 +2854,22 @@ var Indexer = class {
2958
2854
  for (const parsed of parsedFiles) {
2959
2855
  currentFilePaths.add(parsed.path);
2960
2856
  if (parsed.chunks.length === 0) {
2961
- const relativePath = path6.relative(this.projectRoot, parsed.path);
2857
+ const relativePath = path4.relative(this.projectRoot, parsed.path);
2962
2858
  stats.parseFailures.push(relativePath);
2963
2859
  }
2860
+ let fileChunkCount = 0;
2964
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
+ }
2965
2868
  const id = generateChunkId(parsed.path, chunk);
2966
2869
  const contentHash = generateChunkHash(chunk);
2967
2870
  currentChunkIds.add(id);
2968
2871
  if (existingChunks.get(id) === contentHash) {
2872
+ fileChunkCount++;
2969
2873
  continue;
2970
2874
  }
2971
2875
  const text = createEmbeddingText(chunk, parsed.path);
@@ -2979,6 +2883,7 @@ var Indexer = class {
2979
2883
  hash: contentHash
2980
2884
  };
2981
2885
  pendingChunks.push({ id, text, content: chunk.content, metadata });
2886
+ fileChunkCount++;
2982
2887
  }
2983
2888
  }
2984
2889
  let removedCount = 0;
@@ -3068,6 +2973,7 @@ var Indexer = class {
3068
2973
  });
3069
2974
  } catch (error) {
3070
2975
  stats.failedChunks += batch.length;
2976
+ this.addFailedBatch(batch, String(error));
3071
2977
  console.error(`Failed to embed batch after retries: ${error}`);
3072
2978
  }
3073
2979
  });
@@ -3085,6 +2991,9 @@ var Indexer = class {
3085
2991
  this.fileHashCache = currentFileHashes;
3086
2992
  this.saveFileHashCache();
3087
2993
  stats.durationMs = Date.now() - startTime;
2994
+ if (stats.failedChunks > 0) {
2995
+ stats.failedBatchesPath = this.failedBatchesPath;
2996
+ }
3088
2997
  onProgress?.({
3089
2998
  phase: "complete",
3090
2999
  filesProcessed: files.length,
@@ -3230,7 +3139,7 @@ var Indexer = class {
3230
3139
  const removedFilePaths = [];
3231
3140
  let removedCount = 0;
3232
3141
  for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
3233
- if (!existsSync4(filePath)) {
3142
+ if (!existsSync3(filePath)) {
3234
3143
  for (const key of chunkKeys) {
3235
3144
  store.remove(key);
3236
3145
  invertedIndex.removeChunk(key);
@@ -3245,6 +3154,58 @@ var Indexer = class {
3245
3154
  }
3246
3155
  return { removed: removedCount, filePaths: removedFilePaths };
3247
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
+ }
3248
3209
  };
3249
3210
 
3250
3211
  // node_modules/chokidar/index.js
@@ -3337,7 +3298,7 @@ var ReaddirpStream = class extends Readable {
3337
3298
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
3338
3299
  const statMethod = opts.lstat ? lstat : stat;
3339
3300
  if (wantBigintFsStats) {
3340
- this._stat = (path9) => statMethod(path9, { bigint: true });
3301
+ this._stat = (path7) => statMethod(path7, { bigint: true });
3341
3302
  } else {
3342
3303
  this._stat = statMethod;
3343
3304
  }
@@ -3362,8 +3323,8 @@ var ReaddirpStream = class extends Readable {
3362
3323
  const par = this.parent;
3363
3324
  const fil = par && par.files;
3364
3325
  if (fil && fil.length > 0) {
3365
- const { path: path9, depth } = par;
3366
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
3326
+ const { path: path7, depth } = par;
3327
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path7));
3367
3328
  const awaited = await Promise.all(slice);
3368
3329
  for (const entry of awaited) {
3369
3330
  if (!entry)
@@ -3403,20 +3364,20 @@ var ReaddirpStream = class extends Readable {
3403
3364
  this.reading = false;
3404
3365
  }
3405
3366
  }
3406
- async _exploreDir(path9, depth) {
3367
+ async _exploreDir(path7, depth) {
3407
3368
  let files;
3408
3369
  try {
3409
- files = await readdir(path9, this._rdOptions);
3370
+ files = await readdir(path7, this._rdOptions);
3410
3371
  } catch (error) {
3411
3372
  this._onError(error);
3412
3373
  }
3413
- return { files, depth, path: path9 };
3374
+ return { files, depth, path: path7 };
3414
3375
  }
3415
- async _formatEntry(dirent, path9) {
3376
+ async _formatEntry(dirent, path7) {
3416
3377
  let entry;
3417
3378
  const basename3 = this._isDirent ? dirent.name : dirent;
3418
3379
  try {
3419
- const fullPath = presolve(pjoin(path9, basename3));
3380
+ const fullPath = presolve(pjoin(path7, basename3));
3420
3381
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
3421
3382
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
3422
3383
  } catch (err) {
@@ -3816,16 +3777,16 @@ var delFromSet = (main, prop, item) => {
3816
3777
  };
3817
3778
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
3818
3779
  var FsWatchInstances = /* @__PURE__ */ new Map();
3819
- function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
3780
+ function createFsWatchInstance(path7, options, listener, errHandler, emitRaw) {
3820
3781
  const handleEvent = (rawEvent, evPath) => {
3821
- listener(path9);
3822
- emitRaw(rawEvent, evPath, { watchedPath: path9 });
3823
- if (evPath && path9 !== evPath) {
3824
- fsWatchBroadcast(sp.resolve(path9, evPath), KEY_LISTENERS, sp.join(path9, 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));
3825
3786
  }
3826
3787
  };
3827
3788
  try {
3828
- return fs_watch(path9, {
3789
+ return fs_watch(path7, {
3829
3790
  persistent: options.persistent
3830
3791
  }, handleEvent);
3831
3792
  } catch (error) {
@@ -3841,12 +3802,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
3841
3802
  listener(val1, val2, val3);
3842
3803
  });
3843
3804
  };
3844
- var setFsWatchListener = (path9, fullPath, options, handlers) => {
3805
+ var setFsWatchListener = (path7, fullPath, options, handlers) => {
3845
3806
  const { listener, errHandler, rawEmitter } = handlers;
3846
3807
  let cont = FsWatchInstances.get(fullPath);
3847
3808
  let watcher;
3848
3809
  if (!options.persistent) {
3849
- watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
3810
+ watcher = createFsWatchInstance(path7, options, listener, errHandler, rawEmitter);
3850
3811
  if (!watcher)
3851
3812
  return;
3852
3813
  return watcher.close.bind(watcher);
@@ -3857,7 +3818,7 @@ var setFsWatchListener = (path9, fullPath, options, handlers) => {
3857
3818
  addAndConvert(cont, KEY_RAW, rawEmitter);
3858
3819
  } else {
3859
3820
  watcher = createFsWatchInstance(
3860
- path9,
3821
+ path7,
3861
3822
  options,
3862
3823
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
3863
3824
  errHandler,
@@ -3872,7 +3833,7 @@ var setFsWatchListener = (path9, fullPath, options, handlers) => {
3872
3833
  cont.watcherUnusable = true;
3873
3834
  if (isWindows && error.code === "EPERM") {
3874
3835
  try {
3875
- const fd = await open(path9, "r");
3836
+ const fd = await open(path7, "r");
3876
3837
  await fd.close();
3877
3838
  broadcastErr(error);
3878
3839
  } catch (err) {
@@ -3903,7 +3864,7 @@ var setFsWatchListener = (path9, fullPath, options, handlers) => {
3903
3864
  };
3904
3865
  };
3905
3866
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
3906
- var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
3867
+ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
3907
3868
  const { listener, rawEmitter } = handlers;
3908
3869
  let cont = FsWatchFileInstances.get(fullPath);
3909
3870
  const copts = cont && cont.options;
@@ -3925,7 +3886,7 @@ var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
3925
3886
  });
3926
3887
  const currmtime = curr.mtimeMs;
3927
3888
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
3928
- foreach(cont.listeners, (listener2) => listener2(path9, curr));
3889
+ foreach(cont.listeners, (listener2) => listener2(path7, curr));
3929
3890
  }
3930
3891
  })
3931
3892
  };
@@ -3955,13 +3916,13 @@ var NodeFsHandler = class {
3955
3916
  * @param listener on fs change
3956
3917
  * @returns closer for the watcher instance
3957
3918
  */
3958
- _watchWithNodeFs(path9, listener) {
3919
+ _watchWithNodeFs(path7, listener) {
3959
3920
  const opts = this.fsw.options;
3960
- const directory = sp.dirname(path9);
3961
- const basename3 = sp.basename(path9);
3921
+ const directory = sp.dirname(path7);
3922
+ const basename3 = sp.basename(path7);
3962
3923
  const parent = this.fsw._getWatchedDir(directory);
3963
3924
  parent.add(basename3);
3964
- const absolutePath = sp.resolve(path9);
3925
+ const absolutePath = sp.resolve(path7);
3965
3926
  const options = {
3966
3927
  persistent: opts.persistent
3967
3928
  };
@@ -3971,12 +3932,12 @@ var NodeFsHandler = class {
3971
3932
  if (opts.usePolling) {
3972
3933
  const enableBin = opts.interval !== opts.binaryInterval;
3973
3934
  options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
3974
- closer = setFsWatchFileListener(path9, absolutePath, options, {
3935
+ closer = setFsWatchFileListener(path7, absolutePath, options, {
3975
3936
  listener,
3976
3937
  rawEmitter: this.fsw._emitRaw
3977
3938
  });
3978
3939
  } else {
3979
- closer = setFsWatchListener(path9, absolutePath, options, {
3940
+ closer = setFsWatchListener(path7, absolutePath, options, {
3980
3941
  listener,
3981
3942
  errHandler: this._boundHandleError,
3982
3943
  rawEmitter: this.fsw._emitRaw
@@ -3998,7 +3959,7 @@ var NodeFsHandler = class {
3998
3959
  let prevStats = stats;
3999
3960
  if (parent.has(basename3))
4000
3961
  return;
4001
- const listener = async (path9, newStats) => {
3962
+ const listener = async (path7, newStats) => {
4002
3963
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
4003
3964
  return;
4004
3965
  if (!newStats || newStats.mtimeMs === 0) {
@@ -4012,11 +3973,11 @@ var NodeFsHandler = class {
4012
3973
  this.fsw._emit(EV.CHANGE, file, newStats2);
4013
3974
  }
4014
3975
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
4015
- this.fsw._closeFile(path9);
3976
+ this.fsw._closeFile(path7);
4016
3977
  prevStats = newStats2;
4017
3978
  const closer2 = this._watchWithNodeFs(file, listener);
4018
3979
  if (closer2)
4019
- this.fsw._addPathCloser(path9, closer2);
3980
+ this.fsw._addPathCloser(path7, closer2);
4020
3981
  } else {
4021
3982
  prevStats = newStats2;
4022
3983
  }
@@ -4048,7 +4009,7 @@ var NodeFsHandler = class {
4048
4009
  * @param item basename of this item
4049
4010
  * @returns true if no more processing is needed for this entry.
4050
4011
  */
4051
- async _handleSymlink(entry, directory, path9, item) {
4012
+ async _handleSymlink(entry, directory, path7, item) {
4052
4013
  if (this.fsw.closed) {
4053
4014
  return;
4054
4015
  }
@@ -4058,7 +4019,7 @@ var NodeFsHandler = class {
4058
4019
  this.fsw._incrReadyCount();
4059
4020
  let linkPath;
4060
4021
  try {
4061
- linkPath = await fsrealpath(path9);
4022
+ linkPath = await fsrealpath(path7);
4062
4023
  } catch (e) {
4063
4024
  this.fsw._emitReady();
4064
4025
  return true;
@@ -4068,12 +4029,12 @@ var NodeFsHandler = class {
4068
4029
  if (dir.has(item)) {
4069
4030
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
4070
4031
  this.fsw._symlinkPaths.set(full, linkPath);
4071
- this.fsw._emit(EV.CHANGE, path9, entry.stats);
4032
+ this.fsw._emit(EV.CHANGE, path7, entry.stats);
4072
4033
  }
4073
4034
  } else {
4074
4035
  dir.add(item);
4075
4036
  this.fsw._symlinkPaths.set(full, linkPath);
4076
- this.fsw._emit(EV.ADD, path9, entry.stats);
4037
+ this.fsw._emit(EV.ADD, path7, entry.stats);
4077
4038
  }
4078
4039
  this.fsw._emitReady();
4079
4040
  return true;
@@ -4103,9 +4064,9 @@ var NodeFsHandler = class {
4103
4064
  return;
4104
4065
  }
4105
4066
  const item = entry.path;
4106
- let path9 = sp.join(directory, item);
4067
+ let path7 = sp.join(directory, item);
4107
4068
  current.add(item);
4108
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
4069
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path7, item)) {
4109
4070
  return;
4110
4071
  }
4111
4072
  if (this.fsw.closed) {
@@ -4114,8 +4075,8 @@ var NodeFsHandler = class {
4114
4075
  }
4115
4076
  if (item === target || !target && !previous.has(item)) {
4116
4077
  this.fsw._incrReadyCount();
4117
- path9 = sp.join(dir, sp.relative(dir, path9));
4118
- this._addToNodeFs(path9, initialAdd, wh, depth + 1);
4078
+ path7 = sp.join(dir, sp.relative(dir, path7));
4079
+ this._addToNodeFs(path7, initialAdd, wh, depth + 1);
4119
4080
  }
4120
4081
  }).on(EV.ERROR, this._boundHandleError);
4121
4082
  return new Promise((resolve4, reject) => {
@@ -4184,13 +4145,13 @@ var NodeFsHandler = class {
4184
4145
  * @param depth Child path actually targeted for watch
4185
4146
  * @param target Child path actually targeted for watch
4186
4147
  */
4187
- async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
4148
+ async _addToNodeFs(path7, initialAdd, priorWh, depth, target) {
4188
4149
  const ready = this.fsw._emitReady;
4189
- if (this.fsw._isIgnored(path9) || this.fsw.closed) {
4150
+ if (this.fsw._isIgnored(path7) || this.fsw.closed) {
4190
4151
  ready();
4191
4152
  return false;
4192
4153
  }
4193
- const wh = this.fsw._getWatchHelpers(path9);
4154
+ const wh = this.fsw._getWatchHelpers(path7);
4194
4155
  if (priorWh) {
4195
4156
  wh.filterPath = (entry) => priorWh.filterPath(entry);
4196
4157
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -4206,8 +4167,8 @@ var NodeFsHandler = class {
4206
4167
  const follow = this.fsw.options.followSymlinks;
4207
4168
  let closer;
4208
4169
  if (stats.isDirectory()) {
4209
- const absPath = sp.resolve(path9);
4210
- const targetPath = follow ? await fsrealpath(path9) : path9;
4170
+ const absPath = sp.resolve(path7);
4171
+ const targetPath = follow ? await fsrealpath(path7) : path7;
4211
4172
  if (this.fsw.closed)
4212
4173
  return;
4213
4174
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -4217,29 +4178,29 @@ var NodeFsHandler = class {
4217
4178
  this.fsw._symlinkPaths.set(absPath, targetPath);
4218
4179
  }
4219
4180
  } else if (stats.isSymbolicLink()) {
4220
- const targetPath = follow ? await fsrealpath(path9) : path9;
4181
+ const targetPath = follow ? await fsrealpath(path7) : path7;
4221
4182
  if (this.fsw.closed)
4222
4183
  return;
4223
4184
  const parent = sp.dirname(wh.watchPath);
4224
4185
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
4225
4186
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
4226
- closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
4187
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path7, wh, targetPath);
4227
4188
  if (this.fsw.closed)
4228
4189
  return;
4229
4190
  if (targetPath !== void 0) {
4230
- this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
4191
+ this.fsw._symlinkPaths.set(sp.resolve(path7), targetPath);
4231
4192
  }
4232
4193
  } else {
4233
4194
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
4234
4195
  }
4235
4196
  ready();
4236
4197
  if (closer)
4237
- this.fsw._addPathCloser(path9, closer);
4198
+ this.fsw._addPathCloser(path7, closer);
4238
4199
  return false;
4239
4200
  } catch (error) {
4240
4201
  if (this.fsw._handleError(error)) {
4241
4202
  ready();
4242
- return path9;
4203
+ return path7;
4243
4204
  }
4244
4205
  }
4245
4206
  }
@@ -4282,24 +4243,24 @@ function createPattern(matcher) {
4282
4243
  }
4283
4244
  return () => false;
4284
4245
  }
4285
- function normalizePath(path9) {
4286
- if (typeof path9 !== "string")
4246
+ function normalizePath(path7) {
4247
+ if (typeof path7 !== "string")
4287
4248
  throw new Error("string expected");
4288
- path9 = sp2.normalize(path9);
4289
- path9 = path9.replace(/\\/g, "/");
4249
+ path7 = sp2.normalize(path7);
4250
+ path7 = path7.replace(/\\/g, "/");
4290
4251
  let prepend = false;
4291
- if (path9.startsWith("//"))
4252
+ if (path7.startsWith("//"))
4292
4253
  prepend = true;
4293
- path9 = path9.replace(DOUBLE_SLASH_RE, "/");
4254
+ path7 = path7.replace(DOUBLE_SLASH_RE, "/");
4294
4255
  if (prepend)
4295
- path9 = "/" + path9;
4296
- return path9;
4256
+ path7 = "/" + path7;
4257
+ return path7;
4297
4258
  }
4298
4259
  function matchPatterns(patterns, testString, stats) {
4299
- const path9 = normalizePath(testString);
4260
+ const path7 = normalizePath(testString);
4300
4261
  for (let index = 0; index < patterns.length; index++) {
4301
4262
  const pattern = patterns[index];
4302
- if (pattern(path9, stats)) {
4263
+ if (pattern(path7, stats)) {
4303
4264
  return true;
4304
4265
  }
4305
4266
  }
@@ -4337,19 +4298,19 @@ var toUnix = (string) => {
4337
4298
  }
4338
4299
  return str;
4339
4300
  };
4340
- var normalizePathToUnix = (path9) => toUnix(sp2.normalize(toUnix(path9)));
4341
- var normalizeIgnored = (cwd = "") => (path9) => {
4342
- if (typeof path9 === "string") {
4343
- return normalizePathToUnix(sp2.isAbsolute(path9) ? path9 : sp2.join(cwd, path9));
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));
4344
4305
  } else {
4345
- return path9;
4306
+ return path7;
4346
4307
  }
4347
4308
  };
4348
- var getAbsolutePath = (path9, cwd) => {
4349
- if (sp2.isAbsolute(path9)) {
4350
- return path9;
4309
+ var getAbsolutePath = (path7, cwd) => {
4310
+ if (sp2.isAbsolute(path7)) {
4311
+ return path7;
4351
4312
  }
4352
- return sp2.join(cwd, path9);
4313
+ return sp2.join(cwd, path7);
4353
4314
  };
4354
4315
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
4355
4316
  var DirEntry = class {
@@ -4414,10 +4375,10 @@ var WatchHelper = class {
4414
4375
  dirParts;
4415
4376
  followSymlinks;
4416
4377
  statMethod;
4417
- constructor(path9, follow, fsw) {
4378
+ constructor(path7, follow, fsw) {
4418
4379
  this.fsw = fsw;
4419
- const watchPath = path9;
4420
- this.path = path9 = path9.replace(REPLACER_RE, "");
4380
+ const watchPath = path7;
4381
+ this.path = path7 = path7.replace(REPLACER_RE, "");
4421
4382
  this.watchPath = watchPath;
4422
4383
  this.fullWatchPath = sp2.resolve(watchPath);
4423
4384
  this.dirParts = [];
@@ -4557,20 +4518,20 @@ var FSWatcher = class extends EventEmitter2 {
4557
4518
  this._closePromise = void 0;
4558
4519
  let paths = unifyPaths(paths_);
4559
4520
  if (cwd) {
4560
- paths = paths.map((path9) => {
4561
- const absPath = getAbsolutePath(path9, cwd);
4521
+ paths = paths.map((path7) => {
4522
+ const absPath = getAbsolutePath(path7, cwd);
4562
4523
  return absPath;
4563
4524
  });
4564
4525
  }
4565
- paths.forEach((path9) => {
4566
- this._removeIgnoredPath(path9);
4526
+ paths.forEach((path7) => {
4527
+ this._removeIgnoredPath(path7);
4567
4528
  });
4568
4529
  this._userIgnored = void 0;
4569
4530
  if (!this._readyCount)
4570
4531
  this._readyCount = 0;
4571
4532
  this._readyCount += paths.length;
4572
- Promise.all(paths.map(async (path9) => {
4573
- const res = await this._nodeFsHandler._addToNodeFs(path9, !_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);
4574
4535
  if (res)
4575
4536
  this._emitReady();
4576
4537
  return res;
@@ -4592,17 +4553,17 @@ var FSWatcher = class extends EventEmitter2 {
4592
4553
  return this;
4593
4554
  const paths = unifyPaths(paths_);
4594
4555
  const { cwd } = this.options;
4595
- paths.forEach((path9) => {
4596
- if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
4556
+ paths.forEach((path7) => {
4557
+ if (!sp2.isAbsolute(path7) && !this._closers.has(path7)) {
4597
4558
  if (cwd)
4598
- path9 = sp2.join(cwd, path9);
4599
- path9 = sp2.resolve(path9);
4559
+ path7 = sp2.join(cwd, path7);
4560
+ path7 = sp2.resolve(path7);
4600
4561
  }
4601
- this._closePath(path9);
4602
- this._addIgnoredPath(path9);
4603
- if (this._watched.has(path9)) {
4562
+ this._closePath(path7);
4563
+ this._addIgnoredPath(path7);
4564
+ if (this._watched.has(path7)) {
4604
4565
  this._addIgnoredPath({
4605
- path: path9,
4566
+ path: path7,
4606
4567
  recursive: true
4607
4568
  });
4608
4569
  }
@@ -4666,38 +4627,38 @@ var FSWatcher = class extends EventEmitter2 {
4666
4627
  * @param stats arguments to be passed with event
4667
4628
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
4668
4629
  */
4669
- async _emit(event, path9, stats) {
4630
+ async _emit(event, path7, stats) {
4670
4631
  if (this.closed)
4671
4632
  return;
4672
4633
  const opts = this.options;
4673
4634
  if (isWindows)
4674
- path9 = sp2.normalize(path9);
4635
+ path7 = sp2.normalize(path7);
4675
4636
  if (opts.cwd)
4676
- path9 = sp2.relative(opts.cwd, path9);
4677
- const args = [path9];
4637
+ path7 = sp2.relative(opts.cwd, path7);
4638
+ const args = [path7];
4678
4639
  if (stats != null)
4679
4640
  args.push(stats);
4680
4641
  const awf = opts.awaitWriteFinish;
4681
4642
  let pw;
4682
- if (awf && (pw = this._pendingWrites.get(path9))) {
4643
+ if (awf && (pw = this._pendingWrites.get(path7))) {
4683
4644
  pw.lastChange = /* @__PURE__ */ new Date();
4684
4645
  return this;
4685
4646
  }
4686
4647
  if (opts.atomic) {
4687
4648
  if (event === EVENTS.UNLINK) {
4688
- this._pendingUnlinks.set(path9, [event, ...args]);
4649
+ this._pendingUnlinks.set(path7, [event, ...args]);
4689
4650
  setTimeout(() => {
4690
- this._pendingUnlinks.forEach((entry, path10) => {
4651
+ this._pendingUnlinks.forEach((entry, path8) => {
4691
4652
  this.emit(...entry);
4692
4653
  this.emit(EVENTS.ALL, ...entry);
4693
- this._pendingUnlinks.delete(path10);
4654
+ this._pendingUnlinks.delete(path8);
4694
4655
  });
4695
4656
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
4696
4657
  return this;
4697
4658
  }
4698
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
4659
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path7)) {
4699
4660
  event = EVENTS.CHANGE;
4700
- this._pendingUnlinks.delete(path9);
4661
+ this._pendingUnlinks.delete(path7);
4701
4662
  }
4702
4663
  }
4703
4664
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -4715,16 +4676,16 @@ var FSWatcher = class extends EventEmitter2 {
4715
4676
  this.emitWithAll(event, args);
4716
4677
  }
4717
4678
  };
4718
- this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
4679
+ this._awaitWriteFinish(path7, awf.stabilityThreshold, event, awfEmit);
4719
4680
  return this;
4720
4681
  }
4721
4682
  if (event === EVENTS.CHANGE) {
4722
- const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
4683
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path7, 50);
4723
4684
  if (isThrottled)
4724
4685
  return this;
4725
4686
  }
4726
4687
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
4727
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
4688
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path7) : path7;
4728
4689
  let stats2;
4729
4690
  try {
4730
4691
  stats2 = await stat3(fullPath);
@@ -4755,23 +4716,23 @@ var FSWatcher = class extends EventEmitter2 {
4755
4716
  * @param timeout duration of time to suppress duplicate actions
4756
4717
  * @returns tracking object or false if action should be suppressed
4757
4718
  */
4758
- _throttle(actionType, path9, timeout) {
4719
+ _throttle(actionType, path7, timeout) {
4759
4720
  if (!this._throttled.has(actionType)) {
4760
4721
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
4761
4722
  }
4762
4723
  const action = this._throttled.get(actionType);
4763
4724
  if (!action)
4764
4725
  throw new Error("invalid throttle");
4765
- const actionPath = action.get(path9);
4726
+ const actionPath = action.get(path7);
4766
4727
  if (actionPath) {
4767
4728
  actionPath.count++;
4768
4729
  return false;
4769
4730
  }
4770
4731
  let timeoutObject;
4771
4732
  const clear = () => {
4772
- const item = action.get(path9);
4733
+ const item = action.get(path7);
4773
4734
  const count = item ? item.count : 0;
4774
- action.delete(path9);
4735
+ action.delete(path7);
4775
4736
  clearTimeout(timeoutObject);
4776
4737
  if (item)
4777
4738
  clearTimeout(item.timeoutObject);
@@ -4779,7 +4740,7 @@ var FSWatcher = class extends EventEmitter2 {
4779
4740
  };
4780
4741
  timeoutObject = setTimeout(clear, timeout);
4781
4742
  const thr = { timeoutObject, clear, count: 0 };
4782
- action.set(path9, thr);
4743
+ action.set(path7, thr);
4783
4744
  return thr;
4784
4745
  }
4785
4746
  _incrReadyCount() {
@@ -4793,44 +4754,44 @@ var FSWatcher = class extends EventEmitter2 {
4793
4754
  * @param event
4794
4755
  * @param awfEmit Callback to be called when ready for event to be emitted.
4795
4756
  */
4796
- _awaitWriteFinish(path9, threshold, event, awfEmit) {
4757
+ _awaitWriteFinish(path7, threshold, event, awfEmit) {
4797
4758
  const awf = this.options.awaitWriteFinish;
4798
4759
  if (typeof awf !== "object")
4799
4760
  return;
4800
4761
  const pollInterval = awf.pollInterval;
4801
4762
  let timeoutHandler;
4802
- let fullPath = path9;
4803
- if (this.options.cwd && !sp2.isAbsolute(path9)) {
4804
- fullPath = sp2.join(this.options.cwd, path9);
4763
+ let fullPath = path7;
4764
+ if (this.options.cwd && !sp2.isAbsolute(path7)) {
4765
+ fullPath = sp2.join(this.options.cwd, path7);
4805
4766
  }
4806
4767
  const now = /* @__PURE__ */ new Date();
4807
4768
  const writes = this._pendingWrites;
4808
4769
  function awaitWriteFinishFn(prevStat) {
4809
4770
  statcb(fullPath, (err, curStat) => {
4810
- if (err || !writes.has(path9)) {
4771
+ if (err || !writes.has(path7)) {
4811
4772
  if (err && err.code !== "ENOENT")
4812
4773
  awfEmit(err);
4813
4774
  return;
4814
4775
  }
4815
4776
  const now2 = Number(/* @__PURE__ */ new Date());
4816
4777
  if (prevStat && curStat.size !== prevStat.size) {
4817
- writes.get(path9).lastChange = now2;
4778
+ writes.get(path7).lastChange = now2;
4818
4779
  }
4819
- const pw = writes.get(path9);
4780
+ const pw = writes.get(path7);
4820
4781
  const df = now2 - pw.lastChange;
4821
4782
  if (df >= threshold) {
4822
- writes.delete(path9);
4783
+ writes.delete(path7);
4823
4784
  awfEmit(void 0, curStat);
4824
4785
  } else {
4825
4786
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
4826
4787
  }
4827
4788
  });
4828
4789
  }
4829
- if (!writes.has(path9)) {
4830
- writes.set(path9, {
4790
+ if (!writes.has(path7)) {
4791
+ writes.set(path7, {
4831
4792
  lastChange: now,
4832
4793
  cancelWait: () => {
4833
- writes.delete(path9);
4794
+ writes.delete(path7);
4834
4795
  clearTimeout(timeoutHandler);
4835
4796
  return event;
4836
4797
  }
@@ -4841,8 +4802,8 @@ var FSWatcher = class extends EventEmitter2 {
4841
4802
  /**
4842
4803
  * Determines whether user has asked to ignore this path.
4843
4804
  */
4844
- _isIgnored(path9, stats) {
4845
- if (this.options.atomic && DOT_RE.test(path9))
4805
+ _isIgnored(path7, stats) {
4806
+ if (this.options.atomic && DOT_RE.test(path7))
4846
4807
  return true;
4847
4808
  if (!this._userIgnored) {
4848
4809
  const { cwd } = this.options;
@@ -4852,17 +4813,17 @@ var FSWatcher = class extends EventEmitter2 {
4852
4813
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
4853
4814
  this._userIgnored = anymatch(list, void 0);
4854
4815
  }
4855
- return this._userIgnored(path9, stats);
4816
+ return this._userIgnored(path7, stats);
4856
4817
  }
4857
- _isntIgnored(path9, stat4) {
4858
- return !this._isIgnored(path9, stat4);
4818
+ _isntIgnored(path7, stat4) {
4819
+ return !this._isIgnored(path7, stat4);
4859
4820
  }
4860
4821
  /**
4861
4822
  * Provides a set of common helpers and properties relating to symlink handling.
4862
4823
  * @param path file or directory pattern being watched
4863
4824
  */
4864
- _getWatchHelpers(path9) {
4865
- return new WatchHelper(path9, this.options.followSymlinks, this);
4825
+ _getWatchHelpers(path7) {
4826
+ return new WatchHelper(path7, this.options.followSymlinks, this);
4866
4827
  }
4867
4828
  // Directory helpers
4868
4829
  // -----------------
@@ -4894,63 +4855,63 @@ var FSWatcher = class extends EventEmitter2 {
4894
4855
  * @param item base path of item/directory
4895
4856
  */
4896
4857
  _remove(directory, item, isDirectory) {
4897
- const path9 = sp2.join(directory, item);
4898
- const fullPath = sp2.resolve(path9);
4899
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
4900
- if (!this._throttle("remove", path9, 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))
4901
4862
  return;
4902
4863
  if (!isDirectory && this._watched.size === 1) {
4903
4864
  this.add(directory, item, true);
4904
4865
  }
4905
- const wp = this._getWatchedDir(path9);
4866
+ const wp = this._getWatchedDir(path7);
4906
4867
  const nestedDirectoryChildren = wp.getChildren();
4907
- nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
4868
+ nestedDirectoryChildren.forEach((nested) => this._remove(path7, nested));
4908
4869
  const parent = this._getWatchedDir(directory);
4909
4870
  const wasTracked = parent.has(item);
4910
4871
  parent.remove(item);
4911
4872
  if (this._symlinkPaths.has(fullPath)) {
4912
4873
  this._symlinkPaths.delete(fullPath);
4913
4874
  }
4914
- let relPath = path9;
4875
+ let relPath = path7;
4915
4876
  if (this.options.cwd)
4916
- relPath = sp2.relative(this.options.cwd, path9);
4877
+ relPath = sp2.relative(this.options.cwd, path7);
4917
4878
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
4918
4879
  const event = this._pendingWrites.get(relPath).cancelWait();
4919
4880
  if (event === EVENTS.ADD)
4920
4881
  return;
4921
4882
  }
4922
- this._watched.delete(path9);
4883
+ this._watched.delete(path7);
4923
4884
  this._watched.delete(fullPath);
4924
4885
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
4925
- if (wasTracked && !this._isIgnored(path9))
4926
- this._emit(eventName, path9);
4927
- this._closePath(path9);
4886
+ if (wasTracked && !this._isIgnored(path7))
4887
+ this._emit(eventName, path7);
4888
+ this._closePath(path7);
4928
4889
  }
4929
4890
  /**
4930
4891
  * Closes all watchers for a path
4931
4892
  */
4932
- _closePath(path9) {
4933
- this._closeFile(path9);
4934
- const dir = sp2.dirname(path9);
4935
- this._getWatchedDir(dir).remove(sp2.basename(path9));
4893
+ _closePath(path7) {
4894
+ this._closeFile(path7);
4895
+ const dir = sp2.dirname(path7);
4896
+ this._getWatchedDir(dir).remove(sp2.basename(path7));
4936
4897
  }
4937
4898
  /**
4938
4899
  * Closes only file-specific watchers
4939
4900
  */
4940
- _closeFile(path9) {
4941
- const closers = this._closers.get(path9);
4901
+ _closeFile(path7) {
4902
+ const closers = this._closers.get(path7);
4942
4903
  if (!closers)
4943
4904
  return;
4944
4905
  closers.forEach((closer) => closer());
4945
- this._closers.delete(path9);
4906
+ this._closers.delete(path7);
4946
4907
  }
4947
- _addPathCloser(path9, closer) {
4908
+ _addPathCloser(path7, closer) {
4948
4909
  if (!closer)
4949
4910
  return;
4950
- let list = this._closers.get(path9);
4911
+ let list = this._closers.get(path7);
4951
4912
  if (!list) {
4952
4913
  list = [];
4953
- this._closers.set(path9, list);
4914
+ this._closers.set(path7, list);
4954
4915
  }
4955
4916
  list.push(closer);
4956
4917
  }
@@ -4980,7 +4941,7 @@ function watch(paths, options = {}) {
4980
4941
  var chokidar_default = { watch, FSWatcher };
4981
4942
 
4982
4943
  // src/watcher/index.ts
4983
- import * as path7 from "path";
4944
+ import * as path5 from "path";
4984
4945
  var FileWatcher = class {
4985
4946
  watcher = null;
4986
4947
  projectRoot;
@@ -5001,7 +4962,7 @@ var FileWatcher = class {
5001
4962
  const ignoreFilter = createIgnoreFilter(this.projectRoot);
5002
4963
  this.watcher = chokidar_default.watch(this.projectRoot, {
5003
4964
  ignored: (filePath) => {
5004
- const relativePath = path7.relative(this.projectRoot, filePath);
4965
+ const relativePath = path5.relative(this.projectRoot, filePath);
5005
4966
  if (!relativePath) return false;
5006
4967
  if (ignoreFilter.ignores(relativePath)) {
5007
4968
  return true;
@@ -5045,7 +5006,7 @@ var FileWatcher = class {
5045
5006
  return;
5046
5007
  }
5047
5008
  const changes = Array.from(this.pendingChanges.entries()).map(
5048
- ([path9, type]) => ({ path: path9, type })
5009
+ ([path7, type]) => ({ path: path7, type })
5049
5010
  );
5050
5011
  this.pendingChanges.clear();
5051
5012
  try {
@@ -5237,10 +5198,10 @@ function formatStatus(status) {
5237
5198
 
5238
5199
  // src/index.ts
5239
5200
  function loadPluginConfig(projectRoot) {
5240
- const configPath = path8.join(projectRoot, ".opencode", "codebase-index.json");
5201
+ const configPath = path6.join(projectRoot, ".opencode", "codebase-index.json");
5241
5202
  try {
5242
- if (existsSync5(configPath)) {
5243
- const content = readFileSync6(configPath, "utf-8");
5203
+ if (existsSync4(configPath)) {
5204
+ const content = readFileSync4(configPath, "utf-8");
5244
5205
  return JSON.parse(content);
5245
5206
  }
5246
5207
  } catch {
@@ -5269,6 +5230,39 @@ var plugin = async ({ directory }) => {
5269
5230
  index_codebase,
5270
5231
  index_status,
5271
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
+ };
5272
5266
  }
5273
5267
  };
5274
5268
  };