raggrep 0.6.1 → 0.7.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.
package/dist/cli/main.js CHANGED
@@ -430,10 +430,14 @@ var init_config = __esm(() => {
430
430
  ];
431
431
  });
432
432
 
433
+ // src/domain/entities/literal.ts
434
+ var init_literal = () => {};
435
+
433
436
  // src/domain/entities/index.ts
434
437
  var init_entities = __esm(() => {
435
438
  init_searchResult();
436
439
  init_config();
440
+ init_literal();
437
441
  });
438
442
 
439
443
  // src/infrastructure/config/configLoader.ts
@@ -2660,10 +2664,299 @@ function generateChunkId(filepath, startLine, endLine) {
2660
2664
  }
2661
2665
  var DEFAULT_CHUNK_SIZE = 30, DEFAULT_OVERLAP = 5;
2662
2666
 
2667
+ // src/domain/services/queryLiteralParser.ts
2668
+ function parseQueryLiterals(query) {
2669
+ if (!query || query.trim() === "") {
2670
+ return { literals: [], remainingQuery: "" };
2671
+ }
2672
+ const literals = [];
2673
+ let remainingQuery = query;
2674
+ const matchedPositions = new Set;
2675
+ const backtickResult = extractExplicitLiterals(remainingQuery, /`([^`]+)`/g, "explicit-backtick", matchedPositions);
2676
+ literals.push(...backtickResult.literals);
2677
+ remainingQuery = backtickResult.remainingQuery;
2678
+ const quoteResult = extractExplicitLiterals(remainingQuery, /"([^"]+)"/g, "explicit-quote", matchedPositions);
2679
+ literals.push(...quoteResult.literals);
2680
+ remainingQuery = quoteResult.remainingQuery;
2681
+ const implicitLiterals = extractImplicitLiterals(query, matchedPositions);
2682
+ literals.push(...implicitLiterals);
2683
+ return {
2684
+ literals,
2685
+ remainingQuery: remainingQuery.trim()
2686
+ };
2687
+ }
2688
+ function extractExplicitLiterals(query, pattern, method, matchedPositions) {
2689
+ const literals = [];
2690
+ let remainingQuery = query;
2691
+ pattern.lastIndex = 0;
2692
+ let match;
2693
+ const replacements = [];
2694
+ while ((match = pattern.exec(query)) !== null) {
2695
+ const value = match[1];
2696
+ const rawValue = match[0];
2697
+ if (!value || value.trim() === "") {
2698
+ continue;
2699
+ }
2700
+ const posKey = `${match.index}:${match.index + rawValue.length}`;
2701
+ matchedPositions.add(posKey);
2702
+ matchedPositions.add(`value:${value.toLowerCase()}`);
2703
+ literals.push({
2704
+ value,
2705
+ rawValue,
2706
+ confidence: "high",
2707
+ detectionMethod: method,
2708
+ inferredType: inferTypeFromValue(value)
2709
+ });
2710
+ replacements.push({
2711
+ start: match.index,
2712
+ end: match.index + rawValue.length,
2713
+ text: ""
2714
+ });
2715
+ }
2716
+ replacements.sort((a, b) => b.start - a.start).forEach((r) => {
2717
+ remainingQuery = remainingQuery.slice(0, r.start) + r.text + remainingQuery.slice(r.end);
2718
+ });
2719
+ return { literals, remainingQuery };
2720
+ }
2721
+ function extractImplicitLiterals(query, matchedPositions) {
2722
+ const literals = [];
2723
+ const seenValues = new Set;
2724
+ for (const patternDef of IMPLICIT_PATTERNS) {
2725
+ patternDef.pattern.lastIndex = 0;
2726
+ let match;
2727
+ while ((match = patternDef.pattern.exec(query)) !== null) {
2728
+ const value = match[1];
2729
+ if (patternDef.minLength && value.length < patternDef.minLength) {
2730
+ continue;
2731
+ }
2732
+ const posKey = `${match.index}:${match.index + value.length}`;
2733
+ if (matchedPositions.has(posKey)) {
2734
+ continue;
2735
+ }
2736
+ if (matchedPositions.has(`value:${value.toLowerCase()}`)) {
2737
+ continue;
2738
+ }
2739
+ const lowerValue = value.toLowerCase();
2740
+ if (seenValues.has(lowerValue)) {
2741
+ continue;
2742
+ }
2743
+ seenValues.add(lowerValue);
2744
+ if (isCommonWord(value)) {
2745
+ continue;
2746
+ }
2747
+ literals.push({
2748
+ value,
2749
+ rawValue: value,
2750
+ confidence: patternDef.confidence,
2751
+ detectionMethod: "implicit-casing",
2752
+ inferredType: patternDef.inferredType
2753
+ });
2754
+ }
2755
+ }
2756
+ return literals;
2757
+ }
2758
+ function inferTypeFromValue(value) {
2759
+ if (/^[A-Z][a-z]+(?:[A-Z][a-z0-9]*)+$/.test(value)) {
2760
+ return "className";
2761
+ }
2762
+ if (/^[a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]*)+$/.test(value)) {
2763
+ return "functionName";
2764
+ }
2765
+ if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(value)) {
2766
+ return "variableName";
2767
+ }
2768
+ if (/^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/.test(value)) {
2769
+ return "identifier";
2770
+ }
2771
+ if (/^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$/.test(value)) {
2772
+ return "packageName";
2773
+ }
2774
+ return;
2775
+ }
2776
+ function isCommonWord(word) {
2777
+ const commonWords = new Set([
2778
+ "find",
2779
+ "the",
2780
+ "a",
2781
+ "an",
2782
+ "is",
2783
+ "are",
2784
+ "was",
2785
+ "were",
2786
+ "what",
2787
+ "where",
2788
+ "when",
2789
+ "how",
2790
+ "why",
2791
+ "which",
2792
+ "who",
2793
+ "this",
2794
+ "that",
2795
+ "these",
2796
+ "those",
2797
+ "and",
2798
+ "or",
2799
+ "but",
2800
+ "for",
2801
+ "with",
2802
+ "from",
2803
+ "to",
2804
+ "in",
2805
+ "on",
2806
+ "at",
2807
+ "by",
2808
+ "of",
2809
+ "all",
2810
+ "any",
2811
+ "some",
2812
+ "get",
2813
+ "set",
2814
+ "new",
2815
+ "class",
2816
+ "function",
2817
+ "const",
2818
+ "let",
2819
+ "var",
2820
+ "type",
2821
+ "interface",
2822
+ "import",
2823
+ "export",
2824
+ "default",
2825
+ "return",
2826
+ "async",
2827
+ "await",
2828
+ "null",
2829
+ "undefined",
2830
+ "true",
2831
+ "false"
2832
+ ]);
2833
+ return commonWords.has(word.toLowerCase());
2834
+ }
2835
+ var IMPLICIT_PATTERNS;
2836
+ var init_queryLiteralParser = __esm(() => {
2837
+ IMPLICIT_PATTERNS = [
2838
+ {
2839
+ pattern: /\b([A-Z][a-z]+(?:[A-Z][a-z0-9]*)+)\b/g,
2840
+ confidence: "medium",
2841
+ inferredType: "className",
2842
+ minLength: 3
2843
+ },
2844
+ {
2845
+ pattern: /\b([a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]*)+)\b/g,
2846
+ confidence: "medium",
2847
+ inferredType: "functionName",
2848
+ minLength: 3
2849
+ },
2850
+ {
2851
+ pattern: /\b([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+)\b/g,
2852
+ confidence: "medium",
2853
+ inferredType: "variableName",
2854
+ minLength: 3
2855
+ },
2856
+ {
2857
+ pattern: /\b([a-z][a-z0-9]*(?:_[a-z0-9]+)+)\b/g,
2858
+ confidence: "low",
2859
+ inferredType: "identifier",
2860
+ minLength: 3
2861
+ },
2862
+ {
2863
+ pattern: /(?<![/:.])\b([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\b(?![/:])/g,
2864
+ confidence: "low",
2865
+ inferredType: "packageName",
2866
+ minLength: 3
2867
+ }
2868
+ ];
2869
+ });
2870
+
2871
+ // src/domain/services/literalExtractor.ts
2872
+ function extractLiterals(chunk) {
2873
+ const literals = [];
2874
+ if (chunk.name) {
2875
+ const literalType = CHUNK_TYPE_TO_LITERAL_TYPE[chunk.type] || "identifier";
2876
+ literals.push({
2877
+ value: chunk.name,
2878
+ type: literalType,
2879
+ matchType: "definition"
2880
+ });
2881
+ }
2882
+ return literals;
2883
+ }
2884
+ var CHUNK_TYPE_TO_LITERAL_TYPE;
2885
+ var init_literalExtractor = __esm(() => {
2886
+ CHUNK_TYPE_TO_LITERAL_TYPE = {
2887
+ class: "className",
2888
+ function: "functionName",
2889
+ interface: "interfaceName",
2890
+ type: "typeName",
2891
+ enum: "enumName",
2892
+ variable: "variableName"
2893
+ };
2894
+ });
2895
+
2896
+ // src/domain/services/literalScorer.ts
2897
+ function calculateLiteralMultiplier(matchType, confidence) {
2898
+ return LITERAL_SCORING_CONSTANTS.MULTIPLIERS[matchType][confidence];
2899
+ }
2900
+ function calculateMaxMultiplier(matches) {
2901
+ if (!matches || matches.length === 0) {
2902
+ return 1;
2903
+ }
2904
+ return Math.max(...matches.map((m) => calculateLiteralMultiplier(m.indexedLiteral.matchType, m.queryLiteral.confidence)));
2905
+ }
2906
+ function calculateLiteralContribution(matches, hasSemanticOrBm25) {
2907
+ if (!matches || matches.length === 0) {
2908
+ return {
2909
+ multiplier: 1,
2910
+ literalOnly: false,
2911
+ matchCount: 0
2912
+ };
2913
+ }
2914
+ let bestMatch = null;
2915
+ let bestMultiplier = 0;
2916
+ for (const match of matches) {
2917
+ const mult = calculateLiteralMultiplier(match.indexedLiteral.matchType, match.queryLiteral.confidence);
2918
+ if (mult > bestMultiplier) {
2919
+ bestMultiplier = mult;
2920
+ bestMatch = match;
2921
+ }
2922
+ }
2923
+ return {
2924
+ multiplier: bestMultiplier,
2925
+ literalOnly: !hasSemanticOrBm25,
2926
+ bestMatchType: bestMatch?.indexedLiteral.matchType,
2927
+ bestConfidence: bestMatch?.queryLiteral.confidence,
2928
+ matchCount: matches.length
2929
+ };
2930
+ }
2931
+ function applyLiteralBoost(baseScore, matches, hasSemanticOrBm25) {
2932
+ if (!matches || matches.length === 0) {
2933
+ return baseScore;
2934
+ }
2935
+ const multiplier = calculateMaxMultiplier(matches);
2936
+ if (!hasSemanticOrBm25) {
2937
+ return LITERAL_SCORING_CONSTANTS.BASE_SCORE * multiplier;
2938
+ }
2939
+ return baseScore * multiplier;
2940
+ }
2941
+ var LITERAL_SCORING_CONSTANTS;
2942
+ var init_literalScorer = __esm(() => {
2943
+ LITERAL_SCORING_CONSTANTS = {
2944
+ BASE_SCORE: 0.5,
2945
+ MULTIPLIERS: {
2946
+ definition: { high: 2.5, medium: 2, low: 1.5 },
2947
+ reference: { high: 2, medium: 1.5, low: 1.3 },
2948
+ import: { high: 1.5, medium: 1.3, low: 1.1 }
2949
+ }
2950
+ };
2951
+ });
2952
+
2663
2953
  // src/domain/services/index.ts
2664
2954
  var init_services = __esm(() => {
2665
2955
  init_keywords();
2666
2956
  init_queryIntent();
2957
+ init_queryLiteralParser();
2958
+ init_literalExtractor();
2959
+ init_literalScorer();
2667
2960
  });
2668
2961
 
2669
2962
  // src/modules/language/typescript/parseCode.ts
@@ -2983,10 +3276,158 @@ var init_symbolicIndex = __esm(() => {
2983
3276
  init_keywords();
2984
3277
  });
2985
3278
 
3279
+ // src/infrastructure/storage/literalIndex.ts
3280
+ import * as fs4 from "fs/promises";
3281
+ import * as path9 from "path";
3282
+
3283
+ class LiteralIndex {
3284
+ indexPath;
3285
+ moduleId;
3286
+ entries = new Map;
3287
+ static VERSION = "1.0.0";
3288
+ constructor(indexDir, moduleId) {
3289
+ this.indexPath = path9.join(indexDir, "index", moduleId, "literals");
3290
+ this.moduleId = moduleId;
3291
+ }
3292
+ async initialize() {
3293
+ try {
3294
+ await this.load();
3295
+ } catch {
3296
+ this.entries = new Map;
3297
+ }
3298
+ }
3299
+ addLiterals(chunkId, filepath, literals) {
3300
+ for (const literal of literals) {
3301
+ const key = literal.value.toLowerCase();
3302
+ const existingEntries = this.entries.get(key) || [];
3303
+ const existingIndex = existingEntries.findIndex((e) => e.chunkId === chunkId);
3304
+ const newEntry = {
3305
+ chunkId,
3306
+ filepath,
3307
+ originalCasing: literal.value,
3308
+ type: literal.type,
3309
+ matchType: literal.matchType
3310
+ };
3311
+ if (existingIndex >= 0) {
3312
+ const existing = existingEntries[existingIndex];
3313
+ if (shouldReplaceMatchType(existing.matchType, literal.matchType)) {
3314
+ existingEntries[existingIndex] = newEntry;
3315
+ }
3316
+ } else {
3317
+ existingEntries.push(newEntry);
3318
+ }
3319
+ this.entries.set(key, existingEntries);
3320
+ }
3321
+ }
3322
+ removeChunk(chunkId) {
3323
+ for (const [key, entries] of this.entries) {
3324
+ const filtered = entries.filter((e) => e.chunkId !== chunkId);
3325
+ if (filtered.length === 0) {
3326
+ this.entries.delete(key);
3327
+ } else if (filtered.length !== entries.length) {
3328
+ this.entries.set(key, filtered);
3329
+ }
3330
+ }
3331
+ }
3332
+ findMatches(queryLiterals) {
3333
+ const matches = [];
3334
+ for (const queryLiteral of queryLiterals) {
3335
+ const key = queryLiteral.value.toLowerCase();
3336
+ const entries = this.entries.get(key);
3337
+ if (!entries) {
3338
+ continue;
3339
+ }
3340
+ for (const entry of entries) {
3341
+ const exactMatch = entry.originalCasing === queryLiteral.value;
3342
+ matches.push({
3343
+ queryLiteral,
3344
+ indexedLiteral: {
3345
+ value: entry.originalCasing,
3346
+ type: entry.type,
3347
+ matchType: entry.matchType
3348
+ },
3349
+ chunkId: entry.chunkId,
3350
+ filepath: entry.filepath,
3351
+ exactMatch
3352
+ });
3353
+ }
3354
+ }
3355
+ return matches;
3356
+ }
3357
+ getChunksForLiteral(literal) {
3358
+ const key = literal.toLowerCase();
3359
+ const entries = this.entries.get(key);
3360
+ return entries ? entries.map((e) => e.chunkId) : [];
3361
+ }
3362
+ async save() {
3363
+ await fs4.mkdir(this.indexPath, { recursive: true });
3364
+ const data = {
3365
+ version: LiteralIndex.VERSION,
3366
+ entries: Object.fromEntries(this.entries)
3367
+ };
3368
+ const indexFile = path9.join(this.indexPath, "_index.json");
3369
+ await fs4.writeFile(indexFile, JSON.stringify(data, null, 2));
3370
+ }
3371
+ async load() {
3372
+ const indexFile = path9.join(this.indexPath, "_index.json");
3373
+ const content = await fs4.readFile(indexFile, "utf-8");
3374
+ const data = JSON.parse(content);
3375
+ if (data.version !== LiteralIndex.VERSION) {
3376
+ console.warn(`Literal index version mismatch: expected ${LiteralIndex.VERSION}, got ${data.version}`);
3377
+ }
3378
+ this.entries = new Map(Object.entries(data.entries));
3379
+ }
3380
+ async exists() {
3381
+ try {
3382
+ const indexFile = path9.join(this.indexPath, "_index.json");
3383
+ await fs4.access(indexFile);
3384
+ return true;
3385
+ } catch {
3386
+ return false;
3387
+ }
3388
+ }
3389
+ clear() {
3390
+ this.entries.clear();
3391
+ }
3392
+ get size() {
3393
+ return this.entries.size;
3394
+ }
3395
+ get totalMappings() {
3396
+ let count = 0;
3397
+ for (const entries of this.entries.values()) {
3398
+ count += entries.length;
3399
+ }
3400
+ return count;
3401
+ }
3402
+ getAllLiterals() {
3403
+ return Array.from(this.entries.keys());
3404
+ }
3405
+ buildMatchMap(queryLiterals) {
3406
+ const matches = this.findMatches(queryLiterals);
3407
+ const matchMap = new Map;
3408
+ for (const match of matches) {
3409
+ const existing = matchMap.get(match.chunkId) || [];
3410
+ existing.push(match);
3411
+ matchMap.set(match.chunkId, existing);
3412
+ }
3413
+ return matchMap;
3414
+ }
3415
+ }
3416
+ function shouldReplaceMatchType(existing, incoming) {
3417
+ const priority = {
3418
+ definition: 3,
3419
+ reference: 2,
3420
+ import: 1
3421
+ };
3422
+ return priority[incoming] > priority[existing];
3423
+ }
3424
+ var init_literalIndex = () => {};
3425
+
2986
3426
  // src/infrastructure/storage/index.ts
2987
3427
  var init_storage = __esm(() => {
2988
3428
  init_fileIndexStorage();
2989
3429
  init_symbolicIndex();
3430
+ init_literalIndex();
2990
3431
  });
2991
3432
 
2992
3433
  // src/modules/language/typescript/index.ts
@@ -2999,9 +3440,9 @@ __export(exports_typescript, {
2999
3440
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
3000
3441
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
3001
3442
  });
3002
- import * as path9 from "path";
3443
+ import * as path10 from "path";
3003
3444
  function isTypeScriptFile(filepath) {
3004
- const ext = path9.extname(filepath).toLowerCase();
3445
+ const ext = path10.extname(filepath).toLowerCase();
3005
3446
  return TYPESCRIPT_EXTENSIONS.includes(ext);
3006
3447
  }
3007
3448
  function calculateChunkTypeBoost(chunk) {
@@ -3036,7 +3477,9 @@ class TypeScriptModule {
3036
3477
  }
3037
3478
  embeddingConfig = null;
3038
3479
  symbolicIndex = null;
3480
+ literalIndex = null;
3039
3481
  pendingSummaries = new Map;
3482
+ pendingLiterals = new Map;
3040
3483
  rootDir = "";
3041
3484
  logger = undefined;
3042
3485
  async initialize(config) {
@@ -3050,6 +3493,7 @@ class TypeScriptModule {
3050
3493
  }
3051
3494
  configureEmbeddings(this.embeddingConfig);
3052
3495
  this.pendingSummaries.clear();
3496
+ this.pendingLiterals.clear();
3053
3497
  }
3054
3498
  async indexFile(filepath, content, ctx) {
3055
3499
  if (!isTypeScriptFile(filepath)) {
@@ -3109,6 +3553,17 @@ class TypeScriptModule {
3109
3553
  }
3110
3554
  };
3111
3555
  this.pendingSummaries.set(filepath, fileSummary);
3556
+ for (const chunk of chunks) {
3557
+ const literals = extractLiterals(chunk);
3558
+ if (literals.length > 0) {
3559
+ const existing = this.pendingLiterals.get(chunk.id);
3560
+ if (existing) {
3561
+ existing.literals.push(...literals);
3562
+ } else {
3563
+ this.pendingLiterals.set(chunk.id, { filepath, literals });
3564
+ }
3565
+ }
3566
+ }
3112
3567
  return {
3113
3568
  filepath,
3114
3569
  lastModified: stats.lastModified,
@@ -3126,7 +3581,14 @@ class TypeScriptModule {
3126
3581
  }
3127
3582
  this.symbolicIndex.buildBM25Index();
3128
3583
  await this.symbolicIndex.save();
3584
+ this.literalIndex = new LiteralIndex(indexDir, this.id);
3585
+ await this.literalIndex.initialize();
3586
+ for (const [chunkId, { filepath, literals }] of this.pendingLiterals) {
3587
+ this.literalIndex.addLiterals(chunkId, filepath, literals);
3588
+ }
3589
+ await this.literalIndex.save();
3129
3590
  this.pendingSummaries.clear();
3591
+ this.pendingLiterals.clear();
3130
3592
  }
3131
3593
  async search(query, ctx, options = {}) {
3132
3594
  const {
@@ -3134,8 +3596,15 @@ class TypeScriptModule {
3134
3596
  minScore = DEFAULT_MIN_SCORE2,
3135
3597
  filePatterns
3136
3598
  } = options;
3599
+ const { literals: queryLiterals, remainingQuery } = parseQueryLiterals(query);
3137
3600
  const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
3138
3601
  const symbolicIndex = new SymbolicIndex(indexDir, this.id);
3602
+ const literalIndex = new LiteralIndex(indexDir, this.id);
3603
+ let literalMatchMap = new Map;
3604
+ try {
3605
+ await literalIndex.initialize();
3606
+ literalMatchMap = literalIndex.buildMatchMap(queryLiterals);
3607
+ } catch {}
3139
3608
  let allFiles;
3140
3609
  try {
3141
3610
  await symbolicIndex.initialize();
@@ -3155,7 +3624,8 @@ class TypeScriptModule {
3155
3624
  });
3156
3625
  });
3157
3626
  }
3158
- const queryEmbedding = await getEmbedding(query);
3627
+ const semanticQuery = remainingQuery.trim() || query;
3628
+ const queryEmbedding = await getEmbedding(semanticQuery);
3159
3629
  const bm25Index = new BM25Index;
3160
3630
  const allChunksData = [];
3161
3631
  for (const filepath of filesToSearch) {
@@ -3189,14 +3659,14 @@ class TypeScriptModule {
3189
3659
  const summary = symbolicIndex.getFileSummary(filepath);
3190
3660
  if (summary?.pathContext) {
3191
3661
  let boost = 0;
3192
- const ctx2 = summary.pathContext;
3193
- if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
3662
+ const pathCtx = summary.pathContext;
3663
+ if (pathCtx.domain && queryTerms.some((t) => pathCtx.domain.includes(t) || t.includes(pathCtx.domain))) {
3194
3664
  boost += 0.1;
3195
3665
  }
3196
- if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
3666
+ if (pathCtx.layer && queryTerms.some((t) => pathCtx.layer.includes(t) || t.includes(pathCtx.layer))) {
3197
3667
  boost += 0.05;
3198
3668
  }
3199
- const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3669
+ const segmentMatch = pathCtx.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3200
3670
  if (segmentMatch) {
3201
3671
  boost += 0.05;
3202
3672
  }
@@ -3204,6 +3674,7 @@ class TypeScriptModule {
3204
3674
  }
3205
3675
  }
3206
3676
  const results = [];
3677
+ const processedChunkIds = new Set;
3207
3678
  for (const { filepath, chunk, embedding } of allChunksData) {
3208
3679
  const semanticScore = cosineSimilarity(queryEmbedding, embedding);
3209
3680
  const bm25Score = bm25Scores.get(chunk.id) || 0;
@@ -3211,13 +3682,18 @@ class TypeScriptModule {
3211
3682
  const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3212
3683
  const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3213
3684
  const exportBoost = calculateExportBoost(chunk);
3214
- const totalBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3215
- const hybridScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score + totalBoost;
3216
- if (hybridScore >= minScore || bm25Score > 0.3) {
3685
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3686
+ const baseScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score;
3687
+ const literalMatches = literalMatchMap.get(chunk.id) || [];
3688
+ const literalContribution = calculateLiteralContribution(literalMatches, true);
3689
+ const boostedScore = applyLiteralBoost(baseScore, literalMatches, true);
3690
+ const finalScore = boostedScore + additiveBoost;
3691
+ processedChunkIds.add(chunk.id);
3692
+ if (finalScore >= minScore || bm25Score > 0.3 || literalMatches.length > 0) {
3217
3693
  results.push({
3218
3694
  filepath,
3219
3695
  chunk,
3220
- score: hybridScore,
3696
+ score: finalScore,
3221
3697
  moduleId: this.id,
3222
3698
  context: {
3223
3699
  semanticScore,
@@ -3225,7 +3701,78 @@ class TypeScriptModule {
3225
3701
  pathBoost,
3226
3702
  fileTypeBoost,
3227
3703
  chunkTypeBoost,
3228
- exportBoost
3704
+ exportBoost,
3705
+ literalMultiplier: literalContribution.multiplier,
3706
+ literalMatchType: literalContribution.bestMatchType,
3707
+ literalConfidence: literalContribution.bestConfidence,
3708
+ literalMatchCount: literalContribution.matchCount
3709
+ }
3710
+ });
3711
+ }
3712
+ }
3713
+ const literalOnlyFiles = new Map;
3714
+ for (const [chunkId, matches] of literalMatchMap) {
3715
+ if (processedChunkIds.has(chunkId)) {
3716
+ continue;
3717
+ }
3718
+ const filepath = matches[0]?.filepath;
3719
+ if (!filepath)
3720
+ continue;
3721
+ const existing = literalOnlyFiles.get(filepath) || [];
3722
+ existing.push(...matches);
3723
+ literalOnlyFiles.set(filepath, existing);
3724
+ }
3725
+ for (const [filepath, matches] of literalOnlyFiles) {
3726
+ const fileIndex = await ctx.loadFileIndex(filepath);
3727
+ if (!fileIndex)
3728
+ continue;
3729
+ const moduleData = fileIndex.moduleData;
3730
+ const chunkMatches = new Map;
3731
+ for (const match of matches) {
3732
+ const existing = chunkMatches.get(match.chunkId) || [];
3733
+ existing.push(match);
3734
+ chunkMatches.set(match.chunkId, existing);
3735
+ }
3736
+ for (const [chunkId, chunkLiteralMatches] of chunkMatches) {
3737
+ if (processedChunkIds.has(chunkId))
3738
+ continue;
3739
+ const chunkIndex = fileIndex.chunks.findIndex((c) => c.id === chunkId);
3740
+ if (chunkIndex === -1)
3741
+ continue;
3742
+ const chunk = fileIndex.chunks[chunkIndex];
3743
+ const embedding = moduleData?.embeddings?.[chunkIndex];
3744
+ let semanticScore = 0;
3745
+ if (embedding) {
3746
+ semanticScore = cosineSimilarity(queryEmbedding, embedding);
3747
+ }
3748
+ const bm25Score = bm25Scores.get(chunkId) || 0;
3749
+ const pathBoost = pathBoosts.get(filepath) || 0;
3750
+ const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3751
+ const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3752
+ const exportBoost = calculateExportBoost(chunk);
3753
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3754
+ const literalContribution = calculateLiteralContribution(chunkLiteralMatches, false);
3755
+ const baseScore = semanticScore > 0 ? SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score : LITERAL_SCORING_CONSTANTS.BASE_SCORE;
3756
+ const boostedScore = applyLiteralBoost(baseScore, chunkLiteralMatches, semanticScore > 0);
3757
+ const finalScore = boostedScore + additiveBoost;
3758
+ processedChunkIds.add(chunkId);
3759
+ results.push({
3760
+ filepath,
3761
+ chunk,
3762
+ score: finalScore,
3763
+ moduleId: this.id,
3764
+ context: {
3765
+ semanticScore,
3766
+ bm25Score,
3767
+ pathBoost,
3768
+ fileTypeBoost,
3769
+ chunkTypeBoost,
3770
+ exportBoost,
3771
+ literalMultiplier: literalContribution.multiplier,
3772
+ literalMatchType: literalContribution.bestMatchType,
3773
+ literalConfidence: literalContribution.bestConfidence,
3774
+ literalMatchCount: literalContribution.matchCount,
3775
+ literalOnly: true
3229
3776
  }
3230
3777
  });
3231
3778
  }
@@ -3241,16 +3788,16 @@ class TypeScriptModule {
3241
3788
  while ((match = importRegex.exec(content)) !== null) {
3242
3789
  const importPath = match[1];
3243
3790
  if (importPath.startsWith(".")) {
3244
- const dir = path9.dirname(filepath);
3245
- const resolved = path9.normalize(path9.join(dir, importPath));
3791
+ const dir = path10.dirname(filepath);
3792
+ const resolved = path10.normalize(path10.join(dir, importPath));
3246
3793
  references.push(resolved);
3247
3794
  }
3248
3795
  }
3249
3796
  while ((match = requireRegex.exec(content)) !== null) {
3250
3797
  const importPath = match[1];
3251
3798
  if (importPath.startsWith(".")) {
3252
- const dir = path9.dirname(filepath);
3253
- const resolved = path9.normalize(path9.join(dir, importPath));
3799
+ const dir = path10.dirname(filepath);
3800
+ const resolved = path10.normalize(path10.join(dir, importPath));
3254
3801
  references.push(resolved);
3255
3802
  }
3256
3803
  }
@@ -3287,9 +3834,9 @@ __export(exports_json, {
3287
3834
  DEFAULT_TOP_K: () => DEFAULT_TOP_K3,
3288
3835
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE3
3289
3836
  });
3290
- import * as path10 from "path";
3837
+ import * as path11 from "path";
3291
3838
  function isJsonFile(filepath) {
3292
- const ext = path10.extname(filepath).toLowerCase();
3839
+ const ext = path11.extname(filepath).toLowerCase();
3293
3840
  return JSON_EXTENSIONS.includes(ext);
3294
3841
  }
3295
3842
  function extractJsonKeys(obj, prefix = "") {
@@ -3370,7 +3917,7 @@ class JsonModule {
3370
3917
  return null;
3371
3918
  }
3372
3919
  const chunkContents = textChunks.map((c) => {
3373
- const filename = path10.basename(filepath);
3920
+ const filename = path11.basename(filepath);
3374
3921
  return `${filename}: ${c.content}`;
3375
3922
  });
3376
3923
  const embeddings = await getEmbeddings(chunkContents);
@@ -3521,9 +4068,9 @@ __export(exports_markdown, {
3521
4068
  DEFAULT_TOP_K: () => DEFAULT_TOP_K4,
3522
4069
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE4
3523
4070
  });
3524
- import * as path11 from "path";
4071
+ import * as path12 from "path";
3525
4072
  function isMarkdownFile(filepath) {
3526
- const ext = path11.extname(filepath).toLowerCase();
4073
+ const ext = path12.extname(filepath).toLowerCase();
3527
4074
  return MARKDOWN_EXTENSIONS.includes(ext);
3528
4075
  }
3529
4076
  function parseMarkdownSections(content) {
@@ -3650,7 +4197,7 @@ class MarkdownModule {
3650
4197
  return null;
3651
4198
  }
3652
4199
  const chunkContents = sections.map((s) => {
3653
- const filename = path11.basename(filepath);
4200
+ const filename = path12.basename(filepath);
3654
4201
  const headingContext = s.heading ? `${s.heading}: ` : "";
3655
4202
  return `${filename} ${headingContext}${s.content}`;
3656
4203
  });
@@ -3837,15 +4384,15 @@ var init_registry = __esm(() => {
3837
4384
  });
3838
4385
 
3839
4386
  // src/infrastructure/introspection/projectDetector.ts
3840
- import * as path12 from "path";
3841
- import * as fs4 from "fs/promises";
4387
+ import * as path13 from "path";
4388
+ import * as fs5 from "fs/promises";
3842
4389
  async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3843
4390
  if (depth > MAX_SCAN_DEPTH)
3844
4391
  return [];
3845
4392
  const results = [];
3846
- const fullDir = currentDir ? path12.join(rootDir, currentDir) : rootDir;
4393
+ const fullDir = currentDir ? path13.join(rootDir, currentDir) : rootDir;
3847
4394
  try {
3848
- const entries = await fs4.readdir(fullDir, { withFileTypes: true });
4395
+ const entries = await fs5.readdir(fullDir, { withFileTypes: true });
3849
4396
  const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
3850
4397
  if (hasPackageJson && currentDir) {
3851
4398
  const info = await parsePackageJson(rootDir, currentDir);
@@ -3866,10 +4413,10 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3866
4413
  }
3867
4414
  async function parsePackageJson(rootDir, relativePath) {
3868
4415
  try {
3869
- const packageJsonPath = path12.join(rootDir, relativePath, "package.json");
3870
- const content = await fs4.readFile(packageJsonPath, "utf-8");
4416
+ const packageJsonPath = path13.join(rootDir, relativePath, "package.json");
4417
+ const content = await fs5.readFile(packageJsonPath, "utf-8");
3871
4418
  const pkg = JSON.parse(content);
3872
- const name = pkg.name || path12.basename(relativePath);
4419
+ const name = pkg.name || path13.basename(relativePath);
3873
4420
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3874
4421
  let type = "unknown";
3875
4422
  if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
@@ -3905,7 +4452,7 @@ async function detectProjectStructure(rootDir) {
3905
4452
  const projectMap = new Map;
3906
4453
  let isMonorepo = false;
3907
4454
  try {
3908
- const entries = await fs4.readdir(rootDir, { withFileTypes: true });
4455
+ const entries = await fs5.readdir(rootDir, { withFileTypes: true });
3909
4456
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
3910
4457
  const monorepoPatterns = ["apps", "packages", "libs", "services"];
3911
4458
  const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
@@ -3914,9 +4461,9 @@ async function detectProjectStructure(rootDir) {
3914
4461
  for (const pattern of monorepoPatterns) {
3915
4462
  if (!dirNames.includes(pattern))
3916
4463
  continue;
3917
- const patternDir = path12.join(rootDir, pattern);
4464
+ const patternDir = path13.join(rootDir, pattern);
3918
4465
  try {
3919
- const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
4466
+ const subDirs = await fs5.readdir(patternDir, { withFileTypes: true });
3920
4467
  for (const subDir of subDirs) {
3921
4468
  if (!subDir.isDirectory())
3922
4469
  continue;
@@ -3945,8 +4492,8 @@ async function detectProjectStructure(rootDir) {
3945
4492
  }
3946
4493
  let rootType = "unknown";
3947
4494
  try {
3948
- const rootPkgPath = path12.join(rootDir, "package.json");
3949
- const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
4495
+ const rootPkgPath = path13.join(rootDir, "package.json");
4496
+ const rootPkg = JSON.parse(await fs5.readFile(rootPkgPath, "utf-8"));
3950
4497
  if (rootPkg.workspaces)
3951
4498
  isMonorepo = true;
3952
4499
  const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
@@ -3985,8 +4532,8 @@ var init_projectDetector = __esm(() => {
3985
4532
  });
3986
4533
 
3987
4534
  // src/infrastructure/introspection/IntrospectionIndex.ts
3988
- import * as path13 from "path";
3989
- import * as fs5 from "fs/promises";
4535
+ import * as path14 from "path";
4536
+ import * as fs6 from "fs/promises";
3990
4537
 
3991
4538
  class IntrospectionIndex {
3992
4539
  rootDir;
@@ -3999,8 +4546,8 @@ class IntrospectionIndex {
3999
4546
  async initialize() {
4000
4547
  this.structure = await detectProjectStructure(this.rootDir);
4001
4548
  try {
4002
- const configPath = path13.join(this.rootDir, ".raggrep", "config.json");
4003
- const configContent = await fs5.readFile(configPath, "utf-8");
4549
+ const configPath = path14.join(this.rootDir, ".raggrep", "config.json");
4550
+ const configContent = await fs6.readFile(configPath, "utf-8");
4004
4551
  const config = JSON.parse(configContent);
4005
4552
  this.config = config.introspection || {};
4006
4553
  } catch {}
@@ -4039,28 +4586,28 @@ class IntrospectionIndex {
4039
4586
  }
4040
4587
  }
4041
4588
  async save(config) {
4042
- const introDir = path13.join(getRaggrepDir(this.rootDir, config), "introspection");
4043
- await fs5.mkdir(introDir, { recursive: true });
4044
- const projectPath = path13.join(introDir, "_project.json");
4045
- await fs5.writeFile(projectPath, JSON.stringify({
4589
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
4590
+ await fs6.mkdir(introDir, { recursive: true });
4591
+ const projectPath = path14.join(introDir, "_project.json");
4592
+ await fs6.writeFile(projectPath, JSON.stringify({
4046
4593
  version: "1.0.0",
4047
4594
  lastUpdated: new Date().toISOString(),
4048
4595
  structure: this.structure
4049
4596
  }, null, 2));
4050
4597
  for (const [filepath, intro] of this.files) {
4051
- const introFilePath = path13.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
4052
- await fs5.mkdir(path13.dirname(introFilePath), { recursive: true });
4053
- await fs5.writeFile(introFilePath, JSON.stringify(intro, null, 2));
4598
+ const introFilePath = path14.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
4599
+ await fs6.mkdir(path14.dirname(introFilePath), { recursive: true });
4600
+ await fs6.writeFile(introFilePath, JSON.stringify(intro, null, 2));
4054
4601
  }
4055
4602
  }
4056
4603
  async load(config) {
4057
- const introDir = path13.join(getRaggrepDir(this.rootDir, config), "introspection");
4604
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
4058
4605
  try {
4059
- const projectPath = path13.join(introDir, "_project.json");
4060
- const projectContent = await fs5.readFile(projectPath, "utf-8");
4606
+ const projectPath = path14.join(introDir, "_project.json");
4607
+ const projectContent = await fs6.readFile(projectPath, "utf-8");
4061
4608
  const projectData = JSON.parse(projectContent);
4062
4609
  this.structure = projectData.structure;
4063
- await this.loadFilesRecursive(path13.join(introDir, "files"), "");
4610
+ await this.loadFilesRecursive(path14.join(introDir, "files"), "");
4064
4611
  } catch {
4065
4612
  this.structure = null;
4066
4613
  this.files.clear();
@@ -4068,14 +4615,14 @@ class IntrospectionIndex {
4068
4615
  }
4069
4616
  async loadFilesRecursive(basePath, prefix) {
4070
4617
  try {
4071
- const entries = await fs5.readdir(basePath, { withFileTypes: true });
4618
+ const entries = await fs6.readdir(basePath, { withFileTypes: true });
4072
4619
  for (const entry of entries) {
4073
- const entryPath = path13.join(basePath, entry.name);
4620
+ const entryPath = path14.join(basePath, entry.name);
4074
4621
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
4075
4622
  if (entry.isDirectory()) {
4076
4623
  await this.loadFilesRecursive(entryPath, relativePath);
4077
4624
  } else if (entry.name.endsWith(".json")) {
4078
- const content = await fs5.readFile(entryPath, "utf-8");
4625
+ const content = await fs6.readFile(entryPath, "utf-8");
4079
4626
  const intro = JSON.parse(content);
4080
4627
  this.files.set(intro.filepath, intro);
4081
4628
  }
@@ -4101,7 +4648,7 @@ var init_introspection2 = __esm(() => {
4101
4648
 
4102
4649
  // src/app/indexer/watcher.ts
4103
4650
  import { watch } from "chokidar";
4104
- import * as path14 from "path";
4651
+ import * as path15 from "path";
4105
4652
  async function watchDirectory(rootDir, options = {}) {
4106
4653
  const {
4107
4654
  debounceMs = DEFAULT_DEBOUNCE_MS,
@@ -4112,7 +4659,7 @@ async function watchDirectory(rootDir, options = {}) {
4112
4659
  onFileChange,
4113
4660
  onError
4114
4661
  } = options;
4115
- rootDir = path14.resolve(rootDir);
4662
+ rootDir = path15.resolve(rootDir);
4116
4663
  const config = await loadConfig(rootDir);
4117
4664
  const indexLocation = getIndexLocation(rootDir);
4118
4665
  const validExtensions = new Set(config.extensions);
@@ -4122,7 +4669,7 @@ async function watchDirectory(rootDir, options = {}) {
4122
4669
  "**/.git/**"
4123
4670
  ];
4124
4671
  function shouldWatchFile(filepath) {
4125
- const ext = path14.extname(filepath);
4672
+ const ext = path15.extname(filepath);
4126
4673
  return validExtensions.has(ext);
4127
4674
  }
4128
4675
  let isRunning = true;
@@ -4204,7 +4751,7 @@ async function watchDirectory(rootDir, options = {}) {
4204
4751
  function handleFileEvent(event, filepath) {
4205
4752
  if (!isRunning)
4206
4753
  return;
4207
- const relativePath = path14.relative(rootDir, filepath);
4754
+ const relativePath = path15.relative(rootDir, filepath);
4208
4755
  if (!shouldWatchFile(filepath)) {
4209
4756
  return;
4210
4757
  }
@@ -4282,8 +4829,8 @@ __export(exports_indexer, {
4282
4829
  cleanupIndex: () => cleanupIndex
4283
4830
  });
4284
4831
  import { glob } from "glob";
4285
- import * as fs6 from "fs/promises";
4286
- import * as path15 from "path";
4832
+ import * as fs7 from "fs/promises";
4833
+ import * as path16 from "path";
4287
4834
  import * as os3 from "os";
4288
4835
  async function parallelMap(items, processor, concurrency) {
4289
4836
  const results = new Array(items.length);
@@ -4326,7 +4873,7 @@ async function indexDirectory(rootDir, options = {}) {
4326
4873
  const quiet = options.quiet ?? false;
4327
4874
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
4328
4875
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4329
- rootDir = path15.resolve(rootDir);
4876
+ rootDir = path16.resolve(rootDir);
4330
4877
  const location = getIndexLocation(rootDir);
4331
4878
  logger.info(`Indexing directory: ${rootDir}`);
4332
4879
  logger.info(`Index location: ${location.indexDir}`);
@@ -4378,12 +4925,12 @@ async function indexDirectory(rootDir, options = {}) {
4378
4925
  rootDir,
4379
4926
  config,
4380
4927
  readFile: async (filepath) => {
4381
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4382
- return fs6.readFile(fullPath, "utf-8");
4928
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
4929
+ return fs7.readFile(fullPath, "utf-8");
4383
4930
  },
4384
4931
  getFileStats: async (filepath) => {
4385
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4386
- const stats = await fs6.stat(fullPath);
4932
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
4933
+ const stats = await fs7.stat(fullPath);
4387
4934
  return { lastModified: stats.mtime.toISOString() };
4388
4935
  }
4389
4936
  };
@@ -4408,7 +4955,7 @@ async function isIndexVersionCompatible(rootDir) {
4408
4955
  const config = await loadConfig(rootDir);
4409
4956
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4410
4957
  try {
4411
- const content = await fs6.readFile(globalManifestPath, "utf-8");
4958
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4412
4959
  const manifest = JSON.parse(content);
4413
4960
  return manifest.version === INDEX_SCHEMA_VERSION;
4414
4961
  } catch {
@@ -4418,11 +4965,11 @@ async function isIndexVersionCompatible(rootDir) {
4418
4965
  async function deleteIndex(rootDir) {
4419
4966
  const indexDir = getRaggrepDir(rootDir);
4420
4967
  try {
4421
- await fs6.rm(indexDir, { recursive: true, force: true });
4968
+ await fs7.rm(indexDir, { recursive: true, force: true });
4422
4969
  } catch {}
4423
4970
  }
4424
4971
  async function resetIndex(rootDir) {
4425
- rootDir = path15.resolve(rootDir);
4972
+ rootDir = path16.resolve(rootDir);
4426
4973
  const status = await getIndexStatus(rootDir);
4427
4974
  if (!status.exists) {
4428
4975
  throw new Error(`No index found for ${rootDir}`);
@@ -4437,7 +4984,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4437
4984
  const verbose = options.verbose ?? false;
4438
4985
  const quiet = options.quiet ?? false;
4439
4986
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4440
- rootDir = path15.resolve(rootDir);
4987
+ rootDir = path16.resolve(rootDir);
4441
4988
  const status = await getIndexStatus(rootDir);
4442
4989
  if (!status.exists) {
4443
4990
  logger.info(`No index found. Creating index...
