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/index.js CHANGED
@@ -130,10 +130,14 @@ var init_config = __esm(() => {
130
130
  ];
131
131
  });
132
132
 
133
+ // src/domain/entities/literal.ts
134
+ var init_literal = () => {};
135
+
133
136
  // src/domain/entities/index.ts
134
137
  var init_entities = __esm(() => {
135
138
  init_searchResult();
136
139
  init_config();
140
+ init_literal();
137
141
  });
138
142
 
139
143
  // src/infrastructure/config/configLoader.ts
@@ -2566,10 +2570,299 @@ function generateChunkId(filepath, startLine, endLine) {
2566
2570
  }
2567
2571
  var DEFAULT_CHUNK_SIZE = 30, DEFAULT_OVERLAP = 5;
2568
2572
 
2573
+ // src/domain/services/queryLiteralParser.ts
2574
+ function parseQueryLiterals(query) {
2575
+ if (!query || query.trim() === "") {
2576
+ return { literals: [], remainingQuery: "" };
2577
+ }
2578
+ const literals = [];
2579
+ let remainingQuery = query;
2580
+ const matchedPositions = new Set;
2581
+ const backtickResult = extractExplicitLiterals(remainingQuery, /`([^`]+)`/g, "explicit-backtick", matchedPositions);
2582
+ literals.push(...backtickResult.literals);
2583
+ remainingQuery = backtickResult.remainingQuery;
2584
+ const quoteResult = extractExplicitLiterals(remainingQuery, /"([^"]+)"/g, "explicit-quote", matchedPositions);
2585
+ literals.push(...quoteResult.literals);
2586
+ remainingQuery = quoteResult.remainingQuery;
2587
+ const implicitLiterals = extractImplicitLiterals(query, matchedPositions);
2588
+ literals.push(...implicitLiterals);
2589
+ return {
2590
+ literals,
2591
+ remainingQuery: remainingQuery.trim()
2592
+ };
2593
+ }
2594
+ function extractExplicitLiterals(query, pattern, method, matchedPositions) {
2595
+ const literals = [];
2596
+ let remainingQuery = query;
2597
+ pattern.lastIndex = 0;
2598
+ let match;
2599
+ const replacements = [];
2600
+ while ((match = pattern.exec(query)) !== null) {
2601
+ const value = match[1];
2602
+ const rawValue = match[0];
2603
+ if (!value || value.trim() === "") {
2604
+ continue;
2605
+ }
2606
+ const posKey = `${match.index}:${match.index + rawValue.length}`;
2607
+ matchedPositions.add(posKey);
2608
+ matchedPositions.add(`value:${value.toLowerCase()}`);
2609
+ literals.push({
2610
+ value,
2611
+ rawValue,
2612
+ confidence: "high",
2613
+ detectionMethod: method,
2614
+ inferredType: inferTypeFromValue(value)
2615
+ });
2616
+ replacements.push({
2617
+ start: match.index,
2618
+ end: match.index + rawValue.length,
2619
+ text: ""
2620
+ });
2621
+ }
2622
+ replacements.sort((a, b) => b.start - a.start).forEach((r) => {
2623
+ remainingQuery = remainingQuery.slice(0, r.start) + r.text + remainingQuery.slice(r.end);
2624
+ });
2625
+ return { literals, remainingQuery };
2626
+ }
2627
+ function extractImplicitLiterals(query, matchedPositions) {
2628
+ const literals = [];
2629
+ const seenValues = new Set;
2630
+ for (const patternDef of IMPLICIT_PATTERNS) {
2631
+ patternDef.pattern.lastIndex = 0;
2632
+ let match;
2633
+ while ((match = patternDef.pattern.exec(query)) !== null) {
2634
+ const value = match[1];
2635
+ if (patternDef.minLength && value.length < patternDef.minLength) {
2636
+ continue;
2637
+ }
2638
+ const posKey = `${match.index}:${match.index + value.length}`;
2639
+ if (matchedPositions.has(posKey)) {
2640
+ continue;
2641
+ }
2642
+ if (matchedPositions.has(`value:${value.toLowerCase()}`)) {
2643
+ continue;
2644
+ }
2645
+ const lowerValue = value.toLowerCase();
2646
+ if (seenValues.has(lowerValue)) {
2647
+ continue;
2648
+ }
2649
+ seenValues.add(lowerValue);
2650
+ if (isCommonWord(value)) {
2651
+ continue;
2652
+ }
2653
+ literals.push({
2654
+ value,
2655
+ rawValue: value,
2656
+ confidence: patternDef.confidence,
2657
+ detectionMethod: "implicit-casing",
2658
+ inferredType: patternDef.inferredType
2659
+ });
2660
+ }
2661
+ }
2662
+ return literals;
2663
+ }
2664
+ function inferTypeFromValue(value) {
2665
+ if (/^[A-Z][a-z]+(?:[A-Z][a-z0-9]*)+$/.test(value)) {
2666
+ return "className";
2667
+ }
2668
+ if (/^[a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]*)+$/.test(value)) {
2669
+ return "functionName";
2670
+ }
2671
+ if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(value)) {
2672
+ return "variableName";
2673
+ }
2674
+ if (/^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/.test(value)) {
2675
+ return "identifier";
2676
+ }
2677
+ if (/^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$/.test(value)) {
2678
+ return "packageName";
2679
+ }
2680
+ return;
2681
+ }
2682
+ function isCommonWord(word) {
2683
+ const commonWords = new Set([
2684
+ "find",
2685
+ "the",
2686
+ "a",
2687
+ "an",
2688
+ "is",
2689
+ "are",
2690
+ "was",
2691
+ "were",
2692
+ "what",
2693
+ "where",
2694
+ "when",
2695
+ "how",
2696
+ "why",
2697
+ "which",
2698
+ "who",
2699
+ "this",
2700
+ "that",
2701
+ "these",
2702
+ "those",
2703
+ "and",
2704
+ "or",
2705
+ "but",
2706
+ "for",
2707
+ "with",
2708
+ "from",
2709
+ "to",
2710
+ "in",
2711
+ "on",
2712
+ "at",
2713
+ "by",
2714
+ "of",
2715
+ "all",
2716
+ "any",
2717
+ "some",
2718
+ "get",
2719
+ "set",
2720
+ "new",
2721
+ "class",
2722
+ "function",
2723
+ "const",
2724
+ "let",
2725
+ "var",
2726
+ "type",
2727
+ "interface",
2728
+ "import",
2729
+ "export",
2730
+ "default",
2731
+ "return",
2732
+ "async",
2733
+ "await",
2734
+ "null",
2735
+ "undefined",
2736
+ "true",
2737
+ "false"
2738
+ ]);
2739
+ return commonWords.has(word.toLowerCase());
2740
+ }
2741
+ var IMPLICIT_PATTERNS;
2742
+ var init_queryLiteralParser = __esm(() => {
2743
+ IMPLICIT_PATTERNS = [
2744
+ {
2745
+ pattern: /\b([A-Z][a-z]+(?:[A-Z][a-z0-9]*)+)\b/g,
2746
+ confidence: "medium",
2747
+ inferredType: "className",
2748
+ minLength: 3
2749
+ },
2750
+ {
2751
+ pattern: /\b([a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]*)+)\b/g,
2752
+ confidence: "medium",
2753
+ inferredType: "functionName",
2754
+ minLength: 3
2755
+ },
2756
+ {
2757
+ pattern: /\b([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+)\b/g,
2758
+ confidence: "medium",
2759
+ inferredType: "variableName",
2760
+ minLength: 3
2761
+ },
2762
+ {
2763
+ pattern: /\b([a-z][a-z0-9]*(?:_[a-z0-9]+)+)\b/g,
2764
+ confidence: "low",
2765
+ inferredType: "identifier",
2766
+ minLength: 3
2767
+ },
2768
+ {
2769
+ pattern: /(?<![/:.])\b([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\b(?![/:])/g,
2770
+ confidence: "low",
2771
+ inferredType: "packageName",
2772
+ minLength: 3
2773
+ }
2774
+ ];
2775
+ });
2776
+
2777
+ // src/domain/services/literalExtractor.ts
2778
+ function extractLiterals(chunk) {
2779
+ const literals = [];
2780
+ if (chunk.name) {
2781
+ const literalType = CHUNK_TYPE_TO_LITERAL_TYPE[chunk.type] || "identifier";
2782
+ literals.push({
2783
+ value: chunk.name,
2784
+ type: literalType,
2785
+ matchType: "definition"
2786
+ });
2787
+ }
2788
+ return literals;
2789
+ }
2790
+ var CHUNK_TYPE_TO_LITERAL_TYPE;
2791
+ var init_literalExtractor = __esm(() => {
2792
+ CHUNK_TYPE_TO_LITERAL_TYPE = {
2793
+ class: "className",
2794
+ function: "functionName",
2795
+ interface: "interfaceName",
2796
+ type: "typeName",
2797
+ enum: "enumName",
2798
+ variable: "variableName"
2799
+ };
2800
+ });
2801
+
2802
+ // src/domain/services/literalScorer.ts
2803
+ function calculateLiteralMultiplier(matchType, confidence) {
2804
+ return LITERAL_SCORING_CONSTANTS.MULTIPLIERS[matchType][confidence];
2805
+ }
2806
+ function calculateMaxMultiplier(matches) {
2807
+ if (!matches || matches.length === 0) {
2808
+ return 1;
2809
+ }
2810
+ return Math.max(...matches.map((m) => calculateLiteralMultiplier(m.indexedLiteral.matchType, m.queryLiteral.confidence)));
2811
+ }
2812
+ function calculateLiteralContribution(matches, hasSemanticOrBm25) {
2813
+ if (!matches || matches.length === 0) {
2814
+ return {
2815
+ multiplier: 1,
2816
+ literalOnly: false,
2817
+ matchCount: 0
2818
+ };
2819
+ }
2820
+ let bestMatch = null;
2821
+ let bestMultiplier = 0;
2822
+ for (const match of matches) {
2823
+ const mult = calculateLiteralMultiplier(match.indexedLiteral.matchType, match.queryLiteral.confidence);
2824
+ if (mult > bestMultiplier) {
2825
+ bestMultiplier = mult;
2826
+ bestMatch = match;
2827
+ }
2828
+ }
2829
+ return {
2830
+ multiplier: bestMultiplier,
2831
+ literalOnly: !hasSemanticOrBm25,
2832
+ bestMatchType: bestMatch?.indexedLiteral.matchType,
2833
+ bestConfidence: bestMatch?.queryLiteral.confidence,
2834
+ matchCount: matches.length
2835
+ };
2836
+ }
2837
+ function applyLiteralBoost(baseScore, matches, hasSemanticOrBm25) {
2838
+ if (!matches || matches.length === 0) {
2839
+ return baseScore;
2840
+ }
2841
+ const multiplier = calculateMaxMultiplier(matches);
2842
+ if (!hasSemanticOrBm25) {
2843
+ return LITERAL_SCORING_CONSTANTS.BASE_SCORE * multiplier;
2844
+ }
2845
+ return baseScore * multiplier;
2846
+ }
2847
+ var LITERAL_SCORING_CONSTANTS;
2848
+ var init_literalScorer = __esm(() => {
2849
+ LITERAL_SCORING_CONSTANTS = {
2850
+ BASE_SCORE: 0.5,
2851
+ MULTIPLIERS: {
2852
+ definition: { high: 2.5, medium: 2, low: 1.5 },
2853
+ reference: { high: 2, medium: 1.5, low: 1.3 },
2854
+ import: { high: 1.5, medium: 1.3, low: 1.1 }
2855
+ }
2856
+ };
2857
+ });
2858
+
2569
2859
  // src/domain/services/index.ts
2570
2860
  var init_services = __esm(() => {
2571
2861
  init_keywords();
2572
2862
  init_queryIntent();
2863
+ init_queryLiteralParser();
2864
+ init_literalExtractor();
2865
+ init_literalScorer();
2573
2866
  });
2574
2867
 
2575
2868
  // src/modules/language/typescript/parseCode.ts
@@ -2889,10 +3182,182 @@ var init_symbolicIndex = __esm(() => {
2889
3182
  init_keywords();
2890
3183
  });
2891
3184
 
3185
+ // src/infrastructure/storage/literalIndex.ts
3186
+ var exports_literalIndex = {};
3187
+ __export(exports_literalIndex, {
3188
+ getLiteralIndexPath: () => getLiteralIndexPath,
3189
+ LiteralIndex: () => LiteralIndex
3190
+ });
3191
+ import * as fs4 from "fs/promises";
3192
+ import * as path9 from "path";
3193
+
3194
+ class LiteralIndex {
3195
+ indexPath;
3196
+ moduleId;
3197
+ entries = new Map;
3198
+ static VERSION = "1.0.0";
3199
+ constructor(indexDir, moduleId) {
3200
+ this.indexPath = path9.join(indexDir, "index", moduleId, "literals");
3201
+ this.moduleId = moduleId;
3202
+ }
3203
+ async initialize() {
3204
+ try {
3205
+ await this.load();
3206
+ } catch {
3207
+ this.entries = new Map;
3208
+ }
3209
+ }
3210
+ addLiterals(chunkId, filepath, literals) {
3211
+ for (const literal of literals) {
3212
+ const key = literal.value.toLowerCase();
3213
+ const existingEntries = this.entries.get(key) || [];
3214
+ const existingIndex = existingEntries.findIndex((e) => e.chunkId === chunkId);
3215
+ const newEntry = {
3216
+ chunkId,
3217
+ filepath,
3218
+ originalCasing: literal.value,
3219
+ type: literal.type,
3220
+ matchType: literal.matchType
3221
+ };
3222
+ if (existingIndex >= 0) {
3223
+ const existing = existingEntries[existingIndex];
3224
+ if (shouldReplaceMatchType(existing.matchType, literal.matchType)) {
3225
+ existingEntries[existingIndex] = newEntry;
3226
+ }
3227
+ } else {
3228
+ existingEntries.push(newEntry);
3229
+ }
3230
+ this.entries.set(key, existingEntries);
3231
+ }
3232
+ }
3233
+ removeChunk(chunkId) {
3234
+ for (const [key, entries] of this.entries) {
3235
+ const filtered = entries.filter((e) => e.chunkId !== chunkId);
3236
+ if (filtered.length === 0) {
3237
+ this.entries.delete(key);
3238
+ } else if (filtered.length !== entries.length) {
3239
+ this.entries.set(key, filtered);
3240
+ }
3241
+ }
3242
+ }
3243
+ removeFile(filepath) {
3244
+ let removed = 0;
3245
+ for (const [key, entries] of this.entries) {
3246
+ const filtered = entries.filter((e) => e.filepath !== filepath);
3247
+ const removedCount = entries.length - filtered.length;
3248
+ if (removedCount > 0) {
3249
+ removed += removedCount;
3250
+ if (filtered.length === 0) {
3251
+ this.entries.delete(key);
3252
+ } else {
3253
+ this.entries.set(key, filtered);
3254
+ }
3255
+ }
3256
+ }
3257
+ return removed;
3258
+ }
3259
+ findMatches(queryLiterals) {
3260
+ const matches = [];
3261
+ for (const queryLiteral of queryLiterals) {
3262
+ const key = queryLiteral.value.toLowerCase();
3263
+ const entries = this.entries.get(key);
3264
+ if (!entries) {
3265
+ continue;
3266
+ }
3267
+ for (const entry of entries) {
3268
+ const exactMatch = entry.originalCasing === queryLiteral.value;
3269
+ matches.push({
3270
+ queryLiteral,
3271
+ indexedLiteral: {
3272
+ value: entry.originalCasing,
3273
+ type: entry.type,
3274
+ matchType: entry.matchType
3275
+ },
3276
+ chunkId: entry.chunkId,
3277
+ filepath: entry.filepath,
3278
+ exactMatch
3279
+ });
3280
+ }
3281
+ }
3282
+ return matches;
3283
+ }
3284
+ getChunksForLiteral(literal) {
3285
+ const key = literal.toLowerCase();
3286
+ const entries = this.entries.get(key);
3287
+ return entries ? entries.map((e) => e.chunkId) : [];
3288
+ }
3289
+ async save() {
3290
+ await fs4.mkdir(this.indexPath, { recursive: true });
3291
+ const data = {
3292
+ version: LiteralIndex.VERSION,
3293
+ entries: Object.fromEntries(this.entries)
3294
+ };
3295
+ const indexFile = path9.join(this.indexPath, "_index.json");
3296
+ await fs4.writeFile(indexFile, JSON.stringify(data, null, 2));
3297
+ }
3298
+ async load() {
3299
+ const indexFile = path9.join(this.indexPath, "_index.json");
3300
+ const content = await fs4.readFile(indexFile, "utf-8");
3301
+ const data = JSON.parse(content);
3302
+ if (data.version !== LiteralIndex.VERSION) {
3303
+ console.warn(`Literal index version mismatch: expected ${LiteralIndex.VERSION}, got ${data.version}`);
3304
+ }
3305
+ this.entries = new Map(Object.entries(data.entries));
3306
+ }
3307
+ async exists() {
3308
+ try {
3309
+ const indexFile = path9.join(this.indexPath, "_index.json");
3310
+ await fs4.access(indexFile);
3311
+ return true;
3312
+ } catch {
3313
+ return false;
3314
+ }
3315
+ }
3316
+ clear() {
3317
+ this.entries.clear();
3318
+ }
3319
+ get size() {
3320
+ return this.entries.size;
3321
+ }
3322
+ get totalMappings() {
3323
+ let count = 0;
3324
+ for (const entries of this.entries.values()) {
3325
+ count += entries.length;
3326
+ }
3327
+ return count;
3328
+ }
3329
+ getAllLiterals() {
3330
+ return Array.from(this.entries.keys());
3331
+ }
3332
+ buildMatchMap(queryLiterals) {
3333
+ const matches = this.findMatches(queryLiterals);
3334
+ const matchMap = new Map;
3335
+ for (const match of matches) {
3336
+ const existing = matchMap.get(match.chunkId) || [];
3337
+ existing.push(match);
3338
+ matchMap.set(match.chunkId, existing);
3339
+ }
3340
+ return matchMap;
3341
+ }
3342
+ }
3343
+ function shouldReplaceMatchType(existing, incoming) {
3344
+ const priority = {
3345
+ definition: 3,
3346
+ reference: 2,
3347
+ import: 1
3348
+ };
3349
+ return priority[incoming] > priority[existing];
3350
+ }
3351
+ function getLiteralIndexPath(rootDir, moduleId, indexDir = ".raggrep") {
3352
+ return path9.join(rootDir, indexDir, "index", moduleId, "literals");
3353
+ }
3354
+ var init_literalIndex = () => {};
3355
+
2892
3356
  // src/infrastructure/storage/index.ts
2893
3357
  var init_storage = __esm(() => {
2894
3358
  init_fileIndexStorage();
2895
3359
  init_symbolicIndex();
3360
+ init_literalIndex();
2896
3361
  });
2897
3362
 
2898
3363
  // src/modules/language/typescript/index.ts
@@ -2905,9 +3370,9 @@ __export(exports_typescript, {
2905
3370
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
2906
3371
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
2907
3372
  });
2908
- import * as path9 from "path";
3373
+ import * as path10 from "path";
2909
3374
  function isTypeScriptFile(filepath) {
2910
- const ext = path9.extname(filepath).toLowerCase();
3375
+ const ext = path10.extname(filepath).toLowerCase();
2911
3376
  return TYPESCRIPT_EXTENSIONS.includes(ext);
2912
3377
  }
2913
3378
  function calculateChunkTypeBoost(chunk) {
@@ -2942,7 +3407,9 @@ class TypeScriptModule {
2942
3407
  }
2943
3408
  embeddingConfig = null;
2944
3409
  symbolicIndex = null;
3410
+ literalIndex = null;
2945
3411
  pendingSummaries = new Map;
3412
+ pendingLiterals = new Map;
2946
3413
  rootDir = "";
2947
3414
  logger = undefined;
2948
3415
  async initialize(config) {
@@ -2956,6 +3423,7 @@ class TypeScriptModule {
2956
3423
  }
2957
3424
  configureEmbeddings(this.embeddingConfig);
2958
3425
  this.pendingSummaries.clear();
3426
+ this.pendingLiterals.clear();
2959
3427
  }
2960
3428
  async indexFile(filepath, content, ctx) {
2961
3429
  if (!isTypeScriptFile(filepath)) {
@@ -3015,6 +3483,17 @@ class TypeScriptModule {
3015
3483
  }
3016
3484
  };
3017
3485
  this.pendingSummaries.set(filepath, fileSummary);
3486
+ for (const chunk of chunks) {
3487
+ const literals = extractLiterals(chunk);
3488
+ if (literals.length > 0) {
3489
+ const existing = this.pendingLiterals.get(chunk.id);
3490
+ if (existing) {
3491
+ existing.literals.push(...literals);
3492
+ } else {
3493
+ this.pendingLiterals.set(chunk.id, { filepath, literals });
3494
+ }
3495
+ }
3496
+ }
3018
3497
  return {
3019
3498
  filepath,
3020
3499
  lastModified: stats.lastModified,
@@ -3032,7 +3511,24 @@ class TypeScriptModule {
3032
3511
  }
3033
3512
  this.symbolicIndex.buildBM25Index();
3034
3513
  await this.symbolicIndex.save();
3514
+ this.literalIndex = new LiteralIndex(indexDir, this.id);
3515
+ await this.literalIndex.initialize();
3516
+ const indexedFilepaths = new Set;
3517
+ for (const filepath of this.pendingSummaries.keys()) {
3518
+ indexedFilepaths.add(filepath);
3519
+ }
3520
+ for (const { filepath } of this.pendingLiterals.values()) {
3521
+ indexedFilepaths.add(filepath);
3522
+ }
3523
+ for (const filepath of indexedFilepaths) {
3524
+ this.literalIndex.removeFile(filepath);
3525
+ }
3526
+ for (const [chunkId, { filepath, literals }] of this.pendingLiterals) {
3527
+ this.literalIndex.addLiterals(chunkId, filepath, literals);
3528
+ }
3529
+ await this.literalIndex.save();
3035
3530
  this.pendingSummaries.clear();
3531
+ this.pendingLiterals.clear();
3036
3532
  }
3037
3533
  async search(query, ctx, options = {}) {
3038
3534
  const {
@@ -3040,8 +3536,15 @@ class TypeScriptModule {
3040
3536
  minScore = DEFAULT_MIN_SCORE2,
3041
3537
  filePatterns
3042
3538
  } = options;
3539
+ const { literals: queryLiterals, remainingQuery } = parseQueryLiterals(query);
3043
3540
  const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
3044
3541
  const symbolicIndex = new SymbolicIndex(indexDir, this.id);
3542
+ const literalIndex = new LiteralIndex(indexDir, this.id);
3543
+ let literalMatchMap = new Map;
3544
+ try {
3545
+ await literalIndex.initialize();
3546
+ literalMatchMap = literalIndex.buildMatchMap(queryLiterals);
3547
+ } catch {}
3045
3548
  let allFiles;
3046
3549
  try {
3047
3550
  await symbolicIndex.initialize();
@@ -3061,7 +3564,8 @@ class TypeScriptModule {
3061
3564
  });
3062
3565
  });
3063
3566
  }
3064
- const queryEmbedding = await getEmbedding(query);
3567
+ const semanticQuery = remainingQuery.trim() || query;
3568
+ const queryEmbedding = await getEmbedding(semanticQuery);
3065
3569
  const bm25Index = new BM25Index;
3066
3570
  const allChunksData = [];
3067
3571
  for (const filepath of filesToSearch) {
@@ -3095,14 +3599,14 @@ class TypeScriptModule {
3095
3599
  const summary = symbolicIndex.getFileSummary(filepath);
3096
3600
  if (summary?.pathContext) {
3097
3601
  let boost = 0;
3098
- const ctx2 = summary.pathContext;
3099
- if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
3602
+ const pathCtx = summary.pathContext;
3603
+ if (pathCtx.domain && queryTerms.some((t) => pathCtx.domain.includes(t) || t.includes(pathCtx.domain))) {
3100
3604
  boost += 0.1;
3101
3605
  }
3102
- if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
3606
+ if (pathCtx.layer && queryTerms.some((t) => pathCtx.layer.includes(t) || t.includes(pathCtx.layer))) {
3103
3607
  boost += 0.05;
3104
3608
  }
3105
- const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3609
+ const segmentMatch = pathCtx.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3106
3610
  if (segmentMatch) {
3107
3611
  boost += 0.05;
3108
3612
  }
@@ -3110,6 +3614,7 @@ class TypeScriptModule {
3110
3614
  }
3111
3615
  }
3112
3616
  const results = [];
3617
+ const processedChunkIds = new Set;
3113
3618
  for (const { filepath, chunk, embedding } of allChunksData) {
3114
3619
  const semanticScore = cosineSimilarity(queryEmbedding, embedding);
3115
3620
  const bm25Score = bm25Scores.get(chunk.id) || 0;
@@ -3117,13 +3622,84 @@ class TypeScriptModule {
3117
3622
  const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3118
3623
  const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3119
3624
  const exportBoost = calculateExportBoost(chunk);
3120
- const totalBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3121
- const hybridScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score + totalBoost;
3122
- if (hybridScore >= minScore || bm25Score > 0.3) {
3625
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3626
+ const baseScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score;
3627
+ const literalMatches = literalMatchMap.get(chunk.id) || [];
3628
+ const literalContribution = calculateLiteralContribution(literalMatches, true);
3629
+ const boostedScore = applyLiteralBoost(baseScore, literalMatches, true);
3630
+ const finalScore = boostedScore + additiveBoost;
3631
+ processedChunkIds.add(chunk.id);
3632
+ if (finalScore >= minScore || bm25Score > 0.3 || literalMatches.length > 0) {
3123
3633
  results.push({
3124
3634
  filepath,
3125
3635
  chunk,
3126
- score: hybridScore,
3636
+ score: finalScore,
3637
+ moduleId: this.id,
3638
+ context: {
3639
+ semanticScore,
3640
+ bm25Score,
3641
+ pathBoost,
3642
+ fileTypeBoost,
3643
+ chunkTypeBoost,
3644
+ exportBoost,
3645
+ literalMultiplier: literalContribution.multiplier,
3646
+ literalMatchType: literalContribution.bestMatchType,
3647
+ literalConfidence: literalContribution.bestConfidence,
3648
+ literalMatchCount: literalContribution.matchCount
3649
+ }
3650
+ });
3651
+ }
3652
+ }
3653
+ const literalOnlyFiles = new Map;
3654
+ for (const [chunkId, matches] of literalMatchMap) {
3655
+ if (processedChunkIds.has(chunkId)) {
3656
+ continue;
3657
+ }
3658
+ const filepath = matches[0]?.filepath;
3659
+ if (!filepath)
3660
+ continue;
3661
+ const existing = literalOnlyFiles.get(filepath) || [];
3662
+ existing.push(...matches);
3663
+ literalOnlyFiles.set(filepath, existing);
3664
+ }
3665
+ for (const [filepath, matches] of literalOnlyFiles) {
3666
+ const fileIndex = await ctx.loadFileIndex(filepath);
3667
+ if (!fileIndex)
3668
+ continue;
3669
+ const moduleData = fileIndex.moduleData;
3670
+ const chunkMatches = new Map;
3671
+ for (const match of matches) {
3672
+ const existing = chunkMatches.get(match.chunkId) || [];
3673
+ existing.push(match);
3674
+ chunkMatches.set(match.chunkId, existing);
3675
+ }
3676
+ for (const [chunkId, chunkLiteralMatches] of chunkMatches) {
3677
+ if (processedChunkIds.has(chunkId))
3678
+ continue;
3679
+ const chunkIndex = fileIndex.chunks.findIndex((c) => c.id === chunkId);
3680
+ if (chunkIndex === -1)
3681
+ continue;
3682
+ const chunk = fileIndex.chunks[chunkIndex];
3683
+ const embedding = moduleData?.embeddings?.[chunkIndex];
3684
+ let semanticScore = 0;
3685
+ if (embedding) {
3686
+ semanticScore = cosineSimilarity(queryEmbedding, embedding);
3687
+ }
3688
+ const bm25Score = bm25Scores.get(chunkId) || 0;
3689
+ const pathBoost = pathBoosts.get(filepath) || 0;
3690
+ const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3691
+ const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3692
+ const exportBoost = calculateExportBoost(chunk);
3693
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3694
+ const literalContribution = calculateLiteralContribution(chunkLiteralMatches, false);
3695
+ const baseScore = semanticScore > 0 ? SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score : LITERAL_SCORING_CONSTANTS.BASE_SCORE;
3696
+ const boostedScore = applyLiteralBoost(baseScore, chunkLiteralMatches, semanticScore > 0);
3697
+ const finalScore = boostedScore + additiveBoost;
3698
+ processedChunkIds.add(chunkId);
3699
+ results.push({
3700
+ filepath,
3701
+ chunk,
3702
+ score: finalScore,
3127
3703
  moduleId: this.id,
3128
3704
  context: {
3129
3705
  semanticScore,
@@ -3131,7 +3707,12 @@ class TypeScriptModule {
3131
3707
  pathBoost,
3132
3708
  fileTypeBoost,
3133
3709
  chunkTypeBoost,
3134
- exportBoost
3710
+ exportBoost,
3711
+ literalMultiplier: literalContribution.multiplier,
3712
+ literalMatchType: literalContribution.bestMatchType,
3713
+ literalConfidence: literalContribution.bestConfidence,
3714
+ literalMatchCount: literalContribution.matchCount,
3715
+ literalOnly: true
3135
3716
  }
3136
3717
  });
3137
3718
  }
@@ -3147,16 +3728,16 @@ class TypeScriptModule {
3147
3728
  while ((match = importRegex.exec(content)) !== null) {
3148
3729
  const importPath = match[1];
3149
3730
  if (importPath.startsWith(".")) {
3150
- const dir = path9.dirname(filepath);
3151
- const resolved = path9.normalize(path9.join(dir, importPath));
3731
+ const dir = path10.dirname(filepath);
3732
+ const resolved = path10.normalize(path10.join(dir, importPath));
3152
3733
  references.push(resolved);
3153
3734
  }
3154
3735
  }
3155
3736
  while ((match = requireRegex.exec(content)) !== null) {
3156
3737
  const importPath = match[1];
3157
3738
  if (importPath.startsWith(".")) {
3158
- const dir = path9.dirname(filepath);
3159
- const resolved = path9.normalize(path9.join(dir, importPath));
3739
+ const dir = path10.dirname(filepath);
3740
+ const resolved = path10.normalize(path10.join(dir, importPath));
3160
3741
  references.push(resolved);
3161
3742
  }
3162
3743
  }
@@ -3193,9 +3774,9 @@ __export(exports_json, {
3193
3774
  DEFAULT_TOP_K: () => DEFAULT_TOP_K3,
3194
3775
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE3
3195
3776
  });
3196
- import * as path10 from "path";
3777
+ import * as path11 from "path";
3197
3778
  function isJsonFile(filepath) {
3198
- const ext = path10.extname(filepath).toLowerCase();
3779
+ const ext = path11.extname(filepath).toLowerCase();
3199
3780
  return JSON_EXTENSIONS.includes(ext);
3200
3781
  }
3201
3782
  function extractJsonKeys(obj, prefix = "") {
@@ -3276,7 +3857,7 @@ class JsonModule {
3276
3857
  return null;
3277
3858
  }
3278
3859
  const chunkContents = textChunks.map((c) => {
3279
- const filename = path10.basename(filepath);
3860
+ const filename = path11.basename(filepath);
3280
3861
  return `${filename}: ${c.content}`;
3281
3862
  });
3282
3863
  const embeddings = await getEmbeddings(chunkContents);
@@ -3427,9 +4008,9 @@ __export(exports_markdown, {
3427
4008
  DEFAULT_TOP_K: () => DEFAULT_TOP_K4,
3428
4009
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE4
3429
4010
  });
3430
- import * as path11 from "path";
4011
+ import * as path12 from "path";
3431
4012
  function isMarkdownFile(filepath) {
3432
- const ext = path11.extname(filepath).toLowerCase();
4013
+ const ext = path12.extname(filepath).toLowerCase();
3433
4014
  return MARKDOWN_EXTENSIONS.includes(ext);
3434
4015
  }
3435
4016
  function parseMarkdownSections(content) {
@@ -3556,7 +4137,7 @@ class MarkdownModule {
3556
4137
  return null;
3557
4138
  }
3558
4139
  const chunkContents = sections.map((s) => {
3559
- const filename = path11.basename(filepath);
4140
+ const filename = path12.basename(filepath);
3560
4141
  const headingContext = s.heading ? `${s.heading}: ` : "";
3561
4142
  return `${filename} ${headingContext}${s.content}`;
3562
4143
  });
@@ -3711,8 +4292,8 @@ var init_markdown = __esm(() => {
3711
4292
  // src/app/indexer/index.ts
3712
4293
  init_config2();
3713
4294
  import { glob } from "glob";
3714
- import * as fs6 from "fs/promises";
3715
- import * as path14 from "path";
4295
+ import * as fs7 from "fs/promises";
4296
+ import * as path15 from "path";
3716
4297
  import * as os3 from "os";
3717
4298
 
3718
4299
  // src/modules/registry.ts
@@ -3747,12 +4328,12 @@ async function registerBuiltInModules() {
3747
4328
  }
3748
4329
 
3749
4330
  // src/infrastructure/introspection/IntrospectionIndex.ts
3750
- import * as path13 from "path";
3751
- import * as fs5 from "fs/promises";
4331
+ import * as path14 from "path";
4332
+ import * as fs6 from "fs/promises";
3752
4333
 
3753
4334
  // src/infrastructure/introspection/projectDetector.ts
3754
- import * as path12 from "path";
3755
- import * as fs4 from "fs/promises";
4335
+ import * as path13 from "path";
4336
+ import * as fs5 from "fs/promises";
3756
4337
  var MAX_SCAN_DEPTH = 4;
3757
4338
  var SKIP_DIRS = new Set([
3758
4339
  "node_modules",
@@ -3768,9 +4349,9 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3768
4349
  if (depth > MAX_SCAN_DEPTH)
3769
4350
  return [];
3770
4351
  const results = [];
3771
- const fullDir = currentDir ? path12.join(rootDir, currentDir) : rootDir;
4352
+ const fullDir = currentDir ? path13.join(rootDir, currentDir) : rootDir;
3772
4353
  try {
3773
- const entries = await fs4.readdir(fullDir, { withFileTypes: true });
4354
+ const entries = await fs5.readdir(fullDir, { withFileTypes: true });
3774
4355
  const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
3775
4356
  if (hasPackageJson && currentDir) {
3776
4357
  const info = await parsePackageJson(rootDir, currentDir);
@@ -3791,10 +4372,10 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3791
4372
  }
3792
4373
  async function parsePackageJson(rootDir, relativePath) {
3793
4374
  try {
3794
- const packageJsonPath = path12.join(rootDir, relativePath, "package.json");
3795
- const content = await fs4.readFile(packageJsonPath, "utf-8");
4375
+ const packageJsonPath = path13.join(rootDir, relativePath, "package.json");
4376
+ const content = await fs5.readFile(packageJsonPath, "utf-8");
3796
4377
  const pkg = JSON.parse(content);
3797
- const name = pkg.name || path12.basename(relativePath);
4378
+ const name = pkg.name || path13.basename(relativePath);
3798
4379
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3799
4380
  let type = "unknown";
3800
4381
  if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
@@ -3830,7 +4411,7 @@ async function detectProjectStructure(rootDir) {
3830
4411
  const projectMap = new Map;
3831
4412
  let isMonorepo = false;
3832
4413
  try {
3833
- const entries = await fs4.readdir(rootDir, { withFileTypes: true });
4414
+ const entries = await fs5.readdir(rootDir, { withFileTypes: true });
3834
4415
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
3835
4416
  const monorepoPatterns = ["apps", "packages", "libs", "services"];
3836
4417
  const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
@@ -3839,9 +4420,9 @@ async function detectProjectStructure(rootDir) {
3839
4420
  for (const pattern of monorepoPatterns) {
3840
4421
  if (!dirNames.includes(pattern))
3841
4422
  continue;
3842
- const patternDir = path12.join(rootDir, pattern);
4423
+ const patternDir = path13.join(rootDir, pattern);
3843
4424
  try {
3844
- const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
4425
+ const subDirs = await fs5.readdir(patternDir, { withFileTypes: true });
3845
4426
  for (const subDir of subDirs) {
3846
4427
  if (!subDir.isDirectory())
3847
4428
  continue;
@@ -3870,8 +4451,8 @@ async function detectProjectStructure(rootDir) {
3870
4451
  }
3871
4452
  let rootType = "unknown";
3872
4453
  try {
3873
- const rootPkgPath = path12.join(rootDir, "package.json");
3874
- const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
4454
+ const rootPkgPath = path13.join(rootDir, "package.json");
4455
+ const rootPkg = JSON.parse(await fs5.readFile(rootPkgPath, "utf-8"));
3875
4456
  if (rootPkg.workspaces)
3876
4457
  isMonorepo = true;
3877
4458
  const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
@@ -3911,8 +4492,8 @@ class IntrospectionIndex {
3911
4492
  async initialize() {
3912
4493
  this.structure = await detectProjectStructure(this.rootDir);
3913
4494
  try {
3914
- const configPath = path13.join(this.rootDir, ".raggrep", "config.json");
3915
- const configContent = await fs5.readFile(configPath, "utf-8");
4495
+ const configPath = path14.join(this.rootDir, ".raggrep", "config.json");
4496
+ const configContent = await fs6.readFile(configPath, "utf-8");
3916
4497
  const config = JSON.parse(configContent);
3917
4498
  this.config = config.introspection || {};
3918
4499
  } catch {}
@@ -3951,28 +4532,28 @@ class IntrospectionIndex {
3951
4532
  }
3952
4533
  }
3953
4534
  async save(config) {
3954
- const introDir = path13.join(getRaggrepDir(this.rootDir, config), "introspection");
3955
- await fs5.mkdir(introDir, { recursive: true });
3956
- const projectPath = path13.join(introDir, "_project.json");
3957
- await fs5.writeFile(projectPath, JSON.stringify({
4535
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
4536
+ await fs6.mkdir(introDir, { recursive: true });
4537
+ const projectPath = path14.join(introDir, "_project.json");
4538
+ await fs6.writeFile(projectPath, JSON.stringify({
3958
4539
  version: "1.0.0",
3959
4540
  lastUpdated: new Date().toISOString(),
3960
4541
  structure: this.structure
3961
4542
  }, null, 2));
3962
4543
  for (const [filepath, intro] of this.files) {
3963
- const introFilePath = path13.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
3964
- await fs5.mkdir(path13.dirname(introFilePath), { recursive: true });
3965
- await fs5.writeFile(introFilePath, JSON.stringify(intro, null, 2));
4544
+ const introFilePath = path14.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
4545
+ await fs6.mkdir(path14.dirname(introFilePath), { recursive: true });
4546
+ await fs6.writeFile(introFilePath, JSON.stringify(intro, null, 2));
3966
4547
  }
3967
4548
  }
3968
4549
  async load(config) {
3969
- const introDir = path13.join(getRaggrepDir(this.rootDir, config), "introspection");
4550
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
3970
4551
  try {
3971
- const projectPath = path13.join(introDir, "_project.json");
3972
- const projectContent = await fs5.readFile(projectPath, "utf-8");
4552
+ const projectPath = path14.join(introDir, "_project.json");
4553
+ const projectContent = await fs6.readFile(projectPath, "utf-8");
3973
4554
  const projectData = JSON.parse(projectContent);
3974
4555
  this.structure = projectData.structure;
3975
- await this.loadFilesRecursive(path13.join(introDir, "files"), "");
4556
+ await this.loadFilesRecursive(path14.join(introDir, "files"), "");
3976
4557
  } catch {
3977
4558
  this.structure = null;
3978
4559
  this.files.clear();
@@ -3980,14 +4561,14 @@ class IntrospectionIndex {
3980
4561
  }
3981
4562
  async loadFilesRecursive(basePath, prefix) {
3982
4563
  try {
3983
- const entries = await fs5.readdir(basePath, { withFileTypes: true });
4564
+ const entries = await fs6.readdir(basePath, { withFileTypes: true });
3984
4565
  for (const entry of entries) {
3985
- const entryPath = path13.join(basePath, entry.name);
4566
+ const entryPath = path14.join(basePath, entry.name);
3986
4567
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
3987
4568
  if (entry.isDirectory()) {
3988
4569
  await this.loadFilesRecursive(entryPath, relativePath);
3989
4570
  } else if (entry.name.endsWith(".json")) {
3990
- const content = await fs5.readFile(entryPath, "utf-8");
4571
+ const content = await fs6.readFile(entryPath, "utf-8");
3991
4572
  const intro = JSON.parse(content);
3992
4573
  this.files.set(intro.filepath, intro);
3993
4574
  }
@@ -4133,7 +4714,7 @@ async function indexDirectory(rootDir, options = {}) {
4133
4714
  const quiet = options.quiet ?? false;
4134
4715
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
4135
4716
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4136
- rootDir = path14.resolve(rootDir);
4717
+ rootDir = path15.resolve(rootDir);
4137
4718
  const location = getIndexLocation(rootDir);
4138
4719
  logger.info(`Indexing directory: ${rootDir}`);
4139
4720
  logger.info(`Index location: ${location.indexDir}`);
@@ -4185,12 +4766,12 @@ async function indexDirectory(rootDir, options = {}) {
4185
4766
  rootDir,
4186
4767
  config,
4187
4768
  readFile: async (filepath) => {
4188
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4189
- return fs6.readFile(fullPath, "utf-8");
4769
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4770
+ return fs7.readFile(fullPath, "utf-8");
4190
4771
  },
4191
4772
  getFileStats: async (filepath) => {
4192
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4193
- const stats = await fs6.stat(fullPath);
4773
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4774
+ const stats = await fs7.stat(fullPath);
4194
4775
  return { lastModified: stats.mtime.toISOString() };
4195
4776
  }
4196
4777
  };
@@ -4215,7 +4796,7 @@ async function isIndexVersionCompatible(rootDir) {
4215
4796
  const config = await loadConfig(rootDir);
4216
4797
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4217
4798
  try {
4218
- const content = await fs6.readFile(globalManifestPath, "utf-8");
4799
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4219
4800
  const manifest = JSON.parse(content);
4220
4801
  return manifest.version === INDEX_SCHEMA_VERSION;
4221
4802
  } catch {
@@ -4225,11 +4806,11 @@ async function isIndexVersionCompatible(rootDir) {
4225
4806
  async function deleteIndex(rootDir) {
4226
4807
  const indexDir = getRaggrepDir(rootDir);
4227
4808
  try {
4228
- await fs6.rm(indexDir, { recursive: true, force: true });
4809
+ await fs7.rm(indexDir, { recursive: true, force: true });
4229
4810
  } catch {}
4230
4811
  }
4231
4812
  async function resetIndex(rootDir) {
4232
- rootDir = path14.resolve(rootDir);
4813
+ rootDir = path15.resolve(rootDir);
4233
4814
  const status = await getIndexStatus(rootDir);
4234
4815
  if (!status.exists) {
4235
4816
  throw new Error(`No index found for ${rootDir}`);
@@ -4244,7 +4825,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4244
4825
  const verbose = options.verbose ?? false;
4245
4826
  const quiet = options.quiet ?? false;
4246
4827
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4247
- rootDir = path14.resolve(rootDir);
4828
+ rootDir = path15.resolve(rootDir);
4248
4829
  const status = await getIndexStatus(rootDir);
4249
4830
  if (!status.exists) {
4250
4831
  logger.info(`No index found. Creating index...
