empathy-framework 4.6.6__py3-none-any.whl → 4.7.0__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 (247) hide show
  1. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
  2. empathy_framework-4.7.0.dist-info/RECORD +354 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -2
  4. empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
  5. empathy_llm_toolkit/agent_factory/__init__.py +6 -6
  6. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
  7. empathy_llm_toolkit/agents_md/__init__.py +22 -0
  8. empathy_llm_toolkit/agents_md/loader.py +218 -0
  9. empathy_llm_toolkit/agents_md/parser.py +271 -0
  10. empathy_llm_toolkit/agents_md/registry.py +307 -0
  11. empathy_llm_toolkit/commands/__init__.py +51 -0
  12. empathy_llm_toolkit/commands/context.py +375 -0
  13. empathy_llm_toolkit/commands/loader.py +301 -0
  14. empathy_llm_toolkit/commands/models.py +231 -0
  15. empathy_llm_toolkit/commands/parser.py +371 -0
  16. empathy_llm_toolkit/commands/registry.py +429 -0
  17. empathy_llm_toolkit/config/__init__.py +8 -8
  18. empathy_llm_toolkit/config/unified.py +3 -7
  19. empathy_llm_toolkit/context/__init__.py +22 -0
  20. empathy_llm_toolkit/context/compaction.py +455 -0
  21. empathy_llm_toolkit/context/manager.py +434 -0
  22. empathy_llm_toolkit/hooks/__init__.py +24 -0
  23. empathy_llm_toolkit/hooks/config.py +306 -0
  24. empathy_llm_toolkit/hooks/executor.py +289 -0
  25. empathy_llm_toolkit/hooks/registry.py +302 -0
  26. empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
  27. empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
  28. empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
  29. empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
  30. empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
  31. empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
  32. empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
  33. empathy_llm_toolkit/learning/__init__.py +30 -0
  34. empathy_llm_toolkit/learning/evaluator.py +438 -0
  35. empathy_llm_toolkit/learning/extractor.py +514 -0
  36. empathy_llm_toolkit/learning/storage.py +560 -0
  37. empathy_llm_toolkit/providers.py +4 -11
  38. empathy_llm_toolkit/security/__init__.py +17 -17
  39. empathy_llm_toolkit/utils/tokens.py +2 -5
  40. empathy_os/__init__.py +202 -70
  41. empathy_os/cache_monitor.py +5 -3
  42. empathy_os/cli/__init__.py +11 -55
  43. empathy_os/cli/__main__.py +29 -15
  44. empathy_os/cli/commands/inspection.py +21 -12
  45. empathy_os/cli/commands/memory.py +4 -12
  46. empathy_os/cli/commands/profiling.py +198 -0
  47. empathy_os/cli/commands/utilities.py +27 -7
  48. empathy_os/cli.py +28 -57
  49. empathy_os/cli_unified.py +525 -1164
  50. empathy_os/cost_tracker.py +9 -3
  51. empathy_os/dashboard/server.py +200 -2
  52. empathy_os/hot_reload/__init__.py +7 -7
  53. empathy_os/hot_reload/config.py +6 -7
  54. empathy_os/hot_reload/integration.py +35 -35
  55. empathy_os/hot_reload/reloader.py +57 -57
  56. empathy_os/hot_reload/watcher.py +28 -28
  57. empathy_os/hot_reload/websocket.py +2 -2
  58. empathy_os/memory/__init__.py +11 -4
  59. empathy_os/memory/claude_memory.py +1 -1
  60. empathy_os/memory/cross_session.py +8 -12
  61. empathy_os/memory/edges.py +6 -6
  62. empathy_os/memory/file_session.py +770 -0
  63. empathy_os/memory/graph.py +30 -30
  64. empathy_os/memory/nodes.py +6 -6
  65. empathy_os/memory/short_term.py +15 -9
  66. empathy_os/memory/unified.py +606 -140
  67. empathy_os/meta_workflows/agent_creator.py +3 -9
  68. empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
  69. empathy_os/meta_workflows/form_engine.py +6 -18
  70. empathy_os/meta_workflows/intent_detector.py +64 -24
  71. empathy_os/meta_workflows/models.py +3 -1
  72. empathy_os/meta_workflows/pattern_learner.py +13 -31
  73. empathy_os/meta_workflows/plan_generator.py +55 -47
  74. empathy_os/meta_workflows/session_context.py +2 -3
  75. empathy_os/meta_workflows/workflow.py +20 -51
  76. empathy_os/models/cli.py +2 -2
  77. empathy_os/models/tasks.py +1 -2
  78. empathy_os/models/telemetry.py +4 -1
  79. empathy_os/models/token_estimator.py +3 -1
  80. empathy_os/monitoring/alerts.py +938 -9
  81. empathy_os/monitoring/alerts_cli.py +346 -183
  82. empathy_os/orchestration/execution_strategies.py +12 -29
  83. empathy_os/orchestration/pattern_learner.py +20 -26
  84. empathy_os/orchestration/real_tools.py +6 -15
  85. empathy_os/platform_utils.py +2 -1
  86. empathy_os/plugins/__init__.py +2 -2
  87. empathy_os/plugins/base.py +64 -64
  88. empathy_os/plugins/registry.py +32 -32
  89. empathy_os/project_index/index.py +49 -15
  90. empathy_os/project_index/models.py +1 -2
  91. empathy_os/project_index/reports.py +1 -1
  92. empathy_os/project_index/scanner.py +1 -0
  93. empathy_os/redis_memory.py +10 -7
  94. empathy_os/resilience/__init__.py +1 -1
  95. empathy_os/resilience/health.py +10 -10
  96. empathy_os/routing/__init__.py +7 -7
  97. empathy_os/routing/chain_executor.py +37 -37
  98. empathy_os/routing/classifier.py +36 -36
  99. empathy_os/routing/smart_router.py +40 -40
  100. empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
  101. empathy_os/scaffolding/__init__.py +8 -8
  102. empathy_os/scaffolding/__main__.py +1 -1
  103. empathy_os/scaffolding/cli.py +28 -28
  104. empathy_os/socratic/__init__.py +3 -19
  105. empathy_os/socratic/ab_testing.py +25 -36
  106. empathy_os/socratic/blueprint.py +38 -38
  107. empathy_os/socratic/cli.py +34 -20
  108. empathy_os/socratic/collaboration.py +30 -28
  109. empathy_os/socratic/domain_templates.py +9 -1
  110. empathy_os/socratic/embeddings.py +17 -13
  111. empathy_os/socratic/engine.py +135 -70
  112. empathy_os/socratic/explainer.py +70 -60
  113. empathy_os/socratic/feedback.py +24 -19
  114. empathy_os/socratic/forms.py +15 -10
  115. empathy_os/socratic/generator.py +51 -35
  116. empathy_os/socratic/llm_analyzer.py +25 -23
  117. empathy_os/socratic/mcp_server.py +99 -159
  118. empathy_os/socratic/session.py +19 -13
  119. empathy_os/socratic/storage.py +98 -67
  120. empathy_os/socratic/success.py +38 -27
  121. empathy_os/socratic/visual_editor.py +51 -39
  122. empathy_os/socratic/web_ui.py +99 -66
  123. empathy_os/telemetry/cli.py +3 -1
  124. empathy_os/telemetry/usage_tracker.py +1 -3
  125. empathy_os/test_generator/__init__.py +3 -3
  126. empathy_os/test_generator/cli.py +28 -28
  127. empathy_os/test_generator/generator.py +64 -66
  128. empathy_os/test_generator/risk_analyzer.py +11 -11
  129. empathy_os/vscode_bridge.py +173 -0
  130. empathy_os/workflows/__init__.py +212 -120
  131. empathy_os/workflows/batch_processing.py +8 -24
  132. empathy_os/workflows/bug_predict.py +1 -1
  133. empathy_os/workflows/code_review.py +20 -5
  134. empathy_os/workflows/code_review_pipeline.py +13 -8
  135. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  136. empathy_os/workflows/manage_documentation.py +1 -0
  137. empathy_os/workflows/orchestrated_health_check.py +6 -11
  138. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  139. empathy_os/workflows/pr_review.py +18 -10
  140. empathy_os/workflows/progressive/__init__.py +2 -12
  141. empathy_os/workflows/progressive/cli.py +14 -37
  142. empathy_os/workflows/progressive/core.py +12 -12
  143. empathy_os/workflows/progressive/orchestrator.py +166 -144
  144. empathy_os/workflows/progressive/reports.py +22 -31
  145. empathy_os/workflows/progressive/telemetry.py +8 -14
  146. empathy_os/workflows/progressive/test_gen.py +29 -48
  147. empathy_os/workflows/progressive/workflow.py +31 -70
  148. empathy_os/workflows/release_prep.py +21 -6
  149. empathy_os/workflows/release_prep_crew.py +1 -0
  150. empathy_os/workflows/secure_release.py +13 -6
  151. empathy_os/workflows/security_audit.py +8 -3
  152. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  153. empathy_os/workflows/test_maintenance_crew.py +1 -0
  154. empathy_os/workflows/test_runner.py +16 -12
  155. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  156. empathy_software_plugin/cli.py +0 -122
  157. coach_wizards/__init__.py +0 -45
  158. coach_wizards/accessibility_wizard.py +0 -91
  159. coach_wizards/api_wizard.py +0 -91
  160. coach_wizards/base_wizard.py +0 -209
  161. coach_wizards/cicd_wizard.py +0 -91
  162. coach_wizards/code_reviewer_README.md +0 -60
  163. coach_wizards/code_reviewer_wizard.py +0 -180
  164. coach_wizards/compliance_wizard.py +0 -91
  165. coach_wizards/database_wizard.py +0 -91
  166. coach_wizards/debugging_wizard.py +0 -91
  167. coach_wizards/documentation_wizard.py +0 -91
  168. coach_wizards/generate_wizards.py +0 -347
  169. coach_wizards/localization_wizard.py +0 -173
  170. coach_wizards/migration_wizard.py +0 -91
  171. coach_wizards/monitoring_wizard.py +0 -91
  172. coach_wizards/observability_wizard.py +0 -91
  173. coach_wizards/performance_wizard.py +0 -91
  174. coach_wizards/prompt_engineering_wizard.py +0 -661
  175. coach_wizards/refactoring_wizard.py +0 -91
  176. coach_wizards/scaling_wizard.py +0 -90
  177. coach_wizards/security_wizard.py +0 -92
  178. coach_wizards/testing_wizard.py +0 -91
  179. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  180. empathy_llm_toolkit/wizards/__init__.py +0 -43
  181. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  182. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  183. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  184. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  185. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  186. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  187. empathy_os/wizard_factory_cli.py +0 -170
  188. empathy_software_plugin/wizards/__init__.py +0 -42
  189. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  190. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  191. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  192. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  193. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  194. empathy_software_plugin/wizards/base_wizard.py +0 -288
  195. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  196. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  197. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  198. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  199. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  200. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  201. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  202. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  203. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  204. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  205. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  206. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  207. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  208. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  209. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  210. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  211. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  212. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  213. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  214. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  215. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  216. empathy_software_plugin/wizards/security/__init__.py +0 -32
  217. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  218. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  219. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  220. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  221. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  222. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  223. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  224. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  225. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  226. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  227. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  228. wizards/__init__.py +0 -82
  229. wizards/admission_assessment_wizard.py +0 -644
  230. wizards/care_plan.py +0 -321
  231. wizards/clinical_assessment.py +0 -769
  232. wizards/discharge_planning.py +0 -77
  233. wizards/discharge_summary_wizard.py +0 -468
  234. wizards/dosage_calculation.py +0 -497
  235. wizards/incident_report_wizard.py +0 -454
  236. wizards/medication_reconciliation.py +0 -85
  237. wizards/nursing_assessment.py +0 -171
  238. wizards/patient_education.py +0 -654
  239. wizards/quality_improvement.py +0 -705
  240. wizards/sbar_report.py +0 -324
  241. wizards/sbar_wizard.py +0 -608
  242. wizards/shift_handoff_wizard.py +0 -535
  243. wizards/soap_note_wizard.py +0 -679
  244. wizards/treatment_plan.py +0 -15
  245. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/WHEEL +0 -0
  246. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
  247. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -142,8 +142,12 @@ class CostTracker:
