MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / handleImpact

Method handleImpact

src/mcp/tools.ts:3128–3189  ·  view source on GitHub ↗

* Handle codegraph_impact

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

3126 * Handle codegraph_impact
3127 */
3128 private async handleImpact(args: Record<string, unknown>): Promise<ToolResult> {
3129 const symbol = this.validateString(args.symbol, 'symbol');
3130 if (typeof symbol !== 'string') return symbol;
3131
3132 const cg = this.getCodeGraph(args.projectPath as string | undefined);
3133 const depth = clamp((args.depth as number) || 2, 1, 10);
3134 const fileFilter = typeof args.file === 'string' ? args.file : undefined;
3135
3136 const allMatches = this.findAllSymbols(cg, symbol);
3137 if (allMatches.nodes.length === 0) {
3138 return this.textResult(`Symbol "${symbol}" not found in the codebase${allMatches.note}`);
3139 }
3140
3141 const { groups, filteredOut } = this.groupDefinitions(allMatches.nodes, fileFilter);
3142 const answerPaths = new Set(groups.flat().map(node => node.filePath));
3143 const filterNote = filteredOut
3144 ? `\n\n> **Note:** no definition of "${symbol}" matches file "${fileFilter}" — showing all definitions instead.`
3145 : '';
3146
3147 const impactOf = (defNodes: Node[]) => {
3148 const mergedNodes = new Map<string, Node>();
3149 const mergedEdges: Edge[] = [];
3150 const seenEdges = new Set<string>();
3151 for (const node of defNodes) {
3152 const impact = cg.getImpactRadius(node.id, depth);
3153 for (const [id, n] of impact.nodes) {
3154 mergedNodes.set(id, n);
3155 answerPaths.add(n.filePath);
3156 }
3157 for (const e of impact.edges) {
3158 const key = `${e.source}->${e.target}:${e.kind}`;
3159 if (!seenEdges.has(key)) {
3160 seenEdges.add(key);
3161 mergedEdges.push(e);
3162 }
3163 }
3164 }
3165 return { nodes: mergedNodes, edges: mergedEdges, roots: defNodes.map((n) => n.id) };
3166 };
3167
3168 // Single definition (or same-file overloads): the familiar merged report.
3169 if (groups.length === 1) {
3170 const formatted = this.formatImpact(symbol, impactOf(groups[0]!)) + (fileFilter && !filteredOut ? "" : allMatches.note) + filterNote;
3171 return this.answerResult(cg, this.truncateOutput(formatted), answerPaths);
3172 }
3173
3174 // Multiple DISTINCT definitions (#764): a blast radius PER definition —
3175 // merging unrelated same-named classes (one UserService per monorepo app)
3176 // overstated impact and confused agents. Narrow with `file`.
3177 const sections: string[] = [
3178 `**Impact of ${symbol} — ${groups.length} distinct definitions (each with its own blast radius; narrow with \`file\`)**`,
3179 ];
3180 for (const group of groups) {
3181 const head = group[0]!;
3182 const line = head.startLine ? `:${head.startLine}` : '';
3183 sections.push(
3184 '',
3185 this.formatImpact(`${head.qualifiedName} (${head.filePath}${line})`, impactOf(group))

Callers 1

dispatchToolMethod · 0.95

Calls 12

validateStringMethod · 0.95
getCodeGraphMethod · 0.95
findAllSymbolsMethod · 0.95
textResultMethod · 0.95
groupDefinitionsMethod · 0.95
formatImpactMethod · 0.95
answerResultMethod · 0.95
truncateOutputMethod · 0.95
clampFunction · 0.90
mapMethod · 0.80
pushMethod · 0.80
joinMethod · 0.45

Tested by

no test coverage detected