@@ -4271,7 +4852,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4271
4852
  const introspection = new IntrospectionIndex(rootDir);
4272
4853
  await introspection.initialize();
4273
4854
  const currentFiles = await findFiles(rootDir, config);
4274
- const currentFileSet = new Set(currentFiles.map((f) => path14.relative(rootDir, f)));
4855
+ const currentFileSet = new Set(currentFiles.map((f) => path15.relative(rootDir, f)));
4275
4856
  let totalIndexed = 0;
4276
4857
  let totalRemoved = 0;
4277
4858
  let totalUnchanged = 0;
@@ -4299,29 +4880,43 @@ async function ensureIndexFresh(rootDir, options = {}) {
4299
4880
  filesToRemove.push(filepath);
4300
4881
  }
4301
4882
  }
4883
+ const removedFilepaths = [];
4302
4884
  for (const filepath of filesToRemove) {
4303
4885
  logger.debug(` Removing stale: ${filepath}`);
4304
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4886
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4305
4887
  try {
4306
- await fs6.unlink(indexFilePath);
4888
+ await fs7.unlink(indexFilePath);
4307
4889
  } catch {}
4308
- const symbolicFilePath = path14.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4890
+ const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4309
4891
  try {
4310
- await fs6.unlink(symbolicFilePath);
4892
+ await fs7.unlink(symbolicFilePath);
4311
4893
  } catch {}
4312
4894
  delete manifest.files[filepath];
4895
+ removedFilepaths.push(filepath);
4313
4896
  totalRemoved++;
4314
4897
  }