142
142
  with open(self.costs_summary) as f:
143
143
  summary_data = json.load(f)
144
144
  self.data["daily_totals"] = summary_data.get("daily_totals", {})
145
- self.data["created_at"] = summary_data.get("created_at", self.data["created_at"])
146
- self.data["last_updated"] = summary_data.get("last_updated", self.data["last_updated"])
145
+ self.data["created_at"] = summary_data.get(
146
+ "created_at", self.data["created_at"]
147
+ )
148
+ self.data["last_updated"] = summary_data.get(
149
+ "last_updated", self.data["last_updated"]
150
+ )
147
151
  return # Summary loaded, done
148
152
  except (OSError, json.JSONDecodeError):
149
153
  pass # Fall through to JSON fallback
@@ -155,7 +159,9 @@ class CostTracker:
155
159
  json_data = json.load(f)
156
160
  self.data["daily_totals"] = json_data.get("daily_totals", {})
157
161
  self.data["created_at"] = json_data.get("created_at", self.data["created_at"])
158
- self.data["last_updated"] = json_data.get("last_updated", self.data["last_updated"])
162
+ self.data["last_updated"] = json_data.get(
163
+ "last_updated", self.data["last_updated"]
164
+ )
159
165
  # Don't load requests here - they'll be lazy-loaded
