empathy-framework 3.2.3__py3-none-any.whl → 3.8.2__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 (328) hide show
  1. coach_wizards/__init__.py +11 -12
  2. coach_wizards/accessibility_wizard.py +12 -12
  3. coach_wizards/api_wizard.py +12 -12
  4. coach_wizards/base_wizard.py +26 -20
  5. coach_wizards/cicd_wizard.py +15 -13
  6. coach_wizards/code_reviewer_README.md +60 -0
  7. coach_wizards/code_reviewer_wizard.py +180 -0
  8. coach_wizards/compliance_wizard.py +12 -12
  9. coach_wizards/database_wizard.py +12 -12
  10. coach_wizards/debugging_wizard.py +12 -12
  11. coach_wizards/documentation_wizard.py +12 -12
  12. coach_wizards/generate_wizards.py +1 -2
  13. coach_wizards/localization_wizard.py +101 -19
  14. coach_wizards/migration_wizard.py +12 -12
  15. coach_wizards/monitoring_wizard.py +12 -12
  16. coach_wizards/observability_wizard.py +12 -12
  17. coach_wizards/performance_wizard.py +12 -12
  18. coach_wizards/prompt_engineering_wizard.py +22 -25
  19. coach_wizards/refactoring_wizard.py +12 -12
  20. coach_wizards/scaling_wizard.py +12 -12
  21. coach_wizards/security_wizard.py +12 -12
  22. coach_wizards/testing_wizard.py +12 -12
  23. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/METADATA +513 -58
  24. empathy_framework-3.8.2.dist-info/RECORD +333 -0
  25. empathy_framework-3.8.2.dist-info/entry_points.txt +22 -0
  26. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/top_level.txt +5 -1
  27. empathy_healthcare_plugin/__init__.py +1 -2
  28. empathy_healthcare_plugin/monitors/__init__.py +9 -0
  29. empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
  30. empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
  31. empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
  32. empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
  33. empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
  34. empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
  35. empathy_llm_toolkit/__init__.py +7 -7
  36. empathy_llm_toolkit/agent_factory/__init__.py +53 -0
  37. empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
  38. empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
  39. empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
  40. empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
  41. empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
  42. empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
  43. empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
  44. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
  45. empathy_llm_toolkit/agent_factory/base.py +305 -0
  46. empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
  47. empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
  48. empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
  49. empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
  50. empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
  51. empathy_llm_toolkit/agent_factory/decorators.py +286 -0
  52. empathy_llm_toolkit/agent_factory/factory.py +558 -0
  53. empathy_llm_toolkit/agent_factory/framework.py +192 -0
  54. empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
  55. empathy_llm_toolkit/agent_factory/resilient.py +320 -0
  56. empathy_llm_toolkit/claude_memory.py +14 -15
  57. empathy_llm_toolkit/cli/__init__.py +8 -0
  58. empathy_llm_toolkit/cli/sync_claude.py +487 -0
  59. empathy_llm_toolkit/code_health.py +177 -22
  60. empathy_llm_toolkit/config/__init__.py +29 -0
  61. empathy_llm_toolkit/config/unified.py +295 -0
  62. empathy_llm_toolkit/contextual_patterns.py +11 -12
  63. empathy_llm_toolkit/core.py +51 -49
  64. empathy_llm_toolkit/git_pattern_extractor.py +16 -12
  65. empathy_llm_toolkit/levels.py +6 -13
  66. empathy_llm_toolkit/pattern_confidence.py +14 -18
  67. empathy_llm_toolkit/pattern_resolver.py +10 -12
  68. empathy_llm_toolkit/pattern_summary.py +13 -11
  69. empathy_llm_toolkit/providers.py +194 -28
  70. empathy_llm_toolkit/routing/__init__.py +32 -0
  71. empathy_llm_toolkit/routing/model_router.py +362 -0
  72. empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
  73. empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
  74. empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  75. empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
  76. empathy_llm_toolkit/security/README.md +262 -0
  77. empathy_llm_toolkit/security/__init__.py +62 -0
  78. empathy_llm_toolkit/security/audit_logger.py +929 -0
  79. empathy_llm_toolkit/security/audit_logger_example.py +152 -0
  80. empathy_llm_toolkit/security/pii_scrubber.py +640 -0
  81. empathy_llm_toolkit/security/secrets_detector.py +678 -0
  82. empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
  83. empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
  84. empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
  85. empathy_llm_toolkit/session_status.py +18 -20
  86. empathy_llm_toolkit/state.py +20 -21
  87. empathy_llm_toolkit/wizards/__init__.py +38 -0
  88. empathy_llm_toolkit/wizards/base_wizard.py +364 -0
  89. empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
  90. empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
  91. empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
  92. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
  93. empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
  94. empathy_os/__init__.py +76 -77
  95. empathy_os/adaptive/__init__.py +13 -0
  96. empathy_os/adaptive/task_complexity.py +127 -0
  97. empathy_os/{monitoring.py → agent_monitoring.py} +27 -27
  98. empathy_os/cache/__init__.py +117 -0
  99. empathy_os/cache/base.py +166 -0
  100. empathy_os/cache/dependency_manager.py +253 -0
  101. empathy_os/cache/hash_only.py +248 -0
  102. empathy_os/cache/hybrid.py +390 -0
  103. empathy_os/cache/storage.py +282 -0
  104. empathy_os/cli.py +515 -109
  105. empathy_os/cli_unified.py +189 -42
  106. empathy_os/config/__init__.py +63 -0
  107. empathy_os/config/xml_config.py +239 -0
  108. empathy_os/config.py +87 -36
  109. empathy_os/coordination.py +48 -54
  110. empathy_os/core.py +90 -99
  111. empathy_os/cost_tracker.py +20 -23
  112. empathy_os/dashboard/__init__.py +15 -0
  113. empathy_os/dashboard/server.py +743 -0
  114. empathy_os/discovery.py +9 -11
  115. empathy_os/emergence.py +20 -21
  116. empathy_os/exceptions.py +18 -30
  117. empathy_os/feedback_loops.py +27 -30
  118. empathy_os/levels.py +31 -34
  119. empathy_os/leverage_points.py +27 -28
  120. empathy_os/logging_config.py +11 -12
  121. empathy_os/memory/__init__.py +195 -0
  122. empathy_os/memory/claude_memory.py +466 -0
  123. empathy_os/memory/config.py +224 -0
  124. empathy_os/memory/control_panel.py +1298 -0
  125. empathy_os/memory/edges.py +179 -0
  126. empathy_os/memory/graph.py +567 -0
  127. empathy_os/memory/long_term.py +1194 -0
  128. empathy_os/memory/nodes.py +179 -0
  129. empathy_os/memory/redis_bootstrap.py +540 -0
  130. empathy_os/memory/security/__init__.py +31 -0
  131. empathy_os/memory/security/audit_logger.py +930 -0
  132. empathy_os/memory/security/pii_scrubber.py +640 -0
  133. empathy_os/memory/security/secrets_detector.py +678 -0
  134. empathy_os/memory/short_term.py +2119 -0
  135. empathy_os/memory/storage/__init__.py +15 -0
  136. empathy_os/memory/summary_index.py +583 -0
  137. empathy_os/memory/unified.py +619 -0
  138. empathy_os/metrics/__init__.py +12 -0
  139. empathy_os/metrics/prompt_metrics.py +190 -0
  140. empathy_os/models/__init__.py +136 -0
  141. empathy_os/models/__main__.py +13 -0
  142. empathy_os/models/cli.py +655 -0
  143. empathy_os/models/empathy_executor.py +354 -0
  144. empathy_os/models/executor.py +252 -0
  145. empathy_os/models/fallback.py +671 -0
  146. empathy_os/models/provider_config.py +563 -0
  147. empathy_os/models/registry.py +382 -0
  148. empathy_os/models/tasks.py +302 -0
  149. empathy_os/models/telemetry.py +548 -0
  150. empathy_os/models/token_estimator.py +378 -0
  151. empathy_os/models/validation.py +274 -0
  152. empathy_os/monitoring/__init__.py +52 -0
  153. empathy_os/monitoring/alerts.py +23 -0
  154. empathy_os/monitoring/alerts_cli.py +268 -0
  155. empathy_os/monitoring/multi_backend.py +271 -0
  156. empathy_os/monitoring/otel_backend.py +363 -0
  157. empathy_os/optimization/__init__.py +19 -0
  158. empathy_os/optimization/context_optimizer.py +272 -0
  159. empathy_os/pattern_library.py +29 -28
  160. empathy_os/persistence.py +30 -34
  161. empathy_os/platform_utils.py +261 -0
  162. empathy_os/plugins/__init__.py +28 -0
  163. empathy_os/plugins/base.py +361 -0
  164. empathy_os/plugins/registry.py +268 -0
  165. empathy_os/project_index/__init__.py +30 -0
  166. empathy_os/project_index/cli.py +335 -0
  167. empathy_os/project_index/crew_integration.py +430 -0
  168. empathy_os/project_index/index.py +425 -0
  169. empathy_os/project_index/models.py +501 -0
  170. empathy_os/project_index/reports.py +473 -0
  171. empathy_os/project_index/scanner.py +538 -0
  172. empathy_os/prompts/__init__.py +61 -0
  173. empathy_os/prompts/config.py +77 -0
  174. empathy_os/prompts/context.py +177 -0
  175. empathy_os/prompts/parser.py +285 -0
  176. empathy_os/prompts/registry.py +313 -0
  177. empathy_os/prompts/templates.py +208 -0
  178. empathy_os/redis_config.py +144 -58
  179. empathy_os/redis_memory.py +53 -56
  180. empathy_os/resilience/__init__.py +56 -0
  181. empathy_os/resilience/circuit_breaker.py +256 -0
  182. empathy_os/resilience/fallback.py +179 -0
  183. empathy_os/resilience/health.py +300 -0
  184. empathy_os/resilience/retry.py +209 -0
  185. empathy_os/resilience/timeout.py +135 -0
  186. empathy_os/routing/__init__.py +43 -0
  187. empathy_os/routing/chain_executor.py +433 -0
  188. empathy_os/routing/classifier.py +217 -0
  189. empathy_os/routing/smart_router.py +234 -0
  190. empathy_os/routing/wizard_registry.py +307 -0
  191. empathy_os/templates.py +12 -11
  192. empathy_os/trust/__init__.py +28 -0
  193. empathy_os/trust/circuit_breaker.py +579 -0
  194. empathy_os/trust_building.py +44 -36
  195. empathy_os/validation/__init__.py +19 -0
  196. empathy_os/validation/xml_validator.py +281 -0
  197. empathy_os/wizard_factory_cli.py +170 -0
  198. empathy_os/{workflows.py → workflow_commands.py} +123 -31
  199. empathy_os/workflows/__init__.py +360 -0
  200. empathy_os/workflows/base.py +1660 -0
  201. empathy_os/workflows/bug_predict.py +962 -0
  202. empathy_os/workflows/code_review.py +960 -0
  203. empathy_os/workflows/code_review_adapters.py +310 -0
  204. empathy_os/workflows/code_review_pipeline.py +720 -0
  205. empathy_os/workflows/config.py +600 -0
  206. empathy_os/workflows/dependency_check.py +648 -0
  207. empathy_os/workflows/document_gen.py +1069 -0
  208. empathy_os/workflows/documentation_orchestrator.py +1205 -0
  209. empathy_os/workflows/health_check.py +679 -0
  210. empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
  211. empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
  212. empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
  213. empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
  214. empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
  215. empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
  216. empathy_os/workflows/manage_documentation.py +804 -0
  217. empathy_os/workflows/new_sample_workflow1.py +146 -0
  218. empathy_os/workflows/new_sample_workflow1_README.md +150 -0
  219. empathy_os/workflows/perf_audit.py +687 -0
  220. empathy_os/workflows/pr_review.py +748 -0
  221. empathy_os/workflows/progress.py +445 -0
  222. empathy_os/workflows/progress_server.py +322 -0
  223. empathy_os/workflows/refactor_plan.py +693 -0
  224. empathy_os/workflows/release_prep.py +808 -0
  225. empathy_os/workflows/research_synthesis.py +404 -0
  226. empathy_os/workflows/secure_release.py +585 -0
  227. empathy_os/workflows/security_adapters.py +297 -0
  228. empathy_os/workflows/security_audit.py +1046 -0
  229. empathy_os/workflows/step_config.py +234 -0
  230. empathy_os/workflows/test5.py +125 -0
  231. empathy_os/workflows/test5_README.md +158 -0
  232. empathy_os/workflows/test_gen.py +1855 -0
  233. empathy_os/workflows/test_lifecycle.py +526 -0
  234. empathy_os/workflows/test_maintenance.py +626 -0
  235. empathy_os/workflows/test_maintenance_cli.py +590 -0
  236. empathy_os/workflows/test_maintenance_crew.py +821 -0
  237. empathy_os/workflows/xml_enhanced_crew.py +285 -0
  238. empathy_software_plugin/__init__.py +1 -2
  239. empathy_software_plugin/cli/__init__.py +120 -0
  240. empathy_software_plugin/cli/inspect.py +362 -0
  241. empathy_software_plugin/cli.py +35 -26
  242. empathy_software_plugin/plugin.py +4 -8
  243. empathy_software_plugin/wizards/__init__.py +42 -0
  244. empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
  245. empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
  246. empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
  247. empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
  248. empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
  249. empathy_software_plugin/wizards/base_wizard.py +288 -0
  250. empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
  251. empathy_software_plugin/wizards/code_review_wizard.py +606 -0
  252. empathy_software_plugin/wizards/debugging/__init__.py +50 -0
  253. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
  254. empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
  255. empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
  256. empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
  257. empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
  258. empathy_software_plugin/wizards/debugging/verification.py +369 -0
  259. empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
  260. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
  261. empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
  262. empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
  263. empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
  264. empathy_software_plugin/wizards/performance/__init__.py +9 -0
  265. empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
  266. empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
  267. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
  268. empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
  269. empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
  270. empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
  271. empathy_software_plugin/wizards/security/__init__.py +32 -0
  272. empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
  273. empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
  274. empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
  275. empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
  276. empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
  277. empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
  278. empathy_software_plugin/wizards/testing/__init__.py +27 -0
  279. empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
  280. empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
  281. empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
  282. empathy_software_plugin/wizards/testing_wizard.py +274 -0
  283. hot_reload/README.md +473 -0
  284. hot_reload/__init__.py +62 -0
  285. hot_reload/config.py +84 -0
  286. hot_reload/integration.py +228 -0
  287. hot_reload/reloader.py +298 -0
  288. hot_reload/watcher.py +179 -0
  289. hot_reload/websocket.py +176 -0
  290. scaffolding/README.md +589 -0
  291. scaffolding/__init__.py +35 -0
  292. scaffolding/__main__.py +14 -0
  293. scaffolding/cli.py +240 -0
  294. test_generator/__init__.py +38 -0
  295. test_generator/__main__.py +14 -0
  296. test_generator/cli.py +226 -0
  297. test_generator/generator.py +325 -0
  298. test_generator/risk_analyzer.py +216 -0
  299. workflow_patterns/__init__.py +33 -0
  300. workflow_patterns/behavior.py +249 -0
  301. workflow_patterns/core.py +76 -0
  302. workflow_patterns/output.py +99 -0
  303. workflow_patterns/registry.py +255 -0
  304. workflow_patterns/structural.py +288 -0
  305. workflow_scaffolding/__init__.py +11 -0
  306. workflow_scaffolding/__main__.py +12 -0
  307. workflow_scaffolding/cli.py +206 -0
  308. workflow_scaffolding/generator.py +265 -0
  309. agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
  310. agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
  311. agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
  312. agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
  313. agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
  314. agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
  315. agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
  316. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
  317. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
  318. agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
  319. agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
  320. agents/compliance_anticipation_agent.py +0 -1427
  321. agents/epic_integration_wizard.py +0 -541
  322. agents/trust_building_behaviors.py +0 -891
  323. empathy_framework-3.2.3.dist-info/RECORD +0 -104
  324. empathy_framework-3.2.3.dist-info/entry_points.txt +0 -7
  325. empathy_llm_toolkit/htmlcov/status.json +0 -1
  326. empathy_llm_toolkit/security/htmlcov/status.json +0 -1
  327. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/WHEEL +0 -0
  328. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,743 @@
