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

Method handleImpact

src/mcp/tools.ts:3243–3304  ·  view source on GitHub ↗

* Handle codegraph_impact

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

3241 * Handle codegraph_impact
3242 */
3243 private async handleImpact(args: Record<string, unknown>): Promise<ToolResult> {
3244 const symbol = this.validateString(args.symbol, 'symbol');
3245 if (typeof symbol !== 'string') return symbol;
3246
3247 const cg = this.getCodeGraph(args.projectPath as string | undefined);
3248 const depth = clamp((args.depth as number) || 2, 1, 10);
3249 const fileFilter = typeof args.file === 'string' ? args.file : undefined;
3250
3251 const allMatches = this.findAllSymbols(cg, symbol);
3252 if (allMatches.nodes.length === 0) {
3253 return this.textResult(`Symbol "${symbol}" not found in the codebase${allMatches.note}`);
3254 }
3255
3256 const { groups, filteredOut } = this.groupDefinitions(allMatches.nodes, fileFilter);
3257 const answerPaths = new Set(groups.flat().map(node => node.filePath));
3258 const filterNote = filteredOut
3259 ? `\n\n> **Note:** no definition of "${symbol}" matches file "${fileFilter}" — showing all definitions instead.`
3260 : '';
3261
3262 const impactOf = (defNodes: Node[]) => {
3263 const mergedNodes = new Map<string, Node>();
3264 const mergedEdges: Edge[] = [];
3265 const seenEdges = new Set<string>();
3266 for (const node of defNodes) {
3267 const impact = cg.getImpactRadius(node.id, depth);
3268 for (const [id, n] of impact.nodes) {
3269 mergedNodes.set(id, n);
3270 answerPaths.add(n.filePath);
3271 }
3272 for (const e of impact.edges) {
3273 const key = `${e.source}->${e.target}:${e.kind}`;
3274 if (!seenEdges.has(key)) {
3275 seenEdges.add(key);
3276 mergedEdges.push(e);
3277 }
3278 }
3279 }
3280 return { nodes: mergedNodes, edges: mergedEdges, roots: defNodes.map((n) => n.id) };
3281 };
3282
3283 // Single definition (or same-file overloads): the familiar merged report.
3284 if (groups.length === 1) {
3285 const formatted = this.formatImpact(symbol, impactOf(groups[0]!)) + (fileFilter && !filteredOut ? "" : allMatches.note) + filterNote;
3286 return this.answerResult(cg, this.truncateOutput(formatted), answerPaths);
3287 }
3288
3289 // Multiple DISTINCT definitions (#764): a blast radius PER definition —
3290 // merging unrelated same-named classes (one UserService per monorepo app)
3291 // overstated impact and confused agents. Narrow with `file`.
3292 const sections: string[] = [
3293 `**Impact of ${symbol} — ${groups.length} distinct definitions (each with its own blast radius; narrow with \`file\`)**`,
3294 ];
3295 for (const group of groups) {
3296 const head = group[0]!;
3297 const line = head.startLine ? `:${head.startLine}` : '';
3298 sections.push(
3299 '',
3300 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