@@ -123,6 +123,13 @@ def _extract_other_ids(results: list[dict[str, Any]]) -> list[str]:
123123TPL_DESCRIBE_ROUTE_DECLARING = "declaring method: neighbors(['{id}'],'in',['EXPOSES'])"
124124TPL_DESCRIBE_CLIENT_DECLARING = "declaring method: neighbors(['{id}'],'in',['DECLARES_CLIENT'])"
125125TPL_DESCRIBE_PRODUCER_DECLARING = "declaring method: neighbors(['{id}'],'in',['DECLARES_PRODUCER'])"
126+ TPL_DESCRIBE_TYPE_IMPLEMENTORS = "implementors: neighbors(['{id}'],'in',['IMPLEMENTS'])"
127+ TPL_DESCRIBE_TYPE_IMPLEMENTS = "implements: neighbors(['{id}'],'out',['IMPLEMENTS'])"
128+ TPL_DESCRIBE_TYPE_DEPENDENCIES = "dependencies: neighbors(['{id}'],'out',['INJECTS'])"
129+ TPL_DESCRIBE_TYPE_INJECTORS = "injectors: neighbors(['{id}'],'in',['INJECTS'])"
130+ TPL_DESCRIBE_METHOD_OUTBOUND_CALLS = "outbound calls: neighbors(['{id}'],'out',['CALLS'])"
131+ TPL_DESCRIBE_METHOD_SUPER_DECL = "super declaration: neighbors(['{id}'],'out',['OVERRIDES'])"
132+ TPL_DESCRIBE_METHOD_UNRESOLVED = "unresolved: neighbors(['{id}'],'out',['CALLS'],include_unresolved=True)"
126133
127134TPL_FIND_EMPTY_RESOLVE = "no matches — try resolve(identifier, hint_kind='{kind}') for canonical lookup"
128135TPL_FIND_PAGE_FULL = "result page full at {limit} — narrow filter or paginate"
@@ -254,6 +261,27 @@ def _out_count(edge_summary: dict[str, Any] | None, key: str) -> int:
254261 return int (cell .get ("out" , 0 ) or 0 )
255262
256263
264+ def _in_count (edge_summary : dict [str , Any ] | None , key : str ) -> int :
265+ if not edge_summary or key not in edge_summary :
266+ return 0
267+ cell = edge_summary [key ]
268+ if not isinstance (cell , dict ):
269+ return 0
270+ return int (cell .get ("in" , 0 ) or 0 )
271+
272+
273+ def _record_role (rec : dict [str , Any ]) -> str :
274+ return str ((rec .get ("data" ) or {}).get ("role" ) or rec .get ("role" ) or "" )
275+
276+
277+ def _type_rollup_would_emit (edge_summary : dict [str , Any ] | None ) -> bool :
278+ return (
279+ _out_count (edge_summary , "DECLARES.DECLARES_CLIENT" ) > 0
280+ or _out_count (edge_summary , "DECLARES.EXPOSES" ) > 0
281+ or _out_count (edge_summary , "DECLARES.DECLARES_PRODUCER" ) > 0
282+ )
283+
284+
257285def _symbol_declaration_kind (record : dict [str , Any ]) -> str | None :
258286 data = record .get ("data" )
259287 if isinstance (data , dict ):
@@ -1037,13 +1065,25 @@ def generate_hints(
10371065 "neighbors" , {"ids" : [node_id ], "direction" : "in" , "edge_types" : ["DECLARES_CLIENT" ]},
10381066 True , PRIORITY_LEAF_FOLLOWUP ,
10391067 ))
1068+ if _out_count (edge_summary , "HTTP_CALLS" ) > 0 :
1069+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_FIND_SUCCESS_HTTP_TARGETS .format (id = node_id )))
1070+ struct_pairs .append (_StructuredHint (
1071+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["HTTP_CALLS" ]},
1072+ True , PRIORITY_LEAF_FOLLOWUP ,
1073+ ))
10401074 return (finalize_hint_list (pairs ), finalize_structured_hints (struct_pairs ))
10411075 if kind == "producer" :
10421076 pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_PRODUCER_DECLARING .format (id = node_id )))
10431077 struct_pairs .append (_StructuredHint (
10441078 "neighbors" , {"ids" : [node_id ], "direction" : "in" , "edge_types" : ["DECLARES_PRODUCER" ]},
10451079 True , PRIORITY_LEAF_FOLLOWUP ,
10461080 ))
1081+ if _out_count (edge_summary , "ASYNC_CALLS" ) > 0 :
1082+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_FIND_SUCCESS_ASYNC_TARGETS .format (id = node_id )))
1083+ struct_pairs .append (_StructuredHint (
1084+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["ASYNC_CALLS" ]},
1085+ True , PRIORITY_LEAF_FOLLOWUP ,
1086+ ))
10471087 return (finalize_hint_list (pairs ), finalize_structured_hints (struct_pairs ))
10481088
10491089 if kind != "symbol" :
@@ -1078,6 +1118,33 @@ def generate_hints(
10781118 "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["DECLARES.DECLARES_PRODUCER" ]},
10791119 True , PRIORITY_DECLARES_TYPE_ROLLUP ,
10801120 ))
1121+
1122+ if not _type_rollup_would_emit (edge_summary ):
1123+ if decl_kind == "interface" and _in_count (edge_summary , "IMPLEMENTS" ) > 0 :
1124+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_TYPE_IMPLEMENTORS .format (id = node_id )))
1125+ struct_pairs .append (_StructuredHint (
1126+ "neighbors" , {"ids" : [node_id ], "direction" : "in" , "edge_types" : ["IMPLEMENTS" ]},
1127+ True , PRIORITY_LEAF_FOLLOWUP ,
1128+ ))
1129+ if decl_kind == "class" and _out_count (edge_summary , "IMPLEMENTS" ) > 0 :
1130+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_TYPE_IMPLEMENTS .format (id = node_id )))
1131+ struct_pairs .append (_StructuredHint (
1132+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["IMPLEMENTS" ]},
1133+ True , PRIORITY_LEAF_FOLLOWUP ,
1134+ ))
1135+ if decl_kind == "class" and _record_role (rec ) == "SERVICE" and _out_count (edge_summary , "INJECTS" ) > 0 :
1136+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_TYPE_DEPENDENCIES .format (id = node_id )))
1137+ struct_pairs .append (_StructuredHint (
1138+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["INJECTS" ]},
1139+ True , PRIORITY_LEAF_FOLLOWUP ,
1140+ ))
1141+ if decl_kind in {"interface" , "class" } and _in_count (edge_summary , "INJECTS" ) > 0 :
1142+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_TYPE_INJECTORS .format (id = node_id )))
1143+ struct_pairs .append (_StructuredHint (
1144+ "neighbors" , {"ids" : [node_id ], "direction" : "in" , "edge_types" : ["INJECTS" ]},
1145+ True , PRIORITY_LEAF_FOLLOWUP ,
1146+ ))
1147+
10811148 return (finalize_hint_list (pairs ), finalize_structured_hints (struct_pairs ))
10821149
10831150 if is_method :
@@ -1129,6 +1196,32 @@ def generate_hints(
11291196 "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["EXPOSES" ]},
11301197 True , PRIORITY_LEAF_FOLLOWUP ,
11311198 ))
1199+ calls_out = _out_count (edge_summary , "CALLS" )
1200+ if 1 <= calls_out <= 9 :
1201+ method_role = _record_role (rec )
1202+ if method_role != "OTHER" or calls_out >= 3 :
1203+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_METHOD_OUTBOUND_CALLS .format (id = node_id )))
1204+ struct_pairs .append (_StructuredHint (
1205+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["CALLS" ]},
1206+ True , PRIORITY_LEAF_FOLLOWUP ,
1207+ ))
1208+ if _out_count (edge_summary , "OVERRIDES" ) > 0 :
1209+ if _out_count (edge_summary , "OVERRIDDEN_BY" ) == 0 :
1210+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_METHOD_SUPER_DECL .format (id = node_id )))
1211+ struct_pairs .append (_StructuredHint (
1212+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["OVERRIDES" ]},
1213+ True , PRIORITY_LEAF_FOLLOWUP ,
1214+ ))
1215+ data = rec .get ("data" )
1216+ unresolved = 0
1217+ if isinstance (data , dict ):
1218+ unresolved = int (data .get ("unresolved_call_sites_total" ) or 0 )
1219+ if unresolved > 0 :
1220+ pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_METHOD_UNRESOLVED .format (id = node_id )))
1221+ struct_pairs .append (_StructuredHint (
1222+ "neighbors" , {"ids" : [node_id ], "direction" : "out" , "edge_types" : ["CALLS" ], "include_unresolved" : True },
1223+ True , PRIORITY_LEAF_FOLLOWUP ,
1224+ ))
11321225 if _out_count (edge_summary , "CALLS" ) >= 10 :
11331226 pairs .append ((PRIORITY_LEAF_FOLLOWUP , TPL_DESCRIBE_METHOD_MANY_CALLS ))
11341227 struct_pairs .append (_StructuredHint (
0 commit comments