1
+ """Dashboard Server for Empathy Framework
2
+
3
+ Lightweight web server for viewing patterns, costs, and health.
4
+ Uses built-in http.server to avoid external dependencies.
5
+
6
+ Copyright 2025 Smart-AI-Memory
7
+ Licensed under Fair Source License 0.9
8
+ """
9
+
10
+ import http.server
11
+ import json
12
+ import socketserver
13
+ import threading
14
+ import webbrowser
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+ from urllib.parse import urlparse
18
+
19
+ # Try to import optional dependencies
20
+ try:
21
+ from empathy_os.cost_tracker import CostTracker
22
+
23
+ HAS_COST_TRACKER = True
24
+ except ImportError:
25
+ CostTracker = None # type: ignore[misc, assignment]
26
+ HAS_COST_TRACKER = False
27
+
28
+ try:
29
+ from empathy_os.discovery import DiscoveryEngine
30
+
31
+ HAS_DISCOVERY = True
32
+ except ImportError:
33
+ DiscoveryEngine = None # type: ignore[misc, assignment]
34
+ HAS_DISCOVERY = False
35
+
36
+ try:
37
+ from empathy_os.workflows import get_workflow_stats, list_workflows
38
+
39
+ HAS_WORKFLOWS = True
40
+ except ImportError:
41
+ get_workflow_stats = None # type: ignore[assignment]
42
+ list_workflows = None # type: ignore[assignment]
43
+ HAS_WORKFLOWS = False
44
+
45
+
46
+ class DashboardHandler(http.server.BaseHTTPRequestHandler):
47
+ """HTTP request handler for the dashboard."""
48
+
49
+ patterns_dir = "./patterns"
50
+ empathy_dir = ".empathy"
51
+
52
+ def log_message(self, format, *args):
53
+ """Suppress default logging."""
54
+
55
+ def do_GET(self):
56
+ """Handle GET requests."""
57
+ parsed = urlparse(self.path)
58
+ path = parsed.path
59
+
60
+ if path == "/" or path == "/index.html":
61
+ self._serve_dashboard()
62
+ elif path == "/api/patterns":
63
+ self._serve_patterns()
64
+ elif path == "/api/costs":
65
+ self._serve_costs()
66
+ elif path == "/api/stats":
67
+ self._serve_stats()
68
+ elif path == "/api/health":
69
+ self._serve_health()
70
+ elif path == "/api/workflows":
71
+ self._serve_workflows()
72
+ else:
73
+ self.send_error(404, "Not Found")
74
+
75
+ def _serve_dashboard(self):
76
+ """Serve the main dashboard HTML."""
77
+ html = self._generate_dashboard_html()
78
+ self.send_response(200)
79
+ self.send_header("Content-type", "text/html")
80
+ self.send_header("Content-Length", len(html))
81
+ self.end_headers()
82
+ self.wfile.write(html.encode())
83
+
84
+ def _serve_patterns(self):
85
+ """Serve patterns as JSON."""
86
+ patterns = self._load_patterns()
87
+ self._send_json(patterns)
88
+
89
+ def _serve_costs(self):
90
+ """Serve cost data as JSON."""
91
+ if HAS_COST_TRACKER and CostTracker is not None:
92
+ tracker = CostTracker(self.empathy_dir)
93
+ data = tracker.get_summary(30)
94
+ else:
95
+ data = {"error": "Cost tracking not available"}
96
+ self._send_json(data)
97
+
98
+ def _serve_stats(self):
99
+ """Serve discovery stats as JSON."""
100
+ if HAS_DISCOVERY and DiscoveryEngine is not None:
101
+ engine = DiscoveryEngine(self.empathy_dir)
102
+ data = engine.get_stats()
103
+ else:
104
+ data = {"error": "Discovery not available"}
105
+ self._send_json(data)
106
+
107
+ def _serve_health(self):
108
+ """Serve health check."""
109
+ self._send_json({"status": "healthy", "timestamp": datetime.now().isoformat()})
110
+
111
+ def _serve_workflows(self):
112
+ """Serve workflow stats as JSON."""
113
+ if HAS_WORKFLOWS and get_workflow_stats is not None:
114
+ data = get_workflow_stats()
115
+ # Add available workflows
116
+ if list_workflows is not None:
117
+ data["available_workflows"] = list_workflows()
118
+ else:
119
+ data = {"error": "Workflows not available"}
120
+ self._send_json(data)
121
+
122
+ def _send_json(self, data):
123
+ """Send JSON response."""
124
+ content = json.dumps(data, indent=2, default=str)
125
+ self.send_response(200)
126
+ self.send_header("Content-type", "application/json")
127
+ self.send_header("Content-Length", len(content))
128
+ self.end_headers()
129
+ self.wfile.write(content.encode())
130
+
131
+ def _load_patterns(self) -> dict:
132
+ """Load patterns from disk."""
133
+ patterns = {
134
+ "debugging": [],
135
+ "security": [],
136
+ "tech_debt": {"snapshots": []},
137
+ "inspection": [],
138
+ }
139
+
140
+ patterns_path = Path(self.patterns_dir)
141
+ if not patterns_path.exists():
142
+ return patterns
143
+
144
+ for name in ["debugging", "security", "tech_debt", "inspection"]:
145
+ file_path = patterns_path / f"{name}.json"
146
+ if file_path.exists():
147
+ try:
148
+ with open(file_path) as f:
149
+ data = json.load(f)
150
+ patterns[name] = data
151
+ except (OSError, json.JSONDecodeError):
152
+ pass
153
+
154
+ return patterns
155
+
156
+ def _generate_dashboard_html(self) -> str:
157
+ """Generate the dashboard HTML page."""
158
+ patterns = self._load_patterns()
159
+
160
+ # Count patterns (handle both dict and list formats)
161
+ debugging_data = patterns.get("debugging", {})
162
+ if isinstance(debugging_data, dict):
163
+ bug_count = len(debugging_data.get("patterns", []))
164
+ else:
165
+ bug_count = len(debugging_data) if isinstance(debugging_data, list) else 0
166
+
167
+ security_data = patterns.get("security", {})
168
+ if isinstance(security_data, dict):
169
+ security_count = len(security_data.get("decisions", []))
170
+ else:
171
+ security_count = len(security_data) if isinstance(security_data, list) else 0
172
+
173
+ debt_items = 0
174
+ tech_debt_data = patterns.get("tech_debt", {})
175
+ if isinstance(tech_debt_data, dict):
176
+ snapshots = tech_debt_data.get("snapshots", [])
177
+ if snapshots:
178
+ debt_items = snapshots[-1].get("total_items", 0)
179
+
180
+ # Get cost summary
181
+ cost_summary = {"savings": 0, "savings_percent": 0, "requests": 0}
182
+ if CostTracker is not None:
183
+ try:
184
+ tracker = CostTracker(self.empathy_dir)
185
+ cost_summary = tracker.get_summary(30) # noqa: F841
186
+ except Exception:
187
+ pass
188
+
189
+ # Get workflow stats
190
+ workflow_stats = {
191
+ "total_runs": 0,
192
+ "by_workflow": {},
193
+ "by_tier": {"cheap": 0, "capable": 0, "premium": 0},
194
+ "recent_runs": [],
195
+ "total_savings": 0.0,
196
+ "avg_savings_percent": 0.0,
197
+ }
198
+ if HAS_WORKFLOWS and get_workflow_stats is not None:
199
+ try:
200
+ workflow_stats = get_workflow_stats()
201
+ except Exception:
202
+ pass
203
+
204
+ return f"""<!DOCTYPE html>
205
+ <html lang="en">
206
+ <head>
207
+ <meta charset="UTF-8">
208
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
209
+ <title>Empathy Framework Dashboard</title>
210
+ <style>
211
+ :root {{
212
+ --primary: #4f46e5;
213
+ --success: #10b981;
214
+ --warning: #f59e0b;
215
+ --danger: #ef4444;
216
+ --bg: #f3f4f6;
217
+ --card-bg: #ffffff;
218
+ --text: #1f2937;
219
+ --text-muted: #6b7280;
220
+ }}
221
+
222
+ * {{
223
+ margin: 0;
224
+ padding: 0;
225
+ box-sizing: border-box;
226
+ }}
227
+
228
+ body {{
229
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
230
+ background: var(--bg);
231
+ color: var(--text);
232
+ line-height: 1.6;
233
+ }}
234
+
235
+ .container {{
236
+ max-width: 1200px;
237
+ margin: 0 auto;
238
+ padding: 2rem;
239
+ }}
240
+
241
+ header {{
242
+ text-align: center;
243
+ margin-bottom: 2rem;
244
+ }}
245
+
246
+ h1 {{
247
+ color: var(--primary);
248
+ font-size: 2rem;
249
+ margin-bottom: 0.5rem;
250
+ }}
251
+
252
+ .subtitle {{
253
+ color: var(--text-muted);
254
+ }}
255
+
256
+ .grid {{
257
+ display: grid;
258
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
259
+ gap: 1.5rem;
260
+ margin-bottom: 2rem;
261
+ }}
262
+
263
+ .card {{
264
+ background: var(--card-bg);
265
+ border-radius: 12px;
266
+ padding: 1.5rem;
267
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
268
+ }}
269
+
270
+ .card h2 {{
271
+ font-size: 0.875rem;
272
+ color: var(--text-muted);
273
+ text-transform: uppercase;
274
+ letter-spacing: 0.05em;
275
+ margin-bottom: 0.5rem;
276
+ }}
277
+
278
+ .card .value {{
279
+ font-size: 2.5rem;
280
+ font-weight: 700;
281
+ color: var(--text);
282
+ }}
283
+
284
+ .card .label {{
285
+ color: var(--text-muted);
286
+ font-size: 0.875rem;
287
+ }}
288
+
289
+ .card.success .value {{
290
+ color: var(--success);
291
+ }}
292
+
293
+ .card.warning .value {{
294
+ color: var(--warning);
295
+ }}
296
+
297
+ .section {{
298
+ background: var(--card-bg);
299
+ border-radius: 12px;
300
+ padding: 1.5rem;
301
+ margin-bottom: 1.5rem;
302
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
303
+ }}
304
+
305
+ .section h2 {{
306
+ font-size: 1.25rem;
307
+ margin-bottom: 1rem;
308
+ color: var(--text);
309
+ }}
310
+
311
+ table {{
312
+ width: 100%;
313
+ border-collapse: collapse;
314
+ }}
315
+
316
+ th, td {{
317
+ text-align: left;
318
+ padding: 0.75rem;
319
+ border-bottom: 1px solid var(--bg);
320
+ }}
321
+
322
+ th {{
323
+ color: var(--text-muted);
324
+ font-weight: 500;
325
+ font-size: 0.875rem;
326
+ }}
327
+
328
+ .status {{
329
+ display: inline-block;
330
+ padding: 0.25rem 0.75rem;
331
+ border-radius: 9999px;
332
+ font-size: 0.75rem;
333
+ font-weight: 500;
334
+ }}
335
+
336
+ .status.resolved {{
337
+ background: #d1fae5;
338
+ color: #065f46;
339
+ }}
340
+
341
+ .status.investigating {{
342
+ background: #fef3c7;
343
+ color: #92400e;
344
+ }}
345
+
346
+ .commands {{
347
+ display: flex;
348
+ flex-wrap: wrap;
349
+ gap: 0.5rem;
350
+ margin-top: 1rem;
351
+ }}
352
+
353
+ .command {{
354
+ background: var(--bg);
355
+ padding: 0.5rem 1rem;
356
+ border-radius: 6px;
357
+ font-family: monospace;
358
+ font-size: 0.875rem;
359
+ }}
360
+
361
+ .workflow-grid {{
362
+ display: grid;
363
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
364
+ gap: 1rem;
365
+ margin-bottom: 1.5rem;
366
+ }}
367
+
368
+ .workflow-card {{
369
+ background: var(--bg);
370
+ border-radius: 8px;
371
+ padding: 1rem;
372
+ text-align: center;
373
+ }}
374
+
375
+ .workflow-card h3 {{
376
+ font-size: 0.875rem;
377
+ color: var(--text);
378
+ margin-bottom: 0.5rem;
379
+ }}
380
+
381
+ .workflow-card .runs {{
382
+ font-size: 1.5rem;
383
+ font-weight: 600;
384
+ color: var(--primary);
385
+ }}
386
+
387
+ .workflow-card .savings {{
388
+ font-size: 0.875rem;
389
+ color: var(--success);
390
+ }}
391
+
392
+ .tier-bar {{
393
+ display: flex;
394
+ height: 24px;
395
+ border-radius: 4px;
396
+ overflow: hidden;
397
+ margin: 1rem 0;
398
+ }}
399
+
400
+ .tier-bar .tier {{
401
+ display: flex;
402
+ align-items: center;
403
+ justify-content: center;
404
+ font-size: 0.75rem;
405
+ font-weight: 500;
406
+ color: white;
407
+ }}
408
+
409
+ .tier-bar .cheap {{ background: #10b981; }}
410
+ .tier-bar .capable {{ background: #3b82f6; }}
411
+ .tier-bar .premium {{ background: #8b5cf6; }}
412
+
413
+ .recent-run {{
414
+ display: flex;
415
+ align-items: center;
416
+ gap: 1rem;
417
+ padding: 0.75rem 0;
418
+ border-bottom: 1px solid var(--bg);
419
+ }}
420
+
421
+ .recent-run:last-child {{
422
+ border-bottom: none;
423
+ }}
424
+
425
+ .recent-run .name {{
426
+ font-weight: 500;
427
+ min-width: 100px;
428
+ }}
429
+
430
+ .recent-run .provider {{
431
+ font-size: 0.75rem;
432
+ color: var(--text-muted);
433
+ background: var(--bg);
434
+ padding: 0.125rem 0.5rem;
435
+ border-radius: 4px;
436
+ }}
437
+
438
+ .recent-run .result {{
439
+ margin-left: auto;
440
+ display: flex;
441
+ gap: 1rem;
442
+ align-items: center;
443
+ }}
444
+
445
+ .recent-run .savings-badge {{
446
+ font-size: 0.75rem;
447
+ color: var(--success);
448
+ font-weight: 500;
449
+ }}
450
+
451
+ .recent-run .time {{
452
+ font-size: 0.75rem;
453
+ color: var(--text-muted);
454
+ }}
455
+
456
+ footer {{
457
+ text-align: center;
458
+ color: var(--text-muted);
459
+ font-size: 0.875rem;
460
+ padding: 2rem 0;
461
+ }}
462
+
463
+ @media (prefers-color-scheme: dark) {{
464
+ :root {{
465
+ --bg: #111827;
466
+ --card-bg: #1f2937;
467
+ --text: #f9fafb;
468
+ --text-muted: #9ca3af;
469
+ }}
470
+ }}
471
+ </style>
472
+ </head>
473
+ <body>
474
+ <div class="container">
475
+ <header>
476
+ <h1>Empathy Framework Dashboard</h1>
477
+ <p class="subtitle">Pattern learning and cost optimization at a glance</p>
478
+ </header>
479
+
480
+ <div class="grid">
481
+ <div class="card">
482
+ <h2>Bug Patterns</h2>
483
+ <div class="value">{bug_count}</div>
484
+ <div class="label">patterns learned</div>
485
+ </div>
486
+
487
+ <div class="card">
488
+ <h2>Security Decisions</h2>
489
+ <div class="value">{security_count}</div>
490
+ <div class="label">documented</div>
491
+ </div>
492
+
493
+ <div class="card warning">
494
+ <h2>Tech Debt Items</h2>
495
+ <div class="value">{debt_items}</div>
496
+ <div class="label">tracked</div>
497
+ </div>
498
+
499
+ <div class="card">
500
+ <h2>Workflow Runs</h2>
501
+ <div class="value">{workflow_stats.get("total_runs", 0)}</div>
502
+ <div class="label">{workflow_stats.get("avg_savings_percent", 0):.0f}% savings</div>
503
+ </div>
504
+
505
+ <div class="card success">
506
+ <h2>Total Savings</h2>
507
+ <div class="value">${workflow_stats.get("total_savings", 0):.2f}</div>
508
+ <div class="label">workflows + API</div>
509
+ </div>
510
+ </div>
511
+
512
+ <div class="section">
513
+ <h2>Recent Bug Patterns</h2>
514
+ <table>
515
+ <thead>
516
+ <tr>
517
+ <th>Type</th>
518
+ <th>Root Cause</th>
519
+ <th>Status</th>
520
+ <th>Resolved</th>
521
+ </tr>
522
+ </thead>
523
+ <tbody>
524
+ {self._render_bug_table(patterns)}
525
+ </tbody>
526
+ </table>
527
+ </div>
528
+
529
+ <div class="section">
530
+ <h2>Multi-Model Workflows</h2>
531
+ <div class="workflow-grid">
532
+ {self._render_workflow_cards(workflow_stats)}
533
+ </div>
534
+
535
+ <h3 style="margin-bottom: 0.5rem; font-size: 1rem;">Model Tier Usage</h3>
536
+ {self._render_tier_bar(workflow_stats)}
537
+
538
+ <h3 style="margin-top: 1.5rem; margin-bottom: 0.5rem; font-size: 1rem;">Recent Runs</h3>
539
+ {self._render_recent_runs(workflow_stats)}
540
+ </div>
541
+
542
+ <div class="section">
543
+ <h2>Quick Commands</h2>
544
+ <p>Run these commands for common tasks:</p>
545
+ <div class="commands">
546
+ <span class="command">empathy morning</span>
547
+ <span class="command">empathy ship</span>
548
+ <span class="command">empathy fix-all</span>
549
+ <span class="command">empathy learn</span>
550
+ <span class="command">empathy sync-claude</span>
551
+ <span class="command">empathy costs</span>
552
+ </div>
553
+ </div>
554
+
555
+ <footer>
556
+ <p>Empathy Framework Dashboard - Refresh to update data</p>
557
+ <p>Last updated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
558
+ </footer>
559
+ </div>
560
+
561
+ <script>
562
+ // Auto-refresh every 30 seconds
563
+ setTimeout(() => location.reload(), 30000);
564
+ </script>
565
+ </body>
566
+ </html>"""
567
+
568
+ def _render_bug_table(self, patterns: dict) -> str:
569
+ """Render bug patterns as table rows."""
570
+ debugging_data = patterns.get("debugging", {})
571
+ if isinstance(debugging_data, dict):
572
+ bugs = debugging_data.get("patterns", [])
573
+ elif isinstance(debugging_data, list):
574
+ bugs = debugging_data
575
+ else:
576
+ bugs = []
577
+ if not bugs:
578
+ return (
579
+ '<tr><td colspan="4">No patterns yet. Run "empathy learn" to get started.</td></tr>'
580
+ )
581
+
582
+ rows = []
583
+ for bug in bugs[-10:]: # Last 10
584
+ status_class = bug.get("status", "investigating")
585
+ root = bug.get("root_cause", "")
586
+ root_display = (root[:60] + "...") if len(root) > 60 else (root or "-")
587
+ resolved = bug.get("resolved_at") or bug.get("timestamp")
588
+ date_display = resolved[:10] if resolved else "-"
589
+ rows.append(
590
+ f"""
591
+ <tr>
592
+ <td>{bug.get("bug_type", "unknown")}</td>
593
+ <td>{root_display}</td>
594
+ <td><span class="status {status_class}">{status_class}</span></td>
595
+ <td>{date_display}</td>
596
+ </tr>
597
+ """,
598
+ )
599
+
600
+ return "".join(rows)
601
+
602
+ def _render_workflow_cards(self, workflow_stats: dict) -> str:
603
+ """Render workflow stat cards."""
604
+ by_workflow = workflow_stats.get("by_workflow", {})
605
+
606
+ if not by_workflow:
607
+ return '<div class="workflow-card"><p>No workflow runs yet.</p></div>'
608
+
609
+ cards = []
610
+ for name, stats in by_workflow.items():
611
+ runs = stats.get("runs", 0)
612
+ savings = stats.get("savings", 0)
613
+ cards.append(
614
+ f"""
615
+ <div class="workflow-card">
616
+ <h3>{name}</h3>
617
+ <div class="runs">{runs}</div>
618
+ <div class="savings">${savings:.4f} saved</div>
619
+ </div>
620
+ """,
621
+ )
622
+
623
+ return "".join(cards)
624
+
625
+ def _render_tier_bar(self, workflow_stats: dict) -> str:
626
+ """Render model tier usage bar."""
627
+ by_tier = workflow_stats.get("by_tier", {})
628
+ total = sum(by_tier.values())
629
+
630
+ if total == 0:
631
+ return '<div class="tier-bar"><div class="tier">No data yet</div></div>'
632
+
633
+ cheap_pct = (by_tier.get("cheap", 0) / total) * 100 if total > 0 else 0
634
+ capable_pct = (by_tier.get("capable", 0) / total) * 100 if total > 0 else 0
635
+ premium_pct = (by_tier.get("premium", 0) / total) * 100 if total > 0 else 0
636
+
637
+ return f"""
638
+ <div class="tier-bar">
639
+ <div class="tier cheap" style="width: {cheap_pct}%;">{cheap_pct:.0f}% cheap</div>
640
+ <div class="tier capable" style="width: {capable_pct}%;">{capable_pct:.0f}% capable</div>
641
+ <div class="tier premium" style="width: {premium_pct}%;">{premium_pct:.0f}% premium</div>
642
+ </div>
643
+ <div style="display: flex; gap: 1rem; font-size: 0.75rem; color: var(--text-muted);">
644
+ <span>Cheap (Haiku/GPT-mini): ${by_tier.get("cheap", 0):.4f}</span>
645
+ <span>Capable (Sonnet/GPT-4o): ${by_tier.get("capable", 0):.4f}</span>
646
+ <span>Premium (Opus/GPT-5): ${by_tier.get("premium", 0):.4f}</span>
647
+ </div>
648
+ """
649
+
650
+ def _render_recent_runs(self, workflow_stats: dict) -> str:
651
+ """Render recent workflow runs."""
652
+ recent_runs = workflow_stats.get("recent_runs", [])
653
+
654
+ if not recent_runs:
655
+ return '<p style="color: var(--text-muted);">No recent workflow runs.</p>'
656
+
657
+ runs_html = []
658
+ for run in recent_runs[:5]: # Show last 5
659
+ name = run.get("workflow", "unknown")
660
+ provider = run.get("provider", "unknown")
661
+ success = run.get("success", False)
662
+ savings_pct = run.get("savings_percent", 0)
663
+ started = run.get("started_at", "")
664
+
665
+ # Format time
666
+ time_str = started[:16].replace("T", " ") if started else "-"
667
+
668
+ status_icon = "&#10003;" if success else "&#10007;"
669
+ status_color = "var(--success)" if success else "var(--danger)"
670
+
671
+ runs_html.append(
672
+ f"""
673
+ <div class="recent-run">
674
+ <span style="color: {status_color};">{status_icon}</span>
675
+ <span class="name">{name}</span>
676
+ <span class="provider">{provider}</span>
677
+ <span class="result">
678
+ <span class="savings-badge">{savings_pct:.0f}% saved</span>
679
+ <span class="time">{time_str}</span>
680
+ </span>
681
+ </div>
682
+ """,
683
+ )
684
+
685
+ return "".join(runs_html)
686
+
687
+
688
+ class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
689
+ """Threaded HTTP server."""
690
+
691
+ allow_reuse_address = True
692
+
693
+
694
+ def run_dashboard(
695
+ port: int = 8765,
696
+ patterns_dir: str = "./patterns",
697
+ empathy_dir: str = ".empathy",
698
+ open_browser: bool = True,
699
+ ) -> None:
700
+ """Run the dashboard server.
701
+
702
+ Args:
703
+ port: Port to run on (default: 8765)
704
+ patterns_dir: Path to patterns directory
705
+ empathy_dir: Path to empathy data directory
706
+ open_browser: Open browser automatically
707
+
708
+ """
709
+ # Configure handler
710
+ DashboardHandler.patterns_dir = patterns_dir
711
+ DashboardHandler.empathy_dir = empathy_dir
712
+
713
+ url = f"http://localhost:{port}"
714
+
715
+ print("\n Empathy Dashboard")
716
+ print(f" Running at: {url}")
717
+ print(" Press Ctrl+C to stop\n")
718
+
719
+ if open_browser:
720
+ # Open browser in a separate thread to not block server start
721
+ threading.Timer(0.5, lambda: webbrowser.open(url)).start()
722
+
723
+ try:
724
+ with ThreadedHTTPServer(("", port), DashboardHandler) as httpd:
725
+ httpd.serve_forever()
726
+ except KeyboardInterrupt:
727
+ print("\n Dashboard stopped.")
728
+
729
+
730
+ def cmd_dashboard(args):
731
+ """CLI command handler for dashboard."""
732
+ port = getattr(args, "port", 8765)
733
+ patterns_dir = getattr(args, "patterns_dir", "./patterns")
734
+ empathy_dir = getattr(args, "empathy_dir", ".empathy")
735
+ no_browser = getattr(args, "no_browser", False)
736
+
737
+ run_dashboard(
738
+ port=port,
739
+ patterns_dir=patterns_dir,
740
+ empathy_dir=empathy_dir,
741
+ open_browser=not no_browser,
742
+ )
743
+ return 0