160
166
  except (OSError, json.JSONDecodeError):
161
167
  pass # Use defaults
@@ -42,6 +42,14 @@ except ImportError:
42
42
  list_workflows = None # type: ignore[assignment]
43
43
  HAS_WORKFLOWS = False
44
44
 
45
+ try:
46
+ from empathy_os.models.telemetry import TelemetryStore
47
+
48
+ HAS_TELEMETRY = True
49
+ except ImportError:
50
+ TelemetryStore = None # type: ignore[misc, assignment]
51
+ HAS_TELEMETRY = False
52
+
45
53
 
46
54
  class DashboardHandler(http.server.BaseHTTPRequestHandler):
47
55
  """HTTP request handler for the dashboard."""
@@ -69,6 +77,8 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
69
77
  self._serve_health()
70
78
  elif path == "/api/workflows":
71
79
  self._serve_workflows()
80
+ elif path == "/api/tests":
81
+ self._serve_tests()
72
82
  else:
73
83
  self.send_error(404, "Not Found")
74
84
 
@@ -119,6 +129,70 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
119
129
  data = {"error": "Workflows not available"}
120
130
  self._send_json(data)
121
131
 
132
+ def _serve_tests(self):
133
+ """Serve test tracking data as JSON."""
134
+ data = self._get_test_stats()
135
+ self._send_json(data)
136
+
137
+ def _get_test_stats(self) -> dict:
138
+ """Get test tracking statistics.
139
+
140
+ Returns:
141
+ Dictionary with test tracking data including:
142
+ - total_files: Total files with test records
143
+ - passed_files: Files with passing tests
144
+ - failed_files: Files with failing tests
145
+ - coverage_avg: Average coverage percentage
146
+ - recent_tests: Recent test executions
147
+ - files_needing_tests: Files that need attention
148
+ """
149
+ if not HAS_TELEMETRY or TelemetryStore is None:
150
+ return {"error": "Telemetry not available"}
151
+
152
+ try:
153
+ store = TelemetryStore(Path(self.empathy_dir))
154
+
155
+ # Get file test records and convert to dicts
156
+ file_tests_raw = store.get_file_tests(limit=100)
157
+ file_tests = [t.to_dict() if hasattr(t, "to_dict") else t for t in file_tests_raw]
158
+
159
+ # Calculate stats
160
+ total_files = len(file_tests)
161
+ passed_files = sum(1 for t in file_tests if t.get("last_test_result") == "passed")
162
+ failed_files = sum(1 for t in file_tests if t.get("last_test_result") == "failed")
163
+
164
+ # Coverage average (field is coverage_percent)
165
+ coverages = [
166
+ t.get("coverage_percent", 0) for t in file_tests if t.get("coverage_percent")
167
+ ]
168
+ coverage_avg = sum(coverages) / len(coverages) if coverages else 0
169
+
170
+ # If no coverage data, use pass rate as a proxy
171
+ if coverage_avg == 0 and total_files > 0:
172
+ coverage_avg = (passed_files / total_files) * 100
173
+
174
+ # Get files needing attention (failed or stale) and convert to dicts
175
+ files_needing_raw = store.get_files_needing_tests(stale_only=False, failed_only=False)
176
+ files_needing_tests = [
177
+ t.to_dict() if hasattr(t, "to_dict") else t for t in files_needing_raw[:10]
178
+ ]
179
+
180
+ # Get recent test executions and convert to dicts
181
+ recent_raw = store.get_test_executions(limit=10)
182
+ recent_executions = [t.to_dict() if hasattr(t, "to_dict") else t for t in recent_raw]
183
+
184
+ return {
185
+ "total_files": total_files,
186
+ "passed_files": passed_files,
187
+ "failed_files": failed_files,
188
+ "coverage_avg": round(coverage_avg, 1),
189
+ "files_needing_tests": files_needing_tests,
190
+ "recent_executions": recent_executions,
191
+ "file_tests": file_tests[:20], # Most recent 20
192
+ }
193
+ except Exception as e:
194
+ return {"error": str(e)}
195
+
122
196
  def _send_json(self, data):
