raggrep 0.14.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +83 -3
  2. package/dist/app/search/index.d.ts +16 -0
  3. package/dist/cli/main.js +940 -435
  4. package/dist/cli/main.js.map +26 -20
  5. package/dist/domain/entities/index.d.ts +1 -1
  6. package/dist/domain/entities/searchResult.d.ts +54 -0
  7. package/dist/domain/ports/filesystem.d.ts +3 -1
  8. package/dist/domain/services/chunkContext.d.ts +76 -0
  9. package/dist/domain/services/index.d.ts +2 -0
  10. package/dist/domain/services/simpleSearch.d.ts +75 -0
  11. package/dist/domain/usecases/exactSearch.d.ts +53 -0
  12. package/dist/domain/usecases/index.d.ts +1 -0
  13. package/dist/index.d.ts +38 -3
  14. package/dist/index.js +894 -407
  15. package/dist/index.js.map +24 -20
  16. package/dist/types.d.ts +1 -1
  17. package/package.json +2 -1
  18. package/dist/domain/services/bm25.test.d.ts +0 -4
  19. package/dist/domain/services/configValidator.test.d.ts +0 -1
  20. package/dist/domain/services/conventions/conventions.test.d.ts +0 -4
  21. package/dist/domain/services/introspection.test.d.ts +0 -4
  22. package/dist/domain/services/jsonPathExtractor.test.d.ts +0 -4
  23. package/dist/domain/services/lexicon.test.d.ts +0 -6
  24. package/dist/domain/services/literalExtractor.test.d.ts +0 -6
  25. package/dist/domain/services/phraseMatch.test.d.ts +0 -4
  26. package/dist/domain/services/queryLiteralParser.test.d.ts +0 -7
  27. package/dist/infrastructure/embeddings/embeddings.test.d.ts +0 -4
  28. package/dist/infrastructure/parsing/parsing.test.d.ts +0 -10
  29. package/dist/modules/core/symbols.test.d.ts +0 -4
  30. package/dist/modules/language/go/index.test.d.ts +0 -1
  31. package/dist/modules/language/rust/index.test.d.ts +0 -1
  32. package/dist/modules/language/typescript/parseCode.test.d.ts +0 -4
  33. package/dist/tests/integration.test.d.ts +0 -9
  34. package/dist/tests/ranking.test.d.ts +0 -12
  35. package/dist/tests/simulation-phrase-matching.test.d.ts +0 -14
  36. package/dist/tests/simulation-vocabulary.test.d.ts +0 -17
  37. package/dist/tests/vocabulary-scoring.test.d.ts +0 -16
  38. package/dist/tests/vocabulary.test.d.ts +0 -10
package/dist/cli/main.js CHANGED
@@ -16,7 +16,6 @@ var __toESM = (mod, isNodeMode, target) => {
16
16
  });
17
17
  return to;
18
18
  };
19
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
19
  var __export = (target, all) => {
21
20
  for (var name in all)
22
21
  __defProp(target, name, {
@@ -4536,6 +4535,209 @@ var init_phraseMatch = __esm(() => {
4536
4535
  ]);
4537
4536
  });
4538
4537
 
4538
+ // src/domain/services/simpleSearch.ts
4539
+ function isIdentifierQuery(query) {
4540
+ const trimmed = query.trim();
4541
+ if (trimmed.startsWith("`") && trimmed.endsWith("`") || trimmed.startsWith('"') && trimmed.endsWith('"')) {
4542
+ return true;
4543
+ }
4544
+ for (const pattern of Object.values(IDENTIFIER_PATTERNS)) {
4545
+ if (pattern.test(trimmed)) {
4546
+ return true;
4547
+ }
4548
+ }
4549
+ return false;
4550
+ }
4551
+ function extractSearchLiteral(query) {
4552
+ const trimmed = query.trim();
4553
+ if (trimmed.startsWith("`") && trimmed.endsWith("`")) {
4554
+ return trimmed.slice(1, -1);
4555
+ }
4556
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
4557
+ return trimmed.slice(1, -1);
4558
+ }
4559
+ return trimmed;
4560
+ }
4561
+ function findOccurrences(content, literal, options = {}) {
4562
+ const { maxOccurrences = 10, caseInsensitive = false } = options;
4563
+ const occurrences = [];
4564
+ const lines = content.split(`
4565
+ `);
4566
+ const searchContent = caseInsensitive ? content.toLowerCase() : content;
4567
+ const searchLiteral = caseInsensitive ? literal.toLowerCase() : literal;
4568
+ let currentIndex = 0;
4569
+ for (let lineNum = 0;lineNum < lines.length; lineNum++) {
4570
+ const line = lines[lineNum];
4571
+ const searchLine = caseInsensitive ? line.toLowerCase() : line;
4572
+ let columnStart = 0;
4573
+ while (true) {
4574
+ const column = searchLine.indexOf(searchLiteral, columnStart);
4575
+ if (column === -1)
4576
+ break;
4577
+ occurrences.push({
4578
+ line: lineNum + 1,
4579
+ column,
4580
+ lineContent: line,
4581
+ contextBefore: lineNum > 0 ? lines[lineNum - 1] : undefined,
4582
+ contextAfter: lineNum < lines.length - 1 ? lines[lineNum + 1] : undefined
4583
+ });
4584
+ if (occurrences.length >= maxOccurrences) {
4585
+ return occurrences;
4586
+ }
4587
+ columnStart = column + 1;
4588
+ }
4589
+ currentIndex += line.length + 1;
4590
+ }
4591
+ return occurrences;
4592
+ }
4593
+ function searchFiles(files, literal, options = {}) {
4594
+ const {
4595
+ maxFiles = 20,
4596
+ maxOccurrencesPerFile = 5,
4597
+ caseInsensitive = false
4598
+ } = options;
4599
+ const matchingFiles = [];
4600
+ let totalMatches = 0;
4601
+ let totalFilesWithMatches = 0;
4602
+ for (const [filepath, content] of files) {
4603
+ const occurrences = findOccurrences(content, literal, {
4604
+ maxOccurrences: maxOccurrencesPerFile,
4605
+ caseInsensitive
4606
+ });
4607
+ if (occurrences.length > 0) {
4608
+ totalFilesWithMatches++;
4609
+ const searchContent = caseInsensitive ? content.toLowerCase() : content;
4610
+ const searchLiteral = caseInsensitive ? literal.toLowerCase() : literal;
4611
+ let matchCount = 0;
4612
+ let index = 0;
4613
+ while ((index = searchContent.indexOf(searchLiteral, index)) !== -1) {
4614
+ matchCount++;
4615
+ index += 1;
4616
+ }
4617
+ totalMatches += matchCount;
4618
+ if (matchingFiles.length < maxFiles) {
4619
+ matchingFiles.push({
4620
+ filepath,
4621
+ occurrences,
4622
+ matchCount
4623
+ });
4624
+ }
4625
+ }
4626
+ }
4627
+ matchingFiles.sort((a, b) => b.matchCount - a.matchCount);
4628
+ return {
4629
+ query: literal,
4630
+ files: matchingFiles,
4631
+ totalMatches,
4632
+ totalFiles: totalFilesWithMatches,
4633
+ truncated: totalFilesWithMatches > maxFiles
4634
+ };
4635
+ }
4636
+ function isSearchableContent(content, filepath) {
4637
+ if (content.length > 1024 * 1024) {
4638
+ return false;
4639
+ }
4640
+ if (content.includes("\x00")) {
4641
+ return false;
4642
+ }
4643
+ const binaryExtensions = [
4644
+ ".png",
4645
+ ".jpg",
4646
+ ".jpeg",
4647
+ ".gif",
4648
+ ".ico",
4649
+ ".webp",
4650
+ ".pdf",
4651
+ ".zip",
4652
+ ".tar",
4653
+ ".gz",
4654
+ ".rar",
4655
+ ".exe",
4656
+ ".dll",
4657
+ ".so",
4658
+ ".dylib",
4659
+ ".woff",
4660
+ ".woff2",
4661
+ ".ttf",
4662
+ ".eot",
4663
+ ".mp3",
4664
+ ".mp4",
4665
+ ".wav",
4666
+ ".avi"
4667
+ ];
4668
+ const ext = filepath.toLowerCase().slice(filepath.lastIndexOf("."));
4669
+ if (binaryExtensions.includes(ext)) {
4670
+ return false;
4671
+ }
4672
+ return true;
4673
+ }
4674
+ var IDENTIFIER_PATTERNS;
4675
+ var init_simpleSearch = __esm(() => {
4676
+ IDENTIFIER_PATTERNS = {
4677
+ screamingSnake: /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/,
4678
+ camelCase: /^[a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]*)+$/,
4679
+ pascalCase: /^[A-Z][a-z]+(?:[A-Z][a-z0-9]*)+$/,
4680
+ snakeCase: /^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/,
4681
+ kebabCase: /^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$/
4682
+ };
4683
+ });
4684
+
4685
+ // src/domain/services/chunkContext.ts
4686
+ import * as path8 from "path";
4687
+ function prepareChunkForEmbedding(options) {
4688
+ const { filepath, content, name, docComment } = options;
4689
+ const pathContext = parsePathContext(filepath);
4690
+ const pathPrefix = formatPathContextForEmbedding(pathContext);
4691
+ const parts = [];
4692
+ if (pathPrefix) {
4693
+ parts.push(pathPrefix);
4694
+ }
4695
+ const filename = path8.basename(filepath);
4696
+ const filenameWithoutExt = filename.replace(/\.[^.]+$/, "");
4697
+ if (filenameWithoutExt && filenameWithoutExt.length > MIN_SEGMENT_LENGTH) {
4698
+ const pathPrefixLower = pathPrefix.toLowerCase();
4699
+ if (!pathPrefixLower.includes(filenameWithoutExt.toLowerCase())) {
4700
+ parts.push(filenameWithoutExt);
4701
+ }
4702
+ }
4703
+ if (name) {
4704
+ parts.push(`${name}:`);
4705
+ }
4706
+ if (docComment) {
4707
+ parts.push(docComment);
4708
+ }
4709
+ parts.push(content);
4710
+ return parts.join(" ");
4711
+ }
4712
+ function extractPathKeywordsForFileSummary(filepath) {
4713
+ const keywords = extractPathKeywords(filepath);
4714
+ const filtered = keywords.filter((k) => k.length >= MIN_SEGMENT_LENGTH && !GENERIC_SEGMENTS.has(k));
4715
+ return [...new Set(filtered)];
4716
+ }
4717
+ function getPathContextForFileSummary(filepath) {
4718
+ const pathContext = parsePathContext(filepath);
4719
+ return {
4720
+ segments: pathContext.segments,
4721
+ layer: pathContext.layer,
4722
+ domain: pathContext.domain,
4723
+ depth: pathContext.depth
4724
+ };
4725
+ }
4726
+ var GENERIC_SEGMENTS, MIN_SEGMENT_LENGTH = 2;
4727
+ var init_chunkContext = __esm(() => {
4728
+ init_keywords();
4729
+ GENERIC_SEGMENTS = new Set([
4730
+ "src",
4731
+ "lib",
4732
+ "app",
4733
+ "index",
4734
+ "dist",
4735
+ "build",
4736
+ "out",
4737
+ "node_modules"
4738
+ ]);
4739
+ });
4740
+
4539
4741
  // src/domain/services/index.ts
