raggrep 0.6.1 → 0.7.1

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,182 @@ var init_symbolicIndex = __esm(() => {
2983
3276
  init_keywords();
2984
3277
  });
2985
3278
 
3279
+ // src/infrastructure/storage/literalIndex.ts
3280
+ var exports_literalIndex = {};
3281
+ __export(exports_literalIndex, {
3282
+ getLiteralIndexPath: () => getLiteralIndexPath,
3283
+ LiteralIndex: () => LiteralIndex
3284
+ });
3285
+ import * as fs4 from "fs/promises";
3286
+ import * as path9 from "path";
3287
+
3288
+ class LiteralIndex {
3289
+ indexPath;
3290
+ moduleId;
3291
+ entries = new Map;
3292
+ static VERSION = "1.0.0";
3293
+ constructor(indexDir, moduleId) {
3294
+ this.indexPath = path9.join(indexDir, "index", moduleId, "literals");
3295
+ this.moduleId = moduleId;
3296
+ }
3297
+ async initialize() {
3298
+ try {
3299
+ await this.load();
3300
+ } catch {
3301
+ this.entries = new Map;
3302
+ }
3303
+ }
3304
+ addLiterals(chunkId, filepath, literals) {
3305
+ for (const literal of literals) {
3306
+ const key = literal.value.toLowerCase();
3307
+ const existingEntries = this.entries.get(key) || [];
3308
+ const existingIndex = existingEntries.findIndex((e) => e.chunkId === chunkId);
3309
+ const newEntry = {
3310
+ chunkId,
3311
+ filepath,
3312
+ originalCasing: literal.value,
3313
+ type: literal.type,
3314
+ matchType: literal.matchType
3315
+ };
3316
+ if (existingIndex >= 0) {
3317
+ const existing = existingEntries[existingIndex];
3318
+ if (shouldReplaceMatchType(existing.matchType, literal.matchType)) {
3319
+ existingEntries[existingIndex] = newEntry;
3320
+ }
3321
+ } else {
3322
+ existingEntries.push(newEntry);
3323
+ }
3324
+ this.entries.set(key, existingEntries);
3325
+ }
3326
+ }
3327
+ removeChunk(chunkId) {
3328
+ for (const [key, entries] of this.entries) {
3329
+ const filtered = entries.filter((e) => e.chunkId !== chunkId);
3330
+ if (filtered.length === 0) {
3331
+ this.entries.delete(key);
3332
+ } else if (filtered.length !== entries.length) {
3333
+ this.entries.set(key, filtered);
3334
+ }
3335
+ }
3336
+ }
3337
+ removeFile(filepath) {
3338
+ let removed = 0;
3339
+ for (const [key, entries] of this.entries) {
3340
+ const filtered = entries.filter((e) => e.filepath !== filepath);
3341
+ const removedCount = entries.length - filtered.length;
3342
+ if (removedCount > 0) {
3343
+ removed += removedCount;
3344
+ if (filtered.length === 0) {
3345
+ this.entries.delete(key);
3346
+ } else {
3347
+ this.entries.set(key, filtered);
3348
+ }
3349
+ }
3350
+ }
3351
+ return removed;
3352
+ }
3353
+ findMatches(queryLiterals) {
3354
+ const matches = [];
3355
+ for (const queryLiteral of queryLiterals) {
3356
+ const key = queryLiteral.value.toLowerCase();
3357
+ const entries = this.entries.get(key);
3358
+ if (!entries) {
3359
+ continue;
3360
+ }
3361
+ for (const entry of entries) {
3362
+ const exactMatch = entry.originalCasing === queryLiteral.value;
3363
+ matches.push({
3364
+ queryLiteral,
3365
+ indexedLiteral: {
3366
+ value: entry.originalCasing,
3367
+ type: entry.type,
3368
+ matchType: entry.matchType
3369
+ },
3370
+ chunkId: entry.chunkId,
3371
+ filepath: entry.filepath,
3372
+ exactMatch
3373
+ });
3374
+ }
3375
+ }
3376
+ return matches;
3377
+ }
3378
+ getChunksForLiteral(literal) {
3379
+ const key = literal.toLowerCase();
3380
+ const entries = this.entries.get(key);
3381
+ return entries ? entries.map((e) => e.chunkId) : [];
3382
+ }
3383
+ async save() {
3384
+ await fs4.mkdir(this.indexPath, { recursive: true });
3385
+ const data = {
3386
+ version: LiteralIndex.VERSION,
3387
+ entries: Object.fromEntries(this.entries)
3388
+ };
3389
+ const indexFile = path9.join(this.indexPath, "_index.json");
3390
+ await fs4.writeFile(indexFile, JSON.stringify(data, null, 2));
3391
+ }
3392
+ async load() {
3393
+ const indexFile = path9.join(this.indexPath, "_index.json");
3394
+ const content = await fs4.readFile(indexFile, "utf-8");
3395
+ const data = JSON.parse(content);
3396
+ if (data.version !== LiteralIndex.VERSION) {
3397
+ console.warn(`Literal index version mismatch: expected ${LiteralIndex.VERSION}, got ${data.version}`);
3398
+ }
3399
+ this.entries = new Map(Object.entries(data.entries));
3400
+ }
3401
+ async exists() {
3402
+ try {
3403
+ const indexFile = path9.join(this.indexPath, "_index.json");
3404
+ await fs4.access(indexFile);
3405
+ return true;
3406
+ } catch {
3407
+ return false;
3408
+ }
3409
+ }
3410
+ clear() {
3411
+ this.entries.clear();
3412
+ }
3413
+ get size() {
3414
+ return this.entries.size;
3415
+ }
3416
+ get totalMappings() {
3417
+ let count = 0;
3418
+ for (const entries of this.entries.values()) {
3419
+ count += entries.length;
3420
+ }
3421
+ return count;
3422
+ }
3423
+ getAllLiterals() {
3424
+ return Array.from(this.entries.keys());
3425
+ }
3426
+ buildMatchMap(queryLiterals) {
3427
+ const matches = this.findMatches(queryLiterals);
3428
+ const matchMap = new Map;
3429
+ for (const match of matches) {
3430
+ const existing = matchMap.get(match.chunkId) || [];
3431
+ existing.push(match);
3432
+ matchMap.set(match.chunkId, existing);
3433
+ }
3434
+ return matchMap;
3435
+ }
3436
+ }
3437
+ function shouldReplaceMatchType(existing, incoming) {
3438
+ const priority = {
3439
+ definition: 3,
3440
+ reference: 2,
3441
+ import: 1
3442
+ };
3443
+ return priority[incoming] > priority[existing];
3444
+ }
3445
+ function getLiteralIndexPath(rootDir, moduleId, indexDir = ".raggrep") {
3446
+ return path9.join(rootDir, indexDir, "index", moduleId, "literals");
3447
+ }
3448
+ var init_literalIndex = () => {};
3449
+
2986
3450
  // src/infrastructure/storage/index.ts
