3131_as_sequence = _coerce .as_sequence
3232
3333
34- def _health_gauge_html (score : float , grade : str ) -> str :
34+ def _health_gauge_html (
35+ score : float , grade : str , * , health_delta : int | None = None
36+ ) -> str :
3537 """Render an SVG ring gauge for health score."""
3638 if score < 0 :
3739 return _stat_card (
@@ -48,6 +50,17 @@ def _health_gauge_html(score: float, grade: str) -> str:
4850 color = "var(--warning)"
4951 else :
5052 color = "var(--error)"
53+
54+ delta_html = ""
55+ if health_delta is not None and health_delta != 0 :
56+ if health_delta > 0 :
57+ cls = "health-ring-delta--up"
58+ sign = "+"
59+ else :
60+ cls = "health-ring-delta--down"
61+ sign = ""
62+ delta_html = f'<div class="health-ring-delta { cls } ">{ sign } { health_delta } </div>'
63+
5164 return (
5265 '<div class="meta-item overview-health-card">'
5366 '<div class="overview-health-inner">'
@@ -62,6 +75,7 @@ def _health_gauge_html(score: float, grade: str) -> str:
6275 '<div class="health-ring-label">'
6376 f'<div class="health-ring-score">{ score :.0f} </div>'
6477 f'<div class="health-ring-grade">Grade { _escape_html (grade )} </div>'
78+ f"{ delta_html } "
6579 "</div></div></div></div>"
6680 )
6781
@@ -138,6 +152,14 @@ def _answer_and_tone() -> tuple[str, Tone]:
138152
139153 overview_answer , overview_tone = _answer_and_tone ()
140154
155+ # -- MetricsDiff deltas --
156+ md = ctx .metrics_diff
157+ _new_complexity = len (md .new_high_risk_functions ) if md else None
158+ _new_coupling = len (md .new_high_coupling_classes ) if md else None
159+ _new_dead = len (md .new_dead_code ) if md else None
160+ _new_cycles = len (md .new_cycles ) if md else None
161+ _health_delta = md .health_delta if md else None
162+
141163 # KPI cards
142164 kpis = [
143165 _stat_card (
@@ -158,6 +180,7 @@ def _answer_and_tone() -> tuple[str, Tone]:
158180 f"max { complexity_summary .get ('max' , 'n/a' )} "
159181 ),
160182 tip = "Functions with cyclomatic complexity above threshold" ,
183+ delta_new = _new_complexity ,
161184 ),
162185 _stat_card (
163186 "High Coupling" ,
@@ -167,6 +190,7 @@ def _answer_and_tone() -> tuple[str, Tone]:
167190 f"max { coupling_summary .get ('max' , 'n/a' )} "
168191 ),
169192 tip = "Classes with high coupling between objects (CBO)" ,
193+ delta_new = _new_coupling ,
170194 ),
171195 _stat_card (
172196 "Low Cohesion" ,
@@ -182,12 +206,14 @@ def _answer_and_tone() -> tuple[str, Tone]:
182206 dependency_cycle_count ,
183207 detail = f"max depth { dependency_max_depth } " ,
184208 tip = "Circular dependencies between project modules" ,
209+ delta_new = _new_cycles ,
185210 ),
186211 _stat_card (
187212 "Dead Code" ,
188213 dead_total ,
189214 detail = f"{ dead_high_conf } high-confidence" ,
190215 tip = "Potentially unused functions, classes, or imports" ,
216+ delta_new = _new_dead ,
191217 ),
192218 ]
193219
@@ -219,7 +245,9 @@ def _answer_and_tone() -> tuple[str, Tone]:
219245 + "</div></section>"
220246 )
221247
222- health_gauge = _health_gauge_html (health_score , health_grade )
248+ health_gauge = _health_gauge_html (
249+ health_score , health_grade , health_delta = _health_delta
250+ )
223251
224252 return (
225253 insight_block (
0 commit comments