raggrep 0.14.2 → 0.15.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 (36) hide show
  1. package/dist/app/search/index.d.ts +16 -0
  2. package/dist/cli/main.js +714 -260
  3. package/dist/cli/main.js.map +20 -15
  4. package/dist/domain/entities/index.d.ts +1 -1
  5. package/dist/domain/entities/searchResult.d.ts +54 -0
  6. package/dist/domain/ports/filesystem.d.ts +3 -1
  7. package/dist/domain/services/index.d.ts +1 -0
  8. package/dist/domain/services/simpleSearch.d.ts +75 -0
  9. package/dist/domain/usecases/exactSearch.d.ts +53 -0
  10. package/dist/domain/usecases/index.d.ts +1 -0
  11. package/dist/index.d.ts +38 -3
  12. package/dist/index.js +673 -237
  13. package/dist/index.js.map +18 -15
  14. package/dist/types.d.ts +1 -1
  15. package/package.json +2 -1
  16. package/dist/domain/services/bm25.test.d.ts +0 -4
  17. package/dist/domain/services/configValidator.test.d.ts +0 -1
  18. package/dist/domain/services/conventions/conventions.test.d.ts +0 -4
  19. package/dist/domain/services/introspection.test.d.ts +0 -4
  20. package/dist/domain/services/jsonPathExtractor.test.d.ts +0 -4
  21. package/dist/domain/services/lexicon.test.d.ts +0 -6
  22. package/dist/domain/services/literalExtractor.test.d.ts +0 -6
  23. package/dist/domain/services/phraseMatch.test.d.ts +0 -4
  24. package/dist/domain/services/queryLiteralParser.test.d.ts +0 -7
  25. package/dist/infrastructure/embeddings/embeddings.test.d.ts +0 -4
  26. package/dist/infrastructure/parsing/parsing.test.d.ts +0 -10
  27. package/dist/modules/core/symbols.test.d.ts +0 -4
  28. package/dist/modules/language/go/index.test.d.ts +0 -1
  29. package/dist/modules/language/rust/index.test.d.ts +0 -1
  30. package/dist/modules/language/typescript/parseCode.test.d.ts +0 -4
  31. package/dist/tests/integration.test.d.ts +0 -9
  32. package/dist/tests/ranking.test.d.ts +0 -12
  33. package/dist/tests/simulation-phrase-matching.test.d.ts +0 -14
  34. package/dist/tests/simulation-vocabulary.test.d.ts +0 -17
  35. package/dist/tests/vocabulary-scoring.test.d.ts +0 -16
  36. 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,153 @@ 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
+
4539
4685
  // src/domain/services/index.ts
