empathy-framework 3.9.1__py3-none-any.whl → 3.9.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {empathy_framework-3.9.1.dist-info → empathy_framework-3.9.3.dist-info}/METADATA +1 -1
  2. {empathy_framework-3.9.1.dist-info → empathy_framework-3.9.3.dist-info}/RECORD +58 -61
  3. empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
  4. empathy_llm_toolkit/agent_factory/__init__.py +6 -6
  5. empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +4 -1
  6. empathy_llm_toolkit/agent_factory/crews/health_check.py +36 -29
  7. empathy_llm_toolkit/agent_factory/framework.py +2 -1
  8. empathy_llm_toolkit/config/__init__.py +8 -8
  9. empathy_llm_toolkit/security/__init__.py +17 -17
  10. empathy_os/__init__.py +1 -1
  11. empathy_os/adaptive/__init__.py +3 -3
  12. empathy_os/cli.py +5 -8
  13. empathy_os/cli_unified.py +86 -2
  14. empathy_os/config.py +7 -4
  15. empathy_os/hot_reload/integration.py +2 -1
  16. empathy_os/hot_reload/watcher.py +8 -4
  17. empathy_os/hot_reload/websocket.py +2 -1
  18. empathy_os/memory/__init__.py +30 -30
  19. empathy_os/memory/control_panel.py +3 -1
  20. empathy_os/memory/long_term.py +3 -1
  21. empathy_os/models/__init__.py +48 -48
  22. empathy_os/monitoring/__init__.py +7 -7
  23. empathy_os/optimization/__init__.py +3 -3
  24. empathy_os/pattern_library.py +2 -7
  25. empathy_os/plugins/__init__.py +6 -6
  26. empathy_os/resilience/__init__.py +5 -5
  27. empathy_os/scaffolding/cli.py +1 -1
  28. empathy_os/telemetry/cli.py +56 -13
  29. empathy_os/telemetry/usage_tracker.py +2 -5
  30. empathy_os/test_generator/generator.py +1 -1
  31. empathy_os/tier_recommender.py +39 -79
  32. empathy_os/trust/__init__.py +7 -7
  33. empathy_os/validation/__init__.py +3 -3
  34. empathy_os/workflow_patterns/output.py +1 -1
  35. empathy_os/workflow_patterns/structural.py +4 -4
  36. empathy_os/workflows/base.py +5 -2
  37. empathy_os/workflows/code_review_pipeline.py +1 -5
  38. empathy_os/workflows/dependency_check.py +1 -5
  39. empathy_os/workflows/keyboard_shortcuts/__init__.py +5 -5
  40. empathy_os/workflows/tier_tracking.py +40 -30
  41. empathy_software_plugin/cli.py +1 -3
  42. empathy_software_plugin/wizards/advanced_debugging_wizard.py +9 -6
  43. empathy_software_plugin/wizards/code_review_wizard.py +1 -3
  44. empathy_software_plugin/wizards/debugging/__init__.py +4 -4
  45. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +1 -1
  46. empathy_software_plugin/wizards/debugging/config_loaders.py +6 -2
  47. empathy_software_plugin/wizards/debugging/language_patterns.py +4 -2
  48. empathy_software_plugin/wizards/debugging/linter_parsers.py +1 -1
  49. empathy_software_plugin/wizards/performance/profiler_parsers.py +7 -7
  50. empathy_software_plugin/wizards/security/__init__.py +6 -6
  51. empathy_software_plugin/wizards/security/vulnerability_scanner.py +1 -1
  52. empathy_software_plugin/wizards/security_analysis_wizard.py +2 -2
  53. empathy_software_plugin/wizards/testing/quality_analyzer.py +3 -9
  54. empathy_software_plugin/wizards/testing/test_suggester.py +1 -1
  55. empathy_os/.empathy/costs.json +0 -60
  56. empathy_os/.empathy/discovery_stats.json +0 -15
  57. empathy_os/.empathy/workflow_runs.json +0 -45
  58. {empathy_framework-3.9.1.dist-info → empathy_framework-3.9.3.dist-info}/WHEEL +0 -0
  59. {empathy_framework-3.9.1.dist-info → empathy_framework-3.9.3.dist-info}/entry_points.txt +0 -0
  60. {empathy_framework-3.9.1.dist-info → empathy_framework-3.9.3.dist-info}/licenses/LICENSE +0 -0
  61. {empathy_framework-3.9.1.dist-info → empathy_framework-3.9.3.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,7 @@ try:
18
18
  from rich.panel import Panel
19
19
  from rich.table import Table
20
20
  from rich.text import Text
21
+
21
22
  RICH_AVAILABLE = True
22
23
  except ImportError:
23
24
  RICH_AVAILABLE = False
@@ -149,7 +150,9 @@ def cmd_telemetry_show(args: Any) -> int:
149
150
  console.print(f"\n[dim]Data location: {tracker.telemetry_dir}[/dim]")
150
151
  else:
151
152
  # Fallback to plain text
152
- print(f"\n{'Time':<19} {'Workflow':<20} {'Stage':<15} {'Tier':<10} {'Cost':>10} {'Cache':<10} {'Duration':>10}")
153
+ print(
154
+ f"\n{'Time':<19} {'Workflow':<20} {'Stage':<15} {'Tier':<10} {'Cost':>10} {'Cache':<10} {'Duration':>10}"
155
+ )
153
156
  print("-" * 120)
154
157
  total_cost = 0.0
155
158
  for entry in entries:
@@ -162,7 +165,9 @@ def cmd_telemetry_show(args: Any) -> int:
162
165
  duration_ms = entry.get("duration_ms", 0)
163
166
 
164
167
  cache_str = "HIT" if cache.get("hit") else "MISS"
165
- print(f"{ts:<19} {workflow:<20} {stage:<15} {tier:<10} ${cost:>9.4f} {cache_str:<10} {duration_ms:>9}ms")
168
+ print(
169
+ f"{ts:<19} {workflow:<20} {stage:<15} {tier:<10} ${cost:>9.4f} {cache_str:<10} {duration_ms:>9}ms"
170
+ )
166
171
  total_cost += cost
167
172
 
168
173
  print("-" * 120)
@@ -209,7 +214,9 @@ def cmd_telemetry_savings(args: Any) -> int:
209
214
  content_lines.append(f" Actual (tier routing): ${savings['actual_cost']:.2f}")
210
215
  content_lines.append("")
211
216
  savings_color = "green" if savings["savings"] > 0 else "red"
212
- content_lines.append(f"[bold {savings_color}]YOUR SAVINGS: ${savings['savings']:.2f} ({savings['savings_percent']:.1f}%)[/bold {savings_color}]")
217
+ content_lines.append(
218
+ f"[bold {savings_color}]YOUR SAVINGS: ${savings['savings']:.2f} ({savings['savings_percent']:.1f}%)[/bold {savings_color}]"
219
+ )
213
220
  content_lines.append("")
214
221
  content_lines.append(f"Cache savings: ${savings['cache_savings']:.2f}")
215
222
  content_lines.append(f"Total calls: {savings['total_calls']}")
@@ -271,7 +278,11 @@ def cmd_telemetry_compare(args: Any) -> int:
271
278
  table.add_column("Change", justify="right", style="blue")
272
279
 
273
280
  # Total calls
274
- calls_change = ((stats1["total_calls"] - stats2["total_calls"]) / stats2["total_calls"] * 100) if stats2["total_calls"] > 0 else 0
281
+ calls_change = (
282
+ ((stats1["total_calls"] - stats2["total_calls"]) / stats2["total_calls"] * 100)
283
+ if stats2["total_calls"] > 0
284
+ else 0
285
+ )
275
286
  table.add_row(
276
287
  "Total Calls",
277
288
  str(stats1["total_calls"]),
@@ -280,7 +291,11 @@ def cmd_telemetry_compare(args: Any) -> int:
280
291
  )
281
292
 
282
293
  # Total cost
283
- cost_change = ((stats1["total_cost"] - stats2["total_cost"]) / stats2["total_cost"] * 100) if stats2["total_cost"] > 0 else 0
294
+ cost_change = (
295
+ ((stats1["total_cost"] - stats2["total_cost"]) / stats2["total_cost"] * 100)
296
+ if stats2["total_cost"] > 0
297
+ else 0
298
+ )
284
299
  table.add_row(
285
300
  "Total Cost",
286
301
  f"${stats1['total_cost']:.2f}",
@@ -314,14 +329,28 @@ def cmd_telemetry_compare(args: Any) -> int:
314
329
  print("\n" + "=" * 80)
315
330
  print("TELEMETRY COMPARISON")
316
331
  print("=" * 80)
317
- print(f"{'Metric':<20} {'Last ' + str(period1_days) + ' days':>20} {'Last ' + str(period2_days) + ' days':>20} {'Change':>15}")
332
+ print(
333
+ f"{'Metric':<20} {'Last ' + str(period1_days) + ' days':>20} {'Last ' + str(period2_days) + ' days':>20} {'Change':>15}"
334
+ )
318
335
  print("-" * 80)
319
336
 
320
- calls_change = ((stats1["total_calls"] - stats2["total_calls"]) / stats2["total_calls"] * 100) if stats2["total_calls"] > 0 else 0
321
- print(f"{'Total Calls':<20} {stats1['total_calls']:>20} {stats2['total_calls']:>20} {calls_change:>14.1f}%")
337
+ calls_change = (
338
+ ((stats1["total_calls"] - stats2["total_calls"]) / stats2["total_calls"] * 100)
339
+ if stats2["total_calls"] > 0
340
+ else 0
341
+ )
342
+ print(
343
+ f"{'Total Calls':<20} {stats1['total_calls']:>20} {stats2['total_calls']:>20} {calls_change:>14.1f}%"
344
+ )
322
345
 
323
- cost_change = ((stats1["total_cost"] - stats2["total_cost"]) / stats2["total_cost"] * 100) if stats2["total_cost"] > 0 else 0
324
- print(f"{'Total Cost':<20} ${stats1['total_cost']:>19.2f} ${stats2['total_cost']:>19.2f} {cost_change:>14.1f}%")
346
+ cost_change = (
347
+ ((stats1["total_cost"] - stats2["total_cost"]) / stats2["total_cost"] * 100)
348
+ if stats2["total_cost"] > 0
349
+ else 0
350
+ )
351
+ print(
352
+ f"{'Total Cost':<20} ${stats1['total_cost']:>19.2f} ${stats2['total_cost']:>19.2f} {cost_change:>14.1f}%"
353
+ )
325
354
 
326
355
  avg1 = stats1["total_cost"] / stats1["total_calls"] if stats1["total_calls"] > 0 else 0
327
356
  avg2 = stats2["total_cost"] / stats2["total_calls"] if stats2["total_calls"] > 0 else 0
@@ -329,7 +358,9 @@ def cmd_telemetry_compare(args: Any) -> int:
329
358
  print(f"{'Avg Cost/Call':<20} ${avg1:>19.4f} ${avg2:>19.4f} {avg_change:>14.1f}%")
330
359
 
331
360
  cache_change = stats1["cache_hit_rate"] - stats2["cache_hit_rate"]
332
- print(f"{'Cache Hit Rate':<20} {stats1['cache_hit_rate']:>19.1f}% {stats2['cache_hit_rate']:>19.1f}% {cache_change:>14.1f}pp")
361
+ print(
362
+ f"{'Cache Hit Rate':<20} {stats1['cache_hit_rate']:>19.1f}% {stats2['cache_hit_rate']:>19.1f}% {cache_change:>14.1f}pp"
363
+ )
333
364
 
334
365
  print("=" * 80)
335
366
 
@@ -398,8 +429,20 @@ def cmd_telemetry_export(args: Any) -> int:
398
429
  return 0
399
430
 
400
431
  # Get all possible fields
401
- fieldnames = ["ts", "workflow", "stage", "tier", "model", "provider", "cost",
402
- "tokens_input", "tokens_output", "cache_hit", "cache_type", "duration_ms"]
432
+ fieldnames = [
433
+ "ts",
434
+ "workflow",
435
+ "stage",
436
+ "tier",
437
+ "model",
438
+ "provider",
439
+ "cost",
440
+ "tokens_input",
441
+ "tokens_output",
442
+ "cache_hit",
443
+ "cache_type",
444
+ "duration_ms",
445
+ ]
403
446
 
404
447
  if output_file:
405
448
  validated_path = _validate_file_path(output_file)
@@ -356,9 +356,7 @@ class UsageTracker:
356
356
  by_provider[provider] = by_provider.get(provider, 0.0) + cost
357
357
 
358
358
  total_calls = len(entries)
359
- cache_hit_rate = (
360
- (cache_hits / total_calls * 100) if total_calls > 0 else 0.0
361
- )
359
+ cache_hit_rate = (cache_hits / total_calls * 100) if total_calls > 0 else 0.0
362
360
 
363
361
  return {
364
362
  "total_calls": total_calls,
@@ -419,8 +417,7 @@ class UsageTracker:
419
417
 
420
418
  total_calls = len(entries)
421
419
  tier_distribution = {
422
- tier: round(count / total_calls * 100, 1)
423
- for tier, count in tier_counts.items()
420
+ tier: round(count / total_calls * 100, 1) for tier, count in tier_counts.items()
424
421
  }
425
422
 
426
423
  # Cache savings estimation
@@ -60,7 +60,7 @@ class TestGenerator:
60
60
  pattern_ids: list[str],
61
61
  wizard_module: str | None = None,
62
62
  wizard_class: str | None = None,
63
- ) -> dict[str, str]:
63
+ ) -> dict[str, str | None]:
64
64
  """Generate tests for a wizard.
65
65
 
66
66
  Args:
@@ -18,16 +18,16 @@ Usage:
18
18
  print(f"Expected cost: ${tier.expected_cost}")
19
19
  """
20
20
 
21
- from dataclasses import dataclass
22
- from pathlib import Path
23
- from typing import List, Optional, Dict, Tuple
24
21
  import json
25
22
  from collections import defaultdict
23
+ from dataclasses import dataclass
24
+ from pathlib import Path
26
25
 
27
26
 
28
27
  @dataclass
29
28
  class TierRecommendationResult:
30
29
  """Result of tier recommendation."""
30
+
31
31
  tier: str # CHEAP, CAPABLE, or PREMIUM
32
32
  confidence: float # 0.0-1.0
33
33
  reasoning: str
@@ -49,11 +49,7 @@ class TierRecommender:
49
49
  - Cost optimization
50
50
  """
51
51
 
52
- def __init__(
53
- self,
54
- patterns_dir: Optional[Path] = None,
55
- confidence_threshold: float = 0.7
56
- ):
52
+ def __init__(self, patterns_dir: Path | None = None, confidence_threshold: float = 0.7):
57
53
  """
58
54
  Initialize tier recommender.
59
55
 
@@ -67,7 +63,9 @@ class TierRecommender:
67
63
  """
68
64
  # Pattern 4: Range validation
69
65
  if not 0.0 <= confidence_threshold <= 1.0:
70
- raise ValueError(f"confidence_threshold must be between 0.0 and 1.0, got {confidence_threshold}")
66
+ raise ValueError(
67
+ f"confidence_threshold must be between 0.0 and 1.0, got {confidence_threshold}"
68
+ )
71
69
 
72
70
  if patterns_dir is None:
73
71
  patterns_dir = Path(__file__).parent.parent.parent / "patterns" / "debugging"
@@ -79,9 +77,9 @@ class TierRecommender:
79
77
  # Build indexes for fast lookup
80
78
  self._build_indexes()
81
79
 
82
- def _load_patterns(self) -> List[Dict]:
80
+ def _load_patterns(self) -> list[dict]:
83
81
  """Load all enhanced patterns with tier_progression data."""
84
- patterns = []
82
+ patterns: list[dict] = []
85
83
 
86
84
  if not self.patterns_dir.exists():
87
85
  return patterns
@@ -106,8 +104,8 @@ class TierRecommender:
106
104
 
107
105
  def _build_indexes(self):
108
106
  """Build indexes for fast pattern lookup."""
109
- self.bug_type_index: Dict[str, List[Dict]] = defaultdict(list)
110
- self.file_pattern_index: Dict[str, List[Dict]] = defaultdict(list)
107
+ self.bug_type_index: dict[str, list[dict]] = defaultdict(list)
108
+ self.file_pattern_index: dict[str, list[dict]] = defaultdict(list)
111
109
 
112
110
  for pattern in self.patterns:
113
111
  # Index by bug type
@@ -125,8 +123,8 @@ class TierRecommender:
125
123
  def recommend(
126
124
  self,
127
125
  bug_description: str,
128
- files_affected: Optional[List[str]] = None,
129
- complexity_hint: Optional[int] = None
126
+ files_affected: list[str] | None = None,
127
+ complexity_hint: int | None = None,
130
128
  ) -> TierRecommendationResult:
131
129
  """
132
130
  Recommend optimal starting tier for a new bug.
@@ -160,15 +158,13 @@ class TierRecommender:
160
158
 
161
159
  # Step 2: Find similar patterns
162
160
  similar_patterns = self._find_similar_patterns(
163
- bug_type=bug_type,
164
- files_affected=files_affected or []
161
+ bug_type=bug_type, files_affected=files_affected or []
165
162
  )
166
163
 
167
164
  # Step 3: If no similar patterns, use fallback logic
168
165
  if not similar_patterns:
169
166
  return self._fallback_recommendation(
170
- bug_description=bug_description,
171
- complexity_hint=complexity_hint
167
+ bug_description=bug_description, complexity_hint=complexity_hint
172
168
  )
173
169
 
174
170
  # Step 4: Analyze tier distribution in similar patterns
@@ -187,12 +183,12 @@ class TierRecommender:
187
183
  bug_type=bug_type,
188
184
  tier=recommended_tier,
189
185
  confidence=confidence,
190
- similar_count=len(similar_patterns)
186
+ similar_count=len(similar_patterns),
191
187
  ),