123
197
  """Send JSON response."""
124
198
  content = json.dumps(data, indent=2, default=str)
@@ -183,7 +257,8 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
183
257
  try:
184
258
  tracker = CostTracker(self.empathy_dir)
185
259
  cost_summary = tracker.get_summary(30) # noqa: F841
186
- except Exception:
260
+ except Exception: # noqa: BLE001
261
+ # INTENTIONAL: Dashboard should render even if cost tracking unavailable.
187
262
  pass
188
263
 
189
264
  # Get workflow stats
@@ -198,9 +273,13 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
198
273
  if HAS_WORKFLOWS and get_workflow_stats is not None:
199
274
  try:
200
275
  workflow_stats = get_workflow_stats()
201
- except Exception:
276
+ except Exception: # noqa: BLE001
277
+ # INTENTIONAL: Dashboard should render even if workflow stats unavailable.
202
278
  pass
203
279
 
280
+ # Get test stats
281
+ test_stats = self._get_test_stats()
282
+
204
283
  return f"""<!DOCTYPE html>
205
284
  <html lang="en">
206
285
  <head>
@@ -507,6 +586,12 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
507
586
  <div class="value">${workflow_stats.get("total_savings", 0):.2f}</div>
508
587
  <div class="label">workflows + API</div>
509
588
  </div>
589
+
590
+ <div class="card {"success" if test_stats.get("failed_files", 0) == 0 else "warning"}">
591
+ <h2>Test Coverage</h2>
592
+ <div class="value">{test_stats.get("coverage_avg", 0):.0f}%</div>
593
+ <div class="label">{test_stats.get("total_files", 0)} files tracked</div>
594
+ </div>
510
595
  </div>
511
596
 
512
597
  <div class="section">
@@ -539,6 +624,11 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
539
624
  {self._render_recent_runs(workflow_stats)}
540
625
  </div>
541
626
 
627
+ <div class="section">
628
+ <h2>Test Tracking</h2>
629
+ {self._render_test_tracking(test_stats)}
630
+ </div>
631
+
542
632
  <div class="section">
543
633
  <h2>Quick Commands</h2>
544
634
  <p>Run these commands for common tasks:</p>
@@ -684,6 +774,114 @@ class DashboardHandler(http.server.BaseHTTPRequestHandler):
684
774
 
685
775
  return "".join(runs_html)
686
776
 
777
+ def _render_test_tracking(self, test_stats: dict) -> str:
778
+ """Render test tracking section."""
779
+ if "error" in test_stats:
780
+ return f'<p style="color: var(--text-muted);">{test_stats["error"]}</p>'
781
+
782
+ total = test_stats.get("total_files", 0)
783
+ passed = test_stats.get("passed_files", 0)
784
+ failed = test_stats.get("failed_files", 0)
785
+ coverage = test_stats.get("coverage_avg", 0)
786
+
787
+ if total == 0:
788
+ return '<p style="color: var(--text-muted);">No test tracking data yet. Run tests with empathy to start tracking.</p>'
789
+
790
+ # Calculate pass rate
791
+ pass_rate = (passed / total * 100) if total > 0 else 0
792
+
793
+ # Stats grid
794
+ html = f"""
795
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem;">
796
+ <div style="text-align: center; padding: 1rem; background: var(--bg); border-radius: 8px;">
797
+ <div style="font-size: 1.5rem; font-weight: 600; color: var(--success);">{passed}</div>
798
+ <div style="font-size: 0.75rem; color: var(--text-muted);">Passing</div>
799
+ </div>
800
+ <div style="text-align: center; padding: 1rem; background: var(--bg); border-radius: 8px;">
801
+ <div style="font-size: 1.5rem; font-weight: 600; color: var(--danger);">{failed}</div>
802
+ <div style="font-size: 0.75rem; color: var(--text-muted);">Failing</div>
803
+ </div>
804
+ <div style="text-align: center; padding: 1rem; background: var(--bg); border-radius: 8px;">
805
+ <div style="font-size: 1.5rem; font-weight: 600; color: var(--primary);">{pass_rate:.0f}%</div>
806
+ <div style="font-size: 0.75rem; color: var(--text-muted);">Pass Rate</div>
807
+ </div>
808
+ <div style="text-align: center; padding: 1rem; background: var(--bg); border-radius: 8px;">
809
+ <div style="font-size: 1.5rem; font-weight: 600; color: var(--warning);">{coverage:.0f}%</div>
810
+ <div style="font-size: 0.75rem; color: var(--text-muted);">Coverage</div>
811
+ </div>
812
+ </div>
813
+ """
814
+
815
+ # Files needing attention
816
+ files_needing = test_stats.get("files_needing_tests", [])
817
+ if files_needing:
818
+ html += """
819
+ <h3 style="margin-bottom: 0.5rem; font-size: 1rem;">Files Needing Attention</h3>
820
+ <table>
821
+ <thead>
822
+ <tr>
823
+ <th>File</th>
824
+ <th>Status</th>
825
+ <th>Last Run</th>
826
+ </tr>
827
+ </thead>
828
+ <tbody>
829
+ """
830
+ for file_record in files_needing[:5]:
831
+ file_path = file_record.get("file_path", "unknown")
832
+ # Truncate long paths
833
+ display_path = ("..." + file_path[-40:]) if len(file_path) > 40 else file_path
834
+ result = file_record.get("last_test_result", "unknown")
835
+ timestamp = (
836
+ file_record.get("timestamp", "")[:10] if file_record.get("timestamp") else "-"
837
+ )
838
+ status_class = "resolved" if result == "passed" else "investigating"
839
+ html += f"""
840
+ <tr>
841
+ <td title="{file_path}">{display_path}</td>
842
+ <td><span class="status {status_class}">{result}</span></td>
843
+ <td>{timestamp}</td>
844
+ </tr>
845
+ """
846
+ html += "</tbody></table>"
847
+
848
+ # Recent test executions
849
+ recent = test_stats.get("recent_executions", [])
850
+ if recent:
851
+ html += """
852
+ <h3 style="margin-top: 1.5rem; margin-bottom: 0.5rem; font-size: 1rem;">Recent Test Runs</h3>
853
+ """
854
+ for execution in recent[:5]:
855
+ suite = execution.get("test_suite", "unknown")
856
+ total_tests = execution.get("total_tests", 0)
857
+ exec_passed = execution.get("passed", 0)
858
+ exec_failed = execution.get("failed", 0)
859
+ duration = execution.get("duration_seconds", 0)
860
+ timestamp = (
861
+ execution.get("timestamp", "")[:16].replace("T", " ")
862
+ if execution.get("timestamp")
863
+ else "-"
864
+ )
865
+ success = execution.get("success", False)
866
+
867
+ status_icon = "&#10003;" if success else "&#10007;"
868
+ status_color = "var(--success)" if success else "var(--danger)"
869
+
870
+ html += f"""
871
+ <div class="recent-run">
872
+ <span style="color: {status_color};">{status_icon}</span>
873
+ <span class="name">{suite}</span>
874
+ <span class="provider">{total_tests} tests</span>
875
+ <span class="result">
876
+ <span style="color: var(--success);">{exec_passed} passed</span>
877
+ {f'<span style="color: var(--danger);">{exec_failed} failed</span>' if exec_failed > 0 else ""}
878
+ <span class="time">{duration:.1f}s | {timestamp}</span>
879
+ </span>
880
+ </div>
881
+ """
882
+
883
+ return html
884
+
687
885
 
688
886
  class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
689
887
  """Threaded HTTP server."""
@@ -1,6 +1,6 @@
1
- """Hot-Reload Infrastructure for Wizard Factory.
1
+ """Hot-Reload Infrastructure for Workflow Factory.
2
2
 
3
- Enables real-time wizard reloading during development without server restarts.
3
+ Enables real-time workflow reloading during development without server restarts.
4
4
 
5
5
  Features:
6
6
  - File system monitoring with watchdog
@@ -13,7 +13,7 @@ Usage:
13
13
  from hot_reload.integration import HotReloadIntegration
14
14
 
15
15
  app = FastAPI()
16
- hot_reload = HotReloadIntegration(app, register_wizard)
16
+ hot_reload = HotReloadIntegration(app, register_workflow)
17
17
 
18
18
  @app.on_event("startup")
19
19
  async def startup():
@@ -35,8 +35,8 @@ Licensed under Fair Source 0.9
35
35
 
36
36
  from .config import HotReloadConfig, get_hot_reload_config
37
37
  from .integration import HotReloadIntegration
38
- from .reloader import ReloadResult, WizardReloader
39
- from .watcher import WizardFileWatcher
38
+ from .reloader import ReloadResult, WorkflowReloader
39
+ from .watcher import WorkflowFileWatcher
40
40
  from .websocket import (
41
41
  ReloadNotificationManager,
42
42
  create_notification_callback,
@@ -45,8 +45,8 @@ from .websocket import (
45
45
 
46
46
  __all__ = [
47
47
  # Core components
48
- "WizardFileWatcher",
49
- "WizardReloader",
48
+ "WorkflowFileWatcher",
49
+ "WorkflowReloader",
50
50
  "ReloadResult",
51
51
  # WebSocket
52
52
  "ReloadNotificationManager",
@@ -16,7 +16,7 @@ class HotReloadConfig:
16
16
  # Development mode (enables hot-reload)
17
17
  self.enabled = os.getenv("HOT_RELOAD_ENABLED", "false").lower() == "true"
18
18
 
19
- # Wizard directories to watch
19
+ # Workflow directories to watch
20
20
  self.watch_dirs = self._get_watch_dirs()
21
21
 
22
22
  # WebSocket endpoint for notifications
@@ -26,20 +26,19 @@ class HotReloadConfig:
26
26
  self.reload_delay = float(os.getenv("HOT_RELOAD_DELAY", "0.5"))
27
27
 
28
28
  def _get_watch_dirs(self) -> list[Path]:
29
- """Get directories to watch for wizard changes.
29
+ """Get directories to watch for workflow changes.
30
30
 
31
31
  Returns:
32
32
  List of directories to watch
33
33
 
34
34
  """