4540
4686
  var init_services = __esm(() => {
4541
4687
  init_keywords();
@@ -4547,6 +4693,7 @@ var init_services = __esm(() => {
4547
4693
  init_introspection();
4548
4694
  init_configValidator();
4549
4695
  init_phraseMatch();
4696
+ init_simpleSearch();
4550
4697
  });
4551
4698
 
4552
4699
  // src/modules/language/typescript/parseCode.ts
@@ -9946,210 +10093,215 @@ var init_indexer = __esm(() => {
9946
10093
  DEFAULT_CONCURRENCY = getOptimalConcurrency();
9947
10094
  });
9948
10095
 
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);
10096
+ // node_modules/@isaacs/balanced-match/dist/esm/index.js
10097
+ var balanced = (a, b, str) => {
10098
+ const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
10099
+ const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
10100
+ const r = ma !== null && mb != null && range(ma, mb, str);
10101
+ return r && {
10102
+ start: r[0],
10103
+ end: r[1],
10104
+ pre: str.slice(0, r[0]),
10105
+ body: str.slice(r[0] + ma.length, r[1]),
10106
+ post: str.slice(r[1] + mb.length)
10107
+ };
10108
+ }, maybeMatch = (reg, str) => {
10109
+ const m = str.match(reg);
10110
+ return m ? m[0] : null;
10111
+ }, range = (a, b, str) => {
10112
+ let begs, beg, left, right = undefined, result;
10113
+ let ai = str.indexOf(a);
10114
+ let bi = str.indexOf(b, ai + 1);
10115
+ let i = ai;
10116
+ if (ai >= 0 && bi > 0) {
10117
+ if (a === b) {
10118
+ return [ai, bi];
10119
+ }
10120
+ begs = [];
10121
+ left = str.length;
10122
+ while (i >= 0 && !result) {
10123
+ if (i === ai) {
10124
+ begs.push(i);
10125
+ ai = str.indexOf(a, i + 1);
10126
+ } else if (begs.length === 1) {
10127
+ const r = begs.pop();
10128
+ if (r !== undefined)
10129
+ result = [r, bi];
10130
+ } else {
10131
+ beg = begs.pop();
10132
+ if (beg !== undefined && beg < left) {
10133
+ left = beg;
10134
+ right = bi;
9995
10135
  }
9996
- i = ai < bi && ai >= 0 ? ai : bi;
9997
- }
9998
- if (begs.length) {
9999
- result = [left, right];
10136
+ bi = str.indexOf(b, i + 1);
10000
10137
  }
10138
+ i = ai < bi && ai >= 0 ? ai : bi;
10001
10139
  }
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);
10140
+ if (begs.length && right !== undefined) {
10141
+ result = [left, right];
10049
10142
  }
10050
- return expand(escapeBraces(str), true).map(unescapeBraces);
10051
10143
  }
10052
- function embrace(str) {
10053
- return "{" + str + "}";
10054
- }
10055
- function isPadded(el) {
10056
- return /^-?0\d/.test(el);
10057
- }
10058
- function lte(i, y) {
10059
- return i <= y;
10144
+ return result;
10145
+ };
10146
+
10147
+ // node_modules/@isaacs/brace-expansion/dist/esm/index.js
10148
+ function numeric(str) {
10149
+ return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
10150
+ }
10151
+ function escapeBraces(str) {
10152
+ return str.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
10153
+ }
10154
+ function unescapeBraces(str) {
10155
+ return str.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
10156
+ }
10157
+ function parseCommaParts(str) {
10158
+ if (!str) {
10159
+ return [""];
10060
10160
  }
10061
- function gte(i, y) {
10062
- return i >= y;
10161
+ const parts = [];
10162
+ const m = balanced("{", "}", str);
10163
+ if (!m) {
10164
+ return str.split(",");
10165
+ }
10166
+ const { pre, body, post } = m;
10167
+ const p = pre.split(",");
10168
+ p[p.length - 1] += "{" + body + "}";
10169
+ const postParts = parseCommaParts(post);
10170
+ if (post.length) {
10171
+ p[p.length - 1] += postParts.shift();
10172
+ p.push.apply(p, postParts);
10173
+ }
10174
+ parts.push.apply(parts, p);
10175
+ return parts;
10176
+ }
10177
+ function expand(str) {
10178
+ if (!str) {
10179
+ return [];
10063
10180
  }
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);
10181
+ if (str.slice(0, 2) === "{}") {
10182
+ str = "\\{\\}" + str.slice(2);
10183
+ }
10184
+ return expand_(escapeBraces(str), true).map(unescapeBraces);
10185
+ }
10186
+ function embrace(str) {
10187
+ return "{" + str + "}";
10188
+ }
10189
+ function isPadded(el) {
10190
+ return /^-?0\d/.test(el);
10191
+ }
10192
+ function lte(i, y) {
10193
+ return i <= y;
10194
+ }
10195
+ function gte(i, y) {
10196
+ return i >= y;
10197
+ }
10198
+ function expand_(str, isTop) {
10199
+ const expansions = [];
10200
+ const m = balanced("{", "}", str);
10201
+ if (!m)
10202
+ return [str];
10203
+ const pre = m.pre;
10204
+ const post = m.post.length ? expand_(m.post, false) : [""];
10205
+ if (/\$$/.test(m.pre)) {
10206
+ for (let k = 0;k < post.length; k++) {
10207
+ const expansion = pre + "{" + m.body + "}" + post[k];
10208
+ expansions.push(expansion);
10209
+ }
10210
+ } else {
10211
+ const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
10212
+ const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
10213
+ const isSequence = isNumericSequence || isAlphaSequence;
10214
+ const isOptions = m.body.indexOf(",") >= 0;
10215
+ if (!isSequence && !isOptions) {
10216
+ if (m.post.match(/,(?!,).*\}/)) {
10217
+ str = m.pre + "{" + m.body + escClose + m.post;
10218
+ return expand_(str);
10075
10219
  }
10220
+ return [str];
10221
+ }
10222
+ let n;
10223
+ if (isSequence) {
10224
+ n = m.body.split(/\.\./);
10076
10225
  } 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);
10226
+ n = parseCommaParts(m.body);
10227
+ if (n.length === 1 && n[0] !== undefined) {
10228
+ n = expand_(n[0], false).map(embrace);
10093
10229
  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
- });
10230
+ return post.map((p) => m.pre + n[0] + p);
10231
+ }
10232
+ }
10233
+ }
10234
+ let N;
10235
+ if (isSequence && n[0] !== undefined && n[1] !== undefined) {
10236
+ const x = numeric(n[0]);
10237
+ const y = numeric(n[1]);
10238
+ const width = Math.max(n[0].length, n[1].length);
10239
+ let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
10240
+ let test = lte;
10241
+ const reverse = y < x;
10242
+ if (reverse) {
10243
+ incr *= -1;
10244
+ test = gte;
10245
+ }
10246
+ const pad = n.some(isPadded);
10247
+ N = [];
10248
+ for (let i = x;test(i, y); i += incr) {
10249
+ let c;
10250
+ if (isAlphaSequence) {
10251
+ c = String.fromCharCode(i);
10252
+ if (c === "\\") {
10253
+ c = "";
10099
10254
  }
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;
10255
+ } else {
10256
+ c = String(i);
10257
+ if (pad) {
10258
+ const need = width - c.length;
10259
+ if (need > 0) {
10260
+ const z = new Array(need + 1).join("0");
10261
+ if (i < 0) {
10262
+ c = "-" + z + c.slice(1);
10263
+ } else {
10264
+ c = z + c;
10132
10265
  }
10133
10266
  }
10134
10267
  }
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
10268
  }
10269
+ N.push(c);
10270
+ }
10271
+ } else {
10272
+ N = [];
10273
+ for (let j = 0;j < n.length; j++) {
10274
+ N.push.apply(N, expand_(n[j], false));
10142
10275
  }
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);
10276
+ }
10277
+ for (let j = 0;j < N.length; j++) {
10278
+ for (let k = 0;k < post.length; k++) {
10279
+ const expansion = pre + N[j] + post[k];
10280
+ if (!isTop || isSequence || expansion) {
10281
+ expansions.push(expansion);
10148
10282
  }
10149
10283
  }
10150
10284
  }
10151
- return expansions;
10152
10285
  }
10286
+ return expansions;
10287
+ }
10288
+ var escSlash, escOpen, escClose, escComma, escPeriod, escSlashPattern, escOpenPattern, escClosePattern, escCommaPattern, escPeriodPattern, slashPattern, openPattern, closePattern, commaPattern, periodPattern;
10289
+ var init_esm = __esm(() => {
10290
+ escSlash = "\x00SLASH" + Math.random() + "\x00";
10291
+ escOpen = "\x00OPEN" + Math.random() + "\x00";
10292
+ escClose = "\x00CLOSE" + Math.random() + "\x00";
10293
+ escComma = "\x00COMMA" + Math.random() + "\x00";
10294
+ escPeriod = "\x00PERIOD" + Math.random() + "\x00";
10295
+ escSlashPattern = new RegExp(escSlash, "g");
10296
+ escOpenPattern = new RegExp(escOpen, "g");
10297
+ escClosePattern = new RegExp(escClose, "g");
10298
+ escCommaPattern = new RegExp(escComma, "g");
10299
+ escPeriodPattern = new RegExp(escPeriod, "g");
10300
+ slashPattern = /\\\\/g;
10301
+ openPattern = /\\{/g;
10302
+ closePattern = /\\}/g;
10303
+ commaPattern = /\\,/g;
10304
+ periodPattern = /\\./g;
10153
10305
  });