@@ -4464,7 +5011,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4464
5011
  const introspection = new IntrospectionIndex(rootDir);
4465
5012
  await introspection.initialize();
4466
5013
  const currentFiles = await findFiles(rootDir, config);
4467
- const currentFileSet = new Set(currentFiles.map((f) => path15.relative(rootDir, f)));
5014
+ const currentFileSet = new Set(currentFiles.map((f) => path16.relative(rootDir, f)));
4468
5015
  let totalIndexed = 0;
4469
5016
  let totalRemoved = 0;
4470
5017
  let totalUnchanged = 0;
@@ -4494,13 +5041,13 @@ async function ensureIndexFresh(rootDir, options = {}) {
4494
5041
  }
4495
5042
  for (const filepath of filesToRemove) {
4496
5043
  logger.debug(` Removing stale: ${filepath}`);
4497
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5044
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4498
5045
  try {
4499
- await fs6.unlink(indexFilePath);
5046
+ await fs7.unlink(indexFilePath);
4500
5047
  } catch {}
4501
- const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
5048
+ const symbolicFilePath = path16.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4502
5049
  try {
4503
- await fs6.unlink(symbolicFilePath);
5050
+ await fs7.unlink(symbolicFilePath);
4504
5051
  } catch {}
4505
5052
  delete manifest.files[filepath];
4506
5053
  totalRemoved++;
@@ -4509,12 +5056,12 @@ async function ensureIndexFresh(rootDir, options = {}) {
4509
5056
  rootDir,
4510
5057
  config,
4511
5058
  readFile: async (filepath) => {
4512
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4513
- return fs6.readFile(fullPath, "utf-8");
5059
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5060
+ return fs7.readFile(fullPath, "utf-8");
4514
5061
  },
4515
5062
  getFileStats: async (filepath) => {
4516
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4517
- const stats = await fs6.stat(fullPath);
5063
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5064
+ const stats = await fs7.stat(fullPath);
4518
5065
  return { lastModified: stats.mtime.toISOString() };
4519
5066
  },
4520
5067
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4522,10 +5069,10 @@ async function ensureIndexFresh(rootDir, options = {}) {
4522
5069
  const totalFiles = currentFiles.length;
4523
5070
  for (let i = 0;i < currentFiles.length; i++) {
4524
5071
  const filepath = currentFiles[i];
4525
- const relativePath = path15.relative(rootDir, filepath);
5072
+ const relativePath = path16.relative(rootDir, filepath);
4526
5073
  const progress = `[${i + 1}/${totalFiles}]`;
4527
5074
  try {
4528
- const stats = await fs6.stat(filepath);
5075
+ const stats = await fs7.stat(filepath);
4529
5076
  const lastModified = stats.mtime.toISOString();
4530
5077
  const existingEntry = manifest.files[relativePath];
4531
5078
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4533,7 +5080,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4533
5080
  continue;
4534
5081
  }
4535
5082
  logger.progress(` ${progress} Indexing: ${relativePath}`);
4536
- const content = await fs6.readFile(filepath, "utf-8");
5083
+ const content = await fs7.readFile(filepath, "utf-8");
4537
5084
  introspection.addFile(relativePath, content);
4538
5085
  const fileIndex = await module.indexFile(relativePath, content, ctx);
4539
5086
  if (fileIndex) {
@@ -4582,7 +5129,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4582
5129
  };
4583
5130
  const manifest = await loadModuleManifest(rootDir, module.id, config);
4584
5131
  const indexPath = getModuleIndexPath(rootDir, module.id, config);
4585
- const currentFileSet = new Set(files.map((f) => path15.relative(rootDir, f)));
5132
+ const currentFileSet = new Set(files.map((f) => path16.relative(rootDir, f)));
4586
5133
  const filesToRemove = [];
4587
5134
  for (const filepath of Object.keys(manifest.files)) {
4588
5135
  if (!currentFileSet.has(filepath)) {
@@ -4593,13 +5140,13 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4593
5140
  logger.info(` Removing ${filesToRemove.length} stale entries...`);
4594
5141
  for (const filepath of filesToRemove) {
4595
5142
  logger.debug(` Removing: ${filepath}`);
4596
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5143
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4597
5144
  try {
4598
- await fs6.unlink(indexFilePath);
5145
+ await fs7.unlink(indexFilePath);
4599
5146
  } catch {}
4600
- const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
5147
+ const symbolicFilePath = path16.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4601
5148
  try {
4602
- await fs6.unlink(symbolicFilePath);
5149
+ await fs7.unlink(symbolicFilePath);
4603
5150
  } catch {}
4604
5151
  delete manifest.files[filepath];
4605
5152
  }
@@ -4609,12 +5156,12 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4609
5156
  rootDir,
4610
5157
  config,
4611
5158
  readFile: async (filepath) => {
4612
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4613
- return fs6.readFile(fullPath, "utf-8");
5159
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5160
+ return fs7.readFile(fullPath, "utf-8");
4614
5161
  },
4615
5162
  getFileStats: async (filepath) => {
4616
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4617
- const stats = await fs6.stat(fullPath);
5163
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5164
+ const stats = await fs7.stat(fullPath);
4618
5165
  return { lastModified: stats.mtime.toISOString() };
4619
5166
  },
4620
5167
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4622,9 +5169,9 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4622
5169
  const totalFiles = files.length;
4623
5170
  let completedCount = 0;
4624
5171
  const processFile = async (filepath, _index) => {
4625
- const relativePath = path15.relative(rootDir, filepath);
5172
+ const relativePath = path16.relative(rootDir, filepath);
4626
5173
  try {
4627
- const stats = await fs6.stat(filepath);
5174
+ const stats = await fs7.stat(filepath);
4628
5175
  const lastModified = stats.mtime.toISOString();
4629
5176
  const existingEntry = manifest.files[relativePath];
4630
5177
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4632,7 +5179,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4632
5179
  logger.debug(` [${completedCount}/${totalFiles}] Skipped ${relativePath} (unchanged)`);
4633
5180
  return { relativePath, status: "skipped" };
4634
5181
  }
4635
- const content = await fs6.readFile(filepath, "utf-8");
5182
+ const content = await fs7.readFile(filepath, "utf-8");
4636
5183
  introspection.addFile(relativePath, content);
4637
5184
  completedCount++;
4638
5185
  logger.progress(` [${completedCount}/${totalFiles}] Processing: ${relativePath}`);
@@ -4700,7 +5247,7 @@ async function findFiles(rootDir, config) {
4700
5247
  async function loadModuleManifest(rootDir, moduleId, config) {
4701
5248
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
4702
5249
  try {
4703
- const content = await fs6.readFile(manifestPath, "utf-8");
5250
+ const content = await fs7.readFile(manifestPath, "utf-8");
4704
5251
  return JSON.parse(content);
4705
5252
  } catch {
4706
5253
  return {
@@ -4713,14 +5260,14 @@ async function loadModuleManifest(rootDir, moduleId, config) {
4713
5260
  }
4714
5261
  async function writeModuleManifest(rootDir, moduleId, manifest, config) {
4715
5262
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
4716
- await fs6.mkdir(path15.dirname(manifestPath), { recursive: true });
4717
- await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
5263
+ await fs7.mkdir(path16.dirname(manifestPath), { recursive: true });
5264
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4718
5265
  }
4719
5266
  async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
4720
5267
  const indexPath = getModuleIndexPath(rootDir, moduleId, config);
4721
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4722
- await fs6.mkdir(path15.dirname(indexFilePath), { recursive: true });
4723
- await fs6.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
5268
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5269
+ await fs7.mkdir(path16.dirname(indexFilePath), { recursive: true });
5270
+ await fs7.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
4724
5271
  }
4725
5272
  async function updateGlobalManifest(rootDir, modules, config) {
4726
5273
  const manifestPath = getGlobalManifestPath(rootDir, config);
@@ -4729,13 +5276,13 @@ async function updateGlobalManifest(rootDir, modules, config) {
4729
5276
  lastUpdated: new Date().toISOString(),
4730
5277
  modules: modules.map((m) => m.id)
4731
5278
  };
4732
- await fs6.mkdir(path15.dirname(manifestPath), { recursive: true });
4733
- await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
5279
+ await fs7.mkdir(path16.dirname(manifestPath), { recursive: true });
5280
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4734
5281
  }
4735
5282
  async function cleanupIndex(rootDir, options = {}) {
4736
5283
  const verbose = options.verbose ?? false;
4737
5284
  const logger = options.logger ?? createLogger({ verbose });
4738
- rootDir = path15.resolve(rootDir);
5285
+ rootDir = path16.resolve(rootDir);
4739
5286
  logger.info(`Cleaning up index in: ${rootDir}`);
4740
5287
  const config = await loadConfig(rootDir);
4741
5288
  await registerBuiltInModules();
@@ -4765,9 +5312,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4765
5312
  const filesToRemove = [];
4766
5313
  const updatedFiles = {};
4767
5314
  for (const [filepath, entry] of Object.entries(manifest.files)) {
4768
- const fullPath = path15.join(rootDir, filepath);
5315
+ const fullPath = path16.join(rootDir, filepath);
4769
5316
  try {
4770
- await fs6.access(fullPath);
5317
+ await fs7.access(fullPath);
4771
5318
  updatedFiles[filepath] = entry;
4772
5319
  result.kept++;
4773
5320
  } catch {
@@ -4777,9 +5324,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4777
5324
  }
4778
5325
  }
4779
5326
  for (const filepath of filesToRemove) {
4780
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5327
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4781
5328
  try {
4782
- await fs6.unlink(indexFilePath);
5329
+ await fs7.unlink(indexFilePath);
4783
5330
  } catch {}
4784
5331
  }
4785
5332
  manifest.files = updatedFiles;
@@ -4790,16 +5337,16 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4790
5337
  }
4791
5338
  async function cleanupEmptyDirectories(dir) {
4792
5339
  try {
4793
- const entries = await fs6.readdir(dir, { withFileTypes: true });
5340
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
4794
5341
  for (const entry of entries) {
4795
5342
  if (entry.isDirectory()) {
4796
- const subDir = path15.join(dir, entry.name);
5343
+ const subDir = path16.join(dir, entry.name);
4797
5344
  await cleanupEmptyDirectories(subDir);
4798
5345
  }
4799
5346
  }
4800
- const remainingEntries = await fs6.readdir(dir);
5347
+ const remainingEntries = await fs7.readdir(dir);
4801
5348
  if (remainingEntries.length === 0) {
4802
- await fs6.rmdir(dir);
5349
+ await fs7.rmdir(dir);
4803
5350
  return true;
4804
5351
  }
4805
5352
  return false;
@@ -4808,7 +5355,7 @@ async function cleanupEmptyDirectories(dir) {
4808
5355
  }
4809
5356
  }
4810
5357
  async function getIndexStatus(rootDir) {
4811
- rootDir = path15.resolve(rootDir);
5358
+ rootDir = path16.resolve(rootDir);
4812
5359
  const config = await loadConfig(rootDir);
4813
5360
  const location = getIndexLocation(rootDir);
4814
5361
  const indexDir = location.indexDir;
@@ -4820,13 +5367,13 @@ async function getIndexStatus(rootDir) {
4820
5367
  totalFiles: 0
4821
5368
  };
4822
5369
  try {
4823
- await fs6.access(indexDir);
5370
+ await fs7.access(indexDir);
4824
5371
  } catch {
4825
5372
  return status;
4826
5373
  }
4827
5374
  try {
4828
5375
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4829
- const content = await fs6.readFile(globalManifestPath, "utf-8");
5376
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4830
5377
  const globalManifest = JSON.parse(content);
4831
5378
  status.exists = true;
4832
5379
  status.lastUpdated = globalManifest.lastUpdated;
@@ -4844,7 +5391,7 @@ async function getIndexStatus(rootDir) {
4844
5391
  }
4845
5392
  } catch {
4846
5393
  try {
4847
- const entries = await fs6.readdir(path15.join(indexDir, "index"));
5394
+ const entries = await fs7.readdir(path16.join(indexDir, "index"));
4848
5395
  if (entries.length > 0) {
4849
5396
  status.exists = true;
4850
5397
  for (const entry of entries) {
@@ -4885,10 +5432,10 @@ __export(exports_search, {
4885
5432
  search: () => search,
4886
5433
  formatSearchResults: () => formatSearchResults
4887
5434
  });
4888
- import * as fs7 from "fs/promises";
4889
- import * as path16 from "path";
5435
+ import * as fs8 from "fs/promises";
5436
+ import * as path17 from "path";
4890
5437
  async function search(rootDir, query, options = {}) {
4891
- rootDir = path16.resolve(rootDir);
5438
+ rootDir = path17.resolve(rootDir);
4892
5439
  const ensureFresh = options.ensureFresh ?? DEFAULT_SEARCH_OPTIONS.ensureFresh;
4893
5440
  if (ensureFresh) {
4894
5441
  await ensureIndexFresh(rootDir, { quiet: true });
@@ -4941,9 +5488,9 @@ function createSearchContext(rootDir, moduleId, config) {
4941
5488
  config,
4942
5489
  loadFileIndex: async (filepath) => {
4943
5490
  const hasExtension = /\.[^./]+$/.test(filepath);
4944
- const indexFilePath = hasExtension ? path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path16.join(indexPath, filepath + ".json");
5491
+ const indexFilePath = hasExtension ? path17.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path17.join(indexPath, filepath + ".json");
4945
5492
  try {
4946
- const content = await fs7.readFile(indexFilePath, "utf-8");
5493
+ const content = await fs8.readFile(indexFilePath, "utf-8");
4947
5494
  return JSON.parse(content);
4948
5495
  } catch {
4949
5496
  return null;
@@ -4953,7 +5500,7 @@ function createSearchContext(rootDir, moduleId, config) {
4953
5500
  const files = [];
4954
5501
  await traverseDirectory(indexPath, files, indexPath);
4955
5502
  return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
4956
- const relative4 = path16.relative(indexPath, f);
5503
+ const relative4 = path17.relative(indexPath, f);
4957
5504
  return relative4.replace(/\.json$/, "");
4958
5505
  });
4959
5506
  }
@@ -4961,9 +5508,9 @@ function createSearchContext(rootDir, moduleId, config) {
4961
5508
  }
4962
5509
  async function traverseDirectory(dir, files, basePath) {
4963
5510
  try {
4964
- const entries = await fs7.readdir(dir, { withFileTypes: true });
5511
+ const entries = await fs8.readdir(dir, { withFileTypes: true });
4965
5512
  for (const entry of entries) {
4966
- const fullPath = path16.join(dir, entry.name);
5513
+ const fullPath = path17.join(dir, entry.name);
4967
5514
  if (entry.isDirectory()) {
4968
5515
  await traverseDirectory(fullPath, files, basePath);
4969
5516
  } else if (entry.isFile()) {
@@ -4975,7 +5522,7 @@ async function traverseDirectory(dir, files, basePath) {
4975
5522
  async function loadGlobalManifest(rootDir, config) {
4976
5523
  const manifestPath = getGlobalManifestPath(rootDir, config);
4977
5524
  try {
4978
- const content = await fs7.readFile(manifestPath, "utf-8");
5525
+ const content = await fs8.readFile(manifestPath, "utf-8");
4979
5526
  return JSON.parse(content);
4980
5527
  } catch {
4981
5528
  return null;
@@ -5041,7 +5588,7 @@ init_logger();
5041
5588
  // package.json
5042
5589
  var package_default = {
5043
5590
  name: "raggrep",
5044
- version: "0.6.1",
5591
+ version: "0.7.0",
5045
5592
  description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
5046
5593
  type: "module",
5047
5594
  main: "./dist/index.js",
@@ -5502,4 +6049,4 @@ Run 'raggrep <command> --help' for more information.
5502
6049
  }
5503
6050
  main();
5504
6051
 
5505
- //# debugId=8FCF189E9FBCBFE264756E2164756E21
6052
+ //# debugId=4DE86C1D7DC4E21964756E2164756E21