35
- # Default wizard directories
35
+ # Default workflow directories
36
36
  project_root = Path(__file__).parent.parent
37
37
 
38
38
  default_dirs = [
39
- project_root / "wizards",
40
- project_root / "coach_wizards",
41
- project_root / "empathy_software_plugin" / "wizards",
42
- project_root / "empathy_llm_toolkit" / "wizards",
39
+ project_root / "workflows",
40
+ project_root / "empathy_software_plugin" / "workflows",
41
+ project_root / "empathy_llm_toolkit" / "workflows",
43
42
  ]
44
43
 
45
44
  # Filter to only existing directories
@@ -1,6 +1,6 @@
1
- """Integration example for hot-reload with wizard API.
1
+ """Integration example for hot-reload with workflow API.
2
2
 
3
- Shows how to integrate hot-reload into the existing wizard_api.py.
3
+ Shows how to integrate hot-reload into the existing workflow_api.py.
4
4
 
5
5
  Copyright 2025 Smart AI Memory, LLC
6
6
  Licensed under Fair Source 0.9
@@ -12,17 +12,17 @@ from collections.abc import Callable
12
12
  from fastapi import FastAPI, WebSocket, WebSocketDisconnect
13
13
 
14
14
  from .config import get_hot_reload_config
15
- from .reloader import WizardReloader
16
- from .watcher import WizardFileWatcher
15
+ from .reloader import WorkflowReloader
16
+ from .watcher import WorkflowFileWatcher
17
17
  from .websocket import create_notification_callback, get_notification_manager
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
22
  class HotReloadIntegration:
23
- """Integrates hot-reload with wizard API.
23
+ """Integrates hot-reload with workflow API.
24
24
 
25
- Example usage in wizard_api.py:
25
+ Example usage in workflow_api.py:
26
26
 
27
27
  from hot_reload.integration import HotReloadIntegration
28
28
 
@@ -30,11 +30,11 @@ class HotReloadIntegration:
30
30
  app = FastAPI()
31
31
 
32
32
  # Initialize hot-reload (if enabled)
33
- hot_reload = HotReloadIntegration(app, register_wizard)
33
+ hot_reload = HotReloadIntegration(app, register_workflow)
34
34
 
35
35
  @app.on_event("startup")
36
36
  async def startup_event():
37
- init_wizards() # Initialize wizards
37
+ init_workflows() # Initialize workflows
38
38
  hot_reload.start() # Start hot-reload watcher
39
39
 
40
40
  @app.on_event("shutdown")
@@ -52,7 +52,7 @@ class HotReloadIntegration:
52
52
 
53
53
  Args:
54
54
  app: FastAPI application instance
55
- register_callback: Function to register wizard (wizard_id, wizard_class) -> bool
55
+ register_callback: Function to register workflow (workflow_id, workflow_class) -> bool
56
56
 
57
57
  """
58
58
  self.app = app
@@ -61,32 +61,32 @@ class HotReloadIntegration:
61
61
 
62
62
  # Initialize components
63
63
  self.notification_callback = create_notification_callback()
64
- self.reloader = WizardReloader(
65
- register_callback=self._register_wizard_wrapper,
64
+ self.reloader = WorkflowReloader(
65
+ register_callback=self._register_workflow_wrapper,
66
66
  notification_callback=self.notification_callback,
67
67
  )
68
68
 
69
- self.watcher: WizardFileWatcher | None = None
69
+ self.watcher: WorkflowFileWatcher | None = None
70
70
 
71
71
  # Add WebSocket endpoint to app
72
72
  if self.config.enabled:
73
73
  self._setup_websocket_endpoint()
74
74
 
75
- def _register_wizard_wrapper(self, wizard_id: str, wizard_class: type) -> bool:
75
+ def _register_workflow_wrapper(self, workflow_id: str, workflow_class: type) -> bool:
76
76
  """Wrapper for register callback that handles errors.