192
188
  expected_cost=cost_estimate["avg_cost"],
193
189
  expected_attempts=cost_estimate["avg_attempts"],
194
190
  similar_patterns_count=len(similar_patterns),
195
- fallback_used=False
191
+ fallback_used=False,
196
192
  )
197
193
 
198
194
  def _classify_bug_type(self, description: str) -> str:
@@ -215,11 +211,7 @@ class TierRecommender:
215
211
 
216
212
  return "unknown"
217
213
 
218
- def _find_similar_patterns(
219
- self,
220
- bug_type: str,
221
- files_affected: List[str]
222
- ) -> List[Dict]:
214
+ def _find_similar_patterns(self, bug_type: str, files_affected: list[str]) -> list[dict]:
223
215
  """Find patterns similar to current bug.
224
216
 
225
217
  Raises:
@@ -247,16 +239,11 @@ class TierRecommender:
247
239
 
248
240
  return similar
249
241
 
250
- def _analyze_tier_distribution(
251
- self,
252
- patterns: List[Dict]
253
- ) -> Dict[str, Dict]:
242
+ def _analyze_tier_distribution(self, patterns: list[dict]) -> dict[str, dict]:
254
243
  """Analyze tier success rates from similar patterns."""
255
- tier_stats: Dict[str, Dict] = defaultdict(lambda: {
256
- "count": 0,
257
- "total_cost": 0.0,
258
- "total_attempts": 0
259
- })
244
+ tier_stats: dict[str, dict] = defaultdict(
245
+ lambda: {"count": 0, "total_cost": 0.0, "total_attempts": 0}
246
+ )
260
247
 
261
248
  for pattern in patterns:
262
249
  tp = pattern["tier_progression"]
@@ -268,7 +255,7 @@ class TierRecommender:
268
255
  stats["total_attempts"] += tp["total_attempts"]
269
256
 
270
257
  # Calculate averages
271
- for tier, stats in tier_stats.items():
258
+ for _tier, stats in tier_stats.items():
272
259
  count = stats["count"]
273
260
  stats["success_rate"] = count / len(patterns)
274
261
  stats["avg_cost"] = stats["total_cost"] / count
@@ -276,19 +263,14 @@ class TierRecommender:
276
263
 
277
264
  return dict(tier_stats)
278
265
 
279
- def _select_tier(
280
- self,
281
- tier_analysis: Dict[str, Dict]
282
- ) -> Tuple[str, float]:
266
+ def _select_tier(self, tier_analysis: dict[str, dict]) -> tuple[str, float]:
283
267
  """Select best tier based on success rate and cost."""
284
268
  if not tier_analysis:
285
269
  return "CHEAP", 0.5
286
270
 
287
271
  # Sort by success rate
288
272
  sorted_tiers = sorted(
289
- tier_analysis.items(),
290
- key=lambda x: x[1]["success_rate"],
291
- reverse=True
273
+ tier_analysis.items(), key=lambda x: x[1]["success_rate"], reverse=True
292
274
  )
293
275
 
294
276
  best_tier, stats = sorted_tiers[0]
@@ -296,16 +278,9 @@ class TierRecommender:
296
278
 
297
279
  return best_tier, confidence
298
280
 
299
- def _estimate_cost(
300
- self,
301
- patterns: List[Dict],
302
- tier: str
303
- ) -> Dict[str, float]:
281
+ def _estimate_cost(self, patterns: list[dict], tier: str) -> dict[str, float]:
304
282
  """Estimate cost and attempts for recommended tier."""
305
- matching = [
306
- p for p in patterns
307
- if p["tier_progression"]["successful_tier"] == tier
308
- ]
283
+ matching = [p for p in patterns if p["tier_progression"]["successful_tier"] == tier]
309
284
 
310
285
  if not matching:
311
286
  # Default estimates by tier
@@ -316,24 +291,16 @@ class TierRecommender:
316
291
  }
317
292
  return defaults.get(tier, defaults["CHEAP"])
318
293
 
319
- total_cost = sum(
320
- p["tier_progression"]["cost_breakdown"]["total_cost"]
321
- for p in matching
322
- )
323
- total_attempts = sum(
324
- p["tier_progression"]["total_attempts"]
325
- for p in matching
326
- )
294
+ total_cost = sum(p["tier_progression"]["cost_breakdown"]["total_cost"] for p in matching)
295
+ total_attempts = sum(p["tier_progression"]["total_attempts"] for p in matching)
327
296
 
328
297
  return {
329
298
  "avg_cost": total_cost / len(matching),
330
- "avg_attempts": total_attempts / len(matching)
299
+ "avg_attempts": total_attempts / len(matching),
331
300
  }
332
301
 
333
302
  def _fallback_recommendation(
334
- self,
335
- bug_description: str,
336
- complexity_hint: Optional[int]
303
+ self, bug_description: str, complexity_hint: int | None
337
304
  ) -> TierRecommendationResult:
338
305
  """Provide fallback recommendation when no historical data available."""
339
306
 
@@ -356,7 +323,7 @@ class TierRecommender:
356
323
  expected_cost=cost,
357
324
  expected_attempts=2.0,
358
325
  similar_patterns_count=0,
359
- fallback_used=True
326
+ fallback_used=True,
360
327
  )
361
328
 
362
329
  # Default: start with CHEAP tier (conservative)
@@ -367,15 +334,11 @@ class TierRecommender:
367
334
  expected_cost=0.030,
368
335
  expected_attempts=1.5,
369
336
  similar_patterns_count=0,
370
- fallback_used=True
337
+ fallback_used=True,
371
338
  )
372
339
 
373
340
  def _generate_reasoning(
374
- self,
375
- bug_type: str,
376
- tier: str,
377
- confidence: float,
378
- similar_count: int
341
+ self, bug_type: str, tier: str, confidence: float, similar_count: int
379
342
  ) -> str:
380
343
  """Generate human-readable reasoning for recommendation."""
381
344
  percent = int(confidence * 100)
@@ -390,17 +353,14 @@ class TierRecommender:
390
353
  f"resolved at {tier} tier"
391
354
  )
392
355
 
393
- def get_stats(self) -> Dict:
356
+ def get_stats(self) -> dict:
394
357
  """Get overall statistics about pattern learning."""
395
358
  if not self.patterns:
396
- return {
397
- "total_patterns": 0,
398
- "message": "No patterns loaded"
399
- }
359
+ return {"total_patterns": 0, "message": "No patterns loaded"}
400
360
 
401
361
  # Calculate tier distribution
402
- tier_dist = defaultdict(int)
403
- bug_type_dist = defaultdict(int)
362
+ tier_dist: dict[str, int] = defaultdict(int)
363
+ bug_type_dist: dict[str, int] = defaultdict(int)
404
364
  total_savings = 0.0
405
365
 
406
366
  for pattern in self.patterns:
@@ -418,5 +378,5 @@ class TierRecommender:
418
378
  "CHEAP": tier_dist.get("CHEAP", 0),
419
379
  "CAPABLE": tier_dist.get("CAPABLE", 0),
420
380
  "PREMIUM": tier_dist.get("PREMIUM", 0),
421
- }
381
+ },
422
382
  }
@@ -8,13 +8,13 @@ Transfer: Protect user trust like protecting system stability
8
8
  """