4540
4742
  var init_services = __esm(() => {
4541
4743
  init_keywords();
@@ -4547,6 +4749,8 @@ var init_services = __esm(() => {
4547
4749
  init_introspection();
4548
4750
  init_configValidator();
4549
4751
  init_phraseMatch();
4752
+ init_simpleSearch();
4753
+ init_chunkContext();
4550
4754
  });
4551
4755
 
4552
4756
  // src/modules/language/typescript/parseCode.ts
@@ -4722,7 +4926,7 @@ var init_fileIndexStorage = __esm(() => {
4722
4926
 
4723
4927
  // src/infrastructure/storage/symbolicIndex.ts
4724
4928
  import * as fs3 from "fs/promises";
4725
- import * as path8 from "path";
4929
+ import * as path9 from "path";
4726
4930
 
4727
4931
  class SymbolicIndex {
4728
4932
  meta = null;
@@ -4731,7 +4935,7 @@ class SymbolicIndex {
4731
4935
  symbolicPath;
4732
4936
  moduleId;
4733
4937
  constructor(indexDir, moduleId) {
4734
- this.symbolicPath = path8.join(indexDir, "index", moduleId, "symbolic");
4938
+ this.symbolicPath = path9.join(indexDir, "index", moduleId, "symbolic");
4735
4939
  this.moduleId = moduleId;
4736
4940
  }
4737
4941
  async initialize() {
@@ -4818,11 +5022,11 @@ class SymbolicIndex {
4818
5022
  this.meta.bm25Serialized = this.bm25Index.serialize();
4819
5023
  }
4820
5024
  await fs3.mkdir(this.symbolicPath, { recursive: true });
4821
- const metaPath = path8.join(this.symbolicPath, "_meta.json");
5025
+ const metaPath = path9.join(this.symbolicPath, "_meta.json");
4822
5026
  await fs3.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
4823
5027
  for (const [filepath, summary] of this.fileSummaries) {
4824
5028
  const summaryPath = this.getFileSummaryPath(filepath);
4825
- await fs3.mkdir(path8.dirname(summaryPath), { recursive: true });
5029
+ await fs3.mkdir(path9.dirname(summaryPath), { recursive: true });
4826
5030
  await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
4827
5031
  }
4828
5032
  }
@@ -4835,19 +5039,19 @@ class SymbolicIndex {
4835
5039
  this.meta.bm25Serialized = this.bm25Index.serialize();
4836
5040
  }
4837
5041
  await fs3.mkdir(this.symbolicPath, { recursive: true });
4838
- const metaPath = path8.join(this.symbolicPath, "_meta.json");
5042
+ const metaPath = path9.join(this.symbolicPath, "_meta.json");
4839
5043
  await fs3.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
4840
5044
  for (const filepath of filepaths) {
4841
5045
  const summary = this.fileSummaries.get(filepath);
4842
5046
  if (summary) {
4843
5047
  const summaryPath = this.getFileSummaryPath(filepath);
4844
- await fs3.mkdir(path8.dirname(summaryPath), { recursive: true });
5048
+ await fs3.mkdir(path9.dirname(summaryPath), { recursive: true });
4845
5049
  await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
4846
5050
  }
4847
5051
  }
4848
5052
  }
4849
5053
  async load() {
4850
- const metaPath = path8.join(this.symbolicPath, "_meta.json");
5054
+ const metaPath = path9.join(this.symbolicPath, "_meta.json");
4851
5055
  const metaContent = await fs3.readFile(metaPath, "utf-8");
4852
5056
  this.meta = JSON.parse(metaContent);
4853
5057
  this.fileSummaries.clear();
@@ -4862,7 +5066,7 @@ class SymbolicIndex {
4862
5066
  try {
4863
5067
  const entries = await fs3.readdir(dir, { withFileTypes: true });
4864
5068
  for (const entry of entries) {
4865
- const fullPath = path8.join(dir, entry.name);
5069
+ const fullPath = path9.join(dir, entry.name);
4866
5070
  if (entry.isDirectory()) {
4867
5071
  await this.loadFileSummariesRecursive(fullPath);
4868
5072
  } else if (entry.name.endsWith(".json") && entry.name !== "_meta.json") {
@@ -4879,7 +5083,7 @@ class SymbolicIndex {
4879
5083
  }
4880
5084
  getFileSummaryPath(filepath) {
4881
5085
  const jsonPath = filepath.replace(/\.[^.]+$/, ".json");
4882
- return path8.join(this.symbolicPath, jsonPath);
5086
+ return path9.join(this.symbolicPath, jsonPath);
4883
5087
  }
4884
5088
  async deleteFileSummary(filepath) {
4885
5089
  try {
@@ -4889,7 +5093,7 @@ class SymbolicIndex {
4889
5093
  }
4890
5094
  async exists() {
4891
5095
  try {
4892
- const metaPath = path8.join(this.symbolicPath, "_meta.json");
5096
+ const metaPath = path9.join(this.symbolicPath, "_meta.json");
4893
5097
  await fs3.access(metaPath);
4894
5098
  return true;
4895
5099
  } catch {
@@ -4923,7 +5127,7 @@ __export(exports_literalIndex, {
4923
5127
  LiteralIndex: () => LiteralIndex
4924
5128
  });
4925
5129
  import * as fs4 from "fs/promises";
4926
- import * as path9 from "path";
5130
+ import * as path10 from "path";
4927
5131
 
4928
5132
  class LiteralIndex {
4929
5133
  indexPath;
@@ -4932,7 +5136,7 @@ class LiteralIndex {
4932
5136
  vocabularyIndex = new Map;
4933
5137
  static VERSION = "1.1.0";
4934
5138
  constructor(indexDir, moduleId) {
4935
- this.indexPath = path9.join(indexDir, "index", moduleId, "literals");
5139
+ this.indexPath = path10.join(indexDir, "index", moduleId, "literals");
4936
5140
  this.moduleId = moduleId;
4937
5141
  }
4938
5142
  async initialize() {
@@ -5098,11 +5302,11 @@ class LiteralIndex {
5098
5302
  version: LiteralIndex.VERSION,
5099
5303
  entries: Object.fromEntries(this.entries)
5100
5304
  };
5101
- const indexFile = path9.join(this.indexPath, "_index.json");
5305
+ const indexFile = path10.join(this.indexPath, "_index.json");
5102
5306
  await fs4.writeFile(indexFile, JSON.stringify(data, null, 2));
5103
5307
  }
5104
5308
  async load() {
5105
- const indexFile = path9.join(this.indexPath, "_index.json");
5309
+ const indexFile = path10.join(this.indexPath, "_index.json");
5106
5310
  const content = await fs4.readFile(indexFile, "utf-8");
5107
5311
  const data = JSON.parse(content);
5108
5312
  if (data.version !== LiteralIndex.VERSION) {
@@ -5112,7 +5316,7 @@ class LiteralIndex {
5112
5316
  }
5113
5317
  async exists() {
5114
5318
  try {
5115
- const indexFile = path9.join(this.indexPath, "_index.json");
5319
+ const indexFile = path10.join(this.indexPath, "_index.json");
5116
5320
  await fs4.access(indexFile);
5117
5321
  return true;
5118
5322
  } catch {
@@ -5156,7 +5360,7 @@ function shouldReplaceMatchType(existing, incoming) {
5156
5360
  return priority[incoming] > priority[existing];
5157
5361
  }
5158
5362
  function getLiteralIndexPath(rootDir, moduleId, indexDir = ".raggrep") {
5159
- return path9.join(rootDir, indexDir, "index", moduleId, "literals");
5363
+ return path10.join(rootDir, indexDir, "index", moduleId, "literals");
5160
5364
  }
5161
5365
  var init_literalIndex = () => {};
5162
5366
 
@@ -5177,9 +5381,9 @@ __export(exports_typescript, {
5177
5381
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
5178
5382
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
5179
5383
  });
5180
- import * as path10 from "path";
5384
+ import * as path11 from "path";
5181
5385
  function isTypeScriptFile(filepath) {
5182
- const ext = path10.extname(filepath).toLowerCase();
5386
+ const ext = path11.extname(filepath).toLowerCase();
5183
5387
  return TYPESCRIPT_EXTENSIONS.includes(ext);
5184
5388
  }
5185
5389
  function calculateChunkTypeBoost(chunk) {
@@ -5241,8 +5445,6 @@ class TypeScriptModule {
5241
5445
  if (parsedChunks.length === 0) {
5242
5446
  return null;
5243
5447
  }
5244
- const pathContext = parsePathContext(filepath);
5245
- const pathPrefix = formatPathContextForEmbedding(pathContext);
5246
5448
  const includeFullFileChunk = parsedChunks.length > 1;
5247
5449
  const allParsedChunks = [...parsedChunks];
5248
5450
  if (includeFullFileChunk) {
@@ -5253,13 +5455,17 @@ class TypeScriptModule {
5253
5455
  startLine: 1,
5254
5456
  endLine: lines.length,
5255
5457
  type: "file",
5256
- name: path10.basename(filepath),
5458
+ name: path11.basename(filepath),
5257
5459
  isExported: false
5258
5460
  });
5259
5461
  }
5260
5462
  const chunkContents = allParsedChunks.map((c) => {
5261
- const namePrefix = c.name ? `${c.name}: ` : "";
5262
- return `${pathPrefix} ${namePrefix}${c.content}`;
5463
+ return prepareChunkForEmbedding({
5464
+ filepath,
5465
+ content: c.content,
5466
+ name: c.name,
5467
+ docComment: c.jsDoc
5468
+ });
5263
5469
  });
5264
5470
  const embeddings = await getEmbeddings(chunkContents);
5265
5471
  const chunks = allParsedChunks.map((pc) => ({
@@ -5283,25 +5489,21 @@ class TypeScriptModule {
5283
5489
  ...new Set(parsedChunks.map((pc) => pc.type))
5284
5490
  ];
5285
5491
  const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
5286
- const allKeywords = new Set;
5492
+ const contentKeywords = new Set;
5287
5493
  for (const pc of parsedChunks) {
5288
5494
  const keywords = extractKeywords(pc.content, pc.name);
5289
- keywords.forEach((k) => allKeywords.add(k));
5495
+ keywords.forEach((k) => contentKeywords.add(k));
5290
5496
  }
5291
- pathContext.keywords.forEach((k) => allKeywords.add(k));
5497
+ const pathKeywords = extractPathKeywordsForFileSummary(filepath);
5498
+ const allKeywords = [...contentKeywords, ...pathKeywords];
5292
5499
  const fileSummary = {
5293
5500
  filepath,
5294
5501
  chunkCount: chunks.length,
5295
5502
  chunkTypes,
5296
- keywords: Array.from(allKeywords),
5503
+ keywords: [...new Set(allKeywords)],
5297
5504
  exports,
5298
5505
  lastModified: stats.lastModified,
5299
- pathContext: {
5300
- segments: pathContext.segments,
5301
- layer: pathContext.layer,
5302
- domain: pathContext.domain,
5303
- depth: pathContext.depth
5304
- }
5506
+ pathContext: getPathContextForFileSummary(filepath)
5305
5507
  };
5306
5508
  this.pendingSummaries.set(filepath, fileSummary);
5307
5509
  for (const chunk of chunks) {
@@ -5580,16 +5782,16 @@ class TypeScriptModule {
5580
5782
  while ((match = importRegex.exec(content)) !== null) {
5581
5783
  const importPath = match[1];
5582
5784
  if (importPath.startsWith(".")) {
5583
- const dir = path10.dirname(filepath);
5584
- const resolved = path10.normalize(path10.join(dir, importPath));
5785
+ const dir = path11.dirname(filepath);
5786
+ const resolved = path11.normalize(path11.join(dir, importPath));
5585
5787
  references.push(resolved);
5586
5788
  }
5587
5789
  }
5588
5790
  while ((match = requireRegex.exec(content)) !== null) {
5589
5791
  const importPath = match[1];
5590
5792
  if (importPath.startsWith(".")) {
5591
- const dir = path10.dirname(filepath);
5592
- const resolved = path10.normalize(path10.join(dir, importPath));
5793
+ const dir = path11.dirname(filepath);
5794
+ const resolved = path11.normalize(path11.join(dir, importPath));
5593
5795
  references.push(resolved);
5594
5796
  }
5595
5797
  }
@@ -5617,7 +5819,7 @@ var init_typescript = __esm(() => {
5617
5819
  });
5618
5820
 
5619
5821
  // src/infrastructure/parsing/typescriptParser.ts
5620
- import * as path11 from "path";
5822
+ import * as path12 from "path";
5621
5823
 
5622
5824
  class TypeScriptParser {
5623
5825
  supportedLanguages = ["typescript", "javascript"];
@@ -5633,12 +5835,12 @@ class TypeScriptParser {
5633
5835
  startLine: 1,
5634
5836
  endLine: lines.length,
5635
5837
  type: "file",
5636
- name: path11.basename(filepath),
5838
+ name: path12.basename(filepath),
5637
5839
  isExported: false
5638
5840
  };
5639
5841
  chunks.unshift(fullFileChunk);
5640
5842
  }
5641
- const ext = path11.extname(filepath).toLowerCase();
5843
+ const ext = path12.extname(filepath).toLowerCase();
5642
5844
  const language = ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs" ? "javascript" : "typescript";
5643
5845
  return {
5644
5846
  chunks,
@@ -5655,7 +5857,7 @@ class TypeScriptParser {
5655
5857
  }
5656
5858
  }
5657
5859
  canParse(filepath) {
5658
- const ext = path11.extname(filepath).toLowerCase();
5860
+ const ext = path12.extname(filepath).toLowerCase();
5659
5861
  return TYPESCRIPT_EXTENSIONS2.includes(ext);
5660
5862
  }
5661
5863
  convertChunk(tc) {
@@ -5670,7 +5872,7 @@ class TypeScriptParser {
5670
5872
  };
5671
5873
  }
5672
5874
  detectLanguage(filepath) {
5673
- const ext = path11.extname(filepath).toLowerCase();
5875
+ const ext = path12.extname(filepath).toLowerCase();
5674
5876
  if ([".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
5675
5877
  return "javascript";
5676
5878
  }
@@ -5875,7 +6077,7 @@ var init_grammarManager = __esm(() => {
5875
6077
  });
5876
6078
 
5877
6079
  // src/infrastructure/parsing/treeSitterParser.ts
5878
- import * as path12 from "path";
6080
+ import * as path13 from "path";
5879
6081
  import * as fs5 from "fs";
5880
6082
 
5881
6083
  class TreeSitterParser {
@@ -5898,7 +6100,7 @@ class TreeSitterParser {
5898
6100
  chunks: [],
5899
6101
  language: "typescript",
5900
6102
  success: false,
5901
- error: `Unsupported file type: ${path12.extname(filepath)}`
6103
+ error: `Unsupported file type: ${path13.extname(filepath)}`
5902
6104
  };
5903
6105
  }
5904
6106
  try {
@@ -5918,11 +6120,11 @@ class TreeSitterParser {
5918
6120
  }
5919
6121
  }
5920
6122
  canParse(filepath) {
5921
- const ext = path12.extname(filepath).toLowerCase();
6123
+ const ext = path13.extname(filepath).toLowerCase();
5922
6124
  return ext in EXTENSION_TO_LANGUAGE2;
5923
6125
  }
5924
6126
  detectLanguage(filepath) {
5925
- const ext = path12.extname(filepath).toLowerCase();
6127
+ const ext = path13.extname(filepath).toLowerCase();
5926
6128
  return EXTENSION_TO_LANGUAGE2[ext] || null;
5927
6129
  }
5928
6130
  async ensureInitialized() {
@@ -5956,17 +6158,17 @@ class TreeSitterParser {
5956
6158
  async resolveWasmPath() {
5957
6159
  try {
5958
6160
  const webTreeSitterPath = __require.resolve("web-tree-sitter");
5959
- const wasmPath = path12.join(path12.dirname(webTreeSitterPath), "web-tree-sitter.wasm");
6161
+ const wasmPath = path13.join(path13.dirname(webTreeSitterPath), "web-tree-sitter.wasm");
5960
6162
  if (fs5.existsSync(wasmPath)) {
5961
6163
  return wasmPath;
5962
6164
  }
5963
6165
  } catch {}
5964
6166
  try {
5965
6167
  const possiblePaths = [
5966
- path12.join(__dirname, "../../../node_modules/web-tree-sitter/web-tree-sitter.wasm"),
5967
- path12.join(__dirname, "../../node_modules/web-tree-sitter/web-tree-sitter.wasm"),
5968
- path12.join(__dirname, "../../../../node_modules/web-tree-sitter/web-tree-sitter.wasm"),
5969
- path12.join(__dirname, "web-tree-sitter.wasm")
6168
+ path13.join(__dirname, "../../../node_modules/web-tree-sitter/web-tree-sitter.wasm"),
6169
+ path13.join(__dirname, "../../node_modules/web-tree-sitter/web-tree-sitter.wasm"),
6170
+ path13.join(__dirname, "../../../../node_modules/web-tree-sitter/web-tree-sitter.wasm"),
6171
+ path13.join(__dirname, "web-tree-sitter.wasm")
5970
6172
  ];
5971
6173
  for (const wasmPath of possiblePaths) {
5972
6174
  if (fs5.existsSync(wasmPath)) {
@@ -6010,7 +6212,7 @@ class TreeSitterParser {
6010
6212
  startLine: 1,
6011
6213
  endLine: lines.length,
6012
6214
  type: "file",
6013
- name: path12.basename(filepath),
6215
+ name: path13.basename(filepath),
6014
6216
  isExported: false
6015
6217
  });
6016
6218
  }
@@ -6357,7 +6559,7 @@ class TreeSitterParser {
6357
6559
  startLine: 1,
6358
6560
  endLine: lines.length,
6359
6561
  type: "file",
6360
- name: path12.basename(filepath)
6562
+ name: path13.basename(filepath)
6361
6563
  });
6362
6564
  return {
6363
6565
  chunks,
@@ -6387,7 +6589,7 @@ var init_treeSitterParser = __esm(() => {
6387
6589
  });
6388
6590
 
6389
6591
  // src/infrastructure/parsing/parserFactory.ts
6390
- import * as path13 from "path";
6592
+ import * as path14 from "path";
6391
6593
  function getTypeScriptParser() {
6392
6594
  if (!typescriptParserInstance) {
6393
6595
  typescriptParserInstance = new TypeScriptParser;
@@ -6401,7 +6603,7 @@ function getTreeSitterParser() {
6401
6603
  return treeSitterParserInstance;
6402
6604
  }
6403
6605
  function createParserForFile(filepath) {
6404
- const ext = path13.extname(filepath).toLowerCase();
6606
+ const ext = path14.extname(filepath).toLowerCase();
6405
6607
  const parserType = EXTENSION_PARSER_MAP[ext];
6406
6608
  if (!parserType) {
6407
6609
  return null;
@@ -6450,9 +6652,9 @@ __export(exports_python, {
6450
6652
  DEFAULT_TOP_K: () => DEFAULT_TOP_K3,
6451
6653
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE3
6452
6654
  });
6453
- import * as path14 from "path";
6655
+ import * as path15 from "path";
6454
6656
  function isPythonFile(filepath) {
6455
- const ext = path14.extname(filepath).toLowerCase();
6657
+ const ext = path15.extname(filepath).toLowerCase();
6456
6658
  return PYTHON_EXTENSIONS.includes(ext);
6457
6659
  }
6458
6660
  function generateChunkId3(filepath, startLine, endLine) {
@@ -6536,7 +6738,7 @@ class PythonModule {
6536
6738
  startLine: 1,
6537
6739
  endLine: lines.length,
6538
6740
  type: "file",
6539
- name: path14.basename(filepath)
6741
+ name: path15.basename(filepath)
6540
6742
  });
6541
6743
  const funcRegex = /^(\s*)(async\s+)?def\s+(\w+)\s*\([^)]*\)\s*:/gm;
6542
6744
  let match;
@@ -6614,12 +6816,13 @@ class PythonModule {
6614
6816
  return chunks;
6615
6817
  }
6616
6818
  async createFileIndex(filepath, content, parsedChunks, ctx) {
6617
- const pathContext = parsePathContext(filepath);
6618
- const pathPrefix = formatPathContextForEmbedding(pathContext);
6619
6819
  const chunkContents = parsedChunks.map((c) => {
6620
- const namePrefix = c.name ? `${c.name}: ` : "";
6621
- const docPrefix = c.docComment ? `${c.docComment} ` : "";
6622
- return `${pathPrefix} ${namePrefix}${docPrefix}${c.content}`;
6820
+ return prepareChunkForEmbedding({
6821
+ filepath,
6822
+ content: c.content,
6823
+ name: c.name,
6824
+ docComment: c.docComment
6825
+ });
6623
6826
  });
6624
6827
  const embeddings = await getEmbeddings(chunkContents);
6625
6828
  const chunks = parsedChunks.map((pc) => ({
@@ -6642,25 +6845,21 @@ class PythonModule {
6642
6845
  ...new Set(parsedChunks.map((pc) => pc.type))
6643
6846
  ];
6644
6847
  const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
6645
- const allKeywords = new Set;
6848
+ const contentKeywords = new Set;
6646
6849
  for (const pc of parsedChunks) {
6647
6850
  const keywords = extractKeywords(pc.content, pc.name);
6648
- keywords.forEach((k) => allKeywords.add(k));
6851
+ keywords.forEach((k) => contentKeywords.add(k));
6649
6852
  }
6650
- pathContext.keywords.forEach((k) => allKeywords.add(k));
6853
+ const pathKeywords = extractPathKeywordsForFileSummary(filepath);
6854
+ const allKeywords = [...contentKeywords, ...pathKeywords];
6651
6855
  const fileSummary = {
6652
6856
  filepath,
6653
6857
  chunkCount: chunks.length,
6654
6858
  chunkTypes,
6655
- keywords: Array.from(allKeywords),
6859
+ keywords: [...new Set(allKeywords)],
6656
6860
  exports,
6657
6861
  lastModified: stats.lastModified,
6658
- pathContext: {
6659
- segments: pathContext.segments,
6660
- layer: pathContext.layer,
6661
- domain: pathContext.domain,
6662
- depth: pathContext.depth
6663
- }
6862
+ pathContext: getPathContextForFileSummary(filepath)
6664
6863
  };
6665
6864
  this.pendingSummaries.set(filepath, fileSummary);
6666
6865
  for (const chunk of chunks) {
@@ -6913,9 +7112,9 @@ __export(exports_go, {
6913
7112
  DEFAULT_TOP_K: () => DEFAULT_TOP_K4,
6914
7113
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE4
6915
7114
  });
6916
- import * as path15 from "path";
7115
+ import * as path16 from "path";
6917
7116
  function isGoFile(filepath) {
6918
- const ext = path15.extname(filepath).toLowerCase();
7117
+ const ext = path16.extname(filepath).toLowerCase();
6919
7118
  return GO_EXTENSIONS.includes(ext);
6920
7119
  }
6921
7120
  function generateChunkId4(filepath, startLine, endLine) {
@@ -7000,7 +7199,7 @@ class GoModule {
7000
7199
  startLine: 1,
7001
7200
  endLine: lines.length,
7002
7201
  type: "file",
7003
- name: path15.basename(filepath)
7202
+ name: path16.basename(filepath)
7004
7203
  });
7005
7204
  const funcRegex = /^func\s+(?:\(\s*\w+\s+\*?\w+\s*\)\s+)?(\w+)\s*\(/gm;
7006
7205
  let match;
@@ -7149,12 +7348,13 @@ class GoModule {
7149
7348
  return chunks;
7150
7349
  }
7151
7350
  async createFileIndex(filepath, content, parsedChunks, ctx) {
7152
- const pathContext = parsePathContext(filepath);
7153
- const pathPrefix = formatPathContextForEmbedding(pathContext);
7154
7351
  const chunkContents = parsedChunks.map((c) => {
7155
- const namePrefix = c.name ? `${c.name}: ` : "";
7156
- const docPrefix = c.docComment ? `${c.docComment} ` : "";
7157
- return `${pathPrefix} ${namePrefix}${docPrefix}${c.content}`;
7352
+ return prepareChunkForEmbedding({
7353
+ filepath,
7354
+ content: c.content,
7355
+ name: c.name,
7356
+ docComment: c.docComment
7357
+ });
7158
7358
  });
7159
7359
  const embeddings = await getEmbeddings(chunkContents);
7160
7360
  const chunks = parsedChunks.map((pc) => ({
@@ -7177,25 +7377,21 @@ class GoModule {
7177
7377
  ...new Set(parsedChunks.map((pc) => pc.type))
7178
7378
  ];
7179
7379
  const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
7180
- const allKeywords = new Set;
7380
+ const contentKeywords = new Set;
7181
7381
  for (const pc of parsedChunks) {
7182
7382
  const keywords = extractKeywords(pc.content, pc.name);
7183
- keywords.forEach((k) => allKeywords.add(k));
7383
+ keywords.forEach((k) => contentKeywords.add(k));
7184
7384
  }
7185
- pathContext.keywords.forEach((k) => allKeywords.add(k));
7385
+ const pathKeywords = extractPathKeywordsForFileSummary(filepath);
7386
+ const allKeywords = [...contentKeywords, ...pathKeywords];
7186
7387
  const fileSummary = {
7187
7388
  filepath,
7188
7389
  chunkCount: chunks.length,
7189
7390
  chunkTypes,
7190
- keywords: Array.from(allKeywords),
7391
+ keywords: [...new Set(allKeywords)],
7191
7392
  exports,
7192
7393
  lastModified: stats.lastModified,
7193
- pathContext: {
7194
- segments: pathContext.segments,
7195
- layer: pathContext.layer,
7196
- domain: pathContext.domain,
7197
- depth: pathContext.depth
7198
- }
7394
+ pathContext: getPathContextForFileSummary(filepath)
7199
7395
  };
7200
7396
  this.pendingSummaries.set(filepath, fileSummary);
7201
7397
  for (const chunk of chunks) {
@@ -7397,9 +7593,9 @@ __export(exports_rust, {
7397
7593
  DEFAULT_TOP_K: () => DEFAULT_TOP_K5,
7398
7594
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE5
7399
7595
  });
7400
- import * as path16 from "path";
7596
+ import * as path17 from "path";
7401
7597
  function isRustFile(filepath) {
7402
- const ext = path16.extname(filepath).toLowerCase();
7598
+ const ext = path17.extname(filepath).toLowerCase();
7403
7599
  return RUST_EXTENSIONS.includes(ext);
7404
7600
  }
7405
7601
  function generateChunkId5(filepath, startLine, endLine) {
@@ -7486,7 +7682,7 @@ class RustModule {
7486
7682
  startLine: 1,
7487
7683
  endLine: lines.length,
7488
7684
  type: "file",
7489
- name: path16.basename(filepath)
7685
+ name: path17.basename(filepath)
7490
7686
  });
7491
7687
  const funcRegex = /^(pub(?:\s*\([^)]*\))?\s+)?(?:async\s+)?fn\s+(\w+)/gm;
7492
7688
  let match;
@@ -7712,12 +7908,13 @@ class RustModule {
7712
7908
  return chunks;
7713
7909
  }
7714
7910
  async createFileIndex(filepath, content, parsedChunks, ctx) {
7715
- const pathContext = parsePathContext(filepath);
7716
- const pathPrefix = formatPathContextForEmbedding(pathContext);
7717
7911
  const chunkContents = parsedChunks.map((c) => {
7718
- const namePrefix = c.name ? `${c.name}: ` : "";
7719
- const docPrefix = c.docComment ? `${c.docComment} ` : "";
7720
- return `${pathPrefix} ${namePrefix}${docPrefix}${c.content}`;
7912
+ return prepareChunkForEmbedding({
7913
+ filepath,
7914
+ content: c.content,
7915
+ name: c.name,
7916
+ docComment: c.docComment
7917
+ });
7721
7918
  });
7722
7919
  const embeddings = await getEmbeddings(chunkContents);
7723
7920
  const chunks = parsedChunks.map((pc) => ({
@@ -7740,25 +7937,21 @@ class RustModule {
7740
7937
  ...new Set(parsedChunks.map((pc) => pc.type))
7741
7938
  ];
7742
7939
  const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
7743
- const allKeywords = new Set;
7940
+ const contentKeywords = new Set;
7744
7941
  for (const pc of parsedChunks) {
7745
7942
  const keywords = extractKeywords(pc.content, pc.name);
7746
- keywords.forEach((k) => allKeywords.add(k));
7943
+ keywords.forEach((k) => contentKeywords.add(k));
7747
7944
  }
7748
- pathContext.keywords.forEach((k) => allKeywords.add(k));
7945
+ const pathKeywords = extractPathKeywordsForFileSummary(filepath);
7946
+ const allKeywords = [...contentKeywords, ...pathKeywords];
7749
7947
  const fileSummary = {
7750
7948
  filepath,
7751
7949
  chunkCount: chunks.length,
7752
7950
  chunkTypes,
7753
- keywords: Array.from(allKeywords),
7951
+ keywords: [...new Set(allKeywords)],
7754
7952
  exports,
7755
7953
  lastModified: stats.lastModified,
7756
- pathContext: {
7757
- segments: pathContext.segments,
7758
- layer: pathContext.layer,
7759
- domain: pathContext.domain,
7760
- depth: pathContext.depth
7761
- }
7954
+ pathContext: getPathContextForFileSummary(filepath)
7762
7955
  };
7763
7956
  this.pendingSummaries.set(filepath, fileSummary);
7764
7957
  for (const chunk of chunks) {
@@ -7960,9 +8153,9 @@ __export(exports_json, {
7960
8153
  DEFAULT_TOP_K: () => DEFAULT_TOP_K6,
7961
8154
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE6
7962
8155
  });
7963
- import * as path17 from "path";
8156
+ import * as path18 from "path";
7964
8157
  function isJsonFile(filepath) {
7965
- const ext = path17.extname(filepath).toLowerCase();
8158
+ const ext = path18.extname(filepath).toLowerCase();
7966
8159
  return JSON_EXTENSIONS.includes(ext);
7967
8160
  }
7968
8161
 
@@ -7996,7 +8189,7 @@ class JsonModule {
7996
8189
  } catch {
7997
8190
  return null;
7998
8191
  }
7999
- const fileBasename = path17.basename(filepath, path17.extname(filepath));
8192
+ const fileBasename = path18.basename(filepath, path18.extname(filepath));
8000
8193
  const jsonPathLiterals = extractJsonPaths(parsed, fileBasename);
8001
8194
  const lines = content.split(`
8002
8195
  `);
@@ -8203,7 +8396,7 @@ __export(exports_markdown, {
8203
8396
  DEFAULT_TOP_K: () => DEFAULT_TOP_K7,
8204
8397
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE7
8205
8398
  });
8206
- import * as path18 from "path";
8399
+ import * as path19 from "path";
8207
8400
  function calculateHeadingLevelBoost(chunk) {
8208
8401
  const metadata = chunk.metadata;
8209
8402
  const level = metadata?.headingLevel ?? 0;
@@ -8223,7 +8416,7 @@ function calculateHeadingLevelBoost(chunk) {
8223
8416
  }
8224
8417
  }
8225
8418
  function isMarkdownFile(filepath) {
8226
- const ext = path18.extname(filepath).toLowerCase();
8419
+ const ext = path19.extname(filepath).toLowerCase();
8227
8420
  return MARKDOWN_EXTENSIONS.includes(ext);
8228
8421
  }
8229
8422
  function parseMarkdownHierarchical(content, maxDepth = 4) {
@@ -8355,9 +8548,11 @@ class MarkdownModule {
8355
8548
  return null;
8356
8549
  }
8357
8550
  const chunkContents = hierarchicalChunks.map((s) => {
8358
- const filename = path18.basename(filepath);
8359
- const headingContext = s.heading ? `${s.heading}: ` : "";
8360
- return `${filename} ${headingContext}${s.content}`;
8551
+ return prepareChunkForEmbedding({
8552
+ filepath,
8553
+ content: s.content,
8554
+ name: s.heading || undefined
8555
+ });
8361
8556
  });
8362
8557
  const embeddings = await getEmbeddings(chunkContents);
8363
8558
  const chunks = hierarchicalChunks.map((section) => ({
@@ -8381,14 +8576,17 @@ class MarkdownModule {
8381
8576
  embeddingModel: currentConfig.model,
8382
8577
  headings: uniqueHeadings
8383
8578
  };
8384
- const keywords = extractMarkdownKeywords(content);
8579
+ const contentKeywords = extractMarkdownKeywords(content);
8580
+ const pathKeywords = extractPathKeywordsForFileSummary(filepath);
8581
+ const allKeywords = [...new Set([...contentKeywords, ...pathKeywords])];
8385
8582
  const fileSummary = {
8386
8583
  filepath,
8387
8584
  chunkCount: chunks.length,
8388
8585
  chunkTypes: ["block"],
8389
- keywords,
8586
+ keywords: allKeywords,
8390
8587
  exports: uniqueHeadings,
8391
- lastModified: stats.lastModified
8588
+ lastModified: stats.lastModified,
8589
+ pathContext: getPathContextForFileSummary(filepath)
8392
8590
  };
8393
8591
  this.pendingSummaries.set(filepath, fileSummary);
8394
8592
  return {
@@ -8560,13 +8758,13 @@ var init_registry = __esm(() => {
8560
8758
  });
8561
8759
 
8562
8760
  // src/infrastructure/introspection/projectDetector.ts
8563
- import * as path19 from "path";
8761
+ import * as path20 from "path";
8564
8762
  import * as fs6 from "fs/promises";
8565
8763
  async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
8566
8764
  if (depth > MAX_SCAN_DEPTH)
8567
8765
  return [];
8568
8766
  const results = [];
8569
- const fullDir = currentDir ? path19.join(rootDir, currentDir) : rootDir;
8767
+ const fullDir = currentDir ? path20.join(rootDir, currentDir) : rootDir;
8570
8768
  try {
8571
8769
  const entries = await fs6.readdir(fullDir, { withFileTypes: true });
8572
8770
  const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
@@ -8589,10 +8787,10 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
8589
8787
  }
8590
8788
  async function parsePackageJson(rootDir, relativePath) {
8591
8789
  try {
8592
- const packageJsonPath = path19.join(rootDir, relativePath, "package.json");
8790
+ const packageJsonPath = path20.join(rootDir, relativePath, "package.json");
8593
8791
  const content = await fs6.readFile(packageJsonPath, "utf-8");
8594
8792
  const pkg = JSON.parse(content);
8595
- const name = pkg.name || path19.basename(relativePath);
8793
+ const name = pkg.name || path20.basename(relativePath);
8596
8794
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
8597
8795
  let type = "unknown";
8598
8796
  if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
@@ -8637,7 +8835,7 @@ async function detectProjectStructure(rootDir) {
8637
8835
  for (const pattern of monorepoPatterns) {
8638
8836
  if (!dirNames.includes(pattern))
8639
8837
  continue;
8640
- const patternDir = path19.join(rootDir, pattern);
8838
+ const patternDir = path20.join(rootDir, pattern);
8641
8839
  try {
8642
8840
  const subDirs = await fs6.readdir(patternDir, { withFileTypes: true });
8643
8841
  for (const subDir of subDirs) {
@@ -8668,7 +8866,7 @@ async function detectProjectStructure(rootDir) {
8668
8866
  }
8669
8867
  let rootType = "unknown";
8670
8868
  try {
8671
- const rootPkgPath = path19.join(rootDir, "package.json");
8869
+ const rootPkgPath = path20.join(rootDir, "package.json");
8672
8870
  const rootPkg = JSON.parse(await fs6.readFile(rootPkgPath, "utf-8"));
8673
8871
  if (rootPkg.workspaces)
8674
8872
  isMonorepo = true;
@@ -8708,7 +8906,7 @@ var init_projectDetector = __esm(() => {
8708
8906
  });
8709
8907
 
8710
8908
  // src/infrastructure/introspection/IntrospectionIndex.ts
8711
- import * as path20 from "path";
8909
+ import * as path21 from "path";
8712
8910
  import * as fs7 from "fs/promises";
8713
8911
  import * as fsSync from "fs";
8714
8912
 
@@ -8723,7 +8921,7 @@ class IntrospectionIndex {
8723
8921
  async initialize() {
8724
8922
  this.structure = await detectProjectStructure(this.rootDir);
8725
8923
  try {
8726
- const configPath = path20.join(this.rootDir, ".raggrep", "config.json");
8924
+ const configPath = path21.join(this.rootDir, ".raggrep", "config.json");
8727
8925
  const configContent = await fs7.readFile(configPath, "utf-8");
8728
8926
  const config = JSON.parse(configContent);
8729
8927
  this.config = config.introspection || {};
@@ -8738,7 +8936,7 @@ class IntrospectionIndex {
8738
8936
  }
8739
8937
  const fileExists = enableReadmeContext ? (relativePath) => {
8740
8938
  try {
8741
- const absolutePath = path20.join(this.rootDir, relativePath);
8939
+ const absolutePath = path21.join(this.rootDir, relativePath);
8742
8940
  return fsSync.existsSync(absolutePath);
8743
8941
  } catch {
8744
8942
  return false;
@@ -8774,28 +8972,28 @@ class IntrospectionIndex {
8774
8972
  }
8775
8973
  }
8776
8974
  async save(config) {
8777
- const introDir = path20.join(getRaggrepDir(this.rootDir, config), "introspection");
8975
+ const introDir = path21.join(getRaggrepDir(this.rootDir, config), "introspection");
8778
8976
  await fs7.mkdir(introDir, { recursive: true });
8779
- const projectPath = path20.join(introDir, "_project.json");
8977
+ const projectPath = path21.join(introDir, "_project.json");
8780
8978
  await fs7.writeFile(projectPath, JSON.stringify({
8781
8979
  version: "1.0.0",
8782
8980
  lastUpdated: new Date().toISOString(),
8783
8981
  structure: this.structure
8784
8982
  }, null, 2));
8785
8983
  for (const [filepath, intro] of this.files) {
8786
- const introFilePath = path20.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
8787
- await fs7.mkdir(path20.dirname(introFilePath), { recursive: true });
8984
+ const introFilePath = path21.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
8985
+ await fs7.mkdir(path21.dirname(introFilePath), { recursive: true });
8788
8986
  await fs7.writeFile(introFilePath, JSON.stringify(intro, null, 2));
8789
8987
  }
8790
8988
  }
8791
8989
  async load(config) {
8792
- const introDir = path20.join(getRaggrepDir(this.rootDir, config), "introspection");
8990
+ const introDir = path21.join(getRaggrepDir(this.rootDir, config), "introspection");
8793
8991
  try {
8794
- const projectPath = path20.join(introDir, "_project.json");
8992
+ const projectPath = path21.join(introDir, "_project.json");
8795
8993
  const projectContent = await fs7.readFile(projectPath, "utf-8");
8796
8994
  const projectData = JSON.parse(projectContent);
8797
8995
  this.structure = projectData.structure;
8798
- await this.loadFilesRecursive(path20.join(introDir, "files"), "");
8996
+ await this.loadFilesRecursive(path21.join(introDir, "files"), "");
8799
8997
  } catch {
8800
8998
  this.structure = null;
8801
8999
  this.files.clear();
@@ -8805,7 +9003,7 @@ class IntrospectionIndex {
8805
9003
  try {
8806
9004
  const entries = await fs7.readdir(basePath, { withFileTypes: true });
8807
9005
  for (const entry of entries) {
8808
- const entryPath = path20.join(basePath, entry.name);
9006
+ const entryPath = path21.join(basePath, entry.name);
8809
9007
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
8810
9008
  if (entry.isDirectory()) {
8811
9009
  await this.loadFilesRecursive(entryPath, relativePath);
@@ -8836,7 +9034,7 @@ var init_introspection2 = __esm(() => {
8836
9034
 
8837
9035
  // src/app/indexer/watcher.ts
8838
9036
  import { watch } from "chokidar";
8839
- import * as path21 from "path";
9037
+ import * as path22 from "path";
8840
9038
  async function watchDirectory(rootDir, options = {}) {
8841
9039
  const {
8842
9040
  debounceMs = DEFAULT_DEBOUNCE_MS,
@@ -8847,7 +9045,7 @@ async function watchDirectory(rootDir, options = {}) {
8847
9045
  onFileChange,
8848
9046
  onError
8849
9047
  } = options;
8850
- rootDir = path21.resolve(rootDir);
9048
+ rootDir = path22.resolve(rootDir);
8851
9049
  const config = await loadConfig(rootDir);
8852
9050
  const indexLocation = getIndexLocation(rootDir);
8853
9051
  const validExtensions = new Set(config.extensions);
@@ -8857,7 +9055,7 @@ async function watchDirectory(rootDir, options = {}) {
8857
9055
  "**/.git/**"
8858
9056
  ];
8859
9057
  function shouldWatchFile(filepath) {
8860
- const ext = path21.extname(filepath);
9058
+ const ext = path22.extname(filepath);
8861
9059
  return validExtensions.has(ext);
8862
9060
  }
8863
9061
  let isRunning = true;
@@ -8940,7 +9138,7 @@ async function watchDirectory(rootDir, options = {}) {
8940
9138
  function handleFileEvent(event, filepath) {
8941
9139
  if (!isRunning)
8942
9140
  return;
8943
- const relativePath = path21.relative(rootDir, filepath);
9141
+ const relativePath = path22.relative(rootDir, filepath);
8944
9142
  if (!shouldWatchFile(filepath)) {
8945
9143
  return;
8946
9144
  }
@@ -9019,7 +9217,7 @@ __export(exports_indexer, {
9019
9217
  cleanupIndex: () => cleanupIndex
9020
9218
  });
9021
9219
  import * as fs8 from "fs/promises";
9022
- import * as path22 from "path";
9220
+ import * as path23 from "path";
9023
9221
  import * as os3 from "os";
9024
9222
  import * as crypto2 from "crypto";
9025
9223
  function clearFreshnessCache() {
@@ -9071,7 +9269,7 @@ async function indexDirectory(rootDir, options = {}) {
9071
9269
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
9072
9270
  clearFreshnessCache();
9073
9271
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
9074
- rootDir = path22.resolve(rootDir);
9272
+ rootDir = path23.resolve(rootDir);
9075
9273
  const location = getIndexLocation(rootDir);
9076
9274
  logger.info(`Indexing directory: ${rootDir}`);
9077
9275
  logger.info(`Index location: ${location.indexDir}`);
@@ -9123,11 +9321,11 @@ async function indexDirectory(rootDir, options = {}) {
9123
9321
  rootDir,
9124
9322
  config,
9125
9323
  readFile: async (filepath) => {
9126
- const fullPath = path22.isAbsolute(filepath) ? filepath : path22.join(rootDir, filepath);
9324
+ const fullPath = path23.isAbsolute(filepath) ? filepath : path23.join(rootDir, filepath);
9127
9325
  return fs8.readFile(fullPath, "utf-8");
9128
9326
  },
9129
9327
  getFileStats: async (filepath) => {
9130
- const fullPath = path22.isAbsolute(filepath) ? filepath : path22.join(rootDir, filepath);
9328
+ const fullPath = path23.isAbsolute(filepath) ? filepath : path23.join(rootDir, filepath);
9131
9329
  const stats = await fs8.stat(fullPath);
9132
9330
  return { lastModified: stats.mtime.toISOString() };
9133
9331
  }
@@ -9167,7 +9365,7 @@ async function deleteIndex(rootDir) {
9167
9365
  } catch {}
9168
9366
  }
9169
9367
  async function resetIndex(rootDir) {
9170
- rootDir = path22.resolve(rootDir);
9368
+ rootDir = path23.resolve(rootDir);
9171
9369
  clearFreshnessCache();
9172
9370
  const status = await getIndexStatus(rootDir);
9173
9371
  if (!status.exists) {
@@ -9192,7 +9390,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
9192
9390
  let filesChanged = 0;
9193
9391
  let filesReindexed = 0;
9194
9392
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
9195
- rootDir = path22.resolve(rootDir);
9393
+ rootDir = path23.resolve(rootDir);
9196
9394
  const status = await getIndexStatus(rootDir);
9197
9395
  if (!status.exists) {
9198
9396
  clearFreshnessCache();
@@ -9254,7 +9452,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
9254
9452
  const { allFiles: currentFiles, changedFiles, changedFileMtimes } = discoveryResult;
9255
9453
  filesDiscovered = currentFiles.length;
9256
9454
  filesChanged = changedFiles.length;
9257
- const currentFileSet = new Set(currentFiles.map((f) => path22.relative(rootDir, f)));
9455
+ const currentFileSet = new Set(currentFiles.map((f) => path23.relative(rootDir, f)));
9258
9456
  const changedFileSet = new Set(changedFiles);
9259
9457
  let totalIndexed = 0;
9260
9458
  let totalRemoved = 0;
@@ -9288,8 +9486,8 @@ async function ensureIndexFresh(rootDir, options = {}) {
9288
9486
  if (filesToRemove.length > 0) {
9289
9487
  await Promise.all(filesToRemove.map(async (filepath) => {
9290
9488
  logger.debug(` Removing stale: ${filepath}`);
9291
- const indexFilePath = path22.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9292
- const symbolicFilePath = path22.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
9489
+ const indexFilePath = path23.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9490
+ const symbolicFilePath = path23.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
9293
9491
  await Promise.all([
9294
9492
  fs8.unlink(indexFilePath).catch(() => {}),
9295
9493
  fs8.unlink(symbolicFilePath).catch(() => {})
@@ -9316,11 +9514,11 @@ async function ensureIndexFresh(rootDir, options = {}) {
9316
9514
  rootDir,
9317
9515
  config,
9318
9516
  readFile: async (filepath) => {
9319
- const fullPath = path22.isAbsolute(filepath) ? filepath : path22.join(rootDir, filepath);
9517
+ const fullPath = path23.isAbsolute(filepath) ? filepath : path23.join(rootDir, filepath);
9320
9518
  return fs8.readFile(fullPath, "utf-8");
9321
9519
  },
9322
9520
  getFileStats: async (filepath) => {
9323
- const fullPath = path22.isAbsolute(filepath) ? filepath : path22.join(rootDir, filepath);
9521
+ const fullPath = path23.isAbsolute(filepath) ? filepath : path23.join(rootDir, filepath);
9324
9522
  const stats = await fs8.stat(fullPath);
9325
9523
  return { lastModified: stats.mtime.toISOString() };
9326
9524
  },
@@ -9328,7 +9526,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
9328
9526
  };
9329
9527
  const moduleChangedFiles = module.supportsFile ? changedFiles.filter((f) => module.supportsFile(f)) : changedFiles;
9330
9528
  const filesToProcess = moduleChangedFiles.map((filepath) => {
9331
- const relativePath = path22.relative(rootDir, filepath);
9529
+ const relativePath = path23.relative(rootDir, filepath);
9332
9530
  const existingEntry = manifest.files[relativePath];
9333
9531
  const lastModified = changedFileMtimes.get(filepath) || new Date().toISOString();
9334
9532
  return {
@@ -9505,7 +9703,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
9505
9703
  };
9506
9704
  const manifest = await loadModuleManifest(rootDir, module.id, config);
9507
9705
  const indexPath = getModuleIndexPath(rootDir, module.id, config);
9508
- const currentFileSet = new Set(files.map((f) => path22.relative(rootDir, f)));
9706
+ const currentFileSet = new Set(files.map((f) => path23.relative(rootDir, f)));
9509
9707
  const filesToRemove = [];
9510
9708
  for (const filepath of Object.keys(manifest.files)) {
9511
9709
  if (!currentFileSet.has(filepath)) {
@@ -9516,11 +9714,11 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
9516
9714
  logger.info(` Removing ${filesToRemove.length} stale entries...`);
9517
9715
  for (const filepath of filesToRemove) {
9518
9716
  logger.debug(` Removing: ${filepath}`);
9519
- const indexFilePath = path22.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9717
+ const indexFilePath = path23.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9520
9718
  try {
9521
9719
  await fs8.unlink(indexFilePath);
9522
9720
  } catch {}
9523
- const symbolicFilePath = path22.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
9721
+ const symbolicFilePath = path23.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
9524
9722
  try {
9525
9723
  await fs8.unlink(symbolicFilePath);
9526
9724
  } catch {}
@@ -9532,11 +9730,11 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
9532
9730
  rootDir,
9533
9731
  config,
9534
9732
  readFile: async (filepath) => {
9535
- const fullPath = path22.isAbsolute(filepath) ? filepath : path22.join(rootDir, filepath);
9733
+ const fullPath = path23.isAbsolute(filepath) ? filepath : path23.join(rootDir, filepath);
9536
9734
  return fs8.readFile(fullPath, "utf-8");
9537
9735
  },
9538
9736
  getFileStats: async (filepath) => {
9539
- const fullPath = path22.isAbsolute(filepath) ? filepath : path22.join(rootDir, filepath);
9737
+ const fullPath = path23.isAbsolute(filepath) ? filepath : path23.join(rootDir, filepath);
9540
9738
  const stats = await fs8.stat(fullPath);
9541
9739
  return { lastModified: stats.mtime.toISOString() };
9542
9740
  },
@@ -9549,7 +9747,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
9549
9747
  let indexedCount = 0;
9550
9748
  let skippedCount = 0;
9551
9749
  const processFile = async (filepath, _index) => {
9552
- const relativePath = path22.relative(rootDir, filepath);
9750
+ const relativePath = path23.relative(rootDir, filepath);
9553
9751
  try {
9554
9752
  const stats = await fs8.stat(filepath);
9555
9753
  const lastModified = stats.mtime.toISOString();
@@ -9642,8 +9840,8 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
9642
9840
  return result;
9643
9841
  }
9644
9842
  function isLikelyBinary(filepath) {
9645
- const ext = path22.extname(filepath).toLowerCase();
9646
- const basename15 = path22.basename(filepath).toLowerCase();
9843
+ const ext = path23.extname(filepath).toLowerCase();
9844
+ const basename15 = path23.basename(filepath).toLowerCase();
9647
9845
  const binaryExtensions = new Set([
9648
9846
  ".png",
9649
9847
  ".jpg",
@@ -9719,7 +9917,7 @@ async function findFilesWithStats(rootDir, config, lastIndexStarted) {
9719
9917
  const ignoreDirs = new Set(config.ignorePaths);
9720
9918
  const lastIndexMs = lastIndexStarted?.getTime() ?? 0;
9721
9919
  const crawler = new Builder().withFullPaths().exclude((dirName) => ignoreDirs.has(dirName)).filter((filePath) => {
9722
- const ext = path22.extname(filePath);
9920
+ const ext = path23.extname(filePath);
9723
9921
  return validExtensions.has(ext);
9724
9922
  }).crawl(rootDir);
9725
9923
  const allFiles = await crawler.withPromise();
@@ -9774,13 +9972,13 @@ async function loadModuleManifest(rootDir, moduleId, config) {
9774
9972
  }
9775
9973
  async function writeModuleManifest(rootDir, moduleId, manifest, config) {
9776
9974
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
9777
- await fs8.mkdir(path22.dirname(manifestPath), { recursive: true });
9975
+ await fs8.mkdir(path23.dirname(manifestPath), { recursive: true });
9778
9976
  await fs8.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
9779
9977
  }
9780
9978
  async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
9781
9979
  const indexPath = getModuleIndexPath(rootDir, moduleId, config);
9782
- const indexFilePath = path22.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9783
- await fs8.mkdir(path22.dirname(indexFilePath), { recursive: true });
9980
+ const indexFilePath = path23.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9981
+ await fs8.mkdir(path23.dirname(indexFilePath), { recursive: true });
9784
9982
  await fs8.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
9785
9983
  }
9786
9984
  async function loadGlobalManifest(rootDir, config) {
@@ -9800,13 +9998,13 @@ async function updateGlobalManifest(rootDir, modules, config, indexStartTime) {
9800
9998
  lastIndexStarted: indexStartTime,
9801
9999
  modules: modules.map((m) => m.id)
9802
10000
  };
9803
- await fs8.mkdir(path22.dirname(manifestPath), { recursive: true });
10001
+ await fs8.mkdir(path23.dirname(manifestPath), { recursive: true });
9804
10002
  await fs8.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
9805
10003
  }
9806
10004
  async function cleanupIndex(rootDir, options = {}) {
9807
10005
  const verbose = options.verbose ?? false;
9808
10006
  const logger = options.logger ?? createLogger({ verbose });
9809
- rootDir = path22.resolve(rootDir);
10007
+ rootDir = path23.resolve(rootDir);
9810
10008
  logger.info(`Cleaning up index in: ${rootDir}`);
9811
10009
  const config = await loadConfig(rootDir);
9812
10010
  await registerBuiltInModules();
@@ -9836,7 +10034,7 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
9836
10034
  const filesToRemove = [];
9837
10035
  const updatedFiles = {};
9838
10036
  for (const [filepath, entry] of Object.entries(manifest.files)) {
9839
- const fullPath = path22.join(rootDir, filepath);
10037
+ const fullPath = path23.join(rootDir, filepath);
9840
10038
  try {
9841
10039
  await fs8.access(fullPath);
9842
10040
  updatedFiles[filepath] = entry;
@@ -9848,7 +10046,7 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
9848
10046
  }
9849
10047
  }
9850
10048
  for (const filepath of filesToRemove) {
9851
- const indexFilePath = path22.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
10049
+ const indexFilePath = path23.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
9852
10050
  try {
9853
10051
  await fs8.unlink(indexFilePath);
9854
10052
  } catch {}
@@ -9864,7 +10062,7 @@ async function cleanupEmptyDirectories(dir) {
9864
10062
  const entries = await fs8.readdir(dir, { withFileTypes: true });
9865
10063
  for (const entry of entries) {
9866
10064
  if (entry.isDirectory()) {
9867
- const subDir = path22.join(dir, entry.name);
10065
+ const subDir = path23.join(dir, entry.name);
9868
10066
  await cleanupEmptyDirectories(subDir);
9869
10067
  }
9870
10068
  }
@@ -9879,7 +10077,7 @@ async function cleanupEmptyDirectories(dir) {
9879
10077
  }
9880
10078
  }
9881
10079
  async function getIndexStatus(rootDir) {
9882
- rootDir = path22.resolve(rootDir);
10080
+ rootDir = path23.resolve(rootDir);
9883
10081
  const config = await loadConfig(rootDir);
9884
10082
  const location = getIndexLocation(rootDir);
9885
10083
  const indexDir = location.indexDir;
@@ -9915,7 +10113,7 @@ async function getIndexStatus(rootDir) {
9915
10113
  }
9916
10114
  } catch {
9917
10115
  try {
9918
- const entries = await fs8.readdir(path22.join(indexDir, "index"));
10116
+ const entries = await fs8.readdir(path23.join(indexDir, "index"));
9919
10117
  if (entries.length > 0) {
9920
10118
  status.exists = true;
9921
10119
  for (const entry of entries) {
@@ -9946,210 +10144,215 @@ var init_indexer = __esm(() => {
9946
10144
  DEFAULT_CONCURRENCY = getOptimalConcurrency();
9947
10145
  });
9948
10146
 
9949
- // node_modules/balanced-match/index.js
9950
- var require_balanced_match = __commonJS((exports, module) => {
9951
- module.exports = balanced;
9952
- function balanced(a, b, str) {
9953
- if (a instanceof RegExp)
9954
- a = maybeMatch(a, str);
9955
- if (b instanceof RegExp)
9956
- b = maybeMatch(b, str);
9957
- var r = range(a, b, str);
9958
- return r && {
9959
- start: r[0],
9960
- end: r[1],
9961
- pre: str.slice(0, r[0]),
9962
- body: str.slice(r[0] + a.length, r[1]),
9963
- post: str.slice(r[1] + b.length)
9964
- };
9965
- }
9966
- function maybeMatch(reg, str) {
9967
- var m = str.match(reg);
9968
- return m ? m[0] : null;
9969
- }
9970
- balanced.range = range;
9971
- function range(a, b, str) {
9972
- var begs, beg, left, right, result;
9973
- var ai = str.indexOf(a);
9974
- var bi = str.indexOf(b, ai + 1);
9975
- var i = ai;
9976
- if (ai >= 0 && bi > 0) {
9977
- if (a === b) {
9978
- return [ai, bi];
9979
- }
9980
- begs = [];
9981
- left = str.length;
9982
- while (i >= 0 && !result) {
9983
- if (i == ai) {
9984
- begs.push(i);
9985
- ai = str.indexOf(a, i + 1);
9986
- } else if (begs.length == 1) {
9987
- result = [begs.pop(), bi];
9988
- } else {
9989
- beg = begs.pop();
9990
- if (beg < left) {
9991
- left = beg;
9992
- right = bi;
9993
- }
9994
- bi = str.indexOf(b, i + 1);
10147
+ // node_modules/@isaacs/balanced-match/dist/esm/index.js
10148
+ var balanced = (a, b, str) => {
10149
+ const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
10150
+ const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
10151
+ const r = ma !== null && mb != null && range(ma, mb, str);
10152
+ return r && {
10153
+ start: r[0],
10154
+ end: r[1],
10155
+ pre: str.slice(0, r[0]),
10156
+ body: str.slice(r[0] + ma.length, r[1]),
10157
+ post: str.slice(r[1] + mb.length)
10158
+ };
10159
+ }, maybeMatch = (reg, str) => {
10160
+ const m = str.match(reg);
10161
+ return m ? m[0] : null;
10162
+ }, range = (a, b, str) => {
10163
+ let begs, beg, left, right = undefined, result;
10164
+ let ai = str.indexOf(a);
10165
+ let bi = str.indexOf(b, ai + 1);
10166
+ let i = ai;
10167
+ if (ai >= 0 && bi > 0) {
10168
+ if (a === b) {
10169
+ return [ai, bi];
10170
+ }
10171
+ begs = [];
10172
+ left = str.length;
10173
+ while (i >= 0 && !result) {
10174
+ if (i === ai) {
10175
+ begs.push(i);
10176
+ ai = str.indexOf(a, i + 1);
10177
+ } else if (begs.length === 1) {
10178
+ const r = begs.pop();
10179
+ if (r !== undefined)
10180
+ result = [r, bi];
10181
+ } else {
10182
+ beg = begs.pop();
10183
+ if (beg !== undefined && beg < left) {
10184
+ left = beg;
10185
+ right = bi;
9995
10186
  }
9996
- i = ai < bi && ai >= 0 ? ai : bi;
9997
- }
9998
- if (begs.length) {
9999
- result = [left, right];
10187
+ bi = str.indexOf(b, i + 1);
10000
10188
  }
10189
+ i = ai < bi && ai >= 0 ? ai : bi;
10001
10190
  }
10002
- return result;
10003
- }
10004
- });
10005
-
10006
- // node_modules/brace-expansion/index.js
10007
- var require_brace_expansion = __commonJS((exports, module) => {
10008
- var balanced = require_balanced_match();
10009
- module.exports = expandTop;
10010
- var escSlash = "\x00SLASH" + Math.random() + "\x00";
10011
- var escOpen = "\x00OPEN" + Math.random() + "\x00";
10012
- var escClose = "\x00CLOSE" + Math.random() + "\x00";
10013
- var escComma = "\x00COMMA" + Math.random() + "\x00";
10014
- var escPeriod = "\x00PERIOD" + Math.random() + "\x00";
10015
- function numeric(str) {
10016
- return parseInt(str, 10) == str ? parseInt(str, 10) : str.charCodeAt(0);
10017
- }
10018
- function escapeBraces(str) {
10019
- return str.split("\\\\").join(escSlash).split("\\{").join(escOpen).split("\\}").join(escClose).split("\\,").join(escComma).split("\\.").join(escPeriod);
10020
- }
10021
- function unescapeBraces(str) {
10022
- return str.split(escSlash).join("\\").split(escOpen).join("{").split(escClose).join("}").split(escComma).join(",").split(escPeriod).join(".");
10023
- }
10024
- function parseCommaParts(str) {
10025
- if (!str)
10026
- return [""];
10027
- var parts = [];
10028
- var m = balanced("{", "}", str);
10029
- if (!m)
10030
- return str.split(",");
10031
- var pre = m.pre;
10032
- var body = m.body;
10033
- var post = m.post;
10034
- var p = pre.split(",");
10035
- p[p.length - 1] += "{" + body + "}";
10036
- var postParts = parseCommaParts(post);
10037
- if (post.length) {
10038
- p[p.length - 1] += postParts.shift();
10039
- p.push.apply(p, postParts);
10040
- }
10041
- parts.push.apply(parts, p);
10042
- return parts;
10043
- }
10044
- function expandTop(str) {
10045
- if (!str)
10046
- return [];
10047
- if (str.substr(0, 2) === "{}") {
10048
- str = "\\{\\}" + str.substr(2);
10191
+ if (begs.length && right !== undefined) {
10192
+ result = [left, right];
10049
10193
  }
10050
- return expand(escapeBraces(str), true).map(unescapeBraces);
10051
- }
10052
- function embrace(str) {
10053
- return "{" + str + "}";
10054
10194
  }
10055
- function isPadded(el) {
10056
- return /^-?0\d/.test(el);
10057
- }
10058
- function lte(i, y) {
10059
- return i <= y;
10195
+ return result;
10196
+ };
10197
+
10198
+ // node_modules/@isaacs/brace-expansion/dist/esm/index.js
10199
+ function numeric(str) {
10200
+ return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
10201
+ }
10202
+ function escapeBraces(str) {
10203
+ return str.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
10204
+ }
10205
+ function unescapeBraces(str) {
10206
+ return str.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
10207
+ }
10208
+ function parseCommaParts(str) {
10209
+ if (!str) {
10210
+ return [""];
10060
10211
  }
10061
- function gte(i, y) {
10062
- return i >= y;
10212
+ const parts = [];
10213
+ const m = balanced("{", "}", str);
10214
+ if (!m) {
10215
+ return str.split(",");
10216
+ }
10217
+ const { pre, body, post } = m;
10218
+ const p = pre.split(",");
10219
+ p[p.length - 1] += "{" + body + "}";
10220
+ const postParts = parseCommaParts(post);
10221
+ if (post.length) {
10222
+ p[p.length - 1] += postParts.shift();
10223
+ p.push.apply(p, postParts);
10224
+ }
10225
+ parts.push.apply(parts, p);
10226
+ return parts;
10227
+ }
10228
+ function expand(str) {
10229
+ if (!str) {
10230
+ return [];
10063
10231
  }
10064
- function expand(str, isTop) {
10065
- var expansions = [];
10066
- var m = balanced("{", "}", str);
10067
- if (!m)
10068
- return [str];
10069
- var pre = m.pre;
10070
- var post = m.post.length ? expand(m.post, false) : [""];
10071
- if (/\$$/.test(m.pre)) {
10072
- for (var k = 0;k < post.length; k++) {
10073
- var expansion = pre + "{" + m.body + "}" + post[k];
10074
- expansions.push(expansion);
10232
+ if (str.slice(0, 2) === "{}") {
10233
+ str = "\\{\\}" + str.slice(2);
10234
+ }
10235
+ return expand_(escapeBraces(str), true).map(unescapeBraces);
10236
+ }
10237
+ function embrace(str) {
10238
+ return "{" + str + "}";
10239
+ }
10240
+ function isPadded(el) {
10241
+ return /^-?0\d/.test(el);
10242
+ }
10243
+ function lte(i, y) {
10244
+ return i <= y;
10245
+ }
10246
+ function gte(i, y) {
10247
+ return i >= y;
10248
+ }
10249
+ function expand_(str, isTop) {
10250
+ const expansions = [];
10251
+ const m = balanced("{", "}", str);
10252
+ if (!m)
10253
+ return [str];
10254
+ const pre = m.pre;
10255
+ const post = m.post.length ? expand_(m.post, false) : [""];
10256
+ if (/\$$/.test(m.pre)) {
10257
+ for (let k = 0;k < post.length; k++) {
10258
+ const expansion = pre + "{" + m.body + "}" + post[k];
10259
+ expansions.push(expansion);
10260
+ }
10261
+ } else {
10262
+ const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
10263
+ const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
10264
+ const isSequence = isNumericSequence || isAlphaSequence;
10265
+ const isOptions = m.body.indexOf(",") >= 0;
10266
+ if (!isSequence && !isOptions) {
10267
+ if (m.post.match(/,(?!,).*\}/)) {
10268
+ str = m.pre + "{" + m.body + escClose + m.post;
10269
+ return expand_(str);
10075
10270
  }
10271
+ return [str];
10272
+ }
10273
+ let n;
10274
+ if (isSequence) {
10275
+ n = m.body.split(/\.\./);
10076
10276
  } else {
10077
- var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
10078
- var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
10079
- var isSequence = isNumericSequence || isAlphaSequence;
10080
- var isOptions = m.body.indexOf(",") >= 0;
10081
- if (!isSequence && !isOptions) {
10082
- if (m.post.match(/,(?!,).*\}/)) {
10083
- str = m.pre + "{" + m.body + escClose + m.post;
10084
- return expand(str);
10085
- }
10086
- return [str];
10087
- }
10088
- var n;
10089
- if (isSequence) {
10090
- n = m.body.split(/\.\./);
10091
- } else {
10092
- n = parseCommaParts(m.body);
10277
+ n = parseCommaParts(m.body);
10278
+ if (n.length === 1 && n[0] !== undefined) {
10279
+ n = expand_(n[0], false).map(embrace);
10093
10280
  if (n.length === 1) {
10094
- n = expand(n[0], false).map(embrace);
10095
- if (n.length === 1) {
10096
- return post.map(function(p) {
10097
- return m.pre + n[0] + p;
10098
- });
10281
+ return post.map((p) => m.pre + n[0] + p);
10282
+ }
10283
+ }
10284
+ }
10285
+ let N;
10286
+ if (isSequence && n[0] !== undefined && n[1] !== undefined) {
10287
+ const x = numeric(n[0]);
10288
+ const y = numeric(n[1]);
10289
+ const width = Math.max(n[0].length, n[1].length);
10290
+ let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
10291
+ let test = lte;
10292
+ const reverse = y < x;
10293
+ if (reverse) {
10294
+ incr *= -1;
10295
+ test = gte;
10296
+ }
10297
+ const pad = n.some(isPadded);
10298
+ N = [];
10299
+ for (let i = x;test(i, y); i += incr) {
10300
+ let c;
10301
+ if (isAlphaSequence) {
10302
+ c = String.fromCharCode(i);
10303
+ if (c === "\\") {
10304
+ c = "";
10099
10305
  }
10100
- }
10101
- }
10102
- var N;
10103
- if (isSequence) {
10104
- var x = numeric(n[0]);
10105
- var y = numeric(n[1]);
10106
- var width = Math.max(n[0].length, n[1].length);
10107
- var incr = n.length == 3 ? Math.abs(numeric(n[2])) : 1;
10108
- var test = lte;
10109
- var reverse = y < x;
10110
- if (reverse) {
10111
- incr *= -1;
10112
- test = gte;
10113
- }
10114
- var pad = n.some(isPadded);
10115
- N = [];
10116
- for (var i = x;test(i, y); i += incr) {
10117
- var c;
10118
- if (isAlphaSequence) {
10119
- c = String.fromCharCode(i);
10120
- if (c === "\\")
10121
- c = "";
10122
- } else {
10123
- c = String(i);
10124
- if (pad) {
10125
- var need = width - c.length;
10126
- if (need > 0) {
10127
- var z = new Array(need + 1).join("0");
10128
- if (i < 0)
10129
- c = "-" + z + c.slice(1);
10130
- else
10131
- c = z + c;
10306
+ } else {
10307
+ c = String(i);
10308
+ if (pad) {
10309
+ const need = width - c.length;
10310
+ if (need > 0) {
10311
+ const z = new Array(need + 1).join("0");
10312
+ if (i < 0) {
10313
+ c = "-" + z + c.slice(1);
10314
+ } else {
10315
+ c = z + c;
10132
10316
  }
10133
10317
  }
10134
10318
  }
10135
- N.push(c);
10136
- }
10137
- } else {
10138
- N = [];
10139
- for (var j = 0;j < n.length; j++) {
10140
- N.push.apply(N, expand(n[j], false));
10141
10319
  }
10320
+ N.push(c);
10142
10321
  }
10143
- for (var j = 0;j < N.length; j++) {
10144
- for (var k = 0;k < post.length; k++) {
10145
- var expansion = pre + N[j] + post[k];
10146
- if (!isTop || isSequence || expansion)
10147
- expansions.push(expansion);
10322
+ } else {
10323
+ N = [];
10324
+ for (let j = 0;j < n.length; j++) {
10325
+ N.push.apply(N, expand_(n[j], false));
10326
+ }
10327
+ }
10328
+ for (let j = 0;j < N.length; j++) {
10329
+ for (let k = 0;k < post.length; k++) {
10330
+ const expansion = pre + N[j] + post[k];
10331
+ if (!isTop || isSequence || expansion) {
10332
+ expansions.push(expansion);
10148
10333
  }
10149
10334
  }
10150
10335
  }
10151
- return expansions;
10152
10336
  }
10337
+ return expansions;
10338
+ }
10339
+ var escSlash, escOpen, escClose, escComma, escPeriod, escSlashPattern, escOpenPattern, escClosePattern, escCommaPattern, escPeriodPattern, slashPattern, openPattern, closePattern, commaPattern, periodPattern;
10340
+ var init_esm = __esm(() => {
10341
+ escSlash = "\x00SLASH" + Math.random() + "\x00";
10342
+ escOpen = "\x00OPEN" + Math.random() + "\x00";
10343
+ escClose = "\x00CLOSE" + Math.random() + "\x00";
10344
+ escComma = "\x00COMMA" + Math.random() + "\x00";
10345
+ escPeriod = "\x00PERIOD" + Math.random() + "\x00";
10346
+ escSlashPattern = new RegExp(escSlash, "g");
10347
+ escOpenPattern = new RegExp(escOpen, "g");
10348
+ escClosePattern = new RegExp(escClose, "g");
10349
+ escCommaPattern = new RegExp(escComma, "g");
10350
+ escPeriodPattern = new RegExp(escPeriod, "g");
10351
+ slashPattern = /\\\\/g;
10352
+ openPattern = /\\{/g;
10353
+ closePattern = /\\}/g;
10354
+ commaPattern = /\\,/g;
10355
+ periodPattern = /\\./g;
10153
10356
  });
10154
10357
 
10155
10358
  // node_modules/minimatch/dist/esm/assert-valid-pattern.js
@@ -10275,8 +10478,11 @@ var init_brace_expressions = __esm(() => {
10275
10478
  });
10276
10479
 
10277
10480
  // node_modules/minimatch/dist/esm/unescape.js
10278
- var unescape = (s, { windowsPathsNoEscape = false } = {}) => {
10279
- return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
10481
+ var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
10482
+ if (magicalBraces) {
10483
+ return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
10484
+ }
10485
+ return windowsPathsNoEscape ? s.replace(/\[([^\/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\{}])\]/g, "$1$2").replace(/\\([^\/{}])/g, "$1");
10280
10486
  };
10281
10487
 
10282
10488
  // node_modules/minimatch/dist/esm/ast.js
@@ -10545,7 +10751,7 @@ class AST {
10545
10751
  if (this.#root === this)
10546
10752
  this.#fillNegs();
10547
10753
  if (!this.type) {
10548
- const noEmpty = this.isStart() && this.isEnd();
10754
+ const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
10549
10755
  const src = this.#parts.map((p) => {
10550
10756
  const [re, _, hasMagic, uflag] = typeof p === "string" ? AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
10551
10757
  this.#hasMagic = this.#hasMagic || hasMagic;
@@ -10647,10 +10853,7 @@ class AST {
10647
10853
  }
10648
10854
  }
10649
10855
  if (c === "*") {
10650
- if (noEmpty && glob === "*")
10651
- re += starNoEmpty;
10652
- else
10653
- re += star;
10856
+ re += noEmpty && glob === "*" ? starNoEmpty : star;
10654
10857
  hasMagic = true;
10655
10858
  continue;
10656
10859
  }
@@ -10676,7 +10879,10 @@ var init_ast = __esm(() => {
10676
10879
  });
10677
10880
 
10678
10881
  // node_modules/minimatch/dist/esm/escape.js
10679
- var escape = (s, { windowsPathsNoEscape = false } = {}) => {
10882
+ var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
10883
+ if (magicalBraces) {
10884
+ return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
10885
+ }
10680
10886
  return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
10681
10887
  };
10682
10888
 
@@ -11150,16 +11356,27 @@ globstar while`, file, fr, pattern, pr, swallowee);
11150
11356
  pp[i] = twoStar;
11151
11357
  }
11152
11358
  } else if (next === undefined) {
11153
- pp[i - 1] = prev + "(?:\\/|" + twoStar + ")?";
11359
+ pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + ")?";
11154
11360
  } else if (next !== GLOBSTAR) {
11155
11361
  pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next;
11156
11362
  pp[i + 1] = GLOBSTAR;
11157
11363
  }
11158
11364
  });
11159
- return pp.filter((p) => p !== GLOBSTAR).join("/");
11365
+ const filtered = pp.filter((p) => p !== GLOBSTAR);
11366
+ if (this.partial && filtered.length >= 1) {
11367
+ const prefixes = [];
11368
+ for (let i = 1;i <= filtered.length; i++) {
11369
+ prefixes.push(filtered.slice(0, i).join("/"));
11370
+ }
11371
+ return "(?:" + prefixes.join("|") + ")";
11372
+ }
11373
+ return filtered.join("/");
11160
11374
  }).join("|");
11161
11375
  const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
11162
11376
  re = "^" + open + re + close + "$";
11377
+ if (this.partial) {
11378
+ re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
11379
+ }
11163
11380
  if (this.negate)
11164
11381
  re = "^(?!" + re + ").+$";
11165
11382
  try {
@@ -11226,7 +11443,7 @@ globstar while`, file, fr, pattern, pr, swallowee);
11226
11443
  return minimatch.defaults(def).Minimatch;
11227
11444
  }
11228
11445
  }
11229
- var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11446
+ var minimatch = (p, pattern, options = {}) => {
11230
11447
  assertValidPattern(pattern);
11231
11448
  if (!options.nocomment && pattern.charAt(0) === "#") {
11232
11449
  return false;
@@ -11262,7 +11479,7 @@ var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11262
11479
  }, qmarksTestNoExtDot = ([$0]) => {
11263
11480
  const len = $0.length;
11264
11481
  return (f) => f.length === len && f !== "." && f !== "..";
11265
- }, defaultPlatform, path23, sep2, GLOBSTAR, qmark2 = "[^/]", star2, twoStarDot = "(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?", twoStarNoDot = "(?:(?!(?:\\/|^)\\.).)*?", filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options), ext = (a, b = {}) => Object.assign({}, a, b), defaults = (def) => {
11482
+ }, defaultPlatform, path24, sep2, GLOBSTAR, qmark2 = "[^/]", star2, twoStarDot = "(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?", twoStarNoDot = "(?:(?!(?:\\/|^)\\.).)*?", filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options), ext = (a, b = {}) => Object.assign({}, a, b), defaults = (def) => {
11266
11483
  if (!def || typeof def !== "object" || !Object.keys(def).length) {
11267
11484
  return minimatch;
11268
11485
  }
@@ -11300,7 +11517,7 @@ var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11300
11517
  if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
11301
11518
  return [pattern];
11302
11519
  }
11303
- return import_brace_expansion.default(pattern);
11520
+ return expand(pattern);
11304
11521
  }, makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe(), match = (list, pattern, options = {}) => {
11305
11522
  const mm = new Minimatch(pattern, options);
11306
11523
  list = list.filter((f) => mm.match(f));
@@ -11309,22 +11526,22 @@ var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11309
11526
  }
11310
11527
  return list;
11311
11528
  }, globMagic, regExpEscape2 = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
11312
- var init_esm = __esm(() => {
11529
+ var init_esm2 = __esm(() => {
11530
+ init_esm();
11313
11531
  init_assert_valid_pattern();
11314
11532
  init_ast();
11315
11533
  init_ast();
11316
- import_brace_expansion = __toESM(require_brace_expansion(), 1);
11317
11534
  starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
11318
11535
  starDotStarRE = /^\*+\.\*+$/;
11319
11536
  dotStarRE = /^\.\*+$/;
11320
11537
  starRE = /^\*+$/;
11321
11538
  qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
11322
11539
  defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
11323
- path23 = {
11540
+ path24 = {
11324
11541
  win32: { sep: "\\" },
11325
11542
  posix: { sep: "/" }
11326
11543
  };
11327
- sep2 = defaultPlatform === "win32" ? path23.win32.sep : path23.posix.sep;
11544
+ sep2 = defaultPlatform === "win32" ? path24.win32.sep : path24.posix.sep;
11328
11545
  minimatch.sep = sep2;
11329
11546
  GLOBSTAR = Symbol("globstar **");
11330
11547
  minimatch.GLOBSTAR = GLOBSTAR;
@@ -11345,17 +11562,196 @@ var init_esm = __esm(() => {
11345
11562
  var init_types = __esm(() => {
11346
11563
  init_entities();
11347
11564
  });
11565
+ // src/domain/usecases/exactSearch.ts
11566
+ function matchesPathFilter(relativePath, filters, matchFn) {
11567
+ const normalizedPath = relativePath.replace(/\\/g, "/");
11568
+ for (const filter2 of filters) {
11569
+ const normalizedFilter = filter2.replace(/\\/g, "/").replace(/^\//, "").replace(/\/$/, "");
11570
+ const isGlobPattern = /[*?[\]{}!]/.test(normalizedFilter);
11571
+ if (isGlobPattern) {
11572
+ const pattern = normalizedFilter.startsWith("**/") ? normalizedFilter : `**/${normalizedFilter}`;
11573
+ if (matchFn(normalizedPath, pattern)) {
11574
+ return true;
11575
+ }
11576
+ } else {
11577
+ if (normalizedPath.startsWith(normalizedFilter + "/") || normalizedPath === normalizedFilter || normalizedPath.includes("/" + normalizedFilter + "/")) {
11578
+ return true;
11579
+ }
11580
+ }
11581
+ }
11582
+ return false;
11583
+ }
11584
+ async function executeExactSearch(fs9, options, matchFn) {
11585
+ const {
11586
+ rootDir,
11587
+ literal,
11588
+ pathFilter = [],
11589
+ maxFiles = 20,
11590
+ maxOccurrencesPerFile = 5,
11591
+ caseInsensitive = false
11592
+ } = options;
11593
+ const files = new Map;
11594
+ async function walkDir(dir, baseDir) {
11595
+ try {
11596
+ const entries = await fs9.readDir(dir);
11597
+ for (const entry of entries) {
11598
+ const fullPath = fs9.join(dir, entry);
11599
+ const relativePath = fs9.relative(baseDir, fullPath);
11600
+ let isDirectory = false;
11601
+ try {
11602
+ const stats = await fs9.getStats(fullPath);
11603
+ isDirectory = stats.isDirectory ?? false;
11604
+ } catch {
11605
+ continue;
11606
+ }
11607
+ if (isDirectory) {
11608
+ if (DEFAULT_IGNORED_DIRS.includes(entry)) {
11609
+ continue;
11610
+ }
11611
+ await walkDir(fullPath, baseDir);
11612
+ } else {
11613
+ if (pathFilter.length > 0) {
11614
+ if (!matchesPathFilter(relativePath, pathFilter, matchFn)) {
11615
+ continue;
11616
+ }
11617
+ }
11618
+ try {
11619
+ const content = await fs9.readFile(fullPath);
11620
+ if (isSearchableContent(content, fullPath)) {
11621
+ files.set(relativePath, content);
11622
+ }
11623
+ } catch {}
11624
+ }
11625
+ }
11626
+ } catch {}
11627
+ }
11628
+ await walkDir(rootDir, rootDir);
11629
+ return searchFiles(files, literal, {
11630
+ maxFiles,
11631
+ maxOccurrencesPerFile,
11632
+ caseInsensitive
11633
+ });
11634
+ }
11635
+ var DEFAULT_IGNORED_DIRS;
11636
+ var init_exactSearch = __esm(() => {
11637
+ init_simpleSearch();
11638
+ DEFAULT_IGNORED_DIRS = [
11639
+ "node_modules",
11640
+ ".git",
11641
+ ".raggrep",
11642
+ "dist",
11643
+ "build",
11644
+ ".next",
11645
+ "__pycache__",
11646
+ ".venv",
11647
+ "venv"
11648
+ ];
11649
+ });
11650
+
11651
+ // src/domain/usecases/index.ts
11652
+ var init_usecases = __esm(() => {
11653
+ init_exactSearch();
11654
+ });
11655
+
11656
+ // src/infrastructure/filesystem/nodeFileSystem.ts
11657
+ import * as fs9 from "fs/promises";
11658
+ import * as path25 from "path";
11659
+ import { glob } from "glob";
11660
+
11661
+ class NodeFileSystem {
11662
+ async readFile(filepath) {
11663
+ return fs9.readFile(filepath, "utf-8");
11664
+ }
11665
+ async writeFile(filepath, content) {
11666
+ await fs9.mkdir(path25.dirname(filepath), { recursive: true });
11667
+ await fs9.writeFile(filepath, content, "utf-8");
11668
+ }
11669
+ async deleteFile(filepath) {
11670
+ try {
11671
+ await fs9.unlink(filepath);
11672
+ } catch (error) {
11673
+ if (error.code !== "ENOENT") {
11674
+ throw error;
11675
+ }
11676
+ }
11677
+ }
11678
+ async getStats(filepath) {
11679
+ const stats = await fs9.stat(filepath);
11680
+ return {
11681
+ lastModified: stats.mtime.toISOString(),
11682
+ size: stats.isDirectory() ? undefined : stats.size,
11683
+ isDirectory: stats.isDirectory()
11684
+ };
11685
+ }
11686
+ async exists(filepath) {
11687
+ try {
11688
+ await fs9.access(filepath);
11689
+ return true;
11690
+ } catch {
11691
+ return false;
11692
+ }
11693
+ }
11694
+ async mkdir(dirpath) {
11695
+ await fs9.mkdir(dirpath, { recursive: true });
11696
+ }
11697
+ async readDir(dirpath) {
11698
+ return fs9.readdir(dirpath);
11699
+ }
11700
+ async findFiles(rootDir, patterns, ignore) {
11701
+ const ignorePatterns = ignore.map((p) => `**/${p}/**`);
11702
+ const files = [];
11703
+ for (const pattern of patterns) {
11704
+ const matches = await glob(pattern, {
11705
+ cwd: rootDir,
11706
+ absolute: true,
11707
+ ignore: ignorePatterns
11708
+ });
11709
+ files.push(...matches);
11710
+ }
11711
+ return [...new Set(files)];
11712
+ }
11713
+ join(...segments) {
11714
+ return path25.join(...segments);
11715
+ }
11716
+ relative(from, to) {
11717
+ return path25.relative(from, to);
11718
+ }
11719
+ resolve(...segments) {
11720
+ return path25.resolve(...segments);
11721
+ }
11722
+ dirname(filepath) {
11723
+ return path25.dirname(filepath);
11724
+ }
11725
+ extname(filepath) {
11726
+ return path25.extname(filepath);
11727
+ }
11728
+ }
11729
+ var nodeFileSystem;
11730
+ var init_nodeFileSystem = __esm(() => {
11731
+ nodeFileSystem = new NodeFileSystem;
11732
+ });
11733
+
11734
+ // src/infrastructure/filesystem/index.ts
11735
+ var init_filesystem = __esm(() => {
11736
+ init_nodeFileSystem();
11737
+ });
11348
11738
 
11349
11739
  // src/app/search/index.ts
11350
11740
  var exports_search = {};
11351
11741
  __export(exports_search, {
11352
11742
  search: () => search,
11353
- formatSearchResults: () => formatSearchResults
11743
+ hybridSearch: () => hybridSearch,
11744
+ formatSearchResults: () => formatSearchResults2,
11745
+ formatHybridSearchResults: () => formatHybridSearchResults
11354
11746
  });
11355
- import * as fs9 from "fs/promises";
11356
- import * as path24 from "path";
11747
+ import * as fs10 from "fs/promises";
11748
+ import * as path26 from "path";
11357
11749
  async function search(rootDir, query, options = {}) {
11358
- rootDir = path24.resolve(rootDir);
11750
+ const hybridResults = await hybridSearch(rootDir, query, options);
11751
+ return hybridResults.results;
11752
+ }
11753
+ async function hybridSearch(rootDir, query, options = {}) {
11754
+ rootDir = path26.resolve(rootDir);
11359
11755
  const ensureFresh = options.ensureFresh ?? DEFAULT_SEARCH_OPTIONS.ensureFresh;
11360
11756
  if (ensureFresh) {
11361
11757
  await ensureIndexFresh(rootDir, { quiet: true });
@@ -11366,7 +11762,7 @@ async function search(rootDir, query, options = {}) {
11366
11762
  const globalManifest = await loadGlobalManifest2(rootDir, config);
11367
11763
  if (!globalManifest || globalManifest.modules.length === 0) {
11368
11764
  console.log('No index found. Run "raggrep index" first.');
11369
- return [];
11765
+ return { results: [], fusionApplied: false };
11370
11766
  }
11371
11767
  const modulesToSearch = [];
11372
11768
  for (const moduleId of globalManifest.modules) {
@@ -11381,7 +11777,7 @@ async function search(rootDir, query, options = {}) {
11381
11777
  }
11382
11778
  if (modulesToSearch.length === 0) {
11383
11779
  console.log("No enabled modules with indexes found.");
11384
- return [];
11780
+ return { results: [], fusionApplied: false };
11385
11781
  }
11386
11782
  const allResults = [];
11387
11783
  for (const module of modulesToSearch) {
@@ -11405,9 +11801,42 @@ async function search(rootDir, query, options = {}) {
11405
11801
  });
11406
11802
  });
11407
11803
  }
11804
+ let exactMatches;
11805
+ let fusionApplied = false;
11806
+ if (isIdentifierQuery(query)) {
11807
+ const literal = extractSearchLiteral(query);
11808
+ exactMatches = await performExactSearch(rootDir, literal, config, options);
11809
+ if (exactMatches && exactMatches.totalMatches > 0) {
11810
+ const exactMatchFilepaths = new Set(exactMatches.files.map((f) => f.filepath));
11811
+ for (const result of filteredResults) {
11812
+ if (exactMatchFilepaths.has(result.filepath)) {
11813
+ result.score *= 1.5;
11814
+ if (!result.context)
11815
+ result.context = {};
11816
+ result.context.exactMatchFusion = true;
11817
+ fusionApplied = true;
11818
+ }
11819
+ }
11820
+ }
11821
+ }
11408
11822
  filteredResults.sort((a, b) => b.score - a.score);
11409
11823
  const topK = options.topK ?? 10;
11410
- return filteredResults.slice(0, topK);
11824
+ return {
11825
+ results: filteredResults.slice(0, topK),
11826
+ exactMatches,
11827
+ fusionApplied
11828
+ };
11829
+ }
11830
+ async function performExactSearch(rootDir, literal, config, options) {
11831
+ const fs11 = new NodeFileSystem;
11832
+ return executeExactSearch(fs11, {
11833
+ rootDir,
11834
+ literal,
11835
+ pathFilter: options.pathFilter,
11836
+ maxFiles: 20,
11837
+ maxOccurrencesPerFile: 5,
11838
+ caseInsensitive: false
11839
+ }, (path27, pattern) => minimatch(path27, pattern, { matchBase: true }));
11411
11840
  }
11412
11841
  function createSearchContext(rootDir, moduleId, config) {
11413
11842
  const indexPath = getModuleIndexPath(rootDir, moduleId, config);
@@ -11416,9 +11845,9 @@ function createSearchContext(rootDir, moduleId, config) {
11416
11845
  config,
11417
11846
  loadFileIndex: async (filepath) => {
11418
11847
  const hasExtension = /\.[^./]+$/.test(filepath);
11419
- const indexFilePath = hasExtension ? path24.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path24.join(indexPath, filepath + ".json");
11848
+ const indexFilePath = hasExtension ? path26.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path26.join(indexPath, filepath + ".json");
11420
11849
  try {
11421
- const content = await fs9.readFile(indexFilePath, "utf-8");
11850
+ const content = await fs10.readFile(indexFilePath, "utf-8");
11422
11851
  return JSON.parse(content);
11423
11852
  } catch {
11424
11853
  return null;
@@ -11428,17 +11857,17 @@ function createSearchContext(rootDir, moduleId, config) {
11428
11857
  const files = [];
11429
11858
  await traverseDirectory(indexPath, files, indexPath);
11430
11859
  return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
11431
- const relative5 = path24.relative(indexPath, f);
11432
- return relative5.replace(/\.json$/, "");
11860
+ const relative6 = path26.relative(indexPath, f);
11861
+ return relative6.replace(/\.json$/, "");
11433
11862
  });
11434
11863
  }
11435
11864
  };
11436
11865
  }
11437
11866
  async function traverseDirectory(dir, files, basePath) {
11438
11867
  try {
11439
- const entries = await fs9.readdir(dir, { withFileTypes: true });
11868
+ const entries = await fs10.readdir(dir, { withFileTypes: true });
11440
11869
  for (const entry of entries) {
11441
- const fullPath = path24.join(dir, entry.name);
11870
+ const fullPath = path26.join(dir, entry.name);
11442
11871
  if (entry.isDirectory()) {
11443
11872
  await traverseDirectory(fullPath, files, basePath);
11444
11873
  } else if (entry.isFile()) {
@@ -11450,7 +11879,7 @@ async function traverseDirectory(dir, files, basePath) {
11450
11879
  async function loadGlobalManifest2(rootDir, config) {
11451
11880
  const manifestPath = getGlobalManifestPath(rootDir, config);
11452
11881
  try {
11453
- const content = await fs9.readFile(manifestPath, "utf-8");
11882
+ const content = await fs10.readFile(manifestPath, "utf-8");
11454
11883
  return JSON.parse(content);
11455
11884
  } catch {
11456
11885
  return null;
@@ -11470,7 +11899,7 @@ function formatModuleName(moduleId) {
11470
11899
  return moduleId;
11471
11900
  }
11472
11901
  }
11473
- function formatSearchResults(results) {
11902
+ function formatSearchResults2(results) {
11474
11903
  if (results.length === 0) {
11475
11904
  return "No results found.";
11476
11905
  }
@@ -11489,6 +11918,9 @@ function formatSearchResults(results) {
11489
11918
  if (chunk.isExported) {
11490
11919
  output += " | exported";
11491
11920
  }
11921
+ if (result.context?.exactMatchFusion) {
11922
+ output += " | exact match";
11923
+ }
11492
11924
  output += `
11493
11925
  `;
11494
11926
  const lines = chunk.content.split(`
@@ -11503,12 +11935,84 @@ function formatSearchResults(results) {
11503
11935
  }
11504
11936
  return output;
11505
11937
  }
11938
+ function formatHybridSearchResults(hybridResults) {
11939
+ let output = "";
11940
+ if (hybridResults.exactMatches && hybridResults.exactMatches.totalMatches > 0) {
11941
+ const em = hybridResults.exactMatches;
11942
+ const showingCount = Math.min(em.files.length, 10);
11943
+ output += `┌─ Exact Matches `;
11944
+ if (em.truncated || em.files.length < em.totalFiles) {
11945
+ output += `(showing ${showingCount} of ${em.totalFiles} files, ${em.totalMatches} total matches)`;
11946
+ } else {
11947
+ output += `(${em.totalFiles} files, ${em.totalMatches} matches)`;
11948
+ }
11949
+ output += ` ─┐
11950
+ `;
11951
+ output += `│ Query: "${em.query}"
11952
+ `;
11953
+ output += `└─────────────────────────────────────────────────────────────────────┘
11954
+
11955
+ `;
11956
+ for (let i = 0;i < Math.min(em.files.length, 10); i++) {
11957
+ const file = em.files[i];
11958
+ output += ` ${i + 1}. ${file.filepath}`;
11959
+ if (file.matchCount > 1) {
11960
+ output += ` (${file.matchCount} matches)`;
11961
+ }
11962
+ output += `
11963
+ `;
11964
+ const firstOcc = file.occurrences[0];
11965
+ if (firstOcc) {
11966
+ if (firstOcc.contextBefore) {
11967
+ const beforeLine = firstOcc.contextBefore.substring(0, 76);
11968
+ output += ` ${(firstOcc.line - 1).toString().padStart(4)} │ ${beforeLine}${firstOcc.contextBefore.length > 76 ? "..." : ""}
11969
+ `;
11970
+ }
11971
+ const matchLine = firstOcc.lineContent.substring(0, 76);
11972
+ output += ` ► ${firstOcc.line.toString().padStart(4)} │ ${matchLine}${firstOcc.lineContent.length > 76 ? "..." : ""}
11973
+ `;
11974
+ if (firstOcc.contextAfter) {
11975
+ const afterLine = firstOcc.contextAfter.substring(0, 76);
11976
+ output += ` ${(firstOcc.line + 1).toString().padStart(4)} │ ${afterLine}${firstOcc.contextAfter.length > 76 ? "..." : ""}
11977
+ `;
11978
+ }
11979
+ }
11980
+ output += `
11981
+ `;
11982
+ }
11983
+ if (hybridResults.results.length > 0) {
11984
+ output += `
11985
+ `;
11986
+ }
11987
+ }
11988
+ if (hybridResults.results.length > 0) {
11989
+ if (hybridResults.exactMatches?.totalMatches) {
11990
+ output += `┌─ Semantic Results `;
11991
+ if (hybridResults.fusionApplied) {
11992
+ output += `(boosted by exact matches) `;
11993
+ }
11994
+ output += `─┐
11995
+ `;
11996
+ output += `└─────────────────────────────────────────────────────────────────────┘
11997
+
11998
+ `;
11999
+ }
12000
+ output += formatSearchResults2(hybridResults.results);
12001
+ } else if (!hybridResults.exactMatches?.totalMatches) {
12002
+ output += `No results found.
12003
+ `;
12004
+ }
12005
+ return output;
12006
+ }
11506
12007
  var init_search = __esm(() => {
11507
- init_esm();
12008
+ init_esm2();
11508
12009
  init_types();
11509
12010
  init_config2();
11510
12011
  init_registry();
11511
12012
  init_indexer();
12013
+ init_services();
12014
+ init_usecases();
12015
+ init_filesystem();
11512
12016
  });
11513
12017
 
11514
12018
  // src/app/cli/opencode/version-check.ts
@@ -11545,17 +12049,17 @@ function getInstallationMethod(openCodeVersion) {
11545
12049
  async function detectOpenCodeVersion() {
11546
12050
  try {
11547
12051
  const os4 = await import("os");
11548
- const fs10 = await import("fs/promises");
11549
- const path25 = await import("path");
12052
+ const fs11 = await import("fs/promises");
12053
+ const path27 = await import("path");
11550
12054
  const homeDir = os4.homedir();
11551
12055
  const possiblePaths = [
11552
- path25.join(homeDir, ".local", "share", "opencode", "package.json"),
11553
- path25.join(homeDir, ".config", "opencode", "package.json"),
11554
- path25.join(homeDir, ".npm", "global", "node_modules", "opencode", "package.json")
12056
+ path27.join(homeDir, ".local", "share", "opencode", "package.json"),
12057
+ path27.join(homeDir, ".config", "opencode", "package.json"),
12058
+ path27.join(homeDir, ".npm", "global", "node_modules", "opencode", "package.json")
11555
12059
  ];
11556
12060
  for (const packagePath of possiblePaths) {
11557
12061
  try {
11558
- const content = await fs10.readFile(packagePath, "utf-8");
12062
+ const content = await fs11.readFile(packagePath, "utf-8");
11559
12063
  const pkg = JSON.parse(content);
11560
12064
  if (pkg.version) {
11561
12065
  return pkg.version;
@@ -11564,7 +12068,7 @@ async function detectOpenCodeVersion() {
11564
12068
  }
11565
12069
  try {
11566
12070
  const { spawn } = await import("child_process");
11567
- return new Promise((resolve6) => {
12071
+ return new Promise((resolve7) => {
11568
12072
  const proc = spawn("opencode", ["--version"], { stdio: "pipe" });
11569
12073
  let version = "";
11570
12074
  proc.stdout.on("data", (data) => {
@@ -11573,14 +12077,14 @@ async function detectOpenCodeVersion() {
11573
12077
  proc.on("close", (code) => {
11574
12078
  if (code === 0) {
11575
12079
  const match2 = version.match(/v?(\d+\.\d+\.\d+)/);
11576
- resolve6(match2 ? match2[1] : null);
12080
+ resolve7(match2 ? match2[1] : null);
11577
12081
  } else {
11578
- resolve6(null);
12082
+ resolve7(null);
11579
12083
  }
11580
12084
  });
11581
12085
  setTimeout(() => {
11582
12086
  proc.kill();
11583
- resolve6(null);
12087
+ resolve7(null);
11584
12088
  }, 3000);
11585
12089
  });
11586
12090
  } catch {}
@@ -11594,11 +12098,11 @@ async function detectOpenCodeVersion() {
11594
12098
  async function installTool(options = {}) {
11595
12099
  const { logger, checkForOldSkill = true } = options;
11596
12100
  const os4 = await import("os");
11597
- const fs10 = await import("fs/promises");
11598
- const path25 = await import("path");
12101
+ const fs11 = await import("fs/promises");
12102
+ const path27 = await import("path");
11599
12103
  const homeDir = os4.homedir();
11600
- const toolDir = path25.join(homeDir, ".config", "opencode", "tool");
11601
- const toolPath = path25.join(toolDir, "raggrep.ts");
12104
+ const toolDir = path27.join(homeDir, ".config", "opencode", "tool");
12105
+ const toolPath = path27.join(toolDir, "raggrep.ts");
11602
12106
  let removedOldSkill = false;
11603
12107
  const toolContent = `import { tool } from "@opencode-ai/plugin";
11604
12108
 
@@ -11681,11 +12185,11 @@ Please install raggrep globally:
11681
12185
  `;
11682
12186
  try {
11683
12187
  if (checkForOldSkill) {
11684
- const oldSkillDir = path25.join(homeDir, ".config", "opencode", "skill", "raggrep");
11685
- const oldSkillPath = path25.join(oldSkillDir, "SKILL.md");
12188
+ const oldSkillDir = path27.join(homeDir, ".config", "opencode", "skill", "raggrep");
12189
+ const oldSkillPath = path27.join(oldSkillDir, "SKILL.md");
11686
12190
  let oldSkillExists = false;
11687
12191
  try {
11688
- await fs10.access(oldSkillPath);
12192
+ await fs11.access(oldSkillPath);
11689
12193
  oldSkillExists = true;
11690
12194
  } catch {}
11691
12195
  if (oldSkillExists) {
@@ -11703,18 +12207,18 @@ Please install raggrep globally:
11703
12207
  input: process.stdin,
11704
12208
  output: process.stdout
11705
12209
  });
11706
- const answer = await new Promise((resolve6) => {
11707
- rl.question("Remove the existing skill and install tool? (Y/n): ", resolve6);
12210
+ const answer = await new Promise((resolve7) => {
12211
+ rl.question("Remove the existing skill and install tool? (Y/n): ", resolve7);
11708
12212
  });
11709
12213
  rl.close();
11710
12214
  const shouldDelete = answer.toLowerCase() !== "n";
11711
12215
  if (shouldDelete) {
11712
12216
  try {
11713
- await fs10.unlink(oldSkillPath);
11714
- const skillDirContents = await fs10.readdir(oldSkillDir);
12217
+ await fs11.unlink(oldSkillPath);
12218
+ const skillDirContents = await fs11.readdir(oldSkillDir);
11715
12219
  if (skillDirContents.length === 0) {
11716
12220
  try {
11717
- await fs10.rmdir(oldSkillDir);
12221
+ await fs11.rmdir(oldSkillDir);
11718
12222
  console.log("✓ Removed old skill directory.");
11719
12223
  } catch (rmdirError) {
11720
12224
  console.log("✓ Removed old skill file. (Directory not empty or other error)");
@@ -11751,8 +12255,8 @@ Please install raggrep globally:
11751
12255
  }
11752
12256
  }
11753
12257
  }
11754
- await fs10.mkdir(toolDir, { recursive: true });
11755
- await fs10.writeFile(toolPath, toolContent, "utf-8");
12258
+ await fs11.mkdir(toolDir, { recursive: true });
12259
+ await fs11.writeFile(toolPath, toolContent, "utf-8");
11756
12260
  const message = `Installed raggrep tool for OpenCode.
11757
12261
  Location: ${toolPath}
11758
12262
 
@@ -11786,11 +12290,11 @@ The raggrep tool is now available in OpenCode.`;
11786
12290
  async function installSkill(options = {}) {
11787
12291
  const { logger, checkForOldTool = true } = options;
11788
12292
  const os4 = await import("os");
11789
- const fs10 = await import("fs/promises");
11790
- const path25 = await import("path");
12293
+ const fs11 = await import("fs/promises");
12294
+ const path27 = await import("path");
11791
12295
  const homeDir = os4.homedir();
11792
- const skillDir = path25.join(homeDir, ".config", "opencode", "skill", "raggrep");
11793
- const skillPath = path25.join(skillDir, "SKILL.md");
12296
+ const skillDir = path27.join(homeDir, ".config", "opencode", "skill", "raggrep");
12297
+ const skillPath = path27.join(skillDir, "SKILL.md");
11794
12298
  const skillContent = `---
11795
12299
  name: raggrep
11796
12300
  description: AST-powered semantic code search that understands intent, not just text. Superior to grep/rg - finds functions, classes, and logic even when keywords differ. Saves 10x tool calls by searching the actual code structure.
@@ -11902,11 +12406,11 @@ Instead of using grep/rg or manually reading files:
11902
12406
  let removedOldTool = false;
11903
12407
  try {
11904
12408
  if (checkForOldTool) {
11905
- const oldToolDir = path25.join(homeDir, ".config", "opencode", "tool");
11906
- const oldToolPath = path25.join(oldToolDir, "raggrep.ts");
12409
+ const oldToolDir = path27.join(homeDir, ".config", "opencode", "tool");
12410
+ const oldToolPath = path27.join(oldToolDir, "raggrep.ts");
11907
12411
  let oldToolExists = false;
11908
12412
  try {
11909
- await fs10.access(oldToolPath);
12413
+ await fs11.access(oldToolPath);
11910
12414
  oldToolExists = true;
11911
12415
  } catch {}
11912
12416
  if (oldToolExists) {
@@ -11924,18 +12428,18 @@ Instead of using grep/rg or manually reading files:
11924
12428
  input: process.stdin,
11925
12429
  output: process.stdout
11926
12430
  });
11927
- const answer = await new Promise((resolve6) => {
11928
- rl.question("Do you want to remove the old tool file? (Y/n): ", resolve6);
12431
+ const answer = await new Promise((resolve7) => {
12432
+ rl.question("Do you want to remove the old tool file? (Y/n): ", resolve7);
11929
12433
  });
11930
12434
  rl.close();
11931
12435
  const shouldDelete = answer.toLowerCase() !== "n";
11932
12436
  if (shouldDelete) {
11933
12437
  try {
11934
- await fs10.unlink(oldToolPath);
11935
- const toolDirContents = await fs10.readdir(oldToolDir);
12438
+ await fs11.unlink(oldToolPath);
12439
+ const toolDirContents = await fs11.readdir(oldToolDir);
11936
12440
  if (toolDirContents.length === 0) {
11937
12441
  try {
11938
- await fs10.rmdir(oldToolDir);
12442
+ await fs11.rmdir(oldToolDir);
11939
12443
  console.log("✓ Removed old tool directory.");
11940
12444
  } catch (rmdirError) {
11941
12445
  console.log("✓ Removed old tool file. (Directory not empty or other error)");
@@ -11968,8 +12472,8 @@ Instead of using grep/rg or manually reading files:
11968
12472
  }
11969
12473
  }
11970
12474
  }
11971
- await fs10.mkdir(skillDir, { recursive: true });
11972
- await fs10.writeFile(skillPath, skillContent, "utf-8");
12475
+ await fs11.mkdir(skillDir, { recursive: true });
12476
+ await fs11.writeFile(skillPath, skillContent, "utf-8");
11973
12477
  const message = `Installed raggrep skill for OpenCode.
11974
12478
  Location: ${skillPath}
11975
12479
 
@@ -12022,7 +12526,7 @@ init_logger();
12022
12526
  // package.json
12023
12527
  var package_default = {
12024
12528
  name: "raggrep",
12025
- version: "0.14.2",
12529
+ version: "0.16.0",
12026
12530
  description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
12027
12531
  type: "module",
12028
12532
  main: "./dist/index.js",
@@ -12081,6 +12585,7 @@ var package_default = {
12081
12585
  chokidar: "^5.0.0",
12082
12586
  fdir: "^6.5.0",
12083
12587
  glob: "^10.0.0",
12588
+ minimatch: "^10.1.1",
12084
12589
  typescript: "^5.0.0",
12085
12590
  "web-tree-sitter": "^0.26.3"
12086
12591
  },
@@ -12227,13 +12732,13 @@ Examples:
12227
12732
  `);
12228
12733
  process.exit(0);
12229
12734
  }
12230
- const { indexDirectory: indexDirectory2, watchDirectory: watchDirectory2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
12735
+ const { indexDirectory: indexDirectory3, watchDirectory: watchDirectory2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
12231
12736
  const logger = createInlineLogger({ verbose: flags.verbose });
12232
12737
  console.log("RAGgrep Indexer");
12233
12738
  console.log(`================
12234
12739
  `);
12235
12740
  try {
12236
- const results = await indexDirectory2(process.cwd(), {
12741
+ const results = await indexDirectory3(process.cwd(), {
12237
12742
  model: flags.model,
12238
12743
  verbose: flags.verbose,
12239
12744
  concurrency: flags.concurrency,
@@ -12338,7 +12843,6 @@ Examples:
12338
12843
  `);
12339
12844
  process.exit(0);
12340
12845
  }
12341
- const { search: search2, formatSearchResults: formatSearchResults2 } = await Promise.resolve().then(() => (init_search(), exports_search));
12342
12846
  const { ensureIndexFresh: ensureIndexFresh2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
12343
12847
  const query = flags.remaining[0];
12344
12848
  if (!query) {
@@ -12388,14 +12892,15 @@ Examples:
12388
12892
  `);
12389
12893
  }
12390
12894
  const filePatterns = flags.fileType ? [`*.${flags.fileType}`] : undefined;
12391
- const results = await search2(process.cwd(), query, {
12895
+ const { hybridSearch: hybridSearch2, formatHybridSearchResults: formatHybridSearchResults2 } = await Promise.resolve().then(() => (init_search(), exports_search));
12896
+ const hybridResults = await hybridSearch2(process.cwd(), query, {
12392
12897
  topK: flags.topK ?? 10,
12393
12898
  minScore: flags.minScore,
12394
12899
  filePatterns,
12395
12900
  pathFilter: flags.pathFilter,
12396
12901
  ensureFresh: false
12397
12902
  });
12398
- console.log(formatSearchResults2(results));
12903
+ console.log(formatHybridSearchResults2(hybridResults));
12399
12904
  } catch (error) {
12400
12905
  console.error("Error during search:", error);
12401
12906
  process.exit(1);
@@ -12618,4 +13123,4 @@ Run 'raggrep <command> --help' for more information.
12618
13123
  }
12619
13124
  main();
12620
13125
 
12621
- //# debugId=71C6E22A4D7E91CE64756E2164756E21
13126
+ //# debugId=8BF97097099E1F1364756E2164756E21