2987
3451
  var init_storage = __esm(() => {
2988
3452
  init_fileIndexStorage();
2989
3453
  init_symbolicIndex();
3454
+ init_literalIndex();
2990
3455
  });
2991
3456
 
2992
3457
  // src/modules/language/typescript/index.ts
@@ -2999,9 +3464,9 @@ __export(exports_typescript, {
2999
3464
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
3000
3465
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
3001
3466
  });
3002
- import * as path9 from "path";
3467
+ import * as path10 from "path";
3003
3468
  function isTypeScriptFile(filepath) {
3004
- const ext = path9.extname(filepath).toLowerCase();
3469
+ const ext = path10.extname(filepath).toLowerCase();
3005
3470
  return TYPESCRIPT_EXTENSIONS.includes(ext);
3006
3471
  }
3007
3472
  function calculateChunkTypeBoost(chunk) {
@@ -3036,7 +3501,9 @@ class TypeScriptModule {
3036
3501
  }
3037
3502
  embeddingConfig = null;
3038
3503
  symbolicIndex = null;
3504
+ literalIndex = null;
3039
3505
  pendingSummaries = new Map;
3506
+ pendingLiterals = new Map;
3040
3507
  rootDir = "";
3041
3508
  logger = undefined;
3042
3509
  async initialize(config) {
@@ -3050,6 +3517,7 @@ class TypeScriptModule {
3050
3517
  }
3051
3518
  configureEmbeddings(this.embeddingConfig);
3052
3519
  this.pendingSummaries.clear();
3520
+ this.pendingLiterals.clear();
3053
3521
  }
3054
3522
  async indexFile(filepath, content, ctx) {
3055
3523
  if (!isTypeScriptFile(filepath)) {
@@ -3109,6 +3577,17 @@ class TypeScriptModule {
3109
3577
  }
3110
3578
  };
3111
3579
  this.pendingSummaries.set(filepath, fileSummary);
3580
+ for (const chunk of chunks) {
3581
+ const literals = extractLiterals(chunk);
3582
+ if (literals.length > 0) {
3583
+ const existing = this.pendingLiterals.get(chunk.id);
3584
+ if (existing) {
3585
+ existing.literals.push(...literals);
3586
+ } else {
3587
+ this.pendingLiterals.set(chunk.id, { filepath, literals });
3588
+ }
3589
+ }
3590
+ }
3112
3591
  return {
3113
3592
  filepath,
3114
3593
  lastModified: stats.lastModified,
@@ -3126,7 +3605,24 @@ class TypeScriptModule {
3126
3605
  }
3127
3606
  this.symbolicIndex.buildBM25Index();
3128
3607
  await this.symbolicIndex.save();
3608
+ this.literalIndex = new LiteralIndex(indexDir, this.id);
3609
+ await this.literalIndex.initialize();
3610
+ const indexedFilepaths = new Set;
3611
+ for (const filepath of this.pendingSummaries.keys()) {
3612
+ indexedFilepaths.add(filepath);
3613
+ }
3614
+ for (const { filepath } of this.pendingLiterals.values()) {
3615
+ indexedFilepaths.add(filepath);
3616
+ }
3617
+ for (const filepath of indexedFilepaths) {
3618
+ this.literalIndex.removeFile(filepath);
3619
+ }
3620
+ for (const [chunkId, { filepath, literals }] of this.pendingLiterals) {
3621
+ this.literalIndex.addLiterals(chunkId, filepath, literals);
3622
+ }
3623
+ await this.literalIndex.save();
3129
3624
  this.pendingSummaries.clear();
3625
+ this.pendingLiterals.clear();
3130
3626
  }
3131
3627
  async search(query, ctx, options = {}) {
3132
3628
  const {
@@ -3134,8 +3630,15 @@ class TypeScriptModule {
3134
3630
  minScore = DEFAULT_MIN_SCORE2,
3135
3631
  filePatterns
3136
3632
  } = options;
3633
+ const { literals: queryLiterals, remainingQuery } = parseQueryLiterals(query);
3137
3634
  const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
3138
3635
  const symbolicIndex = new SymbolicIndex(indexDir, this.id);
3636
+ const literalIndex = new LiteralIndex(indexDir, this.id);
3637
+ let literalMatchMap = new Map;
3638
+ try {
3639
+ await literalIndex.initialize();
3640
+ literalMatchMap = literalIndex.buildMatchMap(queryLiterals);
3641
+ } catch {}
3139
3642
  let allFiles;
3140
3643
  try {
3141
3644
  await symbolicIndex.initialize();
@@ -3155,7 +3658,8 @@ class TypeScriptModule {
3155
3658
  });
3156
3659
  });
3157
3660
  }
3158
- const queryEmbedding = await getEmbedding(query);
3661
+ const semanticQuery = remainingQuery.trim() || query;
3662
+ const queryEmbedding = await getEmbedding(semanticQuery);
3159
3663
  const bm25Index = new BM25Index;
3160
3664
  const allChunksData = [];
3161
3665
  for (const filepath of filesToSearch) {
@@ -3189,14 +3693,14 @@ class TypeScriptModule {
3189
3693
  const summary = symbolicIndex.getFileSummary(filepath);
3190
3694
  if (summary?.pathContext) {
3191
3695
  let boost = 0;
3192
- const ctx2 = summary.pathContext;
3193
- if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
3696
+ const pathCtx = summary.pathContext;
3697
+ if (pathCtx.domain && queryTerms.some((t) => pathCtx.domain.includes(t) || t.includes(pathCtx.domain))) {
3194
3698
  boost += 0.1;
3195
3699
  }
3196
- if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
3700
+ if (pathCtx.layer && queryTerms.some((t) => pathCtx.layer.includes(t) || t.includes(pathCtx.layer))) {
3197
3701
  boost += 0.05;
3198
3702
  }
3199
- const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3703
+ const segmentMatch = pathCtx.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3200
3704
  if (segmentMatch) {
3201
3705
  boost += 0.05;
3202
3706
  }
@@ -3204,6 +3708,7 @@ class TypeScriptModule {
3204
3708
  }
3205
3709
  }
3206
3710
  const results = [];
3711
+ const processedChunkIds = new Set;
3207
3712
  for (const { filepath, chunk, embedding } of allChunksData) {
3208
3713
  const semanticScore = cosineSimilarity(queryEmbedding, embedding);
3209
3714
  const bm25Score = bm25Scores.get(chunk.id) || 0;
@@ -3211,13 +3716,18 @@ class TypeScriptModule {
3211
3716
  const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3212
3717
  const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3213
3718
  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) {
3719
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3720
+ const baseScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score;
3721
+ const literalMatches = literalMatchMap.get(chunk.id) || [];
3722
+ const literalContribution = calculateLiteralContribution(literalMatches, true);
3723
+ const boostedScore = applyLiteralBoost(baseScore, literalMatches, true);
3724
+ const finalScore = boostedScore + additiveBoost;
3725
+ processedChunkIds.add(chunk.id);
3726
+ if (finalScore >= minScore || bm25Score > 0.3 || literalMatches.length > 0) {
3217
3727
  results.push({
3218
3728
  filepath,
3219
3729
  chunk,
3220
- score: hybridScore,
3730
+ score: finalScore,
3221
3731
  moduleId: this.id,
3222
3732
  context: {
3223
3733
  semanticScore,
@@ -3225,7 +3735,78 @@ class TypeScriptModule {
3225
3735
  pathBoost,
3226
3736
  fileTypeBoost,
3227
3737
  chunkTypeBoost,
3228
- exportBoost
3738
+ exportBoost,
3739
+ literalMultiplier: literalContribution.multiplier,
3740
+ literalMatchType: literalContribution.bestMatchType,
3741
+ literalConfidence: literalContribution.bestConfidence,
3742
+ literalMatchCount: literalContribution.matchCount
3743
+ }
3744
+ });
3745
+ }
3746
+ }
3747
+ const literalOnlyFiles = new Map;
3748
+ for (const [chunkId, matches] of literalMatchMap) {
3749
+ if (processedChunkIds.has(chunkId)) {
3750
+ continue;
3751
+ }
3752
+ const filepath = matches[0]?.filepath;
3753
+ if (!filepath)
3754
+ continue;
3755
+ const existing = literalOnlyFiles.get(filepath) || [];
3756
+ existing.push(...matches);
3757
+ literalOnlyFiles.set(filepath, existing);
3758
+ }
3759
+ for (const [filepath, matches] of literalOnlyFiles) {
3760
+ const fileIndex = await ctx.loadFileIndex(filepath);
3761
+ if (!fileIndex)
3762
+ continue;
3763
+ const moduleData = fileIndex.moduleData;
3764
+ const chunkMatches = new Map;
3765
+ for (const match of matches) {
3766
+ const existing = chunkMatches.get(match.chunkId) || [];
3767
+ existing.push(match);
3768
+ chunkMatches.set(match.chunkId, existing);
3769
+ }
3770
+ for (const [chunkId, chunkLiteralMatches] of chunkMatches) {
3771
+ if (processedChunkIds.has(chunkId))
3772
+ continue;
3773
+ const chunkIndex = fileIndex.chunks.findIndex((c) => c.id === chunkId);
3774
+ if (chunkIndex === -1)
3775
+ continue;
3776
+ const chunk = fileIndex.chunks[chunkIndex];
3777
+ const embedding = moduleData?.embeddings?.[chunkIndex];
3778
+ let semanticScore = 0;
3779
+ if (embedding) {
3780
+ semanticScore = cosineSimilarity(queryEmbedding, embedding);
3781
+ }
3782
+ const bm25Score = bm25Scores.get(chunkId) || 0;
3783
+ const pathBoost = pathBoosts.get(filepath) || 0;
3784
+ const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3785
+ const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3786
+ const exportBoost = calculateExportBoost(chunk);
3787
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3788
+ const literalContribution = calculateLiteralContribution(chunkLiteralMatches, false);
3789
+ const baseScore = semanticScore > 0 ? SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score : LITERAL_SCORING_CONSTANTS.BASE_SCORE;
3790
+ const boostedScore = applyLiteralBoost(baseScore, chunkLiteralMatches, semanticScore > 0);
3791
+ const finalScore = boostedScore + additiveBoost;
3792
+ processedChunkIds.add(chunkId);
3793
+ results.push({
3794
+ filepath,
3795
+ chunk,
3796
+ score: finalScore,
3797
+ moduleId: this.id,
3798
+ context: {
3799
+ semanticScore,
3800
+ bm25Score,
3801
+ pathBoost,
3802
+ fileTypeBoost,
3803
+ chunkTypeBoost,
3804
+ exportBoost,
3805
+ literalMultiplier: literalContribution.multiplier,
3806
+ literalMatchType: literalContribution.bestMatchType,
3807
+ literalConfidence: literalContribution.bestConfidence,
3808
+ literalMatchCount: literalContribution.matchCount,
3809
+ literalOnly: true
3229
3810
  }
3230
3811
  });
3231
3812
  }
@@ -3241,16 +3822,16 @@ class TypeScriptModule {
3241
3822
  while ((match = importRegex.exec(content)) !== null) {
3242
3823
  const importPath = match[1];
3243
3824
  if (importPath.startsWith(".")) {
3244
- const dir = path9.dirname(filepath);
3245
- const resolved = path9.normalize(path9.join(dir, importPath));
3825
+ const dir = path10.dirname(filepath);
3826
+ const resolved = path10.normalize(path10.join(dir, importPath));
3246
3827
  references.push(resolved);
3247
3828
  }
3248
3829
  }
3249
3830
  while ((match = requireRegex.exec(content)) !== null) {
3250
3831
  const importPath = match[1];
3251
3832
  if (importPath.startsWith(".")) {
3252
- const dir = path9.dirname(filepath);
3253
- const resolved = path9.normalize(path9.join(dir, importPath));
3833
+ const dir = path10.dirname(filepath);
3834
+ const resolved = path10.normalize(path10.join(dir, importPath));
3254
3835
  references.push(resolved);
3255
3836
  }
3256
3837
  }
@@ -3287,9 +3868,9 @@ __export(exports_json, {
3287
3868
  DEFAULT_TOP_K: () => DEFAULT_TOP_K3,
3288
3869
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE3
3289
3870
  });
3290
- import * as path10 from "path";
3871
+ import * as path11 from "path";
3291
3872
  function isJsonFile(filepath) {
3292
- const ext = path10.extname(filepath).toLowerCase();
3873
+ const ext = path11.extname(filepath).toLowerCase();
3293
3874
  return JSON_EXTENSIONS.includes(ext);
3294
3875
  }
3295
3876
  function extractJsonKeys(obj, prefix = "") {
@@ -3370,7 +3951,7 @@ class JsonModule {
3370
3951
  return null;
3371
3952
  }
3372
3953
  const chunkContents = textChunks.map((c) => {
3373
- const filename = path10.basename(filepath);
3954
+ const filename = path11.basename(filepath);
3374
3955
  return `${filename}: ${c.content}`;
3375
3956
  });
3376
3957
  const embeddings = await getEmbeddings(chunkContents);
@@ -3521,9 +4102,9 @@ __export(exports_markdown, {
3521
4102
  DEFAULT_TOP_K: () => DEFAULT_TOP_K4,
3522
4103
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE4
3523
4104
  });
3524
- import * as path11 from "path";
4105
+ import * as path12 from "path";
3525
4106
  function isMarkdownFile(filepath) {
3526
- const ext = path11.extname(filepath).toLowerCase();
4107
+ const ext = path12.extname(filepath).toLowerCase();
3527
4108
  return MARKDOWN_EXTENSIONS.includes(ext);
3528
4109
  }
3529
4110
  function parseMarkdownSections(content) {
@@ -3650,7 +4231,7 @@ class MarkdownModule {
3650
4231
  return null;
3651
4232
  }
3652
4233
  const chunkContents = sections.map((s) => {
3653
- const filename = path11.basename(filepath);
4234
+ const filename = path12.basename(filepath);
3654
4235
  const headingContext = s.heading ? `${s.heading}: ` : "";
3655
4236
  return `${filename} ${headingContext}${s.content}`;
3656
4237
  });
@@ -3837,15 +4418,15 @@ var init_registry = __esm(() => {
3837
4418
  });
3838
4419
 
3839
4420
  // src/infrastructure/introspection/projectDetector.ts
3840
- import * as path12 from "path";
3841
- import * as fs4 from "fs/promises";
4421
+ import * as path13 from "path";
4422
+ import * as fs5 from "fs/promises";
3842
4423
  async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3843
4424
  if (depth > MAX_SCAN_DEPTH)
3844
4425
  return [];
3845
4426
  const results = [];
3846
- const fullDir = currentDir ? path12.join(rootDir, currentDir) : rootDir;
4427
+ const fullDir = currentDir ? path13.join(rootDir, currentDir) : rootDir;
3847
4428
  try {
3848
- const entries = await fs4.readdir(fullDir, { withFileTypes: true });
4429
+ const entries = await fs5.readdir(fullDir, { withFileTypes: true });
3849
4430
  const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
3850
4431
  if (hasPackageJson && currentDir) {
3851
4432
  const info = await parsePackageJson(rootDir, currentDir);
@@ -3866,10 +4447,10 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3866
4447
  }
3867
4448
  async function parsePackageJson(rootDir, relativePath) {
3868
4449
  try {
3869
- const packageJsonPath = path12.join(rootDir, relativePath, "package.json");
3870
- const content = await fs4.readFile(packageJsonPath, "utf-8");
4450
+ const packageJsonPath = path13.join(rootDir, relativePath, "package.json");
4451
+ const content = await fs5.readFile(packageJsonPath, "utf-8");
3871
4452
  const pkg = JSON.parse(content);
3872
- const name = pkg.name || path12.basename(relativePath);
4453
+ const name = pkg.name || path13.basename(relativePath);
3873
4454
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3874
4455
  let type = "unknown";
3875
4456
  if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
@@ -3905,7 +4486,7 @@ async function detectProjectStructure(rootDir) {
3905
4486
  const projectMap = new Map;
3906
4487
  let isMonorepo = false;
3907
4488
  try {
3908
- const entries = await fs4.readdir(rootDir, { withFileTypes: true });
4489
+ const entries = await fs5.readdir(rootDir, { withFileTypes: true });
3909
4490
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
3910
4491
  const monorepoPatterns = ["apps", "packages", "libs", "services"];
3911
4492
  const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
@@ -3914,9 +4495,9 @@ async function detectProjectStructure(rootDir) {
3914
4495
  for (const pattern of monorepoPatterns) {
3915
4496
  if (!dirNames.includes(pattern))
3916
4497
  continue;
3917
- const patternDir = path12.join(rootDir, pattern);
4498
+ const patternDir = path13.join(rootDir, pattern);
3918
4499
  try {
3919
- const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
4500
+ const subDirs = await fs5.readdir(patternDir, { withFileTypes: true });
3920
4501
  for (const subDir of subDirs) {
3921
4502
  if (!subDir.isDirectory())
3922
4503
  continue;
@@ -3945,8 +4526,8 @@ async function detectProjectStructure(rootDir) {
3945
4526
  }
3946
4527
  let rootType = "unknown";
3947
4528
  try {
3948
- const rootPkgPath = path12.join(rootDir, "package.json");
3949
- const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
4529
+ const rootPkgPath = path13.join(rootDir, "package.json");
4530
+ const rootPkg = JSON.parse(await fs5.readFile(rootPkgPath, "utf-8"));
3950
4531
  if (rootPkg.workspaces)
3951
4532
  isMonorepo = true;
3952
4533
  const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
@@ -3985,8 +4566,8 @@ var init_projectDetector = __esm(() => {
3985
4566
  });
3986
4567
 
3987
4568
  // src/infrastructure/introspection/IntrospectionIndex.ts
3988
- import * as path13 from "path";
3989
- import * as fs5 from "fs/promises";
4569
+ import * as path14 from "path";
4570
+ import * as fs6 from "fs/promises";
3990
4571
 
3991
4572
  class IntrospectionIndex {
3992
4573
  rootDir;
@@ -3999,8 +4580,8 @@ class IntrospectionIndex {
3999
4580
  async initialize() {
4000
4581
  this.structure = await detectProjectStructure(this.rootDir);
4001
4582
  try {
4002
- const configPath = path13.join(this.rootDir, ".raggrep", "config.json");
4003
- const configContent = await fs5.readFile(configPath, "utf-8");
4583
+ const configPath = path14.join(this.rootDir, ".raggrep", "config.json");
4584
+ const configContent = await fs6.readFile(configPath, "utf-8");
4004
4585
  const config = JSON.parse(configContent);
4005
4586
  this.config = config.introspection || {};
4006
4587
  } catch {}
@@ -4039,28 +4620,28 @@ class IntrospectionIndex {
4039
4620
  }
4040
4621
  }
4041
4622
  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({
4623
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
4624
+ await fs6.mkdir(introDir, { recursive: true });
4625
+ const projectPath = path14.join(introDir, "_project.json");
4626
+ await fs6.writeFile(projectPath, JSON.stringify({
4046
4627
  version: "1.0.0",
4047
4628
  lastUpdated: new Date().toISOString(),
4048
4629
  structure: this.structure
4049
4630
  }, null, 2));
4050
4631
  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));
4632
+ const introFilePath = path14.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
4633
+ await fs6.mkdir(path14.dirname(introFilePath), { recursive: true });
4634
+ await fs6.writeFile(introFilePath, JSON.stringify(intro, null, 2));
4054
4635
  }
4055
4636
  }
4056
4637
  async load(config) {
4057
- const introDir = path13.join(getRaggrepDir(this.rootDir, config), "introspection");
4638
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
4058
4639
  try {
4059
- const projectPath = path13.join(introDir, "_project.json");
4060
- const projectContent = await fs5.readFile(projectPath, "utf-8");
4640
+ const projectPath = path14.join(introDir, "_project.json");
4641
+ const projectContent = await fs6.readFile(projectPath, "utf-8");
4061
4642
  const projectData = JSON.parse(projectContent);
4062
4643
  this.structure = projectData.structure;
4063
- await this.loadFilesRecursive(path13.join(introDir, "files"), "");
4644
+ await this.loadFilesRecursive(path14.join(introDir, "files"), "");
4064
4645
  } catch {
4065
4646
  this.structure = null;
4066
4647
  this.files.clear();
@@ -4068,14 +4649,14 @@ class IntrospectionIndex {
4068
4649
  }
4069
4650
  async loadFilesRecursive(basePath, prefix) {
4070
4651
  try {
4071
- const entries = await fs5.readdir(basePath, { withFileTypes: true });
4652
+ const entries = await fs6.readdir(basePath, { withFileTypes: true });
4072
4653
  for (const entry of entries) {
4073
- const entryPath = path13.join(basePath, entry.name);
4654
+ const entryPath = path14.join(basePath, entry.name);
4074
4655
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
4075
4656
  if (entry.isDirectory()) {
4076
4657
  await this.loadFilesRecursive(entryPath, relativePath);
4077
4658
  } else if (entry.name.endsWith(".json")) {
4078
- const content = await fs5.readFile(entryPath, "utf-8");
4659
+ const content = await fs6.readFile(entryPath, "utf-8");
4079
4660
  const intro = JSON.parse(content);
4080
4661
  this.files.set(intro.filepath, intro);
4081
4662
  }
@@ -4101,7 +4682,7 @@ var init_introspection2 = __esm(() => {
4101
4682
 
4102
4683
  // src/app/indexer/watcher.ts
4103
4684
  import { watch } from "chokidar";
4104
- import * as path14 from "path";
4685
+ import * as path15 from "path";
4105
4686
  async function watchDirectory(rootDir, options = {}) {
4106
4687
  const {
4107
4688
  debounceMs = DEFAULT_DEBOUNCE_MS,
@@ -4112,7 +4693,7 @@ async function watchDirectory(rootDir, options = {}) {
4112
4693
  onFileChange,
4113
4694
  onError
4114
4695
  } = options;
4115
- rootDir = path14.resolve(rootDir);
4696
+ rootDir = path15.resolve(rootDir);
4116
4697
  const config = await loadConfig(rootDir);
4117
4698
  const indexLocation = getIndexLocation(rootDir);
4118
4699
  const validExtensions = new Set(config.extensions);
@@ -4122,7 +4703,7 @@ async function watchDirectory(rootDir, options = {}) {
4122
4703
  "**/.git/**"
4123
4704
  ];
4124
4705
  function shouldWatchFile(filepath) {
4125
- const ext = path14.extname(filepath);
4706
+ const ext = path15.extname(filepath);
4126
4707
  return validExtensions.has(ext);
4127
4708
  }
4128
4709
  let isRunning = true;
@@ -4204,7 +4785,7 @@ async function watchDirectory(rootDir, options = {}) {
4204
4785
  function handleFileEvent(event, filepath) {
4205
4786
  if (!isRunning)
4206
4787
  return;
4207
- const relativePath = path14.relative(rootDir, filepath);
4788
+ const relativePath = path15.relative(rootDir, filepath);
4208
4789
  if (!shouldWatchFile(filepath)) {
4209
4790
  return;
4210
4791
  }
@@ -4282,8 +4863,8 @@ __export(exports_indexer, {
4282
4863
  cleanupIndex: () => cleanupIndex
4283
4864
  });
4284
4865
  import { glob } from "glob";
4285
- import * as fs6 from "fs/promises";
4286
- import * as path15 from "path";
4866
+ import * as fs7 from "fs/promises";
4867
+ import * as path16 from "path";
4287
4868
  import * as os3 from "os";
4288
4869
  async function parallelMap(items, processor, concurrency) {
4289
4870
  const results = new Array(items.length);
@@ -4326,7 +4907,7 @@ async function indexDirectory(rootDir, options = {}) {
4326
4907
  const quiet = options.quiet ?? false;
4327
4908
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
4328
4909
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4329
- rootDir = path15.resolve(rootDir);
4910
+ rootDir = path16.resolve(rootDir);
4330
4911
  const location = getIndexLocation(rootDir);
4331
4912
  logger.info(`Indexing directory: ${rootDir}`);
4332
4913
  logger.info(`Index location: ${location.indexDir}`);
@@ -4378,12 +4959,12 @@ async function indexDirectory(rootDir, options = {}) {
4378
4959
  rootDir,
4379
4960
  config,
4380
4961
  readFile: async (filepath) => {
4381
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4382
- return fs6.readFile(fullPath, "utf-8");
4962
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
4963
+ return fs7.readFile(fullPath, "utf-8");
4383
4964
  },
4384
4965
  getFileStats: async (filepath) => {
4385
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4386
- const stats = await fs6.stat(fullPath);
4966
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
4967
+ const stats = await fs7.stat(fullPath);
4387
4968
  return { lastModified: stats.mtime.toISOString() };
4388
4969
  }
4389
4970
  };
@@ -4408,7 +4989,7 @@ async function isIndexVersionCompatible(rootDir) {
4408
4989
  const config = await loadConfig(rootDir);
4409
4990
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4410
4991
  try {
4411
- const content = await fs6.readFile(globalManifestPath, "utf-8");
4992
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4412
4993
  const manifest = JSON.parse(content);
4413
4994
  return manifest.version === INDEX_SCHEMA_VERSION;
4414
4995
  } catch {
@@ -4418,11 +4999,11 @@ async function isIndexVersionCompatible(rootDir) {
4418
4999
  async function deleteIndex(rootDir) {
4419
5000
  const indexDir = getRaggrepDir(rootDir);
4420
5001
  try {
4421
- await fs6.rm(indexDir, { recursive: true, force: true });
5002
+ await fs7.rm(indexDir, { recursive: true, force: true });
4422
5003
  } catch {}
4423
5004
  }
4424
5005
  async function resetIndex(rootDir) {
4425
- rootDir = path15.resolve(rootDir);
5006
+ rootDir = path16.resolve(rootDir);
4426
5007
  const status = await getIndexStatus(rootDir);
4427
5008
  if (!status.exists) {
4428
5009
  throw new Error(`No index found for ${rootDir}`);
@@ -4437,7 +5018,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4437
5018
  const verbose = options.verbose ?? false;
4438
5019
  const quiet = options.quiet ?? false;
4439
5020
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4440
- rootDir = path15.resolve(rootDir);
5021
+ rootDir = path16.resolve(rootDir);
4441
5022
  const status = await getIndexStatus(rootDir);
4442
5023
  if (!status.exists) {
4443
5024
  logger.info(`No index found. Creating index...
@@ -4464,7 +5045,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4464
5045
  const introspection = new IntrospectionIndex(rootDir);
4465
5046
  await introspection.initialize();
4466
5047
  const currentFiles = await findFiles(rootDir, config);
4467
- const currentFileSet = new Set(currentFiles.map((f) => path15.relative(rootDir, f)));
5048
+ const currentFileSet = new Set(currentFiles.map((f) => path16.relative(rootDir, f)));
4468
5049
  let totalIndexed = 0;
4469
5050
  let totalRemoved = 0;
4470
5051
  let totalUnchanged = 0;
@@ -4492,29 +5073,43 @@ async function ensureIndexFresh(rootDir, options = {}) {
4492
5073
  filesToRemove.push(filepath);
4493
5074
  }
4494
5075
  }
5076
+ const removedFilepaths = [];
4495
5077
  for (const filepath of filesToRemove) {
4496
5078
  logger.debug(` Removing stale: ${filepath}`);
4497
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5079
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4498
5080
  try {
4499
- await fs6.unlink(indexFilePath);
5081
+ await fs7.unlink(indexFilePath);
4500
5082
  } catch {}
4501
- const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
5083
+ const symbolicFilePath = path16.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4502
5084
  try {
4503
- await fs6.unlink(symbolicFilePath);
5085
+ await fs7.unlink(symbolicFilePath);
4504
5086
  } catch {}
4505
5087
  delete manifest.files[filepath];
5088
+ removedFilepaths.push(filepath);
4506
5089
  totalRemoved++;
4507
5090
  }
5091
+ if (removedFilepaths.length > 0) {
5092
+ try {
5093
+ const { LiteralIndex: LiteralIndex2 } = await Promise.resolve().then(() => (init_literalIndex(), exports_literalIndex));
5094
+ const raggrepDir = getRaggrepDir(rootDir, config);
5095
+ const literalIndex = new LiteralIndex2(raggrepDir, module.id);
5096
+ await literalIndex.initialize();
5097
+ for (const filepath of removedFilepaths) {
5098
+ literalIndex.removeFile(filepath);
5099
+ }
5100
+ await literalIndex.save();
5101
+ } catch {}
5102
+ }
4508
5103
  const ctx = {
4509
5104
  rootDir,
4510
5105
  config,
4511
5106
  readFile: async (filepath) => {
4512
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4513
- return fs6.readFile(fullPath, "utf-8");
5107
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5108
+ return fs7.readFile(fullPath, "utf-8");
4514
5109
  },
4515
5110
  getFileStats: async (filepath) => {
4516
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4517
- const stats = await fs6.stat(fullPath);
5111
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5112
+ const stats = await fs7.stat(fullPath);
4518
5113
  return { lastModified: stats.mtime.toISOString() };
4519
5114
  },
4520
5115
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4522,10 +5117,10 @@ async function ensureIndexFresh(rootDir, options = {}) {
4522
5117
  const totalFiles = currentFiles.length;
4523
5118
  for (let i = 0;i < currentFiles.length; i++) {
4524
5119
  const filepath = currentFiles[i];
4525
- const relativePath = path15.relative(rootDir, filepath);
5120
+ const relativePath = path16.relative(rootDir, filepath);
4526
5121
  const progress = `[${i + 1}/${totalFiles}]`;
4527
5122
  try {
4528
- const stats = await fs6.stat(filepath);
5123
+ const stats = await fs7.stat(filepath);
4529
5124
  const lastModified = stats.mtime.toISOString();
4530
5125
  const existingEntry = manifest.files[relativePath];
4531
5126
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4533,7 +5128,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4533
5128
  continue;
4534
5129
  }
4535
5130
  logger.progress(` ${progress} Indexing: ${relativePath}`);
4536
- const content = await fs6.readFile(filepath, "utf-8");
5131
+ const content = await fs7.readFile(filepath, "utf-8");
4537
5132
  introspection.addFile(relativePath, content);
4538
5133
  const fileIndex = await module.indexFile(relativePath, content, ctx);
4539
5134
  if (fileIndex) {
@@ -4582,7 +5177,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4582
5177
  };
4583
5178
  const manifest = await loadModuleManifest(rootDir, module.id, config);
4584
5179
  const indexPath = getModuleIndexPath(rootDir, module.id, config);
4585
- const currentFileSet = new Set(files.map((f) => path15.relative(rootDir, f)));
5180
+ const currentFileSet = new Set(files.map((f) => path16.relative(rootDir, f)));
4586
5181
  const filesToRemove = [];
4587
5182
  for (const filepath of Object.keys(manifest.files)) {
4588
5183
  if (!currentFileSet.has(filepath)) {
@@ -4593,13 +5188,13 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4593
5188
  logger.info(` Removing ${filesToRemove.length} stale entries...`);
4594
5189
  for (const filepath of filesToRemove) {
4595
5190
  logger.debug(` Removing: ${filepath}`);
4596
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5191
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4597
5192
  try {
4598
- await fs6.unlink(indexFilePath);
5193
+ await fs7.unlink(indexFilePath);
4599
5194
  } catch {}
4600
- const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
5195
+ const symbolicFilePath = path16.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4601
5196
  try {
4602
- await fs6.unlink(symbolicFilePath);
5197
+ await fs7.unlink(symbolicFilePath);
4603
5198
  } catch {}
4604
5199
  delete manifest.files[filepath];
4605
5200
  }
@@ -4609,12 +5204,12 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4609
5204
  rootDir,
4610
5205
  config,
4611
5206
  readFile: async (filepath) => {
4612
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4613
- return fs6.readFile(fullPath, "utf-8");
5207
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5208
+ return fs7.readFile(fullPath, "utf-8");
4614
5209
  },
4615
5210
  getFileStats: async (filepath) => {
4616
- const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4617
- const stats = await fs6.stat(fullPath);
5211
+ const fullPath = path16.isAbsolute(filepath) ? filepath : path16.join(rootDir, filepath);
5212
+ const stats = await fs7.stat(fullPath);
4618
5213
  return { lastModified: stats.mtime.toISOString() };
4619
5214
  },
4620
5215
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4622,9 +5217,9 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4622
5217
  const totalFiles = files.length;
4623
5218
  let completedCount = 0;
4624
5219
  const processFile = async (filepath, _index) => {
4625
- const relativePath = path15.relative(rootDir, filepath);
5220
+ const relativePath = path16.relative(rootDir, filepath);
4626
5221
  try {
4627
- const stats = await fs6.stat(filepath);
5222
+ const stats = await fs7.stat(filepath);
4628
5223
  const lastModified = stats.mtime.toISOString();
4629
5224
  const existingEntry = manifest.files[relativePath];
4630
5225
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4632,7 +5227,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4632
5227
  logger.debug(` [${completedCount}/${totalFiles}] Skipped ${relativePath} (unchanged)`);
4633
5228
  return { relativePath, status: "skipped" };
4634
5229
  }
4635
- const content = await fs6.readFile(filepath, "utf-8");
5230
+ const content = await fs7.readFile(filepath, "utf-8");
4636
5231
  introspection.addFile(relativePath, content);
4637
5232
  completedCount++;
4638
5233
  logger.progress(` [${completedCount}/${totalFiles}] Processing: ${relativePath}`);
@@ -4700,7 +5295,7 @@ async function findFiles(rootDir, config) {
4700
5295
  async function loadModuleManifest(rootDir, moduleId, config) {
4701
5296
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
4702
5297
  try {
4703
- const content = await fs6.readFile(manifestPath, "utf-8");
5298
+ const content = await fs7.readFile(manifestPath, "utf-8");
4704
5299
  return JSON.parse(content);
4705
5300
  } catch {
4706
5301
  return {
@@ -4713,14 +5308,14 @@ async function loadModuleManifest(rootDir, moduleId, config) {
4713
5308
  }
4714
5309
  async function writeModuleManifest(rootDir, moduleId, manifest, config) {
4715
5310
  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));
5311
+ await fs7.mkdir(path16.dirname(manifestPath), { recursive: true });
5312
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4718
5313
  }
4719
5314
  async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
4720
5315
  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));
5316
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5317
+ await fs7.mkdir(path16.dirname(indexFilePath), { recursive: true });
5318
+ await fs7.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
4724
5319
  }
4725
5320
  async function updateGlobalManifest(rootDir, modules, config) {
4726
5321
  const manifestPath = getGlobalManifestPath(rootDir, config);
@@ -4729,13 +5324,13 @@ async function updateGlobalManifest(rootDir, modules, config) {
4729
5324
  lastUpdated: new Date().toISOString(),
4730
5325
  modules: modules.map((m) => m.id)
4731
5326
  };
4732
- await fs6.mkdir(path15.dirname(manifestPath), { recursive: true });
4733
- await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
5327
+ await fs7.mkdir(path16.dirname(manifestPath), { recursive: true });
5328
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4734
5329
  }
4735
5330
  async function cleanupIndex(rootDir, options = {}) {
4736
5331
  const verbose = options.verbose ?? false;
4737
5332
  const logger = options.logger ?? createLogger({ verbose });
4738
- rootDir = path15.resolve(rootDir);
5333
+ rootDir = path16.resolve(rootDir);
4739
5334
  logger.info(`Cleaning up index in: ${rootDir}`);
4740
5335
  const config = await loadConfig(rootDir);
4741
5336
  await registerBuiltInModules();
@@ -4765,9 +5360,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4765
5360
  const filesToRemove = [];
4766
5361
  const updatedFiles = {};
4767
5362
  for (const [filepath, entry] of Object.entries(manifest.files)) {
4768
- const fullPath = path15.join(rootDir, filepath);
5363
+ const fullPath = path16.join(rootDir, filepath);
4769
5364
  try {
4770
- await fs6.access(fullPath);
5365
+ await fs7.access(fullPath);
4771
5366
  updatedFiles[filepath] = entry;
4772
5367
  result.kept++;
4773
5368
  } catch {
@@ -4777,9 +5372,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4777
5372
  }
4778
5373
  }
4779
5374
  for (const filepath of filesToRemove) {
4780
- const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5375
+ const indexFilePath = path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4781
5376
  try {
4782
- await fs6.unlink(indexFilePath);
5377
+ await fs7.unlink(indexFilePath);
4783
5378
  } catch {}
4784
5379
  }
4785
5380
  manifest.files = updatedFiles;
@@ -4790,16 +5385,16 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4790
5385
  }
4791
5386
  async function cleanupEmptyDirectories(dir) {
4792
5387
  try {
4793
- const entries = await fs6.readdir(dir, { withFileTypes: true });
5388
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
4794
5389
  for (const entry of entries) {
4795
5390
  if (entry.isDirectory()) {
4796
- const subDir = path15.join(dir, entry.name);
5391
+ const subDir = path16.join(dir, entry.name);
4797
5392
  await cleanupEmptyDirectories(subDir);
4798
5393
  }
4799
5394
  }
4800
- const remainingEntries = await fs6.readdir(dir);
5395
+ const remainingEntries = await fs7.readdir(dir);
4801
5396
  if (remainingEntries.length === 0) {
4802
- await fs6.rmdir(dir);
5397
+ await fs7.rmdir(dir);
4803
5398
  return true;
4804
5399
  }
4805
5400
  return false;
@@ -4808,7 +5403,7 @@ async function cleanupEmptyDirectories(dir) {
4808
5403
  }
4809
5404
  }
4810
5405
  async function getIndexStatus(rootDir) {
4811
- rootDir = path15.resolve(rootDir);
5406
+ rootDir = path16.resolve(rootDir);
4812
5407
  const config = await loadConfig(rootDir);
4813
5408
  const location = getIndexLocation(rootDir);
4814
5409
  const indexDir = location.indexDir;
@@ -4820,13 +5415,13 @@ async function getIndexStatus(rootDir) {
4820
5415
  totalFiles: 0
4821
5416
  };
4822
5417
  try {
4823
- await fs6.access(indexDir);
5418
+ await fs7.access(indexDir);
4824
5419
  } catch {
4825
5420
  return status;
4826
5421
  }
4827
5422
  try {
4828
5423
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4829
- const content = await fs6.readFile(globalManifestPath, "utf-8");
5424
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4830
5425
  const globalManifest = JSON.parse(content);
4831
5426
  status.exists = true;
4832
5427
  status.lastUpdated = globalManifest.lastUpdated;
@@ -4844,7 +5439,7 @@ async function getIndexStatus(rootDir) {
4844
5439
  }
4845
5440
  } catch {
4846
5441
  try {
4847
- const entries = await fs6.readdir(path15.join(indexDir, "index"));
5442
+ const entries = await fs7.readdir(path16.join(indexDir, "index"));
4848
5443
  if (entries.length > 0) {
4849
5444
  status.exists = true;
4850
5445
  for (const entry of entries) {
@@ -4885,10 +5480,10 @@ __export(exports_search, {
4885
5480
  search: () => search,
4886
5481
  formatSearchResults: () => formatSearchResults
4887
5482
  });
4888
- import * as fs7 from "fs/promises";
4889
- import * as path16 from "path";
5483
+ import * as fs8 from "fs/promises";
5484
+ import * as path17 from "path";
4890
5485
  async function search(rootDir, query, options = {}) {
4891
- rootDir = path16.resolve(rootDir);
5486
+ rootDir = path17.resolve(rootDir);
4892
5487
  const ensureFresh = options.ensureFresh ?? DEFAULT_SEARCH_OPTIONS.ensureFresh;
4893
5488
  if (ensureFresh) {
4894
5489
  await ensureIndexFresh(rootDir, { quiet: true });
@@ -4941,9 +5536,9 @@ function createSearchContext(rootDir, moduleId, config) {
4941
5536
  config,
4942
5537
  loadFileIndex: async (filepath) => {
4943
5538
  const hasExtension = /\.[^./]+$/.test(filepath);
4944
- const indexFilePath = hasExtension ? path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path16.join(indexPath, filepath + ".json");
5539
+ const indexFilePath = hasExtension ? path17.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path17.join(indexPath, filepath + ".json");
4945
5540
  try {
4946
- const content = await fs7.readFile(indexFilePath, "utf-8");
5541
+ const content = await fs8.readFile(indexFilePath, "utf-8");
4947
5542
  return JSON.parse(content);
4948
5543
  } catch {
4949
5544
  return null;
@@ -4953,7 +5548,7 @@ function createSearchContext(rootDir, moduleId, config) {
4953
5548
  const files = [];
4954
5549
  await traverseDirectory(indexPath, files, indexPath);
4955
5550
  return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
4956
- const relative4 = path16.relative(indexPath, f);
5551
+ const relative4 = path17.relative(indexPath, f);
4957
5552
  return relative4.replace(/\.json$/, "");
4958
5553
  });
4959
5554
  }
@@ -4961,9 +5556,9 @@ function createSearchContext(rootDir, moduleId, config) {
4961
5556
  }
4962
5557
  async function traverseDirectory(dir, files, basePath) {
4963
5558
  try {
4964
- const entries = await fs7.readdir(dir, { withFileTypes: true });
5559
+ const entries = await fs8.readdir(dir, { withFileTypes: true });
4965
5560
  for (const entry of entries) {
4966
- const fullPath = path16.join(dir, entry.name);
5561
+ const fullPath = path17.join(dir, entry.name);
4967
5562
  if (entry.isDirectory()) {
4968
5563
  await traverseDirectory(fullPath, files, basePath);
4969
5564
  } else if (entry.isFile()) {
@@ -4975,7 +5570,7 @@ async function traverseDirectory(dir, files, basePath) {
4975
5570
  async function loadGlobalManifest(rootDir, config) {
4976
5571
  const manifestPath = getGlobalManifestPath(rootDir, config);
4977
5572
  try {
4978
- const content = await fs7.readFile(manifestPath, "utf-8");
5573
+ const content = await fs8.readFile(manifestPath, "utf-8");
4979
5574
  return JSON.parse(content);
4980
5575
  } catch {
4981
5576
  return null;
@@ -5041,7 +5636,7 @@ init_logger();
5041
5636
  // package.json
5042
5637
  var package_default = {
5043
5638
  name: "raggrep",
5044
- version: "0.6.1",
5639
+ version: "0.7.1",
5045
5640
  description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
5046
5641
  type: "module",
5047
5642
  main: "./dist/index.js",
@@ -5502,4 +6097,4 @@ Run 'raggrep <command> --help' for more information.
5502
6097
  }
5503
6098
  main();
5504
6099
 
5505
- //# debugId=8FCF189E9FBCBFE264756E2164756E21
6100
+ //# debugId=D5C2A5C0F122D20164756E2164756E21