9
9
 
10
10
  from .circuit_breaker import (
11
- TrustCircuitBreaker,
12
- TrustConfig,
13
- TrustDamageEvent,
14
- TrustDamageType,
15
- TrustRecoveryEvent,
16
- TrustState,
17
- create_trust_breaker,
11
+ TrustCircuitBreaker,
12
+ TrustConfig,
13
+ TrustDamageEvent,
14
+ TrustDamageType,
15
+ TrustRecoveryEvent,
16
+ TrustState,
17
+ create_trust_breaker,
18
18
  )
19
19
 
20
20
  __all__ = [
@@ -7,9 +7,9 @@ Licensed under Fair Source License 0.9
7
7
  """
8
8
 
9
9
  from empathy_os.validation.xml_validator import (
10
- ValidationResult,
11
- XMLValidator,
12
- validate_xml_response,
10
+ ValidationResult,
11
+ XMLValidator,
12
+ validate_xml_response,
13
13
  )
14
14
 
15
15
  __all__ = [
@@ -33,7 +33,7 @@ class ResultDataclassPattern(WorkflowPattern):
33
33
 
34
34
  def generate_code_sections(self, context: dict[str, Any]) -> list[CodeSection]:
35
35
  """Generate code for result dataclass."""
36
- workflow_name = context.get("workflow_name", "my-workflow")
36
+ context.get("workflow_name", "my-workflow")
37
37
  class_name = context.get("class_name", "MyWorkflow")
38
38
  result_class_name = f"{class_name}Result"
39
39
 
@@ -34,7 +34,7 @@ class SingleStagePattern(WorkflowPattern):
34
34
  def generate_code_sections(self, context: dict[str, Any]) -> list[CodeSection]:
35
35
  """Generate code for single-stage workflow."""
36
36
  workflow_name = context.get("workflow_name", "MyWorkflow")
37
- class_name = context.get("class_name", "MyWorkflow")
37
+ context.get("class_name", "MyWorkflow")
38
38
  description = context.get("description", "Single-stage workflow")
39
39
  tier = context.get("tier", "CAPABLE")
40
40
 
@@ -118,7 +118,7 @@ class MultiStagePattern(WorkflowPattern):
118
118
  def generate_code_sections(self, context: dict[str, Any]) -> list[CodeSection]:
119
119
  """Generate code for multi-stage workflow."""
120
120
  workflow_name = context.get("workflow_name", "my-workflow")
121
- class_name = context.get("class_name", "MyWorkflow")
121
+ context.get("class_name", "MyWorkflow")
122
122
  description = context.get("description", "Multi-stage workflow")
123
123
  stages = context.get("stages", ["analyze", "process", "report"])
124
124
  tier_map = context.get(
@@ -138,7 +138,7 @@ class MultiStagePattern(WorkflowPattern):
138
138
 
139
139
  # Generate stage routing
140
140
  stage_routing = []
141
- for i, stage in enumerate(stages):
141
+ for _i, stage in enumerate(stages):
142
142
  stage_routing.append(
143
143
  f""" if stage_name == "{stage}":
144
144
  return await self._{stage}(input_data, tier)"""
@@ -229,7 +229,7 @@ class CrewBasedPattern(WorkflowPattern):
229
229
  def generate_code_sections(self, context: dict[str, Any]) -> list[CodeSection]:
230
230
  """Generate code for crew-based workflow."""
231
231
  workflow_name = context.get("workflow_name", "my-crew-workflow")
232
- class_name = context.get("class_name", "MyCrewWorkflow")
232
+ context.get("class_name", "MyCrewWorkflow")
233
233
  description = context.get("description", "Crew-based workflow")
234
234
  crew_name = context.get("crew_name", "MyCrew")
235
235
 
@@ -26,6 +26,9 @@ from enum import Enum
26
26
  from pathlib import Path
27
27
  from typing import TYPE_CHECKING, Any
28
28
 
29
+ if TYPE_CHECKING:
30
+ from .tier_tracking import WorkflowTierTracker
31
+
29
32
  # Load .env file for API keys if python-dotenv is available
30
33
  try:
31
34
  from dotenv import load_dotenv
@@ -430,7 +433,7 @@ class BaseWorkflow(ABC):
430
433
 
431
434
  # Tier tracking support
432
435
  self._enable_tier_tracking = enable_tier_tracking
433
- self._tier_tracker = None
436
+ self._tier_tracker: WorkflowTierTracker | None = None
434
437
 
435
438
  # Telemetry tracking (singleton instance)
436
439
  self._telemetry_tracker: UsageTracker | None = None
@@ -569,7 +572,7 @@ class BaseWorkflow(ABC):
569
572
  logger.debug(f"Cache hit for {self.name}:{stage}")
570
573
  # Determine cache type
571
574
  if hasattr(self._cache, "cache_type"):
572
- ct = self._cache.cache_type # type: ignore
575
+ ct = self._cache.cache_type
573
576
  # Ensure it's a string (not a Mock object)
574
577
  cache_type = str(ct) if ct and isinstance(ct, str) else "hash"
575
578
  else:
@@ -605,11 +605,7 @@ def format_code_review_pipeline_report(result: CodeReviewPipelineResult) -> str:
605
605
  quality_label = (
606
606
  "EXCELLENT"
607
607
  if score >= 90
608
- else "GOOD"
609
- if score >= 70
610
- else "NEEDS WORK"
611
- if score >= 50
612
- else "POOR"
608
+ else "GOOD" if score >= 70 else "NEEDS WORK" if score >= 50 else "POOR"
613
609
  )
614
610
  lines.append("-" * 60)
615
611
  lines.append("QUALITY SCORE")
@@ -321,11 +321,7 @@ class DependencyCheckWorkflow(BaseWorkflow):
321
321
  risk_level = (
322
322
  "critical"
323
323
  if risk_score >= 75
324
- else "high"
325
- if risk_score >= 50
326
- else "medium"
327
- if risk_score >= 25
328
- else "low"
324
+ else "high" if risk_score >= 50 else "medium" if risk_score >= 25 else "low"
329
325
  )
330
326
 
331
327
  # Build vulnerability summary for LLM
@@ -12,11 +12,11 @@ Features:
12
12
 
13
13
  from .generators import CLIAliasGenerator, MarkdownDocGenerator, VSCodeKeybindingsGenerator
14
14
  from .parsers import (
15
- FeatureParser,
16
- LLMFeatureAnalyzer,
17
- PyProjectParser,
18
- VSCodeCommandParser,
19
- YAMLManifestParser,
15
+ FeatureParser,
16
+ LLMFeatureAnalyzer,
17
+ PyProjectParser,
18
+ VSCodeCommandParser,
19
+ YAMLManifestParser,
20
20
  )
21
21
  from .schema import Category, Feature, FeatureManifest, LayoutConfig, ShortcutAssignment
22
22
  from .workflow import KeyboardShortcutWorkflow