Kaynağa Gözat

Port extraction quality improvements from PR #15

- Fix arrow function extraction: explicitly call extractFunction() for
  arrow functions/function expressions in variable declarations instead
  of silently skipping them (all 6 arrow function tests now pass)
- Best-candidate resolution: collect candidates from all strategies and
  return highest confidence match instead of first match
- Fix graph traversal 'both' direction: correctly determine next node
  for mixed incoming/outgoing edges in BFS and DFS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Colby McHenry 4 ay önce
ebeveyn
işleme
94c8d5ccaa

+ 3 - 2
src/extraction/tree-sitter.ts

@@ -1294,9 +1294,10 @@ export class TreeSitterExtractor {
 
           if (nameNode) {
             const name = getNodeText(nameNode, this.source);
-            // Skip if it looks like a function (arrow function or function expression)
+            // Arrow functions / function expressions: extract as function instead of variable
             if (valueNode && (valueNode.type === 'arrow_function' || valueNode.type === 'function_expression')) {
-              continue; // Already handled by function extraction
+              this.extractFunction(valueNode);
+              continue;
             }
 
             // Capture first 100 chars of initializer for context (stored in signature for searchability)

+ 6 - 2
src/graph/traversal.ts

@@ -85,7 +85,9 @@ export class GraphTraverser {
       const adjacentEdges = this.getAdjacentEdges(node.id, opts.direction, opts.edgeKinds);
 
       for (const adjEdge of adjacentEdges) {
-        const nextNodeId = opts.direction === 'incoming' ? adjEdge.source : adjEdge.target;
+        // Determine next node: for 'both' direction, edges can be either
+        // incoming or outgoing, so pick whichever end is not the current node
+        const nextNodeId = adjEdge.source === node.id ? adjEdge.target : adjEdge.source;
 
         if (visited.has(nextNodeId)) {
           continue;
@@ -169,7 +171,9 @@ export class GraphTraverser {
     const adjacentEdges = this.getAdjacentEdges(node.id, opts.direction, opts.edgeKinds);
 
     for (const edge of adjacentEdges) {
-      const nextNodeId = opts.direction === 'incoming' ? edge.source : edge.target;
+      // Determine next node: for 'both' direction, edges can be either
+      // incoming or outgoing, so pick whichever end is not the current node
+      const nextNodeId = edge.source === node.id ? edge.target : edge.source;
 
       if (visited.has(nextNodeId)) {
         continue;

+ 14 - 5
src/resolution/index.ts

@@ -246,27 +246,36 @@ export class ReferenceResolver {
       return null;
     }
 
-    // Strategy 1: Try framework-specific resolution first
+    const candidates: ResolvedRef[] = [];
+
+    // Strategy 1: Try framework-specific resolution
     for (const framework of this.frameworks) {
       const result = framework.resolve(ref, this.context);
       if (result) {
-        return result;
+        if (result.confidence >= 0.9) return result; // High confidence, return immediately
+        candidates.push(result);
       }
     }
 
     // Strategy 2: Try import-based resolution
     const importResult = resolveViaImport(ref, this.context);
     if (importResult) {
-      return importResult;
+      if (importResult.confidence >= 0.9) return importResult;
+      candidates.push(importResult);
     }
 
     // Strategy 3: Try name matching
     const nameResult = matchReference(ref, this.context);
     if (nameResult) {
-      return nameResult;
+      candidates.push(nameResult);
     }
 
-    return null;
+    if (candidates.length === 0) return null;
+
+    // Return highest confidence candidate
+    return candidates.reduce((best, curr) =>
+      curr.confidence > best.confidence ? curr : best
+    );
   }
 
   /**