raggrep 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,158 @@ var init_symbolicIndex = __esm(() => {
2889
3182
  init_keywords();
2890
3183
  });
2891
3184
 
3185
+ // src/infrastructure/storage/literalIndex.ts
3186
+ import * as fs4 from "fs/promises";
3187
+ import * as path9 from "path";
3188
+
3189
+ class LiteralIndex {
3190
+ indexPath;
3191
+ moduleId;
3192
+ entries = new Map;
3193
+ static VERSION = "1.0.0";
3194
+ constructor(indexDir, moduleId) {
3195
+ this.indexPath = path9.join(indexDir, "index", moduleId, "literals");
3196
+ this.moduleId = moduleId;
3197
+ }
3198
+ async initialize() {
3199
+ try {
3200
+ await this.load();
3201
+ } catch {
3202
+ this.entries = new Map;
3203
+ }
3204
+ }
3205
+ addLiterals(chunkId, filepath, literals) {
3206
+ for (const literal of literals) {
3207
+ const key = literal.value.toLowerCase();
3208
+ const existingEntries = this.entries.get(key) || [];
3209
+ const existingIndex = existingEntries.findIndex((e) => e.chunkId === chunkId);
3210
+ const newEntry = {
3211
+ chunkId,
3212
+ filepath,
3213
+ originalCasing: literal.value,
3214
+ type: literal.type,
3215
+ matchType: literal.matchType
3216
+ };
3217
+ if (existingIndex >= 0) {
3218
+ const existing = existingEntries[existingIndex];
3219
+ if (shouldReplaceMatchType(existing.matchType, literal.matchType)) {
3220
+ existingEntries[existingIndex] = newEntry;
3221
+ }
3222
+ } else {
3223
+ existingEntries.push(newEntry);
3224
+ }
3225
+ this.entries.set(key, existingEntries);
3226
+ }
3227
+ }
3228
+ removeChunk(chunkId) {
3229
+ for (const [key, entries] of this.entries) {
3230
+ const filtered = entries.filter((e) => e.chunkId !== chunkId);
3231
+ if (filtered.length === 0) {
3232
+ this.entries.delete(key);
3233
+ } else if (filtered.length !== entries.length) {
3234
+ this.entries.set(key, filtered);
3235
+ }
3236
+ }
3237
+ }
3238
+ findMatches(queryLiterals) {
3239
+ const matches = [];
3240
+ for (const queryLiteral of queryLiterals) {
3241
+ const key = queryLiteral.value.toLowerCase();
3242
+ const entries = this.entries.get(key);
3243
+ if (!entries) {
3244
+ continue;
3245
+ }
3246
+ for (const entry of entries) {
3247
+ const exactMatch = entry.originalCasing === queryLiteral.value;
3248
+ matches.push({
3249
+ queryLiteral,
3250
+ indexedLiteral: {
3251
+ value: entry.originalCasing,
3252
+ type: entry.type,
3253
+ matchType: entry.matchType
3254
+ },
3255
+ chunkId: entry.chunkId,
3256
+ filepath: entry.filepath,
3257
+ exactMatch
3258
+ });
3259
+ }
3260
+ }
3261
+ return matches;
3262
+ }
3263
+ getChunksForLiteral(literal) {
3264
+ const key = literal.toLowerCase();
3265
+ const entries = this.entries.get(key);
3266
+ return entries ? entries.map((e) => e.chunkId) : [];
3267
+ }
3268
+ async save() {
3269
+ await fs4.mkdir(this.indexPath, { recursive: true });
3270
+ const data = {
3271
+ version: LiteralIndex.VERSION,
3272
+ entries: Object.fromEntries(this.entries)
3273
+ };
3274
+ const indexFile = path9.join(this.indexPath, "_index.json");
3275
+ await fs4.writeFile(indexFile, JSON.stringify(data, null, 2));
3276
+ }
3277
+ async load() {
3278
+ const indexFile = path9.join(this.indexPath, "_index.json");
3279
+ const content = await fs4.readFile(indexFile, "utf-8");
3280
+ const data = JSON.parse(content);
3281
+ if (data.version !== LiteralIndex.VERSION) {
3282
+ console.warn(`Literal index version mismatch: expected ${LiteralIndex.VERSION}, got ${data.version}`);
3283
+ }
3284
+ this.entries = new Map(Object.entries(data.entries));
3285
+ }
3286
+ async exists() {
3287
+ try {
3288
+ const indexFile = path9.join(this.indexPath, "_index.json");
3289
+ await fs4.access(indexFile);
3290
+ return true;
3291
+ } catch {
3292
+ return false;
3293
+ }
3294
+ }
3295
+ clear() {
3296
+ this.entries.clear();
3297
+ }
3298
+ get size() {
3299
+ return this.entries.size;
3300
+ }
3301
+ get totalMappings() {
3302
+ let count = 0;
3303
+ for (const entries of this.entries.values()) {
3304
+ count += entries.length;
3305
+ }
3306
+ return count;
3307
+ }
3308
+ getAllLiterals() {
3309
+ return Array.from(this.entries.keys());
3310
+ }
3311
+ buildMatchMap(queryLiterals) {
3312
+ const matches = this.findMatches(queryLiterals);
3313
+ const matchMap = new Map;
3314
+ for (const match of matches) {
3315
+ const existing = matchMap.get(match.chunkId) || [];
3316
+ existing.push(match);
3317
+ matchMap.set(match.chunkId, existing);
3318
+ }
3319
+ return matchMap;
3320
+ }
3321
+ }
3322
+ function shouldReplaceMatchType(existing, incoming) {
3323
+ const priority = {
3324
+ definition: 3,
3325
+ reference: 2,
3326
+ import: 1
3327
+ };
3328
+ return priority[incoming] > priority[existing];
3329
+ }
3330
+ var init_literalIndex = () => {};
3331
+
2892
3332
  // src/infrastructure/storage/index.ts