10154
10306
 
10155
10307
  // node_modules/minimatch/dist/esm/assert-valid-pattern.js
@@ -10275,8 +10427,11 @@ var init_brace_expressions = __esm(() => {
10275
10427
  });
10276
10428
 
10277
10429
  // 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");
10430
+ var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
10431
+ if (magicalBraces) {
10432
+ return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
10433
+ }
10434
+ return windowsPathsNoEscape ? s.replace(/\[([^\/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\{}])\]/g, "$1$2").replace(/\\([^\/{}])/g, "$1");
10280
10435
  };
10281
10436
 
10282
10437
  // node_modules/minimatch/dist/esm/ast.js
@@ -10545,7 +10700,7 @@ class AST {
10545
10700
  if (this.#root === this)
10546
10701
  this.#fillNegs();
10547
10702
  if (!this.type) {
10548
- const noEmpty = this.isStart() && this.isEnd();
10703
+ const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
10549
10704
  const src = this.#parts.map((p) => {
10550
10705
  const [re, _, hasMagic, uflag] = typeof p === "string" ? AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
10551
10706
  this.#hasMagic = this.#hasMagic || hasMagic;
@@ -10647,10 +10802,7 @@ class AST {
10647
10802
  }
10648
10803
  }
10649
10804
  if (c === "*") {
10650
- if (noEmpty && glob === "*")
10651
- re += starNoEmpty;
10652
- else
10653
- re += star;
10805
+ re += noEmpty && glob === "*" ? starNoEmpty : star;
10654
10806
  hasMagic = true;
10655
10807
  continue;
10656
10808
  }
@@ -10676,7 +10828,10 @@ var init_ast = __esm(() => {
10676
10828
  });
10677
10829
 
10678
10830
  // node_modules/minimatch/dist/esm/escape.js
10679
- var escape = (s, { windowsPathsNoEscape = false } = {}) => {
10831
+ var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
10832
+ if (magicalBraces) {
10833
+ return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
10834
+ }
10680
10835
  return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
10681
10836
  };
10682
10837
 
@@ -11150,16 +11305,27 @@ globstar while`, file, fr, pattern, pr, swallowee);
11150
11305
  pp[i] = twoStar;
11151
11306
  }
11152
11307
  } else if (next === undefined) {
11153
- pp[i - 1] = prev + "(?:\\/|" + twoStar + ")?";
11308
+ pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + ")?";
11154
11309
  } else if (next !== GLOBSTAR) {
11155
11310
  pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next;
11156
11311
  pp[i + 1] = GLOBSTAR;
11157
11312
  }
11158
11313
  });
11159
- return pp.filter((p) => p !== GLOBSTAR).join("/");
11314
+ const filtered = pp.filter((p) => p !== GLOBSTAR);
11315
+ if (this.partial && filtered.length >= 1) {
11316
+ const prefixes = [];
11317
+ for (let i = 1;i <= filtered.length; i++) {
11318
+ prefixes.push(filtered.slice(0, i).join("/"));
11319
+ }
11320
+ return "(?:" + prefixes.join("|") + ")";
11321
+ }
11322
+ return filtered.join("/");
11160
11323
  }).join("|");
11161
11324
  const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
11162
11325
  re = "^" + open + re + close + "$";
11326
+ if (this.partial) {
11327
+ re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
11328
+ }
11163
11329
  if (this.negate)
11164
11330
  re = "^(?!" + re + ").+$";
11165
11331
  try {
@@ -11226,7 +11392,7 @@ globstar while`, file, fr, pattern, pr, swallowee);
11226
11392
  return minimatch.defaults(def).Minimatch;
11227
11393
  }
11228
11394
  }
11229
- var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11395
+ var minimatch = (p, pattern, options = {}) => {
11230
11396
  assertValidPattern(pattern);
11231
11397
  if (!options.nocomment && pattern.charAt(0) === "#") {
11232
11398
  return false;
@@ -11300,7 +11466,7 @@ var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11300
11466
  if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
11301
11467
  return [pattern];
11302
11468
  }
11303
- return import_brace_expansion.default(pattern);
11469
+ return expand(pattern);
11304
11470
  }, makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe(), match = (list, pattern, options = {}) => {
11305
11471
  const mm = new Minimatch(pattern, options);
11306
11472
  list = list.filter((f) => mm.match(f));
@@ -11309,11 +11475,11 @@ var import_brace_expansion, minimatch = (p, pattern, options = {}) => {
11309
11475
  }
11310
11476
  return list;
11311
11477
  }, globMagic, regExpEscape2 = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
