Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/codegraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,14 @@ export class CodeGraph {
return this.traverser.getCallers(nodeId, maxDepth);
}

/**
* Depth-1 callers for many targets in two queries — the batch form of
* getCallers(id, 1), for bounded fan-in walks like callerTests.
*/
getCallersMany(nodeIds: readonly string[]): Array<{ node: Node; edge: Edge }> {
return this.traverser.getCallersMany(nodeIds);
}

/**
* Get callees of a function/method
*
Expand Down
23 changes: 23 additions & 0 deletions src/graph/traversal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,29 @@ export class GraphTraverser {
return result;
}

/**
* Depth-1 callers for MANY targets in two round-trips — one incoming-edges
* query + one getNodesByIds — instead of a getCallers(id, 1) pair per id.
* Same kind list as getCallersRecursive; a caller repeats once per target it
* calls, matching separate per-node calls, and once per target at most.
*/
getCallersMany(nodeIds: readonly string[]): Array<{ node: Node; edge: Edge }> {
if (nodeIds.length === 0) return [];
const incoming = this.queries.getIncomingEdgesTo(nodeIds, ['calls', 'references', 'imports', 'instantiates', 'navigates']);
const nodes = this.queries.getNodesByIds(incoming.map((e) => e.source));
const reported = new Set<string>();
const out: Array<{ node: Node; edge: Edge }> = [];
for (const edge of incoming) {
const node = nodes.get(edge.source);
if (!node) continue;
const key = `${edge.target}${edge.source}`;
if (reported.has(key)) continue;
reported.add(key);
out.push({ node, edge });
}
return out;
}

private getCallersRecursive(
nodeId: string,
maxDepth: number,
Expand Down
23 changes: 12 additions & 11 deletions src/mcp/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4559,17 +4559,18 @@ export class ToolHandler {
for (let hop = 2; hop <= MAX_HOPS && frontier.length > 0 && budget > 0; hop++) {
const next: Array<{ node: Node; edge: Edge }> = [];
const found: Array<{ node: Node; edge: Edge }> = [];
for (const { node } of frontier) {
if (budget-- <= 0) break;
let callers: Array<{ node: Node; edge: Edge }> = [];
try { callers = cg.getCallers(node.id); } catch { continue; }
for (const c of callers) {
const n = c?.node;
if (!n || visited.has(n.id)) continue;
visited.add(n.id);
if (isTestPath(n.filePath)) found.push(c);
else next.push(c);
}
// One batched depth-1 callers query per hop instead of a getCallers
// round-trip per frontier node; the same 64-node budget bounds it.
const ids = frontier.slice(0, budget).map((f) => f.node.id);
budget -= ids.length;
let pairs: Array<{ node: Node; edge: Edge }> = [];
try { pairs = cg.getCallersMany(ids); } catch { pairs = []; }
for (const c of pairs) {
const n = c?.node;
if (!n || visited.has(n.id)) continue;
visited.add(n.id);
if (isTestPath(n.filePath)) found.push(c);
else next.push(c);
}
if (found.length > 0) return { callers: found, exhausted: budget <= 0 };
frontier = next;
Expand Down
Loading