2893
3333
  var init_storage = __esm(() => {
2894
3334
  init_fileIndexStorage();
2895
3335
  init_symbolicIndex();
3336
+ init_literalIndex();
2896
3337
  });
2897
3338
 
2898
3339
  // src/modules/language/typescript/index.ts
@@ -2905,9 +3346,9 @@ __export(exports_typescript, {
2905
3346
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
2906
3347
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
2907
3348
  });
2908
- import * as path9 from "path";
3349
+ import * as path10 from "path";
2909
3350
  function isTypeScriptFile(filepath) {
2910
- const ext = path9.extname(filepath).toLowerCase();
3351
+ const ext = path10.extname(filepath).toLowerCase();
2911
3352
  return TYPESCRIPT_EXTENSIONS.includes(ext);
2912
3353
  }
2913
3354
  function calculateChunkTypeBoost(chunk) {
@@ -2942,7 +3383,9 @@ class TypeScriptModule {
2942
3383
  }
2943
3384
  embeddingConfig = null;
2944
3385
  symbolicIndex = null;
3386
+ literalIndex = null;
2945
3387
  pendingSummaries = new Map;
3388
+ pendingLiterals = new Map;
2946
3389
  rootDir = "";
2947
3390
  logger = undefined;
2948
3391
  async initialize(config) {
@@ -2956,6 +3399,7 @@ class TypeScriptModule {
2956
3399
  }
2957
3400
  configureEmbeddings(this.embeddingConfig);
2958
3401
  this.pendingSummaries.clear();
3402
+ this.pendingLiterals.clear();
2959
3403
  }
2960
3404
  async indexFile(filepath, content, ctx) {
2961
3405
  if (!isTypeScriptFile(filepath)) {
@@ -3015,6 +3459,17 @@ class TypeScriptModule {
3015
3459
  }
3016
3460
  };
3017
3461
  this.pendingSummaries.set(filepath, fileSummary);
3462
+ for (const chunk of chunks) {
3463
+ const literals = extractLiterals(chunk);
3464
+ if (literals.length > 0) {
3465
+ const existing = this.pendingLiterals.get(chunk.id);
3466
+ if (existing) {
3467
+ existing.literals.push(...literals);
3468
+ } else {
3469
+ this.pendingLiterals.set(chunk.id, { filepath, literals });
3470
+ }
3471
+ }
3472
+ }
3018
3473
  return {
3019
3474
  filepath,
3020
3475
  lastModified: stats.lastModified,
@@ -3032,7 +3487,14 @@ class TypeScriptModule {
3032
3487
  }
3033
3488
  this.symbolicIndex.buildBM25Index();
3034
3489
  await this.symbolicIndex.save();
3490
+ this.literalIndex = new LiteralIndex(indexDir, this.id);
3491
+ await this.literalIndex.initialize();
3492
+ for (const [chunkId, { filepath, literals }] of this.pendingLiterals) {
3493
+ this.literalIndex.addLiterals(chunkId, filepath, literals);
3494
+ }
3495
+ await this.literalIndex.save();
3035
3496
  this.pendingSummaries.clear();
3497
+ this.pendingLiterals.clear();
3036
3498
  }
3037
3499
  async search(query, ctx, options = {}) {
3038
3500
  const {
@@ -3040,8 +3502,15 @@ class TypeScriptModule {
3040
3502
  minScore = DEFAULT_MIN_SCORE2,
3041
3503
  filePatterns
3042
3504
  } = options;
3505
+ const { literals: queryLiterals, remainingQuery } = parseQueryLiterals(query);
3043
3506
  const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
3044
3507
  const symbolicIndex = new SymbolicIndex(indexDir, this.id);
3508
+ const literalIndex = new LiteralIndex(indexDir, this.id);
3509
+ let literalMatchMap = new Map;
3510
+ try {
3511
+ await literalIndex.initialize();
3512
+ literalMatchMap = literalIndex.buildMatchMap(queryLiterals);
3513
+ } catch {}
3045
3514
  let allFiles;
3046
3515
  try {
3047
3516
  await symbolicIndex.initialize();
@@ -3061,7 +3530,8 @@ class TypeScriptModule {
3061
3530
  });
3062
3531
  });
3063
3532
  }
3064
- const queryEmbedding = await getEmbedding(query);
3533
+ const semanticQuery = remainingQuery.trim() || query;
3534
+ const queryEmbedding = await getEmbedding(semanticQuery);
3065
3535
  const bm25Index = new BM25Index;
3066
3536
  const allChunksData = [];
3067
3537
  for (const filepath of filesToSearch) {
@@ -3095,14 +3565,14 @@ class TypeScriptModule {
3095
3565
  const summary = symbolicIndex.getFileSummary(filepath);
3096
3566
  if (summary?.pathContext) {
3097
3567
  let boost = 0;
3098
- const ctx2 = summary.pathContext;
3099
- if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
3568
+ const pathCtx = summary.pathContext;
3569
+ if (pathCtx.domain && queryTerms.some((t) => pathCtx.domain.includes(t) || t.includes(pathCtx.domain))) {
3100
3570
  boost += 0.1;
3101
3571
  }
3102
- if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
3572
+ if (pathCtx.layer && queryTerms.some((t) => pathCtx.layer.includes(t) || t.includes(pathCtx.layer))) {
3103
3573
  boost += 0.05;
3104
3574
  }
3105
- const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3575
+ const segmentMatch = pathCtx.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
3106
3576
  if (segmentMatch) {
3107
3577
  boost += 0.05;
3108
3578
  }
@@ -3110,6 +3580,7 @@ class TypeScriptModule {
3110
3580
  }
3111
3581
  }
3112
3582
  const results = [];
3583
+ const processedChunkIds = new Set;
3113
3584
  for (const { filepath, chunk, embedding } of allChunksData) {
3114
3585
  const semanticScore = cosineSimilarity(queryEmbedding, embedding);
3115
3586
  const bm25Score = bm25Scores.get(chunk.id) || 0;
@@ -3117,13 +3588,18 @@ class TypeScriptModule {
3117
3588
  const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3118
3589
  const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3119
3590
  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) {
3591
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3592
+ const baseScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score;
3593
+ const literalMatches = literalMatchMap.get(chunk.id) || [];
3594
+ const literalContribution = calculateLiteralContribution(literalMatches, true);
3595
+ const boostedScore = applyLiteralBoost(baseScore, literalMatches, true);
3596
+ const finalScore = boostedScore + additiveBoost;
3597
+ processedChunkIds.add(chunk.id);
3598
+ if (finalScore >= minScore || bm25Score > 0.3 || literalMatches.length > 0) {
3123
3599
  results.push({
3124
3600
  filepath,
3125
3601
  chunk,
3126
- score: hybridScore,
3602
+ score: finalScore,
3127
3603
  moduleId: this.id,
3128
3604
  context: {
3129
3605
  semanticScore,
@@ -3131,7 +3607,78 @@ class TypeScriptModule {
3131
3607
  pathBoost,
3132
3608
  fileTypeBoost,
3133
3609
  chunkTypeBoost,
3134
- exportBoost
3610
+ exportBoost,
3611
+ literalMultiplier: literalContribution.multiplier,
3612
+ literalMatchType: literalContribution.bestMatchType,
3613
+ literalConfidence: literalContribution.bestConfidence,
3614
+ literalMatchCount: literalContribution.matchCount
3615
+ }
3616
+ });
3617
+ }
3618
+ }
3619
+ const literalOnlyFiles = new Map;
3620
+ for (const [chunkId, matches] of literalMatchMap) {
3621
+ if (processedChunkIds.has(chunkId)) {
3622
+ continue;
3623
+ }
3624
+ const filepath = matches[0]?.filepath;
3625
+ if (!filepath)
3626
+ continue;
3627
+ const existing = literalOnlyFiles.get(filepath) || [];
3628
+ existing.push(...matches);
3629
+ literalOnlyFiles.set(filepath, existing);
3630
+ }
3631
+ for (const [filepath, matches] of literalOnlyFiles) {
3632
+ const fileIndex = await ctx.loadFileIndex(filepath);
3633
+ if (!fileIndex)
3634
+ continue;
3635
+ const moduleData = fileIndex.moduleData;
3636
+ const chunkMatches = new Map;
3637
+ for (const match of matches) {
3638
+ const existing = chunkMatches.get(match.chunkId) || [];
3639
+ existing.push(match);
3640
+ chunkMatches.set(match.chunkId, existing);
3641
+ }
3642
+ for (const [chunkId, chunkLiteralMatches] of chunkMatches) {
3643
+ if (processedChunkIds.has(chunkId))
3644
+ continue;
3645
+ const chunkIndex = fileIndex.chunks.findIndex((c) => c.id === chunkId);
3646
+ if (chunkIndex === -1)
3647
+ continue;
3648
+ const chunk = fileIndex.chunks[chunkIndex];
3649
+ const embedding = moduleData?.embeddings?.[chunkIndex];
3650
+ let semanticScore = 0;
3651
+ if (embedding) {
3652
+ semanticScore = cosineSimilarity(queryEmbedding, embedding);
3653
+ }
3654
+ const bm25Score = bm25Scores.get(chunkId) || 0;
3655
+ const pathBoost = pathBoosts.get(filepath) || 0;
3656
+ const fileTypeBoost = calculateFileTypeBoost(filepath, queryTerms);
3657
+ const chunkTypeBoost = calculateChunkTypeBoost(chunk);
3658
+ const exportBoost = calculateExportBoost(chunk);
3659
+ const additiveBoost = pathBoost + fileTypeBoost + chunkTypeBoost + exportBoost;
3660
+ const literalContribution = calculateLiteralContribution(chunkLiteralMatches, false);
3661
+ const baseScore = semanticScore > 0 ? SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score : LITERAL_SCORING_CONSTANTS.BASE_SCORE;
3662
+ const boostedScore = applyLiteralBoost(baseScore, chunkLiteralMatches, semanticScore > 0);
3663
+ const finalScore = boostedScore + additiveBoost;
3664
+ processedChunkIds.add(chunkId);
3665
+ results.push({
3666
+ filepath,
3667
+ chunk,
3668
+ score: finalScore,
3669
+ moduleId: this.id,
3670
+ context: {
3671
+ semanticScore,
3672
+ bm25Score,
3673
+ pathBoost,
3674
+ fileTypeBoost,
3675
+ chunkTypeBoost,
3676
+ exportBoost,
3677
+ literalMultiplier: literalContribution.multiplier,
3678
+ literalMatchType: literalContribution.bestMatchType,
3679
+ literalConfidence: literalContribution.bestConfidence,
3680
+ literalMatchCount: literalContribution.matchCount,
3681
+ literalOnly: true
3135
3682
  }
3136
3683
  });
3137
3684
  }
@@ -3147,16 +3694,16 @@ class TypeScriptModule {
3147
3694
  while ((match = importRegex.exec(content)) !== null) {
3148
3695
  const importPath = match[1];
3149
3696
  if (importPath.startsWith(".")) {
3150
- const dir = path9.dirname(filepath);
3151
- const resolved = path9.normalize(path9.join(dir, importPath));
3697
+ const dir = path10.dirname(filepath);
3698
+ const resolved = path10.normalize(path10.join(dir, importPath));
3152
3699
  references.push(resolved);
3153
3700
  }
3154
3701
  }
3155
3702
  while ((match = requireRegex.exec(content)) !== null) {
3156
3703
  const importPath = match[1];
3157
3704
  if (importPath.startsWith(".")) {
3158
- const dir = path9.dirname(filepath);
3159
- const resolved = path9.normalize(path9.join(dir, importPath));
3705
+ const dir = path10.dirname(filepath);
3706
+ const resolved = path10.normalize(path10.join(dir, importPath));
3160
3707
  references.push(resolved);
3161
3708
  }
3162
3709
  }
@@ -3193,9 +3740,9 @@ __export(exports_json, {
3193
3740
  DEFAULT_TOP_K: () => DEFAULT_TOP_K3,
3194
3741
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE3
3195
3742
  });
3196
- import * as path10 from "path";
3743
+ import * as path11 from "path";
3197
3744
  function isJsonFile(filepath) {
3198
- const ext = path10.extname(filepath).toLowerCase();
3745
+ const ext = path11.extname(filepath).toLowerCase();
3199
3746
  return JSON_EXTENSIONS.includes(ext);
3200
3747
  }
3201
3748
  function extractJsonKeys(obj, prefix = "") {
@@ -3276,7 +3823,7 @@ class JsonModule {
3276
3823
  return null;
3277
3824
  }
3278
3825
  const chunkContents = textChunks.map((c) => {
3279
- const filename = path10.basename(filepath);
3826
+ const filename = path11.basename(filepath);
3280
3827
  return `${filename}: ${c.content}`;
3281
3828
  });
3282
3829
  const embeddings = await getEmbeddings(chunkContents);
@@ -3427,9 +3974,9 @@ __export(exports_markdown, {
3427
3974
  DEFAULT_TOP_K: () => DEFAULT_TOP_K4,
3428
3975
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE4
3429
3976
  });
3430
- import * as path11 from "path";
3977
+ import * as path12 from "path";
3431
3978
  function isMarkdownFile(filepath) {
3432
- const ext = path11.extname(filepath).toLowerCase();
3979
+ const ext = path12.extname(filepath).toLowerCase();
3433
3980
  return MARKDOWN_EXTENSIONS.includes(ext);
3434
3981
  }
3435
3982
  function parseMarkdownSections(content) {
@@ -3556,7 +4103,7 @@ class MarkdownModule {
3556
4103
  return null;
3557
4104
  }
3558
4105
  const chunkContents = sections.map((s) => {
3559
- const filename = path11.basename(filepath);
4106
+ const filename = path12.basename(filepath);
3560
4107
  const headingContext = s.heading ? `${s.heading}: ` : "";
3561
4108
  return `${filename} ${headingContext}${s.content}`;
3562
4109
  });
@@ -3711,8 +4258,8 @@ var init_markdown = __esm(() => {
3711
4258
  // src/app/indexer/index.ts
3712
4259
  init_config2();
3713
4260
  import { glob } from "glob";
3714
- import * as fs6 from "fs/promises";
3715
- import * as path14 from "path";
4261
+ import * as fs7 from "fs/promises";
4262
+ import * as path15 from "path";
3716
4263
  import * as os3 from "os";
3717
4264
 
3718
4265
  // src/modules/registry.ts
@@ -3747,12 +4294,12 @@ async function registerBuiltInModules() {
3747
4294
  }
3748
4295
 
3749
4296
  // src/infrastructure/introspection/IntrospectionIndex.ts
3750
- import * as path13 from "path";
3751
- import * as fs5 from "fs/promises";
4297
+ import * as path14 from "path";
4298
+ import * as fs6 from "fs/promises";
3752
4299
 
3753
4300
  // src/infrastructure/introspection/projectDetector.ts
3754
- import * as path12 from "path";
3755
- import * as fs4 from "fs/promises";
4301
+ import * as path13 from "path";
4302
+ import * as fs5 from "fs/promises";
3756
4303
  var MAX_SCAN_DEPTH = 4;
3757
4304
  var SKIP_DIRS = new Set([
3758
4305
  "node_modules",
@@ -3768,9 +4315,9 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3768
4315
  if (depth > MAX_SCAN_DEPTH)
3769
4316
  return [];
3770
4317
  const results = [];
3771
- const fullDir = currentDir ? path12.join(rootDir, currentDir) : rootDir;
4318
+ const fullDir = currentDir ? path13.join(rootDir, currentDir) : rootDir;
3772
4319
  try {
3773
- const entries = await fs4.readdir(fullDir, { withFileTypes: true });
4320
+ const entries = await fs5.readdir(fullDir, { withFileTypes: true });
3774
4321
  const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
3775
4322
  if (hasPackageJson && currentDir) {
3776
4323
  const info = await parsePackageJson(rootDir, currentDir);
@@ -3791,10 +4338,10 @@ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
3791
4338
  }
3792
4339
  async function parsePackageJson(rootDir, relativePath) {
3793
4340
  try {
3794
- const packageJsonPath = path12.join(rootDir, relativePath, "package.json");
3795
- const content = await fs4.readFile(packageJsonPath, "utf-8");
4341
+ const packageJsonPath = path13.join(rootDir, relativePath, "package.json");
4342
+ const content = await fs5.readFile(packageJsonPath, "utf-8");
3796
4343
  const pkg = JSON.parse(content);
3797
- const name = pkg.name || path12.basename(relativePath);
4344
+ const name = pkg.name || path13.basename(relativePath);
3798
4345
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3799
4346
  let type = "unknown";
3800
4347
  if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
@@ -3830,7 +4377,7 @@ async function detectProjectStructure(rootDir) {
3830
4377
  const projectMap = new Map;
3831
4378
  let isMonorepo = false;
3832
4379
  try {
3833
- const entries = await fs4.readdir(rootDir, { withFileTypes: true });
4380
+ const entries = await fs5.readdir(rootDir, { withFileTypes: true });
3834
4381
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
3835
4382
  const monorepoPatterns = ["apps", "packages", "libs", "services"];
3836
4383
  const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
@@ -3839,9 +4386,9 @@ async function detectProjectStructure(rootDir) {
3839
4386
  for (const pattern of monorepoPatterns) {
3840
4387
  if (!dirNames.includes(pattern))
3841
4388
  continue;
3842
- const patternDir = path12.join(rootDir, pattern);
4389
+ const patternDir = path13.join(rootDir, pattern);
3843
4390
  try {
3844
- const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
4391
+ const subDirs = await fs5.readdir(patternDir, { withFileTypes: true });
3845
4392
  for (const subDir of subDirs) {
3846
4393
  if (!subDir.isDirectory())
3847
4394
  continue;
@@ -3870,8 +4417,8 @@ async function detectProjectStructure(rootDir) {
3870
4417
  }
3871
4418
  let rootType = "unknown";
3872
4419
  try {
3873
- const rootPkgPath = path12.join(rootDir, "package.json");
3874
- const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
4420
+ const rootPkgPath = path13.join(rootDir, "package.json");
4421
+ const rootPkg = JSON.parse(await fs5.readFile(rootPkgPath, "utf-8"));
3875
4422
  if (rootPkg.workspaces)
3876
4423
  isMonorepo = true;
3877
4424
  const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
@@ -3911,8 +4458,8 @@ class IntrospectionIndex {
3911
4458
  async initialize() {
3912
4459
  this.structure = await detectProjectStructure(this.rootDir);
3913
4460
  try {
3914
- const configPath = path13.join(this.rootDir, ".raggrep", "config.json");
3915
- const configContent = await fs5.readFile(configPath, "utf-8");
4461
+ const configPath = path14.join(this.rootDir, ".raggrep", "config.json");
4462
+ const configContent = await fs6.readFile(configPath, "utf-8");
3916
4463
  const config = JSON.parse(configContent);
3917
4464
  this.config = config.introspection || {};
3918
4465
  } catch {}
@@ -3951,28 +4498,28 @@ class IntrospectionIndex {
3951
4498
  }
3952
4499
  }
3953
4500
  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({
4501
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
4502
+ await fs6.mkdir(introDir, { recursive: true });
4503
+ const projectPath = path14.join(introDir, "_project.json");
4504
+ await fs6.writeFile(projectPath, JSON.stringify({
3958
4505
  version: "1.0.0",
3959
4506
  lastUpdated: new Date().toISOString(),
3960
4507
  structure: this.structure
3961
4508
  }, null, 2));
3962
4509
  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));
4510
+ const introFilePath = path14.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
4511
+ await fs6.mkdir(path14.dirname(introFilePath), { recursive: true });
4512
+ await fs6.writeFile(introFilePath, JSON.stringify(intro, null, 2));
3966
4513
  }
3967
4514
  }
3968
4515
  async load(config) {
3969
- const introDir = path13.join(getRaggrepDir(this.rootDir, config), "introspection");
4516
+ const introDir = path14.join(getRaggrepDir(this.rootDir, config), "introspection");
3970
4517
  try {
3971
- const projectPath = path13.join(introDir, "_project.json");
3972
- const projectContent = await fs5.readFile(projectPath, "utf-8");
4518
+ const projectPath = path14.join(introDir, "_project.json");
4519
+ const projectContent = await fs6.readFile(projectPath, "utf-8");
3973
4520
  const projectData = JSON.parse(projectContent);
3974
4521
  this.structure = projectData.structure;
3975
- await this.loadFilesRecursive(path13.join(introDir, "files"), "");
4522
+ await this.loadFilesRecursive(path14.join(introDir, "files"), "");
3976
4523
  } catch {
3977
4524
  this.structure = null;
3978
4525
  this.files.clear();
@@ -3980,14 +4527,14 @@ class IntrospectionIndex {
3980
4527
  }
3981
4528
  async loadFilesRecursive(basePath, prefix) {
3982
4529
  try {
3983
- const entries = await fs5.readdir(basePath, { withFileTypes: true });
4530
+ const entries = await fs6.readdir(basePath, { withFileTypes: true });
3984
4531
  for (const entry of entries) {
3985
- const entryPath = path13.join(basePath, entry.name);
4532
+ const entryPath = path14.join(basePath, entry.name);
3986
4533
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
3987
4534
  if (entry.isDirectory()) {
3988
4535
  await this.loadFilesRecursive(entryPath, relativePath);
3989
4536
  } else if (entry.name.endsWith(".json")) {
3990
- const content = await fs5.readFile(entryPath, "utf-8");
4537
+ const content = await fs6.readFile(entryPath, "utf-8");
3991
4538
  const intro = JSON.parse(content);
3992
4539
  this.files.set(intro.filepath, intro);
3993
4540
  }
@@ -4133,7 +4680,7 @@ async function indexDirectory(rootDir, options = {}) {
4133
4680
  const quiet = options.quiet ?? false;
4134
4681
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
4135
4682
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4136
- rootDir = path14.resolve(rootDir);
4683
+ rootDir = path15.resolve(rootDir);
4137
4684
  const location = getIndexLocation(rootDir);
4138
4685
  logger.info(`Indexing directory: ${rootDir}`);
4139
4686
  logger.info(`Index location: ${location.indexDir}`);
@@ -4185,12 +4732,12 @@ async function indexDirectory(rootDir, options = {}) {
4185
4732
  rootDir,
4186
4733
  config,
4187
4734
  readFile: async (filepath) => {
4188
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4189
- return fs6.readFile(fullPath, "utf-8");
4735
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4736
+ return fs7.readFile(fullPath, "utf-8");
4190
4737
  },
4191
4738
  getFileStats: async (filepath) => {
4192
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4193
- const stats = await fs6.stat(fullPath);
4739
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4740
+ const stats = await fs7.stat(fullPath);
4194
4741
  return { lastModified: stats.mtime.toISOString() };
4195
4742
  }
4196
4743
  };
@@ -4215,7 +4762,7 @@ async function isIndexVersionCompatible(rootDir) {
4215
4762
  const config = await loadConfig(rootDir);
4216
4763
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4217
4764
  try {
4218
- const content = await fs6.readFile(globalManifestPath, "utf-8");
4765
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4219
4766
  const manifest = JSON.parse(content);
4220
4767
  return manifest.version === INDEX_SCHEMA_VERSION;
4221
4768
  } catch {
@@ -4225,11 +4772,11 @@ async function isIndexVersionCompatible(rootDir) {
4225
4772
  async function deleteIndex(rootDir) {
4226
4773
  const indexDir = getRaggrepDir(rootDir);
4227
4774
  try {
4228
- await fs6.rm(indexDir, { recursive: true, force: true });
4775
+ await fs7.rm(indexDir, { recursive: true, force: true });
4229
4776
  } catch {}
4230
4777
  }
4231
4778
  async function resetIndex(rootDir) {
4232
- rootDir = path14.resolve(rootDir);
4779
+ rootDir = path15.resolve(rootDir);
4233
4780
  const status = await getIndexStatus(rootDir);
4234
4781
  if (!status.exists) {
4235
4782
  throw new Error(`No index found for ${rootDir}`);
@@ -4244,7 +4791,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4244
4791
  const verbose = options.verbose ?? false;
4245
4792
  const quiet = options.quiet ?? false;
4246
4793
  const logger = options.logger ? options.logger : quiet ? createSilentLogger() : createLogger({ verbose });
4247
- rootDir = path14.resolve(rootDir);
4794
+ rootDir = path15.resolve(rootDir);
4248
4795
  const status = await getIndexStatus(rootDir);
4249
4796
  if (!status.exists) {
4250
4797
  logger.info(`No index found. Creating index...
@@ -4271,7 +4818,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4271
4818
  const introspection = new IntrospectionIndex(rootDir);
4272
4819
  await introspection.initialize();
4273
4820
  const currentFiles = await findFiles(rootDir, config);
4274
- const currentFileSet = new Set(currentFiles.map((f) => path14.relative(rootDir, f)));
4821
+ const currentFileSet = new Set(currentFiles.map((f) => path15.relative(rootDir, f)));
4275
4822
  let totalIndexed = 0;
4276
4823
  let totalRemoved = 0;
4277
4824
  let totalUnchanged = 0;
@@ -4301,13 +4848,13 @@ async function ensureIndexFresh(rootDir, options = {}) {
4301
4848
  }
4302
4849
  for (const filepath of filesToRemove) {
4303
4850
  logger.debug(` Removing stale: ${filepath}`);
4304
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4851
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4305
4852
  try {
4306
- await fs6.unlink(indexFilePath);
4853
+ await fs7.unlink(indexFilePath);
4307
4854
  } catch {}
4308
- const symbolicFilePath = path14.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4855
+ const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4309
4856
  try {
4310
- await fs6.unlink(symbolicFilePath);
4857
+ await fs7.unlink(symbolicFilePath);
4311
4858
  } catch {}
4312
4859
  delete manifest.files[filepath];
4313
4860
  totalRemoved++;
@@ -4316,12 +4863,12 @@ async function ensureIndexFresh(rootDir, options = {}) {
4316
4863
  rootDir,
4317
4864
  config,
4318
4865
  readFile: async (filepath) => {
4319
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4320
- return fs6.readFile(fullPath, "utf-8");
4866
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4867
+ return fs7.readFile(fullPath, "utf-8");
4321
4868
  },
4322
4869
  getFileStats: async (filepath) => {
4323
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4324
- const stats = await fs6.stat(fullPath);
4870
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4871
+ const stats = await fs7.stat(fullPath);
4325
4872
  return { lastModified: stats.mtime.toISOString() };
4326
4873
  },
4327
4874
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4329,10 +4876,10 @@ async function ensureIndexFresh(rootDir, options = {}) {
4329
4876
  const totalFiles = currentFiles.length;
4330
4877
  for (let i = 0;i < currentFiles.length; i++) {
4331
4878
  const filepath = currentFiles[i];
4332
- const relativePath = path14.relative(rootDir, filepath);
4879
+ const relativePath = path15.relative(rootDir, filepath);
4333
4880
  const progress = `[${i + 1}/${totalFiles}]`;
4334
4881
  try {
4335
- const stats = await fs6.stat(filepath);
4882
+ const stats = await fs7.stat(filepath);
4336
4883
  const lastModified = stats.mtime.toISOString();
4337
4884
  const existingEntry = manifest.files[relativePath];
4338
4885
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4340,7 +4887,7 @@ async function ensureIndexFresh(rootDir, options = {}) {
4340
4887
  continue;
4341
4888
  }
4342
4889
  logger.progress(` ${progress} Indexing: ${relativePath}`);
4343
- const content = await fs6.readFile(filepath, "utf-8");
4890
+ const content = await fs7.readFile(filepath, "utf-8");
4344
4891
  introspection.addFile(relativePath, content);
4345
4892
  const fileIndex = await module.indexFile(relativePath, content, ctx);
4346
4893
  if (fileIndex) {
@@ -4389,7 +4936,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4389
4936
  };
4390
4937
  const manifest = await loadModuleManifest(rootDir, module.id, config);
4391
4938
  const indexPath = getModuleIndexPath(rootDir, module.id, config);
4392
- const currentFileSet = new Set(files.map((f) => path14.relative(rootDir, f)));
4939
+ const currentFileSet = new Set(files.map((f) => path15.relative(rootDir, f)));
4393
4940
  const filesToRemove = [];
4394
4941
  for (const filepath of Object.keys(manifest.files)) {
4395
4942
  if (!currentFileSet.has(filepath)) {
@@ -4400,13 +4947,13 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4400
4947
  logger.info(` Removing ${filesToRemove.length} stale entries...`);
4401
4948
  for (const filepath of filesToRemove) {
4402
4949
  logger.debug(` Removing: ${filepath}`);
4403
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4950
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4404
4951
  try {
4405
- await fs6.unlink(indexFilePath);
4952
+ await fs7.unlink(indexFilePath);
4406
4953
  } catch {}
4407
- const symbolicFilePath = path14.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4954
+ const symbolicFilePath = path15.join(indexPath, "symbolic", filepath.replace(/\.[^.]+$/, ".json"));
4408
4955
  try {
4409
- await fs6.unlink(symbolicFilePath);
4956
+ await fs7.unlink(symbolicFilePath);
4410
4957
  } catch {}
4411
4958
  delete manifest.files[filepath];
4412
4959
  }
@@ -4416,12 +4963,12 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4416
4963
  rootDir,
4417
4964
  config,
4418
4965
  readFile: async (filepath) => {
4419
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4420
- return fs6.readFile(fullPath, "utf-8");
4966
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4967
+ return fs7.readFile(fullPath, "utf-8");
4421
4968
  },
4422
4969
  getFileStats: async (filepath) => {
4423
- const fullPath = path14.isAbsolute(filepath) ? filepath : path14.join(rootDir, filepath);
4424
- const stats = await fs6.stat(fullPath);
4970
+ const fullPath = path15.isAbsolute(filepath) ? filepath : path15.join(rootDir, filepath);
4971
+ const stats = await fs7.stat(fullPath);
4425
4972
  return { lastModified: stats.mtime.toISOString() };
4426
4973
  },
4427
4974
  getIntrospection: (filepath) => introspection.getFile(filepath)
@@ -4429,9 +4976,9 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4429
4976
  const totalFiles = files.length;
4430
4977
  let completedCount = 0;
4431
4978
  const processFile = async (filepath, _index) => {
4432
- const relativePath = path14.relative(rootDir, filepath);
4979
+ const relativePath = path15.relative(rootDir, filepath);
4433
4980
  try {
4434
- const stats = await fs6.stat(filepath);
4981
+ const stats = await fs7.stat(filepath);
4435
4982
  const lastModified = stats.mtime.toISOString();
4436
4983
  const existingEntry = manifest.files[relativePath];
4437
4984
  if (existingEntry && existingEntry.lastModified === lastModified) {
@@ -4439,7 +4986,7 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
4439
4986
  logger.debug(` [${completedCount}/${totalFiles}] Skipped ${relativePath} (unchanged)`);
4440
4987
  return { relativePath, status: "skipped" };
4441
4988
  }
4442
- const content = await fs6.readFile(filepath, "utf-8");
4989
+ const content = await fs7.readFile(filepath, "utf-8");
4443
4990
  introspection.addFile(relativePath, content);
4444
4991
  completedCount++;
4445
4992
  logger.progress(` [${completedCount}/${totalFiles}] Processing: ${relativePath}`);
@@ -4507,7 +5054,7 @@ async function findFiles(rootDir, config) {
4507
5054
  async function loadModuleManifest(rootDir, moduleId, config) {
4508
5055
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
4509
5056
  try {
4510
- const content = await fs6.readFile(manifestPath, "utf-8");
5057
+ const content = await fs7.readFile(manifestPath, "utf-8");
4511
5058
  return JSON.parse(content);
4512
5059
  } catch {
4513
5060
  return {
@@ -4520,14 +5067,14 @@ async function loadModuleManifest(rootDir, moduleId, config) {
4520
5067
  }
4521
5068
  async function writeModuleManifest(rootDir, moduleId, manifest, config) {
4522
5069
  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));
5070
+ await fs7.mkdir(path15.dirname(manifestPath), { recursive: true });
5071
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4525
5072
  }
4526
5073
  async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
4527
5074
  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));
5075
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5076
+ await fs7.mkdir(path15.dirname(indexFilePath), { recursive: true });
5077
+ await fs7.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
4531
5078
  }
4532
5079
  async function updateGlobalManifest(rootDir, modules, config) {
4533
5080
  const manifestPath = getGlobalManifestPath(rootDir, config);
@@ -4536,13 +5083,13 @@ async function updateGlobalManifest(rootDir, modules, config) {
4536
5083
  lastUpdated: new Date().toISOString(),
4537
5084
  modules: modules.map((m) => m.id)
4538
5085
  };
4539
- await fs6.mkdir(path14.dirname(manifestPath), { recursive: true });
4540
- await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
5086
+ await fs7.mkdir(path15.dirname(manifestPath), { recursive: true });
5087
+ await fs7.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4541
5088
  }
4542
5089
  async function cleanupIndex(rootDir, options = {}) {
4543
5090
  const verbose = options.verbose ?? false;
4544
5091
  const logger = options.logger ?? createLogger({ verbose });
4545
- rootDir = path14.resolve(rootDir);
5092
+ rootDir = path15.resolve(rootDir);
4546
5093
  logger.info(`Cleaning up index in: ${rootDir}`);
4547
5094
  const config = await loadConfig(rootDir);
4548
5095
  await registerBuiltInModules();
@@ -4572,9 +5119,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4572
5119
  const filesToRemove = [];
4573
5120
  const updatedFiles = {};
4574
5121
  for (const [filepath, entry] of Object.entries(manifest.files)) {
4575
- const fullPath = path14.join(rootDir, filepath);
5122
+ const fullPath = path15.join(rootDir, filepath);
4576
5123
  try {
4577
- await fs6.access(fullPath);
5124
+ await fs7.access(fullPath);
4578
5125
  updatedFiles[filepath] = entry;
4579
5126
  result.kept++;
4580
5127
  } catch {
@@ -4584,9 +5131,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4584
5131
  }
4585
5132
  }
4586
5133
  for (const filepath of filesToRemove) {
4587
- const indexFilePath = path14.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
5134
+ const indexFilePath = path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
4588
5135
  try {
4589
- await fs6.unlink(indexFilePath);
5136
+ await fs7.unlink(indexFilePath);
4590
5137
  } catch {}
4591
5138
  }
4592
5139
  manifest.files = updatedFiles;
@@ -4597,16 +5144,16 @@ async function cleanupModuleIndex(rootDir, moduleId, config, logger) {
4597
5144
  }
4598
5145
  async function cleanupEmptyDirectories(dir) {
4599
5146
  try {
4600
- const entries = await fs6.readdir(dir, { withFileTypes: true });
5147
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
4601
5148
  for (const entry of entries) {
4602
5149
  if (entry.isDirectory()) {
4603
- const subDir = path14.join(dir, entry.name);
5150
+ const subDir = path15.join(dir, entry.name);
4604
5151
  await cleanupEmptyDirectories(subDir);
4605
5152
  }
4606
5153
  }
4607
- const remainingEntries = await fs6.readdir(dir);
5154
+ const remainingEntries = await fs7.readdir(dir);
4608
5155
  if (remainingEntries.length === 0) {
4609
- await fs6.rmdir(dir);
5156
+ await fs7.rmdir(dir);
4610
5157
  return true;
4611
5158
  }
4612
5159
  return false;
@@ -4615,7 +5162,7 @@ async function cleanupEmptyDirectories(dir) {
4615
5162
  }
4616
5163
  }
4617
5164
  async function getIndexStatus(rootDir) {
4618
- rootDir = path14.resolve(rootDir);
5165
+ rootDir = path15.resolve(rootDir);
4619
5166
  const config = await loadConfig(rootDir);
4620
5167
  const location = getIndexLocation(rootDir);
4621
5168
  const indexDir = location.indexDir;
@@ -4627,13 +5174,13 @@ async function getIndexStatus(rootDir) {
4627
5174
  totalFiles: 0
4628
5175
  };
4629
5176
  try {
4630
- await fs6.access(indexDir);
5177
+ await fs7.access(indexDir);
4631
5178
  } catch {
4632
5179
  return status;
4633
5180
  }
4634
5181
  try {
4635
5182
  const globalManifestPath = getGlobalManifestPath(rootDir, config);
4636
- const content = await fs6.readFile(globalManifestPath, "utf-8");
5183
+ const content = await fs7.readFile(globalManifestPath, "utf-8");
4637
5184
  const globalManifest = JSON.parse(content);
4638
5185
  status.exists = true;
4639
5186
  status.lastUpdated = globalManifest.lastUpdated;
@@ -4651,7 +5198,7 @@ async function getIndexStatus(rootDir) {
4651
5198
  }
4652
5199
  } catch {
4653
5200
  try {
4654
- const entries = await fs6.readdir(path14.join(indexDir, "index"));
5201
+ const entries = await fs7.readdir(path15.join(indexDir, "index"));
4655
5202
  if (entries.length > 0) {
4656
5203
  status.exists = true;
4657
5204
  for (const entry of entries) {
@@ -4673,8 +5220,8 @@ async function getIndexStatus(rootDir) {
4673
5220
  }
4674
5221
 
4675
5222
  // src/app/search/index.ts
4676
- import * as fs7 from "fs/promises";
4677
- import * as path15 from "path";
5223
+ import * as fs8 from "fs/promises";
5224
+ import * as path16 from "path";
4678
5225
 
4679
5226
  // src/types.ts
4680
5227
  init_entities();
@@ -4682,7 +5229,7 @@ init_entities();
4682
5229
  // src/app/search/index.ts
4683
5230
  init_config2();
4684
5231
  async function search(rootDir, query, options = {}) {
4685
- rootDir = path15.resolve(rootDir);
5232
+ rootDir = path16.resolve(rootDir);
4686
5233
  const ensureFresh = options.ensureFresh ?? DEFAULT_SEARCH_OPTIONS.ensureFresh;
4687
5234
  if (ensureFresh) {
4688
5235
  await ensureIndexFresh(rootDir, { quiet: true });
@@ -4735,9 +5282,9 @@ function createSearchContext(rootDir, moduleId, config) {
4735
5282
  config,
4736
5283
  loadFileIndex: async (filepath) => {
4737
5284
  const hasExtension = /\.[^./]+$/.test(filepath);
4738
- const indexFilePath = hasExtension ? path15.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path15.join(indexPath, filepath + ".json");
5285
+ const indexFilePath = hasExtension ? path16.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path16.join(indexPath, filepath + ".json");
4739
5286
  try {
4740
- const content = await fs7.readFile(indexFilePath, "utf-8");
5287
+ const content = await fs8.readFile(indexFilePath, "utf-8");
4741
5288
  return JSON.parse(content);
4742
5289
  } catch {
4743
5290
  return null;
@@ -4747,7 +5294,7 @@ function createSearchContext(rootDir, moduleId, config) {
4747
5294
  const files = [];
4748
5295
  await traverseDirectory(indexPath, files, indexPath);
4749
5296
  return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
4750
- const relative3 = path15.relative(indexPath, f);
5297
+ const relative3 = path16.relative(indexPath, f);
4751
5298
  return relative3.replace(/\.json$/, "");
4752
5299
  });
4753
5300
  }
@@ -4755,9 +5302,9 @@ function createSearchContext(rootDir, moduleId, config) {
4755
5302
  }
4756
5303
  async function traverseDirectory(dir, files, basePath) {
4757
5304
  try {
4758
- const entries = await fs7.readdir(dir, { withFileTypes: true });
5305
+ const entries = await fs8.readdir(dir, { withFileTypes: true });
4759
5306
  for (const entry of entries) {
4760
- const fullPath = path15.join(dir, entry.name);
5307
+ const fullPath = path16.join(dir, entry.name);
4761
5308
  if (entry.isDirectory()) {
4762
5309
  await traverseDirectory(fullPath, files, basePath);
4763
5310
  } else if (entry.isFile()) {
@@ -4769,7 +5316,7 @@ async function traverseDirectory(dir, files, basePath) {
4769
5316
  async function loadGlobalManifest(rootDir, config) {
4770
5317
  const manifestPath = getGlobalManifestPath(rootDir, config);
4771
5318
  try {
4772
- const content = await fs7.readFile(manifestPath, "utf-8");
5319
+ const content = await fs8.readFile(manifestPath, "utf-8");
4773
5320
  return JSON.parse(content);
4774
5321
  } catch {
4775
5322
  return null;
@@ -4859,4 +5406,4 @@ export {
4859
5406
  ConsoleLogger
4860
5407
  };
4861
5408
 
4862
- //# debugId=4915A936C06DA9B864756E2164756E21
5409
+ //# debugId=8F3468BC25F1B30164756E2164756E21