77
77
 
78
78
  Args:
79
- wizard_id: Wizard identifier
80
- wizard_class: Wizard class to register
79
+ workflow_id: Workflow identifier
80
+ workflow_class: Workflow class to register
81
81
 
82
82
  Returns:
83
83
  True if registration succeeded
84
84
 
85
85
  """
86
86
  try:
87
- return self.register_callback(wizard_id, wizard_class)
87
+ return self.register_callback(workflow_id, workflow_class)
88
88
  except Exception as e:
89
- logger.error(f"Error registering wizard {wizard_id}: {e}")
89
+ logger.error(f"Error registering workflow {workflow_id}: {e}")
90
90
  return False
91
91
 
92
92
  def _setup_websocket_endpoint(self) -> None:
@@ -120,7 +120,7 @@ class HotReloadIntegration:
120
120
  return
121
121
 
122
122
  if not self.config.watch_dirs:
123
- logger.warning("No wizard directories found to watch")
123
+ logger.warning("No workflow directories found to watch")
124
124
  return
125
125
 
126
126
  if self.watcher and self.watcher.is_running():
@@ -128,8 +128,8 @@ class HotReloadIntegration:
128
128
  return
129
129
 
130
130
  # Create watcher
131
- self.watcher = WizardFileWatcher(
132
- wizard_dirs=self.config.watch_dirs,
131
+ self.watcher = WorkflowFileWatcher(
132
+ workflow_dirs=self.config.watch_dirs,
133
133
  reload_callback=self._on_file_change,
134
134
  )
135
135
 
@@ -145,18 +145,18 @@ class HotReloadIntegration:
145
145
  self.watcher = None
146
146
  logger.info("Hot-reload stopped")
147
147
 
148
- def _on_file_change(self, wizard_id: str, file_path: str) -> None:
148
+ def _on_file_change(self, workflow_id: str, file_path: str) -> None:
149
149
  """Handle file change event.