4898
+ if (removedFilepaths.length > 0) {
4899
+ try {
4900
+ const { LiteralIndex: LiteralIndex2 } = await Promise.resolve().then(() => (init_literalIndex(), exports_literalIndex));
4901
+ const raggrepDir = getRaggrepDir(rootDir, config);
4902
+ const literalIndex = new LiteralIndex2(raggrepDir, module.id);
4903
+ await literalIndex.initialize();
4904
+ for (const filepath of removedFilepaths) {
4905
+ literalIndex.removeFile(filepath);
4906
+ }
4907
+ await literalIndex.save();
4908
+ } catch {}
4909
+ }
4315
4910
  const ctx = {
4316
4911
  rootDir,
4317
4912
  config,
4318
4913
  readFile: async (filepath) => {
4319
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4320
- return fs6.readFile(fullPath, "utf-8");
4914
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4915
+ return fs7.readFile(fullPath, "utf-8");
4321
4916
  },
4322
4917
  getFileStats: async (filepath) => {
4323
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4324
- const stats = await fs6.stat(fullPath);
4918
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4919
+ const stats = await fs7.stat(fullPath);
4325
4920
  return { lastModified: stats.mtime.toISOString() };
4326
4921
  },
4327
4922
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4329,10 +4924,10 @@ async function ensureIndexFresh(rootDir, options = {}) {
4329
4924
  const totalFiles = currentFiles.length;
4330
4925
  for (let i = 0;i < currentFiles.length; i++) {
4331
4926
  const filepath = currentFiles[i];
4332
- const relativePath = path14.relative(rootDir, filepath);
4927
+ const relativePath = path15.relative(rootDir, filepath);
4333
4928
  const progress = `[${i + 1}/${totalFiles}]`;
4334
4929
  try {
4335
- const stats = await fs6.stat(filepath);
4930
+ const stats = await fs7.stat(filepath);
4336
4931
  const lastModified = stats.mtime.toISOString();
4337
4932
  const existingEntry = manifest.files[relativePath];
4338
4933
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4340,7 +4935,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4340
4935
  continue;
4341
4936
  }
4342
4937
  logger.progress(` ${progress} Indexing: ${relativePath}`);
4343
- const content = await fs6.readFile(filepath, "utf-8");
4938
+ const content = await fs7.readFile(filepath, "utf-8");
4344
4939
  introspection.addFile(relativePath, content);
4345
4940
  const fileIndex = await module.indexFile(relativePath, content, ctx);
4346
4941
  if (fileIndex) {
@@ -4389,7 +4984,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4389
4984
  };
4390
4985
  const manifest = await loadModuleManifest(rootDir, module.id, config);
4391
4986
  const indexPath = getModuleIndexPath(rootDir, module.id, config);
4392
- const currentFileSet = new Set(files.map((f) => path14.relative(rootDir, f)));
4987
+ const currentFileSet = new Set(files.map((f) => path15.relative(rootDir, f)));
4393
4988
  const filesToRemove = [];
4394
4989
  for (const filepath of Object.keys(manifest.files)) {
4395
4990
  if (!currentFileSet.has(filepath)) {
@@ -4400,13 +4995,13 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4400
4995
  logger.info(` Removing ${filesToRemove.length} stale entries...`);
4401
4996
  for (const filepath of filesToRemove) {
4402
4997
  logger.debug(` Removing: ${filepath}`);
4403
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4998
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4404
4999
  try {
4405
- await fs6.unlink(indexFilePath);
5000
+ await fs7.unlink(indexFilePath);
4406
5001
  } catch {}
4407
- const symbolicFilePath = path14.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
5002
+ const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4408
5003
  try {
4409
- await fs6.unlink(symbolicFilePath);
5004
+ await fs7.unlink(symbolicFilePath);
4410
5005
  } catch {}
4411
5006
  delete manifest.files[filepath];
4412
5007
  }
@@ -4416,12 +5011,12 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4416
5011
  rootDir,
4417
5012
  config,
4418
5013
  readFile: async (filepath) => {
4419
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4420
- return fs6.readFile(fullPath, "utf-8");
5014
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
5015
+ return fs7.readFile(fullPath, "utf-8");
4421
5016
  },
4422
5017
  getFileStats: async (filepath) => {
4423
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4424
- const stats = await fs6.stat(fullPath);
5018
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
5019
+ const stats = await fs7.stat(fullPath);
4425
5020
  return { lastModified: stats.mtime.toISOString() };
4426
5021
  },
4427
5022
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4429,9 +5024,9 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4429
5024
  const totalFiles = files.length;
4430
5025
  let completedCount = 0;
4431
5026
  const processFile = async (filepath, _index) => {
4432
- const relativePath = path14.relative(rootDir, filepath);
5027
+ const relativePath = path15.relative(rootDir, filepath);
4433
5028
  try {
4434
- const stats = await fs6.stat(filepath);
5029
+ const stats = await fs7.stat(filepath);
4435
5030
  const lastModified = stats.mtime.toISOString();
4436
5031
  const existingEntry = manifest.files[relativePath];
4437
5032
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4439,7 +5034,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4439
5034
  logger.debug(` [${completedCount}/${totalFiles}] Skipped ${relativePath} (unchanged)`);
4440
5035
  return { relativePath, status: "skipped" };
4441
5036
  }
4442
- const content = await fs6.readFile(filepath, "utf-8");
5037
+ const content = await fs7.readFile(filepath, "utf-8");
4443
5038
  introspection.addFile(relativePath, content);
4444
5039
  completedCount++;
4445
5040
  logger.progress(` [${completedCount}/${totalFiles}] Processing: ${relativePath}`);
@@ -4507,7 +5102,7 @@ async function findFiles(rootDir, config) {
4507
5102
  async function loadModuleManifest(rootDir, moduleId, config) {
4508
5103
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
4509
5104
  try {
4510
- const content = await fs6.readFile(manifestPath, "utf-8");
5105
+ const content = await fs7.readFile(manifestPath, "utf-8");
4511
5106
  return JSON.parse(content);
4512
5107
  } catch {
4513
5108
  return {
@@ -4520,14 +5115,14 @@ async function loadModuleManifest(rootDir, moduleId, config) {
4520
5115
  }
4521
5116
  async function writeModuleManifest(rootDir, moduleId, manifest, config) {
4522
5117
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
4523
- await fs6.mkdir(path14.dirname(manifestPath), { recursive: true });
4524
- await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
5118
+ await fs7.mkdir(path15.dirname(manifestPath), { recursive: true });
5119
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4525
5120
  }
4526
5121
  async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
4527
5122
  const indexPath = getModuleIndexPath(rootDir, moduleId, config);
4528
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4529
- await fs6.mkdir(path14.dirname(indexFilePath), { recursive: true });
4530
- await fs6.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
5123
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5124
+ await fs7.mkdir(path15.dirname(indexFilePath), { recursive: true });
5125
+ await fs7.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
4531
5126
  }
4532
5127
  async function updateGlobalManifest(rootDir, modules, config) {
4533
5128
  const manifestPath = getGlobalManifestPath(rootDir, config);
@@ -4536,13 +5131,13 @@ async function updateGlobalManifest(rootDir, modules, config) {
4536
5131
  lastUpdated: new Date().toISOString(),
4537
5132
  modules: modules.map((m) => m.id)
4538
5133
  };
4539
- await fs6.mkdir(path14.dirname(manifestPath), { recursive: true });
4540
- await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
5134
+ await fs7.mkdir(path15.dirname(manifestPath), { recursive: true });
5135
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4541
5136
  }
4542
5137
  async function cleanupIndex(rootDir, options = {}) {
4543
5138
  const verbose = options.verbose ?? false;
4544
5139
  const logger = options.logger ?? createLogger({ verbose });
4545
- rootDir = path14.resolve(rootDir);
5140
+ rootDir = path15.resolve(rootDir);
4546
5141
  logger.info(`Cleaning up index in: ${rootDir}`);
4547
5142
  const config = await loadConfig(rootDir);
4548
5143
  await registerBuiltInModules();
@@ -4572,9 +5167,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4572
5167
  const filesToRemove = [];
4573
5168
  const updatedFiles = {};
4574
5169
  for (const [filepath, entry] of Object.entries(manifest.files)) {
4575
- const fullPath = path14.join(rootDir, filepath);
5170
+ const fullPath = path15.join(rootDir, filepath);
4576
5171
  try {
4577
- await fs6.access(fullPath);
5172
+ await fs7.access(fullPath);
4578
5173
  updatedFiles[filepath] = entry;
4579
5174
  result.kept++;
4580
5175
  } catch {
@@ -4584,9 +5179,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4584
5179
  }
4585
5180
  }
4586
5181
  for (const filepath of filesToRemove) {
4587
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5182
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4588
5183
  try {
4589
- await fs6.unlink(indexFilePath);
5184
+ await fs7.unlink(indexFilePath);
4590
5185
  } catch {}
4591
5186
  }
4592
5187
  manifest.files = updatedFiles;
@@ -4597,16 +5192,16 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4597
5192
  }
4598
5193
  async function cleanupEmptyDirectories(dir) {
4599
5194
  try {
4600
- const entries = await fs6.readdir(dir, { withFileTypes: true });
5195
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
4601
5196
  for (const entry of entries) {
4602
5197
  if (entry.isDirectory()) {
4603
- const subDir = path14.join(dir, entry.name);
5198
+ const subDir = path15.join(dir, entry.name);
4604
5199
  await cleanupEmptyDirectories(subDir);
4605
5200
  }
4606
5201
  }
4607
- const remainingEntries = await fs6.readdir(dir);
5202
+ const remainingEntries = await fs7.readdir(dir);
4608
5203
  if (remainingEntries.length === 0) {
4609
- await fs6.rmdir(dir);
5204
+ await fs7.rmdir(dir);
4610
5205
  return true;
4611
5206
  }
4612
5207
  return false;
@@ -4615,7 +5210,7 @@ async function cleanupEmptyDirectories(dir) {
4615
5210
  }
4616
5211
  }
4617
5212
  async function getIndexStatus(rootDir) {
4618
- rootDir = path14.resolve(rootDir);
5213
+ rootDir = path15.resolve(rootDir);
4619
5214
  const config = await loadConfig(rootDir);
4620
5215
  const location = getIndexLocation(rootDir);
4621
5216
  const indexDir = location.indexDir;
@@ -4627,13 +5222,13 @@ async function getIndexStatus(rootDir) {
4627
5222
  totalFiles: 0
4628
5223
  };
4629
5224
  try {
4630
- await fs6.access(indexDir);
5225
+ await fs7.access(indexDir);
4631
5226
  } catch {
4632
5227
  return status;
4633
5228
  }
4634
5229
  try {
4635
5230
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4636
- const content = await fs6.readFile(globalManifestPath, "utf-8");
5231
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4637
5232
  const globalManifest = JSON.parse(content);
4638
5233
  status.exists = true;
4639
5234
  status.lastUpdated = globalManifest.lastUpdated;
@@ -4651,7 +5246,7 @@ async function getIndexStatus(rootDir) {
4651
5246
  }
4652
5247
  } catch {
4653
5248
  try {
4654
- const entries = await fs6.readdir(path14.join(indexDir, "index"));
5249
+ const entries = await fs7.readdir(path15.join(indexDir, "index"));
4655
5250
  if (entries.length > 0) {
4656
5251
  status.exists = true;
4657
5252
  for (const entry of entries) {
@@ -4673,8 +5268,8 @@ async function getIndexStatus(rootDir) {
4673
5268
  }
4674
5269
 
4675
5270
  // src/app/search/index.ts
4676
- import * as fs7 from "fs/promises";
4677
- import * as path15 from "path";
5271
+ import * as fs8 from "fs/promises";
5272
+ import * as path16 from "path";
4678
5273
 
4679
5274
  // src/types.ts
4680
5275
  init_entities();
@@ -4682,7 +5277,7 @@ init_entities();
4682
5277
  // src/app/search/index.ts
4683
5278
  init_config2();
4684
5279
  async function search(rootDir, query, options = {}) {
4685
- rootDir = path15.resolve(rootDir);
5280
+ rootDir = path16.resolve(rootDir);
4686
5281
  const ensureFresh = options.ensureFresh ?? DEFAULT_SEARCH_OPTIONS.ensureFresh;
4687
5282
  if (ensureFresh) {
4688
5283
  await ensureIndexFresh(rootDir, { quiet: true });
@@ -4735,9 +5330,9 @@ function createSearchContext(rootDir, moduleId, config) {
4735
5330
  config,
4736
5331
  loadFileIndex: async (filepath) => {
4737
5332
  const hasExtension = /\.[^./]+$/.test(filepath);
4738
- const indexFilePath = hasExtension ? path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path15.join(indexPath, filepath + ".json");
5333
+ const indexFilePath = hasExtension ? path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path16.join(indexPath, filepath + ".json");
4739
5334
  try {
4740
- const content = await fs7.readFile(indexFilePath, "utf-8");
5335
+ const content = await fs8.readFile(indexFilePath, "utf-8");
4741
5336
  return JSON.parse(content);
4742
5337
  } catch {
4743
5338
  return null;
@@ -4747,7 +5342,7 @@ function createSearchContext(rootDir, moduleId, config) {
4747
5342
  const files = [];
4748
5343
  await traverseDirectory(indexPath, files, indexPath);
4749
5344
  return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
4750
- const relative3 = path15.relative(indexPath, f);
5345
+ const relative3 = path16.relative(indexPath, f);
4751
5346
  return relative3.replace(/\.json$/, "");
4752
5347
  });
4753
5348
  }
@@ -4755,9 +5350,9 @@ function createSearchContext(rootDir, moduleId, config) {
4755
5350
  }
4756
5351
  async function traverseDirectory(dir, files, basePath) {
4757
5352
  try {
4758
- const entries = await fs7.readdir(dir, { withFileTypes: true });
5353
+ const entries = await fs8.readdir(dir, { withFileTypes: true });
4759
5354
  for (const entry of entries) {
4760
- const fullPath = path15.join(dir, entry.name);
5355
+ const fullPath = path16.join(dir, entry.name);
4761
5356
  if (entry.isDirectory()) {
4762
5357
  await traverseDirectory(fullPath, files, basePath);
4763
5358
  } else if (entry.isFile()) {
@@ -4769,7 +5364,7 @@ async function traverseDirectory(dir, files, basePath) {
4769
5364
  async function loadGlobalManifest(rootDir, config) {
4770
5365
  const manifestPath = getGlobalManifestPath(rootDir, config);
4771
5366
  try {
4772
- const content = await fs7.readFile(manifestPath, "utf-8");
5367
+ const content = await fs8.readFile(manifestPath, "utf-8");
4773
5368
  return JSON.parse(content);
4774
5369
  } catch {
4775
5370
  return null;
@@ -4859,4 +5454,4 @@ export {
4859
5454
  ConsoleLogger
4860
5455
  };
4861
5456
 
4862
- //# debugId=4915A936C06DA9B864756E2164756E21
5457
+ //# debugId=F5160C7762E8AC7864756E2164756E21