11312
- var init_esm = __esm(() => {
11478
+ var init_esm2 = __esm(() => {
11479
+ init_esm();
11313
11480
  init_assert_valid_pattern();
11314
11481
  init_ast();
11315
11482
  init_ast();
11316
- import_brace_expansion = __toESM(require_brace_expansion(), 1);
11317
11483
  starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
11318
11484
  starDotStarRE = /^\*+\.\*+$/;
11319
11485
  dotStarRE = /^\.\*+$/;
@@ -11345,17 +11511,196 @@ var init_esm = __esm(() => {
11345
11511
  var init_types = __esm(() => {
11346
11512
  init_entities();
11347
11513
  });
11514
+ // src/domain/usecases/exactSearch.ts
11515
+ function matchesPathFilter(relativePath, filters, matchFn) {
11516
+ const normalizedPath = relativePath.replace(/\\/g, "/");
11517
+ for (const filter2 of filters) {
11518
+ const normalizedFilter = filter2.replace(/\\/g, "/").replace(/^\//, "").replace(/\/$/, "");
11519
+ const isGlobPattern = /[*?[\]{}!]/.test(normalizedFilter);
11520
+ if (isGlobPattern) {
11521
+ const pattern = normalizedFilter.startsWith("**/") ? normalizedFilter : `**/${normalizedFilter}`;
11522
+ if (matchFn(normalizedPath, pattern)) {
11523
+ return true;
11524
+ }
11525
+ } else {
11526
+ if (normalizedPath.startsWith(normalizedFilter + "/") || normalizedPath === normalizedFilter || normalizedPath.includes("/" + normalizedFilter + "/")) {
11527
+ return true;
11528
+ }
11529
+ }
11530
+ }
11531
+ return false;
11532
+ }
11533
+ async function executeExactSearch(fs9, options, matchFn) {
11534
+ const {
11535
+ rootDir,
11536
+ literal,
11537
+ pathFilter = [],
11538
+ maxFiles = 20,
11539
+ maxOccurrencesPerFile = 5,
11540
+ caseInsensitive = false
11541
+ } = options;
11542
+ const files = new Map;
11543
+ async function walkDir(dir, baseDir) {
11544
+ try {
11545
+ const entries = await fs9.readDir(dir);
11546
+ for (const entry of entries) {
11547
+ const fullPath = fs9.join(dir, entry);
11548
+ const relativePath = fs9.relative(baseDir, fullPath);
11549
+ let isDirectory = false;
11550
+ try {
11551
+ const stats = await fs9.getStats(fullPath);
11552
+ isDirectory = stats.isDirectory ?? false;
11553
+ } catch {
11554
+ continue;
11555
+ }
11556
+ if (isDirectory) {
11557
+ if (DEFAULT_IGNORED_DIRS.includes(entry)) {
11558
+ continue;
11559
+ }
11560
+ await walkDir(fullPath, baseDir);
11561
+ } else {
11562
+ if (pathFilter.length > 0) {
11563
+ if (!matchesPathFilter(relativePath, pathFilter, matchFn)) {
11564
+ continue;
11565
+ }
11566
+ }
11567
+ try {
11568
+ const content = await fs9.readFile(fullPath);
11569
+ if (isSearchableContent(content, fullPath)) {
11570
+ files.set(relativePath, content);
11571
+ }
11572
+ } catch {}
11573
+ }
11574
+ }
11575
+ } catch {}
11576
+ }
11577
+ await walkDir(rootDir, rootDir);
11578
+ return searchFiles(files, literal, {
11579
+ maxFiles,
11580
+ maxOccurrencesPerFile,
11581
+ caseInsensitive
11582
+ });
11583
+ }
11584
+ var DEFAULT_IGNORED_DIRS;
11585
+ var init_exactSearch = __esm(() => {
11586
+ init_simpleSearch();
11587
+ DEFAULT_IGNORED_DIRS = [
11588
+ "node_modules",
11589
+ ".git",
11590
+ ".raggrep",
11591
+ "dist",
11592
+ "build",
11593
+ ".next",
11594
+ "__pycache__",
11595
+ ".venv",
11596
+ "venv"
11597
+ ];
11598
+ });
11599
+
11600
+ // src/domain/usecases/index.ts
11601
+ var init_usecases = __esm(() => {
11602
+ init_exactSearch();
11603
+ });
11604
+
11605
+ // src/infrastructure/filesystem/nodeFileSystem.ts
11606
+ import * as fs9 from "fs/promises";
11607
+ import * as path24 from "path";
11608
+ import { glob } from "glob";
11609
+
11610
+ class NodeFileSystem {
11611
+ async readFile(filepath) {
11612
+ return fs9.readFile(filepath, "utf-8");
11613
+ }
11614
+ async writeFile(filepath, content) {
11615
+ await fs9.mkdir(path24.dirname(filepath), { recursive: true });
11616
+ await fs9.writeFile(filepath, content, "utf-8");
11617
+ }
11618
+ async deleteFile(filepath) {
11619
+ try {
11620
+ await fs9.unlink(filepath);
11621
+ } catch (error) {
11622
+ if (error.code !== "ENOENT") {
11623
+ throw error;
11624
+ }
11625
+ }
11626
+ }
11627
+ async getStats(filepath) {
11628
+ const stats = await fs9.stat(filepath);
11629
+ return {
11630
+ lastModified: stats.mtime.toISOString(),
11631
+ size: stats.isDirectory() ? undefined : stats.size,
11632
+ isDirectory: stats.isDirectory()
11633
+ };
11634
+ }
11635
+ async exists(filepath) {
11636
+ try {
11637
+ await fs9.access(filepath);
11638
+ return true;
11639
+ } catch {
11640
+ return false;
11641
+ }
11642
+ }
11643
+ async mkdir(dirpath) {
11644
+ await fs9.mkdir(dirpath, { recursive: true });
11645
+ }
11646
+ async readDir(dirpath) {
11647
+ return fs9.readdir(dirpath);
11648
+ }
11649
+ async findFiles(rootDir, patterns, ignore) {
11650
+ const ignorePatterns = ignore.map((p) => `**/${p}/**`);
11651
+ const files = [];
11652
+ for (const pattern of patterns) {
11653
+ const matches = await glob(pattern, {
11654
+ cwd: rootDir,
11655
+ absolute: true,
11656
+ ignore: ignorePatterns
11657
+ });
11658
+ files.push(...matches);
11659
+ }
11660
+ return [...new Set(files)];
11661
+ }
11662
+ join(...segments) {
11663
+ return path24.join(...segments);
11664
+ }
11665
+ relative(from, to) {
11666
+ return path24.relative(from, to);
11667
+ }
11668
+ resolve(...segments) {
11669
+ return path24.resolve(...segments);
11670
+ }
11671
+ dirname(filepath) {
11672
+ return path24.dirname(filepath);
11673
+ }
11674
+ extname(filepath) {
11675
+ return path24.extname(filepath);
11676
+ }
11677
+ }
11678
+ var nodeFileSystem;
11679
+ var init_nodeFileSystem = __esm(() => {
11680
+ nodeFileSystem = new NodeFileSystem;
11681
+ });
11682
+
11683
+ // src/infrastructure/filesystem/index.ts
11684
+ var init_filesystem = __esm(() => {
11685
+ init_nodeFileSystem();
11686
+ });
11348
11687
 
11349
11688
  // src/app/search/index.ts
11350
11689
  var exports_search = {};
11351
11690
  __export(exports_search, {
11352
11691
  search: () => search,
11353
- formatSearchResults: () => formatSearchResults
11692
+ hybridSearch: () => hybridSearch,
11693
+ formatSearchResults: () => formatSearchResults2,
11694
+ formatHybridSearchResults: () => formatHybridSearchResults
11354
11695
  });
11355
- import * as fs9 from "fs/promises";
11356
- import * as path24 from "path";
11696
+ import * as fs10 from "fs/promises";
11697
+ import * as path25 from "path";
11357
11698
  async function search(rootDir, query, options = {}) {
11358
- rootDir = path24.resolve(rootDir);
11699
+ const hybridResults = await hybridSearch(rootDir, query, options);
11700
+ return hybridResults.results;
11701
+ }
11702
+ async function hybridSearch(rootDir, query, options = {}) {
11703
+ rootDir = path25.resolve(rootDir);
11359
11704
  const ensureFresh = options.ensureFresh ?? DEFAULT_SEARCH_OPTIONS.ensureFresh;
11360
11705
  if (ensureFresh) {
11361
11706
  await ensureIndexFresh(rootDir, { quiet: true });
@@ -11366,7 +11711,7 @@ async function search(rootDir, query, options = {}) {
11366
11711
  const globalManifest = await loadGlobalManifest2(rootDir, config);
11367
11712
  if (!globalManifest || globalManifest.modules.length === 0) {
11368
11713
  console.log('No index found. Run "raggrep index" first.');
11369
- return [];
11714
+ return { results: [], fusionApplied: false };
11370
11715
  }
11371
11716
  const modulesToSearch = [];
11372
11717
  for (const moduleId of globalManifest.modules) {
@@ -11381,7 +11726,7 @@ async function search(rootDir, query, options = {}) {
11381
11726
  }
11382
11727
  if (modulesToSearch.length === 0) {
11383
11728
  console.log("No enabled modules with indexes found.");
11384
- return [];
11729
+ return { results: [], fusionApplied: false };
11385
11730
  }
11386
11731
  const allResults = [];
11387
11732
  for (const module of modulesToSearch) {
@@ -11405,9 +11750,42 @@ async function search(rootDir, query, options = {}) {
11405
11750
  });
11406
11751
  });
11407
11752
  }
11753
+ let exactMatches;
11754
+ let fusionApplied = false;
11755
+ if (isIdentifierQuery(query)) {
11756
+ const literal = extractSearchLiteral(query);
11757
+ exactMatches = await performExactSearch(rootDir, literal, config, options);
11758
+ if (exactMatches && exactMatches.totalMatches > 0) {
11759
+ const exactMatchFilepaths = new Set(exactMatches.files.map((f) => f.filepath));
11760
+ for (const result of filteredResults) {
11761
+ if (exactMatchFilepaths.has(result.filepath)) {
11762
+ result.score *= 1.5;
11763
+ if (!result.context)
11764
+ result.context = {};
11765
+ result.context.exactMatchFusion = true;
11766
+ fusionApplied = true;
11767
+ }
11768
+ }
11769
+ }
11770
+ }
11408
11771
  filteredResults.sort((a, b) => b.score - a.score);
11409
11772
  const topK = options.topK ?? 10;
11410
- return filteredResults.slice(0, topK);
11773
+ return {
11774
+ results: filteredResults.slice(0, topK),
11775
+ exactMatches,
11776
+ fusionApplied
11777
+ };
11778
+ }
11779
+ async function performExactSearch(rootDir, literal, config, options) {
11780
+ const fs11 = new NodeFileSystem;
11781
+ return executeExactSearch(fs11, {
11782
+ rootDir,
11783
+ literal,
11784
+ pathFilter: options.pathFilter,
11785
+ maxFiles: 20,
11786
+ maxOccurrencesPerFile: 5,
11787
+ caseInsensitive: false
11788
+ }, (path26, pattern) => minimatch(path26, pattern, { matchBase: true }));
11411
11789
  }
11412
11790
  function createSearchContext(rootDir, moduleId, config) {
11413
11791
  const indexPath = getModuleIndexPath(rootDir, moduleId, config);
@@ -11416,9 +11794,9 @@ function createSearchContext(rootDir, moduleId, config) {
11416
11794
  config,
11417
11795
  loadFileIndex: async (filepath) => {
11418
11796
  const hasExtension = /\.[^./]+$/.test(filepath);
11419
- const indexFilePath = hasExtension ? path24.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path24.join(indexPath, filepath + ".json");
11797
+ const indexFilePath = hasExtension ? path25.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path25.join(indexPath, filepath + ".json");
11420
11798
  try {
11421
- const content = await fs9.readFile(indexFilePath, "utf-8");
11799
+ const content = await fs10.readFile(indexFilePath, "utf-8");
11422
11800
  return JSON.parse(content);
11423
11801
  } catch {
11424
11802
  return null;
@@ -11428,17 +11806,17 @@ function createSearchContext(rootDir, moduleId, config) {
11428
11806
  const files = [];
11429
11807
  await traverseDirectory(indexPath, files, indexPath);
11430
11808
  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$/, "");
11809
+ const relative6 = path25.relative(indexPath, f);
11810
+ return relative6.replace(/\.json$/, "");
11433
11811
  });
11434
11812
  }
11435
11813
  };
11436
11814
  }
11437
11815
  async function traverseDirectory(dir, files, basePath) {
11438
11816
  try {
11439
- const entries = await fs9.readdir(dir, { withFileTypes: true });
11817
+ const entries = await fs10.readdir(dir, { withFileTypes: true });
11440
11818
  for (const entry of entries) {
11441
- const fullPath = path24.join(dir, entry.name);
11819
+ const fullPath = path25.join(dir, entry.name);
11442
11820
  if (entry.isDirectory()) {
11443
11821
  await traverseDirectory(fullPath, files, basePath);
11444
11822
  } else if (entry.isFile()) {
@@ -11450,7 +11828,7 @@ async function traverseDirectory(dir, files, basePath) {
11450
11828
  async function loadGlobalManifest2(rootDir, config) {
11451
11829
  const manifestPath = getGlobalManifestPath(rootDir, config);
11452
11830
  try {
11453
- const content = await fs9.readFile(manifestPath, "utf-8");
11831
+ const content = await fs10.readFile(manifestPath, "utf-8");
11454
11832
  return JSON.parse(content);
11455
11833
  } catch {
11456
11834
  return null;
@@ -11470,7 +11848,7 @@ function formatModuleName(moduleId) {
11470
11848
  return moduleId;
11471
11849
  }
11472
11850
  }
11473
- function formatSearchResults(results) {
11851
+ function formatSearchResults2(results) {
11474
11852
  if (results.length === 0) {
11475
11853
  return "No results found.";
11476
11854
  }
@@ -11489,6 +11867,9 @@ function formatSearchResults(results) {
11489
11867
  if (chunk.isExported) {
11490
11868
  output += " | exported";
11491
11869
  }
11870
+ if (result.context?.exactMatchFusion) {
11871
+ output += " | exact match";
11872
+ }
11492
11873
  output += `
11493
11874
  `;
11494
11875
  const lines = chunk.content.split(`
@@ -11503,12 +11884,84 @@ function formatSearchResults(results) {
11503
11884
  }
11504
11885
  return output;
11505
11886
  }
11887
+ function formatHybridSearchResults(hybridResults) {
11888
+ let output = "";
11889
+ if (hybridResults.exactMatches && hybridResults.exactMatches.totalMatches > 0) {
11890
+ const em = hybridResults.exactMatches;
11891
+ const showingCount = Math.min(em.files.length, 10);
11892
+ output += `┌─ Exact Matches `;
11893
+ if (em.truncated || em.files.length < em.totalFiles) {
11894
+ output += `(showing ${showingCount} of ${em.totalFiles} files, ${em.totalMatches} total matches)`;
11895
+ } else {
11896
+ output += `(${em.totalFiles} files, ${em.totalMatches} matches)`;
11897
+ }
11898
+ output += ` ─┐
11899
+ `;
11900
+ output += `│ Query: "${em.query}"
11901
+ `;
11902
+ output += `└─────────────────────────────────────────────────────────────────────┘
11903
+
11904
+ `;
11905
+ for (let i = 0;i < Math.min(em.files.length, 10); i++) {
11906
+ const file = em.files[i];
11907
+ output += ` ${i + 1}. ${file.filepath}`;
11908
+ if (file.matchCount > 1) {
11909
+ output += ` (${file.matchCount} matches)`;
11910
+ }
11911
+ output += `
11912
+ `;
11913
+ const firstOcc = file.occurrences[0];
11914
+ if (firstOcc) {
11915
+ if (firstOcc.contextBefore) {
11916
+ const beforeLine = firstOcc.contextBefore.substring(0, 76);
11917
+ output += ` ${(firstOcc.line - 1).toString().padStart(4)} │ ${beforeLine}${firstOcc.contextBefore.length > 76 ? "..." : ""}
11918
+ `;
11919
+ }
11920
+ const matchLine = firstOcc.lineContent.substring(0, 76);
11921
+ output += ` ► ${firstOcc.line.toString().padStart(4)} │ ${matchLine}${firstOcc.lineContent.length > 76 ? "..." : ""}
11922
+ `;
11923
+ if (firstOcc.contextAfter) {
11924
+ const afterLine = firstOcc.contextAfter.substring(0, 76);
11925
+ output += ` ${(firstOcc.line + 1).toString().padStart(4)} │ ${afterLine}${firstOcc.contextAfter.length > 76 ? "..." : ""}
11926
+ `;
11927
+ }
11928
+ }
11929
+ output += `
11930
+ `;
11931
+ }
11932
+ if (hybridResults.results.length > 0) {
11933
+ output += `
11934
+ `;
11935
+ }
11936
+ }
11937
+ if (hybridResults.results.length > 0) {
11938
+ if (hybridResults.exactMatches?.totalMatches) {
11939
+ output += `┌─ Semantic Results `;
11940
+ if (hybridResults.fusionApplied) {
11941
+ output += `(boosted by exact matches) `;
11942
+ }
11943
+ output += `─┐
11944
+ `;
11945
+ output += `└─────────────────────────────────────────────────────────────────────┘
11946
+
11947
+ `;
11948
+ }
11949
+ output += formatSearchResults2(hybridResults.results);
11950
+ } else if (!hybridResults.exactMatches?.totalMatches) {
11951
+ output += `No results found.
11952
+ `;
11953
+ }
11954
+ return output;
11955
+ }
11506
11956
  var init_search = __esm(() => {
11507
- init_esm();
11957
+ init_esm2();
11508
11958
  init_types();
11509
11959
  init_config2();
11510
11960
  init_registry();
11511
11961
  init_indexer();
11962
+ init_services();
11963
+ init_usecases();
11964
+ init_filesystem();
11512
11965
  });
11513
11966
 
11514
11967
  // src/app/cli/opencode/version-check.ts
@@ -11545,17 +11998,17 @@ function getInstallationMethod(openCodeVersion) {
11545
11998
  async function detectOpenCodeVersion() {
11546
11999
  try {
11547
12000
  const os4 = await import("os");
11548
- const fs10 = await import("fs/promises");
11549
- const path25 = await import("path");
12001
+ const fs11 = await import("fs/promises");
12002
+ const path26 = await import("path");
11550
12003
  const homeDir = os4.homedir();
11551
12004
  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")
12005
+ path26.join(homeDir, ".local", "share", "opencode", "package.json"),
12006
+ path26.join(homeDir, ".config", "opencode", "package.json"),
12007
+ path26.join(homeDir, ".npm", "global", "node_modules", "opencode", "package.json")
11555
12008
  ];
11556
12009
  for (const packagePath of possiblePaths) {
11557
12010
  try {
11558
- const content = await fs10.readFile(packagePath, "utf-8");
12011
+ const content = await fs11.readFile(packagePath, "utf-8");
11559
12012
  const pkg = JSON.parse(content);
11560
12013
  if (pkg.version) {
11561
12014
  return pkg.version;
@@ -11564,7 +12017,7 @@ async function detectOpenCodeVersion() {
11564
12017
  }
11565
12018
  try {
11566
12019
  const { spawn } = await import("child_process");
11567
- return new Promise((resolve6) => {
12020
+ return new Promise((resolve7) => {
11568
12021
  const proc = spawn("opencode", ["--version"], { stdio: "pipe" });
11569
12022
  let version = "";
11570
12023
  proc.stdout.on("data", (data) => {
@@ -11573,14 +12026,14 @@ async function detectOpenCodeVersion() {
11573
12026
  proc.on("close", (code) => {
11574
12027
  if (code === 0) {
11575
12028
  const match2 = version.match(/v?(\d+\.\d+\.\d+)/);
11576
- resolve6(match2 ? match2[1] : null);
12029
+ resolve7(match2 ? match2[1] : null);
11577
12030
  } else {
11578
- resolve6(null);
12031
+ resolve7(null);
11579
12032
  }
11580
12033
  });
11581
12034
  setTimeout(() => {
11582
12035
  proc.kill();
11583
- resolve6(null);
12036
+ resolve7(null);
11584
12037
  }, 3000);
11585
12038
  });
11586
12039
  } catch {}
@@ -11594,11 +12047,11 @@ async function detectOpenCodeVersion() {
11594
12047
  async function installTool(options = {}) {
11595
12048
  const { logger, checkForOldSkill = true } = options;
11596
12049
  const os4 = await import("os");
11597
- const fs10 = await import("fs/promises");
11598
- const path25 = await import("path");
12050
+ const fs11 = await import("fs/promises");
12051
+ const path26 = await import("path");
11599
12052
  const homeDir = os4.homedir();
11600
- const toolDir = path25.join(homeDir, ".config", "opencode", "tool");
11601
- const toolPath = path25.join(toolDir, "raggrep.ts");
12053
+ const toolDir = path26.join(homeDir, ".config", "opencode", "tool");
12054
+ const toolPath = path26.join(toolDir, "raggrep.ts");
11602
12055
  let removedOldSkill = false;
11603
12056
  const toolContent = `import { tool } from "@opencode-ai/plugin";
11604
12057
 
@@ -11681,11 +12134,11 @@ Please install raggrep globally:
11681
12134
  `;
11682
12135
  try {
11683
12136
  if (checkForOldSkill) {
11684
- const oldSkillDir = path25.join(homeDir, ".config", "opencode", "skill", "raggrep");
11685
- const oldSkillPath = path25.join(oldSkillDir, "SKILL.md");
12137
+ const oldSkillDir = path26.join(homeDir, ".config", "opencode", "skill", "raggrep");
12138
+ const oldSkillPath = path26.join(oldSkillDir, "SKILL.md");
11686
12139
  let oldSkillExists = false;
11687
12140
  try {
11688
- await fs10.access(oldSkillPath);
12141
+ await fs11.access(oldSkillPath);
11689
12142
  oldSkillExists = true;
11690
12143
  } catch {}
11691
12144
  if (oldSkillExists) {
@@ -11703,18 +12156,18 @@ Please install raggrep globally:
11703
12156
  input: process.stdin,
11704
12157
  output: process.stdout
11705
12158
  });
11706
- const answer = await new Promise((resolve6) => {
11707
- rl.question("Remove the existing skill and install tool? (Y/n): ", resolve6);
12159
+ const answer = await new Promise((resolve7) => {
12160
+ rl.question("Remove the existing skill and install tool? (Y/n): ", resolve7);
11708
12161
  });
11709
12162
  rl.close();
11710
12163
  const shouldDelete = answer.toLowerCase() !== "n";
11711
12164
  if (shouldDelete) {
11712
12165
  try {
11713
- await fs10.unlink(oldSkillPath);
11714
- const skillDirContents = await fs10.readdir(oldSkillDir);
12166
+ await fs11.unlink(oldSkillPath);
12167
+ const skillDirContents = await fs11.readdir(oldSkillDir);
11715
12168
  if (skillDirContents.length === 0) {
11716
12169
  try {
11717
- await fs10.rmdir(oldSkillDir);
12170
+ await fs11.rmdir(oldSkillDir);
11718
12171
  console.log("✓ Removed old skill directory.");
11719
12172
  } catch (rmdirError) {
11720
12173
  console.log("✓ Removed old skill file. (Directory not empty or other error)");
@@ -11751,8 +12204,8 @@ Please install raggrep globally:
11751
12204
  }
11752
12205
  }
11753
12206
  }
11754
- await fs10.mkdir(toolDir, { recursive: true });
11755
- await fs10.writeFile(toolPath, toolContent, "utf-8");
12207
+ await fs11.mkdir(toolDir, { recursive: true });
12208
+ await fs11.writeFile(toolPath, toolContent, "utf-8");
11756
12209
  const message = `Installed raggrep tool for OpenCode.
11757
12210
  Location: ${toolPath}
11758
12211
 
@@ -11786,11 +12239,11 @@ The raggrep tool is now available in OpenCode.`;
11786
12239
  async function installSkill(options = {}) {
11787
12240
  const { logger, checkForOldTool = true } = options;
11788
12241
  const os4 = await import("os");
11789
- const fs10 = await import("fs/promises");
11790
- const path25 = await import("path");
12242
+ const fs11 = await import("fs/promises");
12243
+ const path26 = await import("path");
11791
12244
  const homeDir = os4.homedir();
11792
- const skillDir = path25.join(homeDir, ".config", "opencode", "skill", "raggrep");
11793
- const skillPath = path25.join(skillDir, "SKILL.md");
12245
+ const skillDir = path26.join(homeDir, ".config", "opencode", "skill", "raggrep");
12246
+ const skillPath = path26.join(skillDir, "SKILL.md");
11794
12247
  const skillContent = `---
11795
12248
  name: raggrep
11796
12249
  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 +12355,11 @@ Instead of using grep/rg or manually reading files:
11902
12355
  let removedOldTool = false;
11903
12356
  try {
11904
12357
  if (checkForOldTool) {
11905
- const oldToolDir = path25.join(homeDir, ".config", "opencode", "tool");
11906
- const oldToolPath = path25.join(oldToolDir, "raggrep.ts");
12358
+ const oldToolDir = path26.join(homeDir, ".config", "opencode", "tool");
12359
+ const oldToolPath = path26.join(oldToolDir, "raggrep.ts");
11907
12360
  let oldToolExists = false;
11908
12361
  try {
11909
- await fs10.access(oldToolPath);
12362
+ await fs11.access(oldToolPath);
11910
12363
  oldToolExists = true;
11911
12364
  } catch {}
11912
12365
  if (oldToolExists) {
@@ -11924,18 +12377,18 @@ Instead of using grep/rg or manually reading files:
11924
12377
  input: process.stdin,
11925
12378
  output: process.stdout
11926
12379
  });
11927
- const answer = await new Promise((resolve6) => {
11928
- rl.question("Do you want to remove the old tool file? (Y/n): ", resolve6);
12380
+ const answer = await new Promise((resolve7) => {
12381
+ rl.question("Do you want to remove the old tool file? (Y/n): ", resolve7);
11929
12382
  });
11930
12383
  rl.close();
11931
12384
  const shouldDelete = answer.toLowerCase() !== "n";
11932
12385
  if (shouldDelete) {
11933
12386
  try {
11934
- await fs10.unlink(oldToolPath);
11935
- const toolDirContents = await fs10.readdir(oldToolDir);
12387
+ await fs11.unlink(oldToolPath);
12388
+ const toolDirContents = await fs11.readdir(oldToolDir);
11936
12389
  if (toolDirContents.length === 0) {
11937
12390
  try {
11938
- await fs10.rmdir(oldToolDir);
12391
+ await fs11.rmdir(oldToolDir);
11939
12392
  console.log("✓ Removed old tool directory.");
11940
12393
  } catch (rmdirError) {
11941
12394
  console.log("✓ Removed old tool file. (Directory not empty or other error)");
@@ -11968,8 +12421,8 @@ Instead of using grep/rg or manually reading files:
11968
12421
  }
11969
12422
  }
11970
12423
  }
11971
- await fs10.mkdir(skillDir, { recursive: true });
11972
- await fs10.writeFile(skillPath, skillContent, "utf-8");
12424
+ await fs11.mkdir(skillDir, { recursive: true });
12425
+ await fs11.writeFile(skillPath, skillContent, "utf-8");
11973
12426
  const message = `Installed raggrep skill for OpenCode.
11974
12427
  Location: ${skillPath}
11975
12428
 
@@ -12022,7 +12475,7 @@ init_logger();
12022
12475
  // package.json
12023
12476
  var package_default = {
12024
12477
  name: "raggrep",
12025
- version: "0.14.2",
12478
+ version: "0.15.0",
12026
12479
  description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
12027
12480
  type: "module",
12028
12481
  main: "./dist/index.js",
@@ -12081,6 +12534,7 @@ var package_default = {
12081
12534
  chokidar: "^5.0.0",
12082
12535
  fdir: "^6.5.0",
12083
12536
  glob: "^10.0.0",
12537
+ minimatch: "^10.1.1",
12084
12538
  typescript: "^5.0.0",
12085
12539
  "web-tree-sitter": "^0.26.3"
12086
12540
  },
@@ -12227,13 +12681,13 @@ Examples:
12227
12681
  `);
12228
12682
  process.exit(0);
12229
12683
  }
12230
- const { indexDirectory: indexDirectory2, watchDirectory: watchDirectory2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
12684
+ const { indexDirectory: indexDirectory3, watchDirectory: watchDirectory2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
12231
12685
  const logger = createInlineLogger({ verbose: flags.verbose });
12232
12686
  console.log("RAGgrep Indexer");
12233
12687
  console.log(`================
12234
12688
  `);
12235
12689
  try {
12236
- const results = await indexDirectory2(process.cwd(), {
12690
+ const results = await indexDirectory3(process.cwd(), {
12237
12691
  model: flags.model,
12238
12692
  verbose: flags.verbose,
12239
12693
  concurrency: flags.concurrency,
@@ -12338,7 +12792,6 @@ Examples:
12338
12792
  `);
12339
12793
  process.exit(0);
12340
12794
  }
12341
- const { search: search2, formatSearchResults: formatSearchResults2 } = await Promise.resolve().then(() => (init_search(), exports_search));
12342
12795
  const { ensureIndexFresh: ensureIndexFresh2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
12343
12796
  const query = flags.remaining[0];
12344
12797
  if (!query) {
@@ -12388,14 +12841,15 @@ Examples:
12388
12841
  `);
12389
12842
  }
12390
12843
  const filePatterns = flags.fileType ? [`*.${flags.fileType}`] : undefined;
12391
- const results = await search2(process.cwd(), query, {
12844
+ const { hybridSearch: hybridSearch2, formatHybridSearchResults: formatHybridSearchResults2 } = await Promise.resolve().then(() => (init_search(), exports_search));
12845
+ const hybridResults = await hybridSearch2(process.cwd(), query, {
12392
12846
  topK: flags.topK ?? 10,
12393
12847
  minScore: flags.minScore,
12394
12848
  filePatterns,
12395
12849
  pathFilter: flags.pathFilter,
12396
12850
  ensureFresh: false
12397
12851
  });
12398
- console.log(formatSearchResults2(results));
12852
+ console.log(formatHybridSearchResults2(hybridResults));
12399
12853
  } catch (error) {
12400
12854
  console.error("Error during search:", error);
12401
12855
  process.exit(1);
@@ -12618,4 +13072,4 @@ Run 'raggrep <command> --help' for more information.
12618
13072
  }
12619
13073
  main();
12620
13074
 
12621
- //# debugId=71C6E22A4D7E91CE64756E2164756E21
13075
+ //# debugId=2B4BB49E8FD1AEE664756E2164756E21