150
150
 
151
151
  Args:
152
- wizard_id: ID of wizard that changed
152
+ workflow_id: ID of workflow that changed
153
153
  file_path: Path to changed file
154
154
 
155
155
  """
156
- logger.info(f"File change detected: {wizard_id} ({file_path})")
156
+ logger.info(f"File change detected: {workflow_id} ({file_path})")
157
157
 
158
- # Reload wizard
159
- result = self.reloader.reload_wizard(wizard_id, file_path)
158
+ # Reload workflow
159
+ result = self.reloader.reload_workflow(workflow_id, file_path)
160
160
 
161
161
  if result.success:
162
162
  logger.info(f"✓ {result.message}")
@@ -180,25 +180,25 @@ class HotReloadIntegration:
180
180
  }
181
181
 
182
182
 
183
- # Example usage in wizard_api.py:
183
+ # Example usage in workflow_api.py:
184
184
  """
185
185
  from fastapi import FastAPI
186
186
  from hot_reload.integration import HotReloadIntegration
187
187
 
188
- app = FastAPI(title="Empathy Wizard API")
188
+ app = FastAPI(title="Empathy Workflow API")
189
189
 
190
190
  # Global hot-reload instance
191
191
  hot_reload = None
192
192
 
193
193
 
194
- def register_wizard(wizard_id: str, wizard_class: type, *args, **kwargs) -> bool:
195
- '''Register wizard with WIZARDS dict'''
194
+ def register_workflow(workflow_id: str, workflow_class: type, *args, **kwargs) -> bool:
195
+ '''Register workflow with WORKFLOWS dict'''
196
196
  try:
197
- WIZARDS[wizard_id] = wizard_class(*args, **kwargs)
198
- logger.info(f"✓ Registered wizard: {wizard_id}")
197
+ WORKFLOWS[workflow_id] = workflow_class(*args, **kwargs)
198
+ logger.info(f"✓ Registered workflow: {workflow_id}")
199
199
  return True
200
200
  except Exception as e:
201
- logger.error(f"Failed to register {wizard_id}: {e}")
201
+ logger.error(f"Failed to register {workflow_id}: {e}")
202
202
  return False
203
203
 
204
204
 
@@ -206,11 +206,11 @@ def register_wizard(wizard_id: str, wizard_class: type, *args, **kwargs) -> bool
206
206
  async def startup_event():
207
207
  global hot_reload
208
208
 
209
- # Initialize wizards
210
- init_wizards()
209
+ # Initialize workflows
210
+ init_workflows()
211
211
 
212
212
  # Start hot-reload
213
- hot_reload = HotReloadIntegration(app, register_wizard)
213
+ hot_reload = HotReloadIntegration(app, register_workflow)
214
214
  hot_reload.start()
215
215
 
216
216