MCPcopy Create free account
hub / github.com/ScriptedAlchemy/tracedecay / find_entry_points

Method find_entry_points

src/context/builder.rs:178–294  ·  view source on GitHub ↗

Searches for entry-point nodes matching the query and extracted symbols. Pipeline: 1. FTS search on the full query, each extracted symbol, stem variants, and agent-provided extra keywords. 2. Exact name supplement — ensures perfect name matches are never buried by BM25 noise. 3. Re-rank with structural signals (kind, visibility, path). 4. Connectivity boost (incoming call counts). 5. Co-occurrenc

(
        &self,
        query: &str,
        symbols: &[String],
        options: &BuildContextOptions,
    )

Source from the content-addressed store, hash-verified

176 /// 6. Per-file diversity cap — limits how many symbols from a single file
177 /// appear so one large file doesn't dominate the output.
178 async fn find_entry_points(
179 &self,
180 query: &str,
181 symbols: &[String],
182 options: &BuildContextOptions,
183 ) -> Result<Vec<Node>> {
184 debug_assert!(
185 !query.is_empty(),
186 "find_entry_points called with empty query"
187 );
188 debug_assert!(options.search_limit > 0, "search_limit must be positive");
189 let mut seen_ids: HashSet<String> = options.exclude_node_ids.clone();
190 let mut candidates: Vec<SearchResult> = Vec::new();
191 let cap = options.max_nodes * 2;
192
193 // Build a deduplicated, ordered list of FTS terms. Earlier revisions
194 // searched the full query, every extracted symbol, every stem, and
195 // every extra keyword separately — but these sets overlap heavily
196 // (e.g. `symbol` "foo" and `keyword` "foo" produce identical FTS
197 // results). libsql serializes queries on a single connection, so each
198 // duplicate term costs a full roundtrip. Order matters for the
199 // `cap`-based early exit, so we keep the original priority:
200 // full query → symbols → stems → extra keywords.
201 let mut fts_terms: Vec<String> = Vec::new();
202 let mut fts_seen: HashSet<String> = HashSet::new();
203 let push_term = |t: String, terms: &mut Vec<String>, seen: &mut HashSet<String>| {
204 if !t.is_empty() && seen.insert(t.clone()) {
205 terms.push(t);
206 }
207 };
208 push_term(query.to_string(), &mut fts_terms, &mut fts_seen);
209 for s in symbols {
210 push_term(s.clone(), &mut fts_terms, &mut fts_seen);
211 }
212 let stems = generate_stem_variants(symbols);
213 for s in &stems {
214 push_term(s.clone(), &mut fts_terms, &mut fts_seen);
215 }
216 for k in &options.extra_keywords {
217 push_term(k.clone(), &mut fts_terms, &mut fts_seen);
218 }
219
220 for term in &fts_terms {
221 if candidates.len() >= cap {
222 break;
223 }
224 let results = self.db.search_nodes(term, options.search_limit).await?;
225 for sr in results {
226 if Self::score_passes(sr.score, options.min_score)
227 && seen_ids.insert(sr.node.id.clone())
228 {
229 candidates.push(sr);
230 }
231 }
232 }
233
234 // --- Exact name supplement ---
235 // Ensures perfect name matches aren't buried by BM25 noise.

Callers 2

build_contextMethod · 0.80
find_relevant_contextMethod · 0.80

Calls 13

generate_stem_variantsFunction · 0.85
rerank_candidatesFunction · 0.85
apply_connectivity_boostFunction · 0.85
apply_cooccurrence_boostFunction · 0.85
apply_per_file_capFunction · 0.85
insertMethod · 0.80
pushMethod · 0.80
search_nodesMethod · 0.80
collectMethod · 0.80
is_emptyMethod · 0.45

Tested by

no test coverage detected