@comfanion/usethis_search 3.0.0-dev.22 → 3.0.0-dev.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tools/search.ts +35 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/usethis_search",
3
- "version": "3.0.0-dev.22",
3
+ "version": "3.0.0-dev.23",
4
4
  "description": "OpenCode plugin: semantic search with graph-based context (v3: graph relations, 1-hop context, LSP + regex analyzers)",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
package/tools/search.ts CHANGED
@@ -164,24 +164,34 @@ Examples:
164
164
  }
165
165
 
166
166
  // ── Reranking — boost results where query keywords appear in text ──────
167
+ // Also store score components for breakdown display
167
168
  const queryKeywords = args.query.toLowerCase().split(/\s+/).filter((w: string) => w.length > 2)
168
- if (queryKeywords.length > 0) {
169
- for (const r of allResults) {
170
- const text = (r.content || "").toLowerCase()
171
- let keywordHits = 0
169
+ for (const r of allResults) {
170
+ // Vector score (L2 similarity)
171
+ const vectorScore = r._distance != null ? Math.max(0, 1 - r._distance / 2) : 0
172
+ r._vectorScore = vectorScore
173
+
174
+ // BM25 component (present only in hybrid mode — embedded in _combinedScore)
175
+ // If _combinedScore exists and differs from vectorScore, the difference is BM25 contribution
176
+ r._bm25Component = r._combinedScore != null ? Math.max(0, r._combinedScore - vectorScore) : 0
177
+
178
+ // Base score before keyword boost
179
+ const baseScore = r._combinedScore ?? vectorScore
180
+
181
+ // Keyword matching
182
+ const text = (r.content || "").toLowerCase()
183
+ const matchedKeywords: string[] = []
184
+ if (queryKeywords.length > 0) {
172
185
  for (const kw of queryKeywords) {
173
- if (text.includes(kw)) keywordHits++
186
+ if (text.includes(kw)) matchedKeywords.push(kw)
174
187
  }
175
- const keywordBonus = queryKeywords.length > 0 ? (keywordHits / queryKeywords.length) * 0.15 : 0
176
- const baseScore = r._combinedScore ?? (r._distance != null ? Math.max(0, 1 - r._distance / 2) : 0)
177
- r._finalScore = baseScore + keywordBonus
178
- }
179
- allResults.sort((a: any, b: any) => (b._finalScore ?? 0) - (a._finalScore ?? 0))
180
- } else {
181
- for (const r of allResults) {
182
- r._finalScore = r._combinedScore ?? (r._distance != null ? Math.max(0, 1 - r._distance / 2) : 0)
183
188
  }
189
+ r._matchedKeywords = matchedKeywords
190
+ const keywordBonus = queryKeywords.length > 0 ? (matchedKeywords.length / queryKeywords.length) * 0.15 : 0
191
+ r._keywordBonus = keywordBonus
192
+ r._finalScore = baseScore + keywordBonus
184
193
  }
194
+ allResults.sort((a: any, b: any) => (b._finalScore ?? 0) - (a._finalScore ?? 0))
185
195
 
186
196
  // ── Group by file — best chunk per file, with chunk count ─────────────
187
197
  const fileGroups = new Map<string, { best: any, chunks: any[] }>()
@@ -233,8 +243,19 @@ Examples:
233
243
  if (r.class_name) metaParts.push(`class: ${r.class_name}`)
234
244
  const metaLine = metaParts.length > 0 ? ` (${metaParts.join(", ")})` : ""
235
245
 
246
+ // Score breakdown: vector + bm25 + keywords
247
+ const breakdownParts: string[] = [`vec: ${(r._vectorScore ?? 0).toFixed(2)}`]
248
+ if (r._bm25Component > 0.005) breakdownParts.push(`bm25: +${r._bm25Component.toFixed(2)}`)
249
+ if (r._keywordBonus > 0.005) breakdownParts.push(`kw: +${r._keywordBonus.toFixed(2)}`)
250
+ const breakdown = breakdownParts.join(", ")
251
+
252
+ // Matched keywords
253
+ const kwDisplay = r._matchedKeywords && r._matchedKeywords.length > 0
254
+ ? ` | matched: "${r._matchedKeywords.join('", "')}"`
255
+ : ""
256
+
236
257
  output += `### ${i + 1}. ${r.file}${indexLabel}${chunkNote}\n`
237
- output += `**Relevance:** ${score}${metaLine}\n\n`
258
+ output += `**Score:** ${score} (${breakdown}${kwDisplay})${metaLine}\n\n`
238
259
  output += "```\n"
239
260
  const content = r.content.length > 500 ? r.content.substring(0, 500) + "\n... (truncated)" : r.content
240
261
  output += content